feat(web): 前端 SPA 化 + 工具页任务面板统一与历史批量删除
- SPA 化:22 个 MPA html 入口与 *-main.ts 合并为 index.html + vue-router(URL 无 .html 后缀), 页面跳转全部 router-link,/new_web_source/xxx.html 旧路径归一为 /xxx - 任务面板统一:共享 TaskCenterPanel/TaskItemCard/TaskStatCards/HistoryTaskLayer, 16 个工具页右侧统一为统计卡 + 当前任务 + 历史任务弹层(任务ID/开始/结束/状态必展示) - 历史记录支持单条删除 + 批量勾选删除(确认框/全选/失败提示) - Java 7 模块(dedupe/convert/split/productrisk/shopmatch/pricetrack/deletebrand) history 接口补齐任务时间字段(VO+Service,复用 biz_file_task 列) - 图片工作台/API 层(brand/permission/user)既有未提交改动一并提交
This commit is contained in:
@@ -1,100 +1,107 @@
|
||||
<template>
|
||||
<div class="page-shell module-page amazon-console">
|
||||
<AmazonTopBar />
|
||||
<div
|
||||
class="page-shell module-page amazon-console"
|
||||
:style="{ '--scroll-color': currentGroup?.color ?? '#2dd4bf' }"
|
||||
>
|
||||
<AmazonTopBar :active-cat="activeCat" @change="onCatChange" />
|
||||
|
||||
<main class="console-body">
|
||||
<!-- Hero 宣传区 -->
|
||||
<section class="hero">
|
||||
<div class="hero-inner">
|
||||
<div class="hero-left">
|
||||
<span class="hero-pill">运营辅助 · 数据安全 · 高效跟价</span>
|
||||
<h1 class="hero-title">让亚马逊运营,更清晰高效</h1>
|
||||
<p class="hero-sub">
|
||||
从采集、去重、查品到上架跟价,20 个工具按「前端 · 运营 · 后勤」三线组织,每条工具一句话讲清用途,标准流程一步不缺。
|
||||
</p>
|
||||
<div class="hero-stats">
|
||||
<div class="stat">
|
||||
<strong style="color: #2dd4bf">20</strong>
|
||||
<span>个工具</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<strong style="color: #60a5fa">3</strong>
|
||||
<span>大分类</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<strong style="color: #f59e0b">8</strong>
|
||||
<span>前端标准流程步骤</span>
|
||||
</div>
|
||||
<!-- Hero(仅前端工具分类显示,对齐 gemini-code 主页) -->
|
||||
<section v-if="currentGroup && currentGroup.key === 'front'" class="hero">
|
||||
<div class="hero-left">
|
||||
<span class="hero-pill">运营辅助 · 数据安全 · 高效跟价</span>
|
||||
<h1 class="hero-title">让亚马逊运营,更清晰高效</h1>
|
||||
<p class="hero-sub">
|
||||
从采集、去重、查品到上架跟价,20 个工具按「前端 · 运营 · 后勤」三线组织,每条工具一句话讲清用途,标准流程一步不缺。
|
||||
</p>
|
||||
<div class="hero-stats">
|
||||
<div class="stat">
|
||||
<strong style="color: #2dd4bf">20</strong>
|
||||
<span>个工具</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<strong style="color: #60a5fa">3</strong>
|
||||
<span>大分类</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<strong style="color: #f59e0b">8</strong>
|
||||
<span>前端标准流程步骤</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-card">
|
||||
<div class="hero-card">
|
||||
<div>
|
||||
<div class="hero-card-head">
|
||||
<div class="hero-card-dots">
|
||||
<i class="dot dot--teal"></i>
|
||||
<i class="dot dot--blue"></i>
|
||||
<i class="dot dot--brand"></i>
|
||||
<span class="dots-line"></span>
|
||||
</div>
|
||||
<span class="hero-card-caption">数富AI · 运营工作台</span>
|
||||
</div>
|
||||
<h2 class="hero-card-title">让每一次运营动作,<br />都有清晰的工作流。</h2>
|
||||
<div class="hero-card-divider"></div>
|
||||
<p class="hero-card-sub">内置完整使用教程,从软件配置到运营上架,每一步流程清晰可查。</p>
|
||||
<button type="button" class="hero-card-btn" @click="scrollToTools">查看工具</button>
|
||||
<p class="hero-card-sub">内置完整使用教程,从软件配置到运营上架,</p>
|
||||
</div>
|
||||
<div class="hero-card-btn-row">
|
||||
<span class="hero-card-sub">每一步流程清晰可查。</span>
|
||||
<button class="hero-dl" type="button" @click="onDownloadClick">
|
||||
<span>📥</span>
|
||||
<span>立即下载教程</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 三组工具:标题 + 标准工作流 + 卡片墙 -->
|
||||
<section v-for="group in visibleGroups" :key="group.key" class="tool-group" :id="`group-${group.key}`">
|
||||
<header class="group-head">
|
||||
<i class="group-dot" :style="{ background: group.color }"></i>
|
||||
<span class="group-name">{{ group.name }}</span>
|
||||
<span class="group-desc">{{ group.desc }}</span>
|
||||
<span class="group-count">{{ group.tools.length }} 个工具</span>
|
||||
</header>
|
||||
|
||||
<!-- 标准工作流时间线 -->
|
||||
<section v-if="visibleWorkflow(group)?.length" class="workflow-panel">
|
||||
<!-- 当前分类:标准工作流 + 卡片墙(分类切换式,同 gemini-code) -->
|
||||
<section v-if="currentGroup" class="tool-group" :id="`group-${currentGroup.key}`">
|
||||
<section v-if="visibleWorkflow(currentGroup)?.length" class="workflow-panel">
|
||||
<div class="workflow-head">
|
||||
<span class="workflow-title">{{ group.workflowTitle }}</span>
|
||||
<span class="workflow-badge" :style="{ color: group.color }">{{ group.workflowBadge }}</span>
|
||||
<span class="workflow-hint">{{ group.workflowHint }}</span>
|
||||
<span class="workflow-title">{{ currentGroup.workflowTitle }}</span>
|
||||
<span class="workflow-badge">{{ currentGroup.workflowBadge }}</span>
|
||||
<span class="workflow-hint">{{ currentGroup.workflowHint }}</span>
|
||||
</div>
|
||||
<ol class="workflow-steps">
|
||||
<li
|
||||
v-for="(step, index) in visibleWorkflow(group)"
|
||||
:key="`${group.key}-${index}`"
|
||||
class="workflow-step"
|
||||
:class="{ 'workflow-step--disabled': !step.toolId || !stepHref(step.toolId) }"
|
||||
@click="onWorkflowStepClick(step)"
|
||||
>
|
||||
<span class="step-circle" :style="{ background: group.weak, color: group.color }">{{ index + 1 }}</span>
|
||||
<span class="step-name">{{ step.name }}</span>
|
||||
<span class="step-desc">{{ step.desc }}</span>
|
||||
<span v-if="index < visibleWorkflow(group).length - 1" class="step-line" :style="{ background: group.weak }"></span>
|
||||
</li>
|
||||
<template v-for="(step, index) in visibleWorkflow(currentGroup)" :key="`${currentGroup.key}-${index}`">
|
||||
<li
|
||||
class="workflow-step"
|
||||
:class="{ 'workflow-step--disabled': !step.toolId || !stepHref(step.toolId) }"
|
||||
:style="{ '--gc': currentGroup.color }"
|
||||
@click="onWorkflowStepClick(step)"
|
||||
>
|
||||
<span class="step-circle" :style="circleStyle(currentGroup)">{{ index + 1 }}</span>
|
||||
<span class="step-name">{{ step.name }}</span>
|
||||
<span class="step-desc">{{ step.desc }}</span>
|
||||
</li>
|
||||
<span v-if="index < visibleWorkflow(currentGroup).length - 1" class="step-line" :style="lineStyle()"></span>
|
||||
</template>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<!-- 卡片墙 -->
|
||||
<header class="group-head">
|
||||
<i class="group-dot" :style="{ background: currentGroup.color }"></i>
|
||||
<span class="group-name">{{ currentGroup.name }}</span>
|
||||
<span class="group-desc">{{ currentGroup.desc }}</span>
|
||||
<span class="group-count">{{ currentGroup.tools.length }} 个工具</span>
|
||||
</header>
|
||||
|
||||
<div class="card-grid">
|
||||
<template v-for="tool in group.tools" :key="tool.id">
|
||||
<a v-if="resolveHref(tool.href)" :href="resolveHref(tool.href)!" class="tool-card">
|
||||
<template v-for="tool in currentGroup.tools" :key="tool.id">
|
||||
<router-link v-if="resolveHref(tool.href)" :to="resolveHref(tool.href)!" class="tool-card" :style="cardStyle(currentGroup)">
|
||||
<div class="tool-card-top">
|
||||
<ToolIcon :name="tool.id" />
|
||||
<span v-if="tool.tag" class="tool-tag" :style="{ color: group.color }">{{ tool.tag }}</span>
|
||||
<span v-if="tool.tag" class="tool-tag" :style="tagStyle(currentGroup)">{{ tool.tag }}</span>
|
||||
</div>
|
||||
<div class="tool-card-name">{{ tool.name }}</div>
|
||||
<div class="tool-card-desc">{{ tool.desc }}</div>
|
||||
<div class="tool-card-go">打开工具 →</div>
|
||||
</a>
|
||||
</router-link>
|
||||
|
||||
<button v-else type="button" class="tool-card tool-card--disabled" @click="onPendingClick(tool)">
|
||||
<button v-else type="button" class="tool-card tool-card--disabled" :style="cardStyle(currentGroup)" @click="onPendingClick(tool)">
|
||||
<div class="tool-card-top">
|
||||
<ToolIcon :name="tool.id" />
|
||||
<span v-if="tool.tag" class="tool-tag" :style="{ color: group.color }">{{ tool.tag }}</span>
|
||||
<span v-if="tool.tag" class="tool-tag" :style="tagStyle(currentGroup)">{{ tool.tag }}</span>
|
||||
</div>
|
||||
<div class="tool-card-name">{{ tool.name }}</div>
|
||||
<div class="tool-card-desc">{{ tool.desc }}</div>
|
||||
@@ -112,25 +119,33 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import AmazonTopBar from '@/pages/amazon/components/AmazonTopBar.vue'
|
||||
import AmazonFooterBar from '@/pages/amazon/components/AmazonFooterBar.vue'
|
||||
import ToolIcon from '@/pages/amazon/components/ToolIcon.vue'
|
||||
import { TUTORIAL_DOWNLOAD_URL } from '@/pages/amazon/download-btn'
|
||||
import { filterGroupsByPermission, filterWorkflowByPermission, TOOL_GROUPS, TOOL_MAP } from '@/pages/amazon/tool-catalog'
|
||||
import type { ToolInfo, VisibleGroup, WorkflowStep } from '@/pages/amazon/tool-catalog'
|
||||
import type { ToolGroup, ToolInfo, VisibleGroup, WorkflowStep } from '@/pages/amazon/tool-catalog'
|
||||
import { resolvePageHref } from '@/shared/page-prefix'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
import { getCurrentUserAppColumnKeys } from '@/shared/api/permission'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const allowedKeys = ref<string[]>([])
|
||||
const visibleGroups = ref<VisibleGroup[]>(TOOL_GROUPS.map((group) => ({ ...group, tools: [...group.tools] })))
|
||||
/** 权限接口异常时为 true:展示全部工具与时间线,避免页面空白 */
|
||||
const permissionUnavailable = ref(false)
|
||||
const statusText = ref('')
|
||||
const statusType = ref<'normal' | 'error'>('normal')
|
||||
const activeCat = ref<'front' | 'ops' | 'logi'>('front')
|
||||
let statusTimer: number | undefined
|
||||
|
||||
/** 链接本地化:dev 预览去掉 /new_web_source 前缀;/brand、/web_source/* 等旧静态页仅桌面端存在 */
|
||||
const currentGroup = computed(() => visibleGroups.value.find((g) => g.key === activeCat.value) ?? visibleGroups.value[0])
|
||||
|
||||
/** 链接归一:/new_web_source/xxx.html → /xxx(SPA 路径,见 shared/page-prefix) */
|
||||
function resolveHref(rawHref?: string): string {
|
||||
return resolvePageHref(rawHref)
|
||||
}
|
||||
@@ -145,6 +160,20 @@ function stepHref(toolId?: string) {
|
||||
return resolveHref(TOOL_MAP.get(toolId)?.href ?? '')
|
||||
}
|
||||
|
||||
/* 卡片/tag:weak 弱底 + 分类色文字(同 gemini-code,无核心特判) */
|
||||
function tagStyle(group: ToolGroup) {
|
||||
return { background: group.weak, color: group.color }
|
||||
}
|
||||
function circleStyle(group: ToolGroup) {
|
||||
return { background: group.weak, color: group.color }
|
||||
}
|
||||
function lineStyle() {
|
||||
return { background: '#4C5A75' }
|
||||
}
|
||||
function cardStyle(group: ToolGroup) {
|
||||
return { '--gc': group.color } as Record<string, string>
|
||||
}
|
||||
|
||||
function onWorkflowStepClick(step: WorkflowStep) {
|
||||
if (!step.toolId) {
|
||||
return
|
||||
@@ -152,7 +181,7 @@ function onWorkflowStepClick(step: WorkflowStep) {
|
||||
const target = TOOL_MAP.get(step.toolId)
|
||||
const href = resolveHref(target?.href ?? '')
|
||||
if (href) {
|
||||
window.location.href = href
|
||||
router.push(href)
|
||||
return
|
||||
}
|
||||
showSoon(target?.href ? `${step.name}仅桌面客户端可用` : `${step.name}暂未开通,敬请期待`)
|
||||
@@ -167,31 +196,46 @@ function onPendingClick(tool: ToolInfo) {
|
||||
}
|
||||
|
||||
function showSoon(name: string) {
|
||||
statusText.value = `${name}暂未开通,敬请期待`
|
||||
statusType.value = 'normal'
|
||||
toast(`${name}暂未开通,敬请期待`, 'normal')
|
||||
}
|
||||
|
||||
function toast(msg: string, type: 'normal' | 'error' = 'normal') {
|
||||
statusText.value = msg
|
||||
statusType.value = type
|
||||
if (statusTimer) {
|
||||
window.clearTimeout(statusTimer)
|
||||
}
|
||||
statusTimer = window.setTimeout(() => {
|
||||
statusText.value = ''
|
||||
}, 2200)
|
||||
}, 2500)
|
||||
}
|
||||
|
||||
function scrollToTools() {
|
||||
void nextTick(() => {
|
||||
document.querySelector('.card-grid')?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
})
|
||||
}
|
||||
|
||||
function scrollToHashGroup() {
|
||||
const hash = window.location.hash.replace('#', '')
|
||||
if (!hash) return
|
||||
const target = document.getElementById(hash)
|
||||
if (target) {
|
||||
void nextTick(() => {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
})
|
||||
async function onDownloadClick() {
|
||||
// 教程客户端压缩包取自 MinIO 公开直链(client/tutorial/,见 download-btn.ts)
|
||||
const api = getPywebviewApi()
|
||||
if (api?.open_external_url) {
|
||||
// 桌面端:交系统默认浏览器下载,可看到下载进度
|
||||
try {
|
||||
const res = await api.open_external_url(TUTORIAL_DOWNLOAD_URL)
|
||||
if (!res?.success) toast(`下载失败:${res?.error ?? '请重试'}`, 'error')
|
||||
} catch (_error) {
|
||||
toast('下载失败,请重试', 'error')
|
||||
}
|
||||
return
|
||||
}
|
||||
// 网页端:新窗口打开直链下载
|
||||
window.open(TUTORIAL_DOWNLOAD_URL, '_blank', 'noopener')
|
||||
}
|
||||
|
||||
function onCatChange(key: 'front' | 'ops' | 'logi') {
|
||||
activeCat.value = key
|
||||
history.replaceState(null, '', `#group-${key}`)
|
||||
}
|
||||
|
||||
function parseHashGroup() {
|
||||
const hash = window.location.hash.replace('#', '')
|
||||
const m = /^group-(front|ops|logi)$/.exec(hash)
|
||||
if (m) activeCat.value = m[1] as 'front' | 'ops' | 'logi'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -204,7 +248,7 @@ onMounted(async () => {
|
||||
visibleGroups.value = permissionUnavailable.value
|
||||
? TOOL_GROUPS.map((group) => ({ ...group, tools: [...group.tools] }))
|
||||
: filterGroupsByPermission(TOOL_GROUPS, allowedKeys.value)
|
||||
scrollToHashGroup()
|
||||
parseHashGroup()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -214,7 +258,11 @@ onMounted(async () => {
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
background: #151a25;
|
||||
background-color: #1a1f2e;
|
||||
/* 深色底 + 青色网点纹理(对齐 gemini-code body) */
|
||||
background-image: radial-gradient(rgba(45, 212, 191, 0.08) 1px, transparent 1px);
|
||||
background-size: 48px 48px;
|
||||
font-family: "PingFang SC", "Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", sans-serif;
|
||||
}
|
||||
|
||||
.console-body {
|
||||
@@ -222,23 +270,46 @@ onMounted(async () => {
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding: 22px 34px 44px;
|
||||
background: #151a25;
|
||||
padding: 14px 28px 28px;
|
||||
background: transparent;
|
||||
/* 滚动条滑块随分类色(保持各分类有独立主色) */
|
||||
scrollbar-color: var(--scroll-color, #2dd4bf) #1a1f2e;
|
||||
}
|
||||
|
||||
/* ===== Hero ===== */
|
||||
.console-body::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.console-body::-webkit-scrollbar-track {
|
||||
background: #1a1f2e;
|
||||
}
|
||||
|
||||
.console-body::-webkit-scrollbar-thumb {
|
||||
min-height: 40px;
|
||||
background: var(--scroll-color, #2dd4bf);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.console-body::-webkit-scrollbar-thumb:hover {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
.console-body::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ===== Hero(仅 front 显示;PANEL 底 + LINE 边框 + 12px 圆角,对齐 gemini-code) ===== */
|
||||
.hero {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto 26px;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, #1b2233 0%, #151a24 100%);
|
||||
}
|
||||
|
||||
.hero-inner {
|
||||
max-width: 1440px;
|
||||
margin: 0 auto 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 26px 28px;
|
||||
padding: 26px 30px;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 12px;
|
||||
background: #232a3b;
|
||||
}
|
||||
|
||||
.hero-left {
|
||||
@@ -248,75 +319,78 @@ onMounted(async () => {
|
||||
|
||||
.hero-pill {
|
||||
display: inline-block;
|
||||
padding: 5px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(45, 212, 191, 0.14);
|
||||
margin-bottom: 12px;
|
||||
padding: 4px 12px;
|
||||
border: 1px solid rgba(45, 212, 191, 0.2);
|
||||
border-radius: 20px;
|
||||
background: #0f2e28;
|
||||
color: #2dd4bf;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
margin: 14px 0 0;
|
||||
margin: 0 0 8px;
|
||||
color: #f5f8fc;
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
line-height: 1.3;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
max-width: 620px;
|
||||
margin: 10px 0 0;
|
||||
color: #a0acbe;
|
||||
max-width: 650px;
|
||||
margin: 0 0 20px;
|
||||
color: #c8d2e2;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
display: flex;
|
||||
gap: 44px;
|
||||
margin-top: 22px;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.stat strong {
|
||||
display: block;
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.stat span {
|
||||
display: block;
|
||||
margin-top: 3px;
|
||||
color: #5e6878;
|
||||
font-size: 10px;
|
||||
margin-top: 4px;
|
||||
color: #a0acbe;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Hero 右侧工作台卡 */
|
||||
/* Hero 右侧工作台卡(350 宽 / 10px 圆角,对齐 gemini-code hero-right-card) */
|
||||
.hero-card {
|
||||
width: 300px;
|
||||
width: 350px;
|
||||
flex-shrink: 0;
|
||||
padding: 18px 20px;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 12px;
|
||||
background: #202a3e;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 10px;
|
||||
background: #2b3447;
|
||||
}
|
||||
|
||||
.hero-card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 8px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.hero-card-dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@@ -326,70 +400,189 @@ onMounted(async () => {
|
||||
|
||||
.dot--blue {
|
||||
background: #60a5fa;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.dot--brand {
|
||||
background: #f59e0b;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.dots-line {
|
||||
display: block;
|
||||
width: 42px;
|
||||
height: 2px;
|
||||
margin-top: 14px;
|
||||
background: #3e4a62;
|
||||
}
|
||||
|
||||
.hero-card-caption {
|
||||
color: #5e6878;
|
||||
font-size: 10px;
|
||||
color: #a0acbe;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.hero-card-title {
|
||||
margin: 16px 0 0;
|
||||
margin: 0 0 12px;
|
||||
color: #f5f8fc;
|
||||
font-size: 17px;
|
||||
font-weight: 800;
|
||||
line-height: 1.6;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.hero-card-divider {
|
||||
height: 1px;
|
||||
margin: 14px 0;
|
||||
margin-bottom: 12px;
|
||||
background: #3e4a62;
|
||||
}
|
||||
|
||||
.hero-card-sub {
|
||||
margin: 0;
|
||||
color: #c8d2e2;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 下载按钮(渐变文字按钮,对齐 gemini-code download-btn;下载教学客户端 zip 直链) */
|
||||
.hero-card-btn-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.hero-card-btn-row .hero-card-sub {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hero-dl {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 18px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, #f59e0b, #d97706);
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 2px 10px rgba(245, 158, 11, 0.4);
|
||||
}
|
||||
|
||||
.hero-dl:hover {
|
||||
filter: brightness(1.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* ===== 标准工作流时间线(PANEL 底 + LINE 边框 + 圆形编号,对齐 gemini-code) ===== */
|
||||
.workflow-panel {
|
||||
max-width: 1440px;
|
||||
margin: 0 auto 22px;
|
||||
padding: 18px 24px;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 10px;
|
||||
background: #232a3b;
|
||||
}
|
||||
|
||||
.workflow-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.workflow-title {
|
||||
color: #f5f8fc;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workflow-badge {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background: #3a2a10;
|
||||
color: #f59e0b;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workflow-hint {
|
||||
margin-left: auto;
|
||||
color: #a0acbe;
|
||||
font-size: 11px;
|
||||
line-height: 1.7;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hero-card-btn {
|
||||
margin-top: 14px;
|
||||
padding: 9px 22px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: #f59e0b;
|
||||
color: #fff;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
.workflow-steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.workflow-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
cursor: pointer;
|
||||
transition: background 0.18s ease;
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
|
||||
.hero-card-btn:hover {
|
||||
background: #f7b13c;
|
||||
.workflow-step:not(.workflow-step--disabled):hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* ===== 工具分组 ===== */
|
||||
.workflow-step--disabled {
|
||||
cursor: default;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 悬停:编号圆填充分类色白字、名称变亮(已确认保留的高亮反馈) */
|
||||
.workflow-step:not(.workflow-step--disabled):hover .step-circle {
|
||||
background: var(--gc, #2dd4bf) !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.workflow-step:not(.workflow-step--disabled):hover .step-name {
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.step-circle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
margin-bottom: 2px;
|
||||
color: #c8d2e2;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
color: #a0acbe;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 步骤连接线:圆编号列之间贯穿的横线(垂直居中于编号行,对齐 gemini-code timeline-line) */
|
||||
.step-line {
|
||||
flex: 1 1 auto;
|
||||
min-width: 14px;
|
||||
height: 2px;
|
||||
margin: 0 4px;
|
||||
transform: translateY(-16px);
|
||||
}
|
||||
|
||||
/* ===== 分类标题 + 卡片墙 ===== */
|
||||
.tool-group {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto 34px;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.group-head {
|
||||
@@ -407,177 +600,101 @@ onMounted(async () => {
|
||||
|
||||
.group-name {
|
||||
color: #f5f8fc;
|
||||
font-size: 17px;
|
||||
font-weight: 800;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.group-desc {
|
||||
color: #5e6878;
|
||||
color: #a0acbe;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.group-count {
|
||||
margin-left: auto;
|
||||
color: #5e6878;
|
||||
color: #a0acbe;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* ===== 工作流时间线 ===== */
|
||||
.workflow-panel {
|
||||
margin-bottom: 18px;
|
||||
padding: 16px 22px 20px;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 12px;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.workflow-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workflow-title {
|
||||
color: #f5f8fc;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workflow-badge {
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
background: rgba(245, 158, 11, 0.14);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workflow-hint {
|
||||
margin-left: auto;
|
||||
color: #5e6878;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.workflow-steps {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin: 16px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.workflow-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.workflow-step--disabled {
|
||||
cursor: default;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.step-circle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
margin-top: 8px;
|
||||
color: #c8d2e2;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
margin-top: 2px;
|
||||
color: #5e6878;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-line {
|
||||
flex: 1 1 auto;
|
||||
min-width: 12px;
|
||||
height: 2px;
|
||||
margin-top: 15px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
/* ===== 卡片墙 ===== */
|
||||
/* 卡片墙(CARD 底 + 2px #2A3344 边框 + 10px 圆角 min-height 165,对齐 gemini-code tool-card) */
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
gap: 14px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.tool-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-width: 0;
|
||||
padding: 16px 18px 14px;
|
||||
border: 2px solid #2e3a52;
|
||||
border-radius: 12px;
|
||||
background: #1c2333;
|
||||
min-height: 165px;
|
||||
padding: 16px;
|
||||
border: 2px solid #2a3344;
|
||||
border-radius: 10px;
|
||||
background: #2b3447;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.18s ease, background 0.18s ease;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
/* 悬浮:边框高亮为分类色 + 背景变亮 + 上浮阴影(对齐 gemini-code hover 并按要求加边框高亮) */
|
||||
.tool-card:hover {
|
||||
border-color: #2dd4bf;
|
||||
background: #262f42;
|
||||
background: #323d52;
|
||||
border-color: var(--gc, #2dd4bf);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.tool-card-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.tool-tag {
|
||||
padding: 3px 9px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
font-size: 9px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 悬浮:角标变分类色实底白字(保留的亮反馈) */
|
||||
.tool-card:hover .tool-tag {
|
||||
background: var(--gc, #2dd4bf) !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.tool-card-name {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 6px;
|
||||
color: #f5f8fc;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tool-card-desc {
|
||||
margin-top: 6px;
|
||||
color: #5e6878;
|
||||
flex: 1;
|
||||
color: #a0acbe;
|
||||
font-size: 11px;
|
||||
line-height: 1.6;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tool-card-go {
|
||||
margin-top: 14px;
|
||||
color: #5e6878;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 12px;
|
||||
color: #a0acbe;
|
||||
font-size: 11px;
|
||||
transition: color 0.18s ease;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.tool-card:hover .tool-card-go {
|
||||
color: #2dd4bf;
|
||||
color: var(--gc, #2dd4bf);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -587,31 +704,33 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.tool-card--disabled:hover {
|
||||
border-color: #2e3a52;
|
||||
background: #1c2333;
|
||||
border-color: #2a3344;
|
||||
background: #2b3447;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tool-card--disabled:hover .tool-card-go {
|
||||
color: #5e6878;
|
||||
color: #a0acbe;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* ===== toast ===== */
|
||||
/* ===== toast(右下角 + 品牌色左边条,对齐 gemini-code toast) ===== */
|
||||
.toast {
|
||||
position: fixed;
|
||||
bottom: 40px;
|
||||
left: 50%;
|
||||
bottom: 60px;
|
||||
right: 30px;
|
||||
z-index: 1000;
|
||||
padding: 12px 24px;
|
||||
max-width: min(420px, calc(100vw - 48px));
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
max-width: min(420px, calc(100vw - 60px));
|
||||
padding: 12px 20px;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid #f59e0b;
|
||||
background: #3d4a63;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateX(-50%);
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
@@ -620,6 +739,7 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.toast.error {
|
||||
border-left-color: #f87171;
|
||||
background: rgba(176, 42, 55, 0.92);
|
||||
}
|
||||
|
||||
@@ -628,8 +748,9 @@ onMounted(async () => {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.hero-inner {
|
||||
.hero {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
@@ -637,7 +758,7 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.console-body {
|
||||
padding: 18px 20px 36px;
|
||||
padding: 14px 16px 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user