fix: 修正打包脚本插件目录名(addon)、重建框架文件清单、修复 install.sql(sms 表冲突/菜单归属/权限名拼写)
This commit is contained in:
@@ -0,0 +1,681 @@
|
||||
<!--
|
||||
* @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=0;Market::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>
|
||||
Reference in New Issue
Block a user