chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
<link href="/static/member/css/common.css" rel="stylesheet" />
<style>
.card-exchange { max-width: 520px; margin: 30px auto; }
.card-exchange .layui-card-body { padding: 30px; }
.card-tip { color: #999; font-size: 13px; line-height: 22px; margin-top: 10px; }
</style>
<div class="card-exchange">
<div class="layui-card">
<div class="layui-card-header">卡密充值</div>
<div class="layui-card-body">
<form class="layui-form" lay-filter="cardExchangeForm">
<div class="layui-form-item">
<label class="layui-form-label">卡号</label>
<div class="layui-input-block">
<input type="text" name="cardno" required lay-verify="required" placeholder="请输入充值卡号" autocomplete="off" 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="password" required lay-verify="required" placeholder="请输入充值密码" autocomplete="off" 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="cardExchangeSubmit">立即充值</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
<div class="card-tip">
说明:充值卡密为一次性使用,兑换成功后余额将实时到账,可在「账户中心」查看余额变动记录。
</div>
</form>
</div>
</div>
</div>
<script>
layui.use(['layer', 'http', 'form'], function () {
var layer = layui.layer, http = layui.http, form = layui.form, $ = layui.$;
form.on('submit(cardExchangeSubmit)', function (data) {
var field = data.field;
http.post('/user/card/redeem', field).then(function (res) {
if (res.code === 0) {
layer.msg(res.message || '兑换成功', { icon: 1 });
$('form[lay-filter=cardExchangeForm]')[0].reset();
form.render();
} else {
layer.msg(res.message || '兑换失败', { icon: 2 });
}
}).catch(function (e) {
layer.msg('请求失败:' + e, { icon: 2 });
});
return false;
});
});
</script>
+35
View File
@@ -0,0 +1,35 @@
<link href="/static/member/css/common.css" rel="stylesheet" />
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-header">我的兑换记录</div>
<div class="layui-card-body">
<table class="layui-hide" id="recordTable" lay-filter="recordTable"></table>
</div>
</div>
</div>
<script>
layui.use(['table', 'http'], function () {
var table = layui.table, http = layui.http;
table.render({
elem: '#recordTable',
url: '/user/card/records',
toolbar: ['filter', 'exports', 'print'],
parseData: function (res) {
return { code: res.code === 0 ? 0 : 1, msg: res.message || '', count: res.count || 0, data: res.data || [] };
},
cols: [[
{ field: 'id', width: 80, title: 'ID', sort: true },
{ field: 'cardno', minWidth: 200, title: '卡号' },
{ field: 'amount', width: 120, title: '面值(元)', sort: true, templet: function (d) { return '¥' + parseFloat(d.amount).toFixed(2); } },
{ field: 'use_time_text', width: 180, title: '兑换时间' },
{ field: 'create_at', width: 180, title: '生成时间' }
]],
limits: [10, 15, 20, 50],
limit: 15,
page: true,
skin: 'line'
});
});
</script>
+103
View File
@@ -0,0 +1,103 @@
<!--
* @Author: YwxApp <ywx@ywxapp.cn>
* @Date: 2026-04-29 02:32:33
* @LastEditors: YwxApp <ywx@ywxapp.cn>
* @LastEditTime: 2026-08-06 23:31:51
* @Description:
* @FilePath: \ywxapp_dev\app\member\view\layout.html
* @CustomString: Copyright (c) 2026 YwxApp
-->
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>{$title | default='YwxApp 会员中心'}</title>
<meta name="renderer" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<link href="/assets/layui/css/layui.css" rel="stylesheet" />
<link href="/static/member/css/common.css" rel="stylesheet" />
<style>
html,
body {
height: 100%;
display: flex;
flex-direction: column;
}
body {
padding-bottom: 64px;
}
.layui-card {
flex: 1;
display: flex;
flex-direction: column;
}
.layui-card-body {
display: flex;
flex-direction: column;
flex: 1;
}
.layui-table-view {
display: flex;
flex-direction: column;
flex: 1;
}
.layui-table-box {
display: flex;
flex-direction: column;
flex: 1;
}
.layui-table-body {
display: flex;
flex-direction: column;
flex: 1;
}
</style>
<script src="/assets/layui/layui.js"></script>
<script type="text/javascript">
layui.app = {
root: "{$site.root|default=''}",
assetUrl: "/assets",
module: "{$site.module}",
app: "{$site.app}",
routeBase: "{$route_base}",
controller: "{$site.controller}",
action: "{$site.action}"
};
</script>
<script src="/assets/ywxapp/ywxapp.js" module="{$site.app}"></script>
</head>
<body>
{__CONTENT__}
<script>
layui.use(["element", "http", "route"], function () {
var element = layui.element;
var http = layui.http;
var $ = layui.$;
$("#logout").on("click", function () {
http.post("{:url('user/Login/logout')}").then(function (res) {
if (res.code == 0) {
var appName = (layui.setter && layui.setter.appName) || (layui.app && wlayui.app.module) || "user";
layui.data(appName, { key: "access_token", remove: true });
layui.data(appName, { key: "refresh_token", remove: true });
layui.data(appName, { key: "menus", remove: true });
location.href = "/";
} else {
layer.msg(res.msg || "退出登录失败");
}
});
});
});
</script>
</body>
</html>
+148
View File
@@ -0,0 +1,148 @@
<!--
* @Author: YwxApp <ywx@ywxapp.cn>
* @Date: 2026-08-06 22:05:38
* @LastEditors: YwxApp <ywx@ywxapp.cn>
* @LastEditTime: 2026-08-07 20:44:17
* @Description:
* @FilePath: \ywxapp_dev\app\member\view\index\index.html
* @CustomString: Copyright (c) 2026 YwxApp
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Member-YwxApp</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/assets/layui/css/layui.css" rel="stylesheet">
<!-- ywxapp.css 与 wxapp.css 内容完全一致(均含完整 .layui-body {fixed} 布局链),
此处仅引 ywxapp.css 一份即可,避免重复加载 -->
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
</head>
<body class="layui-layout-body" id="LAY_home_iframe">
<div id="LAY_app" style="visibility: hidden">
<div class="layui-layout layui-layout-admin">
<div class="layui-header">
<!-- 头部区域 -->
<ul class="layui-nav layui-layout-left">
<li class="layui-nav-item layadmin-flexible" lay-unselect>
<a href="javascript:;" layadmin-event="flexible" title="侧边伸缩">
<i class="layui-icon layui-icon-shrink-right" id="LAY_app_flexible"></i>
</a>
</li>
<li class="layui-nav-item" lay-unselect>
<a href="javascript:;" layadmin-event="refresh" title="刷新">
<i class="layui-icon layui-icon-refresh-3"></i>
</a>
</li>
</ul>
<ul class="layui-nav layui-layout-right" lay-filter="layadmin-layout-right">
<li class="layui-nav-item layui-hide-xs" lay-unselect>
<a href="javascript:;" layadmin-event="theme" title="主题">
<i class="layui-icon layui-icon-theme"></i>
</a>
</li>
<li class="layui-nav-item layui-hide-xs" lay-unselect>
<a href="javascript:;" layadmin-event="fullscreen" title="全屏">
<i class="layui-icon layui-icon-screen-full"></i>
</a>
</li>
<li class="layui-nav-item" lay-unselect>
<a href="javascript:;">
<img src="{$user.avatar|default='/static/common/images/avatar.jpg'}" class="layui-nav-img">
<cite>{$user.nickname|default='会员'}</cite>
</a>
<dl class="layui-nav-child">
<dd><a href="javascript:;" lay-href="{:url('user/profile/index')}" lay-text="个人资料">个人资料</a></dd>
<dd><a href="javascript:;" lay-href="{:url('user/profile/avatar')}" lay-text="头像设置">头像设置</a></dd>
<dd><a href="javascript:;" lay-href="{:url('user/profile/password')}" lay-text="修改密码">修改密码</a></dd>
<hr>
<dd style="text-align: center;"><a href="javascript:;" id="logoutBtn">退出登录</a></dd>
</dl>
</li>
</ul>
</div>
<!-- 侧边菜单 -->
<div class="layui-side layui-side-menu">
<div class="layui-side-scroll">
<div class="layui-logo" lay-href="/user/index">
<span>会员中心</span>
</div>
<ul class="layui-nav layui-nav-tree" lay-accordion id="LAY-system-side-menu" lay-filter="layadmin-system-side-menu">
</ul>
</div>
</div>
<!-- 页面标签 -->
<div class="layadmin-pagetabs" id="LAY_app_tabs">
<div class="layui-icon layadmin-tabs-control layui-icon-prev" layadmin-event="leftPage"></div>
<div class="layui-icon layadmin-tabs-control layui-icon-next" layadmin-event="rightPage"></div>
<div class="layui-icon layadmin-tabs-control layui-icon-down">
<ul class="layui-nav layadmin-tabs-select" lay-filter="layadmin-pagetabs-nav">
<li class="layui-nav-item" lay-unselect>
<a href="javascript:;"></a>
<dl class="layui-nav-child layui-anim-fadein">
<dd layadmin-event="closeThisTabs"><a href="javascript:;">关闭当前标签页</a></dd>
<dd layadmin-event="closeOtherTabs"><a href="javascript:;">关闭其它标签页</a></dd>
<dd layadmin-event="closeRightTabs"><a href="javascript:;">关闭右侧标签页</a></dd>
<dd layadmin-event="closeAllTabs"><a href="javascript:;">关闭全部标签页</a></dd>
</dl>
</li>
</ul>
</div>
<div class="layui-tab" lay-unauto lay-allowClose="true" lay-filter="layadmin-layout-tabs">
<ul class="layui-tab-title" id="LAY_app_tabsheader">
<li lay-id="console/index" lay-attr="console/index" class="layui-this"><i class="layui-icon layui-icon-home"></i></li>
</ul>
</div>
</div>
<!-- 主体内容 -->
<div class="layui-body" id="LAY_app_body">
<div class="layadmin-tabsbody-item layui-show">
<iframe src="/static/member/views/console/index.html" frameborder="0" class="layadmin-iframe"></iframe>
</div>
</div>
<!-- 辅助元素,一般用于移动设备下遮罩 -->
<div class="layadmin-body-shade" layadmin-event="shade"></div>
</div>
</div>
<script src="/assets/layui/layui.js"></script>
<script type="text/javascript">
layui.app = {
root: "{$site.root|default=''}",
assetUrl: "/assets",
module: "{$site.module|default='user'}",
realModule: "{$site.app|default='member'}",
routeBase: "{$route_base|default=''}",
controller: "{$site.controller|default=''}",
action: "{$site.action|default=''}",
devToken: "{$site.devToken|default=''}",
devAddon: "{$site.devAddon|default=''}",
};
</script>
<script src="/assets/ywxapp/ywxapp.js" module="member"></script>
<script>
layui.use(['index']);
</script>
<script>
layui.use(['jquery'], function () {
var $ = layui.$;
$('#logoutBtn').on('click', function () {
$.post('login/logout', {}, function () {
location.href = "{:url('login/index')}";
}, 'json').fail(function () { location.href = "{:url('login/index')}"; });
});
});
</script>
</body>
</html>
+95
View File
@@ -0,0 +1,95 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>Member-YwxApp</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/assets/layui/css/layui.css" rel="stylesheet">
<!-- ywxapp.css 与 wxapp.css 内容完全一致(均含完整 .layui-body {fixed} 布局链),
此处仅引 ywxapp.css 一份即可,避免重复加载 -->
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
<link href="/static/member/css/login.css" rel="stylesheet">
</head>
<body>
<div class="layadmin-user-login layadmin-user-display-show" id="LAY-user-login">
<div class="layadmin-user-login-main">
<div class="layadmin-user-login-box layadmin-user-login-header">
<h2>YwxApp用户管理中心</h2>
<p> </p>
</div>
<div class="layadmin-user-login-box layadmin-user-login-body layui-form">
<div class="layui-form-item">
<label class="layadmin-user-login-icon layui-icon layui-icon-username" for="LAY-user-login-username"></label>
<input type="text" name="username" id="LAY-user-login-username" lay-verify="required" placeholder="用户名" class="layui-input">
</div>
<div class="layui-form-item">
<label class="layadmin-user-login-icon layui-icon layui-icon-password" for="LAY-user-login-password"></label>
<input type="password" name="password" id="LAY-user-login-password" lay-verify="required" placeholder="密码" class="layui-input">
</div>
<div class="layui-form-item">
<div class="layui-row">
<div class="layui-col-xs7">
<label class="layadmin-user-login-icon layui-icon layui-icon-vercode" for="LAY-user-login-vercode"></label>
<input type="text" name="captcha" id="LAY-user-login-vercode" lay-verify="required" placeholder="图形验证码" class="layui-input">
</div>
<div class="layui-col-xs5">
<div style="margin-left: 10px;">
<img class="layadmin-user-login-codeimg verification-img" lay-filter="verifyCode" src="{:url('user/ajax/verify')}" alt="点击刷新验证码" style="cursor:pointer;transition:opacity 0.3s" title="点击刷新验证码" />
</div>
</div>
</div>
</div>
<div class="layui-form-item" style="margin-bottom: 20px;">
<input type="checkbox" name="remember" lay-skin="primary" title="记住密码">
<a href="#" class="layadmin-user-jump-change layadmin-link" style="margin-top: 7px;">忘记密码?</a>
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="dataSubmit">登 入</button>
</div>
<div class="layui-trans layui-form-item layadmin-user-login-other">
<label>社交账号登入</label>
<a href="javascript:;"><i class="layui-icon layui-icon-login-qq"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-wechat"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-weibo"></i></a>
<a href="{:url('register/index')}" class="layadmin-user-jump-change layadmin-link">注册帐号</a>
</div>
</div>
</div>
<div class="layui-trans layadmin-user-login-footer"><p>© All Rights Reserved</p></div>
<div class="ladmin-user-login-theme">
<script type="text/html" template>
<ul>
<li data-theme=""><img src="{{ layui.setter.paths.base }}style/imgs/bg-none.jpg"></li>
<li data-theme="#03152A" style="background-color: #03152A;"></li>
<li data-theme="#2E241B" style="background-color: #2E241B;"></li>
<li data-theme="#50314F" style="background-color: #50314F;"></li>
<li data-theme="#344058" style="background-color: #344058;"></li>
<li data-theme="#20222A" style="background-color: #20222A;"></li>
</ul>
</script>
</div>
</div>
<!-- 资 源 引 入 -->
<script src="/assets/layui/layui.js"></script>
<script type="text/javascript">
layui.app = {
root: "{$site.root|default=''}",
assetUrl: "/assets",
module: "{$site.module|default='user'}",
realModule: "{$site.app|default='member'}",
routeBase: "{$route_base|default=''}",
controller: "{$site.controller|default=''}",
action: "{$site.action|default=''}",
devToken: "{$site.devToken|default=''}",
devAddon: "{$site.devAddon|default=''}",
};
</script>
<script src="/assets/ywxapp/ywxapp.js" module="member"></script>
<script>
layui.use('login')
</script>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
<div class="uc-wrap">
<div class="layui-card">
<div class="layui-card-header">我的勋章</div>
<div class="layui-card-body" pad15>
<div class="medal-grid" id="medalGrid"></div>
<div class="medal-empty" id="medalEmpty" style="display:none;color:#999;padding:30px 0;text-align:center;">
<i class="layui-icon layui-icon-trophy" style="font-size:40px;display:block;margin-bottom:8px;color:#ccc;"></i>
暂无勋章,去「任务中心」完成任务即可获得
</div>
</div>
</div>
</div>
<script>
layui.use(['http', 'layer', 'jquery'], function () {
var http = layui.http, layer = layui.layer, $ = layui.jquery;
function render(list) {
var box = $('#medalGrid'), html = '';
if (!list || !list.length) {
$('#medalEmpty').show();
return;
}
$('#medalEmpty').hide();
list.forEach(function (m) {
var img = m.image
? '<img src="' + m.image + '" alt="' + m.title + '" />'
: '<i class="layui-icon layui-icon-trophy"></i>';
html += ''
+ '<div class="medal-item">'
+ '<div class="medal-icon">' + img + '</div>'
+ '<div class="medal-title">' + m.title + '</div>'
+ '<div class="medal-desc">' + (m.description || '') + '</div>'
+ '<div class="medal-time">' + (m.create_time_text || '') + ' 获得</div>'
+ '</div>';
});
box.html(html);
}
http.get('list').then(function (res) {
if (res.code === 0) {
render(res.data || []);
} else {
layer.msg(res.message || '加载失败', { icon: 2 });
}
}).catch(function () {
layer.msg('加载失败', { icon: 2 });
});
});
</script>
<style>
.medal-grid { display:flex; flex-wrap:wrap; gap:16px; }
.medal-item { width:160px; padding:18px 12px; border:1px solid #f0f0f0; border-radius:10px; text-align:center; background:#fff; transition:.2s; }
.medal-item:hover { box-shadow:0 4px 16px rgba(0,0,0,.08); transform:translateY(-2px); }
.medal-icon { width:64px; height:64px; line-height:64px; margin:0 auto 10px; border-radius:50%; background:linear-gradient(135deg,#fff7e6,#ffe7ba); overflow:hidden; }
.medal-icon img { width:64px; height:64px; object-fit:cover; }
.medal-icon .layui-icon { font-size:32px; color:#fa8c16; }
.medal-title { font-size:15px; font-weight:600; color:#333; }
.medal-desc { font-size:12px; color:#999; margin:6px 0; line-height:1.5; min-height:34px; }
.medal-time { font-size:11px; color:#bbb; }
</style>
+139
View File
@@ -0,0 +1,139 @@
<link href="/static/member/css/common.css" rel="stylesheet" />
<style>
.uc-wrap { max-width: 900px; }
.plan-card { transition: 0.2s; }
.plan-card:hover { box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12); }
.plan-card .layui-card-body h1 { font-size: 34px; color: #009688; margin: 6px 0 12px; }
.plan-card li { line-height: 28px; color: #666; }
#payMethods { margin: 10px 0; }
</style>
<div class="uc-wrap">
<h2 style="text-align: center">选择适合您的套餐</h2>
<br />
<div class="layui-row layui-col-space20" id="planList">
{volist name="plans" id="plan"}
<div class="layui-col-md4">
<div class="layui-card plan-card">
<div class="layui-card-header">{$plan.title}</div>
<div class="layui-card-body">
<h1>¥{$plan.price}<span style="font-size: 16px">/月</span></h1>
<ul>
<li>{$plan.desc}</li>
</ul>
{if $plan.price == 0}
<button class="layui-btn layui-btn-primary layui-btn-fluid" onclick="pay({$plan.id})">当前版本</button>
{else}
<button class="layui-btn layui-btn-danger layui-btn-fluid" onclick="pay({$plan.id})">立即开通</button>
{/if}
</div>
</div>
</div>
{/volist}
</div>
</div>
<!-- 支付方式选择弹窗 -->
<div id="payModal" style="display: none; padding: 20px">
<div id="payMethods"></div>
<div id="payBox" style="margin-top: 15px; text-align: center"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<script>
layui.use(['layer', 'http', 'form'], function () {
var layer = layui.layer, http = layui.http, form = layui.form, $ = layui.$;
// 套餐价格映射(用于免费判定)
var PLAN_PRICE = {};
{volist name = "plans" id = "plan" }
PLAN_PRICE[{ $plan.id }] = { $plan.price };
{/volist }
var curPlan = 0; // 当前选择的套餐
// 加载可用支付方式(插件通过 PaymentMethods 事件注入)
function loadMethods(cb) {
http.get('methods').then(function (res) {
if (res.code === 0 && res.data && res.data.length) {
cb(res.data);
} else {
layer.msg('暂无可用支付方式,请先安装支付插件', { icon: 2 });
cb([]);
}
}).catch(function () {
layer.msg('支付方式加载失败', { icon: 2 });
cb([]);
});
}
// 对外暴露:点击套餐按钮触发
window.pay = function (planId) {
curPlan = planId;
if (PLAN_PRICE[planId] == 0) {
http.post('create', { plan_id: planId, method: 'free' }).then(function (res) {
if (res.code === 0) { layer.msg('已开通' + (res.data.plan || ''), { icon: 1 }); }
else { layer.msg(res.message || '开通失败', { icon: 2 }); }
});
return;
}
loadMethods(function (methods) {
if (!methods.length) return;
var html = '<div class="layui-form">';
methods.forEach(function (m, i) {
html += '<input type="radio" name="payMethod" value="' + m.code + '" title="' + m.name + '" ' + (i === 0 ? 'checked' : '') + '>'
+ '<div class="layui-word-aux" style="margin:2px 0 10px;">' + (m.desc || '') + '</div>';
});
html += '</div><button class="layui-btn layui-btn-fluid" id="doPay">确认支付</button>';
$('#payMethods').html(html);
$('#payBox').empty();
form.render();
layer.open({ type: 1, title: '选择支付方式', area: '360px', content: $('#payModal') });
});
};
// 确认支付
$(document).on('click', '#doPay', function () {
var method = $('input[name="payMethod"]:checked').val();
http.post('create', { plan_id: curPlan, method: method }).then(function (res) {
if (res.code !== 0) { layer.msg(res.message || '创建订单失败', { icon: 2 }); return; }
var g = res.data || {};
if (g.type === 'error') {
layer.msg(g.message || '支付未配置完成,无法发起支付', { icon: 2 });
return;
}
if (g.type === 'redirect' && g.redirect_url) {
location.href = g.redirect_url;
} else if (g.type === 'qrcode' && g.qrcode_url) {
$('#payBox').empty().append('<div id="qrcode" style="display:inline-block;"></div><p style="color:#f60;margin-top:8px;">请使用' + (method === 'wechat' ? '微信' : '支付宝') + '扫码支付</p>');
new QRCode(document.getElementById('qrcode'), g.qrcode_url);
startPoll(g.order_sn);
} else if (g.type === 'form' && g.html) {
$('#payBox').html(g.html);
} else if (g.type === 'free') {
layer.msg('开通成功', { icon: 1 });
} else {
layer.msg('已生成支付,请按提示完成', { icon: 1 });
}
}).catch(function () { layer.msg('请求失败', { icon: 2 }); });
});
// 轮询订单支付状态。
// 注意:支付查询由各支付插件提供对应接口(如插件暴露 /user/payment/query),
// 框架不写死第三方路径。下方 demo 调用的 /paydemo/pay/query 为开发占位,
// 已在真实环境移除避免 404;接入支付插件后请在此调用插件提供的查询地址。
function startPoll(orderSn) {
if (window.__payTimer) { clearInterval(window.__payTimer); }
window.__payTimer = setInterval(function () {
// TODO: 替换为支付插件提供的订单查询接口(如 url('user/payment/query')
// $.post(url('user/payment/query'), { order_sn: orderSn }, function (r) {
// if (r && r.code === 0 && r.data && r.data.paid) {
// clearInterval(window.__payTimer);
// window.__payTimer = null;
// layer.msg('支付成功,会员已开通', { icon: 1 });
// setTimeout(function () { location.reload(); }, 1200);
// }
// }, 'json');
}, 2000);
}
});
</script>
+119
View File
@@ -0,0 +1,119 @@
<link rel="stylesheet" href="/assets/ywxapp/modules/cropper/cropper.css">
<style>
.readyimg { background: #f7f7f7; height: 420px; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.readyimg img { max-width: 100%; max-height: 100%; display: block; }
.img-preview { width: 150px; height: 150px; overflow: hidden; border-radius: 50%; border: 1px solid #eee; background: #f7f7f7; }
.img-preview img { width: 100%; }
</style>
<div class="uc-wrap">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-form-item">
<button type="button" class="layui-btn layui-btn-primary" id="pickBtn">
<i class="layui-icon">&#xe67c;</i> 选择图片
</button>
<input id="avatarImgUpload" type="file" accept="image/*" name="file" style="display:none">
<div class="layui-form-mid layui-word-aux">支持 jpg / png / gif,建议尺寸 150x150 以上,大小 4M 以内</div>
</div>
<div class="layui-row layui-col-space15">
<div class="layui-col-md9">
<div class="readyimg"><img id="cropImage" src=""></div>
</div>
<div class="layui-col-md3">
<div class="layui-form-mid">预览:</div>
<div class="img-preview" style="margin-top:8px;"></div>
</div>
</div>
<div class="layui-row layui-col-space15" style="margin-top:14px;">
<div class="layui-col-md9">
<button type="button" class="layui-btn" id="rotateLeft"><i class="layui-icon layui-icon-left"></i> 向左旋转</button>
<button type="button" class="layui-btn" id="rotateRight"><i class="layui-icon layui-icon-right"></i> 向右旋转</button>
<button type="button" class="layui-btn" id="resetImg"><i class="layui-icon layui-icon-refresh"></i> 重置</button>
</div>
<div class="layui-col-md3">
<button type="button" class="layui-btn layui-btn-fluid" id="confirmSave">保存修改</button>
</div>
</div>
</div>
</div>
</div>
<script>
layui.config({ base: '/assets/ywxapp/modules/' }).extend({ cropper: 'cropper/cropper' });
layui.use(['jquery', 'layer', 'cropper', 'http'], function () {
var $ = layui.$;
var layer = layui.layer;
var http = layui.http;
var $image = $('#cropImage');
var options = { aspectRatio: 1 / 1, preview: '.img-preview', viewMode: 1 };
var cropped = false;
// 选择图片
$('#pickBtn').on('click', function () { $('#avatarImgUpload').click(); });
$('#avatarImgUpload').on('change', function () {
var file = this.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function (e) {
$image.attr('src', e.target.result);
$image.off('load.crop').on('load.crop', function () {
if (cropped) { $image.cropper('destroy'); }
$image.cropper(options);
cropped = true;
});
};
reader.readAsDataURL(file);
this.value = '';
});
// 旋转 / 重置
$('#rotateLeft').on('click', function () { if (cropped) $image.cropper('rotate', -15); });
$('#rotateRight').on('click', function () { if (cropped) $image.cropper('rotate', 15); });
$('#resetImg').on('click', function () { if (cropped) $image.cropper('reset'); });
// 保存:裁剪后上传
$('#confirmSave').on('click', function () {
if (!cropped) { layer.msg('请先选择图片', { icon: 2 }); return; }
var canvas = $image.cropper('getCroppedCanvas', { width: 300, height: 300 });
if (!canvas) { layer.msg('裁剪失败,请重试', { icon: 2 }); return; }
layer.load(1, { shade: [0.3, '#fff'] });
canvas.toBlob(function (blob) {
var fd = new FormData();
fd.append('file', blob, 'avatar.png');
fd.append('type', 'avatar');
http.post("{:url('user/profile/avatar')}", fd).then(function (res) {
layer.closeAll('loading');
if (res.code === 0 && res.data && res.data.src) {
layer.msg(res.msg || '头像上传成功', { icon: 1 });
refreshParentAvatar(res.data.src);
} else {
layer.msg(res.msg || '上传失败', { icon: 2 });
}
}).catch(function () {
layer.closeAll('loading');
layer.msg('网络错误,上传失败', { icon: 2 });
});
}, 'image/png');
});
// 刷新会员中心顶部头像与个人资料页预览
function refreshParentAvatar(src) {
try {
if (window !== top && parent.layui && parent.layui.ywxapp) {
var ts = '?t=' + Date.now();
parent.document.querySelectorAll('img.layui-nav-img').forEach(function (im) {
im.src = src + ts;
});
var prev = parent.document.getElementById('avatarPreview');
if (prev) { prev.src = src + ts; }
setTimeout(function () { parent.layui.ywxapp.closeThisTabs(); }, 700);
return;
}
} catch (e) { /* 跨域等情况忽略 */ }
setTimeout(function () { location.href = '/user/profile'; }, 700);
}
});
</script>
+137
View File
@@ -0,0 +1,137 @@
<div class="uc-wrap">
<div class="layui-card">
<div class="layui-card-header">个人资料</div>
<div class="layui-card-body" pad15>
<form class="layui-form" lay-filter="profileForm">
<div class="layui-form-item">
<label class="layui-form-label">头像</label>
<div class="layui-input-inline">
<img id="avatarPreview" src="{$profile.avatar|default=''}" class="layui-nav-img"
style="width:48px;height:48px;border-radius:50%;">
</div>
<div class="layui-form-mid layui-word-aux" style="padding-top:14px;">
<a href="{:url('user/Profile/avatar')}" class="layui-btn layui-btn-xs">修改头像</a>
</div>
</div>
<div class="layui-form-item" style="margin-bottom:0;">
<label class="layui-form-label">我的勋章</label>
<div class="layui-input-block">
{if $medals}
<div class="profile-medals">
{volist name="medals" id="m"}
<span class="profile-medal" title="{$m.title}{$m.create_at|date='Y-m-d'} 获得)">
{if $m.image}
<img src="{$m.image}" alt="{$m.title}">
{else/}
<i class="layui-icon layui-icon-trophy"></i>
{/if}
</span>
{/volist}
<a href="{:url('user/Medal/index')}" class="profile-medal-more">全部 </a>
</div>
{else/}
<div class="layui-form-mid layui-word-aux">暂无勋章,去 <a href="{:url('user/Task/index')}">任务中心</a> 领取吧</div>
{/if}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-inline">
<input type="text" value="{$profile.account|default=''}" class="layui-input" disabled>
</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">
<input type="text" name="nickname" value="{$profile.nickname|default=''}" lay-verify="required"
placeholder="请输入昵称" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">手机号</label>
<div class="layui-input-inline">
<input type="text" name="mobile" value="{$profile.mobile|default=''}" lay-verify="phone"
placeholder="请输入手机号" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">邮箱</label>
<div class="layui-input-inline">
<input type="text" name="email" value="{$profile.email|default=''}" lay-verify="email"
placeholder="请输入邮箱" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">个人简介</label>
<div class="layui-input-block">
<textarea name="bio" placeholder="请输入个人简介" class="layui-textarea">{$profile.bio|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="saveProfile">保存</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</div>
<div class="layui-card" style="margin-top:16px;">
<div class="layui-card-header">账号安全</div>
<div class="layui-card-body" pad15>
<div class="layui-form-item">
<label class="layui-form-label">登录密码</label>
<div class="layui-input-inline">
<input type="text" value="********" class="layui-input" disabled>
</div>
<div class="layui-form-mid layui-word-aux" style="padding-top:8px;">
<a href="{:url('user/Profile/password')}" class="layui-btn layui-btn-xs">修改密码</a>
</div>
</div>
</div>
</div>
</div>
<style>
.profile-medals { display:flex; flex-wrap:wrap; align-items:center; gap:10px; padding-top:4px; }
.profile-medal { display:inline-flex; align-items:center; justify-content:center; width:36px; height:36px;
border-radius:50%; background:linear-gradient(135deg,#fff7e6,#ffe7ba); overflow:hidden; cursor:default; }
.profile-medal img { width:36px; height:36px; object-fit:cover; }
.profile-medal .layui-icon { font-size:20px; color:#fa8c16; }
.profile-medal-more { font-size:12px; color:#999; padding-left:4px; }
.profile-medal-more:hover { color:#1e9fff; }
</style>
<script>
layui.use(['form', 'http', 'layer'], function () {
var form = layui.form, http = layui.http, layer = layui.layer, $ = layui.$;
form.on('submit(saveProfile)', function (obj) {
http.post('save', obj.field).then(function (res) {
if (res.code === 0) {
layer.msg('保存成功', { icon: 1 });
try {
if (window !== top && parent.layui && parent.layui.ywxapp) {
var nav = parent.document.querySelector('.layui-layout-admin .layui-nav-child, .user-nick');
if (nav) { nav.textContent = obj.field.nickname; }
}
} catch (e) {}
} else {
layer.msg(res.message || res.msg || '保存失败', { icon: 2 });
}
}).catch(function () {
layer.msg('请求失败', { icon: 2 });
});
return false;
});
});
</script>
+68
View File
@@ -0,0 +1,68 @@
<div class="uc-wrap">
<div class="layui-card">
<div class="layui-card-header">重置登录密码</div>
<div class="layui-card-body" pad15>
<div class="layui-form" lay-filter="passForm">
<div class="layui-form-item">
<label class="layui-form-label">当前密码</label>
<div class="layui-input-inline">
<input type="password" name="old_password" lay-verify="required" placeholder="请输入当前密码" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">新密码</label>
<div class="layui-input-inline">
<input type="password" name="password" id="LAY_password" lay-verify="pass" placeholder="6-12 位,不含空格" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">确认新密码</label>
<div class="layui-input-inline">
<input type="password" name="password_confirm" lay-verify="repass" placeholder="请输入确认密码" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="setmypass">确认修改</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
layui.use(['form', 'layer'], function () {
var form = layui.form, layer = layui.layer, $ = layui.$;
form.verify({
pass: [/^[\S]{6,12}$/, '密码必须 6 到 12 位,且不能出现空格'],
repass: function (value) {
if (value !== $('#LAY_password').val()) {
return '两次密码输入不一致';
}
}
});
form.on('submit(setmypass)', function (obj) {
$.post('/user/profile/passwordSave', obj.field, function (res) {
if (res.code === 1 || res.code === 0) {
layer.msg(res.msg || '修改成功', { icon: 1 });
setTimeout(function () {
if (window !== top && parent.layui && parent.layui.ywxapp) {
parent.layui.ywxapp.closeThisTabs();
} else {
location.href = '/user/index/index';
}
}, 800);
} else {
layer.msg(res.msg || '修改失败', { icon: 2 });
}
}, 'json').fail(function () {
layer.msg('请求失败', { icon: 2 });
});
return false;
});
});
</script>
+94
View File
@@ -0,0 +1,94 @@
<!--
* @Author: YwxApp <ywx@ywxapp.cn>
* @Date: 2026-08-06 22:05:38
* @LastEditors: YwxApp <ywx@ywxapp.cn>
* @LastEditTime: 2026-08-07 21:22:41
* @Description:
* @FilePath: \ywxapp_dev\app\member\view\register\index.html
* @CustomString: Copyright (c) 2026 YwxApp
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>注册</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/assets/layui/css/layui.css" rel="stylesheet">
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
<link href="/static/member/css/login.css" rel="stylesheet">
</head>
<body>
<div class="layadmin-user-login layadmin-user-display-show" id="LAY-user-login" style="display: none;">
<div class="layadmin-user-login-main">
<div class="layadmin-user-login-box layadmin-user-login-header">
<h2>新用户注册</h2>
<p> </p>
</div>
<div class="layadmin-user-login-box layadmin-user-login-body layui-form">
<div class="layui-form-item">
<label class="layadmin-user-login-icon layui-icon layui-icon-cellphone" for="LAY-user-login-cellphone"></label>
<input type="text" name="cellphone" id="LAY-user-login-cellphone" lay-verify="phone" placeholder="手机" class="layui-input">
</div>
<div class="layui-form-item">
<div class="layui-row">
<div class="layui-col-xs7">
<label class="layadmin-user-login-icon layui-icon layui-icon-vercode" for="LAY-user-login-vercode"></label>
<input type="text" name="vercode" id="LAY-user-login-vercode" lay-verify="required" placeholder="验证码" class="layui-input">
</div>
<div class="layui-col-xs5">
<div style="margin-left: 10px;">
<button type="button" class="layui-btn layui-btn-primary layui-btn-fluid" id="LAY-user-getsmscode">获取验证码</button>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layadmin-user-login-icon layui-icon layui-icon-password" for="LAY-user-login-password"></label>
<input type="password" name="password" id="LAY-user-login-password" lay-verify="pass" placeholder="密码" class="layui-input">
</div>
<div class="layui-form-item">
<label class="layadmin-user-login-icon layui-icon layui-icon-password" for="LAY-user-login-repass"></label>
<input type="password" name="repass" id="LAY-user-login-repass" lay-verify="required" placeholder="确认密码" class="layui-input">
</div>
<div class="layui-form-item">
<label class="layadmin-user-login-icon layui-icon layui-icon-username" for="LAY-user-login-nickname"></label>
<input type="text" name="nickname" id="LAY-user-login-nickname" lay-verify="nickname" placeholder="昵称" class="layui-input">
</div>
<div class="layui-form-item">
<input type="checkbox" name="agreement" lay-skin="primary" title="同意用户协议" checked>
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="LAY-user-reg-submit">注 册</button>
</div>
<div class="layui-trans layui-form-item layadmin-user-login-other">
<label>社交账号注册</label>
<a href="javascript:;"><i class="layui-icon layui-icon-login-qq"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-wechat"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-weibo"></i></a>
<a href="{:url('user/login')}" class="layadmin-user-jump-change layadmin-link layui-hide-xs">用已有帐号登入</a>
<a href="{:url('user/login')}" class="layadmin-user-jump-change layadmin-link layui-hide-sm layui-show-xs-inline-block">登入</a>
</div>
</div>
</div>
<div class="layui-trans layadmin-user-login-footer">
<p>© All Rights Reserved </p>
</div>
</div>
<script src="/assets/layui/layui.js"></script>
<script src="/assets/ywxapp/ywxapp.js" module="member"></script>
<script>
layui.use(['register'], function () { });
</script>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
<div class="uc-wrap">
<div class="layui-card">
<div class="layui-card-header">基础资料</div>
<div class="layui-card-body" pad15>
<form class="layui-form" lay-filter="settingsForm">
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-inline">
<input type="text" value="{$info.account|default=''}" class="layui-input" disabled>
</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">
<input type="text" value="{$info.mobile|default=''}" class="layui-input" disabled>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">昵称</label>
<div class="layui-input-inline">
<input type="text" name="nickname" value="{$info.nickname|default=''}" lay-verify="required" placeholder="请输入昵称" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">邮箱</label>
<div class="layui-input-inline">
<input type="text" name="email" value="{$info.email|default=''}" lay-verify="email" placeholder="请输入邮箱" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="saveSettings">保存</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</div>
</div>
<script>
layui.use(['form', 'http', 'layer'], function () {
var form = layui.form, http = layui.http, layer = layui.layer;
form.on('submit(saveSettings)', function (obj) {
http.post('save', obj.field).then(function (res) {
if (res.code === 0) {
layer.msg('保存成功', { icon: 1 });
} else {
layer.msg(res.message || '保存失败', { icon: 2 });
}
}).catch(function () {
layer.msg('请求失败', { icon: 2 });
});
return false;
});
});
</script>
+94
View File
@@ -0,0 +1,94 @@
<div class="uc-wrap">
<div class="layui-card">
<div class="layui-card-header">任务中心</div>
<div class="layui-card-body" pad15>
<div class="task-list" id="taskList">
<div class="task-empty" id="taskEmpty" style="display:none;color:#999;padding:20px 0;text-align:center;">暂无进行中的任务</div>
</div>
</div>
</div>
</div>
<script>
layui.use(['http', 'layer', 'jquery'], function () {
var http = layui.http, layer = layui.layer, $ = layui.jquery;
function render(list) {
var box = $('#taskList'), html = '';
if (!list || !list.length) {
$('#taskEmpty').show();
return;
}
$('#taskEmpty').hide();
list.forEach(function (t) {
var reward = '';
if (t.reward_type == 1) {
reward = '<span class="task-reward score">' + t.reward_num + ' 积分</span>';
} else if (t.reward_type == 2) {
reward = '<span class="task-reward coin">' + t.reward_num + ' 金币</span>';
} else if (t.reward_type == 3) {
reward = '<span class="task-reward prop">道具 #' + t.reward_num + '</span>';
}
var btn = t.claimed
? '<button class="layui-btn layui-btn-disabled layui-btn-xs" disabled>已领取</button>'
: '<button class="layui-btn layui-btn-xs task-claim" data-id="' + t.id + '">领取奖励</button>';
html += ''
+ '<div class="task-item">'
+ '<div class="task-main">'
+ '<div class="task-title">' + t.title + '</div>'
+ '<div class="task-desc">' + (t.description || '') + '</div>'
+ '</div>'
+ '<div class="task-side">'
+ '<div class="task-reward-box">' + reward + '</div>'
+ btn
+ '</div>'
+ '</div>';
});
box.html(html);
}
http.get('list').then(function (res) {
if (res.code === 0) {
render(res.data || []);
} else {
layer.msg(res.message || '加载失败', { icon: 2 });
}
}).catch(function () {
layer.msg('加载失败', { icon: 2 });
});
$('#taskList').on('click', '.task-claim', function () {
var id = $(this).data('id'), $btn = $(this);
$btn.attr('disabled', true);
http.post('claim', { task_id: id }).then(function (res) {
if (res.code === 0) {
layer.msg(res.message || '领取成功', { icon: 1 });
$btn.parent().find('.task-claim').removeClass('task-claim').addClass('layui-btn-disabled').text('已领取');
http.get('list').then(function (r) {
if (r.code === 0) { render(r.data || []); }
});
} else {
layer.msg(res.message || '领取失败', { icon: 2 });
$btn.attr('disabled', false);
}
}).catch(function () {
layer.msg('请求失败', { icon: 2 });
$btn.attr('disabled', false);
});
});
});
</script>
<style>
.task-item { display:flex; align-items:center; justify-content:space-between; padding:14px 4px; border-bottom:1px solid #f0f0f0; }
.task-item:last-child { border-bottom:none; }
.task-main { flex:1; padding-right:16px; }
.task-title { font-size:15px; font-weight:600; color:#333; }
.task-desc { font-size:13px; color:#888; margin-top:4px; line-height:1.5; }
.task-side { text-align:right; min-width:120px; }
.task-reward-box { margin-bottom:8px; }
.task-reward { display:inline-block; padding:2px 10px; border-radius:12px; font-size:12px; }
.task-reward.score { background:#e6f0ff; color:#1e6fff; }
.task-reward.coin { background:#fff3e0; color:#ff8c00; }
.task-reward.prop { background:#e8f8ee; color:#1aab5b; }
</style>
+62
View File
@@ -0,0 +1,62 @@
<div class="uc-wrap">
<div class="layui-card">
<div class="layui-card-header">我的道具</div>
<div class="layui-card-body" pad15>
<div class="myprop-list" id="mypropList">
<div class="myprop-empty" id="mypropEmpty" style="display:none;color:#999;padding:20px 0;text-align:center;">你还没有领取任何道具,去任务中心看看吧</div>
</div>
</div>
</div>
</div>
<script>
layui.use(['http', 'layer', 'jquery'], function () {
var http = layui.http, layer = layui.layer, $ = layui.jquery;
function render(list) {
var box = $('#mypropList'), html = '';
if (!list || !list.length) {
$('#mypropEmpty').show();
return;
}
$('#mypropEmpty').hide();
list.forEach(function (p) {
var icon = p.icon
? '<img src="' + p.icon + '" class="myprop-icon" alt="' + (p.title || '') + '">'
: '<span class="myprop-icon myprop-icon-default">道具</span>';
html += ''
+ '<div class="myprop-item">'
+ '<div class="myprop-icon-box">' + icon + '</div>'
+ '<div class="myprop-info">'
+ '<div class="myprop-title">' + (p.title || '道具#' + p.prop_id) + '</div>'
+ '<div class="myprop-time">获得于 ' + (p.create_at ? new Date(p.create_at * 1000).toLocaleString() : '-') + '</div>'
+ '</div>'
+ '<div class="myprop-num">x' + (p.num || 1) + '</div>'
+ '</div>';
});
box.html(html);
}
http.get('myPropList').then(function (res) {
if (res.code === 0) {
render(res.data || []);
} else {
layer.msg(res.message || '加载失败', { icon: 2 });
}
}).catch(function () {
layer.msg('加载失败', { icon: 2 });
});
});
</script>
<style>
.myprop-item { display:flex; align-items:center; padding:12px 4px; border-bottom:1px solid #f0f0f0; }
.myprop-item:last-child { border-bottom:none; }
.myprop-icon-box { width:48px; height:48px; margin-right:14px; flex-shrink:0; }
.myprop-icon { width:48px; height:48px; border-radius:8px; object-fit:cover; display:block; }
.myprop-icon-default { width:48px; height:48px; line-height:48px; text-align:center; background:#f0f4ff; color:#1e6fff; border-radius:8px; font-size:12px; }
.myprop-info { flex:1; }
.myprop-title { font-size:15px; font-weight:600; color:#333; }
.myprop-time { font-size:12px; color:#999; margin-top:4px; }
.myprop-num { color:#ff8c00; font-weight:600; font-size:15px; }
</style>