Files
msh-system/msh_single_uniapp/pages/ai-generate/effect.vue

760 lines
19 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>
<view class="effect-container" :style="{ paddingTop: statusBarHeight + 'px' }">
<!-- 顶部导航 -->
<view class="nav-bar">
<view class="nav-left" @click="goBack">
<text class="iconfont icon-fanhui"></text>
</view>
<view class="nav-title">设计前后效果</view>
<view class="nav-right" @click="toggleFavorite">
<text class="iconfont" :class="isFavorited ? 'icon-aixin' : 'icon-aixin1'"></text>
</view>
</view>
<scroll-view scroll-y class="main-content" :style="{ height: scrollViewHeight }">
<!-- 改造前后对比卡片 -->
<view class="comparison-section">
<view
class="comparison-card"
id="comparisonCard"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
>
<!-- 改造后底层全图 -->
<view class="comparison-item after-item-full">
<image
:src="afterImage"
mode="aspectFill"
class="comparison-image"
@error="onImageError('after')"
></image>
<view class="comparison-tag after-tag">
<text>改造前</text>
</view>
</view>
<!-- 改造前遮罩层根据dividerPosition裁剪 -->
<view
class="comparison-item before-item-overlay"
:style="{ width: dividerPosition + '%' }"
>
<image
:src="beforeImage"
mode="aspectFill"
class="comparison-image"
@error="onImageError('before')"
></image>
<view class="comparison-tag before-tag">
<text>改造后</text>
</view>
</view>
<!-- 分隔线和控制按钮 -->
<view
class="divider-wrapper"
:style="{ left: dividerPosition + '%' }"
@touchstart.stop="onDividerTouchStart"
@touchmove.stop="onDividerTouchMove"
@touchend.stop="onDividerTouchEnd"
>
<view class="divider-line"></view>
<view class="divider-btn" @click.stop="toggleComparison">
<view class="pause-icon">
<view class="pause-bar"></view>
<view class="pause-bar"></view>
</view>
</view>
</view>
</view>
</view>
<!-- 生成提示词 -->
<view class="prompt-section">
<view class="prompt-header">
<text class="prompt-title">生成提示词</text>
</view>
<view class="prompt-content">
<text class="prompt-text">{{ promptText }}</text>
</view>
</view>
<view class="bottom-placeholder"></view>
</scroll-view>
<!-- 底部操作按钮 -->
<view class="footer-actions">
<view class="action-btn" @click="regenerate">
<view class="action-icon">
<text class="iconfont icon-shuaxin"></text>
</view>
<text class="action-text">重新生成</text>
</view>
<view class="action-btn" @click="downloadSave">
<view class="action-icon">
<text class="iconfont icon-xiazai1"></text>
</view>
<text class="action-text">下载保存</text>
</view>
<view class="action-btn" @click="share">
<view class="action-icon">
<text class="iconfont icon-fenxiang"></text>
</view>
<text class="action-text">分享</text>
</view>
</view>
</view>
</template>
<script>
import api from '@/api/models-api.js'
export default {
data() {
return {
statusBarHeight: 0,
scrollViewHeight: '100vh',
isFavorited: false,
isPlaying: false,
beforeImage: '',
afterImage: '',
promptText: '将卧室改造成现代简约北欧风格,采用温暖的木质色调和柔和的中性色,保留原有空间布局,增加自然光线和绿植元素,营造舒适宁静的氛围',
articleId: null,
articleData: null,
// 拖动相关
dividerPosition: 50, // 分隔线位置百分比 (0-100)
isDragging: false,
startX: 0,
cardWidth: 0
};
},
onLoad(options) {
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight || 0;
const navHeight = 44;
const footerHeight = 100;
this.scrollViewHeight = `calc(100vh - ${this.statusBarHeight + navHeight + footerHeight}px)`;
// 获取传递的参数
if (options.id) {
this.articleId = options.id;
this.loadArticleData();
}
if (options.beforeImage) {
this.beforeImage = decodeURIComponent(options.beforeImage);
}
if (options.afterImage) {
this.afterImage = decodeURIComponent(options.afterImage);
}
if (options.promptText) {
this.promptText = decodeURIComponent(options.promptText);
}
// 获取卡片宽度
this.$nextTick(() => {
setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
query.select('#comparisonCard').boundingClientRect(data => {
if (data) {
this.cardWidth = data.width;
}
}).exec();
}, 300);
});
},
onUnload() {
// 清理动画定时器
this.stopComparisonAnimation();
},
methods: {
goBack() {
// 清理动画定时器
this.stopComparisonAnimation();
uni.navigateBack();
},
toggleFavorite() {
this.isFavorited = !this.isFavorited;
uni.showToast({
title: this.isFavorited ? '已收藏' : '已取消收藏',
icon: 'none',
duration: 1500
});
},
toggleComparison() {
this.isPlaying = !this.isPlaying;
if (this.isPlaying) {
// 开始播放对比动画
this.startComparisonAnimation();
} else {
// 暂停动画
this.stopComparisonAnimation();
}
},
startComparisonAnimation() {
// 自动左右滑动对比
let direction = 1; // 1: 向右, -1: 向左
this.animationTimer = setInterval(() => {
this.dividerPosition += direction * 2;
if (this.dividerPosition >= 80) {
direction = -1;
} else if (this.dividerPosition <= 20) {
direction = 1;
}
}, 30);
},
stopComparisonAnimation() {
if (this.animationTimer) {
clearInterval(this.animationTimer);
this.animationTimer = null;
}
},
// 触摸开始
onTouchStart(e) {
if (this.isPlaying) {
this.stopComparisonAnimation();
this.isPlaying = false;
}
},
onTouchMove(e) {
// 卡片区域的触摸移动(可选)
},
onTouchEnd(e) {
// 触摸结束
},
// 分隔线拖动
onDividerTouchStart(e) {
this.isDragging = true;
this.startX = e.touches[0].clientX;
// 停止自动播放动画
if (this.isPlaying) {
this.stopComparisonAnimation();
this.isPlaying = false;
}
// 获取卡片宽度
const query = uni.createSelectorQuery().in(this);
query.select('#comparisonCard').boundingClientRect(data => {
if (data) {
this.cardWidth = data.width;
}
}).exec();
},
onDividerTouchMove(e) {
if (!this.isDragging) return;
const currentX = e.touches[0].clientX;
const deltaX = currentX - this.startX;
// 计算新的位置百分比
if (this.cardWidth > 0) {
const deltaPercent = (deltaX / this.cardWidth) * 100;
let newPosition = this.dividerPosition + deltaPercent;
// 限制范围 0-100
if (newPosition < 0) newPosition = 0;
if (newPosition > 100) newPosition = 100;
this.dividerPosition = newPosition;
this.startX = currentX;
}
},
onDividerTouchEnd(e) {
this.isDragging = false;
},
onImageError(type) {
console.error(`${type} image load error`);
uni.showToast({
title: `${type === 'before' ? '改造前' : '改造后'}图片加载失败`,
icon: 'none'
});
},
async loadArticleData() {
if (!this.articleId) return;
try {
uni.showLoading({ title: '加载中...' });
const response = await api.getArticleById(this.articleId);
const data = response.data || response;
this.articleData = data;
// 设置图片和提示词
if (data.imageInput && !this.beforeImage) {
this.beforeImage = data.imageInput;
}
if (data.videoUrl && !this.afterImage) {
this.afterImage = data.videoUrl;
}
if (data.prompt && !this.promptText) {
this.promptText = data.prompt;
} else if (data.synopsis && !this.promptText) {
this.promptText = data.synopsis;
}
uni.hideLoading();
} catch (error) {
console.error('Load article data error:', error);
uni.hideLoading();
uni.showToast({
title: '加载数据失败',
icon: 'none'
});
}
},
regenerate() {
uni.showModal({
title: '重新生成',
content: '确定要重新生成设计效果吗?',
success: (res) => {
if (res.confirm) {
// 跳转到设计页面,携带当前参数
const params = {
beforeImage: encodeURIComponent(this.beforeImage),
promptText: encodeURIComponent(this.promptText)
};
const queryString = Object.keys(params)
.map(key => `${key}=${params[key]}`)
.join('&');
uni.navigateTo({
url: `/pages/ai-generate/design?${queryString}&regenerate=true`
});
}
}
});
},
downloadSave() {
if (!this.afterImage) {
uni.showToast({
title: '暂无图片可下载',
icon: 'none'
});
return;
}
uni.showLoading({ title: '下载中...', mask: true });
uni.downloadFile({
url: this.afterImage,
success: (res) => {
if (res.statusCode === 200) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.hideLoading();
uni.showToast({
title: '已保存到相册',
icon: 'success',
duration: 2000
});
},
fail: (error) => {
uni.hideLoading();
if ((error.errMsg || '').includes('auth')) {
uni.showModal({
title: '需要相册权限',
content: '请在设置中开启相册权限',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting();
}
}
});
} else {
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
}
});
} else {
uni.hideLoading();
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: () => {
uni.hideLoading();
uni.showToast({
title: '下载失败,请重试',
icon: 'none'
});
}
});
},
share() {
uni.showActionSheet({
itemList: ['分享到微信', '分享到朋友圈', '复制链接'],
success: (res) => {
if (res.tapIndex === 0) {
uni.showToast({ title: '分享到微信', icon: 'none' });
} else if (res.tapIndex === 1) {
uni.showToast({ title: '分享到朋友圈', icon: 'none' });
} else if (res.tapIndex === 2) {
this.copyLink();
}
}
});
},
copyLink() {
const shareUrl = this.articleId
? `https://yourapp.com/pages/ai-generate/effect?id=${this.articleId}`
: 'https://yourapp.com/pages/ai-generate/effect';
uni.setClipboardData({
data: shareUrl,
success: () => {
uni.showToast({
title: '链接已复制',
icon: 'success'
});
}
});
}
}
};
</script>
<style lang="scss" scoped>
.effect-container {
width: 100%;
height: 100vh;
background: #000000;
color: #ffffff;
display: flex;
flex-direction: column;
}
.nav-bar {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
.nav-left, .nav-right {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
font-size: 17px;
font-weight: 500;
color: #ffffff;
}
.iconfont {
color: #ffffff;
font-size: 20px;
&.icon-aixin {
color: #ff6b6b;
}
}
}
.main-content {
flex: 1;
padding: 16px;
box-sizing: border-box;
}
// 对比卡片
.comparison-section {
margin-bottom: 24px;
.comparison-card {
width: 100%;
height: 600px; // 从400px增加到600px增加了50%的高度
border-radius: 16px;
overflow: hidden;
position: relative;
background: #1a1a1a;
touch-action: none; // 防止触摸时页面滚动
// 改造后(底层全图)
.after-item-full {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.comparison-image {
width: 100%;
height: 100%;
}
.comparison-tag {
position: absolute;
top: 12px;
right: 12px;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
backdrop-filter: blur(10px);
background: rgba(0, 0, 0, 0.6);
color: #ffffff;
z-index: 5;
}
}
// 改造前(遮罩层)
.before-item-overlay {
position: absolute;
top: 0;
left: 0;
height: 100%;
overflow: hidden;
z-index: 8;
transition: width 0.05s linear; // 添加平滑过渡
.comparison-image {
width: 100vw; // 使用视口宽度保证图片不变形
max-width: none;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.comparison-tag {
position: absolute;
top: 12px;
left: 12px;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
backdrop-filter: blur(10px);
background: rgba(66, 202, 77, 0.9);
color: #ffffff;
z-index: 5;
}
}
.divider-wrapper {
position: absolute;
top: 0;
bottom: 0;
width: 44px; // 增加触摸区域
transform: translateX(-50%);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
cursor: ew-resize; // 鼠标样式
transition: left 0.05s linear; // 添加平滑过渡
.divider-line {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 0;
bottom: 0;
width: 4px;
background: #42ca4d;
box-shadow: 0 0 8px rgba(66, 202, 77, 0.6);
}
.divider-btn {
position: relative;
width: 36px;
height: 36px;
background: #42ca4d;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(66, 202, 77, 0.4);
border: 2px solid #ffffff;
.iconfont {
color: #ffffff;
font-size: 16px;
}
.pause-icon {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
.pause-bar {
width: 3px;
height: 14px;
background: #ffffff;
border-radius: 2px;
}
}
&:active {
transform: scale(0.95);
}
}
}
}
}
// 提示词区域
.prompt-section {
margin-bottom: 24px;
.prompt-header {
margin-bottom: 12px;
.prompt-title {
font-size: 15px;
font-weight: 500;
color: #ffffff;
}
}
.prompt-content {
background: rgba(26, 26, 26, 0.6);
border-radius: 12px;
padding: 16px;
border: 1px solid #333;
.prompt-text {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.6;
word-break: break-all;
}
}
}
.bottom-placeholder {
height: 20px;
}
// 底部操作按钮
.footer-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 50px;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.95) 80%, rgba(0, 0, 0, 0.8) 100%);
backdrop-filter: blur(20px);
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
justify-content: space-around;
padding: 12px 16px 30px;
z-index: 100;
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
flex: 1;
padding: 8px 12px;
// background: linear-gradient(135deg, rgba(66, 202, 77, 0.15) 0%, rgba(66, 202, 77, 0.08) 100%);
// border: 1px solid rgba(66, 202, 77, 0.3);
border-radius: 20px;
margin: 4px 6px;
transition: all 0.3s ease;
.action-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: linear-gradient(135deg, #42ca4d 0%, #38b045 100%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(66, 202, 77, 0.3);
transition: all 0.3s ease;
.iconfont {
font-size: 16px;
color: #ffffff;
}
}
.action-text {
font-size: 13px;
font-weight: 500;
color: #ffffff;
letter-spacing: 0.5px;
}
&:active {
transform: scale(0.96);
background: linear-gradient(135deg, rgba(66, 202, 77, 0.25) 0%, rgba(66, 202, 77, 0.15) 100%);
border-color: rgba(66, 202, 77, 0.5);
.action-icon {
transform: scale(0.9);
box-shadow: 0 1px 4px rgba(66, 202, 77, 0.4);
}
}
// 为不同按钮添加特定样式
&:first-child {
.action-icon {
background: linear-gradient(135deg, #42ca4d 0%, #38b045 100%);
}
}
&:nth-child(2) {
.action-icon {
background: linear-gradient(135deg, #4caf50 0%, #45a049 100%);
}
}
&:last-child {
.action-icon {
background: linear-gradient(135deg, #5cb85c 0%, #4cae4c 100%);
}
}
}
}
</style>