♻️ refactored code
This commit is contained in:
@@ -1,2 +1,7 @@
|
||||
node_modules
|
||||
*.log
|
||||
# 忽略大模型目录
|
||||
dist/tfjs/
|
||||
dist/caffemodel/
|
||||
|
||||
default.conf
|
||||
|
22
Dockerfile
22
Dockerfile
@@ -1,12 +1,30 @@
|
||||
|
||||
FROM nginx:latest
|
||||
FROM nginx:alpine
|
||||
|
||||
LABEL maintainer="landaiqing <<landaiqing@126.com>>"
|
||||
|
||||
ENV TimeZone=Asia/Shanghai
|
||||
|
||||
# 创建空目录用于后续挂载
|
||||
RUN mkdir -p /usr/share/nginx/html/tfjs \
|
||||
&& mkdir -p /usr/share/nginx/html/caffemodel \
|
||||
&& mkdir -p /etc/nginx/conf.d
|
||||
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
# 强制删除模型目录(确保后续挂载覆盖)
|
||||
RUN rm -rf /usr/share/nginx/html/tfjs \
|
||||
&& rm -rf /usr/share/nginx/html/caffemodel \
|
||||
&& rm -f /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
#docker run -d \
|
||||
# -v /your_host/nginx.conf:/etc/nginx/nginx.conf \
|
||||
# -v /your_host/conf.d/:/etc/nginx/conf.d/ \
|
||||
# -v /your_host/tfjs:/usr/share/nginx/html/tfjs \
|
||||
# -v /your_host/caffemodel:/usr/share/nginx/html/caffemodel \
|
||||
# --restart unless-stopped \
|
||||
# -p 80:80 \
|
||||
# --name your_image
|
||||
# your_image
|
||||
|
3
auto-import.d.ts
vendored
3
auto-import.d.ts
vendored
@@ -6,7 +6,10 @@
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const AppstoreOutlined: typeof import('@ant-design/icons-vue')['AppstoreOutlined']
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const MailOutlined: typeof import('@ant-design/icons-vue')['MailOutlined']
|
||||
const SettingOutlined: typeof import('@ant-design/icons-vue')['SettingOutlined']
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||
|
29
components.d.ts
vendored
29
components.d.ts
vendored
@@ -14,6 +14,7 @@ declare module 'vue' {
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
ACard: typeof import('ant-design-vue/es')['Card']
|
||||
ACascader: typeof import('ant-design-vue/es')['Cascader']
|
||||
AccountSetting: typeof import('./src/views/User/AccountSetting/AccountSetting.vue')['default']
|
||||
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
||||
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
||||
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||
@@ -28,9 +29,9 @@ declare module 'vue' {
|
||||
AImage: typeof import('ant-design-vue/es')['Image']
|
||||
AImagePreviewGroup: typeof import('ant-design-vue/es')['ImagePreviewGroup']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
|
||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
AlbumCard: typeof import('./src/views/Album/Phoalbum/AlbumCard.vue')['default']
|
||||
AlbumShareModal: typeof import('./src/views/Album/Phoalbum/AlbumShareModal.vue')['default']
|
||||
AllPhoto: typeof import('./src/views/Photograph/AllPhoto/AllPhoto.vue')['default']
|
||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||
@@ -43,7 +44,6 @@ declare module 'vue' {
|
||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
||||
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
|
||||
ArrowDownOutlined: typeof import('@ant-design/icons-vue')['ArrowDownOutlined']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||
@@ -79,15 +79,16 @@ declare module 'vue' {
|
||||
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
|
||||
GradientText: typeof import('./src/components/MyUI/GradientText/GradientText.vue')['default']
|
||||
ImageShare: typeof import('./src/views/Share/ImageShare/ImageShare.vue')['default']
|
||||
ImageToolbar: typeof import('./src/views/Photograph/ImageToolbar/ImageToolbar.vue')['default']
|
||||
ImageUpload: typeof import('./src/views/Photograph/ImageUpload/ImageUpload.vue')['default']
|
||||
ItalicOutlined: typeof import('@ant-design/icons-vue')['ItalicOutlined']
|
||||
ImageToolbar: typeof import('./src/components/ImageToolbar/ImageToolbar.vue')['default']
|
||||
ImageUpload: typeof import('./src/components/ImageUpload/ImageUpload.vue')['default']
|
||||
ImageWaterfallList: typeof import('./src/components/ImageWaterfallList/ImageWaterfallList.vue')['default']
|
||||
Index: typeof import('./src/views/Album/ThingAlbum/Index.vue')['default']
|
||||
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
||||
LeftOutlined: typeof import('@ant-design/icons-vue')['LeftOutlined']
|
||||
LinkOutlined: typeof import('@ant-design/icons-vue')['LinkOutlined']
|
||||
LoadingGraphic: typeof import('./src/components/LoadingGraphic/LoadingGraphic.vue')['default']
|
||||
LocationAlbum: typeof import('./src/views/Album/LocationAlbum/LocationAlbum.vue')['default']
|
||||
LocationAlbumDetail: typeof import('./src/views/Album/LocationAlbum/LocationAlbumDetail.vue')['default']
|
||||
LocationAlbumIndex: typeof import('./src/views/Album/LocationAlbum/LocationAlbumIndex.vue')['default']
|
||||
LocationAlbumList: typeof import('./src/views/Album/LocationAlbum/LocationAlbumList.vue')['default']
|
||||
LockOutlined: typeof import('@ant-design/icons-vue')['LockOutlined']
|
||||
LoginFooter: typeof import('./src/views/Login/LoginFooter.vue')['default']
|
||||
@@ -97,13 +98,14 @@ declare module 'vue' {
|
||||
NotFound: typeof import('./src/views/404/NotFound.vue')['default']
|
||||
OrderedListOutlined: typeof import('@ant-design/icons-vue')['OrderedListOutlined']
|
||||
ParameterSetting: typeof import('./src/views/Upscale/ParameterSetting.vue')['default']
|
||||
PeopleAlbum: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbum.vue')['default']
|
||||
PeopleAlbumDetail: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbumDetail.vue')['default']
|
||||
PeopleAlbumIndex: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbumIndex.vue')['default']
|
||||
PeopleAlbumList: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbumList.vue')['default']
|
||||
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
|
||||
PeopleAlbumToolbar: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbumToolbar.vue')['default']
|
||||
PersonalCenter: typeof import('./src/views/User/PersonalCenter/PersonalCenter.vue')['default']
|
||||
PhoalbumDetail: typeof import('./src/views/Album/Phoalbum/PhoalbumDetail.vue')['default']
|
||||
PhoalbumIndex: typeof import('./src/views/Album/Phoalbum/PhoalbumIndex.vue')['default']
|
||||
PhoalbumList: typeof import('./src/views/Album/Phoalbum/PhoalbumList.vue')['default']
|
||||
PhoneOutlined: typeof import('@ant-design/icons-vue')['PhoneOutlined']
|
||||
PhotoStack: typeof import('./src/components/PhotoStack/PhotoStack.vue')['default']
|
||||
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
|
||||
PlusSquareOutlined: typeof import('@ant-design/icons-vue')['PlusSquareOutlined']
|
||||
@@ -129,14 +131,19 @@ declare module 'vue' {
|
||||
Spin: typeof import('./src/components/MyUI/Spin/Spin.vue')['default']
|
||||
StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default']
|
||||
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
||||
ThingAlbum: typeof import('./src/views/Album/ThingAlbum/ThingAlbum.vue')['default']
|
||||
ThingAlbumDetail: typeof import('./src/views/Album/ThingAlbum/ThingAlbumDetail.vue')['default']
|
||||
ThingAlbumIndex: typeof import('./src/views/Album/ThingAlbum/ThingAlbumIndex.vue')['default']
|
||||
ThingAlbumList: typeof import('./src/views/Album/ThingAlbum/ThingAlbumList.vue')['default']
|
||||
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
|
||||
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
||||
UploadOutlined: typeof import('@ant-design/icons-vue')['UploadOutlined']
|
||||
Upscale: typeof import('./src/views/Upscale/index.vue')['default']
|
||||
UpscalePhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/UpscalePhoneUpload.vue')['default']
|
||||
UserCenterAnalysis: typeof import('./src/views/User/PersonalCenter/components/UserCenterAnalysis/UserCenterAnalysis.vue')['default']
|
||||
UserCenterDynamic: typeof import('./src/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue')['default']
|
||||
UserCenterHome: typeof import('./src/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue')['default']
|
||||
UserCenterInfo: typeof import('./src/views/User/PersonalCenter/components/UserCenterInfo/UserCenterInfo.vue')['default']
|
||||
UserCenterSetting: typeof import('./src/views/User/PersonalCenter/components/UserCenterSetting/UserCenterSetting.vue')['default']
|
||||
UserCenterShare: typeof import('./src/views/User/PersonalCenter/components/UserCenterShare/UserCenterShare.vue')['default']
|
||||
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
|
||||
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
||||
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
||||
|
@@ -27,7 +27,7 @@ server {
|
||||
}
|
||||
location /sys/ {
|
||||
rewrite ^/sys/(.*)$ /$1 break;
|
||||
proxy_pass http://127.0.0.1:80;
|
||||
proxy_pass http://landaiqing.cn:80;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
15
package.json
15
package.json
@@ -7,7 +7,7 @@
|
||||
"dev": "vite --mode development --host",
|
||||
"build": "vue-tsc -b --noEmit && vite build --mode production",
|
||||
"preview": "vite preview",
|
||||
"docker-build": "docker build -t schisandra/schisandra-cloud-album-front ."
|
||||
"docker-build": "docker build -t landaiqing/schisandra-album-front:v1.0.0 ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@alova/adapter-axios": "^2.0.13",
|
||||
@@ -39,7 +39,7 @@
|
||||
"alova": "^3.2.8",
|
||||
"animejs": "^3.2.2",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"axios": "^1.7.9",
|
||||
"axios": "^1.8.1",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
@@ -55,6 +55,7 @@
|
||||
"moment": "^2.30.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"nsfwjs": "^4.2.1",
|
||||
"opencv-qr": "^0.7.0",
|
||||
"pinia": "^3.0.1",
|
||||
"pinia-plugin-persistedstate-2": "^2.0.28",
|
||||
"qrcode": "^1",
|
||||
@@ -77,18 +78,18 @@
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"globals": "^16.0.0",
|
||||
"sass": "^1.85.0",
|
||||
"sass": "^1.85.1",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"typescript-eslint": "^8.25.0",
|
||||
"unplugin-vue-components": "^28.4.0",
|
||||
"vite": "^6.1.1",
|
||||
"vite": "^6.2.0",
|
||||
"vite-plugin-bundle-obfuscator": "1.4.1",
|
||||
"vite-plugin-chunk-split": "^0.5.0",
|
||||
"vue-tsc": "2.2.2"
|
||||
"vue-tsc": "2.2.4"
|
||||
},
|
||||
"overrides": {
|
||||
"vite-plugin-chunk-split": {
|
||||
"vite": "^6.1.1"
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
public/caffemodel/detect.caffemodel
Normal file
BIN
public/caffemodel/detect.caffemodel
Normal file
Binary file not shown.
2716
public/caffemodel/detect.prototxt
Normal file
2716
public/caffemodel/detect.prototxt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/caffemodel/sr.caffemodel
Normal file
BIN
public/caffemodel/sr.caffemodel
Normal file
Binary file not shown.
403
public/caffemodel/sr.prototxt
Normal file
403
public/caffemodel/sr.prototxt
Normal file
@@ -0,0 +1,403 @@
|
||||
layer {
|
||||
name: "data"
|
||||
type: "Input"
|
||||
top: "data"
|
||||
input_param {
|
||||
shape {
|
||||
dim: 1
|
||||
dim: 1
|
||||
dim: 224
|
||||
dim: 224
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "conv0"
|
||||
type: "Convolution"
|
||||
bottom: "data"
|
||||
top: "conv0"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "conv0/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "conv0"
|
||||
top: "conv0"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/reduce"
|
||||
type: "Convolution"
|
||||
bottom: "conv0"
|
||||
top: "db1/reduce"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/reduce/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db1/reduce"
|
||||
top: "db1/reduce"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/3x3"
|
||||
type: "Convolution"
|
||||
bottom: "db1/reduce"
|
||||
top: "db1/3x3"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 8
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/3x3/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db1/3x3"
|
||||
top: "db1/3x3"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/1x1"
|
||||
type: "Convolution"
|
||||
bottom: "db1/3x3"
|
||||
top: "db1/1x1"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/1x1/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db1/1x1"
|
||||
top: "db1/1x1"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/concat"
|
||||
type: "Concat"
|
||||
bottom: "conv0"
|
||||
bottom: "db1/1x1"
|
||||
top: "db1/concat"
|
||||
concat_param {
|
||||
axis: 1
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/reduce"
|
||||
type: "Convolution"
|
||||
bottom: "db1/concat"
|
||||
top: "db2/reduce"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/reduce/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db2/reduce"
|
||||
top: "db2/reduce"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/3x3"
|
||||
type: "Convolution"
|
||||
bottom: "db2/reduce"
|
||||
top: "db2/3x3"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 8
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/3x3/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db2/3x3"
|
||||
top: "db2/3x3"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/1x1"
|
||||
type: "Convolution"
|
||||
bottom: "db2/3x3"
|
||||
top: "db2/1x1"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/1x1/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db2/1x1"
|
||||
top: "db2/1x1"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/concat"
|
||||
type: "Concat"
|
||||
bottom: "db1/concat"
|
||||
bottom: "db2/1x1"
|
||||
top: "db2/concat"
|
||||
concat_param {
|
||||
axis: 1
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/reduce"
|
||||
type: "Convolution"
|
||||
bottom: "db2/concat"
|
||||
top: "upsample/reduce"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/reduce/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "upsample/reduce"
|
||||
top: "upsample/reduce"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/deconv"
|
||||
type: "Deconvolution"
|
||||
bottom: "upsample/reduce"
|
||||
top: "upsample/deconv"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 32
|
||||
stride: 2
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "upsample/deconv"
|
||||
top: "upsample/deconv"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/rec"
|
||||
type: "Convolution"
|
||||
bottom: "upsample/deconv"
|
||||
top: "upsample/rec"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 1
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "nearest"
|
||||
type: "Deconvolution"
|
||||
bottom: "data"
|
||||
top: "nearest"
|
||||
param {
|
||||
lr_mult: 0.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 1
|
||||
bias_term: false
|
||||
pad: 0
|
||||
kernel_size: 2
|
||||
group: 1
|
||||
stride: 2
|
||||
weight_filler {
|
||||
type: "constant"
|
||||
value: 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "Crop1"
|
||||
type: "Crop"
|
||||
bottom: "nearest"
|
||||
bottom: "upsample/rec"
|
||||
top: "Crop1"
|
||||
}
|
||||
layer {
|
||||
name: "fc"
|
||||
type: "Eltwise"
|
||||
bottom: "Crop1"
|
||||
bottom: "upsample/rec"
|
||||
top: "fc"
|
||||
eltwise_param {
|
||||
operation: SUM
|
||||
}
|
||||
}
|
BIN
src/assets/images/bg.webp
Normal file
BIN
src/assets/images/bg.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
1
src/assets/svgs/data_analysis.svg
Normal file
1
src/assets/svgs/data_analysis.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1740650615689" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7146" width="200" height="200"><path d="M723.2 188.8a106.88 106.88 0 1 0 213.76 0 106.88 106.88 0 1 0-213.76 0z" fill="#08979C" opacity=".3" p-id="7147"></path><path d="M857.267 361.62a151.199 151.199 0 0 1-60.938 12.816c-83.881 0-151.88-67.999-151.88-151.88 0-21.688 4.624-42.276 12.816-60.938-6.093-0.517-12.217-0.937-18.437-0.937H301.307C180.644 160.676 81.92 259.395 81.92 380.058v337.515c0 120.663 98.724 219.387 219.387 219.387h337.515c120.663 0 219.387-98.724 219.387-219.387V380.058c-0.005-6.221-0.425-12.345-0.942-18.438zM703.32 460.908l-135.004 168.76a39.357 39.357 0 0 1-55.957 5.647l-104.146-86.784-109.91 137.39a39.286 39.286 0 0 1-30.777 14.777 39.24 39.24 0 0 1-24.57-8.622 39.388 39.388 0 0 1-6.15-55.353l135.01-168.76a39.404 39.404 0 0 1 26.792-14.582 39.552 39.552 0 0 1 29.164 8.935L531.917 549.1l109.91-137.39c13.58-16.973 38.355-19.723 55.348-6.155a39.388 39.388 0 0 1 6.144 55.353z" fill="#08979C" p-id="7148"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
1
src/assets/svgs/dynamic.svg
Normal file
1
src/assets/svgs/dynamic.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1740650748717" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9995" width="200" height="200"><path d="M608.09216 61.92128c0.64 0 1.26976 0 1.69984 0.10752 1.05984 5.72928 0.84992 20.26496-8.27904 44.76928l-79.03232 214.50752c-20.48 55.48032-67.89632 95.68768-112.768 95.68768-8.38144 0-16.54784-1.3824-24.192-4.2496-50.176-18.4576-75.84768-74.26048-57.39008-124.43648 22.912-62.06464 110.11584-149.89824 186.60352-187.8784l51.24096-25.46176c23.86432-11.77088 37.34528-13.04576 42.11712-13.04576M702.08 329.14944c11.35104 0 22.49216 2.01728 33.09568 5.9392 62.06464 22.91712 149.90336 110.11584 187.87328 186.5984l25.472 51.24096c11.65824 23.4496 13.35808 37.86752 13.05088 43.70944-1.28 0.20992-3.18976 0.43008-5.9392 0.43008-7.11168 0-19.52256-1.48992-38.93248-8.61184l-214.50752-79.03232c-67.69152-24.92928-109.57824-87.62368-91.43808-136.95488 13.99296-37.85728 50.69312-63.31904 91.32544-63.31904M61.00992 413.37856c7.10656 0 19.51744 1.48992 38.93248 8.59648l214.50752 79.03744c34.47808 12.72832 63.86688 35.95776 80.73216 63.75936 15.06304 24.92416 18.88256 50.9184 10.71104 73.1904-13.99808 37.87776-50.60096 63.33952-91.22816 63.33952-11.35104 0-22.49216-2.0224-33.1008-5.9392-62.05952-22.8096-149.89824-110.12096-187.87328-186.59328L68.22912 457.5232c-11.66848-23.44448-13.36832-37.87264-13.05088-43.70944 1.26464-0.22016 3.1744-0.4352 5.83168-0.4352M606.91968 613.3504c8.38144 0 16.54784 1.3824 24.192 4.24448 50.18112 18.45248 75.84768 74.26048 57.39008 124.43648-22.8096 62.06464-110.11584 149.89824-186.5984 187.99104L450.6624 955.4944c-23.6544 11.77088-37.23776 13.04576-42.0096 13.04576-0.64 0-1.27488 0-1.69984-0.10752-1.06496-5.72928-0.84992-20.25984 8.27904-44.76928l79.03232-214.50752c20.25984-55.49568 67.78368-95.80544 112.65536-95.80544" fill="" p-id="9996"></path><path d="M608.09216 16.62464c-16.8704 0-37.86752 5.72928-62.16704 17.71008l-51.24096 25.46176C406.73792 103.50592 312.74496 199.30624 285.696 272.60416c-27.05408 73.51808 10.81856 155.62752 84.23424 182.6816a114.39104 114.39104 0 0 0 39.7824 6.99904c62.90432 0 127.40608-49.54112 155.30496-125.3888l79.03232-214.5024c24.61184-66.62144 8.27392-105.76896-35.95776-105.76896z" fill="#F48134" p-id="9997"></path><path d="M702.08 283.8528c-57.92256 0-112.66048 35.64032-133.77536 93.03552-27.04896 73.40544 26.20416 161.2544 118.28736 195.09248l214.50752 79.03232c21.11488 7.7568 39.36256 11.45856 54.63552 11.45856 51.23584 0 67.1488-41.79968 33.52064-109.58848l-25.46176-51.22048c-43.70432-87.94624-139.50464-181.9392-212.80256-208.98816a139.14112 139.14112 0 0 0-48.91136-8.82176z" fill="#61F24C" p-id="9998"></path><path d="M61.00992 367.97952c-51.34336 0-67.15392 41.79456-33.52576 109.58848l25.46176 51.23072c43.70944 87.94624 139.50464 181.9392 212.80768 208.98816a140.49792 140.49792 0 0 0 48.79872 8.68864c57.92256 0 112.66048-35.64544 133.77024-93.04064 27.05408-73.41056-26.19904-161.24416-118.28224-195.08736L115.64032 379.43808c-21.10976-7.74656-39.45984-11.45856-54.6304-11.45856z" fill="#F2515D" p-id="9999"></path><path d="M606.91968 568.05376c-62.90944 0-127.4112 49.54112-155.30496 125.38368L372.5824 907.95008c-24.50432 66.62144-8.1664 105.8816 36.0704 105.8816 16.86528 0 37.86752-5.72928 62.16192-17.72032l51.24096-25.46176c87.94624-43.70944 181.93408-139.50976 208.98304-212.80768 27.04896-73.40032-10.9312-155.52-84.34176-182.56896a112.0512 112.0512 0 0 0-39.77728-7.2192z" fill="#4678F2" p-id="10000"></path></svg>
|
After Width: | Height: | Size: 3.4 KiB |
1
src/assets/svgs/home.svg
Normal file
1
src/assets/svgs/home.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1740650417427" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3483" width="200" height="200"><path d="M265.2 646.8v116.5c0 39.8 32.3 72.2 72.2 72.2h300.2V572c0-24.9 20.3-45.2 45.2-45.2 24.9 0 45.2 20.3 45.2 45.2v263.4h28.1c39.8 0 72.2-32.3 72.2-72.2V523.8c37.7 0 55.5-46.4 27.6-71.7L592.2 213.4c-25.8-23.4-65.2-23.4-91 0L237.6 452.1c-27.9 25.3-10 71.7 27.6 71.7v46.6" fill="#5CC7B9" p-id="3484"></path><path d="M700.6 848h-44.1V568.6c0-16.1-13.1-29.2-29.2-29.2s-29.2 13.1-29.2 29.2V848H281.9c-48.6 0-88.2-39.5-88.2-88.2V643.4c0-8.8 7.2-16 16-16s16 7.2 16 16v116.5c0 31 25.2 56.2 56.2 56.2h284.2V568.6c0-33.8 27.4-61.2 61.2-61.2 33.8 0 61.2 27.4 61.2 61.2V816h12.1c31 0 56.2-25.2 56.2-56.2V504.4h16c16 0 22-12.3 23.4-16.1 1.4-3.8 5.3-17-6.6-27.7L526 221.9c-19.7-17.9-49.9-17.9-69.6 0L192.9 460.6c-11.8 10.7-8.1 23.9-6.6 27.7 1.4 3.8 7.4 16.1 23.4 16.1h16V567c0 8.8-7.2 16-16 16s-16-7.2-16-16v-32.8c-16.9-4.8-30.7-17.3-37.4-34.3-8.6-22.2-2.7-47 15-63L435 198.2c15.4-14 35.4-21.7 56.2-21.7 20.8 0 40.8 7.7 56.2 21.7L811 436.8c17.7 16 23.5 40.7 15 63-6.6 17-20.5 29.5-37.4 34.3v225.8c0.1 48.6-39.4 88.1-88 88.1z m0 0" fill="#333333" p-id="3485"></path><path d="M327 645.2c-8.8 0-16-7.2-16-16v-91.4c0-8.8 7.2-16 16-16s16 7.2 16 16v91.4c0 8.8-7.1 16-16 16z m0 0M308.8 667.5c0 9.3 8.2 16.9 18.2 16.9 10.1 0 18.2-7.6 18.2-16.9 0-9.3-8.2-16.9-18.2-16.9s-18.2 7.6-18.2 16.9z m0 0" fill="#FBFFFD" p-id="3486"></path><path d="M215.2 240.6c0 12 9.7 21.8 21.8 21.8 12 0 21.8-9.7 21.8-21.8 0-12-9.7-21.8-21.8-21.8-12.1 0.1-21.8 9.8-21.8 21.8z m0 0M269.8 202.9c0 7.8 6.3 14.2 14.2 14.2 7.8 0 14.2-6.3 14.2-14.2 0-7.8-6.3-14.2-14.2-14.2-7.8 0-14.2 6.4-14.2 14.2z m0 0M756.5 181l10.7 32.9c0.6 1.7 2.2 2.9 4 2.9h34.6c4.1 0 5.8 5.2 2.5 7.6l-28 20.3c-1.5 1.1-2.1 3-1.5 4.7l10.7 32.9c1.3 3.9-3.2 7.1-6.5 4.7l-28-20.3a4.2 4.2 0 0 0-5 0L722 287c-3.3 2.4-7.7-0.8-6.5-4.7l10.7-32.9c0.6-1.7-0.1-3.6-1.5-4.7l-28-20.3c-3.3-2.4-1.6-7.6 2.5-7.6h34.6c1.8 0 3.4-1.2 4-2.9l10.7-32.9c1.3-3.8 6.8-3.8 8 0z" fill="#5CC7B9" p-id="3487"></path></svg>
|
After Width: | Height: | Size: 2.0 KiB |
69
src/components/ImageWaterfallList/ImageWaterfallList.vue
Normal file
69
src/components/ImageWaterfallList/ImageWaterfallList.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<Spin size="middle" :spinning="imageStore.imageListLoading" indicator="spin-dot" tip="loading..." :rotate="true">
|
||||
<div style="width:100%;height:100%;" v-if="props.imageList">
|
||||
<div v-for="(itemList, index) in props.imageList" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="imageStore.JustifiedLayoutOptions"
|
||||
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-if="!imageStore.imageListLoading && !props.imageList" 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>
|
||||
</Spin>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import useStore from "@/store";
|
||||
|
||||
const props = defineProps({
|
||||
imageList: {
|
||||
type: Array as () => any[],
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const imageStore = useStore().image;
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
@@ -58,10 +58,10 @@
|
||||
</AFlex>
|
||||
|
||||
</div>
|
||||
<div class="header-logo-popover-card" @click="router.push('/main/photo/phone')">
|
||||
<div class="header-logo-popover-card" @click="router.push('/main/photo/upscale')">
|
||||
<AFlex :vertical="false" align="center" justify="space-between">
|
||||
<AAvatar size="small" shape="square" :src="ai"/>
|
||||
<span class="header-logo-popover-text">{{ t('album.phone') }}</span>
|
||||
<span class="header-logo-popover-text">{{ t('album.upscale') }}</span>
|
||||
</AFlex>
|
||||
|
||||
</div>
|
||||
@@ -126,6 +126,7 @@ const cardStyle = computed(() => ({
|
||||
font-family: "Comic Sans MS", cursive;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
text-shadow: 0px 1px 2px rgba(0, 0, 0, .4);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -112,13 +112,13 @@
|
||||
<ADivider/>
|
||||
<div class="avatar-content-menu">
|
||||
<AMenu>
|
||||
<AMenuItem key="1">
|
||||
<AMenuItem key="1" @click="router.push('/main/user/center/home')">
|
||||
<template #icon>
|
||||
<AAvatar size="small" shape="circle" :src="personalCenter"/>
|
||||
</template>
|
||||
<span class="avatar-content-menu-item">个人中心</span>
|
||||
</AMenuItem>
|
||||
<AMenuItem key="2">
|
||||
<AMenuItem key="2" @click="router.push('/main/user/setting')">
|
||||
<template #icon>
|
||||
<AAvatar size="small" shape="circle" :src="accountSetting"/>
|
||||
</template>
|
||||
@@ -154,10 +154,12 @@ import logout from "@/assets/svgs/logout.svg";
|
||||
import wenhao from "@/assets/svgs/wenhao.svg";
|
||||
|
||||
import useStore from "@/store";
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
import ImageUpload from "@/components/ImageUpload/ImageUpload.vue";
|
||||
import {getStorageConfigListApi} from "@/api/storage";
|
||||
import {ProviderIcon} from "@/constant/provider_icon.ts";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
const uploadStore = useStore().upload;
|
||||
const user = useStore().user;
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import PhoalbumPhoalbum from "@/views/Album/Phoalbum/Phoalbum.vue";
|
||||
import PeopleAlbumPeopleAlbum from "@/views/Album/PeopleAlbum/PeopleAlbum.vue";
|
||||
import AlbumPhoalbumIndex from "@/views/Album/Phoalbum/PhoalbumIndex.vue";
|
||||
import PeoplePeopleAlbumIndex from "@/views/Album/PeopleAlbum/PeopleAlbumIndex.vue";
|
||||
import LocationAlbum from "@/views/Album/LocationAlbum/LocationAlbumList.vue";
|
||||
import LocationAlbumIndex from "@/views/Album/LocationAlbum/LocationAlbum.vue";
|
||||
import LocationLocationAlbumIndex from "@/views/Album/LocationAlbum/LocationAlbumIndex.vue";
|
||||
import ThingAlbum from "@/views/Album/ThingAlbum/ThingAlbumList.vue";
|
||||
import ThingAlbumThingAlbum from "@/views/Album/ThingAlbum/ThingAlbum.vue";
|
||||
import ThingThingAlbumIndex from "@/views/Album/ThingAlbum/ThingAlbumIndex.vue";
|
||||
import Phoalbum from "@/views/Album/Phoalbum/PhoalbumList.vue";
|
||||
import PeopleAlbum from "@/views/Album/PeopleAlbum/PeopleAlbumList.vue";
|
||||
import PhoalbumDetail from "@/views/Album/Phoalbum/PhoalbumDetail.vue";
|
||||
@@ -14,7 +14,7 @@ import ThingAlbumDetail from "@/views/Album/ThingAlbum/ThingAlbumDetail.vue";
|
||||
export default [
|
||||
{
|
||||
path: '/main/album/albums',
|
||||
component: PhoalbumPhoalbum,
|
||||
component: AlbumPhoalbumIndex,
|
||||
redirect: '/main/album/albums',
|
||||
children: [
|
||||
{
|
||||
@@ -39,7 +39,7 @@ export default [
|
||||
},
|
||||
{
|
||||
path: '/main/album/people',
|
||||
component: PeopleAlbumPeopleAlbum,
|
||||
component: PeoplePeopleAlbumIndex,
|
||||
redirect: '/main/album/people',
|
||||
children: [
|
||||
{
|
||||
@@ -64,7 +64,7 @@ export default [
|
||||
},
|
||||
{
|
||||
path: '/main/album/location',
|
||||
component: LocationAlbumIndex,
|
||||
component: LocationLocationAlbumIndex,
|
||||
redirect: '/main/album/location',
|
||||
children: [
|
||||
{
|
||||
@@ -89,7 +89,7 @@ export default [
|
||||
},
|
||||
{
|
||||
path: '/main/album/thing',
|
||||
component: ThingAlbumThingAlbum,
|
||||
component: ThingThingAlbumIndex,
|
||||
redirect: '/main/album/thing',
|
||||
children: [
|
||||
{
|
||||
|
@@ -20,7 +20,6 @@ export default [
|
||||
...recycling_bin,
|
||||
...share,
|
||||
...upscale,
|
||||
|
||||
]
|
||||
}, {
|
||||
path: '/main/share/list/:id',
|
||||
|
84
src/router/modules/user.ts
Normal file
84
src/router/modules/user.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import UserCenterHome from "@/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue";
|
||||
import UserCenterDynamic from "@/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue";
|
||||
import UserCenterInfo from "@/views/User/PersonalCenter/components/UserCenterInfo/UserCenterInfo.vue";
|
||||
import UserCenterAnalysis from "@/views/User/PersonalCenter/components/UserCenterAnalysis/UserCenterAnalysis.vue";
|
||||
import UserCenterShare from "@/views/User/PersonalCenter/components/UserCenterShare/UserCenterShare.vue";
|
||||
import UserCenterSetting from "@/views/User/PersonalCenter/components/UserCenterSetting/UserCenterSetting.vue";
|
||||
export default [
|
||||
{
|
||||
|
||||
path: '/main/user/center',
|
||||
name: 'userCenter',
|
||||
redirect: '/main/user/center/home',
|
||||
component: () => import('@/views/User/PersonalCenter/PersonalCenter.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '个人中心'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/main/user/center/home',
|
||||
name: 'home',
|
||||
component: UserCenterHome,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '主页'
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/main/user/center/dynamic',
|
||||
name: 'dynamic',
|
||||
component: UserCenterDynamic,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '动态'
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/main/user/center/info',
|
||||
name: 'info',
|
||||
component: UserCenterInfo,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '个人信息'
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/main/user/center/analysis',
|
||||
name: 'analysis',
|
||||
component: UserCenterAnalysis,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '数据分析'
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/main/user/center/share',
|
||||
name: 'share',
|
||||
component: UserCenterShare,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '我的分享'
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/main/user/center/setting',
|
||||
name: 'setting',
|
||||
component: UserCenterSetting,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '设置'
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/main/user/setting',
|
||||
name: 'userSetting',
|
||||
component: () => import('@/views/User/AccountSetting/AccountSetting.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '账户设置'
|
||||
}
|
||||
}
|
||||
];
|
@@ -8,6 +8,7 @@ import landing from "./modules/landing.ts";
|
||||
import mainRouter from "./modules/main_router.ts";
|
||||
import i18n from "@/locales";
|
||||
import phone_upload from "@/router/modules/phone_upload.ts";
|
||||
import user from "@/router/modules/user.ts";
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
...login,
|
||||
@@ -15,6 +16,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
...landing,
|
||||
...mainRouter,
|
||||
...phone_upload,
|
||||
...user,
|
||||
{
|
||||
path: '/:pathMatch(.*)',
|
||||
redirect: '/404',
|
||||
|
@@ -1,16 +1,12 @@
|
||||
import {ImageList} from "@/types/image";
|
||||
import localForage from "localforage";
|
||||
import {albumListApi, getFaceSamplesList} from "@/api/storage";
|
||||
|
||||
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": "全部",
|
||||
@@ -18,14 +14,72 @@ export const useImageStore = defineStore(
|
||||
"gif": "动图",
|
||||
"screenshot": "截图",
|
||||
});
|
||||
// 清算模式切换
|
||||
// 清爽模式切换
|
||||
const switchValue = ref<boolean>(false);
|
||||
|
||||
|
||||
// 图片列表布局相关
|
||||
const JustifiedLayoutOptions = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
// 图片列表数据
|
||||
const imageList = ref<any[]>([]);
|
||||
const imageListLoading = ref<boolean>(false);
|
||||
|
||||
// 相册相关
|
||||
const albumList = ref<any[]>([]);
|
||||
const albumListLoading = ref<boolean>(false);
|
||||
const sortSelectedKey = ref<boolean>(true);
|
||||
const tabActiveKey = ref<string>("-1");
|
||||
const tabMap = reactive({
|
||||
"-1": "全部相册",
|
||||
"0": "我的相册",
|
||||
"1": "我的分享",
|
||||
"2": "我的收藏",
|
||||
});
|
||||
|
||||
// 相册分享弹窗相关
|
||||
const openAlbumShareDialog = ref<boolean>(false);
|
||||
const albumShareCoverImage = ref<string>("");
|
||||
const albumShareId = ref<number>(0);
|
||||
|
||||
// 人脸相册
|
||||
const faceSelectedKey = ref<string>("0");
|
||||
const faceSelected = ref<number[]>([]);
|
||||
const faceList = ref<any[]>([]);
|
||||
const faceListLoading = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* 获取人脸列表
|
||||
*/
|
||||
async function getFaceList() {
|
||||
faceListLoading.value = true;
|
||||
faceList.value = [];
|
||||
const res: any = await getFaceSamplesList(parseInt(faceSelectedKey.value));
|
||||
if (res && res.code === 200 && res.data.faces) {
|
||||
faceList.value = res.data.faces.map(face => ({
|
||||
...face,
|
||||
showButton: false,
|
||||
showInput: false,
|
||||
}));
|
||||
}
|
||||
faceListLoading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相册列表
|
||||
*/
|
||||
async function getAlbumList() {
|
||||
albumList.value = [];
|
||||
albumListLoading.value = true;
|
||||
const res: any = await albumListApi(parseInt(tabActiveKey.value), sortSelectedKey.value);
|
||||
if (res && res.code === 200) {
|
||||
albumList.value = res.data.albums;
|
||||
}
|
||||
albumListLoading.value = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 统计图片总数
|
||||
* @param imageList 图片列表数据
|
||||
@@ -53,18 +107,38 @@ export const useImageStore = defineStore(
|
||||
homeTabActiveKey,
|
||||
albumShareCoverImage,
|
||||
albumShareId,
|
||||
countTotalImages,
|
||||
imageList,
|
||||
imageListLoading,
|
||||
albumListLoading,
|
||||
albumList,
|
||||
sortSelectedKey,
|
||||
faceSelectedKey,
|
||||
faceSelected,
|
||||
faceList,
|
||||
faceListLoading,
|
||||
openAlbumShareDialog,
|
||||
JustifiedLayoutOptions,
|
||||
countTotalImages,
|
||||
openAlbumShareDialogHandler,
|
||||
getAlbumList,
|
||||
getFaceList,
|
||||
};
|
||||
},
|
||||
{
|
||||
// 开启数据持久化
|
||||
persistedState: {
|
||||
persist: true,
|
||||
storage: localStorage,
|
||||
storage: localForage,
|
||||
key: 'image',
|
||||
includePaths: ["tabActiveKey", "tabMap", "homeTabActiveKey", "homeTabMap", "switchValue"],
|
||||
includePaths: [
|
||||
"tabActiveKey",
|
||||
"tabMap",
|
||||
"homeTabActiveKey",
|
||||
"homeTabMap",
|
||||
"switchValue",
|
||||
"faceSelectedKey",
|
||||
"albumList"
|
||||
],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@@ -2,8 +2,11 @@ export const useMenuStore = defineStore(
|
||||
'menu',
|
||||
() => {
|
||||
const currentMenu = ref<string>('photo/all');
|
||||
|
||||
const userCenterMenu = ref<string>('home');
|
||||
return {
|
||||
currentMenu,
|
||||
userCenterMenu,
|
||||
};
|
||||
},
|
||||
{
|
||||
@@ -12,7 +15,7 @@ export const useMenuStore = defineStore(
|
||||
persist: true,
|
||||
storage: localStorage,
|
||||
key: 'menu',
|
||||
includePaths: ['currentMenu']
|
||||
includePaths: ['currentMenu', 'userCenterMenu']
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@@ -37,7 +37,7 @@ export const useUploadStore = defineStore(
|
||||
|
||||
const storageSelected = ref<any[]>([]);
|
||||
|
||||
const albumSelected = ref<number>();
|
||||
const albumSelected = ref<number>(0);
|
||||
|
||||
/**
|
||||
* 打开上传抽屉
|
||||
|
10
src/utils/QRDetection/qr_detection.ts
Normal file
10
src/utils/QRDetection/qr_detection.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import OpencvQr from "opencv-qr";
|
||||
|
||||
/**
|
||||
* OpenCV QR detection class
|
||||
*/
|
||||
const opencvQr = new OpencvQr({
|
||||
dw: "/caffemodel/detect.caffemodel",
|
||||
sw: "/caffemodel/sr.caffemodel",
|
||||
});
|
||||
export default opencvQr;
|
@@ -13,7 +13,7 @@ const initNSFWJs = async (): Promise<NSFWJS> => {
|
||||
} catch (_error) {
|
||||
console.warn("IndexedDB 中未找到模型,正在从网络加载...");
|
||||
// 如果 IndexedDB 加载失败,从 URL 加载模型并保存到 IndexedDB
|
||||
nsfwModelCache = await nsfwjs.load("/nsfw/mobilenet_v2_mid/", {size: 224, type: "graph"});
|
||||
nsfwModelCache = await nsfwjs.load("/tfjs/nsfw/mobilenet_v2_mid/", {size: 224, type: "graph"});
|
||||
await nsfwModelCache.model.save("indexeddb://nsfwjs-model");
|
||||
console.log("NSFWJS 模型已从网络加载并保存到 IndexedDB");
|
||||
}
|
||||
|
@@ -7,78 +7,39 @@
|
||||
<span class="location-detail-content-nav-name">{{ route.query.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ImageToolbar :selected="imageStore.selected" :imageList="albumList"/>
|
||||
<ImageToolbar :selected="imageStore.selected" :imageList="imageList"/>
|
||||
<div class="location-album-detail-info">
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(albumList) }}张照片</span>
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(imageList) }}张照片</span>
|
||||
</div>
|
||||
<div class="location-album-detail-list">
|
||||
<div style="width:100%;height:100%;" v-if="albumList.length != 0">
|
||||
<div v-for="(itemList, index) in albumList" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
<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"
|
||||
:previewMask="false"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy"/>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty" :image-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>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
|
||||
import {queryLocationDetailListApi} from "@/api/storage";
|
||||
import useStore from "@/store";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import ImageToolbar from "@/components/ImageToolbar/ImageToolbar.vue";
|
||||
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
|
||||
const imageStore = useStore().image;
|
||||
const albumList = ref<any[]>([]);
|
||||
const imageList = ref<any[]>([]);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const upload = useStore().upload;
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
|
||||
|
||||
async function getImageList(id: number) {
|
||||
imageStore.imageListLoading = true;
|
||||
const res: any = await queryLocationDetailListApi(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
console.log(res);
|
||||
if (res && res.code === 200) {
|
||||
albumList.value = res.data.records;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
imageStore.imageListLoading = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@@ -14,78 +14,37 @@
|
||||
<span style="font-size: 14px;color: #333333">{{ route.query.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ImageToolbar :selected="imageStore.selected" :imageList="images"/>
|
||||
<ImageToolbar :selected="imageStore.selected" :imageList="imageList"/>
|
||||
<div class="people-album-detail-info">
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(images) }}张照片</span>
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(imageList) }}张照片</span>
|
||||
</div>
|
||||
<div class="people-album-detail-list">
|
||||
<div style="width:100%;height:100%;" v-if="images &&images.length !== 0">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
<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"
|
||||
:previewMask="false"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy"/>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty" :image-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>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import {getFaceSamplesDetailList} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import ImageToolbar from "@/components/ImageToolbar/ImageToolbar.vue";
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
|
||||
|
||||
const imageStore = useStore().image;
|
||||
const images = ref<any[]>([]);
|
||||
const imageList = ref<any[]>([]);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const upload = useStore().upload;
|
||||
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
|
||||
async function getAlbumList(id: number) {
|
||||
imageStore.imageListLoading = true;
|
||||
const res: any = await getFaceSamplesDetailList(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
images.value = res.data.records;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
imageStore.imageListLoading = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@@ -3,70 +3,37 @@
|
||||
<div class="people-album-header">
|
||||
<ADropdown trigger="click">
|
||||
<AButton type="text" size="large" class="people-album-button">
|
||||
{{ selectedKey === '0' ? '人 物' : '已隐藏' }}
|
||||
{{ imageStore.faceSelectedKey === '0' ? '人 物' : '已隐藏' }}
|
||||
<DownOutlined class="people-album-icon"/>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu selectable :selectedKeys="[selectedKey]" @select="handleSelect">
|
||||
<AMenu selectable :selectedKeys="[imageStore.faceSelectedKey]" @select="handleSelect">
|
||||
<AMenuItem key="0">人 物</AMenuItem>
|
||||
<AMenuItem key="1">已隐藏</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
<span class="people-album-count">共<span style="color: #0e87cc">{{ faceList.length }}</span>位</span>
|
||||
<span class="people-album-count">共<span style="color: #0e87cc">{{ imageStore.faceList.length }}</span>位</span>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div class="people-album-toolbar" v-show="selected.length !== 0">
|
||||
<div class="people-album-toolbar-left">
|
||||
<AButton type="text" shape="circle" size="large" class="people-album-toolbar-btn" @click="cancelSelectPeople">
|
||||
<template #icon>
|
||||
<CloseOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
</AButton>
|
||||
<span style="font-size: 16px;font-weight: bold">
|
||||
已选择 {{ selected.length }} 个人物
|
||||
</span>
|
||||
<AButton type="text" shape="default" class="people-album-toolbar-btn" size="middle" @click="selectAllPeople">
|
||||
全选
|
||||
</AButton>
|
||||
</div>
|
||||
<div class="people-album-toolbar-right">
|
||||
|
||||
<AButton type="text" shape="default" size="middle" class="people-album-toolbar-btn"
|
||||
:disabled="selected.length !== 2" v-if="selectedKey === '0'">
|
||||
<template #icon>
|
||||
<BlockOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
合并人物
|
||||
</AButton>
|
||||
|
||||
<AButton type="text" shape="default" size="middle" class="people-album-toolbar-btn" @click="hiddenFace">
|
||||
<template #icon>
|
||||
<EyeInvisibleOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
{{ selectedKey === '0' ? '隐藏人物' : '取消隐藏' }}
|
||||
</AButton>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<PeopleAlbumToolbar :face-list="imageStore.faceList"/>
|
||||
<div class="people-album-container">
|
||||
<ASpin :spinning="loading" size="large" wrapperClassName="spin-container">
|
||||
<div class="people-album-content" v-if="faceList.length !== 0">
|
||||
<Spin :spinning="imageStore.faceListLoading" size="large" indicator="spin-dot">
|
||||
<div class="people-album-content" v-if="imageStore.faceList.length !== 0">
|
||||
<CheckCard
|
||||
v-for="(item, index) in faceList"
|
||||
v-for="(item, index) in imageStore.faceList"
|
||||
:key="index"
|
||||
@click="handleClick(item.id, item.face_name, item.face_image)"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
v-model="imageStore.faceSelected"
|
||||
:showHoverCircle="true"
|
||||
:background-color="'transparent'"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="false"
|
||||
:value="item.id">
|
||||
<div class="people-album-item"
|
||||
:class="{ 'selected-item': selected.includes(item.id) }"
|
||||
:class="{ 'selected-item': imageStore.faceSelected.includes(item.id) }"
|
||||
@mouseover="item.showButton = true"
|
||||
@mouseleave="item.showButton = false">
|
||||
<div class="people-album-item-avatar">
|
||||
@@ -115,7 +82,7 @@
|
||||
</CheckCard>
|
||||
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<div v-if="!imageStore.imageListLoading&& imageStore.faceList.length === 0" class="empty-content">
|
||||
<AEmpty :image="empty" :image-style="{width: '100%', height: '100%'}">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
@@ -124,49 +91,32 @@
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</ASpin>
|
||||
</Spin>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {getFaceSamplesList, modifyFaceSampleName, modifyFaceTypeBatch} from "@/api/storage";
|
||||
import {modifyFaceSampleName} from "@/api/storage";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import useStore from "@/store";
|
||||
import PeopleAlbumToolbar from "@/views/Album/PeopleAlbum/PeopleAlbumToolbar.vue";
|
||||
|
||||
|
||||
const faceList = ref<any[]>([]);
|
||||
const addNameInputValue = ref<string>('');
|
||||
const selectedKey = ref<string>('0');
|
||||
const loading = ref<boolean>(false);
|
||||
const selected = ref<any[]>([]);
|
||||
|
||||
/**
|
||||
* 获取人脸列表
|
||||
*/
|
||||
async function getFaceList(type: number = 0) {
|
||||
loading.value = true;
|
||||
faceList.value = [];
|
||||
const res: any = await getFaceSamplesList(type);
|
||||
if (res && res.code === 200 && res.data.faces) {
|
||||
faceList.value = res.data.faces.map(face => ({
|
||||
...face,
|
||||
showButton: false,
|
||||
showInput: false,
|
||||
}));
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
const imageStore = useStore().image;
|
||||
|
||||
function showAddNameInput(index: number) {
|
||||
if (faceList.value[index]) {
|
||||
faceList.value[index].showInput = true;
|
||||
faceList.value[index].showButton = false;
|
||||
if (imageStore.faceList[index]) {
|
||||
imageStore.faceList[index].showInput = true;
|
||||
imageStore.faceList[index].showButton = false;
|
||||
}
|
||||
}
|
||||
|
||||
function hideAddNameInput(index: number) {
|
||||
if (faceList.value[index]) {
|
||||
faceList.value[index].showInput = false;
|
||||
faceList.value[index].showButton = false;
|
||||
if (imageStore.faceList[index]) {
|
||||
imageStore.faceList[index].showInput = false;
|
||||
imageStore.faceList[index].showButton = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +129,7 @@ async function modifyFaceName(id: number, index: number) {
|
||||
if (!addNameInputValue.value.trim()) return;
|
||||
const res: any = await modifyFaceSampleName(id, addNameInputValue.value);
|
||||
if (res && res.code === 200) {
|
||||
faceList.value[index].face_name = res.data.face_name;
|
||||
imageStore.faceList[index].face_name = res.data.face_name;
|
||||
addNameInputValue.value = '';
|
||||
hideAddNameInput(index);
|
||||
}
|
||||
@@ -190,35 +140,10 @@ async function modifyFaceName(id: number, index: number) {
|
||||
* @param key
|
||||
*/
|
||||
function handleSelect({key}) {
|
||||
selectedKey.value = key;
|
||||
getFaceList(parseInt(key));
|
||||
imageStore.faceSelectedKey = key;
|
||||
imageStore.getFaceList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 全选
|
||||
*/
|
||||
function selectAllPeople() {
|
||||
selected.value = faceList.value.map((item) => item.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消选择
|
||||
*/
|
||||
function cancelSelectPeople() {
|
||||
selected.value = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏人物
|
||||
*/
|
||||
async function hiddenFace() {
|
||||
if (selected.value.length === 0) return;
|
||||
const res: any = await modifyFaceTypeBatch(selected.value, selectedKey.value === '0' ? 1 : 0);
|
||||
if (res && res.code === 200) {
|
||||
await getFaceList();
|
||||
selected.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -229,12 +154,12 @@ const router = useRouter();
|
||||
* @param name
|
||||
* @param thumb
|
||||
*/
|
||||
function handleClick(id: number, name: string | null,thumb: string | null) {
|
||||
function handleClick(id: number, name: string | null, thumb: string | null) {
|
||||
router.push({path: route.path + `/${id}`, query: {name: name, thumb: thumb}});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getFaceList();
|
||||
imageStore.getFaceList();
|
||||
});
|
||||
|
||||
|
||||
@@ -283,57 +208,6 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
|
||||
.people-album-toolbar {
|
||||
position: fixed;
|
||||
width: calc(100% - 220px);
|
||||
height: 70px;
|
||||
top: 70px;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-image: linear-gradient(45deg, #5789ff, #5c7bff 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 3px 10px 0 rgba(0, 0, 0, .06);
|
||||
padding: 0 20px;
|
||||
|
||||
.people-album-toolbar-left {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.people-album-toolbar-right {
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.people-album-toolbar-icon {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.people-album-toolbar-btn {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.people-album-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
127
src/views/Album/PeopleAlbum/PeopleAlbumToolbar.vue
Normal file
127
src/views/Album/PeopleAlbum/PeopleAlbumToolbar.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div class="people-album-toolbar" v-show="imageStore.faceSelected.length !== 0">
|
||||
<div class="people-album-toolbar-left">
|
||||
<AButton type="text" shape="circle" size="large" class="people-album-toolbar-btn" @click="cancelSelectPeople">
|
||||
<template #icon>
|
||||
<CloseOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
</AButton>
|
||||
<span style="font-size: 16px;font-weight: bold">
|
||||
已选择 {{ imageStore.faceSelected.length }} 个人物
|
||||
</span>
|
||||
<AButton type="text" shape="default" class="people-album-toolbar-btn" size="middle" @click="selectAllPeople">
|
||||
全选
|
||||
</AButton>
|
||||
</div>
|
||||
<div class="people-album-toolbar-right">
|
||||
|
||||
<AButton type="text" shape="default" size="middle" class="people-album-toolbar-btn"
|
||||
:disabled="imageStore.faceSelected.length !== 2" v-if="imageStore.faceSelectedKey === '0'">
|
||||
<template #icon>
|
||||
<BlockOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
合并人物
|
||||
</AButton>
|
||||
|
||||
<AButton type="text" shape="default" size="middle" class="people-album-toolbar-btn" @click="hiddenFace">
|
||||
<template #icon>
|
||||
<EyeInvisibleOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
{{ imageStore.faceSelectedKey === '0' ? '隐藏人物' : '取消隐藏' }}
|
||||
</AButton>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import useStore from "@/store";
|
||||
import {modifyFaceTypeBatch} from "@/api/storage";
|
||||
|
||||
const props = defineProps({
|
||||
faceList: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const imageStore = useStore().image;
|
||||
|
||||
/**
|
||||
* 全选
|
||||
*/
|
||||
function selectAllPeople() {
|
||||
imageStore.faceSelected = props.faceList.map((item) => item.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消选择
|
||||
*/
|
||||
function cancelSelectPeople() {
|
||||
imageStore.faceSelected = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏人物
|
||||
*/
|
||||
async function hiddenFace() {
|
||||
if (imageStore.faceSelected.length === 0) return;
|
||||
const res: any = await modifyFaceTypeBatch(imageStore.faceSelected, imageStore.faceSelectedKey === '0' ? 1 : 0);
|
||||
if (res && res.code === 200) {
|
||||
await imageStore.getFaceList();
|
||||
imageStore.faceSelected= [];
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.people-album-toolbar {
|
||||
position: fixed;
|
||||
width: calc(100% - 220px);
|
||||
height: 70px;
|
||||
top: 70px;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-image: linear-gradient(45deg, #5789ff, #5c7bff 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 3px 10px 0 rgba(0, 0, 0, .06);
|
||||
padding: 0 20px;
|
||||
|
||||
.people-album-toolbar-left {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.people-album-toolbar-right {
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.people-album-toolbar-icon {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.people-album-toolbar-btn {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
184
src/views/Album/Phoalbum/AlbumCard.vue
Normal file
184
src/views/Album/Phoalbum/AlbumCard.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<Spin tip="Loading..." :spinning="imageStore.albumListLoading" size="middle">
|
||||
<div class="phoalbum-item-container" v-if="imageStore.albumList">
|
||||
<div class="phoalbum-item"
|
||||
v-for="(album, index) in imageStore.albumList"
|
||||
:key="album.id"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
:default-src="default_cover"/>
|
||||
<div class="phoalbum-item-info">
|
||||
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||
</div>
|
||||
<div class="phoalbum-item-operation"
|
||||
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||
<ADropdown trigger="click" @click.stop>
|
||||
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||
<template #icon>
|
||||
<AAvatar shape="circle" size="small" :src="more"/>
|
||||
</template>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu>
|
||||
<APopover placement="left" trigger="click">
|
||||
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||
<template #content>
|
||||
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||
v-model:value="albumRenameValue">
|
||||
<template #suffix>
|
||||
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||
@click="renameAlbum(album.id, albumRenameValue)">
|
||||
确认
|
||||
</AButton>
|
||||
</template>
|
||||
</AInput>
|
||||
</template>
|
||||
</APopover>
|
||||
<AMenuItem key="2"
|
||||
@click.prevent="imageStore.openAlbumShareDialogHandler(true,album.cover_image ?`data:image/png;base64,`+album.cover_image: ``,album.id)">
|
||||
分享相册
|
||||
</AMenuItem>
|
||||
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||
<AMenuItem key="4">下载相册</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty" :image-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>
|
||||
</Spin>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import default_cover from "@/assets/images/default-cover.png";
|
||||
import more from "@/assets/svgs/more.svg";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import useStore from "@/store";
|
||||
import {message} from "ant-design-vue";
|
||||
import {deleteAlbumApi, renameAlbumApi} from "@/api/storage";
|
||||
|
||||
const imageStore = useStore().image;
|
||||
const isHovered = ref<number | null>(null);
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const albumRenameValue = ref<string>("");
|
||||
|
||||
/**
|
||||
* 点击相册跳转到详情页
|
||||
* @param id
|
||||
* @param albumName
|
||||
*/
|
||||
function handleClick(id: number, albumName: string) {
|
||||
router.push({
|
||||
path: route.path + `/${id}`, query: {name: albumName}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名相册
|
||||
* @param id
|
||||
* @param name
|
||||
*/
|
||||
async function renameAlbum(id: number, name: string) {
|
||||
if (name.trim() === "") {
|
||||
message.warning("相册名称不能为空");
|
||||
return;
|
||||
}
|
||||
const res: any = await renameAlbumApi(id, name);
|
||||
if (res && res.code === 200) {
|
||||
albumRenameValue.value = "";
|
||||
await imageStore.getAlbumList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除相册
|
||||
* @param id
|
||||
*/
|
||||
async function deleteAlbum(id: number) {
|
||||
const res: any = await deleteAlbumApi(id);
|
||||
if (res && res.code === 200) {
|
||||
await imageStore.getAlbumList();
|
||||
} else {
|
||||
message.error("删除相册失败");
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.phoalbum-item-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 40px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
.phoalbum-item {
|
||||
width: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 15px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.phoalbum-item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
|
||||
.phoalbum-item-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.phoalbum-item-date {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.phoalbum-item-operation {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 10;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
opacity: 1; /* 显示时透明度为1 */
|
||||
transform: scale(1); /* 显示时缩放为1 */
|
||||
z-index: 10; /* 显示时z-index为10 */
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
opacity: 0; /* 隐藏时透明度为0 */
|
||||
transform: scale(0); /* 隐藏时缩放为0 */
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -40,7 +40,7 @@
|
||||
</AButton>
|
||||
</div>
|
||||
<ImageUpload/>
|
||||
<ImageToolbar :selected="imageStore.selected" :image-list="albumList"/>
|
||||
<ImageToolbar :selected="imageStore.selected" :image-list="imageList"/>
|
||||
<div class="phoalbum-detail-content">
|
||||
<div class="phoalbum-detail-content-nav">
|
||||
<div class="phoalbum-detail-content-nav-left">
|
||||
@@ -51,7 +51,7 @@
|
||||
<span class="phoalbum-detail-content-nav-name">{{ route.query.name }}</span>
|
||||
</div>
|
||||
<div class="phoalbum-detail-content-nav-right">
|
||||
<span class="phoalbum-detail-content-nav-date">共 {{ imageStore.countTotalImages(albumList) }} 张照片</span>
|
||||
<span class="phoalbum-detail-content-nav-date">共 {{ imageStore.countTotalImages(imageList) }} 张照片</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="phoalbum-detail-content-desc">
|
||||
@@ -59,82 +59,44 @@
|
||||
<span>相册描述</span>
|
||||
</div>
|
||||
<div class="phoalbum-detail-content-list">
|
||||
<div style="width:100%;height:100%;" v-if="albumList && albumList.length !== 0">
|
||||
<div v-for="(itemList, index) in albumList" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
:height="200"
|
||||
:previewMask="false"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy"/>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
暂无照片,快去上传吧
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
|
||||
import {deleteAlbumApi, queryAlbumDetailListApi, renameAlbumApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import ImageToolbar from "@/components/ImageToolbar/ImageToolbar.vue";
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
|
||||
import ImageUpload from "@/components/ImageUpload/ImageUpload.vue";
|
||||
import {message} from "ant-design-vue";
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
|
||||
|
||||
const imageStore = useStore().image;
|
||||
const albumList = ref<any[]>([]);
|
||||
const imageList = ref<any[]>([]);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
|
||||
const upload = useStore().upload;
|
||||
|
||||
|
||||
async function getAlbumList(id: number) {
|
||||
async function getImageList(id: number) {
|
||||
imageStore.imageListLoading = true;
|
||||
const res: any = await queryAlbumDetailListApi(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
albumList.value = res.data.records;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
imageStore.imageListLoading = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const idParam = route.params.id;
|
||||
const albumId = Array.isArray(idParam) ? idParam[0] : idParam;
|
||||
getAlbumList(parseInt(albumId, 10));
|
||||
getImageList(parseInt(albumId, 10));
|
||||
});
|
||||
|
||||
function openUploadModal(): void {
|
||||
@@ -185,6 +147,7 @@ function goBack(): void {
|
||||
router.go(-1);
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.phoalbum-detail {
|
||||
|
@@ -30,7 +30,7 @@
|
||||
排序
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu selectable :selectedKeys="[selectedKey]" @select="handleSelect">
|
||||
<AMenu selectable :selectedKeys="[imageStore.sortSelectedKey]" @select="handleSelect">
|
||||
<AMenuItem :key="true">按时间排序</AMenuItem>
|
||||
<AMenuItem :key="false">按名称排序</AMenuItem>
|
||||
</AMenu>
|
||||
@@ -53,255 +53,21 @@
|
||||
@change="handleTabChange">
|
||||
<template #rightExtra>
|
||||
<span
|
||||
style="color: #999; font-size: 12px;">已全部加载,共 {{ albumList ? albumList.length : 0 }} 个相册</span>
|
||||
style="color: #999; font-size: 12px;">已全部加载,共 {{
|
||||
imageStore.albumList ? imageStore.albumList.length : 0
|
||||
}} 个相册</span>
|
||||
</template>
|
||||
<ATabPane key="-1" :tab="imageStore.tabMap[-1]">
|
||||
<ASpin tip="Loading..." :spinning="loading" size="large" wrapperClassName="spin-container">
|
||||
<div class="phoalbum-item-container" v-if="albumList && albumList.length != 0">
|
||||
<div class="phoalbum-item"
|
||||
v-for="(album, index) in albumList"
|
||||
:key="album.id"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
:default-src="default_cover"/>
|
||||
<div class="phoalbum-item-info">
|
||||
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||
</div>
|
||||
<div class="phoalbum-item-operation"
|
||||
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||
<ADropdown trigger="click" @click.stop>
|
||||
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||
<template #icon>
|
||||
<AAvatar shape="circle" size="small" :src="more"/>
|
||||
</template>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu>
|
||||
<APopover placement="left" trigger="click">
|
||||
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||
<template #content>
|
||||
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||
v-model:value="albumRenameValue">
|
||||
<template #suffix>
|
||||
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||
@click="renameAlbum(album.id, albumRenameValue)">
|
||||
确认
|
||||
</AButton>
|
||||
</template>
|
||||
</AInput>
|
||||
</template>
|
||||
</APopover>
|
||||
<AMenuItem key="2"
|
||||
@click.prevent="imageStore.openAlbumShareDialogHandler(true,album.cover_image ?`data:image/png;base64,`+album.cover_image: ``,album.id)">
|
||||
分享相册
|
||||
</AMenuItem>
|
||||
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||
<AMenuItem key="4">下载相册</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty" :image-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>
|
||||
</ASpin>
|
||||
<AlbumCard/>
|
||||
</ATabPane>
|
||||
<ATabPane key="0" :tab="imageStore.tabMap[0]">
|
||||
<ASpin tip="Loading..." :spinning="loading" size="large" wrapperClassName="spin-container">
|
||||
<div class="phoalbum-item-container" v-if="albumList && albumList.length != 0">
|
||||
<div class="phoalbum-item"
|
||||
v-for="(album, index) in albumList"
|
||||
:key="album.id"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
:default-src="default_cover"/>
|
||||
<div class="phoalbum-item-info">
|
||||
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||
</div>
|
||||
<div class="phoalbum-item-operation"
|
||||
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||
<ADropdown trigger="click" @click.stop>
|
||||
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||
<template #icon>
|
||||
<AAvatar shape="circle" size="small" :src="more"/>
|
||||
</template>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu>
|
||||
<APopover placement="left" trigger="click">
|
||||
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||
<template #content>
|
||||
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||
v-model:value="albumRenameValue">
|
||||
<template #suffix>
|
||||
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||
@click="renameAlbum(album.id, albumRenameValue)">
|
||||
确认
|
||||
</AButton>
|
||||
</template>
|
||||
</AInput>
|
||||
</template>
|
||||
</APopover>
|
||||
<AMenuItem key="2"
|
||||
@click.prevent="imageStore.openAlbumShareDialogHandler(true,album.cover_image ?`data:image/png;base64,`+album.cover_image: ``,album.id)">
|
||||
分享相册
|
||||
</AMenuItem>
|
||||
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||
<AMenuItem key="4">下载相册</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty" :image-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>
|
||||
</ASpin>
|
||||
<AlbumCard/>
|
||||
</ATabPane>
|
||||
<ATabPane key="1" :tab="imageStore.tabMap[1]">
|
||||
<ASpin tip="Loading..." :spinning="loading" size="large" wrapperClassName="spin-container">
|
||||
<div class="phoalbum-item-container" v-if="albumList && albumList.length != 0">
|
||||
<div class="phoalbum-item"
|
||||
v-for="(album, index) in albumList"
|
||||
:key="album.id"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
:default-src="default_cover"/>
|
||||
<div class="phoalbum-item-info">
|
||||
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||
</div>
|
||||
<div class="phoalbum-item-operation"
|
||||
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||
<ADropdown trigger="click" @click.stop>
|
||||
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||
<template #icon>
|
||||
<AAvatar shape="circle" size="small" :src="more"/>
|
||||
</template>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu>
|
||||
<APopover placement="left" trigger="click">
|
||||
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||
<template #content>
|
||||
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||
v-model:value="albumRenameValue">
|
||||
<template #suffix>
|
||||
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||
@click="renameAlbum(album.id, albumRenameValue)">
|
||||
确认
|
||||
</AButton>
|
||||
</template>
|
||||
</AInput>
|
||||
</template>
|
||||
</APopover>
|
||||
<AMenuItem key="2"
|
||||
@click.prevent="imageStore.openAlbumShareDialogHandler(true,album.cover_image ?`data:image/png;base64,`+album.cover_image: ``,album.id)">
|
||||
分享相册
|
||||
</AMenuItem>
|
||||
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||
<AMenuItem key="4">下载相册</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty" :image-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>
|
||||
</ASpin>
|
||||
<AlbumCard/>
|
||||
</ATabPane>
|
||||
<ATabPane key="2" :tab="imageStore.tabMap[2]">
|
||||
<ASpin tip="Loading..." :spinning="loading" size="large" wrapperClassName="spin-container">
|
||||
<div class="phoalbum-item-container" v-if="albumList && albumList.length != 0">
|
||||
<div class="phoalbum-item"
|
||||
v-for="(album, index) in albumList"
|
||||
:key="album.id"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
:default-src="default_cover"/>
|
||||
<div class="phoalbum-item-info">
|
||||
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||
</div>
|
||||
<div class="phoalbum-item-operation"
|
||||
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||
<ADropdown trigger="click" @click.stop>
|
||||
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||
<template #icon>
|
||||
<AAvatar shape="circle" size="small" :src="more"/>
|
||||
</template>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu>
|
||||
<APopover placement="left" trigger="click">
|
||||
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||
<template #content>
|
||||
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||
v-model:value="albumRenameValue">
|
||||
<template #suffix>
|
||||
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||
@click="renameAlbum(album.id, albumRenameValue)">
|
||||
确认
|
||||
</AButton>
|
||||
</template>
|
||||
</AInput>
|
||||
</template>
|
||||
</APopover>
|
||||
<AMenuItem key="2"
|
||||
@click.prevent="imageStore.openAlbumShareDialogHandler(true,album.cover_image ?`data:image/png;base64,`+album.cover_image: ``,album.id)">
|
||||
分享相册
|
||||
</AMenuItem>
|
||||
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||
<AMenuItem key="4">下载相册</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty" :image-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>
|
||||
</ASpin>
|
||||
<AlbumCard/>
|
||||
</ATabPane>
|
||||
</ATabs>
|
||||
</div>
|
||||
@@ -309,27 +75,21 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import more from "@/assets/svgs/more.svg";
|
||||
import {albumListApi, createAlbumApi, deleteAlbumApi, renameAlbumApi} from "@/api/storage";
|
||||
import {createAlbumApi} from "@/api/storage";
|
||||
import {message} from "ant-design-vue";
|
||||
import default_cover from "@/assets/images/default-cover.png";
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import AlbumShareModal from "@/views/Album/Phoalbum/AlbumShareModal.vue";
|
||||
|
||||
const isHovered = ref<number | null>(null);
|
||||
import useStore from "@/store";
|
||||
|
||||
import AlbumShareModal from "@/views/Album/Phoalbum/AlbumShareModal.vue";
|
||||
import AlbumCard from "@/views/Album/Phoalbum/AlbumCard.vue";
|
||||
|
||||
|
||||
const albumNameValue = ref<string>("未命名相册");
|
||||
const albumRenameValue = ref<string>("");
|
||||
|
||||
const selectedKey = ref<boolean>(true);
|
||||
|
||||
const albumList = ref<any[]>([]);
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
const imageStore = useStore().image;
|
||||
|
||||
|
||||
/**
|
||||
* 创建相册
|
||||
*/
|
||||
@@ -341,7 +101,7 @@ async function createAlbumSubmit() {
|
||||
const res: any = await createAlbumApi(albumNameValue.value);
|
||||
if (res && res.code === 200) {
|
||||
albumNameValue.value = "未命名相册";
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
await imageStore.getAlbumList();
|
||||
} else {
|
||||
message.error("创建相册失败");
|
||||
}
|
||||
@@ -352,8 +112,8 @@ async function createAlbumSubmit() {
|
||||
* @param key
|
||||
*/
|
||||
async function handleSelect({key}) {
|
||||
selectedKey.value = key;
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), key);
|
||||
imageStore.sortSelectedKey = key;
|
||||
await imageStore.getAlbumList();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,70 +122,12 @@ async function handleSelect({key}) {
|
||||
*/
|
||||
async function handleTabChange(activeKey: string) {
|
||||
imageStore.tabActiveKey = activeKey;
|
||||
await getAlbumList(parseInt(activeKey), selectedKey.value);
|
||||
await imageStore.getAlbumList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相册列表
|
||||
* @param type
|
||||
* @param sort
|
||||
*/
|
||||
async function getAlbumList(type: number = 0, sort: boolean = true) {
|
||||
albumList.value = [];
|
||||
loading.value = true;
|
||||
const res: any = await albumListApi(type, sort);
|
||||
if (res && res.code === 200) {
|
||||
albumList.value = res.data.albums;
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名相册
|
||||
* @param id
|
||||
* @param name
|
||||
*/
|
||||
async function renameAlbum(id: number, name: string) {
|
||||
if (name.trim() === "") {
|
||||
message.warning("相册名称不能为空");
|
||||
return;
|
||||
}
|
||||
const res: any = await renameAlbumApi(id, name);
|
||||
if (res && res.code === 200) {
|
||||
albumRenameValue.value = "";
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除相册
|
||||
* @param id
|
||||
*/
|
||||
async function deleteAlbum(id: number) {
|
||||
const res: any = await deleteAlbumApi(id);
|
||||
if (res && res.code === 200) {
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
} else {
|
||||
message.error("删除相册失败");
|
||||
}
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
/**
|
||||
* 点击相册跳转到详情页
|
||||
* @param id
|
||||
* @param albumName
|
||||
*/
|
||||
function handleClick(id: number, albumName: string) {
|
||||
router.push({
|
||||
path: route.path + `/${id}`, query: {name: albumName}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
imageStore.getAlbumList();
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -462,79 +164,6 @@ onMounted(() => {
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: calc(100% - 65px);
|
||||
|
||||
.spin-container {
|
||||
position: relative;
|
||||
transition: opacity 0.3s;
|
||||
width: 100%;
|
||||
height: 70vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.phoalbum-item-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 40px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
.phoalbum-item {
|
||||
width: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 15px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.phoalbum-item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
|
||||
.phoalbum-item-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.phoalbum-item-date {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.phoalbum-item-operation {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 10;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
opacity: 1; /* 显示时透明度为1 */
|
||||
transform: scale(1); /* 显示时缩放为1 */
|
||||
z-index: 10; /* 显示时z-index为10 */
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
opacity: 0; /* 隐藏时透明度为0 */
|
||||
transform: scale(0); /* 隐藏时缩放为0 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,69 +9,37 @@
|
||||
<span class="thing-detail-content-nav-name">{{ getZhLabelNameByEnName(route.query.tag as string) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ImageToolbar :selected="imageStore.selected" :image-list="albumList"/>
|
||||
<ImageToolbar :selected="imageStore.selected" :image-list="imageList"/>
|
||||
<div class="thing-album-detail-info">
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(albumList) }}张照片</span>
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(imageList) }}张照片</span>
|
||||
</div>
|
||||
<div class="thing-album-detail-list">
|
||||
<div style="width:100%;height:100%;">
|
||||
<div v-for="(itemList, index) in albumList" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
:height="200"
|
||||
:previewMask="false"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy"/>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import {queryThingDetailListApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import ImageToolbar from "@/components/ImageToolbar/ImageToolbar.vue";
|
||||
import useStore from "@/store";
|
||||
import {getZhCategoryNameByEnName, getZhLabelNameByEnName} from "@/constant/coco_ssd_label_category.ts";
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
|
||||
|
||||
const imageStore = useStore().image;
|
||||
const albumList = ref<any[]>([]);
|
||||
const imageList = ref<any[]>([]);
|
||||
const upload = useStore().upload;
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
|
||||
async function getImageList(tag_name: string) {
|
||||
imageStore.imageListLoading = true;
|
||||
const res: any = await queryThingDetailListApi(tag_name, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
albumList.value = res.data.records;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
imageStore.imageListLoading = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@@ -5,8 +5,8 @@
|
||||
<AButton type="link" size="large" class="thing-album-button">事物</AButton>
|
||||
|
||||
</div>
|
||||
<div class="thing-album-content" v-if="thingAlbumList && thingAlbumList.length>0">
|
||||
<div class="thing-album-content-item" v-for="(item, index) in thingAlbumList" :key="index">
|
||||
<div class="thing-album-content" v-if="albumList && albumList.length>0">
|
||||
<div class="thing-album-content-item" v-for="(item, index) in albumList" :key="index">
|
||||
<span class="thing-album-title">{{ getZhCategoryNameByEnName(item.category) }}</span>
|
||||
<div class="thing-album-wrapper">
|
||||
<div class="thing-album-container" v-for="(tags, indexList) in item.list" :key="indexList"
|
||||
@@ -40,12 +40,13 @@ import {getZhCategoryNameByEnName, getZhLabelNameByEnName} from "@/constant/coco
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
const thingAlbumList = ref<any[]>([]);
|
||||
|
||||
async function getThingAlbumList(provider: string, bucket: string) {
|
||||
const albumList = ref<any[]>([]);
|
||||
|
||||
async function getAlbumList(provider: string, bucket: string) {
|
||||
const res: any = await queryThingAlbumApi(provider, bucket);
|
||||
if (res && res.code === 200) {
|
||||
thingAlbumList.value = res.data.records;
|
||||
albumList.value = res.data.records;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +65,7 @@ function handleClick(id: string, category: string, tag: string) {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getThingAlbumList(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
getAlbumList(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@@ -62,9 +62,9 @@ import warn from '@/assets/svgs/warn.svg';
|
||||
import remove from '@/assets/svgs/remove.svg';
|
||||
import empty from '@/assets/svgs/empty.svg';
|
||||
import {blobToBase64} from "@/utils/imageUtils/blobToBase64.ts";
|
||||
import {uploadImage} from "src/api/phone";
|
||||
import {message} from "ant-design-vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {uploadImage} from "@/api/phone";
|
||||
|
||||
const upscale = useStore().upscale;
|
||||
|
||||
|
@@ -29,7 +29,7 @@
|
||||
</template>
|
||||
</APopover>
|
||||
</div>
|
||||
<image-toolbar :selected="imageStore.selected" :image-list="images"/>
|
||||
<image-toolbar :selected="imageStore.selected" :image-list="imageList"/>
|
||||
<div class="photo-list">
|
||||
<ATabs size="small" :tabBarGutter="50" type="line" tabPosition="top" :tabBarStyle="{position:'unset'}"
|
||||
@change="handleTabChange"
|
||||
@@ -41,214 +41,20 @@
|
||||
<ATooltip title="开启后即可隐藏已添加到相册的照片" placement="bottom">
|
||||
<QuestionCircleOutlined/>
|
||||
</ATooltip>
|
||||
<ASwitch size="default" v-model:checked="imageStore.switchValue"/>
|
||||
<ASwitch size="default" v-model:checked="imageStore.switchValue" @change="handleSwitchChange"/>
|
||||
</AFlex>
|
||||
</template>
|
||||
<ATabPane key="all" :tab="imageStore.homeTabMap['all']">
|
||||
<ASpin size="large" :spinning="loading" :delay="500" wrapper-class-name="spin-wrapper">
|
||||
<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>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</ATabPane>
|
||||
<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>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</ATabPane>
|
||||
<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>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</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>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</ATabPane>
|
||||
</ATabs>
|
||||
</div>
|
||||
@@ -257,40 +63,34 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
import ImageUpload from "@/components/ImageUpload/ImageUpload.vue";
|
||||
import useStore from "@/store";
|
||||
import {createAlbumApi, queryAllImagesApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
import {message} from "ant-design-vue";
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
|
||||
const imageStore = useStore().image;
|
||||
|
||||
|
||||
const upload = useStore().upload;
|
||||
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
|
||||
const images = ref<any[]>([]);
|
||||
const loading = ref<boolean>(false);
|
||||
const imageList = ref<any[]>([]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
/**
|
||||
* 获取所有图片
|
||||
*/
|
||||
async function getAllImages(type: string) {
|
||||
images.value = [];
|
||||
loading.value = true;
|
||||
imageList.value = [];
|
||||
imageStore.imageListLoading = true;
|
||||
const res: any = await queryAllImagesApi(type, imageStore.switchValue, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
images.value = res.data.records;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
loading.value = false;
|
||||
imageStore.imageListLoading = false;
|
||||
}
|
||||
|
||||
async function handleTabChange(activeKey: string) {
|
||||
@@ -319,6 +119,10 @@ async function createAlbumSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSwitchChange(_checked: boolean) {
|
||||
getAllImages(imageStore.homeTabActiveKey);
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getAllImages(imageStore.homeTabActiveKey);
|
||||
@@ -362,7 +166,4 @@ onMounted(() => {
|
||||
box-shadow: 0 0 10px 0 rgba(77, 167, 255, 0.89);
|
||||
}
|
||||
|
||||
.spin-wrapper {
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -29,52 +29,10 @@
|
||||
</template>
|
||||
</APopover>
|
||||
</div>
|
||||
<image-toolbar :selected="imageStore.selected" :image-list="images"/>
|
||||
<image-toolbar :selected="imageStore.selected" :image-list="imageList"/>
|
||||
<div class="photo-list">
|
||||
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
<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"
|
||||
style="height: 200px"
|
||||
: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 class="photo-list-container">
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</div>
|
||||
</div>
|
||||
<ImageUpload/>
|
||||
@@ -82,28 +40,27 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import useStore from "@/store";
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
import ImageUpload from "@/components/ImageUpload/ImageUpload.vue";
|
||||
import {createAlbumApi, queryRecentImagesApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import ImageToolbar from "@/components/ImageToolbar/ImageToolbar.vue";
|
||||
|
||||
import {message} from "ant-design-vue";
|
||||
import router from "@/router/router.ts";
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
|
||||
const imageStore = useStore().image;
|
||||
const upload = useStore().upload;
|
||||
const images = ref<any[]>([]);
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
const imageList = ref<any[]>([]);
|
||||
|
||||
|
||||
const getRecentImages = async () => {
|
||||
imageStore.imageListLoading = true;
|
||||
const res: any = await queryRecentImagesApi(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
images.value = res.data.records;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
imageStore.imageListLoading = false;
|
||||
};
|
||||
|
||||
const albumNameValue = ref<string>("未命名相册");
|
||||
@@ -154,12 +111,21 @@ onMounted(() => {
|
||||
|
||||
.photo-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: calc(100% - 65px);
|
||||
margin-top: 10px;
|
||||
.photo-list-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -5,74 +5,28 @@
|
||||
<span class="recycling-bin-desc">保存最近10天从云端删除的内容</span>
|
||||
</div>
|
||||
<div class="photo-list">
|
||||
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
style="height: 200px"
|
||||
: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="{ width: '100%', height: '100%' }">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
暂无照片,快去上传吧
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import {getDeletedRecordApi} from "@/api/storage";
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const images = ref<any[]>([]);
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
const imageList = ref<any[]>([]);
|
||||
const upload = useStore().upload;
|
||||
|
||||
const imageStore = useStore().image;
|
||||
/**
|
||||
* 查询回收站
|
||||
*/
|
||||
async function queryRecyclingBin() {
|
||||
imageStore.imageListLoading = true;
|
||||
const res: any = await getDeletedRecordApi(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
images.value = res.data.records;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
imageStore.imageListLoading = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@@ -10,42 +10,11 @@
|
||||
<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 && images.length <= 0">
|
||||
<div class="share-content-verify" v-if="imageList && imageList.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 && 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>
|
||||
|
||||
<ImageWaterfallList :image-list="imageList"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,31 +23,30 @@
|
||||
|
||||
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 // 高度
|
||||
});
|
||||
import {queryShareImageApi} from "@/api/share";
|
||||
import ImageWaterfallList from "@/components/ImageWaterfallList/ImageWaterfallList.vue";
|
||||
import useStore from "@/store";
|
||||
|
||||
const imageList = ref<any[]>([]);
|
||||
|
||||
const route = useRoute();
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
const imageStore = useStore().image;
|
||||
|
||||
/**
|
||||
* 获取分享图片列表
|
||||
* @param e
|
||||
*/
|
||||
async function getShareImages(e) {
|
||||
loading.value = true;
|
||||
imageStore.imageListLoading = 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;
|
||||
imageList.value = res.data.records;
|
||||
}
|
||||
loading.value = false;
|
||||
imageStore.imageListLoading = false;
|
||||
}
|
||||
|
||||
|
||||
|
11
src/views/User/AccountSetting/AccountSetting.vue
Normal file
11
src/views/User/AccountSetting/AccountSetting.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
179
src/views/User/PersonalCenter/PersonalCenter.vue
Normal file
179
src/views/User/PersonalCenter/PersonalCenter.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="personal-center">
|
||||
<div class="personal-center-header">
|
||||
<Header style="background: transparent;box-shadow: none;backdrop-filter: none;"/>
|
||||
<div class="personal-center-header-info">
|
||||
<div class="personal-center-header-info-container">
|
||||
<div class="personal-center-header-info-container-avatar">
|
||||
<AAvatar :size="80" :text-size="1.5" :round="true" :src="userStore.user.avatar"/>
|
||||
</div>
|
||||
<div class="personal-center-header-info-container-description">
|
||||
<div class="personal-center-header-info-container-description-name">
|
||||
<span>{{ userStore.user.nickname }}</span>
|
||||
<img src="/level_icon/icon/lv1.png" class="personal-level" alt="level">
|
||||
</div>
|
||||
<div class="personal-center-header-info-container-description-introduce">
|
||||
<span>描述信息</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-center-content">
|
||||
<AMenu :selectedKeys="[menuStore.userCenterMenu]" mode="horizontal" :selectable="true" :multiple="false"
|
||||
@select="handleClick">
|
||||
<AMenuItem key="home" :style="menuCSSStyle" title="主页">
|
||||
<template #icon>
|
||||
<AAvatar shape="square" size="small" :src="home"/>
|
||||
</template>
|
||||
<span class="ant-menu-item-title">主页</span>
|
||||
</AMenuItem>
|
||||
<AMenuItem key="dynamic" :style="menuCSSStyle" title="动态">
|
||||
<template #icon>
|
||||
<AAvatar shape="square" size="small" :src="dynamic"/>
|
||||
</template>
|
||||
<span class="ant-menu-item-title">动态</span>
|
||||
</AMenuItem>
|
||||
<AMenuItem key="info" :style="menuCSSStyle" title="个人信息">
|
||||
<template #icon>
|
||||
<AAvatar shape="square" size="small" :src="personal_info"/>
|
||||
</template>
|
||||
<span class="ant-menu-item-title">个人信息</span>
|
||||
</AMenuItem>
|
||||
<AMenuItem key="analysis" :style="menuCSSStyle" title="数据分析">
|
||||
<template #icon>
|
||||
<AAvatar shape="square" size="small" :src="data_analysis"/>
|
||||
</template>
|
||||
<span class="ant-menu-item-title">数据分析</span>
|
||||
</AMenuItem>
|
||||
<AMenuItem key="share" :style="menuCSSStyle" title="我的分享">
|
||||
<template #icon>
|
||||
<AAvatar shape="square" size="small" :src="share"/>
|
||||
</template>
|
||||
<span class="ant-menu-item-title">我的分享</span>
|
||||
</AMenuItem>
|
||||
<AMenuItem key="setting" :style="menuCSSStyle" title="设置">
|
||||
<template #icon>
|
||||
<AAvatar shape="square" size="small" :src="setting"/>
|
||||
</template>
|
||||
<span class="ant-menu-item-title">设置</span>
|
||||
</AMenuItem>
|
||||
</AMenu>
|
||||
<div class="personal-center-content-container">
|
||||
<router-view/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Header from "@/layout/default/Header/Header.vue";
|
||||
import useStore from "@/store";
|
||||
import home from "@/assets/svgs/home.svg";
|
||||
import data_analysis from "@/assets/svgs/data_analysis.svg";
|
||||
import dynamic from "@/assets/svgs/dynamic.svg";
|
||||
import share from "@/assets/svgs/share.svg";
|
||||
import setting from "@/assets/svgs/setting.svg";
|
||||
import personal_info from "@/assets/svgs/personal-center.svg";
|
||||
|
||||
|
||||
const userStore = useStore().user;
|
||||
const menuStore = useStore().menu;
|
||||
const router = useRouter();
|
||||
const menuCSSStyle: any = reactive({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
function handleClick({key}) {
|
||||
menuStore.userCenterMenu = key;
|
||||
router.push(`/main/user/center/${key}`);
|
||||
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.personal-center {
|
||||
background-color: #eaeef6;
|
||||
|
||||
.personal-center-header {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.2)), url("@/assets/images/bg.webp");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
|
||||
.personal-center-header-info {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.personal-center-header-info-container {
|
||||
width: 95%;
|
||||
height: 110px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-start;
|
||||
gap: 20px;
|
||||
|
||||
.personal-center-header-info-container-avatar {
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.personal-center-header-info-container-description {
|
||||
height: 70px;
|
||||
width: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
.personal-center-header-info-container-description-name {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-shadow: 0px 1px 2px rgba(0, 0, 0, .4);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
|
||||
.personal-level {
|
||||
width: auto;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.personal-center-header-info-container-description-introduce {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
background-color: rgba(255, 255, 255, .2);
|
||||
box-shadow: 0 0 0 1px rgba(255, 255, 255, .5)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.personal-center-content {
|
||||
|
||||
.ant-menu-item-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.personal-center-content-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 250px);
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
6666
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
Reference in New Issue
Block a user