Files
YwxAppThink/addon/appmall/view/frontend/developer/console.html
T

682 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
* @Author: YwxApp <ywx@ywxapp.cn>
* @Description: 开发者中心控制台(Discuz! 应用中心风格:顶栏 + 左侧菜单 + 内容区)
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>开发者中心 - YwxApp 应用市场</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/assets/layui/css/layui.css" rel="stylesheet">
<link href="/assets/ywxapp/css/wxapp.css" rel="stylesheet">
<style>
:root {
--brand: #1677ff;
--brand-dark: #0e5fd8;
--bg: #f0f2f5;
--card: #fff;
--text: #1f2329;
--muted: #8a919f;
--line: #ebedf0;
--ok: #18a058;
--warn: #d48806;
--danger: #d4380d;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text); font-family: -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif; font-size: 14px; }
a { color: var(--brand); text-decoration: none; }
/* 顶栏 */
.topbar { height: 56px; background: #fff; border-bottom: 1px solid var(--line); display: flex; align-items: center; padding: 0 22px; position: sticky; top: 0; z-index: 10; }
.topbar .brand { font-size: 17px; font-weight: 700; color: var(--text); display: flex; align-items: center; gap: 8px; }
.topbar .brand .logo { width: 26px; height: 26px; border-radius: 6px; background: linear-gradient(135deg, var(--brand), #36cfc9); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 15px; }
.topbar .brand small { font-weight: 400; color: var(--muted); font-size: 13px; margin-left: 2px; }
.topbar .spacer { flex: 1; }
.topbar .who { color: var(--muted); font-size: 13px; margin-right: 14px; }
.topbar .who b { color: var(--text); }
/* 布局 */
.layout { display: flex; min-height: calc(100vh - 56px); }
.sidebar { width: 210px; background: #fff; border-right: 1px solid var(--line); padding: 14px 0; flex-shrink: 0; }
.sidebar .menu { list-style: none; margin: 0; padding: 0; }
.sidebar .menu li { padding: 12px 22px; cursor: pointer; color: #4e5969; display: flex; align-items: center; gap: 10px; font-size: 14px; border-left: 3px solid transparent; transition: all .15s; }
.sidebar .menu li .ic { font-size: 16px; width: 18px; text-align: center; }
.sidebar .menu li:hover { background: #f7f9fc; color: var(--brand); }
.sidebar .menu li.active { background: #eef4ff; color: var(--brand); border-left-color: var(--brand); font-weight: 600; }
.content { flex: 1; padding: 22px 26px; min-width: 0; }
.panel { display: none; }
.panel.active { display: block; animation: fade .2s; }
@keyframes fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.page-title { font-size: 18px; font-weight: 700; margin: 0 0 4px; }
.page-sub { color: var(--muted); margin: 0 0 18px; font-size: 13px; }
/* 卡片/统计 */
.stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 18px; }
.stat-card { background: var(--card); border-radius: 10px; padding: 16px 18px; box-shadow: 0 1px 3px rgba(0,0,0,.04); }
.stat-card .lbl { color: var(--muted); font-size: 13px; }
.stat-card .num { font-size: 26px; font-weight: 700; margin-top: 6px; }
.stat-card .num.ok { color: var(--ok); }
.stat-card .num.brand { color: var(--brand); }
.card { background: var(--card); border-radius: 10px; padding: 18px 20px; box-shadow: 0 1px 3px rgba(0,0,0,.04); margin-bottom: 16px; }
.card h3 { margin: 0 0 14px; font-size: 15px; display: flex; align-items: center; gap: 8px; }
.card h3 .bar { width: 4px; height: 15px; background: var(--brand); border-radius: 2px; display: inline-block; }
/* 表格 */
table.dev-table { width: 100%; border-collapse: collapse; font-size: 13px; }
table.dev-table th, table.dev-table td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--line); }
table.dev-table th { color: var(--muted); font-weight: 500; background: #fafbfc; }
table.dev-table tr:hover td { background: #fafcff; }
.app-name { font-weight: 600; }
.app-meta { color: var(--muted); font-size: 12px; margin-top: 2px; }
.tag { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 12px; }
.tag.up { background: #e8f7ee; color: var(--ok); }
.tag.down { background: #f1f2f4; color: var(--muted); }
.tag.wait { background: #fff7e6; color: var(--warn); }
.tag.reject { background: #fff1f0; color: var(--danger); }
.tag.free { background: #eef4ff; color: var(--brand); }
.tag.t-addon { background: #eef4ff; color: var(--brand); }
.tag.t-tpl { background: #f4eefe; color: #722ed1; }
.empty { text-align: center; color: var(--muted); padding: 40px 0; }
/* 令牌闸门 */
.gate { max-width: 420px; margin: 12vh auto 0; background: #fff; border-radius: 12px; padding: 34px 36px; box-shadow: 0 4px 20px rgba(0,0,0,.08); }
.gate h2 { margin: 0 0 6px; text-align: center; }
.gate p { text-align: center; color: var(--muted); margin: 0 0 22px; font-size: 13px; }
.btn { display: inline-flex; align-items: center; gap: 6px; }
.layui-btn { border-radius: 6px; }
.toolbar { margin-bottom: 14px; display: flex; justify-content: space-between; align-items: center; }
.copybox { background: #f6f8fa; border: 1px dashed #d0d7de; border-radius: 6px; padding: 10px 12px; font-family: monospace; word-break: break-all; margin: 8px 0; }
.kv { display: flex; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.kv:last-child { border-bottom: none; }
.kv .k { width: 110px; color: var(--muted); flex-shrink: 0; }
.kv .v { flex: 1; word-break: break-all; }
.hint { color: var(--muted); font-size: 12px; margin-top: 6px; }
.hidden { display: none !important; }
.act-link { color: var(--brand); cursor: pointer; }
@media (max-width: 880px) { .stat-row { grid-template-columns: repeat(2, 1fr); } .sidebar { width: 64px; } .sidebar .menu li span.txt { display: none; } }
</style>
</head>
<body>
<!-- 顶栏 -->
<div class="topbar" id="topbar">
<div class="brand"><span class="logo">Y</span>YwxApp 应用市场<small>开发者中心</small></div>
<div class="spacer"></div>
<div class="who" id="who"></div>
<a href="javascript:;" class="layui-btn layui-btn-sm layui-btn-primary" id="logoutBtn">退出</a>
</div>
<!-- 令牌闸门 -->
<div id="gate" class="gate hidden">
<h2>开发者令牌登录</h2>
<p>请输入你的开发者令牌以进入控制台(可在「查询我的令牌」页取回)</p>
<form class="layui-form" id="gateForm">
<div class="layui-form-item">
<input type="text" name="token" id="gateToken" required lay-verify="required" placeholder="粘贴开发者令牌" class="layui-input">
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid layui-btn-normal" lay-submit lay-filter="gate">进入控制台</button>
</div>
</form>
<p class="hint" style="text-align:center;">
还没有令牌?<a href="{:url('index')}">立即申请入驻</a> ·
<a href="{:url('mine')}">找回令牌</a>
</p>
</div>
<!-- 控制台主体 -->
<div class="layout hidden" id="console">
<div class="sidebar">
<ul class="menu" id="menu">
<li data-tab="overview" class="active"><span class="ic layui-icon layui-icon-console"></span><span class="txt">概览</span></li>
<li data-tab="apps"><span class="ic layui-icon layui-icon-app"></span><span class="txt">我的应用</span></li>
<li data-tab="submit"><span class="ic layui-icon layui-icon-upload"></span><span class="txt">提交应用</span></li>
<li data-tab="earn"><span class="ic layui-icon layui-icon-rmb"></span><span class="txt">收益账单</span></li>
<li data-tab="withdraw"><span class="ic layui-icon layui-icon-transfer"></span><span class="txt">提现</span></li>
<li data-tab="account"><span class="ic layui-icon layui-icon-user"></span><span class="txt">账户设置</span></li>
</ul>
</div>
<div class="content">
<!-- 概览 -->
<section class="panel active" id="panel-overview">
<h2 class="page-title">概览</h2>
<p class="page-sub">你的应用与市场表现一览</p>
<div class="stat-row" id="statRow">
<div class="stat-card"><div class="lbl">已上架应用</div><div class="num brand" id="stApps">-</div></div>
<div class="stat-card"><div class="lbl">待审核提交</div><div class="num warn" id="stPending">-</div></div>
<div class="stat-card"><div class="lbl">总下载量</div><div class="num" id="stDl">-</div></div>
<div class="stat-card"><div class="lbl">可提现余额</div><div class="num ok" id="stBalance">-</div></div>
</div>
<div class="card">
<h3><span class="bar"></span>近期提交</h3>
<table class="dev-table">
<thead><tr><th>应用</th><th>版本</th><th>状态</th><th>时间</th></tr></thead>
<tbody id="recentBody"><tr><td colspan="4" class="empty">加载中…</td></tr></tbody>
</table>
</div>
</section>
<!-- 我的应用 -->
<section class="panel" id="panel-apps">
<h2 class="page-title">我的应用</h2>
<p class="page-sub">查看你已上架/下架的应用,以及待审核的提交记录</p>
<div class="toolbar">
<span class="hint">如需发布新版本,请前往「提交应用」上传 zip 包</span>
<a href="javascript:;" class="layui-btn layui-btn-sm layui-btn-normal" data-goto="submit">+ 提交新应用</a>
</div>
<div class="card">
<h3><span class="bar"></span>已上架 / 下架应用</h3>
<table class="dev-table">
<thead><tr><th>应用</th><th>版本</th><th>价格</th><th>下载</th><th>状态</th><th>更新时间</th><th>操作</th></tr></thead>
<tbody id="listedBody"><tr><td colspan="7" class="empty">加载中…</td></tr></tbody>
</table>
</div>
<div class="card">
<h3><span class="bar"></span>提交记录</h3>
<table class="dev-table">
<thead><tr><th>应用</th><th>版本</th><th>状态</th><th>说明</th><th>更新时间</th></tr></thead>
<tbody id="subBody"><tr><td colspan="5" class="empty">加载中…</td></tr></tbody>
</table>
</div>
</section>
<!-- 提交应用 -->
<section class="panel" id="panel-submit">
<h2 class="page-title">商品提交</h2>
<p class="page-sub" id="submitSub">上传插件 zip 包(根目录需含 info.php),提交后等待官方审核</p>
<div class="card">
<form class="layui-form" id="submitForm">
<div class="layui-form-item">
<label class="layui-form-label">应用类型</label>
<div class="layui-input-inline" style="width:200px;">
<select name="type" id="appType">
<option value="addon">插件</option>
<option value="template">模板</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">插件含 info.php;模板含 templates/名称/template.json</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">插件标识</label>
<div class="layui-input-inline" style="width:260px;">
<input type="text" name="name" required lay-verify="required" placeholder="如 mqttbroker(仅字母数字下划线)" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux" id="nameHint">与 info.php 中 name 一致</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">版本号</label>
<div class="layui-input-inline" style="width:200px;">
<input type="text" name="version" required lay-verify="required" placeholder="如 1.0.0" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">格式 x.y.z,且未重复提交</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">应用名称</label>
<div class="layui-input-block">
<input type="text" name="title" required lay-verify="required" placeholder="如 MQTT 消息代理" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">作者</label>
<div class="layui-input-block">
<input type="text" name="author" placeholder="开发者/工作室名称" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">分类</label>
<div class="layui-input-inline" style="width:200px;">
<select name="category">
<option value="工具">工具</option>
<option value="电商">电商</option>
<option value="社交">社交</option>
<option value="内容">内容</option>
<option value="教育">教育</option>
<option value="其他">其他</option>
</select>
</div>
<label class="layui-form-label">价格(¥)</label>
<div class="layui-input-inline" style="width:140px;">
<input type="number" name="price" value="0" step="0.01" min="0" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">标签</label>
<div class="layui-input-block">
<input type="text" name="tags" placeholder="逗号分隔,如 物联网,通信" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">封面图URL</label>
<div class="layui-input-block">
<input type="text" name="logo" placeholder="可选,应用封面图地址" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea name="description" placeholder="应用简介/功能描述" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">更新日志</label>
<div class="layui-input-block">
<textarea name="changelog" placeholder="如:1.0.1 修复某某问题;1.0.0 首次发布" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">最低框架版本</label>
<div class="layui-input-block">
<input type="text" name="require_framework" placeholder="如 1.2.0(留空表示不限)" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">插件包</label>
<div class="layui-input-block">
<input type="file" name="file" id="pkgFile" accept=".zip" required lay-verify="required" class="layui-input">
<div class="hint" id="pkgHint">仅支持 .zip,插件包根目录需含 info.php,将进行安全扫描</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="doSubmit">提交审核</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</section>
<!-- 收益账单 -->
<section class="panel" id="panel-earn">
<h2 class="page-title">收益账单</h2>
<p class="page-sub">查看销售收益与结算情况</p>
<div class="stat-row">
<div class="stat-card"><div class="lbl">可提现余额</div><div class="num ok" id="eaBalance">¥0.00</div></div>
<div class="stat-card"><div class="lbl">冻结中(提现待打款)</div><div class="num warn" id="eaFrozen">¥0.00</div></div>
</div>
<div class="card">
<h3><span class="bar"></span>收益明细</h3>
<table class="dev-table">
<thead><tr><th>订单号</th><th>销售额</th><th>抽成</th><th>应得</th><th>状态</th><th>时间</th></tr></thead>
<tbody id="revBody"><tr><td colspan="6" class="empty">加载中…</td></tr></tbody>
</table>
</div>
</section>
<!-- 提现 -->
<section class="panel" id="panel-withdraw">
<h2 class="page-title">提现</h2>
<p class="page-sub">将可提现余额提现至你的收款账号</p>
<div class="card" style="max-width:560px;">
<form class="layui-form" id="withdrawForm">
<div class="layui-form-item">
<label class="layui-form-label">提现金额</label>
<div class="layui-input-inline" style="width:200px;">
<input type="number" name="amount" step="0.01" min="0.01" required lay-verify="required" placeholder="0.00" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">不可超过可提现余额</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">提现渠道</label>
<div class="layui-input-inline" style="width:200px;">
<select name="channel">
<option value="alipay">支付宝</option>
<option value="wechat">微信</option>
<option value="bank">银行卡</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">收款账号</label>
<div class="layui-input-block">
<input type="text" name="account" required lay-verify="required" placeholder="支付宝/微信账号或银行卡号" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="doWithdraw">提交提现申请</button>
</div>
</div>
</form>
</div>
</section>
<!-- 账户设置 -->
<section class="panel" id="panel-account">
<h2 class="page-title">账户设置</h2>
<p class="page-sub">管理你的开发者身份与令牌</p>
<div class="card" style="max-width:620px;">
<div class="kv"><div class="k">开发者名称</div><div class="v" id="acName">-</div></div>
<div class="kv"><div class="k">邮箱</div><div class="v" id="acEmail">-</div></div>
<div class="kv"><div class="k">账户状态</div><div class="v" id="acStatus">-</div></div>
<div class="kv"><div class="k">开发者令牌</div><div class="v">
<div class="copybox" id="acToken">-</div>
<button class="layui-btn layui-btn-sm layui-btn-normal" id="copyToken">复制令牌</button>
<span class="hint">发布插件到官方市场时需使用此令牌(APPMARKET_DEV_TOKEN)。</span>
</div></div>
<div class="kv"><div class="k">操作</div><div class="v">
<a href="{:url('mine')}">丢失令牌?前往找回</a>
</div></div>
</div>
</section>
</div>
</div>
<script src="/assets/layui/layui.js"></script>
<script src="/assets/ywxapp/ywxapp.js" module="{$site.app}"></script>
<script>
layui.use(['form', 'jquery', 'layer', 'http'], function () {
var form = layui.form, $ = layui.jquery, layer = layui.layer, http = layui.http;
var TOKEN_KEY = 'appmall_dev_token';
var token = '';
// ---- 工具 ----
function getParam(name) {
var m = location.search.match(new RegExp('[?&]' + name + '=([^&]*)'));
return m ? decodeURIComponent(m[1]) : '';
}
function maskToken(t) {
if (!t || t.length < 12) return t;
return t.substr(0, 6) + '****' + t.substr(-4);
}
function money(v) { return '¥' + (parseFloat(v || 0)).toFixed(2); }
function fmtTime(ts) {
if (!ts) return '-';
var d = new Date(ts * 1000);
return d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2)
+ ' ' + ('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2);
}
// Result 系接口成功 code=0Market::submit 成功 code=1
function req(url, data, successCode) {
successCode = (successCode === undefined) ? 0 : successCode;
return http.post(url, data).then(function (r) {
if (r && r.code === successCode) return r;
return Promise.reject((r && r.message) || (r && r.msg) || '请求失败');
});
}
// ---- 令牌闸门 ----
function showGate() { $('#gate').removeClass('hidden'); $('#console').addClass('hidden'); $('#topbar').addClass('hidden'); }
function enterConsole(tk) {
token = tk;
try { localStorage.setItem(TOKEN_KEY, tk); } catch (e) {}
$('#gate').addClass('hidden');
$('#topbar').removeClass('hidden');
$('#console').removeClass('hidden');
loadOverview();
loadAccount();
}
// 初始化:URL > localStorage
var initToken = getParam('token') || (function () { try { return localStorage.getItem(TOKEN_KEY) || ''; } catch (e) { return ''; } })();
if (!initToken) {
showGate();
} else {
// 校验令牌有效性
req('/appmall/developer/overview', { token: initToken }).then(function (r) {
enterConsole(initToken);
}).catch(function (msg) {
showGate();
layer.msg(msg || '令牌无效', { icon: 2 });
});
}
form.on('submit(gate)', function (data) {
var tk = (data.field.token || '').trim();
if (!tk) { layer.msg('请输入令牌', { icon: 2 }); return false; }
layer.load(1);
req('/appmall/developer/overview', { token: tk }).then(function () {
layer.closeAll();
enterConsole(tk);
}).catch(function (msg) {
layer.closeAll();
layer.msg(msg || '令牌无效', { icon: 2 });
});
return false;
});
$('#logoutBtn').on('click', function () {
try { localStorage.removeItem(TOKEN_KEY); } catch (e) {}
location.href = '{:url("index")}';
});
// ---- 左侧菜单 ----
$('#menu').on('click', 'li', function () {
var tab = $(this).data('tab');
$('#menu li').removeClass('active');
$(this).addClass('active');
$('.panel').removeClass('active');
$('#panel-' + tab).addClass('active');
if (tab === 'overview') loadOverview();
else if (tab === 'apps') loadApps();
else if (tab === 'earn') loadEarnings();
else if (tab === 'account') loadAccount();
});
$('[data-goto]').on('click', function () {
var tab = $(this).data('goto');
$('#menu li[data-tab="' + tab + '"]').click();
});
// ---- 概览 ----
function loadOverview() {
req('/appmall/developer/overview', { token: token }).then(function (r) {
var d = r.data || {};
$('#who').html('欢迎,<b>' + (d.username || '开发者') + '</b>');
$('#stApps').text(d.app_count || 0);
$('#stPending').text(d.pending_count || 0);
$('#stDl').text(d.total_downloads || 0);
$('#stBalance').text(money(d.balance));
var rows = d.recent || [];
if (!rows.length) {
$('#recentBody').html('<tr><td colspan="4" class="empty">暂无提交记录</td></tr>');
return;
}
var html = '';
rows.forEach(function (it) {
var st = subStatus(it.status);
html += '<tr><td class="app-name">' + esc(it.title) + typeTag(it.type) + '</td><td>' + esc(it.version) + '</td><td>' + st + '</td><td>' + fmtTime(it.update_at) + '</td></tr>';
});
$('#recentBody').html(html);
}).catch(function (msg) { layer.msg(msg, { icon: 2 }); });
}
// ---- 我的应用 ----
function loadApps() {
req('/appmall/developer/apps', { token: token }).then(function (r) {
var d = r.data || {};
var listed = d.listed || [], subs = d.submissions || [];
if (!listed.length) {
$('#listedBody').html('<tr><td colspan="7" class="empty">你还没有已上架的应用</td></tr>');
} else {
var h = '';
listed.forEach(function (it) {
var price = (parseFloat(it.price) > 0) ? money(it.price) : '<span class="tag free">免费</span>';
var st = it.status == 1
? '<span class="tag up">上架中</span>'
: '<span class="tag down">已下架</span>';
h += '<tr>'
+ '<td class="app-name">' + esc(it.title) + typeTag(it.type) + '<div class="app-meta">' + esc(it.name) + (it.category ? ' · ' + esc(it.category) : '') + '</div></td>'
+ '<td>' + esc(it.version) + '</td>'
+ '<td>' + price + '</td>'
+ '<td>' + (it.download_count || 0) + '</td>'
+ '<td>' + st + '</td>'
+ '<td>' + fmtTime(it.update_at) + '</td>'
+ '<td><a class="act-link" data-act="price" data-name="' + esc(it.name) + '" data-price="' + (it.price || 0) + '">改价</a></td></tr>';
});
$('#listedBody').html(h);
}
if (!subs.length) {
$('#subBody').html('<tr><td colspan="5" class="empty">暂无提交记录</td></tr>');
} else {
var h2 = '';
subs.forEach(function (it) {
var st = subStatus(it.status);
var note = it.status == 2 ? (it.reject_reason ? esc(it.reject_reason) : '已驳回,可重新提交') : '-';
h2 += '<tr>'
+ '<td class="app-name">' + esc(it.title) + typeTag(it.type) + '<div class="app-meta">' + esc(it.name) + '</div></td>'
+ '<td>' + esc(it.version) + '</td>'
+ '<td>' + st + '</td>'
+ '<td>' + note + '</td>'
+ '<td>' + fmtTime(it.update_at) + '</td></tr>';
});
$('#subBody').html(h2);
}
}).catch(function (msg) { layer.msg(msg, { icon: 2 }); });
}
// 改价(无需重新打包/重新提审,同步该应用全部版本,市场即时生效)
$('#listedBody').on('click', '[data-act="price"]', function () {
var name = $(this).data('name');
var cur = $(this).data('price') || 0;
layer.prompt({ title: '修改「' + name + '」价格(0 为免费,同步全部版本)', value: String(cur) }, function (val, idx) {
var p = parseFloat(val);
if (isNaN(p) || p < 0) { layer.msg('请输入不小于 0 的价格', { icon: 2 }); return; }
req('/appmall/developer/price', { token: token, name: name, price: p }).then(function (r) {
layer.close(idx);
layer.msg(r.message || '价格已更新', { icon: 1 });
loadApps();
}).catch(function (msg) { layer.msg(msg, { icon: 2 }); });
});
});
function subStatus(s) {
if (s == 0) return '<span class="tag wait">待审核</span>';
if (s == 1) return '<span class="tag up">已通过</span>';
if (s == 2) return '<span class="tag reject">已驳回</span>';
return '<span class="tag down">未知</span>';
}
function typeTag(t) {
return t === 'template'
? ' <span class="tag t-tpl">模板</span>'
: ' <span class="tag t-addon">插件</span>';
}
// 提交类型切换:动态改写包要求与标识提示
$('#appType').on('change', function () {
var isTpl = $(this).val() === 'template';
$('#submitSub').text(isTpl
? '上传模板 zip 包(须含 templates/名称/template.json),提交后等待官方审核'
: '上传插件 zip 包(根目录需含 info.php),提交后等待官方审核');
$('#pkgHint').text(isTpl
? '仅支持 .zip,模板包须含 templates/<名称>/template.json,将进行安全扫描(模板包禁含 PHP 文件)'
: '仅支持 .zip,插件包根目录需含 info.php,将进行安全扫描');
$('#nameHint').text(isTpl ? '与 template.json 中标识(文件夹名)一致' : '与 info.php 中 name 一致');
});
// ---- 提交应用 ----
form.on('submit(doSubmit)', function () {
var file = document.getElementById('pkgFile').files[0];
if (!file) { layer.msg('请选择插件 zip 包', { icon: 2 }); return false; }
var fd = new FormData();
fd.append('file', file);
fd.append('token', token);
fd.append('type', $('select[name="type"]').val());
fd.append('name', $('input[name="name"]').val());
fd.append('version', $('input[name="version"]').val());
fd.append('title', $('input[name="title"]').val());
fd.append('author', $('input[name="author"]').val());
fd.append('category', $('select[name="category"]').val());
fd.append('tags', $('input[name="tags"]').val());
fd.append('price', $('input[name="price"]').val());
fd.append('logo', $('input[name="logo"]').val());
fd.append('description', $('textarea[name="description"]').val());
fd.append('changelog', $('textarea[name="changelog"]').val());
fd.append('require_framework', $('input[name="require_framework"]').val());
layer.load(1);
http.upload('/appmall/api/addon/submit', file, {
token: token,
type: $('select[name="type"]').val(),
name: $('input[name="name"]').val(),
version: $('input[name="version"]').val(),
title: $('input[name="title"]').val(),
author: $('input[name="author"]').val(),
category: $('select[name="category"]').val(),
tags: $('input[name="tags"]').val(),
price: $('input[name="price"]').val(),
logo: $('input[name="logo"]').val(),
description: $('textarea[name="description"]').val()
}).then(function (r) {
layer.closeAll();
if (r && r.code === 1) {
layer.msg(r.msg || '提交成功,等待审核', { icon: 1 });
$('#submitForm')[0].reset();
form.render();
} else {
layer.msg((r && (r.msg || r.message)) || '提交失败', { icon: 2 });
}
}).catch(function () { layer.closeAll(); layer.msg('请求失败', { icon: 2 }); });
return false;
});
// ---- 收益 ----
function loadEarnings() {
req('/appmall/developer/earnings', { token: token }).then(function (r) {
var d = r.data || {};
$('#eaBalance').text(money(d.balance));
$('#eaFrozen').text(money(d.frozen_balance));
var list = d.revenues || [];
if (!list.length) {
$('#revBody').html('<tr><td colspan="6" class="empty">暂无收益记录</td></tr>');
return;
}
var html = '';
list.forEach(function (it) {
var st = it.status == 1 ? '<span style="color:#18a058;">已结算</span>' : '<span style="color:#d48806;">待结算</span>';
html += '<tr>'
+ '<td>' + (it.order_id || '-') + '</td>'
+ '<td>' + money(it.amount) + '</td>'
+ '<td>' + money(it.commission) + '</td>'
+ '<td>' + money(it.income) + '</td>'
+ '<td>' + st + '</td>'
+ '<td>' + fmtTime(it.create_at) + '</td></tr>';
});
$('#revBody').html(html);
}).catch(function (msg) { layer.msg(msg, { icon: 2 }); });
}
// ---- 提现 ----
form.on('submit(doWithdraw)', function (data) {
data.field.token = token;
var amt = parseFloat(data.field.amount);
if (!(amt > 0)) { layer.msg('请输入正确的提现金额', { icon: 2 }); return false; }
req('/appmall/developer/withdraw', data.field).then(function (r) {
layer.msg(r.message || '提交成功', { icon: 1 });
$('#withdrawForm')[0].reset(); form.render();
loadEarnings();
}).catch(function (msg) { layer.msg(msg, { icon: 2 }); });
return false;
});
// ---- 账户 ----
function loadAccount() {
req('/appmall/developer/overview', { token: token }).then(function (r) {
var d = r.data || {};
$('#acName').text(d.username || '-');
$('#acEmail').text(d.email || '-');
$('#acStatus').html('<span class="tag up">正常</span>');
$('#acToken').text(token);
}).catch(function () {});
}
$('#copyToken').on('click', function () {
var t = document.getElementById('acToken').innerText;
var ta = $('<textarea>').val(t).appendTo('body').select();
try { document.execCommand('copy'); layer.msg('已复制', { icon: 1 }); } catch (e) { layer.msg('复制失败', { icon: 2 }); }
ta.remove();
});
function esc(s) {
return $('<div>').text(s == null ? '' : s).html();
}
});
</script>
</body>
</html>