♻️ refactored code

This commit is contained in:
2025-02-27 18:46:15 +08:00
parent 0e1b7c32b0
commit 41fdc58c4e
66 changed files with 4178 additions and 1167 deletions

View File

@@ -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(() => {

View File

@@ -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%;

View 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>