e248b6e43a
- 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)既有未提交改动一并提交
765 lines
19 KiB
Vue
765 lines
19 KiB
Vue
<template>
|
||
<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(仅前端工具分类显示,对齐 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>
|
||
<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>
|
||
</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>
|
||
</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>
|
||
|
||
<!-- 当前分类:标准工作流 + 卡片墙(分类切换式,同 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">{{ currentGroup.workflowTitle }}</span>
|
||
<span class="workflow-badge">{{ currentGroup.workflowBadge }}</span>
|
||
<span class="workflow-hint">{{ currentGroup.workflowHint }}</span>
|
||
</div>
|
||
<ol class="workflow-steps">
|
||
<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 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="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>
|
||
</router-link>
|
||
|
||
<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="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">{{ tool.href ? '仅桌面客户端可用' : '暂未开通' }}</div>
|
||
</button>
|
||
</template>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<AmazonFooterBar />
|
||
|
||
<div class="toast" :class="{ show: Boolean(statusText), error: statusType === 'error' }">{{ statusText }}</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
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 { 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
|
||
|
||
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)
|
||
}
|
||
|
||
function visibleWorkflow(group: VisibleGroup) {
|
||
if (permissionUnavailable.value) return group.workflow ?? []
|
||
return group.workflow ? filterWorkflowByPermission(group.workflow, allowedKeys.value) : []
|
||
}
|
||
|
||
function stepHref(toolId?: string) {
|
||
if (!toolId) return ''
|
||
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
|
||
}
|
||
const target = TOOL_MAP.get(step.toolId)
|
||
const href = resolveHref(target?.href ?? '')
|
||
if (href) {
|
||
router.push(href)
|
||
return
|
||
}
|
||
showSoon(target?.href ? `${step.name}仅桌面客户端可用` : `${step.name}暂未开通,敬请期待`)
|
||
}
|
||
|
||
function onPendingClick(tool: ToolInfo) {
|
||
if (!tool.href) {
|
||
showSoon(`${tool.name}暂未开通,敬请期待`)
|
||
return
|
||
}
|
||
showSoon(`${tool.name}仅桌面客户端可用`)
|
||
}
|
||
|
||
function showSoon(name: string) {
|
||
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 = ''
|
||
}, 2500)
|
||
}
|
||
|
||
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 () => {
|
||
try {
|
||
allowedKeys.value = await getCurrentUserAppColumnKeys()
|
||
} catch (_error) {
|
||
// 权限接口异常时展示全部工具,避免页面空白;正常时按菜单权限过滤
|
||
permissionUnavailable.value = true
|
||
}
|
||
visibleGroups.value = permissionUnavailable.value
|
||
? TOOL_GROUPS.map((group) => ({ ...group, tools: [...group.tools] }))
|
||
: filterGroupsByPermission(TOOL_GROUPS, allowedKeys.value)
|
||
parseHashGroup()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.amazon-console {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
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 {
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
padding: 14px 28px 28px;
|
||
background: transparent;
|
||
/* 滚动条滑块随分类色(保持各分类有独立主色) */
|
||
scrollbar-color: var(--scroll-color, #2dd4bf) #1a1f2e;
|
||
}
|
||
|
||
.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: 1440px;
|
||
margin: 0 auto 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 24px;
|
||
padding: 26px 30px;
|
||
border: 1px solid #3e4a62;
|
||
border-radius: 12px;
|
||
background: #232a3b;
|
||
}
|
||
|
||
.hero-left {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
}
|
||
|
||
.hero-pill {
|
||
display: inline-block;
|
||
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;
|
||
}
|
||
|
||
.hero-title {
|
||
margin: 0 0 8px;
|
||
color: #f5f8fc;
|
||
font-size: 26px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.hero-sub {
|
||
max-width: 650px;
|
||
margin: 0 0 20px;
|
||
color: #c8d2e2;
|
||
font-size: 12px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.hero-stats {
|
||
display: flex;
|
||
gap: 40px;
|
||
}
|
||
|
||
.stat strong {
|
||
display: block;
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
line-height: 1.1;
|
||
}
|
||
|
||
.stat span {
|
||
display: block;
|
||
margin-top: 4px;
|
||
color: #a0acbe;
|
||
font-size: 11px;
|
||
}
|
||
|
||
/* Hero 右侧工作台卡(350 宽 / 10px 圆角,对齐 gemini-code hero-right-card) */
|
||
.hero-card {
|
||
width: 350px;
|
||
flex-shrink: 0;
|
||
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: 8px;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.hero-card-dots {
|
||
display: flex;
|
||
gap: 5px;
|
||
}
|
||
|
||
.dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.dot--teal {
|
||
background: #2dd4bf;
|
||
}
|
||
|
||
.dot--blue {
|
||
background: #60a5fa;
|
||
}
|
||
|
||
.dot--brand {
|
||
background: #f59e0b;
|
||
}
|
||
|
||
.hero-card-caption {
|
||
color: #a0acbe;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.hero-card-title {
|
||
margin: 0 0 12px;
|
||
color: #f5f8fc;
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.hero-card-divider {
|
||
height: 1px;
|
||
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;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.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: transform 0.15s;
|
||
}
|
||
|
||
.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: 1440px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.group-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin: 0 0 14px;
|
||
}
|
||
|
||
.group-dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.group-name {
|
||
color: #f5f8fc;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.group-desc {
|
||
color: #a0acbe;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.group-count {
|
||
margin-left: auto;
|
||
color: #a0acbe;
|
||
font-size: 11px;
|
||
}
|
||
|
||
/* 卡片墙(CARD 底 + 2px #2A3344 边框 + 10px 圆角 min-height 165,对齐 gemini-code tool-card) */
|
||
.card-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
gap: 14px;
|
||
margin-bottom: 28px;
|
||
}
|
||
|
||
.tool-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
min-width: 0;
|
||
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: all 0.2s;
|
||
}
|
||
|
||
/* 悬浮:边框高亮为分类色 + 背景变亮 + 上浮阴影(对齐 gemini-code hover 并按要求加边框高亮) */
|
||
.tool-card:hover {
|
||
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: center;
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.tool-tag {
|
||
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-bottom: 6px;
|
||
color: #f5f8fc;
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.tool-card-desc {
|
||
flex: 1;
|
||
color: #a0acbe;
|
||
font-size: 11px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.tool-card-go {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 12px;
|
||
color: #a0acbe;
|
||
font-size: 11px;
|
||
transition: color 0.2s;
|
||
}
|
||
|
||
.tool-card:hover .tool-card-go {
|
||
color: var(--gc, #2dd4bf);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.tool-card--disabled {
|
||
cursor: default;
|
||
opacity: 0.55;
|
||
}
|
||
|
||
.tool-card--disabled:hover {
|
||
border-color: #2a3344;
|
||
background: #2b3447;
|
||
transform: none;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.tool-card--disabled:hover .tool-card-go {
|
||
color: #a0acbe;
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* ===== toast(右下角 + 品牌色左边条,对齐 gemini-code toast) ===== */
|
||
.toast {
|
||
position: fixed;
|
||
bottom: 60px;
|
||
right: 30px;
|
||
z-index: 1000;
|
||
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;
|
||
transition: opacity 0.3s;
|
||
}
|
||
|
||
.toast.show {
|
||
opacity: 1;
|
||
}
|
||
|
||
.toast.error {
|
||
border-left-color: #f87171;
|
||
background: rgba(176, 42, 55, 0.92);
|
||
}
|
||
|
||
@media (max-width: 1100px) {
|
||
.card-grid {
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
}
|
||
|
||
.hero {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.hero-card {
|
||
width: 100%;
|
||
}
|
||
|
||
.console-body {
|
||
padding: 14px 16px 24px;
|
||
}
|
||
}
|
||
</style>
|