♻️ refactor some code
This commit is contained in:
@@ -1,3 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
|
||||||
*.log
|
*.log
|
||||||
|
12
Dockerfile
12
Dockerfile
@@ -1,12 +1,12 @@
|
|||||||
# 设置基础镜像
|
|
||||||
FROM nginx:latest
|
FROM nginx:latest
|
||||||
# 设置作者信息
|
|
||||||
LABEL maintainer="landaiqing <<landaiqing@126.com>>"
|
LABEL maintainer="landaiqing <<landaiqing@126.com>>"
|
||||||
# 设置时区
|
|
||||||
ENV TimeZone=Asia/Shanghai
|
ENV TimeZone=Asia/Shanghai
|
||||||
# 将dist文件中的内容复制到 /usr/share/nginx/html/ 这个目录下面
|
|
||||||
COPY dist/ /usr/share/nginx/html/
|
COPY dist/ /usr/share/nginx/html/
|
||||||
# 用本地的 default.conf 配置来替换nginx镜像里的默认配置
|
|
||||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||||
# 暴露80端口
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
2
auto-import.d.ts
vendored
2
auto-import.d.ts
vendored
@@ -150,6 +150,7 @@ declare global {
|
|||||||
const useCloned: typeof import('@vueuse/core')['useCloned']
|
const useCloned: typeof import('@vueuse/core')['useCloned']
|
||||||
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
||||||
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
||||||
|
const useCountdown: typeof import('@vueuse/core')['useCountdown']
|
||||||
const useCounter: typeof import('@vueuse/core')['useCounter']
|
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||||
const useCssModule: typeof import('vue')['useCssModule']
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
const useCssVar: typeof import('@vueuse/core')['useCssVar']
|
const useCssVar: typeof import('@vueuse/core')['useCssVar']
|
||||||
@@ -460,6 +461,7 @@ declare module 'vue' {
|
|||||||
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
|
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
|
||||||
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
|
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
|
||||||
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
|
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
|
||||||
|
readonly useCountdown: UnwrapRef<typeof import('@vueuse/core')['useCountdown']>
|
||||||
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
|
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
|
||||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||||
readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']>
|
readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']>
|
||||||
|
@@ -1,17 +0,0 @@
|
|||||||
FROM node:22.12.0 as build
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
FROM nginx:alpine
|
|
||||||
|
|
||||||
COPY --from=build /app/dist /usr/share/nginx/html
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -58,6 +58,7 @@ declare module 'vue' {
|
|||||||
BackgroundAnimation: typeof import('./src/components/BackgroundAnimation/BackgroundAnimation.vue')['default']
|
BackgroundAnimation: typeof import('./src/components/BackgroundAnimation/BackgroundAnimation.vue')['default']
|
||||||
BackTop: typeof import('./src/components/MyUI/BackTop/BackTop.vue')['default']
|
BackTop: typeof import('./src/components/MyUI/BackTop/BackTop.vue')['default']
|
||||||
Badge: typeof import('./src/components/MyUI/Badge/Badge.vue')['default']
|
Badge: typeof import('./src/components/MyUI/Badge/Badge.vue')['default']
|
||||||
|
BlockOutlined: typeof import('@ant-design/icons-vue')['BlockOutlined']
|
||||||
BoxDog: typeof import('./src/components/BoxDog/BoxDog.vue')['default']
|
BoxDog: typeof import('./src/components/BoxDog/BoxDog.vue')['default']
|
||||||
Breadcrumb: typeof import('./src/components/MyUI/Breadcrumb/Breadcrumb.vue')['default']
|
Breadcrumb: typeof import('./src/components/MyUI/Breadcrumb/Breadcrumb.vue')['default']
|
||||||
Button: typeof import('./src/components/MyUI/Button/Button.vue')['default']
|
Button: typeof import('./src/components/MyUI/Button/Button.vue')['default']
|
||||||
@@ -88,6 +89,7 @@ declare module 'vue' {
|
|||||||
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
||||||
Ellipsis: typeof import('./src/components/MyUI/Ellipsis/Ellipsis.vue')['default']
|
Ellipsis: typeof import('./src/components/MyUI/Ellipsis/Ellipsis.vue')['default']
|
||||||
Empty: typeof import('./src/components/MyUI/Empty/Empty.vue')['default']
|
Empty: typeof import('./src/components/MyUI/Empty/Empty.vue')['default']
|
||||||
|
EyeInvisibleOutlined: typeof import('@ant-design/icons-vue')['EyeInvisibleOutlined']
|
||||||
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
|
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
|
||||||
FileImageOutlined: typeof import('@ant-design/icons-vue')['FileImageOutlined']
|
FileImageOutlined: typeof import('@ant-design/icons-vue')['FileImageOutlined']
|
||||||
Flex: typeof import('./src/components/MyUI/Flex/Flex.vue')['default']
|
Flex: typeof import('./src/components/MyUI/Flex/Flex.vue')['default']
|
||||||
|
15
default.conf
15
default.conf
@@ -25,6 +25,21 @@ server {
|
|||||||
add_header Cache-Control "public";
|
add_header Cache-Control "public";
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
}
|
}
|
||||||
|
location /sys/ {
|
||||||
|
rewrite ^/sys/(.*)$ /$1 break;
|
||||||
|
proxy_pass http://127.0.0.1:80;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 3600s; # 设置为1小时
|
||||||
|
proxy_send_timeout 3600s; # 设置为1小时
|
||||||
|
send_timeout 3600s; # 设置为1小时
|
||||||
|
keepalive_timeout 3600s; # 设置为1小时
|
||||||
|
}
|
||||||
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
error_page 500 502 503 504 /50x.html;
|
||||||
location = /50x.html {
|
location = /50x.html {
|
||||||
|
18
nginx.conf
Normal file
18
nginx.conf
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
worker_processes 1;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html index.htm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
package.json
26
package.json
@@ -14,6 +14,7 @@
|
|||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@intlify/eslint-plugin-vue-i18n": "^3.2.0",
|
"@intlify/eslint-plugin-vue-i18n": "^3.2.0",
|
||||||
"@mediapipe/face_detection": "^0.4.1646425229",
|
"@mediapipe/face_detection": "^0.4.1646425229",
|
||||||
|
"@mediapipe/face_mesh": "^0.4.1633559619",
|
||||||
"@tensorflow-models/coco-ssd": "^2.2.3",
|
"@tensorflow-models/coco-ssd": "^2.2.3",
|
||||||
"@tensorflow-models/face-detection": "^1.0.3",
|
"@tensorflow-models/face-detection": "^1.0.3",
|
||||||
"@tensorflow-models/face-landmarks-detection": "^1.0.6",
|
"@tensorflow-models/face-landmarks-detection": "^1.0.6",
|
||||||
@@ -26,17 +27,15 @@
|
|||||||
"@tensorflow/tfjs-backend-webgpu": "^4.22.0",
|
"@tensorflow/tfjs-backend-webgpu": "^4.22.0",
|
||||||
"@tensorflow/tfjs-converter": "^4.22.0",
|
"@tensorflow/tfjs-converter": "^4.22.0",
|
||||||
"@tensorflow/tfjs-core": "^4.22.0",
|
"@tensorflow/tfjs-core": "^4.22.0",
|
||||||
"@tensorflow/tfjs-node": "^4.22.0",
|
|
||||||
"@tensorflow/tfjs-node-gpu": "^4.22.0",
|
|
||||||
"@types/animejs": "^3.1.12",
|
"@types/animejs": "^3.1.12",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/json-stringify-safe": "^5.0.3",
|
"@types/json-stringify-safe": "^5.0.3",
|
||||||
"@types/node": "^22.10.7",
|
"@types/node": "^22.13.1",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@vladmandic/face-api": "^1.7.14",
|
"@vladmandic/face-api": "^1.7.14",
|
||||||
"@vuepic/vue-datepicker": "^11.0.1",
|
"@vuepic/vue-datepicker": "^11.0.1",
|
||||||
"@vueuse/core": "^12.4.0",
|
"@vueuse/core": "^12.5.0",
|
||||||
"@vueuse/integrations": "^12.4.0",
|
"@vueuse/integrations": "^12.5.0",
|
||||||
"alova": "^3.2.8",
|
"alova": "^3.2.8",
|
||||||
"animejs": "^3.2.2",
|
"animejs": "^3.2.2",
|
||||||
"ant-design-vue": "^4.2.6",
|
"ant-design-vue": "^4.2.6",
|
||||||
@@ -45,7 +44,7 @@
|
|||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"eslint": "9.18.0",
|
"eslint": "9.19.0",
|
||||||
"exifr": "^7.1.3",
|
"exifr": "^7.1.3",
|
||||||
"go-captcha-vue": "^2.0.5",
|
"go-captcha-vue": "^2.0.5",
|
||||||
"gsap": "^3.12.7",
|
"gsap": "^3.12.7",
|
||||||
@@ -58,36 +57,37 @@
|
|||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
"pinia-plugin-persistedstate-2": "^2.0.28",
|
"pinia-plugin-persistedstate-2": "^2.0.28",
|
||||||
"qrcode": "^1",
|
"qrcode": "^1",
|
||||||
|
"rimraf": "^6.0.1",
|
||||||
"seedrandom": "^3.0.5",
|
"seedrandom": "^3.0.5",
|
||||||
"swiper": "^11.2.1",
|
"swiper": "^11.2.2",
|
||||||
"unplugin-auto-import": "^19.0.0",
|
"unplugin-auto-import": "^19.0.0",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-html": "^3.2.2",
|
"vite-plugin-html": "^3.2.2",
|
||||||
"vite-plugin-node-polyfills": "^0.23.0",
|
"vite-plugin-node-polyfills": "^0.23.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-dompurify-html": "^5.2.0",
|
"vue-dompurify-html": "^5.2.0",
|
||||||
"vue-i18n": "^11.0.1",
|
"vue-i18n": "^11.1.0",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"vue-waterfall-plugin-next": "^2.6.5",
|
"vue-waterfall-plugin-next": "^2.6.5",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.18.0",
|
"@eslint/js": "^9.19.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"eslint-plugin-vue": "^9.32.0",
|
"eslint-plugin-vue": "^9.32.0",
|
||||||
"globals": "^15.14.0",
|
"globals": "^15.14.0",
|
||||||
"sass": "^1.83.4",
|
"sass": "^1.83.4",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.20.0",
|
"typescript-eslint": "^8.23.0",
|
||||||
"unplugin-vue-components": "^28.0.0",
|
"unplugin-vue-components": "^28.0.0",
|
||||||
"vite": "^6.0.9",
|
"vite": "^6.0.11",
|
||||||
"vite-plugin-bundle-obfuscator": "1.4.0",
|
"vite-plugin-bundle-obfuscator": "1.4.1",
|
||||||
"vite-plugin-chunk-split": "^0.5.0",
|
"vite-plugin-chunk-split": "^0.5.0",
|
||||||
"vue-tsc": "2.2.0"
|
"vue-tsc": "2.2.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"vite-plugin-chunk-split": {
|
"vite-plugin-chunk-split": {
|
||||||
"vite": "^6.0.9"
|
"vite": "^6.0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,3 +8,55 @@ export const uploadFile = (formData) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户人脸样本列表
|
||||||
|
* @param type
|
||||||
|
*/
|
||||||
|
export const getFaceSamplesList = (type: number) => {
|
||||||
|
return service.Post('/api/auth/storage/face/sample/list', {
|
||||||
|
type: type,
|
||||||
|
}, {
|
||||||
|
cacheFor: {
|
||||||
|
expire: 60 * 60 * 24 * 7,
|
||||||
|
mode: "restore",
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
ignoreToken: false,
|
||||||
|
signature: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 修改人脸样本名称
|
||||||
|
* @param id
|
||||||
|
* @param face_name
|
||||||
|
*/
|
||||||
|
export const modifyFaceSampleName = (id: number, face_name: string) => {
|
||||||
|
return service.Post('/api/auth/storage/face/sample/modify/name', {
|
||||||
|
id: id,
|
||||||
|
face_name: face_name,
|
||||||
|
}, {
|
||||||
|
meta: {
|
||||||
|
ignoreToken: false,
|
||||||
|
signature: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 批量修改人脸样本类型
|
||||||
|
* @param ids
|
||||||
|
* @param face_type
|
||||||
|
*/
|
||||||
|
export const modifyFaceTypeBatch = (ids: number[], face_type: number) => {
|
||||||
|
return service.Post('/api/auth/storage/face/sample/modify/type', {
|
||||||
|
ids: ids,
|
||||||
|
face_type: face_type,
|
||||||
|
}, {
|
||||||
|
meta: {
|
||||||
|
ignoreToken: false,
|
||||||
|
signature: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -40,35 +40,32 @@
|
|||||||
<AUpload
|
<AUpload
|
||||||
:accept="'image/jpg, image/png, image/jpeg'"
|
:accept="'image/jpg, image/png, image/jpeg'"
|
||||||
name="images"
|
name="images"
|
||||||
:max-count="3"
|
:max-count="1"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
method="post"
|
method="post"
|
||||||
:directory="false"
|
:directory="false"
|
||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:custom-request="comment.customUploadRequest"
|
:custom-request="comment.customUploadRequest"
|
||||||
:before-upload="comment.beforeUpload"
|
:before-upload="comment.beforeUpload"
|
||||||
:disabled="comment.imageList.length >= 3 || comment.uploadLoading"
|
:disabled="comment.uploadLoading"
|
||||||
>
|
>
|
||||||
<ABadge :count="comment.imageList.length">
|
|
||||||
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
||||||
class="comment-action-icon" :loading="comment.uploadLoading">
|
class="comment-action-icon" :loading="comment.uploadLoading">
|
||||||
{{ t('comment.picture') }}
|
{{ t('comment.picture') }}
|
||||||
</AButton>
|
</AButton>
|
||||||
</ABadge>
|
|
||||||
</AUpload>
|
</AUpload>
|
||||||
<template v-if="comment.imageList.length > 0">
|
<template v-if="comment.imageList">
|
||||||
<AImagePreviewGroup>
|
<ABadge style="margin-left: 10px;">
|
||||||
<ABadge style="margin-left: 10px;" v-for="(item, index) in comment.imageList" :key="index">
|
|
||||||
<template #count>
|
<template #count>
|
||||||
<CloseCircleOutlined @click="comment.removeBase64Image(index)" style="color: #f5222d"/>
|
<CloseCircleOutlined @click="comment.removeBase64Image()" style="color: #f5222d"/>
|
||||||
</template>
|
</template>
|
||||||
<AAvatar shape="square" size="small">
|
<AAvatar shape="square" size="small">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
<AImage :width="24" :height="24" :src="comment.imageList"/>
|
||||||
</template>
|
</template>
|
||||||
</AAvatar>
|
</AAvatar>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
</AImagePreviewGroup>
|
|
||||||
</template>
|
</template>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
@@ -153,10 +150,6 @@ async function commentSubmit(point: any) {
|
|||||||
message.error(t('comment.commentContentNotEmpty'));
|
message.error(t('comment.commentContentNotEmpty'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (comment.imageList.length > 3) {
|
|
||||||
message.error(t('comment.maxImageCount'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const content = commentContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
|
const content = commentContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
|
||||||
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)]/g; // 匹配 [1.gif] 的字符串
|
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)]/g; // 匹配 [1.gif] 的字符串
|
||||||
const contentWithEmoji = content.replace(regex, (_match, p1) => {
|
const contentWithEmoji = content.replace(regex, (_match, p1) => {
|
||||||
@@ -216,10 +209,6 @@ async function showSlideCaptcha() {
|
|||||||
if (commentContent.value.trim() === "") {
|
if (commentContent.value.trim() === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (comment.imageList.length > 3) {
|
|
||||||
message.warning(t('comment.maxImageCount'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const res = await comment.getSlideCaptchaData();
|
const res = await comment.getSlideCaptchaData();
|
||||||
if (res) {
|
if (res) {
|
||||||
showSubmitCaptcha.value = true;
|
showSubmitCaptcha.value = true;
|
||||||
|
@@ -68,8 +68,7 @@
|
|||||||
<AFlex :vertical="false" align="center" class="reply-images" v-if="item.images">
|
<AFlex :vertical="false" align="center" class="reply-images" v-if="item.images">
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
<ASpace direction="horizontal">
|
<ASpace direction="horizontal">
|
||||||
<AImage :width="80" :height="80" v-for="(image, index) in item.images" :key="index"
|
<AImage :width="80" :height="80" :src="item.images">
|
||||||
:src="image">
|
|
||||||
<template #previewMask>
|
<template #previewMask>
|
||||||
<EyeOutlined style="font-size: 20px;"/>
|
<EyeOutlined style="font-size: 20px;"/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -56,24 +56,24 @@
|
|||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:custom-request="comment.customUploadRequest"
|
:custom-request="comment.customUploadRequest"
|
||||||
:before-upload="comment.beforeUpload"
|
:before-upload="comment.beforeUpload"
|
||||||
:disabled="comment.imageList.length >= 3 || comment.uploadLoading"
|
:disabled="comment.uploadLoading"
|
||||||
>
|
>
|
||||||
<ABadge :count="comment.imageList.length">
|
<ABadge>
|
||||||
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
||||||
class="comment-action-icon-reply" :loading="comment.uploadLoading">
|
class="comment-action-icon-reply" :loading="comment.uploadLoading">
|
||||||
{{ t('comment.picture') }}
|
{{ t('comment.picture') }}
|
||||||
</AButton>
|
</AButton>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
</AUpload>
|
</AUpload>
|
||||||
<template v-if="comment.imageList.length > 0">
|
<template v-if="comment.imageList">
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
<ABadge style="margin-left: 10px;" v-for="(item, index) in comment.imageList" :key="index">
|
<ABadge style="margin-left: 10px;">
|
||||||
<template #count>
|
<template #count>
|
||||||
<CloseCircleOutlined @click="comment.removeBase64Image(index)" style="color: #f5222d"/>
|
<CloseCircleOutlined @click="comment.removeBase64Image()" style="color: #f5222d"/>
|
||||||
</template>
|
</template>
|
||||||
<AAvatar shape="square" size="small">
|
<AAvatar shape="square" size="small">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
<AImage :width="24" :height="24" :src="comment.imageList"/>
|
||||||
</template>
|
</template>
|
||||||
</AAvatar>
|
</AAvatar>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
@@ -157,10 +157,6 @@ async function replySubmit(point: any) {
|
|||||||
if (replyContent.value.trim() === "") {
|
if (replyContent.value.trim() === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (comment.imageList.length > 3) {
|
|
||||||
message.warning(t('comment.maxImageCount'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const content = replyContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
|
const content = replyContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
|
||||||
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)\]/g; // 匹配 [1.gif] 的字符串
|
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)\]/g; // 匹配 [1.gif] 的字符串
|
||||||
const contentWithEmoji = content.replace(regex, (_match, p1) => {
|
const contentWithEmoji = content.replace(regex, (_match, p1) => {
|
||||||
@@ -234,10 +230,6 @@ async function showSlideCaptcha() {
|
|||||||
message.warning(t('comment.commentContentNotEmpty'));
|
message.warning(t('comment.commentContentNotEmpty'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (comment.imageList.length > 3) {
|
|
||||||
message.warning(t('comment.maxImageCount'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const res = await comment.getSlideCaptchaData();
|
const res = await comment.getSlideCaptchaData();
|
||||||
if (res) {
|
if (res) {
|
||||||
showSubmitCaptcha.value = true;
|
showSubmitCaptcha.value = true;
|
||||||
|
@@ -51,8 +51,7 @@
|
|||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
<ASpace direction="horizontal">
|
<ASpace direction="horizontal">
|
||||||
<AImage :width="80" :height="80" v-for="(image, index) in child.images" :key="index"
|
<AImage :width="80" :height="80" :src="child.images">
|
||||||
:src="image">
|
|
||||||
<template #previewMask>
|
<template #previewMask>
|
||||||
<EyeOutlined style="font-size: 20px;"/>
|
<EyeOutlined style="font-size: 20px;"/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -59,26 +59,25 @@
|
|||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:custom-request="comment.customUploadRequest"
|
:custom-request="comment.customUploadRequest"
|
||||||
:before-upload="comment.beforeUpload"
|
:before-upload="comment.beforeUpload"
|
||||||
:disabled="comment.imageList.length >= 3 || comment.uploadLoading"
|
:disabled="comment.uploadLoading"
|
||||||
>
|
>
|
||||||
<ABadge :count="comment.imageList.length">
|
<ABadge>
|
||||||
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
||||||
class="comment-action-icon-reply-child" :loading="comment.uploadLoading">
|
class="comment-action-icon-reply-child" :loading="comment.uploadLoading">
|
||||||
{{ t('comment.picture') }}
|
{{ t('comment.picture') }}
|
||||||
</AButton>
|
</AButton>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
</AUpload>
|
</AUpload>
|
||||||
<template v-if="comment.imageList.length > 0">
|
<template v-if="comment.imageList">
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
<ABadge style="margin-left: 10px;" v-for="(item, index) in comment.imageList"
|
<ABadge style="margin-left: 10px;">
|
||||||
:key="index">
|
|
||||||
<template #count>
|
<template #count>
|
||||||
<CloseCircleOutlined @click="comment.removeBase64Image(index)"
|
<CloseCircleOutlined @click="comment.removeBase64Image()"
|
||||||
style="color: #f5222d"/>
|
style="color: #f5222d"/>
|
||||||
</template>
|
</template>
|
||||||
<AAvatar shape="square" size="small">
|
<AAvatar shape="square" size="small">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
<AImage :width="24" :height="24" :src="comment.imageList"/>
|
||||||
</template>
|
</template>
|
||||||
</AAvatar>
|
</AAvatar>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
@@ -168,10 +167,6 @@ async function replyReplySubmit(point: any) {
|
|||||||
if (replyReplyContent.value.trim() === "") {
|
if (replyReplyContent.value.trim() === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (comment.imageList.length > 3) {
|
|
||||||
message.warning(t('comment.maxImageCount'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const content = replyReplyContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
|
const content = replyReplyContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
|
||||||
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)]/g; // 匹配 [1.gif] 的字符串
|
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)]/g; // 匹配 [1.gif] 的字符串
|
||||||
const contentWithEmoji = content.replace(regex, (_match, p1) => {
|
const contentWithEmoji = content.replace(regex, (_match, p1) => {
|
||||||
@@ -240,10 +235,6 @@ async function showSlideCaptcha() {
|
|||||||
message.warning(t('comment.commentContentNotEmpty'));
|
message.warning(t('comment.commentContentNotEmpty'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (comment.imageList.length > 3) {
|
|
||||||
message.warning(t('comment.maxImageCount'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const res = await comment.getSlideCaptchaData();
|
const res = await comment.getSlideCaptchaData();
|
||||||
if (res) {
|
if (res) {
|
||||||
showSubmitCaptcha.value = true;
|
showSubmitCaptcha.value = true;
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
:style="cardStyle">
|
:style="cardStyle">
|
||||||
<div class="hover-circle" @click.stop="toggleSelection"
|
<div class="hover-circle" @click.stop="toggleSelection"
|
||||||
v-if="showHoverCircle"
|
v-if="showHoverCircle && !isSelected"
|
||||||
:style="{ width: iconSize + 'px', height: iconSize + 'px' }">
|
:style="{ width: iconSize + 'px', height: iconSize + 'px' }">
|
||||||
<img :src="greyComplete" alt="Hover" class="hover-icon"
|
<img :src="greyComplete" alt="Hover" class="hover-icon"
|
||||||
:style="{ width: iconSize + 'px', height: iconSize + 'px' }"/>
|
:style="{ width: iconSize + 'px', height: iconSize + 'px' }"/>
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import clickOutside from '@/directives/v-click-outside.ts';
|
import clickOutside from '@/directives/v-click-outside.ts';
|
||||||
import lazyLoad from "@/directives/v-lazy-load.ts";
|
import lazyLoad from "@/directives/v-lazy-load.ts";
|
||||||
|
import focus from "@/directives/v-focus.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register all directives
|
* Register all directives
|
||||||
@@ -10,4 +11,5 @@ import lazyLoad from "@/directives/v-lazy-load.ts";
|
|||||||
export const registerDirectives = (app: any) => {
|
export const registerDirectives = (app: any) => {
|
||||||
app.directive('click-outside', clickOutside);
|
app.directive('click-outside', clickOutside);
|
||||||
app.directive('lazy.ts-load', lazyLoad);
|
app.directive('lazy.ts-load', lazyLoad);
|
||||||
|
app.directive('v-focus', focus);
|
||||||
};
|
};
|
||||||
|
6
src/directives/v-focus.ts
Normal file
6
src/directives/v-focus.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
mounted(el) {
|
||||||
|
// 当元素被挂载到 DOM 时,自动聚焦
|
||||||
|
el.focus();
|
||||||
|
},
|
||||||
|
};
|
@@ -29,8 +29,7 @@ export const useCommentStore = defineStore(
|
|||||||
thumbX: 0,
|
thumbX: 0,
|
||||||
thumbY: 0
|
thumbY: 0
|
||||||
});
|
});
|
||||||
const fileList = ref<any[]>([]);
|
const imageList = ref<string>("");
|
||||||
const imageList = ref<any[]>([]);
|
|
||||||
const uploadLoading = ref<boolean>(false);
|
const uploadLoading = ref<boolean>(false);
|
||||||
const emojiList = ref<any[]>(QQ_EMOJI);
|
const emojiList = ref<any[]>(QQ_EMOJI);
|
||||||
const commentId = ref<number | null>(null);
|
const commentId = ref<number | null>(null);
|
||||||
@@ -199,8 +198,7 @@ export const useCommentStore = defineStore(
|
|||||||
* 清空文件列表
|
* 清空文件列表
|
||||||
*/
|
*/
|
||||||
async function clearFileList() {
|
async function clearFileList() {
|
||||||
fileList.value = [];
|
imageList.value = "";
|
||||||
imageList.value = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -221,7 +219,6 @@ export const useCommentStore = defineStore(
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file); // 文件转换
|
reader.readAsDataURL(file); // 文件转换
|
||||||
reader.onloadend = async function () {
|
reader.onloadend = async function () {
|
||||||
if (fileList.value.length < 3) {
|
|
||||||
const img: HTMLImageElement = document.createElement('img');
|
const img: HTMLImageElement = document.createElement('img');
|
||||||
img.src = reader.result as string;
|
img.src = reader.result as string;
|
||||||
img.onload = async () => {
|
img.onload = async () => {
|
||||||
@@ -230,16 +227,13 @@ export const useCommentStore = defineStore(
|
|||||||
const isNSFW: boolean = await predictNSFW(nsfw, img);
|
const isNSFW: boolean = await predictNSFW(nsfw, img);
|
||||||
if (isNSFW) {
|
if (isNSFW) {
|
||||||
message.error(i18n.global.t('comment.illegalImage'));
|
message.error(i18n.global.t('comment.illegalImage'));
|
||||||
fileList.value.pop();
|
imageList.value = "";
|
||||||
uploadLoading.value = false;
|
uploadLoading.value = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fileList.value.push(img.src);
|
imageList.value = img.src;
|
||||||
uploadLoading.value = false;
|
uploadLoading.value = false;
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -249,16 +243,14 @@ export const useCommentStore = defineStore(
|
|||||||
* 自定义上传图片请求
|
* 自定义上传图片请求
|
||||||
*/
|
*/
|
||||||
async function customUploadRequest() {
|
async function customUploadRequest() {
|
||||||
imageList.value = fileList.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除图片
|
* 移除图片
|
||||||
* @param index
|
|
||||||
*/
|
*/
|
||||||
async function removeBase64Image(index: number) {
|
async function removeBase64Image() {
|
||||||
fileList.value.splice(index, 1);
|
imageList.value = "";
|
||||||
imageList.value.splice(index, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -316,7 +308,6 @@ export const useCommentStore = defineStore(
|
|||||||
replyLoading,
|
replyLoading,
|
||||||
slideCaptchaData,
|
slideCaptchaData,
|
||||||
commentMap,
|
commentMap,
|
||||||
fileList,
|
|
||||||
imageList,
|
imageList,
|
||||||
uploadLoading,
|
uploadLoading,
|
||||||
emojiList,
|
emojiList,
|
||||||
|
4
src/types/comment.d.ts
vendored
4
src/types/comment.d.ts
vendored
@@ -23,14 +23,14 @@ interface CommentContent {
|
|||||||
avatar: string;
|
avatar: string;
|
||||||
nickname: string;
|
nickname: string;
|
||||||
level?: number;
|
level?: number;
|
||||||
images: string[];
|
images: string;
|
||||||
is_liked: boolean;
|
is_liked: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReplyCommentParams {
|
export interface ReplyCommentParams {
|
||||||
topic_id: string,
|
topic_id: string,
|
||||||
content: string,
|
content: string,
|
||||||
images: string[],
|
images: string,
|
||||||
author: string,
|
author: string,
|
||||||
reply_id: number,
|
reply_id: number,
|
||||||
reply_user: string,
|
reply_user: string,
|
||||||
|
@@ -1,70 +1,216 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="people-album">
|
<div class="people-album">
|
||||||
<div class="people-album-header">
|
<div class="people-album-header">
|
||||||
<ADropdown>
|
<ADropdown trigger="click">
|
||||||
<AButton type="text" size="large" class="people-album-button">
|
<AButton type="text" size="large" class="people-album-button">
|
||||||
人物
|
{{ selecetedKey === '0' ? '人 物' : '已隐藏' }}
|
||||||
<DownOutlined class="people-album-icon"/>
|
<DownOutlined class="people-album-icon"/>
|
||||||
</AButton>
|
</AButton>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<AMenu>
|
<AMenu selectable :selectedKeys="[selecetedKey]" @select="handleSelect">
|
||||||
<AMenuItem>人 物</AMenuItem>
|
<AMenuItem key="0">人 物</AMenuItem>
|
||||||
<AMenuItem>已隐藏</AMenuItem>
|
<AMenuItem key="1">已隐藏</AMenuItem>
|
||||||
</AMenu>
|
</AMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</ADropdown>
|
</ADropdown>
|
||||||
|
<span class="people-album-count">共<span style="color: #0e87cc">{{ faceList.length }}</span>位</span>
|
||||||
</div>
|
</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="selecetedKey === '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>
|
||||||
|
{{ selecetedKey === '0' ? '隐藏人物' : '取消隐藏' }}
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
<div class="people-album-container">
|
||||||
|
<ASpin :spinning="loading" size="large" wrapperClassName="people-album-container">
|
||||||
<div class="people-album-content">
|
<div class="people-album-content">
|
||||||
<div class="people-album-item" @mouseover="showButton = true" @mouseleave="showButton = false">
|
<CheckCard
|
||||||
|
v-for="(item, index) in faceList"
|
||||||
|
:key="index"
|
||||||
|
class="photo-item"
|
||||||
|
margin="0"
|
||||||
|
border-radius="0"
|
||||||
|
v-model="selected"
|
||||||
|
:showHoverCircle="true"
|
||||||
|
:background-color="'transparent'"
|
||||||
|
:iconSize="20"
|
||||||
|
:showSelectedEffect="false"
|
||||||
|
:value="item.id">
|
||||||
|
<div class="people-album-item"
|
||||||
|
:class="{ 'selected-item': selected.includes(item.id) }"
|
||||||
|
@mouseover="item.showButton = true"
|
||||||
|
@mouseleave="item.showButton = false">
|
||||||
<div class="people-album-item-avatar">
|
<div class="people-album-item-avatar">
|
||||||
<AAvatar :size="86" shape="circle" src="/test/4.png"/>
|
<AAvatar :size="86" shape="circle" :src="item.face_image"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="people-album-item-name">
|
<div class="people-album-item-name" v-show="!item.face_name">
|
||||||
<AButton @click="showAddNameInput" class="people-album-add-name" v-show="showButton && !showInput" type="link"
|
<AButton @click="showAddNameInput(index)" class="people-album-add-name"
|
||||||
|
v-show="item.showButton && !item.showInput"
|
||||||
|
type="link"
|
||||||
size="small">
|
size="small">
|
||||||
添加名字
|
添加名字
|
||||||
</AButton>
|
</AButton>
|
||||||
<AInput v-show="showInput" @blur="hideAddNameInput" size="small" class="people-album-add-input">
|
<AInput ref="addNameInput" v-model:value="addNameInputValue" v-show="item.showInput"
|
||||||
|
@blur="hideAddNameInput(index)" size="small"
|
||||||
|
:maxlength="10"
|
||||||
|
:placeholder="item.face_name"
|
||||||
|
class="people-album-add-input">
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<AButton type="link" size="small">完成</AButton>
|
<AButton type="link" style="font-size: 12px;" size="small" @mousedown.prevent
|
||||||
|
@click="modifyFaceName(item.id,index)">完成
|
||||||
|
</AButton>
|
||||||
|
</template>
|
||||||
|
</AInput>
|
||||||
|
</div>
|
||||||
|
<div class="people-album-item-name" v-show="item.face_name">
|
||||||
|
<AButton @click="showAddNameInput(index)" class="people-album-add-name" v-show="!item.showInput"
|
||||||
|
type="link"
|
||||||
|
size="small">
|
||||||
|
{{ item.face_name }}
|
||||||
|
</AButton>
|
||||||
|
<AInput ref="addNameInput" v-model:value="addNameInputValue" autofocus v-show="item.showInput"
|
||||||
|
@blur="hideAddNameInput(index)" size="small"
|
||||||
|
:maxlength="10"
|
||||||
|
:placeholder="item.face_name"
|
||||||
|
class="people-album-add-input">
|
||||||
|
<template #suffix>
|
||||||
|
<AButton type="link" style="font-size: 12px;" size="small" @mousedown.prevent
|
||||||
|
@click="modifyFaceName(item.id,index)">完成
|
||||||
|
</AButton>
|
||||||
</template>
|
</template>
|
||||||
</AInput>
|
</AInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="people-album-item" @mouseover="showButton = true" @mouseleave="showButton = false">
|
</CheckCard>
|
||||||
<div class="people-album-item-avatar">
|
|
||||||
<AAvatar :size="86" shape="circle" src="/test/4.png"/>
|
|
||||||
</div>
|
|
||||||
<div class="people-album-item-name">
|
|
||||||
<AButton @click="showAddNameInput" class="people-album-add-name" v-show="showButton && !showInput" type="link"
|
|
||||||
size="small">
|
|
||||||
添加名字
|
|
||||||
</AButton>
|
|
||||||
<AInput v-show="showInput" @blur="hideAddNameInput" size="small" class="people-album-add-input">
|
|
||||||
<template #suffix>
|
|
||||||
<AButton type="link" size="small">完成</AButton>
|
|
||||||
</template>
|
|
||||||
</AInput>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</ASpin>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const showButton = ref(false);
|
import {getFaceSamplesList, modifyFaceSampleName, modifyFaceTypeBatch} from "@/api/storage";
|
||||||
const showInput = ref(false);
|
|
||||||
|
|
||||||
function showAddNameInput() {
|
const faceList = ref<any[]>([]);
|
||||||
showInput.value = true;
|
const addNameInput = ref<any>(null);
|
||||||
showButton.value = false;
|
const addNameInputValue = ref<string>('');
|
||||||
|
const selecetedKey = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideAddNameInput() {
|
function showAddNameInput(index: number) {
|
||||||
showInput.value = false;
|
if (faceList.value[index]) {
|
||||||
showButton.value = false;
|
faceList.value[index].showInput = true;
|
||||||
|
faceList.value[index].showButton = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAddNameInput(index: number) {
|
||||||
|
if (faceList.value[index]) {
|
||||||
|
faceList.value[index].showInput = false;
|
||||||
|
faceList.value[index].showButton = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改人脸名称
|
||||||
|
* @param id
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
addNameInputValue.value = '';
|
||||||
|
hideAddNameInput(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择分类
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
function handleSelect({key}) {
|
||||||
|
selecetedKey.value = key;
|
||||||
|
getFaceList(parseInt(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全选
|
||||||
|
*/
|
||||||
|
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, selecetedKey.value === '0' ? 1 : 0);
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
await getFaceList();
|
||||||
|
selected.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getFaceList();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -103,7 +249,74 @@ function hideAddNameInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.people-album-count {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(129, 129, 138, 0.99);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.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%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
.people-album-content {
|
.people-album-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -111,11 +324,14 @@ function hideAddNameInput() {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
|
|
||||||
.people-album-item {
|
.people-album-item {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
height: 160px;
|
height: 160px;
|
||||||
@@ -124,7 +340,7 @@ function hideAddNameInput() {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@@ -151,8 +367,8 @@ function hideAddNameInput() {
|
|||||||
|
|
||||||
|
|
||||||
.people-album-add-name {
|
.people-album-add-name {
|
||||||
color: rgba(126, 126, 135, 0.99);
|
color: rgba(110, 110, 113, 0.99);
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.people-album-add-name:hover {
|
.people-album-add-name:hover {
|
||||||
@@ -160,11 +376,35 @@ function hideAddNameInput() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.people-album-item:hover {
|
.people-album-item:hover,
|
||||||
|
.people-album-item.selected-item {
|
||||||
background-color: rgba(248, 248, 248, 0.74);
|
background-color: rgba(248, 248, 248, 0.74);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.fade-enter-active, .fade-leave-active {
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from, .fade-leave-to { /* .slide-fade-leave-active 在离开之前 */
|
||||||
|
transform: translateY(-20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from {
|
||||||
|
transform: translateY(-30px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-to {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -77,7 +77,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import more from "@/assets/svgs/more.svg";
|
import more from "@/assets/svgs/more.svg";
|
||||||
|
|
||||||
|
|
||||||
const isHovered = ref<boolean>(false);
|
const isHovered = ref<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.phoalbum {
|
.phoalbum {
|
||||||
|
Reference in New Issue
Block a user