Files
YwxAppThink/addon/appmall/view/frontend/store/detail.html
T

258 lines
13 KiB
HTML

<!--
* @Author: YwxApp <ywx@ywxapp.cn>
* @Description: 插件详情页(公开,仅中心站)· 参考 FastAdmin 插件市场详情版式
* 布局:Hero 标题区 → 左主内容(功能介绍/预览截图/更新日志) + 右 sticky 信息栏(价格/目录)
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>{$addon.title} - 应用市场</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/assets/layui/css/layui.css" rel="stylesheet">
<style>
:root {
--brand: #4f46e5; --brand-2: #7c3aed; --ink: #1f2329;
--muted: #8a8f99; --line: #eceef2; --bg: #f5f6fa;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;
-webkit-font-smoothing: antialiased; }
a { text-decoration: none; }
.brandbar { height: 56px; display: flex; align-items: center; gap: 10px;
padding: 0 24px; background: #fff; border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 30; }
.brandbar .logo { width: 30px; height: 30px; border-radius: 8px;
background: linear-gradient(135deg, var(--brand), var(--brand-2)); display: grid; place-items: center; color: #fff; font-weight: 700; }
.brandbar .name { font-weight: 600; font-size: 16px; }
.brandbar .nav { margin-left: auto; display: flex; gap: 22px; font-size: 14px; }
.brandbar .nav a { color: var(--muted); transition: color .2s; }
.brandbar .nav a:hover, .brandbar .nav a.on { color: var(--brand); }
.crumb { max-width: 1120px; margin: 16px auto 0; padding: 0 24px; font-size: 13px; color: var(--muted); }
.crumb a { color: var(--muted); }
.crumb a:hover { color: var(--brand); }
/* Hero */
.hero { max-width: 1120px; margin: 14px auto 0; padding: 30px 36px; background: #fff; border: 1px solid var(--line); border-radius: 18px;
display: flex; gap: 24px; align-items: center; position: relative; overflow: hidden; }
.hero::before { content: ""; position: absolute; right: -60px; top: -60px; width: 240px; height: 240px; border-radius: 50%;
background: radial-gradient(circle, rgba(124,58,237,.10), transparent 70%); }
.hero .ava { width: 88px; height: 88px; border-radius: 20px; flex: none; overflow: hidden; background: #f0f1f5;
display: grid; place-items: center; color: var(--brand); font-size: 34px; font-weight: 700; box-shadow: 0 8px 22px rgba(79,70,229,.18); }
.hero .ava img { width: 100%; height: 100%; object-fit: cover; }
.hero .h-main { min-width: 0; flex: 1; }
.hero .h-title { font-size: 26px; font-weight: 700; display: flex; align-items: center; gap: 12px; }
.hero .h-title .ver { font-size: 13px; color: #16a34a; background: rgba(22,163,74,.1); border-radius: 6px; padding: 3px 9px; font-weight: 500; }
.hero .h-desc { color: #5b606b; font-size: 14px; margin-top: 8px; line-height: 1.7; }
.hero .h-actions { display: flex; gap: 12px; margin-top: 16px; flex-wrap: wrap; }
.btn-primary { display: inline-flex; align-items: center; gap: 6px; color: #fff; font-size: 14px; font-weight: 600;
padding: 11px 22px; border-radius: 11px; background: linear-gradient(135deg, var(--brand), var(--brand-2)); transition: .2s, transform .1s; }
.btn-primary:hover { opacity: .92; }
.btn-primary:active { transform: scale(.98); }
.btn-ghost { display: inline-flex; align-items: center; gap: 6px; color: var(--brand); font-size: 14px; font-weight: 600;
padding: 11px 22px; border-radius: 11px; background: #fff; border: 1px solid var(--brand); transition: .2s; }
.btn-ghost:hover { background: rgba(79,70,229,.06); }
/* 主体双栏 */
.main { max-width: 1120px; margin: 24px auto 60px; padding: 0 24px; display: grid; gap: 24px;
grid-template-columns: 1fr 320px; align-items: start; }
.col-right { position: sticky; top: 76px; display: flex; flex-direction: column; gap: 18px; }
.panel { background: #fff; border: 1px solid var(--line); border-radius: 16px; padding: 24px; }
.panel + .panel { margin-top: 24px; }
.panel h3 { margin: 0 0 16px; font-size: 17px; display: flex; align-items: center; gap: 9px; }
.panel h3::before { content: ""; width: 4px; height: 17px; border-radius: 2px; background: linear-gradient(var(--brand), var(--brand-2)); }
.prose { color: #4b515c; font-size: 14px; line-height: 1.9; white-space: pre-wrap; }
/* 截图 */
.cover { width: 100%; aspect-ratio: 16/9; border-radius: 14px; overflow: hidden; background: #f0f1f5;
display: grid; place-items: center; color: var(--brand); font-size: 36px; font-weight: 700; }
.cover img { width: 100%; height: 100%; object-fit: cover; }
.thumbs { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.thumbs img { width: 104px; height: 64px; object-fit: cover; border-radius: 9px; border: 2px solid transparent; cursor: pointer; transition: .18s; }
.thumbs img:hover, .thumbs img.on { border-color: var(--brand); }
/* 更新日志时间线 */
.timeline { position: relative; padding-left: 22px; }
.timeline::before { content: ""; position: absolute; left: 5px; top: 6px; bottom: 6px; width: 2px; background: var(--line); }
.tl-item { position: relative; padding-bottom: 22px; }
.tl-item:last-child { padding-bottom: 0; }
.tl-item::before { content: ""; position: absolute; left: -21px; top: 5px; width: 12px; height: 12px; border-radius: 50%;
background: #fff; border: 3px solid var(--brand); }
.tl-item.first::before { background: var(--brand); box-shadow: 0 0 0 4px rgba(79,70,229,.18); }
.tl-head { display: flex; align-items: center; gap: 10px; }
.tl-ver { font-weight: 700; font-size: 14px; }
.tl-badge { font-size: 11px; color: #16a34a; background: rgba(22,163,74,.1); border-radius: 6px; padding: 2px 8px; }
.tl-date { color: var(--muted); font-size: 12px; margin-left: auto; }
.tl-note { color: #5b606b; font-size: 13px; line-height: 1.7; margin-top: 6px; white-space: pre-wrap; }
/* 右栏:价格卡 + 信息 + 目录 */
.price-card { background: linear-gradient(135deg, rgba(79,70,229,.07), rgba(124,58,237,.07));
border: 1px solid rgba(124,58,237,.18); border-radius: 14px; padding: 18px 20px; }
.price-card .price { font-size: 30px; font-weight: 800; color: var(--brand); }
.price-card .price.free { color: #16a34a; }
.price-card .sub { color: var(--muted); font-size: 12px; margin-top: 2px; }
.buy-btn { display: block; text-align: center; color: #fff; font-weight: 600; font-size: 15px; padding: 13px;
border-radius: 11px; background: linear-gradient(135deg, var(--brand), var(--brand-2)); margin-top: 14px; transition: .2s, transform .1s; }
.buy-btn:hover { opacity: .92; }
.buy-btn:active { transform: scale(.98); }
.buy-btn.ghost { background: #fff; color: var(--brand); border: 1px solid var(--brand); margin-top: 10px; }
.kv { font-size: 13px; }
.kv .row { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px dashed var(--line); }
.kv .row:last-child { border-bottom: 0; }
.kv .k { color: var(--muted); }
.kv .v { color: var(--ink); text-align: right; }
.stars { color: #f5a623; letter-spacing: 1px; }
.stars .off { color: #e2e4ea; }
.tags { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.tags span { font-size: 12px; color: var(--brand); background: rgba(79,70,229,.1); border-radius: 6px; padding: 4px 10px; }
.toc { font-size: 13px; }
.toc .t { color: var(--muted); font-size: 12px; margin-bottom: 8px; }
.toc a { display: block; color: #5b606b; padding: 7px 0; border-left: 2px solid transparent; padding-left: 12px; transition: .18s; }
.toc a:hover { color: var(--brand); border-left-color: var(--brand); }
footer { text-align: center; color: var(--muted); font-size: 12px; padding: 26px; }
@media (max-width: 900px) {
.main { grid-template-columns: 1fr; }
.col-right { position: static; }
}
</style>
</head>
<body>
<div class="brandbar">
<div class="logo">Y</div>
<div class="name">{$market_name}</div>
<div class="nav">
<a href="/appmall/store" class="on">应用市场</a>
<a href="/appmall/framework">主框架</a>
</div>
</div>
<div class="crumb">
<a href="/appmall/store">应用市场</a> / <span>{$addon.title}</span>
</div>
<!-- Hero -->
<div class="hero">
<div class="ava">
{if $addon.logo}<img src="{$addon.logo}" alt="{$addon.title}">{else}{$addon.title|substr=0,1}{/if}
</div>
<div class="h-main">
<div class="h-title">{$addon.title}<span class="ver">v{$addon.version}</span></div>
<div class="h-desc">{if $addon.author}by {$addon.author} · {/if}{$addon.category|default='未分类'}</div>
<div class="h-actions">
<a class="btn-primary" href="#"><i class="layui-icon layui-icon-play"></i> 立即体验</a>
<a class="btn-ghost" href="#"><i class="layui-icon layui-icon-cart"></i> 立即购买</a>
</div>
</div>
</div>
<div class="main">
<!-- 左主内容 -->
<div class="col-left">
<div class="panel" id="intro">
<h3>功能介绍</h3>
<div class="prose">{$addon.intro|default='暂无介绍'}</div>
</div>
{if $addon.screenshots}
<div class="panel" id="shots">
<h3>预览截图</h3>
<div class="cover" id="cover">
<img src="{$addon.screenshots.0}" alt="{$addon.title}">
</div>
{if count($addon.screenshots) > 1}
<div class="thumbs" id="thumbs">
{foreach $addon.screenshots as $s}
<img src="{$s}" class="{if $s == $addon.screenshots.0}on{/if}" onclick="swap('{$s}', this)">
{/foreach}
</div>
{/if}
</div>
{/if}
<div class="panel" id="log">
<h3>更新日志</h3>
{if empty($addon.versions)}
<div class="prose" style="color:var(--muted)">暂无版本记录</div>
{else}
<div class="timeline">
{foreach $addon.versions as $k=>$v}
<div class="tl-item {if $k == 0}first{/if}">
<div class="tl-head">
<span class="tl-ver">v{$v.version}</span>
{if $k == 0}<span class="tl-badge">最新版本</span>{/if}
<span class="tl-date">{$v.update_fmt}</span>
</div>
<div class="tl-note">本版本定价:{$v.price_text}</div>
</div>
{/foreach}
</div>
{/if}
</div>
</div>
<!-- 右 sticky 信息栏 -->
<div class="col-right">
<div class="panel">
<div class="kv">
<div class="row"><span class="k">当前版本</span><span class="v">v{$addon.version}</span></div>
<div class="row"><span class="k">更新时间</span><span class="v">{$addon.update_fmt}</span></div>
<div class="row"><span class="k">下载量</span><span class="v">{$addon.downloads}</span></div>
<div class="row"><span class="k">评分</span>
<span class="v">
<span class="stars">
{for start="1" end="6" name="i"}
{if $addon.rating >= $i}<i class="layui-icon layui-icon-rate-solid"></i>{else/}<i class="layui-icon layui-icon-rate off"></i>{/if}
{/for}
</span>
{$addon.rating}
</span>
</div>
<div class="row"><span class="k">开发者</span><span class="v">{$addon.author|default='官方'}</span></div>
<div class="row"><span class="k">分类</span><span class="v">{$addon.category|default='未分类'}</span></div>
</div>
{if $addon.tags}
<div class="tags">
{foreach explode(',', $addon.tags) as $t}
<span>{$t}</span>
{/foreach}
</div>
{/if}
</div>
<div class="panel">
<div class="toc">
<div class="t">本页目录</div>
<a href="#intro">功能介绍</a>
{if $addon.screenshots}<a href="#shots">预览截图</a>{/if}
<a href="#log">更新日志</a>
</div>
</div>
</div>
</div>
<footer>© {:date('Y')} {$market_name} · 由 YwxApp 框架强力驱动</footer>
<script>
function swap(src, el) {
document.querySelector('#cover img').src = src;
document.querySelectorAll('#thumbs img').forEach(function (im) { im.classList.remove('on'); });
el.classList.add('on');
}
// 锚点平滑滚动 + 目录高亮
document.querySelectorAll('.toc a').forEach(function (a) {
a.addEventListener('click', function (e) {
var id = this.getAttribute('href');
var target = document.querySelector(id);
if (target) { e.preventDefault(); target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
});
});
</script>
</body>
</html>