PixelAI-mobile/pages/mobile_web/wall/detail.vue
2025-01-22 20:25:38 +08:00

717 lines
22 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.

<template>
<!-- #ifdef APP -->
<view class="wall-detail" :style="{ paddingTop: CustomBar+'rpx', backgroundImage: getTip.backgroundImage}">
<view class="app-top" :style="{ height: CustomBar+'rpx',
background: topLevel===0? '#ffffff00' : `rgba(255, 255, 255,${topLevel})` }"></view>
<!-- #endif -->
<!-- #ifndef APP -->
<view class="wall-detail" :style="{backgroundImage: getTip.backgroundImage}">
<!-- #endif -->
<view class="wd-top">
<!-- #ifdef MP -->
<view :style="{height: `${StatusBar}px`}"></view>
<view class="app-top" :style="{height: `${StatusBar}px`,background: topLevel===0? '#ffffff00' : `rgba(255, 255, 255,${topLevel})`}"></view>
<view class="mobile-logo" :style="{height: `${CustomBarHeight}px`,backgroundColor: `rgba(255, 255, 255,${topLevel})`}">
<!-- #endif -->
<!-- #ifndef MP -->
<view class="mobile-logo" :style="{backgroundColor: `rgba(255, 255, 255,${topLevel})`}">
<!-- #endif -->
<u-icon name="arrow-left" size="40" class="back" @click="back"
:color="topLevel===0? '#090909' : `rgba(9, 9, 9, ${topLevel})`"></u-icon>
<image :src="fileUrl+logo"></image>
<view style="width: 75rpx;"></view>
</view>
<view class="display-wall">
<view class="display-title">
<view class="display-title-left">
专题
<u-line class="display-line" direction="col" color="#000" length="42rpx" />
</view>
<text @click="showSpecialList = true">{{ form.name || '暂无' }}</text>
</view>
<view class="display-introduce">
<text class="introduce-author">pixel.ai制图社</text>
<text class="introduce-time">{{ dateFormat(photos[0] ? photos[0].createtime : form.createtime) }}</text>
<text>广州</text>
</view>
</view>
<view class="display-tips" :style="{color: getTip.color}">
<text>{{ getTip.emotion }}</text>
<text>{{ getTip.text }}</text>
</view>
<u-waterfall class="display-photos" v-model="photos" ref="uWaterfall">
<template v-slot:left="{leftList}">
<view class="photo-lam" @click="maskTouchend(item)" v-for="(item,index) in leftList" :key="item.id">
<u-lazy-load border-radius="30" class="display-photo"
:image="getPhotoPath(item.watermarkPath)"></u-lazy-load>
<view class="sign-text">{{ item.serviceName || '暂无' }}</view>
<image :lazy-load="true" class="sign" :src="fileUrl+sign"></image>
<view class="work-todo" @click.stop="toDeal(item.serviceId)">
<image :src="fileUrl+magicWand"></image>
<text>做同款</text>
</view>
<view class="item-mask" :class="item.display ? 'hideMask' : ''" v-show="item.mask">
<button :style="{color: getTip.color,backgroundImage: getTip.backgroundImage}" @click.stop="buyOrdPicture(item)"
>购买原图</button>
</view>
<!-- <image v-if="item.sourcePath" class="compare-sign" mode="widthFix" :src="getPhotoPath(item.sourcePath)"></image> -->
</view>
</template>
<template v-slot:right="{rightList}">
<view class="photo-lam" @click="maskTouchend(item)" v-for="(item,index) in rightList" :key="item.id">
<u-lazy-load border-radius="30" class="display-photo"
:image="getPhotoPath(item.watermarkPath)"></u-lazy-load>
<view class="sign-text">{{ item.serviceName || '暂无' }}</view>
<image :lazy-load="true" class="sign" :src="fileUrl+sign"></image>
<view class="work-todo" @click.stop="toDeal(item.serviceId)">
<image :src="fileUrl+magicWand"></image>
<text>做同款</text>
</view>
<view class="item-mask" :class="item.display ? 'hideMask' : ''" v-show="item.mask">
<button :style="{color: getTip.color,backgroundImage: getTip.backgroundImage}" @click.stop="buyOrdPicture(item)">购买原图</button>
</view>
<!-- <image v-if="item.sourcePath" class="compare-sign" mode="widthFix" :src="getPhotoPath(item.sourcePath)"></image> -->
</view>
</template>
</u-waterfall>
<view class="last-tip" :style="{color: getTip.color}">
~~ 持续更新中 ~~
</view>
</view>
<!-- 专题列表 -->
<u-popup ref="popup" v-model="showSpecialList" length="44%" border-radius="16" closeable>
<view class="special-list" :style="{backgroundImage: getTip.backgroundImage}">
<view class="special-top">
专题栏
<image :src="staticIp+blackStar"></image>
</view>
<view class="special-scroll">
<view class="special-item" v-for="(item,index) in labels"
:key="index" @click="navigateTo(item.id)">
<text :style="[getSelectionsStyle(item.id)]">{{ item.name }}</text>
<u-icon name="arrow-right" :color="item.id === labelId ? getTip.color : 'rgba(105, 105, 105, 0.5)'"></u-icon>
</view>
</view>
</view>
</u-popup>
<Movable :textColor="getTip.color" :tipText="tipText" />
<Loading :show="loading"></Loading>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import { tools } from '@/utils/utils.js';
import configService from '@/common/config.service.js';
import Movable from './components/movable.vue';
export default {
components: { Movable },
data(){
return{
// #ifdef MP
// 微信小程序自定义导航栏参数
StatusBar: this.StatusBar || 0,
CustomBarHeight: this.Custom.height+(this.Custom.top-this.StatusBar)*2 || 0,
// #endif
// 基础地址
staticIp: configService.ip,
fileUrl: configService.fileUrl + 'pixel/home/',
// logo图标
logo: 'logo-new.png',
// 魔术棒
magicWand: 'magic-wand.png',
// 选择黑星图片
blackStar: 'static/pixel/work/black-star.png',
// 标签图标
sign: 'typelam.png',
topLevel: 0,// 页面是否滚动到顶
// 全加载
loading: false,
// 点击次数
touchNum: 0,
// 专题信息
form: {},
// 类型列表
typeList: {
'0' : '图生图',
'1' : '文生图',
'2' : '选项+图片',
'3' : '选项+文字',
'4' : '换装',
},
// 操作提示
tipText: ["1、点击标题切换专题","2、双击图片可购买原图"],
// 提示语
tips: {
'0' : { color: '#3f5b9c', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(63, 91, 156, 0.3) 100%)', emotion: '( •̀ ω •́ )y', text: '智 / 能 / 创 / 作' },
'28' : { color: '#8ee200', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(142, 226, 0, 0.3) 100%)', emotion: '(o゜▽゜)o☆', text: '容 / 光 / 焕 / 发' },
'29' : { color: '#dd4747', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(221, 71, 71, 0.3) 100%)', emotion: '( ˘•ω•˘ )◞⁽˙³˙⁾', text: '多 / 样 / 颜 / 平 / 方' },
'31' : { color: '#f64e9c', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(246, 78, 156, 0.3) 100%)', emotion: '(*´∀`)~♥', text: '花 / 样 / 试 / 衣 / 间' },
'33' : { color: '#93931e', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(147, 147, 30, 0.3) 100%)', emotion: '(:3[___]=', text: '妄 / 想 / 时 / 间' },
'35' : { color: '#32bbff', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(50, 187, 255, 0.3) 100%)', emotion: '(☄◣ω◢)☄', text: '壁 / 纸 / 专 / 题 / 展' },
'2' : { color: '#8d5ff8', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(141, 95, 248, 0.3) 100%)', emotion: '(∩^o^)⊃━☆゚.*・。', text: '动 / 感 / 人 / 像' },
'4' : { color: '#fdc535', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(253, 197, 53, 0.3) 100%)', emotion: '(´◉‿◉`)', text: '幻 / 想 / 时 / 间' },
'7' : { color: '#52fec7', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(82, 254, 199, 0.3) 100%)', emotion: 'ヾ(´︶`*)ノ♬', text: '通 / 形 / 文 / 字 / 展' },
'9' : { color: '#0C5460', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(12, 84, 96, 0.3) 100%)', emotion: '(̿▀̿ ̿Ĺ̯̿̿▀̿ ̿)̄', text: '面 / 部 / 造 / 型 / 社' },
'10' : { color: '#0f93b6', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(15, 147, 182, 0.3) 100%)', emotion: 'ƪ(•̃͡ε•̃͡)∫', text: '高 / 端 / 发 / 廊 / 展' },
'11' : { color: '#606971', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(96, 105, 113, 0.3) 100%)', emotion: '∠( ᐛ 」∠)_', text: '岁 / 月 / 时 / 光 / 机' },
'12' : { color: '#b3a0da', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(179, 160, 218, 0.3) 100%)', emotion: '(੭ ᐕ)੭?', text: '棱 / 角 / 分 / 明' },
'13' : { color: '#fde46c', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(253, 228, 108, 0.3) 100%)', emotion: '(≖_≖)✧', text: '漫 / 画 / 世 / 界' },
'14' : { color: '#5dfec9', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(93, 254, 201, 0.3) 100%)', emotion: '-//(ǒ.ǒ)//-', text: '摄 / 像 / 牢 / 笼' },
'15' : { color: '#fb7bb2', backgroundImage: 'linear-gradient( to bottom, #ffffff 0%, rgba(251, 123, 178, 0.3) 100%)', emotion: '-`д´-', text: '人 / 像 / 抠 / 图 / ' },
// (σ′▽‵)′▽‵)σ
},
// 照片墙
photos: [],
// 专题id
labelId: '',
// 展示专题列表
showSpecialList: false,
// 专题列表
labels: []
}
},
onPageScroll(e) {
// #ifndef H5
const level = e.scrollTop/60;
if(level<=1) this.topLevel = level;
else this.topLevel = 1;
// #endif
},
onShow(){
// #ifndef H5
},
// #endif
// #ifdef H5
let that = this;
window.onscroll = function () {
//为了保证兼容性,这里取三个值,哪个有值取哪一个
//scrollTop就是触发滚轮事件时滚轮的高度
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
const level = scrollTop/60;
if(level<=1) that.topLevel = level;
else that.topLevel = 1;
}
},
onHide() {
window.onscroll = null;
},
// #endif
computed: {
// 获取提示语
getTip(){
return this.tips[this.labelId]||this.tips['0'];
},
// 获取专栏列表选中样式
getSelectionsStyle(id){
return (id) => {
return this.labelId===id?{ color: this.getTip.color} : {};
}
},
},
onLoad(options){
if(options.id){
// 类型不同搞死人
this.labelId = Number(options.id);
this.singleLabel();
}
this.getLabels();
},
methods:{
// 获取标签
async getLabels(){
this.loading = true;
let res = await this.$api.getLabels(4);
if(res?.success){
this.labels = res.data;
if(!this.labelId){
this.labelId = this.labels[0]?.id;
this.form = this.labels[0];
}
this.getPhotos();
}else{
this.loading = false;
this.$refs.uToast.show({type:'error',title: "专题列表获取失败!"});
}
},
// 获取单个标签内容
async singleLabel(){
let res = await this.$api.singleLabel(this.labelId);
if(res?.success){
this.form = res.data ? res.data : {name: '', createtime: ''};
}else{
this.$refs.uToast.show({type:'error',title: "专题信息获取失败!"});
}
},
// 返回
back(){
uni.navigateBack();
},
// 图片格式化
getPhotoPath(path){
let index = path?.indexOf('?');
return path?.includes('://') ? path.substring(0,(!index||index===-1) ? path.length : index) : encodeURI(this.staticIp+path);
},
// 跳转专栏
navigateTo(id){
this.showSpecialList= false;
if(id !== this.labelId)
uni.navigateTo({url: `/pages/mobile_web/wall/detail?id=${id}`})
},
// 前往工作室
toDeal(id = null){
if(id!==null&&id!=='')
uni.navigateTo({url: `/pages/mobile_web/workshops/index?id=${id}`});
else this.$refs.uToast.show({type:'error',title: "暂未开放对应功能!"});
},
// 监听单双击,单击-预览图片、双击-显示删除图标
maskTouchend(item){
this.touchNum ++;
setTimeout(()=>{
if(this.touchNum == 1){
if(item.mask){
// 添加淡出的class样式
item.display = 1;
this.$refs.uWaterfall.$forceUpdate();
// 下面的操作应该替换为请求后端数据后,再停止旋转
// 模拟刷新操作这里用setTimeout模拟
setTimeout(() => {
// 移除淡出类
item.mask = 0;
item.display = 0;
this.$refs.uWaterfall.$forceUpdate();
}, 800);
}else{
const array = [this.getPhotoPath(item.watermarkPath)];
if(item.originalPath) array.push(this.getPhotoPath(item.originalPath));
tools.methods.lookImage(0,array);
}
}
if(this.touchNum >= 2){
const result = item.mask?0:1;
if(result === 0){
// 添加淡出的class样式
item.display = 1;
this.$refs.uWaterfall.$forceUpdate();
// 下面的操作应该替换为请求后端数据后,再停止旋转
// 模拟刷新操作这里用setTimeout模拟
setTimeout(() => {
// 移除淡出类
item.mask = result;
item.display = 0;
this.$refs.uWaterfall.$forceUpdate();
}, 800);
}else{
item.mask = result;
this.$refs.uWaterfall.$forceUpdate();
}
}
this.touchNum = 0;
},250)
},
// 获取作品墙
async getPhotos(){
try{
let res = await this.$api.allPictureWall({labelId: this.labelId});
if(res?.success){
this.photos = res.data;
// this.$forceUpdate();
}else{
this.$refs.uToast.show({type:'error',title: "作品墙内容获取失败!"});
}
}catch(e){}
finally{this.loading = false;}
},
// 时间格式化
dateFormat(time){
let date = time ? new Date(time) : new Date();
let month = date.getMonth()+1,
day = date.getDate(),
hour = date.getHours(),
minute = date.getMinutes(),
second = date.getSeconds();
return `${date.getFullYear()}${month<10?'0':''}${month}${day<10?'0':''}${day} ${hour<10?'0':''}${hour}:${minute<10?'0':''}${minute}`;
},
// 购买原图
buyOrdPicture(item){
let that = this;
uni.showModal({
title: '购买提示',
content: `该作品原图购买需支付${item.price}钻石购买后将直接下载若使用了微信内置浏览器请点击右上角打开浏览器再进行购买以免购买后下载失败`,
confirmColor: this.getTip.color,
success: async (res) => {
if (res.confirm) {
let resp = await that.$api.buyOriginalPicture({id: item.id});
if(resp?.success){
await that.download(resp.data.originalPath);
// 关掉蒙层
item.mask = 0;
item.display = 0;
that.$refs.uWaterfall.$forceUpdate();
}else{
this.$refs.uToast.show({type:'error',title: "购买失败,请检查余额是否充足!"});
}
}
}
});
},
// 判断是否在微信内置浏览器打开
// #ifndef APP
isMpWeb(){
if(navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){
this.$refs.uToast.show({type:'warning',title: "请在浏览器打开进行下载,或长按图片保存到相册!"});
return true;
}else{
return false;
}
},
// #endif
// 图片下载
async download(path = ''){
let that = this;
// #ifndef APP
if(this.isMpWeb()) return;
// #endif
if(this.path === '') {
this.$refs.uToast.show({type:'error',title: "暂无结果图片可下载!"});
return;
}
// 下载地址
// #ifdef H5
let downloadUrl = encodeURI(path);
// #endif
// #ifndef H5
let downloadUrl = that.staticIp + encodeURI(path);
// #endif
// #ifdef H5
uni.request({
url: downloadUrl,
method: 'GET',
header: { 'token' : that.$store.state.vuex_token },
responseType: 'arraybuffer',
success(res) {
const arrayBuffer = res.data;
const blob = new Blob([arrayBuffer], {type: 'image/png'}); // 创建 Blob 对象
const imageUrl = URL.createObjectURL(blob); // 通过 Blob 对象创建图片 URL
let a = document.createElement('a'); //创建一个 a 标签
a.href = imageUrl; //把路径赋到a标签的href上
a.download = 'pixel.png';
a.click();
URL.revokeObjectURL(imageUrl);
a = null;
that.$refs.uToast.show({type:'success',title: "图片下载成功!"});
},
fail() {
that.$refs.uToast.show({type:'error',title: "图片下载失败,请重试!"});
},
complete() {
that.functionLoading = false;
}
});
// #endif
// #ifndef H5
uni.downloadFile({
url: downloadUrl,
header: { 'token': that.$store.state.vuex_token },
success: (res) => {
if (res.statusCode === 200) {
//文件保存到本地
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath, //临时路径
success: function(resp) {
that.$refs.uToast.show({type:'success',title: "下载成功,请前往手机相册查看!"});
}
});
}else{
that.$refs.uToast.show({type:'error',title: "下载失败,请重试!"});
}
},
fail() {
that.$refs.uToast.show({type:'error',title: "暂不支持下载!"});
},
complete() {
that.functionLoading = false;
}
});
// #endif
},
}
}
</script>
<style scoped lang="scss">
.wall-detail{
display: flex;
flex-direction: column;
min-height: 100vh;
// #ifdef APP || MP
.app-top{
width: 100%;
position: fixed;
z-index: 81;
}
// #endif
.wd-top{
// #ifdef APP
.app-top{
width: 100%;
position: fixed;
z-index: 81;
top: 0;
}
// #endif
.mobile-logo{
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.back{
margin-left: 35rpx;
margin-top: 5rpx
}
position: fixed;
z-index: 81;
padding: 15rpx 0;
image{
width: 200rpx;
height: 45rpx;
}
}
.display-wall{
margin: 110rpx 35rpx 32rpx;
.display-title{
display: flex;
align-items: center;
font-size: 42rpx;
font-weight: 600;
.display-title-left{
min-width: 130rpx;
display: flex;
align-items: center;
.display-line{
margin: 0 16rpx !important;
border-width: 6rpx !important;
}
}
text{
font-weight: normal;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #576ba3;
}
}
}
.display-introduce{
display: flex;
align-items: center;
margin: 24rpx 0 40rpx;
font-size: 30rpx;
color: #a8abb2;
.introduce-author{
color: #576ba3;
}
.introduce-time{
margin: 0 24rpx;
}
}
.display-tips{
margin-top: 60rpx;
display: flex;
flex-direction: column;
align-items: center;
text{
margin: 10rpx 0;
}
}
.display-photos{
margin: 50rpx 50rpx 40rpx;
// column-count:2;
// -moz-column-count:2; /* Firefox */
// -webkit-column-count:2; /* Safari 和 Chrome */
column-gap: 2.2em;
-moz-column-gap: 2.2em;
-webkit-column-gap: 2.2em;
.photo-lam{
-webkit-column-break-inside: avoid;
break-inside: avoid; /*防止断点*/
margin-bottom: 1em;
position: relative;
padding-top: 12rpx;
.display-photo{
border-radius: 30rpx;
// 骗系统开启硬件加速
transform: transition3d(0, 0, 0);
// 防止图片加载“闪一下”
will-change: transform;
box-shadow: 0 0 20rpx #696969;
}
.work-todo{
position: absolute;
bottom: 0.85em;
right: 0.7em;
z-index: 1;
border-radius: 12rpx;
padding: 14rpx 30rpx;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
text{
font-weight: bold;
background-image: linear-gradient(to right, #cdfbf2 0%, #a3d4ff 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
image{
margin-right: 10rpx;
width: 34rpx;
height: 34rpx;
}
&:active{
opacity: 0.8;
}
}
.sign-text{
width: 140rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
color: #ffffff;
font-size: 28rpx;
position: absolute;
top: 0.85em;
left: 0.9em;
z-index: 1;
}
.sign{
border: none;
border-radius: 0;
position: absolute;
top: 0.8em;
left: 0.6em;
width: 170rpx;
height: 47rpx;
}
.compare-sign{
position: absolute;
left: 0;
bottom: 0.2em;
width: 120rpx;
height: 50%;
border: 0.1em solid #d5ff00;
border-radius: 20rpx;
box-shadow: 0 0 20rpx #eee;
// box-shadow: rgba(213, 255, 0, 0.3) 0px 4px 12px;
}
.hideMask {
animation-duration: calc(1s * 0.75);
animation-fill-mode: both;
animation-name: hideMask !important;
}
.item-mask{
z-index: 1;
position: absolute;
top: 0;
margin-top: 12rpx;
width: 100%;
height: calc(100% - 12rpx);
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 30rpx;
animation-duration: 0.8s;
animation-name: showMask;
button{
border-radius: 56rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #f9f9f9;
}
}
}
}
.last-tip{
color: rgb(63, 91, 156);
font-size: 28rpx;
text-align: center;
margin: 20rpx 0 40rpx;
}
}
.special-list{
height: 100%;
display: flex;
flex-direction: column;
.special-top{
padding: 80rpx 40rpx 40rpx;
margin-left: 14rpx;
font-size: 40rpx;
font-weight: bold;
position: relative;
color: #222;
image{
position: absolute;
margin-top: -10rpx;
width: 30rpx;
height: 34rpx;
}
}
.special-scroll{
flex: 1;
overflow-y: scroll;
.special-item{
margin: 0 20rpx;
padding: 20rpx 30rpx;
color: #696969;
border-radius: 16rpx;
font-size: 34rpx;
border-top: 2rpx solid #eee;
// box-shadow: 0 0 10rpx #eee;
display: flex;
justify-content: space-between;
align-items: center;
.u-icon{
opacity: 0.6;
}
&:active{
opacity: 0.8;
}
&:nth-last-child(1){
border-bottom: 2rpx solid #eee;
}
}
}
}
}
@keyframes showMask {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes hideMask {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
</style>