library-picturebook-activity/lesingle-creation-frontend/src/views/public/create/Index.vue

139 lines
3.3 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 lang="ts">
export default { name: 'AiCreateShell' }
</script>
<template>
<div class="ai-create-shell">
<!-- 初始化加载 -->
<div v-if="loading" class="loading-wrapper">
<div class="spinner"></div>
<p class="loading-text">正在加载创作工坊...</p>
<p v-if="loadError" class="load-error">{{ loadError }}</p>
<a-button v-if="loadError" type="primary" @click="initToken" style="margin-top: 12px">
重新加载
</a-button>
</div>
<!-- 子路由渲染(不 keep-alive避免创作状态错乱 -->
<router-view v-else v-slot="{ Component }">
<transition name="ai-slide" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
import { message } from 'ant-design-vue'
import { leaiApi } from '@/api/public'
import { useAicreateStore } from '@/stores/aicreate'
const store = useAicreateStore()
const route = useRoute()
const loading = ref(true)
const loadError = ref('')
watch(
() => route.path,
(path) => {
if (path.startsWith('/p/create')) {
store.setLastCreateRoute(path)
}
},
{ immediate: true },
)
/** 获取乐读派 Token 并存入 store子路由如 Welcome再处理 resumeWorkId / 内存恢复 */
const initToken = async () => {
loading.value = true
loadError.value = ''
try {
const data = await leaiApi.getToken()
store.setSession(data.orgId, data.token)
} catch (err: any) {
const errMsg = err?.response?.data?.message || err?.message || '加载失败'
loadError.value = errMsg
message.error('加载创作工坊失败:' + errMsg)
} finally {
if (!loadError.value) {
loading.value = false
}
}
}
onMounted(() => {
if (store.sessionToken && store.orgId) {
loading.value = false
} else {
void initToken()
}
})
</script>
<style lang="scss">
// 壳组件样式不使用 scoped因为 aicreate.scss 已通过 .ai-create-shell 隔离
// 覆盖 PublicLayout public-main 样式限制为 H5 单列宽度子页面自己控制内边距
.public-main:has(> .ai-create-shell) {
padding-left: 0 !important;
padding-right: 0 !important;
padding-top: 0 !important;
// 保留 padding-bottom 为底部 tabbar 留空间(桌面 40px / 移动 80px
max-width: 430px;
}
.ai-create-shell {
.loading-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 60vh;
text-align: center;
}
.spinner {
width: 48px;
height: 48px;
border: 4px solid rgba(99, 102, 241, 0.15);
border-top-color: #6366f1;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.loading-text {
font-size: 15px;
color: #6b7280;
margin: 0;
}
.load-error {
font-size: 13px;
color: #ef4444;
margin: 8px 0 0;
}
.ai-slide-enter-active,
.ai-slide-leave-active {
transition: all 0.3s ease;
}
.ai-slide-enter-from {
opacity: 0;
transform: translateX(30px);
}
.ai-slide-leave-to {
opacity: 0;
transform: translateX(-30px);
}
}
</style>