Commit c8d58c47 authored by yaoke.yk's avatar yaoke.yk

微信小程序demo

parent 24b905e2
<template> <template>
<view class="timeline"> <view class="timeline">
<view v-for="(step, index) in normalizedSteps" :key="step.key" class="timeline__item"> <view v-for="(step, index) in normalizedSteps" :key="step.key" class="timeline__item">
<view class="timeline__mark" :class="`timeline__mark--${String(step.status).toLowerCase()}`"> <view class="timeline__rail">
<text>{{ index + 1 }}</text> <view class="timeline__mark" :class="`timeline__mark--${String(step.status).toLowerCase()}`">
<text>{{ index + 1 }}</text>
</view>
<view v-if="index < normalizedSteps.length - 1" class="timeline__line" />
</view> </view>
<view class="timeline__content"> <view class="timeline__content">
<view class="timeline__head"> <view class="timeline__head">
...@@ -71,26 +74,38 @@ function stepHint(status?: AgentRunStatus) { ...@@ -71,26 +74,38 @@ function stepHint(status?: AgentRunStatus) {
.timeline__item { .timeline__item {
display: flex; display: flex;
gap: 18rpx; gap: 18rpx;
padding: 18rpx 0; }
.timeline__rail {
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
} }
.timeline__mark { .timeline__mark {
width: 48rpx; width: 52rpx;
height: 48rpx; height: 52rpx;
border-radius: 50%; border-radius: 50%;
background: #eef2ff; background: #eef4ff;
color: #5b5cf6; color: #5b5cf6;
font-size: 22rpx; font-size: 22rpx;
font-weight: 800; font-weight: 860;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-shrink: 0; }
.timeline__line {
width: 2rpx;
min-height: 62rpx;
flex: 1;
background: #e7ebf3;
} }
.timeline__mark--running { .timeline__mark--running {
background: #d8faf3; background: #e0f2fe;
color: #0f766e; color: #075985;
} }
.timeline__mark--done, .timeline__mark--done,
...@@ -107,8 +122,7 @@ function stepHint(status?: AgentRunStatus) { ...@@ -107,8 +122,7 @@ function stepHint(status?: AgentRunStatus) {
.timeline__content { .timeline__content {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
padding-bottom: 20rpx; padding-bottom: 28rpx;
border-bottom: 1rpx solid #eef1f6;
} }
.timeline__head { .timeline__head {
...@@ -119,14 +133,15 @@ function stepHint(status?: AgentRunStatus) { ...@@ -119,14 +133,15 @@ function stepHint(status?: AgentRunStatus) {
} }
.timeline__name { .timeline__name {
color: #182230; color: #101828;
font-size: 28rpx; font-size: 28rpx;
font-weight: 750; font-weight: 800;
} }
.timeline__status { .timeline__status {
color: #667085; color: #667085;
font-size: 23rpx; font-size: 23rpx;
white-space: nowrap;
} }
.timeline__output { .timeline__output {
...@@ -134,6 +149,6 @@ function stepHint(status?: AgentRunStatus) { ...@@ -134,6 +149,6 @@ function stepHint(status?: AgentRunStatus) {
margin-top: 10rpx; margin-top: 10rpx;
color: #475467; color: #475467;
font-size: 24rpx; font-size: 24rpx;
line-height: 1.5; line-height: 1.55;
} }
</style> </style>
...@@ -9,7 +9,10 @@ ...@@ -9,7 +9,10 @@
<text class="status-pill" :class="statusClass(project.status)">{{ statusText(project.status) }}</text> <text class="status-pill" :class="statusClass(project.status)">{{ statusText(project.status) }}</text>
</view> </view>
<text class="project-card__desc">{{ project.description || "等待 Agent 制作内容" }}</text> <text class="project-card__desc">{{ project.description || "等待 Agent 制作内容" }}</text>
<text class="project-card__time">{{ formatDateTime(project.updatedAt || project.createdAt) }}</text> <view class="project-card__meta">
<text>{{ project.type || "AI 短剧" }}</text>
<text>{{ formatDateTime(project.updatedAt || project.createdAt) }}</text>
</view>
</view> </view>
</view> </view>
</template> </template>
...@@ -40,18 +43,21 @@ const indexLabel = computed(() => String((props.index || 0) + 1).padStart(2, "0" ...@@ -40,18 +43,21 @@ const indexLabel = computed(() => String((props.index || 0) + 1).padStart(2, "0"
} }
.project-card__cover { .project-card__cover {
width: 112rpx; width: 118rpx;
height: 112rpx; height: 118rpx;
border-radius: 14rpx; border-radius: 20rpx;
background: linear-gradient(145deg, #20283a, #5b5cf6); background:
linear-gradient(155deg, rgba(91, 92, 246, 0.95), rgba(30, 167, 255, 0.82)),
#101828;
color: #ffffff; color: #ffffff;
font-size: 28rpx; font-size: 27rpx;
font-weight: 800; font-weight: 860;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
justify-content: flex-start; justify-content: flex-start;
padding: 16rpx; padding: 16rpx;
flex-shrink: 0; flex-shrink: 0;
box-shadow: inset 0 0 0 1rpx rgba(255, 255, 255, 0.16);
} }
.project-card__body { .project-card__body {
...@@ -69,9 +75,9 @@ const indexLabel = computed(() => String((props.index || 0) + 1).padStart(2, "0" ...@@ -69,9 +75,9 @@ const indexLabel = computed(() => String((props.index || 0) + 1).padStart(2, "0"
.project-card__title { .project-card__title {
min-width: 0; min-width: 0;
flex: 1; flex: 1;
color: #141923; color: #101828;
font-size: 29rpx; font-size: 30rpx;
font-weight: 750; font-weight: 820;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
...@@ -82,14 +88,17 @@ const indexLabel = computed(() => String((props.index || 0) + 1).padStart(2, "0" ...@@ -82,14 +88,17 @@ const indexLabel = computed(() => String((props.index || 0) + 1).padStart(2, "0"
margin-top: 12rpx; margin-top: 12rpx;
color: #667085; color: #667085;
font-size: 25rpx; font-size: 25rpx;
line-height: 1.45; line-height: 1.5;
overflow: hidden; overflow: hidden;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
.project-card__time { .project-card__meta {
display: block; display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
margin-top: 14rpx; margin-top: 14rpx;
color: #98a2b3; color: #98a2b3;
font-size: 22rpx; font-size: 22rpx;
......
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
<view class="upload__icon">+</view> <view class="upload__icon">+</view>
<view class="upload__body"> <view class="upload__body">
<text class="upload__title">{{ fileName || "上传剧本文件" }}</text> <text class="upload__title">{{ fileName || "上传剧本文件" }}</text>
<text class="upload__desc">支持 doc、docx、txt、pdf、md,上传后可直接启动 Agent</text> <text class="upload__desc">支持 doc、docx、txt、pdf、md,上传后可直接启动 Agent</text>
</view> </view>
<text class="upload__action">选择</text>
</view> </view>
</template> </template>
...@@ -61,13 +62,13 @@ function pickFile() { ...@@ -61,13 +62,13 @@ function pickFile() {
} }
.upload__icon { .upload__icon {
width: 74rpx; width: 76rpx;
height: 74rpx; height: 76rpx;
border-radius: 18rpx; border-radius: 22rpx;
background: #eef2ff; background: #eef4ff;
color: #5b5cf6; color: #5b5cf6;
font-size: 42rpx; font-size: 42rpx;
font-weight: 500; font-weight: 560;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
...@@ -81,9 +82,9 @@ function pickFile() { ...@@ -81,9 +82,9 @@ function pickFile() {
.upload__title { .upload__title {
display: block; display: block;
color: #141923; color: #101828;
font-size: 29rpx; font-size: 29rpx;
font-weight: 750; font-weight: 800;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
...@@ -96,4 +97,10 @@ function pickFile() { ...@@ -96,4 +97,10 @@ function pickFile() {
font-size: 24rpx; font-size: 24rpx;
line-height: 1.45; line-height: 1.45;
} }
.upload__action {
color: #5b5cf6;
font-size: 25rpx;
font-weight: 760;
}
</style> </style>
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
<view class="preview panel"> <view class="preview panel">
<video v-if="src" class="preview__video" :src="src" controls object-fit="contain" /> <video v-if="src" class="preview__video" :src="src" controls object-fit="contain" />
<view v-else class="preview__empty"> <view v-else class="preview__empty">
<text class="preview__title">视频生成完成后会出现在这里</text> <view class="preview__badge">AI VIDEO</view>
<text class="preview__desc">当前小程序先承接创建和进度查看,成片地址接入后可直接预览。</text> <text class="preview__title">成片完成后会出现在这里</text>
<text class="preview__desc">极速出片会自动等待视频任务和分集合成;PC 精修模式可回到网页端继续制作。</text>
</view> </view>
</view> </view>
</template> </template>
...@@ -23,7 +24,7 @@ defineProps<{ ...@@ -23,7 +24,7 @@ defineProps<{
.preview__video, .preview__video,
.preview__empty { .preview__empty {
width: 100%; width: 100%;
height: 720rpx; height: 700rpx;
} }
.preview__empty { .preview__empty {
...@@ -32,22 +33,33 @@ defineProps<{ ...@@ -32,22 +33,33 @@ defineProps<{
flex-direction: column; flex-direction: column;
justify-content: flex-end; justify-content: flex-end;
background: background:
linear-gradient(180deg, rgba(16, 21, 34, 0.2), rgba(16, 21, 34, 0.96)), linear-gradient(180deg, rgba(16, 21, 34, 0.18), rgba(16, 21, 34, 0.96)),
radial-gradient(circle at 20% 16%, rgba(91, 92, 246, 0.44), transparent 36%), linear-gradient(135deg, rgba(91, 92, 246, 0.5), rgba(30, 167, 255, 0.18)),
#101522; #101522;
} }
.preview__badge {
width: fit-content;
margin-bottom: 20rpx;
padding: 8rpx 14rpx;
border-radius: 999rpx;
color: #bfdbfe;
background: rgba(255, 255, 255, 0.12);
font-size: 20rpx;
font-weight: 760;
}
.preview__title { .preview__title {
color: #ffffff; color: #ffffff;
font-size: 32rpx; font-size: 32rpx;
font-weight: 800; font-weight: 840;
} }
.preview__desc { .preview__desc {
display: block; display: block;
margin-top: 14rpx; margin-top: 14rpx;
color: rgba(255, 255, 255, 0.72); color: rgba(255, 255, 255, 0.74);
font-size: 25rpx; font-size: 25rpx;
line-height: 1.55; line-height: 1.58;
} }
</style> </style>
...@@ -4,49 +4,62 @@ ...@@ -4,49 +4,62 @@
"path": "pages/home/index", "path": "pages/home/index",
"style": { "style": {
"navigationBarTitleText": "瑶光 AI 短剧", "navigationBarTitleText": "瑶光 AI 短剧",
"navigationBarBackgroundColor": "#f7f8fc", "navigationBarBackgroundColor": "#0b1020",
"navigationBarTextStyle": "black" "navigationBarTextStyle": "white"
} }
}, },
{ {
"path": "pages/create/index", "path": "pages/create/index",
"style": { "style": {
"navigationBarTitleText": "创建视频任务" "navigationBarTitleText": "创建视频任务",
"navigationBarBackgroundColor": "#0b1020",
"navigationBarTextStyle": "white"
} }
}, },
{ {
"path": "pages/projects/index", "path": "pages/projects/index",
"style": { "style": {
"navigationBarTitleText": "项目" "navigationBarTitleText": "项目",
"navigationBarBackgroundColor": "#0b1020",
"navigationBarTextStyle": "white"
} }
}, },
{ {
"path": "pages/project-progress/index", "path": "pages/project-progress/index",
"style": { "style": {
"navigationBarTitleText": "Agent 制作" "navigationBarTitleText": "Agent 制作",
"navigationBarBackgroundColor": "#0b1020",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true
} }
}, },
{ {
"path": "pages/result/index", "path": "pages/result/index",
"style": { "style": {
"navigationBarTitleText": "视频结果" "navigationBarTitleText": "视频结果",
"navigationBarBackgroundColor": "#0b1020",
"navigationBarTextStyle": "white"
} }
}, },
{ {
"path": "pages/mine/index", "path": "pages/mine/index",
"style": { "style": {
"navigationBarTitleText": "我的" "navigationBarTitleText": "我的",
"navigationBarBackgroundColor": "#0b1020",
"navigationBarTextStyle": "white"
} }
}, },
{ {
"path": "pages/login/index", "path": "pages/login/index",
"style": { "style": {
"navigationBarTitleText": "登录" "navigationBarTitleText": "登录",
"navigationBarBackgroundColor": "#0b1020",
"navigationBarTextStyle": "white"
} }
} }
], ],
"tabBar": { "tabBar": {
"color": "#667085", "color": "#8a94a6",
"selectedColor": "#5b5cf6", "selectedColor": "#5b5cf6",
"backgroundColor": "#ffffff", "backgroundColor": "#ffffff",
"borderStyle": "white", "borderStyle": "white",
...@@ -66,9 +79,9 @@ ...@@ -66,9 +79,9 @@
] ]
}, },
"globalStyle": { "globalStyle": {
"navigationBarTextStyle": "black", "navigationBarTextStyle": "white",
"navigationBarTitleText": "瑶光 AI 短剧", "navigationBarTitleText": "瑶光 AI 短剧",
"navigationBarBackgroundColor": "#f7f8fc", "navigationBarBackgroundColor": "#0b1020",
"backgroundColor": "#f7f8fc" "backgroundColor": "#0b1020"
} }
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<text class="page-title">创建视频任务</text> <text class="page-title">创建视频任务</text>
<text class="page-subtitle">选择制作模式后上传剧本,Agent 会按不同链路推进。</text> <text class="page-subtitle">选择制作模式后上传剧本,Agent 会按不同链路推进。</text>
<view class="mode-switch panel"> <view class="mode-switch">
<button <button
v-for="mode in RUN_MODES" v-for="mode in RUN_MODES"
:key="mode.value" :key="mode.value"
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
<text class="mode-card__desc">{{ selectedMode.description }}</text> <text class="mode-card__desc">{{ selectedMode.description }}</text>
</view> </view>
<view class="form"> <view class="form panel">
<view> <view>
<text class="field-label">项目名称</text> <text class="field-label">项目名称</text>
<input v-model="projectName" class="field-input" placeholder="例如:都市逆袭第 1 集" /> <input v-model="projectName" class="field-input" placeholder="例如:都市逆袭第 1 集" />
...@@ -34,19 +34,19 @@ ...@@ -34,19 +34,19 @@
</view> </view>
<view class="option-grid"> <view class="option-grid">
<view class="option panel"> <view class="option">
<text class="option__label">风格</text> <text class="option__label">风格</text>
<picker :range="styleLabels" :value="styleIndex" @change="styleIndex = Number($event.detail.value)"> <picker :range="styleLabels" :value="styleIndex" @change="styleIndex = Number($event.detail.value)">
<text class="option__value">{{ styleLabels[styleIndex] }}</text> <text class="option__value">{{ styleLabels[styleIndex] }}</text>
</picker> </picker>
</view> </view>
<view class="option panel"> <view class="option">
<text class="option__label">画幅</text> <text class="option__label">画幅</text>
<picker :range="ASPECT_RATIOS" :value="ratioIndex" @change="ratioIndex = Number($event.detail.value)"> <picker :range="ASPECT_RATIOS" :value="ratioIndex" @change="ratioIndex = Number($event.detail.value)">
<text class="option__value">{{ ASPECT_RATIOS[ratioIndex] }}</text> <text class="option__value">{{ ASPECT_RATIOS[ratioIndex] }}</text>
</picker> </picker>
</view> </view>
<view class="option panel"> <view class="option">
<text class="option__label">清晰度</text> <text class="option__label">清晰度</text>
<picker :range="RESOLUTIONS" :value="resolutionIndex" @change="resolutionIndex = Number($event.detail.value)"> <picker :range="RESOLUTIONS" :value="resolutionIndex" @change="resolutionIndex = Number($event.detail.value)">
<text class="option__value">{{ RESOLUTIONS[resolutionIndex] }}</text> <text class="option__value">{{ RESOLUTIONS[resolutionIndex] }}</text>
...@@ -160,19 +160,19 @@ async function submit() { ...@@ -160,19 +160,19 @@ async function submit() {
.mode-switch { .mode-switch {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8rpx; gap: 10rpx;
margin-top: 30rpx; margin-top: 34rpx;
padding: 8rpx; padding: 8rpx;
background: #eef1f7; border-radius: 22rpx;
box-shadow: none; background: rgba(255, 255, 255, 0.12);
} }
.mode-switch__item { .mode-switch__item {
height: 76rpx; min-height: 80rpx;
border-radius: 12rpx; border-radius: 16rpx;
color: #667085; color: rgba(255, 255, 255, 0.72);
font-size: 27rpx; font-size: 27rpx;
font-weight: 760; font-weight: 800;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
...@@ -180,20 +180,20 @@ async function submit() { ...@@ -180,20 +180,20 @@ async function submit() {
.mode-switch__item--active { .mode-switch__item--active {
background: #ffffff; background: #ffffff;
color: #111827; color: #101828;
box-shadow: 0 10rpx 24rpx rgba(17, 24, 39, 0.08); box-shadow: 0 14rpx 30rpx rgba(0, 0, 0, 0.16);
} }
.mode-card { .mode-card {
margin-top: 18rpx; margin-top: 18rpx;
padding: 26rpx; padding: 28rpx;
} }
.mode-card__title { .mode-card__title {
display: block; display: block;
color: #141923; color: #101828;
font-size: 30rpx; font-size: 31rpx;
font-weight: 820; font-weight: 860;
} }
.mode-card__desc { .mode-card__desc {
...@@ -201,37 +201,41 @@ async function submit() { ...@@ -201,37 +201,41 @@ async function submit() {
margin-top: 10rpx; margin-top: 10rpx;
color: #667085; color: #667085;
font-size: 25rpx; font-size: 25rpx;
line-height: 1.55; line-height: 1.58;
} }
.form { .form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 28rpx; gap: 30rpx;
margin-top: 28rpx; margin-top: 24rpx;
padding: 28rpx;
} }
.option-grid { .option-grid {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16rpx; gap: 14rpx;
} }
.option { .option {
padding: 22rpx; min-height: 118rpx;
padding: 20rpx;
border-radius: 18rpx;
background: #f6f8fc;
} }
.option__label { .option__label {
display: block; display: block;
color: #667085; color: #667085;
font-size: 23rpx; font-size: 22rpx;
} }
.option__value { .option__value {
display: block; display: block;
margin-top: 14rpx; margin-top: 14rpx;
color: #141923; color: #101828;
font-size: 28rpx; font-size: 27rpx;
font-weight: 760; font-weight: 820;
} }
</style> </style>
...@@ -3,13 +3,18 @@ ...@@ -3,13 +3,18 @@
<view class="home-head"> <view class="home-head">
<view> <view>
<text class="page-title">今天生成哪条短剧?</text> <text class="page-title">今天生成哪条短剧?</text>
<text class="page-subtitle">手机端可以快速出片,也可以先让 Agent 制作素材后回 PC 精修。</text> <text class="page-subtitle">极速出片拿样片,或先让 Agent 制作素材后回 PC 精修。</text>
</view> </view>
<button class="home-head__mine" @tap="goMine">我的</button> <button class="home-head__mine" @tap="goMine">我的</button>
</view> </view>
<view class="quick panel"> <view class="quick panel">
<textarea v-model="idea" class="quick__textarea" placeholder="例如:把这个都市逆袭剧本做成 9:16 真人短剧,节奏紧凑,先生成前 10 个分镜。" /> <view class="quick__badge">AI VIDEO AGENT</view>
<textarea
v-model="idea"
class="quick__textarea"
placeholder="例如:把这个都市逆袭剧本做成 9:16 真人短剧,节奏紧凑,先生成前 10 个分镜。"
/>
<view class="quick__actions"> <view class="quick__actions">
<button class="primary-button quick__primary" @tap="startQuickVideo">极速出片</button> <button class="primary-button quick__primary" @tap="startQuickVideo">极速出片</button>
<button class="secondary-button quick__secondary" @tap="goCreate">上传剧本</button> <button class="secondary-button quick__secondary" @tap="goCreate">上传剧本</button>
...@@ -17,13 +22,29 @@ ...@@ -17,13 +22,29 @@
<button class="ghost-button quick__refine" @tap="goCreateForRefine">Agent 制作后去 PC 精修</button> <button class="ghost-button quick__refine" @tap="goCreateForRefine">Agent 制作后去 PC 精修</button>
</view> </view>
<view class="section-head"> <view class="home-metrics">
<text>最近项目</text> <view class="home-metric">
<button @tap="goProjects">全部</button> <text>2</text>
<text>制作模式</text>
</view>
<view class="home-metric">
<text>6</text>
<text>自动步骤</text>
</view>
<view class="home-metric">
<text>9:16</text>
<text>默认短剧</text>
</view>
</view>
<view class="section-head home-section">
<text class="section-head__title">最近项目</text>
<button class="section-head__action" @tap="goProjects">全部</button>
</view> </view>
<view v-if="loading" class="empty panel"> <view v-if="loading" class="empty panel">
<text>正在读取项目...</text> <text class="empty__title">正在读取项目</text>
<text class="empty__desc">项目列表马上就绪。</text>
</view> </view>
<view v-else-if="projects.length" class="project-list"> <view v-else-if="projects.length" class="project-list">
<ProjectCard v-for="(project, index) in projects" :key="project.id" :project="project" :index="index" @open="openProject(project.id)" /> <ProjectCard v-for="(project, index) in projects" :key="project.id" :project="project" :index="index" @open="openProject(project.id)" />
...@@ -147,33 +168,44 @@ function goMine() { ...@@ -147,33 +168,44 @@ function goMine() {
} }
.home-head__mine { .home-head__mine {
width: 104rpx; min-width: 104rpx;
height: 62rpx; height: 64rpx;
padding: 0 22rpx;
border-radius: 999rpx; border-radius: 999rpx;
background: #ffffff; background: rgba(255, 255, 255, 0.14);
color: #475467; color: #ffffff;
font-size: 24rpx; font-size: 24rpx;
font-weight: 700; font-weight: 760;
box-shadow: 0 10rpx 28rpx rgba(17, 24, 39, 0.06);
} }
.quick { .quick {
margin-top: 32rpx; margin-top: 34rpx;
padding: 26rpx; padding: 28rpx;
}
.quick__badge {
width: fit-content;
margin-bottom: 20rpx;
padding: 8rpx 14rpx;
border-radius: 999rpx;
color: #075985;
background: #e0f2fe;
font-size: 20rpx;
font-weight: 800;
} }
.quick__textarea { .quick__textarea {
width: 100%; width: 100%;
min-height: 210rpx; min-height: 220rpx;
color: #182230; color: #101828;
font-size: 29rpx; font-size: 29rpx;
line-height: 1.55; line-height: 1.58;
} }
.quick__actions { .quick__actions {
display: flex; display: flex;
gap: 18rpx; gap: 18rpx;
margin-top: 22rpx; margin-top: 24rpx;
} }
.quick__primary { .quick__primary {
...@@ -188,48 +220,43 @@ function goMine() { ...@@ -188,48 +220,43 @@ function goMine() {
margin-top: 8rpx; margin-top: 8rpx;
} }
.section-head { .home-metrics {
margin-top: 38rpx; display: grid;
margin-bottom: 18rpx; grid-template-columns: repeat(3, minmax(0, 1fr));
display: flex; gap: 14rpx;
align-items: center; margin-top: 22rpx;
justify-content: space-between;
}
.section-head text {
color: #141923;
font-size: 31rpx;
font-weight: 800;
} }
.section-head button { .home-metric {
color: #5b5cf6; padding: 22rpx 12rpx;
font-size: 25rpx; border-radius: 20rpx;
font-weight: 700; background: #ffffff;
text-align: center;
box-shadow: 0 12rpx 30rpx rgba(16, 24, 40, 0.05);
} }
.project-list { .home-metric text:first-child {
display: flex; display: block;
flex-direction: column; color: #101828;
gap: 18rpx; font-size: 30rpx;
font-weight: 860;
} }
.empty { .home-metric text:last-child {
padding: 38rpx; display: block;
margin-top: 6rpx;
color: #667085;
font-size: 22rpx;
} }
.empty__title { .home-section {
display: block; margin-top: 40rpx;
color: #182230; margin-bottom: 18rpx;
font-size: 29rpx;
font-weight: 760;
} }
.empty__desc { .project-list {
display: block; display: flex;
margin-top: 10rpx; flex-direction: column;
color: #667085; gap: 18rpx;
font-size: 25rpx;
line-height: 1.5;
} }
</style> </style>
<template> <template>
<view class="page login-page"> <view class="page login-page">
<view class="login-brand"> <view class="login-hero">
<text class="login-brand__mark">AI</text> <view class="login-hero__mark">AI</view>
<text class="page-title">瑶光 AI 短剧</text> <text class="page-title">瑶光 AI 短剧</text>
<text class="page-subtitle">登录后即可在手机上上传剧本、启动 Agent、查看制作进度。</text> <text class="page-subtitle">在手机上上传剧本、启动 Agent、查看制作进度。</text>
</view> </view>
<view class="login-form panel"> <view class="login-form panel">
<text class="login-form__title">登录工作台</text>
<text class="login-form__desc">继续你的短剧项目</text>
<text class="field-label">账号</text> <text class="field-label">账号</text>
<input <input
:value="email" :value="email"
...@@ -87,26 +90,46 @@ async function handleLogin() { ...@@ -87,26 +90,46 @@ async function handleLogin() {
<style scoped lang="scss"> <style scoped lang="scss">
.login-page { .login-page {
padding-top: 96rpx; padding-top: 84rpx;
}
.login-hero {
padding-bottom: 18rpx;
} }
.login-brand__mark { .login-hero__mark {
width: 92rpx; width: 96rpx;
height: 92rpx; height: 96rpx;
margin-bottom: 28rpx; margin-bottom: 28rpx;
border-radius: 24rpx; border-radius: 28rpx;
background: #141923; background: linear-gradient(135deg, #5b5cf6, #1ea7ff);
color: #ffffff; color: #ffffff;
font-size: 30rpx; font-size: 30rpx;
font-weight: 900; font-weight: 900;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 18rpx 42rpx rgba(30, 167, 255, 0.24);
} }
.login-form { .login-form {
margin-top: 56rpx; margin-top: 42rpx;
padding: 30rpx; padding: 32rpx;
}
.login-form__title {
display: block;
color: #101828;
font-size: 34rpx;
font-weight: 860;
}
.login-form__desc {
display: block;
margin-top: 8rpx;
margin-bottom: 30rpx;
color: #667085;
font-size: 25rpx;
} }
.login-form__label { .login-form__label {
......
...@@ -52,13 +52,14 @@ function goLogin() { ...@@ -52,13 +52,14 @@ function goLogin() {
align-items: center; align-items: center;
gap: 22rpx; gap: 22rpx;
padding: 30rpx; padding: 30rpx;
margin-top: 10rpx;
} }
.profile__avatar { .profile__avatar {
width: 104rpx; width: 108rpx;
height: 104rpx; height: 108rpx;
border-radius: 28rpx; border-radius: 30rpx;
background: #141923; background: linear-gradient(135deg, #5b5cf6, #1ea7ff);
color: #ffffff; color: #ffffff;
font-size: 28rpx; font-size: 28rpx;
font-weight: 900; font-weight: 900;
...@@ -67,11 +68,16 @@ function goLogin() { ...@@ -67,11 +68,16 @@ function goLogin() {
justify-content: center; justify-content: center;
} }
.profile__body {
min-width: 0;
flex: 1;
}
.profile__name { .profile__name {
display: block; display: block;
color: #141923; color: #101828;
font-size: 32rpx; font-size: 32rpx;
font-weight: 820; font-weight: 860;
} }
.profile__email { .profile__email {
...@@ -79,6 +85,9 @@ function goLogin() { ...@@ -79,6 +85,9 @@ function goLogin() {
margin-top: 8rpx; margin-top: 8rpx;
color: #667085; color: #667085;
font-size: 25rpx; font-size: 25rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.setting { .setting {
...@@ -101,8 +110,11 @@ function goLogin() { ...@@ -101,8 +110,11 @@ function goLogin() {
} }
.setting__row text:last-child { .setting__row text:last-child {
color: #182230; min-width: 0;
flex: 1;
color: #101828;
text-align: right; text-align: right;
word-break: break-all;
} }
.logout { .logout {
......
...@@ -2,13 +2,19 @@ ...@@ -2,13 +2,19 @@
<view class="page progress-page"> <view class="page progress-page">
<view class="progress-head panel"> <view class="progress-head panel">
<view class="progress-head__top"> <view class="progress-head__top">
<view> <view class="progress-head__main">
<text class="progress-head__label">{{ modeTitle }}</text> <text class="progress-head__label">{{ modeTitle }}</text>
<text class="progress-head__title">{{ project?.name || project?.title || "视频项目" }}</text> <text class="progress-head__title">{{ project?.name || project?.title || "视频项目" }}</text>
</view> </view>
<text class="status-pill" :class="statusClass(run?.status)">{{ statusText(run?.status) }}</text> <text class="status-pill" :class="statusClass(run?.status)">{{ statusText(run?.status) }}</text>
</view> </view>
<progress class="progress-head__bar" :percent="percent" activeColor="#5b5cf6" backgroundColor="#e8ecf5" stroke-width="8" /> <view class="progress-head__track">
<view class="progress-head__fill" :style="{ width: `${percent}%` }" />
</view>
<view class="progress-head__percent">
<text>制作进度</text>
<text>{{ percent }}%</text>
</view>
<text class="progress-head__goal">{{ run?.goal || project?.description || "等待 Agent 返回任务目标" }}</text> <text class="progress-head__goal">{{ run?.goal || project?.description || "等待 Agent 返回任务目标" }}</text>
</view> </view>
...@@ -115,32 +121,57 @@ function goResult() { ...@@ -115,32 +121,57 @@ function goResult() {
<style scoped lang="scss"> <style scoped lang="scss">
.progress-head { .progress-head {
padding: 28rpx; padding: 30rpx;
} }
.progress-head__top { .progress-head__top {
display: flex; display: flex;
align-items: flex-start;
justify-content: space-between; justify-content: space-between;
gap: 20rpx; gap: 20rpx;
} }
.progress-head__main {
min-width: 0;
flex: 1;
}
.progress-head__label { .progress-head__label {
display: block; display: block;
color: #5b5cf6; color: #5b5cf6;
font-size: 24rpx; font-size: 24rpx;
font-weight: 800; font-weight: 820;
} }
.progress-head__title { .progress-head__title {
display: block; display: block;
margin-top: 8rpx; margin-top: 8rpx;
color: #141923; color: #101828;
font-size: 34rpx; font-size: 34rpx;
font-weight: 820; font-weight: 860;
line-height: 1.3;
}
.progress-head__track {
height: 16rpx;
margin-top: 30rpx;
overflow: hidden;
border-radius: 999rpx;
background: #e8ecf5;
} }
.progress-head__bar { .progress-head__fill {
margin-top: 28rpx; height: 100%;
border-radius: 999rpx;
background: linear-gradient(90deg, #5b5cf6, #1ea7ff);
}
.progress-head__percent {
display: flex;
justify-content: space-between;
margin-top: 14rpx;
color: #667085;
font-size: 24rpx;
} }
.progress-head__goal { .progress-head__goal {
...@@ -148,17 +179,17 @@ function goResult() { ...@@ -148,17 +179,17 @@ function goResult() {
margin-top: 20rpx; margin-top: 20rpx;
color: #667085; color: #667085;
font-size: 25rpx; font-size: 25rpx;
line-height: 1.55; line-height: 1.58;
} }
.timeline-panel { .timeline-panel {
margin-top: 22rpx; margin-top: 22rpx;
padding: 10rpx 28rpx; padding: 20rpx 28rpx;
} }
.actions { .actions {
display: grid; display: grid;
grid-template-columns: 1fr 1.5fr; grid-template-columns: 1fr 1.45fr;
gap: 18rpx; gap: 18rpx;
margin-top: 26rpx; margin-top: 26rpx;
padding-bottom: 40rpx; padding-bottom: 40rpx;
...@@ -167,8 +198,4 @@ function goResult() { ...@@ -167,8 +198,4 @@ function goResult() {
.actions .primary-button:only-child { .actions .primary-button:only-child {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
.disabled {
opacity: 0.58;
}
</style> </style>
...@@ -8,13 +8,14 @@ ...@@ -8,13 +8,14 @@
<button class="projects-head__add" @tap="goCreate">+</button> <button class="projects-head__add" @tap="goCreate">+</button>
</view> </view>
<view v-if="loading" class="empty panel"> <view v-if="loading" class="empty panel projects-empty">
<text>正在加载...</text> <text class="empty__title">正在加载</text>
<text class="empty__desc">项目列表马上就绪。</text>
</view> </view>
<view v-else-if="projects.length" class="project-list"> <view v-else-if="projects.length" class="project-list">
<ProjectCard v-for="(project, index) in projects" :key="project.id" :project="project" :index="index" @open="openProject(project.id)" /> <ProjectCard v-for="(project, index) in projects" :key="project.id" :project="project" :index="index" @open="openProject(project.id)" />
</view> </view>
<view v-else class="empty panel"> <view v-else class="empty panel projects-empty">
<text class="empty__title">暂无项目</text> <text class="empty__title">暂无项目</text>
<text class="empty__desc">创建第一个视频任务后,进度和结果会自动出现在这里。</text> <text class="empty__desc">创建第一个视频任务后,进度和结果会自动出现在这里。</text>
<button class="primary-button empty__button" @tap="goCreate">创建任务</button> <button class="primary-button empty__button" @tap="goCreate">创建任务</button>
...@@ -71,12 +72,12 @@ function goCreate() { ...@@ -71,12 +72,12 @@ function goCreate() {
} }
.projects-head__add { .projects-head__add {
width: 76rpx; width: 80rpx;
height: 76rpx; height: 80rpx;
border-radius: 22rpx; border-radius: 24rpx;
background: #141923; background: rgba(255, 255, 255, 0.14);
color: #ffffff; color: #ffffff;
font-size: 40rpx; font-size: 42rpx;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
...@@ -86,27 +87,11 @@ function goCreate() { ...@@ -86,27 +87,11 @@ function goCreate() {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 18rpx; gap: 18rpx;
margin-top: 32rpx; margin-top: 34rpx;
} }
.empty { .projects-empty {
margin-top: 32rpx; margin-top: 34rpx;
padding: 38rpx;
}
.empty__title {
display: block;
color: #182230;
font-size: 30rpx;
font-weight: 780;
}
.empty__desc {
display: block;
margin-top: 10rpx;
color: #667085;
font-size: 25rpx;
line-height: 1.5;
} }
.empty__button { .empty__button {
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<text>合成状态</text> <text>合成状态</text>
<text>{{ assemblyStatus }}</text> <text>{{ assemblyStatus }}</text>
</view> </view>
<view class="result-info__row"> <view class="result-info__row result-info__row--goal">
<text>制作目标</text> <text>制作目标</text>
<text>{{ run?.goal || project?.description || "暂无" }}</text> <text>{{ run?.goal || project?.description || "暂无" }}</text>
</view> </view>
...@@ -103,39 +103,43 @@ function backToProgress() { ...@@ -103,39 +103,43 @@ function backToProgress() {
<style scoped lang="scss"> <style scoped lang="scss">
.result-preview { .result-preview {
margin-top: 28rpx; margin-top: 34rpx;
} }
.result-info { .result-info {
margin-top: 24rpx; margin-top: 24rpx;
padding: 28rpx; padding: 30rpx;
} }
.result-info__title { .result-info__title {
display: block; display: block;
margin-bottom: 16rpx; margin-bottom: 16rpx;
color: #141923; color: #101828;
font-size: 30rpx; font-size: 31rpx;
font-weight: 800; font-weight: 840;
} }
.result-info__row { .result-info__row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 28rpx; gap: 28rpx;
padding: 18rpx 0; padding: 20rpx 0;
border-top: 1rpx solid #eef1f6; border-top: 1rpx solid #eef1f6;
color: #475467; color: #475467;
font-size: 25rpx; font-size: 25rpx;
line-height: 1.45; line-height: 1.5;
} }
.result-info__row text:last-child { .result-info__row text:last-child {
flex: 1; flex: 1;
color: #182230; color: #101828;
text-align: right; text-align: right;
} }
.result-info__row--goal {
align-items: flex-start;
}
.result-back { .result-back {
margin-top: 26rpx; margin-top: 26rpx;
} }
......
page { page {
min-height: 100%; min-height: 100%;
background: #f7f8fc; background: #0b1020;
color: #141923; color: #101828;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
} }
/* #ifdef H5 */ /* #ifdef H5 */
body { body {
margin: 0; margin: 0;
background: #edf1f7; background: #070b16;
} }
#app { #app {
min-height: 100vh; min-height: 100vh;
max-width: 430px; max-width: 430px;
margin: 0 auto; margin: 0 auto;
background: #f7f8fc; background: #0b1020;
box-shadow: 0 0 60rpx rgba(17, 24, 39, 0.08); box-shadow: 0 0 80rpx rgba(0, 0, 0, 0.32);
} }
/* #endif */ /* #endif */
...@@ -44,121 +44,174 @@ button::after { ...@@ -44,121 +44,174 @@ button::after {
.page { .page {
min-height: 100vh; min-height: 100vh;
padding: 28rpx; padding: 28rpx;
background:
linear-gradient(180deg, #0b1020 0%, #111a2e 430rpx, #f5f7fb 431rpx, #f5f7fb 100%);
} }
.page-title { .page-title {
display: block; display: block;
font-size: 42rpx; color: #ffffff;
font-weight: 800; font-size: 44rpx;
color: #111827; font-weight: 860;
line-height: 1.18;
letter-spacing: 0; letter-spacing: 0;
} }
.page-subtitle { .page-subtitle {
display: block; display: block;
margin-top: 10rpx; margin-top: 14rpx;
color: rgba(255, 255, 255, 0.72);
font-size: 26rpx; font-size: 26rpx;
line-height: 1.6; line-height: 1.65;
color: #667085;
} }
.panel { .panel {
border: 1rpx solid #e7eaf3; border: 1rpx solid rgba(217, 224, 239, 0.94);
border-radius: 16rpx; border-radius: 24rpx;
background: #ffffff; background: #ffffff;
box-shadow: 0 16rpx 40rpx rgba(17, 24, 39, 0.05); box-shadow: 0 18rpx 46rpx rgba(16, 24, 40, 0.08);
} }
.primary-button { .glass-panel {
height: 92rpx; border: 1rpx solid rgba(255, 255, 255, 0.18);
border-radius: 16rpx; border-radius: 28rpx;
background: #5b5cf6; background: rgba(255, 255, 255, 0.11);
color: #ffffff; }
font-size: 30rpx;
font-weight: 700; .primary-button,
.secondary-button,
.ghost-button {
min-height: 88rpx;
border-radius: 18rpx;
font-size: 29rpx;
font-weight: 760;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: opacity 0.18s ease, transform 0.18s ease;
}
.primary-button {
background: linear-gradient(135deg, #5b5cf6, #1ea7ff);
color: #ffffff;
box-shadow: 0 16rpx 36rpx rgba(79, 70, 229, 0.26);
} }
.secondary-button { .secondary-button {
height: 84rpx;
border: 1rpx solid #d9deea; border: 1rpx solid #d9deea;
border-radius: 16rpx;
color: #252b37; color: #252b37;
background: #ffffff; background: #ffffff;
font-size: 28rpx;
font-weight: 650;
display: flex;
align-items: center;
justify-content: center;
} }
.ghost-button { .ghost-button {
height: 76rpx;
color: #5b5cf6; color: #5b5cf6;
font-size: 27rpx; }
font-weight: 650;
display: flex; .primary-button[disabled],
align-items: center; .secondary-button[disabled],
justify-content: center; .ghost-button[disabled],
.disabled {
opacity: 0.56;
} }
.field-label { .field-label {
display: block; display: block;
margin-bottom: 14rpx; margin-bottom: 14rpx;
color: #303846; color: #344054;
font-size: 26rpx; font-size: 26rpx;
font-weight: 700; font-weight: 760;
} }
.field-input, .field-input,
.field-textarea { .field-textarea {
width: 100%; width: 100%;
border: 1rpx solid #dfe4ef; border: 1rpx solid #d9deea;
border-radius: 14rpx; border-radius: 18rpx;
background: #ffffff; background: #ffffff;
color: #141923; color: #101828;
font-size: 28rpx; font-size: 29rpx;
} }
.field-input { .field-input {
height: 88rpx; height: 92rpx;
min-height: 88rpx; min-height: 92rpx;
padding: 0 24rpx; padding: 0 26rpx;
display: block; display: block;
position: relative; position: relative;
z-index: 1; z-index: 1;
line-height: 88rpx; line-height: 92rpx;
} }
.field-textarea { .field-textarea {
min-height: 190rpx; min-height: 210rpx;
padding: 22rpx 24rpx; padding: 24rpx 26rpx;
line-height: 1.55; line-height: 1.58;
} }
.muted { .muted {
color: #667085; color: #667085;
} }
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
}
.section-head__title {
color: #141923;
font-size: 32rpx;
font-weight: 820;
}
.section-head__action {
min-height: 64rpx;
padding: 0 20rpx;
border-radius: 999rpx;
color: #5b5cf6;
font-size: 25rpx;
font-weight: 760;
}
.empty {
padding: 42rpx;
}
.empty__title {
display: block;
color: #182230;
font-size: 31rpx;
font-weight: 820;
}
.empty__desc {
display: block;
margin-top: 12rpx;
color: #667085;
font-size: 25rpx;
line-height: 1.56;
}
.status-pill { .status-pill {
height: 42rpx; min-height: 44rpx;
padding: 0 16rpx; padding: 0 16rpx;
border-radius: 999rpx; border-radius: 999rpx;
font-size: 22rpx; font-size: 22rpx;
font-weight: 700; font-weight: 760;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center;
white-space: nowrap;
} }
.status-running { .status-running {
color: #115e59; color: #075985;
background: #d8faf3; background: #e0f2fe;
} }
.status-done { .status-done {
color: #245b1e; color: #166534;
background: #dcfce7; background: #dcfce7;
} }
...@@ -169,6 +222,6 @@ button::after { ...@@ -169,6 +222,6 @@ button::after {
.status-paused, .status-paused,
.status-pending { .status-pending {
color: #5b3b00; color: #8a4b00;
background: #fff3c4; background: #fff3c4;
} }
...@@ -9,7 +9,7 @@ export const PROJECT_STYLES = [ ...@@ -9,7 +9,7 @@ export const PROJECT_STYLES = [
{ label: "真人短剧", value: "live_action_drama" }, { label: "真人短剧", value: "live_action_drama" },
{ label: "国漫风", value: "guoman" }, { label: "国漫风", value: "guoman" },
{ label: "写实电影感", value: "cinematic_realism" }, { label: "写实电影感", value: "cinematic_realism" },
{ label: "3D动画", value: "three_d_animation" } { label: "3D 动画", value: "three_d_animation" }
]; ];
export const ASPECT_RATIOS = ["9:16", "16:9", "1:1"]; export const ASPECT_RATIOS = ["9:16", "16:9", "1:1"];
...@@ -47,7 +47,7 @@ export const RUN_MODES: Array<{ ...@@ -47,7 +47,7 @@ export const RUN_MODES: Array<{
value: "pc_refine", value: "pc_refine",
label: "PC 精修", label: "PC 精修",
title: "Agent 制作,PC 精修", title: "Agent 制作,PC 精修",
description: "生成剧本理解、角色和分镜,完成后回到 PC 工作台继续改图、改分镜、生成视频。", description: "生成剧本理解、角色和分镜,完成后回到 PC 工作台继续改图、改分镜、生成视频。",
runIntent: "auto", runIntent: "auto",
selectedSteps: PC_REFINE_AGENT_STEPS, selectedSteps: PC_REFINE_AGENT_STEPS,
actionLabel: "创建 Agent 制作任务" actionLabel: "创建 Agent 制作任务"
...@@ -55,8 +55,8 @@ export const RUN_MODES: Array<{ ...@@ -55,8 +55,8 @@ export const RUN_MODES: Array<{
{ {
value: "quick_video", value: "quick_video",
label: "极速出片", label: "极速出片",
title: "一键自动出片", title: "一键自动出片",
description: "让 Agent 尝试自动跑完图片、视频和合成,适合先快速拿到一版样片。", description: "让 Agent 自动跑完图片、视频和合成流程,适合先快速拿到一版样片。",
runIntent: "quick_video", runIntent: "quick_video",
selectedSteps: QUICK_VIDEO_AGENT_STEPS, selectedSteps: QUICK_VIDEO_AGENT_STEPS,
actionLabel: "开始极速出片" actionLabel: "开始极速出片"
......
...@@ -27,6 +27,8 @@ export function statusText(status?: AgentRunStatus) { ...@@ -27,6 +27,8 @@ export function statusText(status?: AgentRunStatus) {
return "草稿"; return "草稿";
case "CREATED": case "CREATED":
return "已创建"; return "已创建";
case "SKIPPED":
return "已跳过";
default: default:
return status || "未开始"; return status || "未开始";
} }
...@@ -39,6 +41,7 @@ export function statusClass(status?: AgentRunStatus) { ...@@ -39,6 +41,7 @@ export function statusClass(status?: AgentRunStatus) {
case "RUNNING": case "RUNNING":
return "status-running"; return "status-running";
case "DONE": case "DONE":
case "SKIPPED":
return "status-done"; return "status-done";
case "FAILED": case "FAILED":
return "status-failed"; return "status-failed";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment