同步
CI/CD / build (push) Successful in 5m12s Details

This commit is contained in:
13121765685 2026-01-16 15:39:59 +08:00
parent 91824a1368
commit 2ffe162423
10 changed files with 370 additions and 42 deletions

View File

@ -0,0 +1,270 @@
<template>
<div v-if="show" class="download-confirm-overlay" @click="handleOverlayClick">
<div class="download-confirm-container" @click.stop>
<!-- 关闭按钮 -->
<button class="close-button" @click="handleCancel">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<!-- 图标 -->
<div class="icon-container warning">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 9V13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 17H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="currentColor" stroke-width="2"/>
</svg>
</div>
<!-- 标题 -->
<h3 class="confirm-title">{{ t('modelModal.downloadConfirmTitle') }}</h3>
<!-- 提示信息 - 黄色警示框 -->
<div class="warning-message-box">
<svg class="warning-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 9V13M12 17H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="currentColor" stroke-width="2"/>
</svg>
<p class="confirm-message">{{ t('modelModal.downloadConfirmMessage') }}</p>
</div>
<!-- 按钮区域 -->
<div class="confirm-actions">
<button class="action-button cancel" @click="handleCancel">
{{ t('modelModal.cancel') }}
</button>
<button class="action-button confirm" @click="handleConfirm">
{{ t('modelModal.confirm') }}
</button>
</div>
</div>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n';
const props = defineProps({
show: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['confirm', 'cancel']);
const { t } = useI18n();
const handleConfirm = () => {
emit('confirm');
};
const handleCancel = () => {
emit('cancel');
};
const handleOverlayClick = () => {
handleCancel();
};
</script>
<style scoped>
.download-confirm-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
backdrop-filter: blur(4px);
animation: fadeIn 0.2s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.download-confirm-container {
background: white;
border-radius: 16px;
width: 90%;
max-width: 480px;
padding: 32px 24px;
position: relative;
box-shadow: 0 20px 60px -12px rgba(0, 0, 0, 0.25);
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.close-button {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
border-radius: 8px;
background-color: #f5f5f5;
border: none;
color: #666;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.close-button:hover {
background-color: #e0e0e0;
color: #333;
}
.icon-container {
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
margin: 0 auto 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
color: white;
}
.icon-container.warning {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
}
.confirm-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 16px;
text-align: center;
color: #1a1a1a;
}
.warning-message-box {
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
border: 1px solid #fbbf24;
border-radius: 12px;
padding: 16px;
margin: 0 0 28px;
display: flex;
gap: 12px;
align-items: flex-start;
}
.warning-icon {
flex-shrink: 0;
color: #d97706;
margin-top: 2px;
}
.confirm-message {
font-size: 14px;
line-height: 1.6;
margin: 0;
color: #92400e;
text-align: left;
}
.confirm-actions {
display: flex;
gap: 12px;
}
.action-button {
flex: 1;
padding: 12px 24px;
border-radius: 8px;
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
border: none;
}
.action-button.cancel {
background-color: #f5f5f5;
color: #666;
}
.action-button.cancel:hover {
background-color: #e0e0e0;
}
.action-button.confirm {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.action-button.confirm:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
/* 响应式设计 */
@media (max-width: 768px) {
.download-confirm-container {
padding: 24px 20px;
width: 95%;
}
.confirm-title {
font-size: 18px;
}
.warning-message-box {
padding: 14px;
gap: 10px;
}
.warning-icon {
width: 18px;
height: 18px;
}
.confirm-message {
font-size: 13px;
}
.action-button {
padding: 10px 20px;
font-size: 13px;
}
}
@media (max-width: 480px) {
.confirm-actions {
flex-direction: column;
}
.action-button {
width: 100%;
}
.warning-message-box {
flex-direction: column;
align-items: center;
text-align: center;
}
.warning-icon {
margin-top: 0;
margin-bottom: 8px;
}
.confirm-message {
text-align: center;
}
}
</style>

View File

@ -7,7 +7,6 @@
</el-icon> </el-icon>
</button> </button>
</div> </div>
<div class="project-name-section"> <div class="project-name-section">
<div class="project-title-container" v-if="!isEditing"> <div class="project-title-container" v-if="!isEditing">
<h1 class="project-title">{{ projectName }}</h1> <h1 class="project-title">{{ projectName }}</h1>

View File

@ -147,7 +147,11 @@ export default {
download: '下载', download: '下载',
delete: '删除', delete: '删除',
purchase: '购买', purchase: '购买',
loadingText: '图片加载中...' loadingText: '图片加载中...',
downloadConfirmTitle: '下载确认',
downloadConfirmMessage: '用于创建的原始图片可能存在版权限制,这些限制可能限制或禁止商业使用。如果您公开分享此作品,请注明由"印你"创作',
confirm: '确定',
cancel: '取消'
}, },
addModal: { addModal: {
title: '添加内容', title: '添加内容',
@ -553,7 +557,7 @@ export default {
verificationCodePlaceholder: '请输入6位验证码', verificationCodePlaceholder: '请输入6位验证码',
confirmBind: '确认绑定', confirmBind: '确认绑定',
deleteConfirmTitle: '删除确认', deleteConfirmTitle: '删除确认',
deleteConfirmContent: '您确定要删除智能体 "{agentName}" 吗?', deleteConfirmContent: '您确定要删除智能体吗?',
deleteWarning: '此操作不可恢复,所有相关数据将被永久删除。', deleteWarning: '此操作不可恢复,所有相关数据将被永久删除。',
devices: '台设备' devices: '台设备'
}, },
@ -1209,6 +1213,7 @@ export default {
confirm: '确认', confirm: '确认',
yes: '是', yes: '是',
no: '否', no: '否',
delete: '删除',
cancel: '取消', cancel: '取消',
generate: '生成', generate: '生成',
back: '返回', back: '返回',
@ -1768,7 +1773,11 @@ export default {
download: 'Save', download: 'Save',
delete: 'Remove', delete: 'Remove',
purchase: 'Purchase', purchase: 'Purchase',
loadingText: 'Loading image...' loadingText: 'Loading image...',
downloadConfirmTitle: 'Download Confirmation',
downloadConfirmMessage: 'The original images used for creation may be subject to copyright restrictions, which may limit or prohibit commercial use. If you share this work publicly, please credit "印你" as the creator',
confirm: 'Confirm',
cancel: 'Cancel'
}, },
addModal: { addModal: {
title: 'Add Content', title: 'Add Content',
@ -2254,7 +2263,7 @@ export default {
verificationCodePlaceholder: 'Please enter 6-digit verification code', verificationCodePlaceholder: 'Please enter 6-digit verification code',
confirmBind: 'Confirm Bind', confirmBind: 'Confirm Bind',
deleteConfirmTitle: 'Delete Confirmation', deleteConfirmTitle: 'Delete Confirmation',
deleteConfirmContent: 'Are you sure you want to delete agent "{agentName}"?', deleteConfirmContent: 'Are you sure you want to delete agent?',
deleteWarning: 'This operation cannot be undone, all related data will be permanently deleted.', deleteWarning: 'This operation cannot be undone, all related data will be permanently deleted.',
devices: 'devices' devices: 'devices'
}, },
@ -2918,6 +2927,7 @@ export default {
back: 'Back', back: 'Back',
save: 'Save', save: 'Save',
create: 'Create', create: 'Create',
delete: 'Delete',
search: 'Search', search: 'Search',
reset: 'Reset', reset: 'Reset',
selectStartTime: 'Select Start Time', selectStartTime: 'Select Start Time',

View File

@ -109,14 +109,14 @@
center center
> >
<div class="delete-dialog-content"> <div class="delete-dialog-content">
<p>{{ t('agentManagement.deleteConfirmContent', { agentName: currentAgentName.value }) }}</p> <p>{{ t('agentManagement.deleteConfirmContent') }}</p>
<p class="delete-warning">{{ t('agentManagement.deleteWarning') }}</p> <p class="delete-warning">{{ t('agentManagement.deleteWarning') }}</p>
</div> </div>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="cancelDelete">{{ t('common.cancel') }}</el-button> <el-button @click="cancelDelete">{{ t('common.cancel') }}</el-button>
<el-button type="danger" @click="confirmDelete"> <el-button type="danger" @click="confirmDelete">
{{ t('agentManagement.confirmDelete') }} {{ t('common.delete') }}
</el-button> </el-button>
</span> </span>
</template> </template>

View File

@ -147,7 +147,6 @@ import { Plus as PlusIcon, Folder as FolderIcon, Edit as EditIcon, View as ViewI
import SeriesSelector from '../../components/SeriesSelector.vue' import SeriesSelector from '../../components/SeriesSelector.vue'
import {Project} from '../Project/index' import {Project} from '../Project/index'
import { dateUtils } from '@deotaland/utils' import { dateUtils } from '@deotaland/utils'
const { formatDate } = dateUtils const { formatDate } = dateUtils
const PluginProject = new Project() const PluginProject = new Project()
const { t } = useI18n() const { t } = useI18n()

View File

@ -21,7 +21,7 @@
type="primary" type="primary"
size="large" size="large"
class="action-btn primary-btn create-btn-large" class="action-btn primary-btn create-btn-large"
@click="navigateToFeature({ path: `/project/new/Done` })"> @click="showSeriesSelector = true">
{{ t('home.welcome.startCreating') }} {{ t('home.welcome.startCreating') }}
</el-button> </el-button>
<div v-else class="guest-actions"> <div v-else class="guest-actions">
@ -103,6 +103,12 @@
</div> </div>
</div> </div>
</section> </section>
<!-- 系列选择弹窗 -->
<SeriesSelector
:show="showSeriesSelector"
@confirm="handleSeriesConfirm"
@cancel="handleSeriesCancel"
/>
</div> </div>
</template> </template>
@ -110,6 +116,7 @@
import { computed, onMounted } from 'vue' import { computed, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import SeriesSelector from '../../components/SeriesSelector.vue'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import CountUp from 'vue-countup-v3' import CountUp from 'vue-countup-v3'
import { ModernHome } from './index.js' import { ModernHome } from './index.js'
@ -132,7 +139,20 @@ import {
Picture, Picture,
Cpu Cpu
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
//
const handleSeriesCancel = () => {
showSeriesSelector.value = false
}
const createNewProject = (series) => {
router.push(`/project/new/${series.name}`)
}
//
const handleSeriesConfirm = (series) => {
showSeriesSelector.value = false
createNewProject(series)
}
//
const showSeriesSelector = ref(false)
const { t } = useI18n() const { t } = useI18n()
const router = useRouter() const router = useRouter()
const authStore = useAuthStore() const authStore = useAuthStore()

View File

@ -129,6 +129,12 @@
:initialIndex="currentImageIndex" :initialIndex="currentImageIndex"
@close="showImagePreview = false" @close="showImagePreview = false"
/> />
<!-- 下载确认弹窗 -->
<DownloadConfirmModal
:show="showDownloadConfirm"
@confirm="handleDownloadConfirm"
@cancel="showDownloadConfirm = false"
/>
<DtCanvasEditor <DtCanvasEditor
:language="locale" :language="locale"
v-model:visible="canvasEditorVisible" v-model:visible="canvasEditorVisible"
@ -179,6 +185,7 @@ import CharacterImportModal from '../../components/CharacterImportModal/index.vu
import HeaderComponent from '../../components/HeaderComponent/HeaderComponent.vue'; import HeaderComponent from '../../components/HeaderComponent/HeaderComponent.vue';
import GuideModal from '../../components/GuideModal/index.vue'; import GuideModal from '../../components/GuideModal/index.vue';
import ImagePreviewModal from '../../components/ImagePreviewModal/index.vue'; import ImagePreviewModal from '../../components/ImagePreviewModal/index.vue';
import DownloadConfirmModal from '../../components/DownloadConfirmModal/index.vue';
import {useRoute,useRouter} from 'vue-router'; import {useRoute,useRouter} from 'vue-router';
import {MeshyServer,GiminiServer,FileServer} from '@deotaland/utils'; import {MeshyServer,GiminiServer,FileServer} from '@deotaland/utils';
import OrderProcessModal from '../../components/OrderProcessModal/index.vue'; import OrderProcessModal from '../../components/OrderProcessModal/index.vue';
@ -216,6 +223,9 @@ const openShow = ref(false);
const showImagePreview = ref(false); const showImagePreview = ref(false);
const previewImages = ref([]); const previewImages = ref([]);
const currentImageIndex = ref(0); const currentImageIndex = ref(0);
//
const showDownloadConfirm = ref(false);
const pendingDownloadUrl = ref(null);
// //
const cleanupFunctions = ref({}); const cleanupFunctions = ref({});
const projectId = ref(null); const projectId = ref(null);
@ -232,6 +242,15 @@ const getGenerateCount = async ()=>{
} }
// //
const downloadImage = async (url) => { const downloadImage = async (url) => {
pendingDownloadUrl.value = url;
showDownloadConfirm.value = true;
}
const handleDownloadConfirm = async () => {
showDownloadConfirm.value = false;
const url = pendingDownloadUrl.value;
pendingDownloadUrl.value = null;
try { try {
const blobUrl = await fileServer.fetchImage(url); const blobUrl = await fileServer.fetchImage(url);
const link = document.createElement('a'); const link = document.createElement('a');
@ -1040,10 +1059,8 @@ const stopSceneDrag = () => {
}; };
// //
const handleWheel = (e) => { const handleWheel = (e) => {
// // 便
if (!isMobile.value) { e.preventDefault();
e.preventDefault(); //
}
// CtrlWindows/LinuxCmdMac // CtrlWindows/LinuxCmdMac
if (e.ctrlKey || e.metaKey) { if (e.ctrlKey || e.metaKey) {
@ -1081,9 +1098,14 @@ const handleWheel = (e) => {
scale.value = newScale; scale.value = newScale;
} }
} else { } else {
// //
const moveAmount = e.deltaY * 0.5; // //
sceneOffsetY.value += (-moveAmount); const moveAmountY = e.deltaY * 0.5;
const moveAmountX = e.deltaX * 0.5;
//
sceneOffsetY.value += (-moveAmountY);
sceneOffsetX.value += (-moveAmountX);
} }
}; };
@ -1133,10 +1155,10 @@ onMounted(() => {
setTimeout(()=>{ setTimeout(()=>{
setTourJson(); setTourJson();
},200) },200)
// 使 // 使便
const removeWheelListener = addPassiveEventListener(document, 'wheel', preventZoom); const removeWheelListener = addPassiveEventListener(document, 'wheel', preventZoom, { passive: false });
const removeTouchStartListener = addPassiveEventListener(document, 'touchstart', preventPinchZoom); const removeTouchStartListener = addPassiveEventListener(document, 'touchstart', preventPinchZoom, { passive: false });
const removeTouchMoveListener = addPassiveEventListener(document, 'touchmove', preventPinchZoom); const removeTouchMoveListener = addPassiveEventListener(document, 'touchmove', preventPinchZoom, { passive: false });
// 便使 // 便使
cleanupFunctions.value = { cleanupFunctions.value = {
wheel: removeWheelListener, wheel: removeWheelListener,
@ -1350,13 +1372,13 @@ html {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
touch-action: none; /* 阻止触摸设备上的默认行为 */ touch-action: pan-x pan-y; /* 允许触摸板左右和上下拖动 */
} }
/* 移动端主内容区域适配 */ /* 移动端主内容区域适配 */
@media (max-width: 768px) { @media (max-width: 768px) {
.main-content { .main-content {
touch-action: pan-x pan-y; /* 移动端允许平移 */ touch-action: none; /* 移动端阻止默认行为,使用自定义触摸处理 */
user-select: auto; /* 移动端允许选择文本 */ user-select: auto; /* 移动端允许选择文本 */
} }
} }

View File

@ -136,6 +136,12 @@
:image-url="canvasEditorImageUrl" :image-url="canvasEditorImageUrl"
@add-prompt-card="handleCanvasSave" @add-prompt-card="handleCanvasSave"
/> />
<!-- 下载确认弹窗 -->
<DownloadConfirmModal
:show="showDownloadConfirm"
@confirm="handleDownloadConfirm"
@cancel="showDownloadConfirm = false"
/>
<!-- 文本输入弹窗 --> <!-- 文本输入弹窗 -->
<div v-if="showTextInput" class="text-input-overlay" role="dialog" aria-modal="true" aria-label="文本输入"> <div v-if="showTextInput" class="text-input-overlay" role="dialog" aria-modal="true" aria-label="文本输入">
<div class="text-input-container"> <div class="text-input-container">
@ -184,6 +190,7 @@ import ThemeToggle from '@/components/ui/ThemeToggle.vue';
import AddModal from '@/components/AddModal/index.vue'; import AddModal from '@/components/AddModal/index.vue';
import PurchaseModal from '@/components/PurchaseModal/index.vue'; import PurchaseModal from '@/components/PurchaseModal/index.vue';
import OrderProcessModal from '@/components/OrderProcessModal/index.vue'; import OrderProcessModal from '@/components/OrderProcessModal/index.vue';
import DownloadConfirmModal from '@/components/DownloadConfirmModal/index.vue';
import {WechatBus,isWeChatBrowser} from '@deotaland/utils'; import {WechatBus,isWeChatBrowser} from '@deotaland/utils';
// //
import ShuCard from '@/components/IPCard/shu.vue'; import ShuCard from '@/components/IPCard/shu.vue';
@ -245,6 +252,10 @@ const openShow = ref(false);
const showImagePreview = ref(false); const showImagePreview = ref(false);
const previewImages = ref([]); const previewImages = ref([]);
const currentImageIndex = ref(0); const currentImageIndex = ref(0);
//
const showDownloadConfirm = ref(false);
// URL
const pendingDownloadUrl = ref(null);
// //
const cleanupFunctions = ref({}); const cleanupFunctions = ref({});
const projectId = ref(null); const projectId = ref(null);
@ -261,6 +272,14 @@ const getGenerateCount = async ()=>{
} }
// //
const downloadImage = async (url) => { const downloadImage = async (url) => {
pendingDownloadUrl.value = url;
showDownloadConfirm.value = true;
}
const handleDownloadConfirm = async () => {
showDownloadConfirm.value = false;
const url = pendingDownloadUrl.value;
pendingDownloadUrl.value = null;
if(isWeChatBrowser()){ if(isWeChatBrowser()){
WechatBus.BusWechartForNavigate('/pages/bus/index',{ WechatBus.BusWechartForNavigate('/pages/bus/index',{
method: 'download', method: 'download',

View File

@ -93,7 +93,8 @@ onMounted(() => {
// //
const handleRegisterSuccess = (userData) => { const handleRegisterSuccess = (userData) => {
router.go(-1) const query = route.query
router.replace({ name: 'login', query })
} }
// //

View File

@ -171,17 +171,16 @@
</div> </div>
</div> </div>
<!-- 邀请信息区域 - 免费会员可见 --> <!-- 邀请信息区域 - 免费会员可见 -->
<div class="invitation-section"> <div class="invitation-section" v-if="userData.inviteCodes.filter(item => item.codeType!='permanent').length > 0">
<h2 v-if="false">{{ $t('userCenter.invitation.title') }}</h2> <h2 >{{ $t('userCenter.invitation.title') }}</h2>
<div v-if="false" class="invitation-info"> <div class="invitation-info">
<div class="info-item"> <div class="info-item">
<label>{{ $t('userCenter.invitation.inviteCount') }}</label> <label>{{ $t('userCenter.invitation.inviteCount') }}</label>
<span>{{ userData.inviteCount }}</span> <span>{{ userData.inviteCount }}</span>
</div> </div>
</div> </div>
<!-- 邀请码卡片区域 --> <!-- 邀请码卡片区域 -->
<div class="invite-codes-section"> <div class="invite-codes-section" >
<div class="section-header"> <div class="section-header">
<h3>{{ $t('userCenter.invitation.inviteCodes') }}</h3> <h3>{{ $t('userCenter.invitation.inviteCodes') }}</h3>
<el-button <el-button
@ -220,17 +219,8 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 邀请规则区域 --> <!-- 邀请规则区域 -->
<div class="invitation-rules"> <div class="invitation-rules" >
<h3>{{ $t('userCenter.invitation.rules.title') }}</h3> <h3>{{ $t('userCenter.invitation.rules.title') }}</h3>
<!-- 规则容器 - 并排展示免费会员和达人会员规则 --> <!-- 规则容器 - 并排展示免费会员和达人会员规则 -->
@ -273,8 +263,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 优惠券详情弹窗 --> <!-- 优惠券详情弹窗 -->
<el-dialog <el-dialog
v-model="showVoucherDetail" v-model="showVoucherDetail"
@ -354,7 +342,7 @@ const userData = ref({
// //
avatar: authStore.user?.avatarUrl, avatar: authStore.user?.avatarUrl,
nickname: authStore.user?.nickname, nickname: authStore.user?.nickname,
email: authStore.user?.email || 'test@example.com', email: authStore.user?.email || '',
// //
currentPoints: 0, currentPoints: 0,
pointsList: [], pointsList: [],