Files
faceFamilySource/src/components/PhotoSquare.vue
2025-09-19 18:08:40 +08:00

578 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, onMounted, computed, watch } from "vue"
import { Request, Storage } from "../libs/utils"
import ModalTransition from "./ModalTransition.vue"
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus';
import { RecycleScroller } from "vue-virtual-scroller";
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
const props = defineProps({
show: false
})
const disableInviteHelp = ref(false);
const emit = defineEmits(['go-my-photo', 'update:show']);
const router = useRouter();
const route = useRoute();
const goBack = () => {
emit('update:show', false);
// 清除URL中的查询参数
if (Object.keys(route.query).length > 0) {
router.replace({ path: route.path, query: {} });
}
};
const username = ref();
const likesCount = ref();
const userImg = ref();
const ranking = ref();
const displayMyPublishPhoto = ref(false);
const fetchImages = async () => {
disableInviteHelp.value = false;
try {
const url = new URL('https://huodong2.lzlj.com/api/faceFamily/face/square');
url.searchParams.append('my_only', '0');
const response = await fetch(url.toString(), {
method: 'GET',
headers: {
'Authorization': `Bearer ${Storage.get("userinfos").api_token}`
}
})
const data = await response.json()
if (response.status == 200 || response.status == 201) {
console.log('Success:', data)
images.value = data.data
if (data.my_published_photo) {
displayMyPublishPhoto.value = true;
} else {
displayMyPublishPhoto.value = false;
}
username.value = data.my_published_photo.user_name;
likesCount.value = data.my_published_photo.likes_count;
userImg.value = data.my_published_photo.avatar;
ranking.value = data.my_published_photo.ranking;
} else {
ElMessage.error(data.message);
emit('update:show', false);
}
} catch (error) {
console.error('Error:', error)
}
}
watch(() => props.show, async (newVal) => {
if (!newVal) {
return
}
fetchImages();
}, {immediate: true})
// 图片数据
const images = ref([]);
const userinfos = Storage.get("userinfos")
let mergeId = '';
let inviteCode = '';
const rankingInvite = ref(0);
const nameInvite = ref('');
const linkCountInvite = ref(0);
const backgroundImageForInvite = ref('');
const urlParams = new URLSearchParams(window.location.search);
mergeId = urlParams.get('merge_id');
inviteCode = urlParams.get('fromid');
const getInviteInfo = ()=> {
disableInviteHelp.value = true;
fetch(`https://huodong2.lzlj.com/api/faceFamily/face/merge/${mergeId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${Storage.get("userinfos").api_token}`
}
})
.then(async response => {
const data = await response.json()
if (response.status == 200 || response.status == 201) {
rankingInvite.value = data.ranking;
nameInvite.value = data.user_name;
linkCountInvite.value = data.likes_count;
backgroundImageForInvite.value = data.result_url;
} else {
ElMessage.error(data.message);
// emit('update:show', false);
}
})
.catch((error) => {
ElMessage.error('分享进入页面失败!');
emit('update:show', false);
});
}
watch(() => mergeId, async (newVal) => {
if (!newVal) {
return
}
if (newVal) {
if (inviteCode === userinfos.invite_code) {
weui.alert("不可以给自己点赞哦!");
return false;
}
getInviteInfo();
}
}, {immediate: true})
const displayInviteHelpBtn = ref(true);
const inviteHelp = ()=> {
const formData = {
invite_code: inviteCode,
merge_id: mergeId
}
fetch('https://huodong2.lzlj.com/api/faceFamily/face/like', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: "application/json",
'Authorization': `Bearer ${Storage.get("userinfos").api_token}`
},
body: JSON.stringify(formData)
})
.then(async response => {
const data = await response.json()
if (response.status == 200 || response.status == 201) {
displayInviteHelpBtn.value = false;
ElMessage.success('点赞成功!');
} else {
ElMessage.error(data.message);
}
})
.catch((error) => {
ElMessage.error('点赞失败!');
console.error('Error:', error);
});
}
// onMounted(()=> {
// getInviteInfo()
// })
const markers = ref([]);
markers.value = [
{ x: 50, y: 32, width: 50, height: 14 }
];
const getBackgroundImage = (item) => {
if (item.result_url) {
return `${item.result_url}?x-oss-process=image/resize,w_400/format,webp/quality,q_80`;
}
};
</script>
<template>
<ModalTransition class="photoSquare" :no-animation="true" :show="show">
<div class="photoSquare-bg">
<div class="scene-item item-1" @click="goBack">
<img src="../assets/images/close-btn.webp" alt="关闭按钮">
</div>
<div v-for="(marker, index) in markers"
:key="index"
class="marker"
:style="{
left: marker.x + 'vw',
top: marker.y + 'vw',
width: marker.width + 'vw',
height: marker.height + 'vw'
}"
@click.stop="$emit('go-my-photo')">
</div>
<p class="my-photo-desc">打榜活动截止至9月30日晚12:00结束中奖信息将于10月1日早10点推送至获奖会员</p>
<div v-if="!disableInviteHelp" class="image-gallery ">
<RecycleScroller
class="scroller"
:items="images"
:item-size="1"
key-field="id"
v-slot="{ item, index }"
>
<div class="image-wrapper">
<div class="image-container mask-background"
:style="{ backgroundImage: `url(${getBackgroundImage(item)})` }">
</div>
<img
src="../assets/images/zpgc-border.webp"
class="border-image"
alt="border"
/>
<div class="list-item">
<div class="flex-container-detail">
<div class="photo-number">{{ (index + 1).toString().padStart(3, '0') }}</div>
<div class="photo-name">{{ item.user_name }}</div>
<div class="right-item photo-name">{{ item.likes_count }}</div>
</div>
</div>
</div>
</RecycleScroller>
<!-- <div
v-for="(item, index) in images"
:key="index"
class="image-wrapper"
>
<div class="image-container mask-background">
</div>
<img
src="../assets/images/zpgc-border.webp"
class="border-image"
alt="border"
/>
<div class="flex-container-detail">
<div class="left-group">
<p class="photo-number">{{ (index + 1).toString().padStart(3, '0') }}</p>
<p class="photo-name">{{ item.user_name }}</p>
</div>
<p class="right-item photo-name" style="padding-left: 3vw;">{{ item.likes_count }}</p>
</div>
</div> -->
</div>
<div v-if="!disableInviteHelp && displayMyPublishPhoto" class="fixed-background-container">
<div class="flex-container">
<div class="left-group">
<p class="photo-number">{{ ranking }}</p>
<img :src="userImg" class="user-img" alt="">
<p class="photo-name-square bold">{{ username }}</p>
</div>
<p class="right-item photo-name bold" style="padding-left: 3vw;">{{ likesCount }}</p>
</div>
</div>
<div v-if="disableInviteHelp" class="share-main">
<div class="image-gallery-share">
<div class="image-wrapper-share">
<div class="image-container-share mask-background"
:style="{ backgroundImage: `url(${backgroundImageForInvite})` }">
</div>
<img src="../assets/images/zpgc-border.webp" class="border-image-share" alt="border" />
<div class="flex-container-detail-invite">
<div class="photo-number">{{ rankingInvite }}</div>
<div class="photo-name-invite">{{ nameInvite }}</div>
<div class="link-count-invite">{{ linkCountInvite }}</div>
</div>
</div>
</div>
<div v-if="displayInviteHelpBtn" class="ranking-title" @click="inviteHelp">
<img src="/src/assets/images/dianzan.webp" alt="点赞">
</div>
</div>
</div>
</ModalTransition>
</template>
<style scoped>
.share-main {
display: flex;
flex-flow: column;
align-items: center;
}
.my-photo-desc {
position: absolute;
width: 87vw;
top: 48vw;
text-align: center;
color: #855211;
font-size: 3.6vw;
}
.scroller {
height: 124vw;
overflow-y: auto;
}
.list-item {
position: relative;
}
.marker {
position: absolute;
}
.flex-container-detail-invite {
position: relative;
display: flex;
justify-content: space-around;
align-items: center;
height: 11vw;
bottom: 10.5vw;
font-size: 3vw;
width: 100%;
padding: 0px 13vw;
}
.photo-name-square {
min-width: 14vw;
max-width: 40vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #93551f;
}
.photo-name-invite {
min-width: 14vw;
max-width: 30vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #93551f;
}
.link-count-invite {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #93551f;
padding-left: 2vw;
margin-left: auto;
flex-shrink: 0;
}
.left-group-left {
display: flex;
flex-flow: row;
padding-right: 16vw;
gap: 3vw;
}
.ranking-title {
width: 48vw;
position: absolute;
bottom: 2vw;
left: 27vw;
}
.ranking-title img {
width: 100%;
}
.image-wrapper-share {
position: relative;
width: 69vw;
height: 57vw;
margin-top: 12vw;
}
.image-container-share {
width: 68vw;
height: 81vw;
background-image: url(/src/assets/images/test.webp);
background-size: 100%;
background-repeat: no-repeat;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: -webkit-fill-available;
position: relative;
cursor: pointer;
}
.border-image-share {
position: absolute;
width: 63vw;
top: -0.8vw;
left: 3vw;
}
.bold {
font-weight: bold;
}
.photo-number {
color: red;
flex-shrink: 0;
padding-right: 2vw;
}
.photo-name {
min-width: 14vw;
max-width: 19vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #93551f;
}
.flex-container-detail {
position: absolute;
display: flex;
justify-content: space-around;
align-items: center;
bottom: 5vw;
font-size: 3vw;
width: 100%;
padding: 0 4vw;
left: 2vw;
}
.flex-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
padding: 0 6vw;
}
.left-group {
display: flex;
align-items: center;
gap: 1vw;
}
.user-img {
width: 8vw;
height: 8vw;
border-radius: 50%;
}
.left-group p, .right-item {
padding-left: 2vw;
margin-left: auto;
flex-shrink: 0;
}
.fixed-background-container {
background-image: url('../assets/images/paiming-border.webp');
position: fixed;
bottom: 1vh;
left: 5vw;
width: 90vw;
height: 17vw;
background-size: cover;
background-repeat: no-repeat;
background-position: bottom center;
}
.image-wrapper {
position: relative;
margin-right: 2vw;
margin-left: 2vw;
width: 38vw;
height: 59vw;
}
.border-image {
position: absolute;
width: 40vw;
top: 2.8vw;
left: -0.8vw;
}
.image-gallery {
overflow-y: auto;
overflow-x: hidden;
width: 84vw;
height: 57vh;
position: relative;
display: flex;
flex-flow: row;
flex-wrap: wrap;
top: 26vw;
padding-bottom: 12vw;
}
.image-container {
width: 38vw;
background-image: url('../assets/images/test.webp');
background-size: 100%;
background-repeat: no-repeat;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
min-height: -webkit-fill-available;
position: relative;
cursor: pointer;
}
.mask-background {
-webkit-mask-image: url("../assets/images/my-photo-mengban.webp");
mask-image: url("../assets/images/my-photo-mengban.webp");
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
}
.content-image {
position: relative;
}
.photoSquare-bg {
width: 100%;
height: 92vh;
background-image: url('../assets/images/photo-squarev2.webp');
background-size: cover;
background-repeat: no-repeat;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
min-height: -webkit-fill-available;
}
.scene-item {
position: absolute;
cursor: pointer;
transition: all 0.4s ease;
border-radius: 8px;
overflow: hidden;
border: 3px solid transparent;
animation: float 4s ease-in-out infinite;
}
.scene-item:hover {
transform: scale(1.05);
}
.btn-login {
text-align: center;
border-radius: 1vw;
background-color: #70b2e2;
margin-top: 4vw;
padding: 3vw;
color: #fff;
position: relative;
}
.item-1 {
top: 9%;
width: 10vw;
right: 4%;
}
.item-3 {
top: 16.3vh;
width: 32vw;
position: absolute;
right: 11vw;
}
.scene-item img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.btn-login.disable {
opacity: .6;
}
.btn-login.subloading {
opacity: .6;
}
.btn-login.subloading::before {
content: "";
position: absolute;
border: .5vw solid #fff;
border-color: rgba(255, 255, 255, .8) transparent transparent transparent;
border-radius: 50%;
width: 3vw;
height: 3vw;
top: 30%;
left: calc(50% - 14vw);
animation: loginloading 1s linear infinite;
}
@keyframes loginloading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>