Compare commits
2 Commits
46b14149c9
...
d1b0a26dd2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1b0a26dd2 | ||
|
|
abfa783907 |
@@ -56,22 +56,51 @@ class Config extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->title = '系统参数配置';
|
||||
$this->files = Storage::types();
|
||||
$this->plugins = Plugin::get(null, true);
|
||||
$this->issuper = AdminService::isSuper();
|
||||
$this->systemid = ModuleService::getRunVar('uni');
|
||||
$this->framework = ModuleService::getLibrarys('topthink/framework');
|
||||
$this->thinkadmin = ModuleService::getLibrarys('zoujingli/think-library');
|
||||
if (AdminService::isSuper() && $this->app->session->get('user.password') === md5('admin')) {
|
||||
$url = url('admin/index/pass', ['id' => AdminService::getUserId()]);
|
||||
$this->showErrorMessage = lang("超级管理员账号的密码未修改,建议立即<a data-modal='%s'>修改密码</a>!", [$url]);
|
||||
if ($this->request->isGet()) {
|
||||
$this->title = '系统参数配置';
|
||||
$this->files = Storage::types();
|
||||
$this->plugins = Plugin::get(null, true);
|
||||
$this->issuper = AdminService::isSuper();
|
||||
$this->systemid = ModuleService::getRunVar('uni');
|
||||
$this->framework = ModuleService::getLibrarys('topthink/framework');
|
||||
$this->thinkadmin = ModuleService::getLibrarys('zoujingli/think-library');
|
||||
if (AdminService::isSuper() && $this->app->session->get('user.password') === md5('admin')) {
|
||||
$url = url('admin/index/pass', ['id' => AdminService::getUserId()]);
|
||||
$this->showErrorMessage = lang("超级管理员账号的密码未修改,建议立即<a data-modal='%s'>修改密码</a>!", [$url]);
|
||||
}
|
||||
uasort($this->plugins, static function ($a, $b) {
|
||||
if ($a['space'] === $b['space']) return 0;
|
||||
return $a['space'] > $b['space'] ? 1 : -1;
|
||||
});
|
||||
$this->fetch();
|
||||
} else {
|
||||
$data = $this->request->post();
|
||||
foreach ($data as $k => $v) {
|
||||
sysconf($k, $v);
|
||||
}
|
||||
$this->success('参数修改成功!');
|
||||
}
|
||||
uasort($this->plugins, static function ($a, $b) {
|
||||
if ($a['space'] === $b['space']) return 0;
|
||||
return $a['space'] > $b['space'] ? 1 : -1;
|
||||
});
|
||||
$this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置参数
|
||||
* @param string $name 配置名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
return sysconf($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改系统配置参数
|
||||
* @param string $name 配置名称
|
||||
* @param mixed $value 配置值
|
||||
* @return mixed
|
||||
*/
|
||||
public function set($name, $value)
|
||||
{
|
||||
return sysconf($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
285
app/admin/controller/api/Account.php
Normal file
285
app/admin/controller/api/Account.php
Normal file
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\admin\controller\api;
|
||||
|
||||
use think\admin\Controller;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* Cursor账号管理接口
|
||||
*/
|
||||
class Account extends Controller
|
||||
{
|
||||
/**
|
||||
* 添加账号
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
try {
|
||||
$data = $this->_vali([
|
||||
'email.require' => '邮箱不能为空',
|
||||
'password.require' => '密码不能为空',
|
||||
'first_name.require' => '名字不能为空',
|
||||
'last_name.require' => '姓氏不能为空',
|
||||
'access_token.require' => 'access_token不能为空',
|
||||
'refresh_token.require' => 'refresh_token不能为空',
|
||||
'registration_time.require' => '注册时间不能为空'
|
||||
]);
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
if (Db::name('cursor_accounts')->where('email', $data['email'])->find()) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '该邮箱已存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 准备插入数据
|
||||
$insertData = [
|
||||
'email' => $data['email'],
|
||||
'password' => $data['password'],
|
||||
'first_name' => $data['first_name'],
|
||||
'last_name' => $data['last_name'],
|
||||
'access_token' => $data['access_token'],
|
||||
'refresh_token' => $data['refresh_token'],
|
||||
'machine_id' => $data['machine_id'] ?? '',
|
||||
'user_agent' => $data['user_agent'] ?? '',
|
||||
'registration_time' => $data['registration_time'],
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'is_used' => 0
|
||||
];
|
||||
|
||||
// 插入数据
|
||||
$id = Db::name('cursor_accounts')->insertGetId($insertData);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '添加成功',
|
||||
'data' => ['id' => $id]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未使用的账号
|
||||
*/
|
||||
public function getUnused()
|
||||
{
|
||||
try {
|
||||
// 获取machine_id
|
||||
$machineId = input('machine_id', '');
|
||||
if (empty($machineId)) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '设备ID不能为空'
|
||||
]);
|
||||
}
|
||||
|
||||
// 优先检查缓存中是否有该设备最近使用的账号
|
||||
$cacheKey = "device_account_{$machineId}";
|
||||
$cachedAccount = \think\facade\Cache::get($cacheKey);
|
||||
|
||||
if ($cachedAccount) {
|
||||
// 检查账号是否仍然可用
|
||||
$account = Db::name('cursor_accounts')
|
||||
->where('id', $cachedAccount['id'])
|
||||
->find();
|
||||
|
||||
if ($account) {
|
||||
// 更新缓存时间
|
||||
\think\facade\Cache::set($cacheKey, $account, 600); // 10分钟缓存
|
||||
|
||||
// 查询设备激活状态用于计算过期时间
|
||||
$activations = Db::name('cursor_activation_codes')
|
||||
->where('used_by', '=', $machineId)
|
||||
->where('is_used', '=', 1)
|
||||
->order('used_at desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if (!empty($activations)) {
|
||||
$totalDays = array_sum(array_column($activations, 'days'));
|
||||
$firstActivationTime = strtotime($activations[count($activations)-1]['used_at']);
|
||||
$expireTime = $firstActivationTime + ($totalDays * 24 * 3600);
|
||||
|
||||
// 返回账号信息
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'email' => $account['email'],
|
||||
'password' => $account['password'],
|
||||
'access_token' => $account['access_token'],
|
||||
'refresh_token' => $account['refresh_token'],
|
||||
'expire_time' => date('Y-m-d H:i:s', $expireTime),
|
||||
'days_left' => ceil(($expireTime - time()) / 86400)
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查询设备激活状态
|
||||
$activations = Db::name('cursor_activation_codes')
|
||||
->where('used_by', '=', $machineId)
|
||||
->where('is_used', '=', 1)
|
||||
->order('used_at desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if (empty($activations)) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '设备未激活'
|
||||
]);
|
||||
}
|
||||
|
||||
// 计算总天数和到期时间
|
||||
$totalDays = array_sum(array_column($activations, 'days'));
|
||||
$firstActivationTime = strtotime($activations[count($activations)-1]['used_at']);
|
||||
$expireTime = $firstActivationTime + ($totalDays * 24 * 3600);
|
||||
|
||||
// 检查是否过期
|
||||
if (time() > $expireTime) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '设备授权已过期'
|
||||
]);
|
||||
}
|
||||
|
||||
// 在分配新账号前检查冷却期
|
||||
$cooldownKey = "device_cooldown_{$machineId}";
|
||||
$isInCooldown = \think\facade\Cache::get($cooldownKey);
|
||||
if ($isInCooldown) {
|
||||
return json([
|
||||
'code' => 429,
|
||||
'msg' => '请求过于频繁,请稍后再试',
|
||||
'data' => [
|
||||
'cooldown_expires' => date('Y-m-d H:i:s', intval($isInCooldown))
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 获取一个未使用的账号
|
||||
$account = Db::name('cursor_accounts')
|
||||
->where('is_used', 0)
|
||||
->lock(true)
|
||||
->find();
|
||||
|
||||
if (empty($account)) {
|
||||
Db::rollback();
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '没有可用的账号'
|
||||
]);
|
||||
}
|
||||
|
||||
// 如果有旧账号,将其标记为未使用
|
||||
if ($cachedAccount) {
|
||||
Db::name('cursor_accounts')
|
||||
->where('id', $cachedAccount['id'])
|
||||
->update([
|
||||
'is_used' => 0,
|
||||
'used_at' => null,
|
||||
'used_by' => ''
|
||||
]);
|
||||
}
|
||||
|
||||
// 更新新账号状态
|
||||
Db::name('cursor_accounts')
|
||||
->where('id', $account['id'])
|
||||
->update([
|
||||
'is_used' => 1,
|
||||
'used_at' => date('Y-m-d H:i:s'),
|
||||
'used_by' => $machineId
|
||||
]);
|
||||
|
||||
// 设置冷却期(仅在成功分配新账号时)
|
||||
$cooldownExpires = time() + 1800; // 30分钟冷却期
|
||||
\think\facade\Cache::set($cooldownKey, $cooldownExpires, 1800);
|
||||
|
||||
Db::commit();
|
||||
|
||||
// 缓存新账号信息
|
||||
\think\facade\Cache::set($cacheKey, $account, 600); // 10分钟缓存
|
||||
|
||||
// 返回账号信息
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'email' => $account['email'],
|
||||
'password' => $account['password'],
|
||||
'access_token' => $account['access_token'],
|
||||
'refresh_token' => $account['refresh_token'],
|
||||
'expire_time' => date('Y-m-d H:i:s', $expireTime),
|
||||
'days_left' => ceil(($expireTime - time()) / 86400)
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台强制重置设备账号
|
||||
* @auth true
|
||||
*/
|
||||
public function resetDeviceAccount()
|
||||
{
|
||||
try {
|
||||
$data = $this->_vali([
|
||||
'machine_id.require' => '设备ID不能为空'
|
||||
]);
|
||||
|
||||
$machineId = $data['machine_id'];
|
||||
|
||||
// 清除设备的账号缓存
|
||||
$cacheKey = "device_account_{$machineId}";
|
||||
\think\facade\Cache::delete($cacheKey);
|
||||
|
||||
// 清除冷却期
|
||||
$cooldownKey = "device_cooldown_{$machineId}";
|
||||
\think\facade\Cache::delete($cooldownKey);
|
||||
|
||||
// 将该设备使用的账号标记为未使用
|
||||
Db::name('cursor_accounts')
|
||||
->where('used_by', $machineId)
|
||||
->update([
|
||||
'is_used' => 0,
|
||||
'used_at' => null,
|
||||
'used_by' => ''
|
||||
]);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '重置成功'
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
225
app/admin/controller/api/Agent.php
Normal file
225
app/admin/controller/api/Agent.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\admin\controller\api;
|
||||
|
||||
use think\admin\Controller;
|
||||
use think\facade\Db;
|
||||
use think\facade\Cache;
|
||||
|
||||
/**
|
||||
* 代理商接口
|
||||
*/
|
||||
class Agent extends Controller
|
||||
{
|
||||
/**
|
||||
* 代理商登录
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
try {
|
||||
$data = $this->_vali([
|
||||
'username.require' => '用户名不能为空',
|
||||
'password.require' => '密码不能为空'
|
||||
]);
|
||||
|
||||
// 查询代理商
|
||||
$agent = Db::name('cursor_agents')
|
||||
->where('username', $data['username'])
|
||||
->find();
|
||||
|
||||
if (empty($agent)) {
|
||||
return json(['code' => 400, 'msg' => '用户名或密码错误']);
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!password_verify($data['password'], $agent['password'])) {
|
||||
return json(['code' => 400, 'msg' => '用户名或密码错误']);
|
||||
}
|
||||
|
||||
// 检查状态
|
||||
if ($agent['status'] != 1) {
|
||||
return json(['code' => 400, 'msg' => '账号已被禁用']);
|
||||
}
|
||||
|
||||
// 生成token
|
||||
$token = md5($agent['id'] . time() . rand(1000, 9999));
|
||||
|
||||
// 存储token,有效期7天
|
||||
Cache::set('agent_token_' . $token, $agent['id'], 7 * 24 * 3600);
|
||||
|
||||
// 更新登录信息
|
||||
Db::name('cursor_agents')
|
||||
->where('id', $agent['id'])
|
||||
->update([
|
||||
'last_login_time' => date('Y-m-d H:i:s'),
|
||||
'last_login_ip' => $this->request->ip(),
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
|
||||
// 返回登录信息
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '登录成功',
|
||||
'data' => [
|
||||
'token' => $token,
|
||||
'agent' => [
|
||||
'id' => $agent['id'],
|
||||
'username' => $agent['username'],
|
||||
'nickname' => $agent['nickname'],
|
||||
'level' => $agent['level'],
|
||||
'balance' => $agent['balance'],
|
||||
'commission_rate' => $agent['commission_rate']
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json(['code' => 500, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证token
|
||||
*/
|
||||
protected function checkToken()
|
||||
{
|
||||
$token = $this->request->header('token');
|
||||
if (empty($token)) {
|
||||
return json(['code' => 401, 'msg' => '请先登录']);
|
||||
}
|
||||
|
||||
$agentId = Cache::get('agent_token_' . $token);
|
||||
if (empty($agentId)) {
|
||||
return json(['code' => 401, 'msg' => '登录已过期,请重新登录']);
|
||||
}
|
||||
|
||||
return $agentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理商信息
|
||||
*/
|
||||
public function info()
|
||||
{
|
||||
try {
|
||||
$agentId = $this->checkToken();
|
||||
if (!is_numeric($agentId)) {
|
||||
return $agentId; // 返回错误信息
|
||||
}
|
||||
|
||||
$agent = Db::name('cursor_agents')
|
||||
->where('id', $agentId)
|
||||
->find();
|
||||
|
||||
if (empty($agent)) {
|
||||
return json(['code' => 400, 'msg' => '代理商不存在']);
|
||||
}
|
||||
|
||||
// 获取激活码统计
|
||||
$codeStats = Db::name('cursor_agent_codes')
|
||||
->where('agent_id', $agentId)
|
||||
->field([
|
||||
'COUNT(*) as total_codes',
|
||||
'SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as settled_codes',
|
||||
'SUM(commission) as total_commission',
|
||||
'SUM(CASE WHEN status = 1 THEN commission ELSE 0 END) as settled_commission'
|
||||
])
|
||||
->find();
|
||||
|
||||
// 获取未使用的激活码
|
||||
$unusedCodes = Db::name('cursor_agent_codes')
|
||||
->alias('ac')
|
||||
->join('cursor_activation_codes c', 'ac.code_id = c.id')
|
||||
->where('ac.agent_id', $agentId)
|
||||
->where('c.is_used', 0)
|
||||
->field('c.code, c.days, ac.price')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'agent' => [
|
||||
'id' => $agent['id'],
|
||||
'username' => $agent['username'],
|
||||
'nickname' => $agent['nickname'],
|
||||
'level' => $agent['level'],
|
||||
'balance' => $agent['balance'],
|
||||
'total_income' => $agent['total_income'],
|
||||
'commission_rate' => $agent['commission_rate'],
|
||||
'last_login_time' => $agent['last_login_time']
|
||||
],
|
||||
'stats' => [
|
||||
'total_codes' => $codeStats['total_codes'] ?? 0,
|
||||
'settled_codes' => $codeStats['settled_codes'] ?? 0,
|
||||
'total_commission' => $codeStats['total_commission'] ?? 0,
|
||||
'settled_commission' => $codeStats['settled_commission'] ?? 0
|
||||
],
|
||||
'unused_codes' => $unusedCodes
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json(['code' => 500, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理商的激活码列表
|
||||
*/
|
||||
public function codes()
|
||||
{
|
||||
try {
|
||||
$agentId = $this->checkToken();
|
||||
if (!is_numeric($agentId)) {
|
||||
return $agentId;
|
||||
}
|
||||
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('limit/d', 20);
|
||||
$status = input('status');
|
||||
|
||||
$query = Db::name('cursor_agent_codes')
|
||||
->alias('ac')
|
||||
->join('cursor_activation_codes c', 'ac.code_id = c.id')
|
||||
->where('ac.agent_id', $agentId);
|
||||
|
||||
if (isset($status)) {
|
||||
$query = $query->where('c.is_used', intval($status));
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
$list = $query->field([
|
||||
'c.code',
|
||||
'c.days',
|
||||
'c.is_used',
|
||||
'c.used_at',
|
||||
'c.used_by',
|
||||
'ac.price',
|
||||
'ac.commission',
|
||||
'ac.status as settle_status',
|
||||
'ac.created_at'
|
||||
])
|
||||
->page($page, $limit)
|
||||
->order('ac.created_at desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json(['code' => 500, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,27 @@ class Mail extends Controller
|
||||
'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36',
|
||||
'MAIL_SERVER' => 'https://tempmail.plus'
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => '备用配置2',
|
||||
'config' => [
|
||||
'DOMAIN' => 'wuen.site',
|
||||
'TEMP_MAIL' => 'actoke',
|
||||
'TEMP_MAIL_EXT' => '@mailto.plus',
|
||||
'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36',
|
||||
'MAIL_SERVER' => 'https://tempmail.plus'
|
||||
]
|
||||
] ,
|
||||
[
|
||||
'name' => '备用配置3',
|
||||
'config' => [
|
||||
'DOMAIN' => 'jxyweb.site',
|
||||
'TEMP_MAIL' => 'exvet',
|
||||
'TEMP_MAIL_EXT' => '@mailto.plus',
|
||||
'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36',
|
||||
'MAIL_SERVER' => 'https://tempmail.plus',
|
||||
'TEMP_MAIL_EPIN'=>'889944'
|
||||
]
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace app\admin\controller\api;
|
||||
|
||||
use app\manager\model\Member as MemberModel;
|
||||
use think\admin\Controller;
|
||||
use think\facade\Db;
|
||||
|
||||
|
||||
/**
|
||||
@@ -82,4 +83,250 @@ class Member extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活码验证并激活
|
||||
* @return void
|
||||
*/
|
||||
public function activate()
|
||||
{
|
||||
try {
|
||||
// 验证激活码
|
||||
$code = input('code');
|
||||
if (empty($code)) {
|
||||
return json(['code' => 400, 'msg' => '激活码不能为空']);
|
||||
}
|
||||
|
||||
// 获取设备信息
|
||||
$machineId = input('machine_id', '');
|
||||
if (empty($machineId)) {
|
||||
return json(['code' => 400, 'msg' => '设备ID不能为空']);
|
||||
}
|
||||
|
||||
// 收集设备详细信息
|
||||
$deviceInfo = [
|
||||
'machine_id' => $machineId,
|
||||
'os' => input('os', ''), // 操作系统信息
|
||||
'device_name' => input('device_name', ''), // 设备名称
|
||||
'ip' => $this->request->ip(), // IP地址
|
||||
'user_agent' => $this->request->header('user-agent', ''), // UA信息
|
||||
'location' => input('location', '') // 地理位置(如果有)
|
||||
];
|
||||
|
||||
// 获取激活码信息
|
||||
$activation = Db::name('cursor_activation_codes')
|
||||
->where('code', $code)
|
||||
->where('is_used', 0)
|
||||
->find();
|
||||
|
||||
if (empty($activation)) {
|
||||
return json(['code' => 400, 'msg' => '激活码无效或已被使用']);
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 标记新激活码为已使用
|
||||
Db::name('cursor_activation_codes')
|
||||
->where('id', $activation['id'])
|
||||
->update([
|
||||
'is_used' => 1,
|
||||
'used_at' => date('Y-m-d H:i:s'),
|
||||
'used_by' => $machineId,
|
||||
'device_info' => json_encode($deviceInfo)
|
||||
]);
|
||||
|
||||
Db::commit();
|
||||
|
||||
// 获取该设备所有的激活记录
|
||||
$activations = Db::name('cursor_activation_codes')
|
||||
->where('used_by', '=', $machineId)
|
||||
->where('is_used', '=', 1)
|
||||
->order('used_at desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 计算总天数
|
||||
$totalDays = array_sum(array_column($activations, 'days'));
|
||||
|
||||
// 计算最终到期时间
|
||||
$now = time();
|
||||
$firstActivationTime = strtotime($activations[count($activations)-1]['used_at']); // 第一次激活时间
|
||||
$expireTime = $firstActivationTime + ($totalDays * 24 * 3600); // 总天数转换为秒数
|
||||
$daysLeft = ceil(($expireTime - $now) / 86400);
|
||||
|
||||
// 返回成功信息
|
||||
$result = [
|
||||
'code' => 200,
|
||||
'msg' => '激活成功',
|
||||
'data' => [
|
||||
'expire_time' => date('Y-m-d H:i:s', $expireTime),
|
||||
'total_days' => $totalDays,
|
||||
'added_days' => $activation['days'],
|
||||
'days_left' => $daysLeft,
|
||||
'machine_id' => $machineId,
|
||||
'device_info' => $deviceInfo,
|
||||
'activation_time' => date('Y-m-d H:i:s'),
|
||||
'activation_records' => array_map(function($record) {
|
||||
return [
|
||||
'code' => $record['code'],
|
||||
'days' => $record['days'],
|
||||
'activation_time' => $record['used_at'],
|
||||
'device_info' => json_decode($record['device_info'], true)
|
||||
];
|
||||
}, $activations)
|
||||
]
|
||||
];
|
||||
|
||||
return json($result);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员状态
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
try {
|
||||
// 获取设备ID
|
||||
$machineId = input('machine_id', '');
|
||||
if (empty($machineId)) {
|
||||
return json(['code' => 400, 'msg' => '设备ID不能为空']);
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取Redis实例
|
||||
$redis = cache('redis');
|
||||
$cacheKey = "device_status:{$machineId}";
|
||||
|
||||
// 尝试获取缓存
|
||||
if ($redis && $statusInfo = $redis->get($cacheKey)) {
|
||||
return json(json_decode($statusInfo, true));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Redis异常时记录日志但继续执行
|
||||
trace("Redis异常: " . $e->getMessage(), 'error');
|
||||
}
|
||||
|
||||
// 获取该设备所有的激活记录
|
||||
$activations = Db::name('cursor_activation_codes')
|
||||
->where('used_by', '=', $machineId)
|
||||
->where('is_used', '=', 1)
|
||||
->field(['days', 'used_at', 'code', 'device_info'])
|
||||
->order('used_at desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if (empty($activations)) {
|
||||
$result = [
|
||||
'code' => 401,
|
||||
'msg' => '未激活',
|
||||
'data' => [
|
||||
'status' => 'inactive',
|
||||
'expire_time' => '',
|
||||
'total_days' => 0,
|
||||
'days_left' => 0,
|
||||
'activation_records' => []
|
||||
]
|
||||
];
|
||||
// 未激活状态缓存1分钟
|
||||
try {
|
||||
if ($redis) {
|
||||
$redis->set($cacheKey, json_encode($result), 60);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
trace("Redis缓存写入异常: " . $e->getMessage(), 'error');
|
||||
}
|
||||
return json($result);
|
||||
}
|
||||
|
||||
// 计算总天数
|
||||
$totalDays = array_sum(array_column($activations, 'days'));
|
||||
|
||||
// 计算最终到期时间
|
||||
$now = time();
|
||||
$firstActivationTime = strtotime($activations[count($activations)-1]['used_at']);
|
||||
$expireTime = $firstActivationTime + ($totalDays * 24 * 3600);
|
||||
$daysLeft = ceil(($expireTime - $now) / 86400);
|
||||
|
||||
// 判断是否过期
|
||||
if ($daysLeft <= 0) {
|
||||
$result = [
|
||||
'code' => 401,
|
||||
'msg' => '已过期',
|
||||
'data' => [
|
||||
'status' => 'expired',
|
||||
'expire_time' => date('Y-m-d H:i:s', $expireTime),
|
||||
'total_days' => $totalDays,
|
||||
'days_left' => 0,
|
||||
'machine_id' => $machineId,
|
||||
'activation_records' => array_map(function($record) {
|
||||
return [
|
||||
'code' => $record['code'],
|
||||
'days' => $record['days'],
|
||||
'activation_time' => $record['used_at'],
|
||||
'device_info' => json_decode($record['device_info'], true)
|
||||
];
|
||||
}, $activations)
|
||||
]
|
||||
];
|
||||
// 过期状态缓存1分钟
|
||||
try {
|
||||
if ($redis) {
|
||||
$redis->set($cacheKey, json_encode($result), 60);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
trace("Redis缓存写入异常: " . $e->getMessage(), 'error');
|
||||
}
|
||||
return json($result);
|
||||
}
|
||||
|
||||
// 正常状态返回
|
||||
$result = [
|
||||
'code' => 200,
|
||||
'msg' => '正常',
|
||||
'data' => [
|
||||
'status' => 'active',
|
||||
'expire_time' => date('Y-m-d H:i:s', $expireTime),
|
||||
'total_days' => $totalDays,
|
||||
'days_left' => $daysLeft,
|
||||
'machine_id' => $machineId,
|
||||
'activation_records' => array_map(function($record) {
|
||||
return [
|
||||
'code' => $record['code'],
|
||||
'days' => $record['days'],
|
||||
'activation_time' => $record['used_at'],
|
||||
'device_info' => json_decode($record['device_info'], true)
|
||||
];
|
||||
}, $activations)
|
||||
]
|
||||
];
|
||||
|
||||
// 正常状态缓存5分钟
|
||||
try {
|
||||
if ($redis) {
|
||||
$redis->set($cacheKey, json_encode($result), 300);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
trace("Redis缓存写入异常: " . $e->getMessage(), 'error');
|
||||
}
|
||||
return json($result);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '系统异常,请稍后重试'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
137
app/admin/controller/api/Version.php
Normal file
137
app/admin/controller/api/Version.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\admin\controller\api;
|
||||
|
||||
use app\admin\model\Version as VersionModel;
|
||||
use think\admin\Controller;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* 版本管理接口
|
||||
* @auth true
|
||||
*
|
||||
* 接口说明:
|
||||
* ====================================================
|
||||
* 接口域名:{:sysconf('site_domain')}
|
||||
*
|
||||
* 公共返回参数:
|
||||
* - code: 错误码,0表示成功,非0表示失败
|
||||
* - msg: 提示信息
|
||||
* - data: 返回的数据,请求失败时可能为空
|
||||
*
|
||||
* 错误码说明:
|
||||
* - 0: 成功
|
||||
* - 1: 一般性错误(具体错误信息见msg)
|
||||
* - 401: 未授权或授权失败
|
||||
* - 404: 请求的资源不存在
|
||||
* - 500: 服务器内部错误
|
||||
*
|
||||
* 版本号格式:x.x.x (例如: 3.4.1)
|
||||
* 平台类型:
|
||||
* - all: 全平台
|
||||
* - windows: Windows平台
|
||||
* - mac: Mac平台
|
||||
* - linux: Linux平台
|
||||
* ====================================================
|
||||
*
|
||||
* 1. 获取最新版本 [GET] /admin/api.version/latest
|
||||
* 请求参数:
|
||||
* - platform: 平台类型(all|windows|mac|linux), 默认为all
|
||||
* 返回数据:
|
||||
* {
|
||||
* "code": 0,
|
||||
* "msg": "获取成功",
|
||||
* "data": {
|
||||
* "id": "1",
|
||||
* "version_no": "3.4.1.4",
|
||||
* "version_name": "听泉cursor助手",
|
||||
* "download_url": "http://domain/upload/xxx.exe",
|
||||
* "is_force": 1, // 是否强制更新(1是,0否)
|
||||
* "min_version": "3.4.0.0", // 最低要求版本
|
||||
* "platform": "all", // 平台类型
|
||||
* "description": "版本描述", // 版本描述
|
||||
* "status": 1, // 状态(1启用,0禁用)
|
||||
* "create_time": "2024-03-20 10:00:00"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 2. 检查版本更新 [GET] /admin/api.version/check
|
||||
* 请求参数:
|
||||
* - version: 当前版本号(必填)
|
||||
* - platform: 平台类型(all|windows|mac|linux), 默认为all
|
||||
* 返回数据:
|
||||
* {
|
||||
* "code": 0,
|
||||
* "msg": "检查完成",
|
||||
* "data": {
|
||||
* "has_update": true, // 是否有更新
|
||||
* "is_force": 1, // 是否强制更新
|
||||
* "version_info": { // 新版本信息(has_update为true时返回)
|
||||
* // 同上面的版本信息
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 错误返回示例:
|
||||
* {
|
||||
* "code": 1,
|
||||
* "msg": "请提供当前版本号",
|
||||
* "data": null
|
||||
* }
|
||||
*/
|
||||
class Version extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取最新版本
|
||||
* @return void
|
||||
*/
|
||||
public function latest()
|
||||
{
|
||||
$platform = $this->request->param('platform', 'all');
|
||||
$version = VersionModel::mk()->getLatestVersion($platform);
|
||||
if (!$version) {
|
||||
$this->error('暂无版本信息');
|
||||
}
|
||||
|
||||
// 处理下载地址
|
||||
$version['download_url'] = $this->getFullUrl($version['download_url']);
|
||||
$this->success('获取成功', $version);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查更新
|
||||
* @return void
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$currentVersion = $this->request->param('version');
|
||||
$platform = $this->request->param('platform', 'all');
|
||||
|
||||
if (empty($currentVersion)) {
|
||||
$this->error('请提供当前版本号');
|
||||
}
|
||||
|
||||
$result = VersionModel::mk()->checkUpdate($currentVersion, $platform);
|
||||
if (isset($result['error'])) {
|
||||
$this->error($result['error']);
|
||||
}
|
||||
|
||||
// 如果有更新,处理下载地址
|
||||
if ($result['has_update'] && isset($result['version_info'])) {
|
||||
$result['version_info']['download_url'] = $this->getFullUrl($result['version_info']['download_url']);
|
||||
}
|
||||
|
||||
$this->success('检查完成', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的下载地址
|
||||
* @param string $path 相对路径
|
||||
* @return string
|
||||
*/
|
||||
protected function getFullUrl($path)
|
||||
{
|
||||
return sysconf('site_domain') . $path;
|
||||
}
|
||||
}
|
||||
167
app/admin/model/Version.php
Normal file
167
app/admin/model/Version.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
class Version extends Model
|
||||
{
|
||||
protected $name = 'version';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 版本状态
|
||||
const STATUS_DISABLED = 0;
|
||||
const STATUS_ENABLED = 1;
|
||||
|
||||
/**
|
||||
* 获取下一个版本号
|
||||
* @param string $currentVersion 当前版本号
|
||||
* @return string
|
||||
*/
|
||||
public static function getNextVersion($currentVersion = '')
|
||||
{
|
||||
if (empty($currentVersion)) {
|
||||
// 获取最新版本号
|
||||
$latest = self::order('version_no DESC')->value('version_no');
|
||||
$currentVersion = $latest ?: '1.0.0';
|
||||
}
|
||||
|
||||
// 拆分版本号
|
||||
$parts = explode('.', $currentVersion);
|
||||
$major = intval($parts[0] ?? 1);
|
||||
$minor = intval($parts[1] ?? 0);
|
||||
$patch = intval($parts[2] ?? 0);
|
||||
|
||||
// 增加修订号
|
||||
$patch++;
|
||||
// 如果修订号超过99,增加次版本号
|
||||
if ($patch > 99) {
|
||||
$minor++;
|
||||
$patch = 0;
|
||||
}
|
||||
// 如果次版本号超过99,增加主版本号
|
||||
if ($minor > 99) {
|
||||
$major++;
|
||||
$minor = 0;
|
||||
}
|
||||
|
||||
return sprintf('%d.%d.%d', $major, $minor, $patch);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成版本名称
|
||||
* @param string $version_no 版本号
|
||||
* @param string $filename 文件名
|
||||
* @return string
|
||||
*/
|
||||
public static function generateVersionName($version_no, $filename = '')
|
||||
{
|
||||
// 如果有文件名,尝试从文件名中提取有意义的部分
|
||||
if ($filename) {
|
||||
// 移除扩展名
|
||||
$name = pathinfo($filename, PATHINFO_FILENAME);
|
||||
// 移除版本号部分 (匹配v后面的所有数字和点)
|
||||
$name = preg_replace('/v\d+[\d\.]+/', '', $name);
|
||||
$name = trim($name);
|
||||
if (!empty($name)) {
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认使用版本号作为名称
|
||||
return sprintf('版本 %s', $version_no);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件名解析版本号
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public static function parseVersionFromFilename($filename)
|
||||
{
|
||||
// 移除扩展名
|
||||
$name = pathinfo($filename, PATHINFO_FILENAME);
|
||||
|
||||
// 匹配版本号 (匹配v后面的所有数字和点)
|
||||
if (preg_match('/v(\d+[\d\.]+)/', $name, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
// 如果没有找到版本号,使用当前最新版本号加1
|
||||
return self::getNextVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新版本信息
|
||||
* @param string $platform 平台
|
||||
* @return array|null
|
||||
*/
|
||||
public function getLatestVersion($platform = 'all')
|
||||
{
|
||||
return $this->where([
|
||||
['status', '=', self::STATUS_ENABLED],
|
||||
['platform', 'in', [$platform, 'all']]
|
||||
])->order('version_no DESC')->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查版本更新
|
||||
* @param string $currentVersion 当前版本
|
||||
* @param string $platform 平台
|
||||
* @return array
|
||||
*/
|
||||
public function checkUpdate($currentVersion, $platform = 'all')
|
||||
{
|
||||
$latest = $this->getLatestVersion($platform);
|
||||
if (!$latest) {
|
||||
return ['has_update' => false];
|
||||
}
|
||||
|
||||
// 版本号比较
|
||||
if (version_compare($latest['version_no'], $currentVersion) > 0) {
|
||||
// 检查是否满足最低版本要求
|
||||
if ($latest['min_version'] && version_compare($currentVersion, $latest['min_version']) < 0) {
|
||||
return [
|
||||
'has_update' => false,
|
||||
'error' => '当前版本过低,请先更新到' . $latest['min_version'] . '版本'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'has_update' => true,
|
||||
'is_force' => $latest['is_force'],
|
||||
'version_info' => $latest
|
||||
];
|
||||
}
|
||||
|
||||
return ['has_update' => false];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查版本权限
|
||||
* @param int $versionId 版本ID
|
||||
* @param int $targetId 目标ID
|
||||
* @param string $targetType 目标类型
|
||||
* @return bool
|
||||
*/
|
||||
public function checkVersionPermission($versionId, $targetId, $targetType)
|
||||
{
|
||||
$control = \think\facade\Db::name('version_control')
|
||||
->where([
|
||||
['version_id', '=', $versionId],
|
||||
['target_id', '=', $targetId],
|
||||
['target_type', '=', $targetType],
|
||||
['expire_time', '>', date('Y-m-d H:i:s')]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$control) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $control['control_type'] === 'white_list';
|
||||
}
|
||||
}
|
||||
@@ -11,224 +11,234 @@
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<!--{notempty name='issuper'}-->
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('运行模式')}</b>( {:lang('仅超级管理员可配置')} )
|
||||
</span>
|
||||
</div>
|
||||
<form onsubmit="return false;" data-auto="true" method="post" class='layui-form layui-card' autocomplete="off">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-group shadow-mini nowrap">
|
||||
<!--{if $app->isDebug()}-->
|
||||
<a class="layui-btn layui-btn-sm layui-btn-active">{:lang('以开发模式运行')}</a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="{:lang('确定要切换到生产模式运行吗?')}" data-load="{:url('admin/api.system/debug')}?state=1">{:lang('以生产模式运行')}</a>
|
||||
<!--{else}-->
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="{:lang('确定要切换到开发模式运行吗?')}" data-load="{:url('admin/api.system/debug')}?state=0">{:lang('以开发模式运行')}</a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-active">{:lang('以生产模式运行')}</a>
|
||||
<!--{/if}-->
|
||||
</div>
|
||||
<div class="margin-top-20">
|
||||
<p><b>{:lang('开发模式')}</b>:{:lang('开发人员或在功能调试时使用,系统异常时会显示详细的错误信息,同时还会记录操作日志及数据库 SQL 语句信息。')}</p>
|
||||
<p><b>{:lang('生产模式')}</b>:{:lang('项目正式部署上线后使用,系统异常时统一显示 “%s”,只记录重要的异常日志信息,强烈推荐上线后使用此模式。',[config('app.error_message')])}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('富编辑器')}</b>( {:lang('仅超级管理员可配置')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body layui-clear">
|
||||
<div class="layui-btn-group shadow-mini">
|
||||
{if !in_array(sysconf('base.editor'),['ckeditor4','ckeditor5','wangEditor','auto'])}{php}sysconf('base.editor','ckeditor4');{/php}{/if}
|
||||
{foreach ['ckeditor4'=>'CKEditor4','ckeditor5'=>'CKEditor5','wangEditor'=>'wangEditor','auto'=>lang('自适应模式')] as $k => $v}{if sysconf('base.editor') eq $k}
|
||||
{if auth('storage')}<a data-title="配置{$v}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
|
||||
{else}
|
||||
{if auth('storage')}<a data-title="配置{$v}" data-action="{:url('admin/api.system/editor')}" data-value="editor#{$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
|
||||
{/if}{/foreach}
|
||||
</div>
|
||||
<div class="margin-top-20 full-width pull-left">
|
||||
<p><b>CKEditor4</b>:{:lang('旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。')}</p>
|
||||
<p><b>CKEditor5</b>:{:lang('新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。')}</p>
|
||||
<p><b>wangEditor</b>:{:lang('国产优质富文本编辑器,对于小程序及App内容支持会更友好,推荐使用。')}</p>
|
||||
<p><b>{:lang('自适应模式')}</b>:{:lang('优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--{/notempty}-->
|
||||
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('存储引擎')}</b>( {:lang('文件默认存储方式')} )
|
||||
</span>
|
||||
</div>
|
||||
<!-- 初始化存储配置 -->
|
||||
{if !sysconf('storage.type')}{php}sysconf('storage.type','local');{/php}{/if}
|
||||
{if !sysconf('storage.link_type')}{php}sysconf('storage.link_type','none');{/php}{/if}
|
||||
{if !sysconf('storage.name_type')}{php}sysconf('storage.name_type','xmd5');{/php}{/if}
|
||||
{if !sysconf('storage.allow_exts')}{php}sysconf('storage.allow_exts','doc,gif,ico,jpg,mp3,mp4,p12,pem,png,rar,xls,xlsx');{/php}{/if}
|
||||
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','follow');{/php}{/if}
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-group shadow-mini nowrap">
|
||||
{foreach $files as $k => $v}{if sysconf('storage.type') eq $k}
|
||||
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
|
||||
{else}
|
||||
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
|
||||
{/if}{/foreach}
|
||||
</div>
|
||||
<div class="margin-top-20 full-width">
|
||||
<p><b>{:lang('本地服务器存储')}</b>:{:lang('文件上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。')}</p>
|
||||
<p><b>{:lang('自建Alist存储')}</b>:{:lang('文件上传到 Alist 存储的服务器或云存储空间,根据服务配置可支持大文件上传,不占用本身服务器空间及服务器带宽流量。')}</p>
|
||||
<p><b>{:lang('七牛云对象存储')}</b>:{:lang('文件上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
<p><b>{:lang('又拍云USS存储')}</b>:{:lang('文件上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
<p><b>{:lang('阿里云OSS存储')}</b>:{:lang('文件上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
<p><b>{:lang('腾讯云COS存储')}</b>:{:lang('文件上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('系统参数')}</b>( {:lang('当前系统配置参数')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('网站名称')}</b>Website</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('site_name')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('site_name')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('网站名称及网站图标,将显示在浏览器的标签上。')}</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('管理程序名称')}</b>Name</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('app_name')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('app_name')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('管理程序名称,将显示在后台左上角标题。')}</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('管理程序版本')}</b>Version</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('app_version')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('app_version')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('管理程序版本,将显示在后台左上角标题。')}</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('公安备案号')}</b>Beian</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('beian')?:'-'}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('beian')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<p class="help-block">
|
||||
{:lang('公安备案号,可以在 %s 查询获取,将在登录页面下面显示。',['<a target="_blank" href="https://www.beian.gov.cn/portal/registerSystemInfo">www.beian.gov.cn</a>'])}
|
||||
</p>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('网站备案号')}</b>Miitbeian</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('miitbeian')?:'-'}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('miitbeian')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">
|
||||
{:lang('网站备案号,可以在 %s 查询获取,将显示在登录页面下面。',['<a target="_blank" href="https://beian.miit.gov.cn">beian.miit.gov.cn</a>'])}
|
||||
|
||||
|
||||
<!--{notempty name='issuper'}-->
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('运行模式')}</b>( {:lang('仅超级管理员可配置')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-group shadow-mini nowrap">
|
||||
<!--{if $app->isDebug()}-->
|
||||
<a class="layui-btn layui-btn-sm layui-btn-active">{:lang('以开发模式运行')}</a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="{:lang('确定要切换到生产模式运行吗?')}" data-load="{:url('admin/api.system/debug')}?state=1">{:lang('以生产模式运行')}</a>
|
||||
<!--{else}-->
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="{:lang('确定要切换到开发模式运行吗?')}" data-load="{:url('admin/api.system/debug')}?state=0">{:lang('以开发模式运行')}</a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-active">{:lang('以生产模式运行')}</a>
|
||||
<!--{/if}-->
|
||||
</div>
|
||||
<div class="margin-top-20">
|
||||
<p><b>{:lang('开发模式')}</b>:{:lang('开发人员或在功能调试时使用,系统异常时会显示详细的错误信息,同时还会记录操作日志及数据库 SQL 语句信息。')}</p>
|
||||
<p><b>{:lang('生产模式')}</b>:{:lang('项目正式部署上线后使用,系统异常时统一显示 "%s",只记录重要的异常日志信息,强烈推荐上线后使用此模式。',[config('app.error_message')])}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('网站版权信息')}</b>Copyright</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('site_copy')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('site_copy')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('网站版权信息,在后台登录页面显示版本信息并链接到备案到信息备案管理系统。')}</div>
|
||||
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('富编辑器')}</b>( {:lang('仅超级管理员可配置')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body layui-clear">
|
||||
<div class="layui-btn-group shadow-mini">
|
||||
{if !in_array(sysconf('base.editor'),['ckeditor4','ckeditor5','wangEditor','auto'])}{php}sysconf('base.editor','ckeditor4');{/php}{/if}
|
||||
{foreach ['ckeditor4'=>'CKEditor4','ckeditor5'=>'CKEditor5','wangEditor'=>'wangEditor','auto'=>lang('自适应模式')] as $k => $v}{if sysconf('base.editor') eq $k}
|
||||
{if auth('storage')}<a data-title="配置{$v}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
|
||||
{else}
|
||||
{if auth('storage')}<a data-title="配置{$v}" data-action="{:url('admin/api.system/editor')}" data-value="editor#{$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
|
||||
{/if}{/foreach}
|
||||
</div>
|
||||
<div class="margin-top-20 full-width pull-left">
|
||||
<p><b>CKEditor4</b>:{:lang('旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。')}</p>
|
||||
<p><b>CKEditor5</b>:{:lang('新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。')}</p>
|
||||
<p><b>wangEditor</b>:{:lang('国产优质富文本编辑器,对于小程序及App内容支持会更友好,推荐使用。')}</p>
|
||||
<p><b>{:lang('自适应模式')}</b>:{:lang('优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--{/notempty}-->
|
||||
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('存储引擎')}</b>( {:lang('文件默认存储方式')} )
|
||||
</span>
|
||||
</div>
|
||||
<!-- 初始化存储配置 -->
|
||||
{if !sysconf('storage.type')}{php}sysconf('storage.type','local');{/php}{/if}
|
||||
{if !sysconf('storage.link_type')}{php}sysconf('storage.link_type','none');{/php}{/if}
|
||||
{if !sysconf('storage.name_type')}{php}sysconf('storage.name_type','xmd5');{/php}{/if}
|
||||
{if !sysconf('storage.allow_exts')}{php}sysconf('storage.allow_exts','doc,gif,ico,jpg,mp3,mp4,p12,pem,png,rar,xls,xlsx');{/php}{/if}
|
||||
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','follow');{/php}{/if}
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-group shadow-mini nowrap">
|
||||
{foreach $files as $k => $v}{if sysconf('storage.type') eq $k}
|
||||
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
|
||||
{else}
|
||||
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
|
||||
{/if}{/foreach}
|
||||
</div>
|
||||
<div class="margin-top-20 full-width">
|
||||
<p><b>{:lang('本地服务器存储')}</b>:{:lang('文件上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。')}</p>
|
||||
<p><b>{:lang('自建Alist存储')}</b>:{:lang('文件上传到 Alist 存储的服务器或云存储空间,根据服务配置可支持大文件上传,不占用本身服务器空间及服务器带宽流量。')}</p>
|
||||
<p><b>{:lang('七牛云对象存储')}</b>:{:lang('文件上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
<p><b>{:lang('又拍云USS存储')}</b>:{:lang('文件上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
<p><b>{:lang('阿里云OSS存储')}</b>:{:lang('文件上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
<p><b>{:lang('腾讯云COS存储')}</b>:{:lang('文件上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('系统参数')}</b>( {:lang('当前系统配置参数')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('网站名称')}</b>Website</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('site_name')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('site_name')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('网站名称及网站图标,将显示在浏览器的标签上。')}</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('管理程序名称')}</b>Name</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('app_name')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('app_name')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('管理程序名称,将显示在后台左上角标题。')}</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('管理程序版本')}</b>Version</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('app_version')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('app_version')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('管理程序版本,将显示在后台左上角标题。')}</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('公安备案号')}</b>Beian</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('beian')?:'-'}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('beian')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<p class="help-block">
|
||||
{:lang('公安备案号,可以在 %s 查询获取,将在登录页面下面显示。',['<a target="_blank" href="https://www.beian.gov.cn/portal/registerSystemInfo">www.beian.gov.cn</a>'])}
|
||||
</p>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('网站备案号')}</b>Miitbeian</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('miitbeian')?:'-'}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('miitbeian')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">
|
||||
{:lang('网站备案号,可以在 %s 查询获取,将显示在登录页面下面。',['<a target="_blank" href="https://beian.miit.gov.cn">beian.miit.gov.cn</a>'])}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="help-label"><b>{:lang('网站版权信息')}</b>Copyright</div>
|
||||
<label class="relative block">
|
||||
<input readonly value="{:sysconf('site_copy')}" class="layui-input layui-bg-gray">
|
||||
<a data-copy="{:sysconf('site_copy')}" class="layui-icon layui-icon-release input-right-icon"></a>
|
||||
</label>
|
||||
<div class="help-block">{:lang('网站版权信息,在后台登录页面显示版本信息并链接到备案到信息备案管理系统。')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--{if $app->isDebug()}-->
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('系统信息')}</b>( {:lang('仅开发模式可见')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" lay-even>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('核心框架')}</th>
|
||||
<td><a target="_blank" href="https://www.thinkphp.cn">ThinkPHP Version {$framework.version|default='None'}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('平台框架')}</th>
|
||||
<td><a target="_blank" href="https://thinkadmin.top">ThinkAdmin Version {$thinkadmin.version|default='6.0.0'}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('操作系统')}</th>
|
||||
<td>{:php_uname()}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('运行环境')}</th>
|
||||
<td>{:ucfirst($request->server('SERVER_SOFTWARE',php_sapi_name()))} & PHP {$Think.const.PHP_VERSION} & {:ucfirst(app()->db->connect()->getConfig('type'))}</td>
|
||||
</tr>
|
||||
<!-- {notempty name='systemid'} -->
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('系统序号')}</th>
|
||||
<td>{$systemid|default=''}</td>
|
||||
</tr>
|
||||
<!-- {/notempty} -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{notempty name='plugins'}
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('应用插件')}</b>( {:lang('仅开发模式可见')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" lay-even>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('应用名称')}</th>
|
||||
<th class="nowrap text-center">{:lang('插件名称')}</th>
|
||||
<th class="nowrap text-left">{:lang('插件包名')}</th>
|
||||
<th class="nowrap text-center">{:lang('插件版本')}</th>
|
||||
<th class="nowrap text-center">{:lang('授权协议')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $plugins as $key=>$plugin}
|
||||
<tr>
|
||||
<td class="nowrap text-center">{$key}</td>
|
||||
<td class="nowrap text-center">{$plugin.name|lang}</td>
|
||||
<td class="nowrap text-left">
|
||||
{if empty($plugin.install.document)}{$plugin.package}
|
||||
{else}<a target="_blank" href="{$plugin.install.document}">{$plugin.package}</a>{/if}
|
||||
</td>
|
||||
<td class="nowrap text-center">{$plugin.install.version|default='unknow'}</td>
|
||||
<td class="nowrap text-center">
|
||||
{if empty($plugin.install.license)} -
|
||||
{elseif is_array($plugin.install.license)}{$plugin.install.license|join='、',###}
|
||||
{else}{$plugin.install.license|default='-'}{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--{/if}-->
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn" type="submit">保存配置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--{if $app->isDebug()}-->
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('系统信息')}</b>( {:lang('仅开发模式可见')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" lay-even>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('核心框架')}</th>
|
||||
<td><a target="_blank" href="https://www.thinkphp.cn">ThinkPHP Version {$framework.version|default='None'}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('平台框架')}</th>
|
||||
<td><a target="_blank" href="https://thinkadmin.top">ThinkAdmin Version {$thinkadmin.version|default='6.0.0'}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('操作系统')}</th>
|
||||
<td>{:php_uname()}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('运行环境')}</th>
|
||||
<td>{:ucfirst($request->server('SERVER_SOFTWARE',php_sapi_name()))} & PHP {$Think.const.PHP_VERSION} & {:ucfirst(app()->db->connect()->getConfig('type'))}</td>
|
||||
</tr>
|
||||
<!-- {notempty name='systemid'} -->
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('系统序号')}</th>
|
||||
<td>{$systemid|default=''}</td>
|
||||
</tr>
|
||||
<!-- {/notempty} -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{notempty name='plugins'}
|
||||
<div class="layui-card padding-20 shadow">
|
||||
<div class="layui-card-header notselect">
|
||||
<span class="help-label">
|
||||
<b style="color:#333!important;">{:lang('应用插件')}</b>( {:lang('仅开发模式可见')} )
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" lay-even>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="nowrap text-center">{:lang('应用名称')}</th>
|
||||
<th class="nowrap text-center">{:lang('插件名称')}</th>
|
||||
<th class="nowrap text-left">{:lang('插件包名')}</th>
|
||||
<th class="nowrap text-center">{:lang('插件版本')}</th>
|
||||
<th class="nowrap text-center">{:lang('授权协议')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $plugins as $key=>$plugin}
|
||||
<tr>
|
||||
<td class="nowrap text-center">{$key}</td>
|
||||
<td class="nowrap text-center">{$plugin.name|lang}</td>
|
||||
<td class="nowrap text-left">
|
||||
{if empty($plugin.install.document)}{$plugin.package}
|
||||
{else}<a target="_blank" href="{$plugin.install.document}">{$plugin.package}</a>{/if}
|
||||
</td>
|
||||
<td class="nowrap text-center">{$plugin.install.version|default='unknow'}</td>
|
||||
<td class="nowrap text-center">
|
||||
{if empty($plugin.install.license)} -
|
||||
{elseif is_array($plugin.install.license)}{$plugin.install.license|join='、',###}
|
||||
{else}{$plugin.install.license|default='-'}{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/notempty}
|
||||
<!--{/if}-->
|
||||
</form>
|
||||
{/block}
|
||||
@@ -99,6 +99,20 @@
|
||||
<input name="site_copy" required placeholder="请输入版权信息" vali-name="版权信息" value="{:sysconf('site_copy')}" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="layui-form-item margin-bottom-5">
|
||||
<div class="help-label label-required-prev"><b>站点域名</b></div>
|
||||
<label class="relative block label-required-null">
|
||||
<input name="base.site_domain" required placeholder="请输入站点域名" value="{:sysconf('base.site_domain')}" class="layui-input">
|
||||
|
||||
</label>
|
||||
<div class="help-block sub-span-blue">
|
||||
请设置站点访问域名,例如:https://www.example.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-col-xs12 help-block padding-top-0">
|
||||
网站备案号和公安备案号可以在<a target="_blank" href="https://beian.miit.gov.cn">备案管理中心</a>查询并获取,网站上线时必需配置备案号,备案号会链接到信息备案管理系统 ~
|
||||
</div>
|
||||
|
||||
734
app/manager/controller/Account.php
Normal file
734
app/manager/controller/Account.php
Normal file
@@ -0,0 +1,734 @@
|
||||
<?php
|
||||
|
||||
namespace app\manager\controller;
|
||||
|
||||
use think\admin\Controller;
|
||||
use think\admin\service\AdminService;
|
||||
|
||||
/**
|
||||
* Cursor账号管理
|
||||
* Class Account
|
||||
* @package app\manager\controller
|
||||
*/
|
||||
class Account extends Controller
|
||||
{
|
||||
/**
|
||||
* Cursor账号管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->title = 'Cursor账号管理';
|
||||
// 创建查询对象
|
||||
$query = $this->_query('cursor_accounts');
|
||||
// 数据列表处理
|
||||
$query->equal('is_used')->like('email');
|
||||
// 列表排序并显示
|
||||
$query->order('id desc')->page();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成激活码
|
||||
* @auth true
|
||||
*/
|
||||
public function generate()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
// 获取参数
|
||||
$count = intval(input('count', 1));
|
||||
$days = intval(input('days', 30));
|
||||
$agentId = input('agent_id', '');
|
||||
$price = floatval(input('price', 0));
|
||||
|
||||
// 参数验证
|
||||
if ($count < 1 || $count > 500) {
|
||||
$this->error('生成数量必须在1-100之间');
|
||||
}
|
||||
if ($days < 1) {
|
||||
$this->error('有效天数必须大于0');
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
\think\facade\Db::startTrans();
|
||||
|
||||
$codes = [];
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
// 生成激活码
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$codes[] = [
|
||||
'code' => strtoupper(substr(md5(uniqid() . mt_rand()), 0, 16)),
|
||||
'days' => $days,
|
||||
'created_at' => $now,
|
||||
'is_used' => 0,
|
||||
'status' => 1 // 设置默认状态为正常
|
||||
];
|
||||
}
|
||||
|
||||
// 批量插入激活码
|
||||
$result = \think\facade\Db::name('cursor_activation_codes')->insertAll($codes);
|
||||
if (!$result) {
|
||||
\think\facade\Db::rollback();
|
||||
$this->error('激活码生成失败');
|
||||
}
|
||||
|
||||
// 获取插入的ID范围
|
||||
$firstId = \think\facade\Db::name('cursor_activation_codes')->getLastInsID();
|
||||
$lastId = $firstId + count($codes) - 1;
|
||||
|
||||
// 如果指定了代理商,则自动分配
|
||||
if (!empty($agentId) && $price > 0) {
|
||||
$agent = \think\facade\Db::name('cursor_agents')
|
||||
->where('id', $agentId)
|
||||
->find();
|
||||
|
||||
if ($agent) {
|
||||
$agentCodes = [];
|
||||
$commission = $price * ($agent['commission_rate'] / 100);
|
||||
|
||||
// 如果是二级代理,计算上级佣金
|
||||
$parentCommission = 0;
|
||||
if ($agent['level'] == 2 && $agent['parent_id'] > 0) {
|
||||
$parent = \think\facade\Db::name('cursor_agents')
|
||||
->where('id', $agent['parent_id'])
|
||||
->find();
|
||||
if ($parent) {
|
||||
$parentCommission = $price * ($parent['commission_rate'] / 100);
|
||||
}
|
||||
}
|
||||
|
||||
// 准备代理商激活码数据
|
||||
for ($id = $firstId; $id <= $lastId; $id++) {
|
||||
$agentCodes[] = [
|
||||
'agent_id' => $agent['id'],
|
||||
'code_id' => $id,
|
||||
'price' => $price,
|
||||
'commission' => $commission,
|
||||
'parent_commission' => $parentCommission,
|
||||
'status' => 0,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now
|
||||
];
|
||||
}
|
||||
|
||||
// 批量插入代理商激活码关联
|
||||
if (!empty($agentCodes)) {
|
||||
$result = \think\facade\Db::name('cursor_agent_codes')->insertAll($agentCodes);
|
||||
if (!$result) {
|
||||
\think\facade\Db::rollback();
|
||||
$this->error('代理商激活码分配失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\think\facade\Db::commit();
|
||||
$this->success('生成成功!');
|
||||
} else {
|
||||
// 获取代理商列表
|
||||
$this->agents = \think\facade\Db::name('cursor_agents')
|
||||
->where('status', 1)
|
||||
->select()
|
||||
->toArray();
|
||||
$this->fetch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活码管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
*/
|
||||
public function codes()
|
||||
{
|
||||
$this->title = '激活码管理';
|
||||
|
||||
// 获取代理商列表用于筛选
|
||||
$this->agents = \think\facade\Db::name('cursor_agents')
|
||||
->where('status', 1)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 创建查询对象
|
||||
$query = $this->_query('cursor_activation_codes')->alias('c')
|
||||
->leftJoin('cursor_agent_codes ac', 'c.id = ac.code_id')
|
||||
->leftJoin('cursor_agents a', 'ac.agent_id = a.id')
|
||||
->field([
|
||||
'c.*', // 包含所有激活码字段,包括 status
|
||||
'a.id as agent_id',
|
||||
'a.username as agent_name',
|
||||
'a.level as agent_level',
|
||||
'ac.price',
|
||||
'ac.commission',
|
||||
'ac.parent_commission',
|
||||
'ac.status as settle_status'
|
||||
]);
|
||||
|
||||
// 数据列表处理
|
||||
$query->where(function($query) {
|
||||
$agentId = input('agent_id', '');
|
||||
if ($agentId !== '') {
|
||||
$query->where('a.id', '=', $agentId);
|
||||
}
|
||||
|
||||
$isUsed = input('is_used', '');
|
||||
if ($isUsed !== '') {
|
||||
$query->where('c.is_used', '=', $isUsed);
|
||||
}
|
||||
|
||||
$code = input('code', '');
|
||||
if ($code) {
|
||||
$query->whereLike('c.code', "%{$code}%");
|
||||
}
|
||||
|
||||
$usedBy = input('used_by', '');
|
||||
if ($usedBy) {
|
||||
$query->whereLike('c.used_by', "%{$usedBy}%");
|
||||
}
|
||||
|
||||
// 添加天数筛选
|
||||
$days = input('days', '');
|
||||
if ($days !== '') {
|
||||
$query->where('c.days', '=', intval($days));
|
||||
}
|
||||
|
||||
// 添加状态筛选
|
||||
$status = input('status', '');
|
||||
if ($status !== '') {
|
||||
$query->where('c.status', '=', $status);
|
||||
}
|
||||
});
|
||||
|
||||
// 列表排序并显示
|
||||
$query->order('c.id desc')->page();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改账号状态
|
||||
* @auth true
|
||||
*/
|
||||
public function state()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$this->_save('cursor_accounts', ['is_used' => input('state')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除账号
|
||||
* @auth true
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$this->_delete('cursor_accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除激活码
|
||||
* @auth true
|
||||
*/
|
||||
public function removeCode()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$this->_delete('cursor_activation_codes');
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除激活码
|
||||
* @auth true
|
||||
*/
|
||||
public function batchRemoveCodes()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$ids = $this->request->post('ids', '');
|
||||
// 如果是字符串,转换为数组
|
||||
if (is_string($ids)) {
|
||||
$ids = explode(',', $ids);
|
||||
}
|
||||
// 确保ids是数组且不为空
|
||||
if (empty($ids) || !is_array($ids)) {
|
||||
$this->error('请选择要删除的激活码');
|
||||
}
|
||||
|
||||
// 删除激活码
|
||||
$deleteResult1 = \think\facade\Db::name('cursor_activation_codes')->whereIn('id', $ids)->delete();
|
||||
// 删除关联的代理商激活码记录
|
||||
$deleteResult2 = \think\facade\Db::name('cursor_agent_codes')->whereIn('code_id', $ids)->delete();
|
||||
|
||||
// 只要有一个删除操作成功就认为是成功的
|
||||
if ($deleteResult1 || $deleteResult2) {
|
||||
$this->success('批量删除成功!');
|
||||
} else {
|
||||
$this->success('没有需要删除的数据!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备账号管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
*/
|
||||
public function devices()
|
||||
{
|
||||
$this->title = '设备账号管理';
|
||||
|
||||
// 创建查询对象
|
||||
$query = $this->_query('cursor_activation_codes')->alias('c')
|
||||
->field([
|
||||
'c.used_by',
|
||||
'MIN(c.used_at) as first_activation',
|
||||
'COUNT(c.id) as total_codes',
|
||||
'SUM(c.days) as total_days',
|
||||
'GROUP_CONCAT(DISTINCT a.username) as agent_names'
|
||||
])
|
||||
->leftJoin('cursor_agent_codes ac', 'c.id = ac.code_id')
|
||||
->leftJoin('cursor_agents a', 'ac.agent_id = a.id')
|
||||
->where('c.used_by', '<>', '')
|
||||
->where('c.is_used', 1)
|
||||
->group('c.used_by');
|
||||
|
||||
// 数据列表处理
|
||||
$query->where(function($query) {
|
||||
$usedBy = input('used_by', '');
|
||||
if ($usedBy) {
|
||||
$query->whereLike('c.used_by', "%{$usedBy}%");
|
||||
}
|
||||
});
|
||||
|
||||
// 获取数据
|
||||
$result = $query->order('first_activation desc')->page();
|
||||
|
||||
// 处理列表数据
|
||||
$list = [];
|
||||
foreach ($result['list'] as $item) {
|
||||
// 获取当前使用的账号
|
||||
$account = \think\facade\Db::name('cursor_accounts')
|
||||
->where('used_by', $item['used_by'])
|
||||
->where('is_used', 1)
|
||||
->find();
|
||||
|
||||
// 计算到期时间
|
||||
$firstActivationTime = strtotime($item['first_activation']);
|
||||
$expireTime = $firstActivationTime + ($item['total_days'] * 24 * 3600);
|
||||
|
||||
// 检查缓存状态
|
||||
$cacheKey = "device_account_{$item['used_by']}";
|
||||
$cooldownKey = "device_cooldown_{$item['used_by']}";
|
||||
|
||||
$cachedAccount = \think\facade\Cache::get($cacheKey);
|
||||
$cooldownTime = \think\facade\Cache::get($cooldownKey);
|
||||
|
||||
// 构建数据
|
||||
$list[] = [
|
||||
'used_by' => $item['used_by'],
|
||||
'email' => $account ? $account['email'] : '--',
|
||||
'agent_names' => $item['agent_names'] ?: '--',
|
||||
'total_codes' => $item['total_codes'],
|
||||
'total_days' => $item['total_days'],
|
||||
'first_activation' => $item['first_activation'],
|
||||
'expire_time' => date('Y-m-d H:i:s', $expireTime),
|
||||
'days_left' => ceil(($expireTime - time()) / 86400),
|
||||
'has_cache' => !empty($cachedAccount),
|
||||
'in_cooldown' => $cooldownTime > time(),
|
||||
'cooldown_time' => $cooldownTime ? date('Y-m-d H:i:s', $cooldownTime) : ''
|
||||
];
|
||||
}
|
||||
|
||||
// 传递数据到模板
|
||||
$this->assign([
|
||||
'list' => $list,
|
||||
'page' => $result['page']
|
||||
]);
|
||||
|
||||
// 渲染模板
|
||||
$this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置设备账号
|
||||
* @auth true
|
||||
*/
|
||||
public function resetDevice()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
try {
|
||||
$machineId = input('machine_id', '');
|
||||
if (empty($machineId)) {
|
||||
$this->error('设备ID不能为空');
|
||||
}
|
||||
|
||||
// 清除设备的账号缓存
|
||||
$cacheKey = "device_account_{$machineId}";
|
||||
\think\facade\Cache::delete($cacheKey);
|
||||
|
||||
// 清除冷却期
|
||||
$cooldownKey = "device_cooldown_{$machineId}";
|
||||
\think\facade\Cache::delete($cooldownKey);
|
||||
|
||||
// 将该设备使用的账号标记为未使用
|
||||
\think\facade\Db::name('cursor_accounts')
|
||||
->where('used_by', $machineId)
|
||||
->update([
|
||||
'is_used' => 0,
|
||||
'used_at' => null,
|
||||
'used_by' => ''
|
||||
]);
|
||||
|
||||
$this->success('重置成功!');
|
||||
} catch (\Exception $e) {
|
||||
$this->error("重置失败:{$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量切换代理商
|
||||
* @auth true
|
||||
*/
|
||||
public function batchChangeAgent()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->_vali([
|
||||
'agent_id.require' => '代理商不能为空',
|
||||
'code_ids.require' => '请选择要切换的激活码',
|
||||
'price.require' => '销售价格不能为空',
|
||||
'price.float' => '销售价格必须是数字'
|
||||
]);
|
||||
|
||||
// 检查代理商是否存在
|
||||
$agent = \think\facade\Db::name('cursor_agents')
|
||||
->where('id', $data['agent_id'])
|
||||
->where('status', 1)
|
||||
->find();
|
||||
if (empty($agent)) {
|
||||
$this->error('代理商不存在或已被禁用!');
|
||||
}
|
||||
|
||||
// 解析激活码ID
|
||||
$codeIds = explode(',', $data['code_ids']);
|
||||
if (empty($codeIds)) {
|
||||
$this->error('请选择要切换的激活码!');
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
\think\facade\Db::startTrans();
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
// 计算佣金
|
||||
$commission = $data['price'] * ($agent['commission_rate'] / 100);
|
||||
|
||||
// 如果是二级代理,计算上级佣金
|
||||
$parentCommission = 0;
|
||||
if ($agent['level'] == 2 && $agent['parent_id'] > 0) {
|
||||
$parent = \think\facade\Db::name('cursor_agents')
|
||||
->where('id', $agent['parent_id'])
|
||||
->find();
|
||||
if ($parent) {
|
||||
$parentCommission = $data['price'] * ($parent['commission_rate'] / 100);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除旧的代理商关联
|
||||
$deleteResult = \think\facade\Db::name('cursor_agent_codes')
|
||||
->whereIn('code_id', $codeIds)
|
||||
->delete();
|
||||
|
||||
// 批量插入新的关联
|
||||
$insertData = [];
|
||||
foreach ($codeIds as $codeId) {
|
||||
$insertData[] = [
|
||||
'agent_id' => $agent['id'],
|
||||
'code_id' => $codeId,
|
||||
'price' => $data['price'],
|
||||
'commission' => $commission,
|
||||
'parent_commission' => $parentCommission,
|
||||
'status' => 0,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now
|
||||
];
|
||||
}
|
||||
|
||||
// 插入新数据
|
||||
if (!empty($insertData)) {
|
||||
$insertResult = \think\facade\Db::name('cursor_agent_codes')->insertAll($insertData);
|
||||
if ($insertResult === false) {
|
||||
\think\facade\Db::rollback();
|
||||
$this->error('切换失败:插入新数据失败');
|
||||
}
|
||||
}
|
||||
|
||||
\think\facade\Db::commit();
|
||||
$this->success('切换成功!');
|
||||
} else {
|
||||
// 获取代理商列表
|
||||
$this->agents = \think\facade\Db::name('cursor_agents')
|
||||
->where('status', 1)
|
||||
->select()
|
||||
->toArray();
|
||||
$this->fetch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 代理商激活码管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
*/
|
||||
public function agentCodes()
|
||||
{
|
||||
$this->title = '代理商激活码';
|
||||
|
||||
// 获取当前管理员ID和角色
|
||||
$adminId = session('user.id');
|
||||
$isSuper = AdminService::isSuper();
|
||||
|
||||
if (!$isSuper) {
|
||||
// 普通管理员,获取关联的代理商信息
|
||||
$agent = \think\facade\Db::name('cursor_agents')
|
||||
->where('admin_id', $adminId)
|
||||
->where('status', 1)
|
||||
->find();
|
||||
|
||||
if (empty($agent)) {
|
||||
$this->error('未找到关联的代理商账号');
|
||||
}
|
||||
}
|
||||
|
||||
// 创建查询对象
|
||||
$query = $this->_query('cursor_activation_codes')->alias('c')
|
||||
->leftJoin('cursor_agent_codes ac', 'c.id = ac.code_id')
|
||||
->leftJoin('cursor_agents a', 'ac.agent_id = a.id')
|
||||
->field([
|
||||
'c.*', // 包含所有激活码字段,包括 status
|
||||
'ac.price',
|
||||
'ac.commission',
|
||||
'ac.status as settle_status', // 重命名代理商激活码的状态字段
|
||||
'a.username as agent_name',
|
||||
'a.level as agent_level'
|
||||
]);
|
||||
|
||||
// 如果不是超级管理员,只显示自己的激活码
|
||||
if (!$isSuper) {
|
||||
$query->where('ac.agent_id', $agent['id']);
|
||||
}
|
||||
|
||||
// 数据列表处理
|
||||
$query->where(function($query) use ($isSuper) {
|
||||
$code = input('code', '');
|
||||
if ($code) {
|
||||
$query->whereLike('c.code', "%{$code}%");
|
||||
}
|
||||
|
||||
$isUsed = input('is_used', '');
|
||||
if ($isUsed !== '') {
|
||||
$query->where('c.is_used', '=', $isUsed);
|
||||
}
|
||||
|
||||
$status = input('status', '');
|
||||
if ($status !== '') {
|
||||
$query->where('c.status', '=', $status);
|
||||
}
|
||||
|
||||
// 添加天数筛选
|
||||
$days = input('days', '');
|
||||
if ($days !== '') {
|
||||
$query->where('c.days', '=', intval($days));
|
||||
}
|
||||
|
||||
// 超级管理员可以按代理商筛选
|
||||
if ($isSuper) {
|
||||
$agentId = input('agent_id', '');
|
||||
if ($agentId !== '') {
|
||||
$query->where('ac.agent_id', '=', $agentId);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加时间范围筛选
|
||||
$createTime = input('create_time', '');
|
||||
if ($createTime) {
|
||||
$createTimeArr = explode(' - ', $createTime);
|
||||
if (count($createTimeArr) == 2) {
|
||||
$query->whereBetween('c.created_at', [
|
||||
"{$createTimeArr[0]} 00:00:00",
|
||||
"{$createTimeArr[1]} 23:59:59"
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用时间范围筛选
|
||||
$useTime = input('use_time', '');
|
||||
if ($useTime) {
|
||||
$useTimeArr = explode(' - ', $useTime);
|
||||
if (count($useTimeArr) == 2) {
|
||||
$query->whereBetween('c.used_at', [
|
||||
"{$useTimeArr[0]} 00:00:00",
|
||||
"{$useTimeArr[1]} 23:59:59"
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 获取代理商列表(仅超级管理员可见)
|
||||
if ($isSuper) {
|
||||
$this->agents = \think\facade\Db::name('cursor_agents')
|
||||
->where('status', 1)
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// 列表排序并显示
|
||||
$query->order('c.id desc')->page();
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用激活码
|
||||
* @auth true
|
||||
*/
|
||||
public function disableCode()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$id = input('id');
|
||||
|
||||
// 更新激活码状态为禁用 (0)
|
||||
$result = \think\facade\Db::name('cursor_activation_codes')
|
||||
->where('id', $id)
|
||||
->update(['status' => 0]);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->success('禁用成功!');
|
||||
} else {
|
||||
$this->error('禁用失败!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量禁用激活码
|
||||
* @auth true
|
||||
*/
|
||||
public function batchDisableCodes()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$ids = $this->request->post('ids', '');
|
||||
|
||||
if (empty($ids)) {
|
||||
$this->error('请选择要禁用的激活码');
|
||||
}
|
||||
|
||||
// 如果是字符串,转换为数组
|
||||
if (is_string($ids)) {
|
||||
$ids = explode(',', $ids);
|
||||
}
|
||||
|
||||
// 批量更新状态为禁用 (0)
|
||||
$result = \think\facade\Db::name('cursor_activation_codes')
|
||||
->whereIn('id', $ids)
|
||||
->update(['status' => 0]);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->success('批量禁用成功!');
|
||||
} else {
|
||||
$this->error('批量禁用失败!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出激活码到txt文件
|
||||
* @auth true
|
||||
*/
|
||||
public function exportCodes()
|
||||
{
|
||||
try {
|
||||
// 获取当前管理员ID和角色
|
||||
$adminId = session('user.id');
|
||||
$isSuper = AdminService::isSuper();
|
||||
|
||||
if (!$isSuper) {
|
||||
// 普通管理员,获取关联的代理商信息
|
||||
$agent = \think\facade\Db::name('cursor_agents')
|
||||
->where('admin_id', $adminId)
|
||||
->where('status', 1)
|
||||
->find();
|
||||
|
||||
if (empty($agent)) {
|
||||
$this->error('未找到关联的代理商账号');
|
||||
}
|
||||
}
|
||||
|
||||
// 创建查询对象
|
||||
$query = \think\facade\Db::name('cursor_activation_codes')->alias('c')
|
||||
->leftJoin('cursor_agent_codes ac', 'c.id = ac.code_id')
|
||||
->leftJoin('cursor_agents a', 'ac.agent_id = a.id')
|
||||
->field([
|
||||
'c.code',
|
||||
'c.days',
|
||||
'c.is_used',
|
||||
'c.status'
|
||||
]);
|
||||
|
||||
// 如果不是超级管理员,只显示自己的激活码
|
||||
if (!$isSuper) {
|
||||
$query->where('ac.agent_id', $agent['id']);
|
||||
}
|
||||
|
||||
// 应用筛选条件
|
||||
$code = input('code', '');
|
||||
if ($code) {
|
||||
$query->whereLike('c.code', "%{$code}%");
|
||||
}
|
||||
|
||||
$isUsed = input('is_used', '');
|
||||
if ($isUsed !== '') {
|
||||
$query->where('c.is_used', '=', $isUsed);
|
||||
}
|
||||
|
||||
$status = input('status', '');
|
||||
if ($status !== '') {
|
||||
$query->where('c.status', '=', $status);
|
||||
}
|
||||
|
||||
$days = input('days', '');
|
||||
if ($days !== '') {
|
||||
$query->where('c.days', '=', intval($days));
|
||||
}
|
||||
|
||||
// 超级管理员可以按代理商筛选
|
||||
if ($isSuper) {
|
||||
$agentId = input('agent_id', '');
|
||||
if ($agentId !== '') {
|
||||
$query->where('ac.agent_id', '=', $agentId);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
$list = $query->order('c.id desc')->select()->toArray();
|
||||
|
||||
// 生成文件内容
|
||||
$content = '';
|
||||
foreach ($list as $item) {
|
||||
// 获取状态文本
|
||||
$statusText = $item['status'] == 1 ? '正常' : '禁用';
|
||||
$usedText = $item['is_used'] == 1 ? '已使用' : '未使用';
|
||||
|
||||
// 格式化每一行: 激活码 天数 状态
|
||||
$content .= sprintf("%s\t%d\t%s/%s\n",
|
||||
$item['code'],
|
||||
$item['days'],
|
||||
$usedText,
|
||||
$statusText
|
||||
);
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
header('Content-Type: text/plain');
|
||||
header('Content-Disposition: attachment; filename="activation_codes_' . date('YmdHis') . '.txt"');
|
||||
|
||||
// 输出内容
|
||||
echo $content;
|
||||
exit;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
373
app/manager/controller/Agent.php
Normal file
373
app/manager/controller/Agent.php
Normal file
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
|
||||
namespace app\manager\controller;
|
||||
|
||||
use think\admin\Controller;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 代理商管理
|
||||
* Class Agent
|
||||
* @package app\manager\controller
|
||||
*/
|
||||
class Agent extends Controller
|
||||
{
|
||||
/**
|
||||
* 代理商列表
|
||||
* @auth true
|
||||
* @menu true
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->title = '代理商管理';
|
||||
// 创建查询对象
|
||||
$query = $this->_query('cursor_agents');
|
||||
// 数据列表处理
|
||||
$query->equal('level,status')->like('username,nickname');
|
||||
// 列表排序并显示
|
||||
$query->order('id desc')->page();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加代理商
|
||||
* @auth true
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->_vali([
|
||||
'username.require' => '用户名不能为空',
|
||||
'password.require' => '密码不能为空',
|
||||
'nickname.default' => '',
|
||||
'level.require' => '代理级别不能为空',
|
||||
'parent_id.default' => 0,
|
||||
'commission_rate.require' => '佣金比例不能为空',
|
||||
'status.default' => 1,
|
||||
'admin_id.default' => 0, // 绑定的管理员ID
|
||||
'remark.default' => ''
|
||||
]);
|
||||
|
||||
// 检查用户名是否存在
|
||||
if (Db::name('cursor_agents')->where('username', $data['username'])->find()) {
|
||||
$this->error('用户名已存在!');
|
||||
}
|
||||
|
||||
// 如果是二级代理,检查上级代理
|
||||
if ($data['level'] == 2) {
|
||||
if (empty($data['parent_id'])) {
|
||||
$this->error('请选择上级代理!');
|
||||
}
|
||||
$parent = Db::name('cursor_agents')->where('id', $data['parent_id'])->find();
|
||||
if (empty($parent) || $parent['level'] != 1) {
|
||||
$this->error('上级代理不存在或不是一级代理!');
|
||||
}
|
||||
}
|
||||
|
||||
// 如果绑定了管理员账号,检查是否存在
|
||||
if (!empty($data['admin_id'])) {
|
||||
$admin = Db::name('system_user')->where('id', $data['admin_id'])->find();
|
||||
if (empty($admin)) {
|
||||
$this->error('管理员账号不存在!');
|
||||
}
|
||||
// 检查管理员账号是否已被绑定
|
||||
$exists = Db::name('cursor_agents')->where('admin_id', $data['admin_id'])->find();
|
||||
if ($exists) {
|
||||
$this->error('该管理员账号已被其他代理商绑定!');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理密码
|
||||
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
|
||||
$data['created_at'] = $data['updated_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (Db::name('cursor_agents')->insert($data) !== false) {
|
||||
$this->success('添加成功!');
|
||||
} else {
|
||||
$this->error('添加失败!');
|
||||
}
|
||||
} else {
|
||||
// 获取一级代理列表(用于选择上级)
|
||||
$this->agents = Db::name('cursor_agents')
|
||||
->where('level', 1)
|
||||
->where('status', 1)
|
||||
->select();
|
||||
|
||||
// 获取管理员列表
|
||||
$this->admins = Db::name('system_user')
|
||||
->where('status', 1)
|
||||
->field('id, username, nickname')
|
||||
->select();
|
||||
|
||||
$this->fetch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑代理商
|
||||
* @auth true
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->_vali([
|
||||
'id.require' => '代理商ID不能为空',
|
||||
'username.require' => '用户名不能为空',
|
||||
'password.default' => '',
|
||||
'nickname.default' => '',
|
||||
'level.require' => '代理级别不能为空',
|
||||
'parent_id.default' => 0,
|
||||
'commission_rate.require' => '佣金比例不能为空',
|
||||
'status.default' => 1,
|
||||
'admin_id.default' => 0, // 绑定的管理员ID
|
||||
'remark.default' => ''
|
||||
]);
|
||||
|
||||
// 检查用户名是否存在
|
||||
$exists = Db::name('cursor_agents')
|
||||
->where('username', $data['username'])
|
||||
->where('id', '<>', $data['id'])
|
||||
->find();
|
||||
if ($exists) {
|
||||
$this->error('用户名已存在!');
|
||||
}
|
||||
|
||||
// 如果是二级代理,检查上级代理
|
||||
if ($data['level'] == 2) {
|
||||
if (empty($data['parent_id'])) {
|
||||
$this->error('请选择上级代理!');
|
||||
}
|
||||
$parent = Db::name('cursor_agents')->where('id', $data['parent_id'])->find();
|
||||
if (empty($parent) || $parent['level'] != 1) {
|
||||
$this->error('上级代理不存在或不是一级代理!');
|
||||
}
|
||||
}
|
||||
|
||||
// 如果绑定了管理员账号,检查是否存在
|
||||
if (!empty($data['admin_id'])) {
|
||||
$admin = Db::name('system_user')->where('id', $data['admin_id'])->find();
|
||||
if (empty($admin)) {
|
||||
$this->error('管理员账号不存在!');
|
||||
}
|
||||
// 检查管理员账号是否已被其他代理商绑定
|
||||
$exists = Db::name('cursor_agents')
|
||||
->where('admin_id', $data['admin_id'])
|
||||
->where('id', '<>', $data['id'])
|
||||
->find();
|
||||
if ($exists) {
|
||||
$this->error('该管理员账号已被其他代理商绑定!');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理密码
|
||||
if (!empty($data['password'])) {
|
||||
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
|
||||
} else {
|
||||
unset($data['password']);
|
||||
}
|
||||
|
||||
$data['updated_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (Db::name('cursor_agents')->update($data) !== false) {
|
||||
$this->success('更新成功!');
|
||||
} else {
|
||||
$this->error('更新失败!');
|
||||
}
|
||||
} else {
|
||||
// 获取一级代理列表(用于选择上级)
|
||||
$this->agents = Db::name('cursor_agents')
|
||||
->where('level', 1)
|
||||
->where('status', 1)
|
||||
->select();
|
||||
|
||||
// 获取管理员列表
|
||||
$this->admins = Db::name('system_user')
|
||||
->where('status', 1)
|
||||
->field('id, username, nickname')
|
||||
->select();
|
||||
|
||||
$this->_form('cursor_agents', 'form');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改代理商状态
|
||||
* @auth true
|
||||
*/
|
||||
public function state()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$this->_save('cursor_agents', ['status' => input('status')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除代理商
|
||||
* @auth true
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->_applyFormToken();
|
||||
$this->_delete('cursor_agents');
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配激活码
|
||||
* @auth true
|
||||
*/
|
||||
public function assignCodes()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->_vali([
|
||||
'agent_id.require' => '代理商不能为空',
|
||||
'code_ids.require' => '请选择要分配的激活码',
|
||||
'price.require' => '请设置销售价格'
|
||||
]);
|
||||
|
||||
// 开启事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
$agent = Db::name('cursor_agents')->where('id', $data['agent_id'])->find();
|
||||
if (empty($agent)) {
|
||||
$this->error('代理商不存在!');
|
||||
}
|
||||
|
||||
$codeIds = explode(',', $data['code_ids']);
|
||||
$insertData = [];
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
foreach ($codeIds as $codeId) {
|
||||
// 检查激活码是否已分配
|
||||
$exists = Db::name('cursor_agent_codes')->where('code_id', $codeId)->find();
|
||||
if ($exists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算佣金
|
||||
$commission = $data['price'] * ($agent['commission_rate'] / 100);
|
||||
|
||||
// 如果是二级代理,计算上级佣金
|
||||
$parentCommission = 0;
|
||||
if ($agent['level'] == 2 && $agent['parent_id'] > 0) {
|
||||
$parent = Db::name('cursor_agents')->where('id', $agent['parent_id'])->find();
|
||||
if ($parent) {
|
||||
$parentCommission = $data['price'] * ($parent['commission_rate'] / 100);
|
||||
}
|
||||
}
|
||||
|
||||
$insertData[] = [
|
||||
'agent_id' => $data['agent_id'],
|
||||
'code_id' => $codeId,
|
||||
'price' => $data['price'],
|
||||
'commission' => $commission,
|
||||
'parent_commission' => $parentCommission,
|
||||
'status' => 0,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now
|
||||
];
|
||||
}
|
||||
|
||||
// 批量插入数据
|
||||
if (!empty($insertData)) {
|
||||
Db::name('cursor_agent_codes')->insertAll($insertData);
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
$this->success('分配成功!');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
$this->error('分配失败:' . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
// 获取未分配的激活码
|
||||
$this->codes = Db::name('cursor_activation_codes')
|
||||
->where('is_used', 0)
|
||||
->whereNotExists(function($query) {
|
||||
$query->table('cursor_agent_codes')->whereRaw('cursor_agent_codes.code_id = cursor_activation_codes.id');
|
||||
})
|
||||
->select();
|
||||
|
||||
// 获取代理商列表
|
||||
$this->agents = Db::name('cursor_agents')
|
||||
->where('status', 1)
|
||||
->select();
|
||||
|
||||
$this->fetch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看代理商激活码
|
||||
* @auth true
|
||||
*/
|
||||
public function codes()
|
||||
{
|
||||
$this->title = '代理商激活码';
|
||||
$query = $this->_query('cursor_agent_codes')->alias('ac')
|
||||
->join('cursor_agents a', 'a.id = ac.agent_id')
|
||||
->join('cursor_activation_codes c', 'c.id = ac.code_id')
|
||||
->field('ac.*, a.username as agent_name, c.code, c.days, c.is_used, c.used_at, c.used_by');
|
||||
|
||||
// 数据列表处理
|
||||
$query->equal('ac.agent_id')->equal('c.is_used')->equal('ac.status');
|
||||
// 列表排序并显示
|
||||
$query->order('ac.id desc')->page();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结算佣金
|
||||
* @auth true
|
||||
*/
|
||||
public function settle()
|
||||
{
|
||||
$id = input('id');
|
||||
if (empty($id)) {
|
||||
$this->error('参数错误!');
|
||||
}
|
||||
|
||||
$agentCode = Db::name('cursor_agent_codes')->where('id', $id)->find();
|
||||
if (empty($agentCode)) {
|
||||
$this->error('记录不存在!');
|
||||
}
|
||||
|
||||
if ($agentCode['status'] == 1) {
|
||||
$this->error('该记录已结算!');
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 更新代理商余额
|
||||
Db::name('cursor_agents')
|
||||
->where('id', $agentCode['agent_id'])
|
||||
->inc('balance', $agentCode['commission'])
|
||||
->inc('total_income', $agentCode['commission'])
|
||||
->update();
|
||||
|
||||
// 如果有上级佣金,更新上级代理商余额
|
||||
if ($agentCode['parent_commission'] > 0) {
|
||||
$agent = Db::name('cursor_agents')->where('id', $agentCode['agent_id'])->find();
|
||||
if ($agent && $agent['parent_id'] > 0) {
|
||||
Db::name('cursor_agents')
|
||||
->where('id', $agent['parent_id'])
|
||||
->inc('balance', $agentCode['parent_commission'])
|
||||
->inc('total_income', $agentCode['parent_commission'])
|
||||
->update();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新结算状态
|
||||
Db::name('cursor_agent_codes')
|
||||
->where('id', $id)
|
||||
->update([
|
||||
'status' => 1,
|
||||
'settle_time' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
|
||||
Db::commit();
|
||||
$this->success('结算成功!');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
$this->error('结算失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
228
app/manager/controller/Version.php
Normal file
228
app/manager/controller/Version.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\manager\controller;
|
||||
|
||||
use app\admin\model\Version as VersionModel;
|
||||
use think\admin\Controller;
|
||||
use think\admin\extend\DataExtend;
|
||||
use think\admin\service\AdminService;
|
||||
use think\admin\service\SystemService;
|
||||
use think\admin\Storage;
|
||||
|
||||
/**
|
||||
* 版本管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
*/
|
||||
class Version extends Controller
|
||||
{
|
||||
/**
|
||||
* 允许上传的文件类型
|
||||
* @var array
|
||||
*/
|
||||
protected $allowExts = ['zip', 'exe', 'dmg', 'pkg', 'deb', 'rpm'];
|
||||
|
||||
/**
|
||||
* 版本列表
|
||||
* @auth true
|
||||
* @menu true
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->title = '版本管理';
|
||||
// 创建查询对象
|
||||
$query = VersionModel::mQuery();
|
||||
// 数据列表处理
|
||||
$query->like('version_no,version_name')->equal('platform,status');
|
||||
// 列表排序处理
|
||||
$query->order('version_no desc')->page();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加版本
|
||||
* @auth true
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$this->title = '添加版本';
|
||||
VersionModel::mForm('form');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑版本
|
||||
* @auth true
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$this->title = '编辑版本';
|
||||
VersionModel::mForm('form');
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单数据处理
|
||||
*/
|
||||
protected function _form_filter(&$data)
|
||||
{
|
||||
if ($this->request->isGet()) {
|
||||
// 获取版本日志
|
||||
if (isset($data['id'])) {
|
||||
$data['logs'] = \think\facade\Db::name('version_log')
|
||||
->where(['version_id' => $data['id']])
|
||||
->order('id asc')
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
} else {
|
||||
// 检查版本号是否已存在
|
||||
if (empty($data['id'])) {
|
||||
$exists = VersionModel::mk()->where('version_no', $data['version_no'])->find();
|
||||
if ($exists) {
|
||||
$this->error('该版本号已存在!');
|
||||
}
|
||||
}
|
||||
|
||||
// 保存版本日志
|
||||
if (isset($data['logs'])) {
|
||||
$logs = [];
|
||||
foreach ($data['logs']['type'] as $key => $type) {
|
||||
if (!empty($data['logs']['content'][$key])) {
|
||||
$logs[] = [
|
||||
'version_id' => $data['id'] ?? 0,
|
||||
'type' => $type,
|
||||
'content' => $data['logs']['content'][$key],
|
||||
'create_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
}
|
||||
}
|
||||
unset($data['logs']);
|
||||
if (!empty($logs)) {
|
||||
if (isset($data['id'])) {
|
||||
\think\facade\Db::name('version_log')->where(['version_id' => $data['id']])->delete();
|
||||
}
|
||||
\think\facade\Db::name('version_log')->insertAll($logs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除版本
|
||||
* @auth true
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
VersionModel::mDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 版本状态
|
||||
* @auth true
|
||||
*/
|
||||
public function state()
|
||||
{
|
||||
VersionModel::mSave($this->_vali([
|
||||
'status.in:0,1' => '状态值范围异常!',
|
||||
'status.require' => '状态值不能为空!',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传更新包
|
||||
* @auth true
|
||||
*/
|
||||
public function upload()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
try {
|
||||
// 获取上传的文件
|
||||
$file = $this->request->file('file');
|
||||
if (empty($file)) {
|
||||
$this->error('文件上传异常,文件可能过大!');
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
$ext = strtolower($file->getOriginalExtension());
|
||||
if (!in_array($ext, $this->allowExts)) {
|
||||
$this->error('不支持的文件类型!');
|
||||
}
|
||||
|
||||
// 获取原始文件名
|
||||
$originalName = $file->getOriginalName();
|
||||
|
||||
// 从文件名解析版本号
|
||||
$version_no = VersionModel::parseVersionFromFilename($originalName);
|
||||
|
||||
// 生成版本名称
|
||||
$version_name = VersionModel::generateVersionName($version_no, $originalName);
|
||||
|
||||
// 执行文件上传
|
||||
$type = sysconf('storage.type|raw') ?: 'local';
|
||||
if ($type === 'local') {
|
||||
$local = \think\admin\storage\LocalStorage::instance();
|
||||
$distName = dirname($local->path($originalName, false)) . '/' . $originalName;
|
||||
$file->move(dirname($distName), basename($distName));
|
||||
$info = [
|
||||
'url' => '/upload/' . $originalName,
|
||||
'key' => $originalName
|
||||
];
|
||||
|
||||
// 设置上传文件的权限为755
|
||||
@chmod($distName, 0755);
|
||||
} else {
|
||||
$bina = file_get_contents($file->getPathname());
|
||||
$info = \think\admin\Storage::instance($type)->set($originalName, $bina, false, $originalName);
|
||||
}
|
||||
|
||||
|
||||
if (isset($info['url'])) {
|
||||
$data = [
|
||||
'code' => 0,
|
||||
'msg' => '文件上传成功!',
|
||||
'data' => [
|
||||
'url' => $info['url'],
|
||||
'name' => $originalName,
|
||||
'version_no' => $version_no,
|
||||
'version_name' => $version_name
|
||||
]
|
||||
];
|
||||
return json($data);
|
||||
} else {
|
||||
$this->error('文件上传失败!');
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->error($exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件名解析版本信息
|
||||
* @param string $filename
|
||||
* @return array
|
||||
*/
|
||||
protected function parseVersionFromFilename($filename)
|
||||
{
|
||||
// 移除扩展名
|
||||
$name = pathinfo($filename, PATHINFO_FILENAME);
|
||||
|
||||
// 匹配版本号 (支持 v3.4.0 或 3.4.0 格式)
|
||||
if (preg_match('/[v]?(\d+\.\d+\.\d+)/', $name, $matches)) {
|
||||
$version_no = $matches[1];
|
||||
// 提取版本名称 (去掉版本号部分)
|
||||
$version_name = trim(str_replace($matches[0], '', $name));
|
||||
if (empty($version_name)) {
|
||||
$version_name = "版本 {$version_no}";
|
||||
}
|
||||
} else {
|
||||
// 如果没有找到版本号,使用时间戳作为版本号
|
||||
$version_no = date('y.m.d');
|
||||
$version_name = $name ?: "版本 {$version_no}";
|
||||
}
|
||||
|
||||
return [
|
||||
'version_no' => $version_no,
|
||||
'version_name' => $version_name
|
||||
];
|
||||
}
|
||||
}
|
||||
39
app/manager/model/ActivationCode.php
Normal file
39
app/manager/model/ActivationCode.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace app\manager\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class ActivationCode extends Model
|
||||
{
|
||||
protected $name = 'cursor_activation_codes';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'created_at';
|
||||
protected $updateTime = false;
|
||||
|
||||
// 设置字段类型
|
||||
protected $type = [
|
||||
'used_at' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
];
|
||||
|
||||
// 获取代理商分配信息
|
||||
public function agentCode()
|
||||
{
|
||||
return $this->hasOne('app\manager\model\AgentCode', 'code_id');
|
||||
}
|
||||
|
||||
// 检查是否已分配给代理商
|
||||
public function isAssigned()
|
||||
{
|
||||
return (bool)$this->agentCode;
|
||||
}
|
||||
|
||||
// 检查是否可用
|
||||
public function isAvailable()
|
||||
{
|
||||
return !$this->is_used && !$this->isAssigned();
|
||||
}
|
||||
}
|
||||
51
app/manager/model/Agent.php
Normal file
51
app/manager/model/Agent.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace app\manager\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class Agent extends Model
|
||||
{
|
||||
protected $name = 'cursor_agents';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'created_at';
|
||||
protected $updateTime = 'updated_at';
|
||||
|
||||
// 设置字段类型
|
||||
protected $type = [
|
||||
'balance' => 'float',
|
||||
'total_income' => 'float',
|
||||
'commission_rate' => 'float',
|
||||
'last_login_time' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
// 获取父级代理
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(Agent::class, 'parent_id');
|
||||
}
|
||||
|
||||
// 获取子级代理
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(Agent::class, 'parent_id');
|
||||
}
|
||||
|
||||
// 获取代理的激活码
|
||||
public function codes()
|
||||
{
|
||||
return $this->hasMany('app\manager\model\AgentCode', 'agent_id');
|
||||
}
|
||||
|
||||
// 更新余额
|
||||
public function updateBalance($amount)
|
||||
{
|
||||
$this->balance += $amount;
|
||||
$this->total_income += max(0, $amount);
|
||||
return $this->save();
|
||||
}
|
||||
}
|
||||
90
app/manager/model/AgentCode.php
Normal file
90
app/manager/model/AgentCode.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace app\manager\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class AgentCode extends Model
|
||||
{
|
||||
protected $name = 'cursor_agent_codes';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'created_at';
|
||||
protected $updateTime = 'updated_at';
|
||||
|
||||
// 设置字段类型
|
||||
protected $type = [
|
||||
'price' => 'float',
|
||||
'commission' => 'float',
|
||||
'parent_commission' => 'float',
|
||||
'settle_time' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
// 定义卡类型配置
|
||||
public static $cardTypes = [
|
||||
1 => ['days' => 1, 'name' => '1天卡'],
|
||||
5 => ['days' => 5, 'name' => '5天卡'],
|
||||
30 => ['days' => 30, 'name' => '30天卡'],
|
||||
90 => ['days' => 90, 'name' => '三个月卡'],
|
||||
180 => ['days' => 180, 'name' => '六个月卡'],
|
||||
365 => ['days' => 365, 'name' => '一年卡'],
|
||||
];
|
||||
|
||||
// 获取所有卡类型
|
||||
public static function getCardTypes()
|
||||
{
|
||||
return self::$cardTypes;
|
||||
}
|
||||
|
||||
// 获取卡类型名称
|
||||
public static function getCardTypeName($days)
|
||||
{
|
||||
return isset(self::$cardTypes[$days]) ? self::$cardTypes[$days]['name'] : "{$days}天卡";
|
||||
}
|
||||
|
||||
// 获取所属代理商
|
||||
public function agent()
|
||||
{
|
||||
return $this->belongsTo('app\manager\model\Agent', 'agent_id');
|
||||
}
|
||||
|
||||
// 获取激活码信息
|
||||
public function code()
|
||||
{
|
||||
return $this->belongsTo('app\manager\model\ActivationCode', 'code_id');
|
||||
}
|
||||
|
||||
// 结算佣金
|
||||
public function settle()
|
||||
{
|
||||
if ($this->status == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
$this->startTrans();
|
||||
try {
|
||||
// 更新代理商余额
|
||||
$this->agent->updateBalance($this->commission);
|
||||
|
||||
// 如果有上级佣金,更新上级代理商余额
|
||||
if ($this->parent_commission > 0 && $this->agent->parent) {
|
||||
$this->agent->parent->updateBalance($this->parent_commission);
|
||||
}
|
||||
|
||||
// 更新结算状态
|
||||
$this->status = 1;
|
||||
$this->settle_time = date('Y-m-d H:i:s');
|
||||
$this->save();
|
||||
|
||||
$this->commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
$this->rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
278
app/manager/view/account/agent_codes.html
Normal file
278
app/manager/view/account/agent_codes.html
Normal file
@@ -0,0 +1,278 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="button"}
|
||||
{if auth("batchDisableCodes")}
|
||||
<button class='layui-btn layui-btn-sm layui-btn-danger' data-batch-confirm="确定要禁用选中的激活码吗?" data-csrf="{:systoken('batchDisableCodes')}" data-action='{:url("batchDisableCodes")}' data-rule="ids#{key}">批量禁用</button>
|
||||
{/if}
|
||||
<button class='layui-btn layui-btn-sm layui-btn-warm' id="batchCopyBtn" data-checkbox="true">批量复制激活码</button>
|
||||
<a class='layui-btn layui-btn-sm layui-btn-normal' href="{:url('exportCodes')}?{:http_build_query($get)}">导出激活码</a>
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">激活码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="code" value="{$get.code|default=''}" placeholder="请输入激活码" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $Think.admin.is_super}
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">代理商</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='agent_id' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}' {if isset($get.agent_id) and $get.agent_id==$agent.id}selected{/if}>
|
||||
{$agent.username} ({if $agent.level==1}一级代理{else}二级代理{/if})
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='is_used' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='0' {if isset($get.is_used) and $get.is_used=='0'}selected{/if}>未使用</option>
|
||||
<option value='1' {if isset($get.is_used) and $get.is_used=='1'}selected{/if}>已使用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">禁用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='status' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='1' {if isset($get.status) and $get.status=='1'}selected{/if}>正常</option>
|
||||
<option value='0' {if isset($get.status) and $get.status=='0'}selected{/if}>已禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">卡类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='days' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
{foreach :app\manager\model\AgentCode::getCardTypes() as $days => $info}
|
||||
<option value='{$days}' {if isset($get.days) and $get.days==$days}selected{/if}>{$info.name}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">生成时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input data-date-range name="create_time" value="{$get.create_time|default=''}" placeholder="请选择生成时间" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input data-date-range name="use_time" value="{$get.use_time|default=''}" placeholder="请选择使用时间" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='list-table-check-td think-checkbox'>
|
||||
<label><input data-auto-none data-check-target='.list-check-box' type='checkbox'></label>
|
||||
</th>
|
||||
<th class='text-left'>激活码</th>
|
||||
<th class='text-left'>有效天数</th>
|
||||
{if $Think.admin.is_super}
|
||||
<th class='text-left'>代理商</th>
|
||||
<th class='text-left'>代理级别</th>
|
||||
{/if}
|
||||
<th class='text-left'>销售价格</th>
|
||||
<th class='text-left'>佣金</th>
|
||||
<th class='text-left'>使用状态</th>
|
||||
<th class='text-left'>使用时间</th>
|
||||
<th class='text-left'>使用者</th>
|
||||
<th class='text-left'>生成时间</th>
|
||||
<th class='text-left'>状态</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr>
|
||||
<td class='list-table-check-td think-checkbox'>
|
||||
<label><input class="list-check-box" value='{$vo.id}' type='checkbox'></label>
|
||||
</td>
|
||||
<td class='text-left'>{$vo.code}</td>
|
||||
<td class='text-left'>{$vo.days} 天</td>
|
||||
{if $Think.admin.is_super}
|
||||
<td class='text-left'>{$vo.agent_name|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.agent_level eq 1}
|
||||
<span class="color-blue">一级代理</span>
|
||||
{elseif $vo.agent_level eq 2}
|
||||
<span class="color-green">二级代理</span>
|
||||
{else}
|
||||
--
|
||||
{/if}
|
||||
</td>
|
||||
{/if}
|
||||
<td class='text-left'>{$vo.price|default='--'}</td>
|
||||
<td class='text-left'>{$vo.commission|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.is_used eq 1}
|
||||
<span class="color-red">已使用</span>
|
||||
{else}
|
||||
<span class="color-green">未使用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.used_at|format_datetime|default='--'}</td>
|
||||
<td class='text-left'>{$vo.used_by|default='--'}</td>
|
||||
<td class='text-left'>{$vo.created_at|format_datetime}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.status eq 1}
|
||||
<span class="color-green">正常</span>
|
||||
{else}
|
||||
<span class="color-red">已禁用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>
|
||||
{if auth("disableCode") && $vo.status eq 1}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要禁用该激活码吗?" data-csrf="{:systoken('disableCode')}" data-action='{:url("disableCode")}' data-value="id#{$vo.id}">禁用</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
|
||||
</div>
|
||||
|
||||
<!-- 复制弹窗 -->
|
||||
<div id="copyDialog" style="display:none;">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<textarea id="codeList" class="layui-textarea" style="height: 500px;margin:10px 0;font-size:14px;line-height:1.8;" readonly></textarea>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button class="layui-btn layui-btn-lg" onclick="copyToClipboard()">一键复制</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 批量复制按钮点击事件
|
||||
$('#batchCopyBtn').click(function(){
|
||||
var codes = [];
|
||||
$('.list-check-box:checked').each(function(){
|
||||
var tr = $(this).closest('tr');
|
||||
var code = tr.find('td:eq(1)').text(); // 激活码
|
||||
var days = tr.find('td:eq(2)').text(); // 天数
|
||||
codes.push(code + ' - ' + days);
|
||||
});
|
||||
|
||||
if(codes.length === 0){
|
||||
layer.msg('请选择要复制的激活码');
|
||||
return;
|
||||
}
|
||||
|
||||
// 格式化激活码列表
|
||||
var formattedCodes = codes.map(function(item){
|
||||
return item.trim();
|
||||
}).join('\n\n');
|
||||
|
||||
// 显示弹窗
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '复制激活码',
|
||||
area: ['800px', '700px'],
|
||||
content: $('#copyDialog'),
|
||||
offset: '50px',
|
||||
fixed: false,
|
||||
resize: true,
|
||||
shadeClose: true,
|
||||
shade: [0.3, '#000'],
|
||||
maxmin: true,
|
||||
moveOut: true,
|
||||
anim: 1,
|
||||
zIndex: layer.zIndex + 1000, // 增加 zIndex 确保显示在最上层
|
||||
success: function(layero, index){
|
||||
layer.setTop(layero);
|
||||
$('#codeList').val(formattedCodes);
|
||||
|
||||
// 添加自定义样式
|
||||
$(layero).css({
|
||||
'background-color': '#fff',
|
||||
'box-shadow': '0 2px 8px rgba(0,0,0,.1)'
|
||||
}).find('.layui-layer-content').css({
|
||||
'padding': '30px'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 复制到剪贴板
|
||||
function copyToClipboard() {
|
||||
var textarea = document.getElementById('codeList');
|
||||
textarea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
layer.msg('复制成功!', {icon: 1, zIndex: layer.zIndex + 101});
|
||||
} catch (err) {
|
||||
layer.msg('复制失败,请手动复制', {icon: 2, zIndex: layer.zIndex + 101});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 美化时间选择器样式 */
|
||||
.layui-laydate {
|
||||
border: none !important;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
}
|
||||
.layui-laydate-header {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.layui-laydate-content td:hover,
|
||||
.layui-laydate-content td.laydate-selected {
|
||||
border-radius: 2px;
|
||||
}
|
||||
.layui-laydate-footer {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
.laydate-time-list>li:hover {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.layui-laydate-content td.layui-this {
|
||||
border-radius: 2px;
|
||||
}
|
||||
/* 输入框样式优化 */
|
||||
.layui-input[readonly] {
|
||||
background-color: #fff !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
.layui-input:hover {
|
||||
border-color: #D2D2D2 !important;
|
||||
}
|
||||
.layui-input:focus {
|
||||
border-color: #009688 !important;
|
||||
box-shadow: 0 0 0 2px rgba(0,150,136,.2);
|
||||
}
|
||||
</style>
|
||||
{/block}
|
||||
44
app/manager/view/account/batch_change_agent.html
Normal file
44
app/manager/view/account/batch_change_agent.html
Normal file
@@ -0,0 +1,44 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">选择代理商</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='agent_id' class='layui-select' lay-search required>
|
||||
<option value=''>请选择代理商</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}'>{$agent.username} ({if $agent.level==1}一级代理{else}二级代理{/if})</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">销售价格</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="price" value='' required placeholder="请输入销售价格" class="layui-input">
|
||||
<tip class="help-block">设置激活码的销售价格,用于计算代理商佣金</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type='hidden' name='code_ids' value=''>
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn" type='submit'>保存设置</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-close>取消</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// 获取选中的激活码ID
|
||||
var ids = [];
|
||||
parent.$('.list-check-box:checked').map(function(){
|
||||
ids.push($(this).val());
|
||||
});
|
||||
// 设置到隐藏字段
|
||||
$('input[name="code_ids"]').val(ids.join(','));
|
||||
</script>
|
||||
{/block}
|
||||
251
app/manager/view/account/codes.html
Normal file
251
app/manager/view/account/codes.html
Normal file
@@ -0,0 +1,251 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="button"}
|
||||
<button class='layui-btn layui-btn-sm layui-btn-primary' data-modal='{:url("generate")}'>生成激活码</button>
|
||||
{if auth("batchRemoveCodes")}
|
||||
<button class='layui-btn layui-btn-sm layui-btn-danger' data-batch-confirm="确定要删除选中的激活码吗?" data-csrf="{:systoken('batchRemoveCodes')}" data-action='{:url("batchRemoveCodes")}' data-rule="ids#{key}">批量删除</button>
|
||||
{/if}
|
||||
{if auth("batchChangeAgent")}
|
||||
<button class='layui-btn layui-btn-sm layui-btn-normal' data-modal='{:url("batchChangeAgent")}' data-title="批量切换代理商" data-checkbox="true">批量切换代理商</button>
|
||||
{/if}
|
||||
<button class='layui-btn layui-btn-sm layui-btn-warm' id="batchCopyBtn" data-checkbox="true">批量复制激活码</button>
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">激活码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="code" value="{$get.code|default=''}" placeholder="请输入激活码" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">代理商</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='agent_id' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}' {if isset($get.agent_id) and $get.agent_id==$agent.id}selected{/if}>
|
||||
{$agent.username} ({if $agent.level==1}一级代理{else}二级代理{/if})
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='is_used' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='0' {if isset($get.is_used) and $get.is_used=='0'}selected{/if}>未使用</option>
|
||||
<option value='1' {if isset($get.is_used) and $get.is_used=='1'}selected{/if}>已使用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">禁用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='status' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='1' {if isset($get.status) and $get.status=='1'}selected{/if}>正常</option>
|
||||
<option value='0' {if isset($get.status) and $get.status=='0'}selected{/if}>已禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">卡类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='days' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
{foreach :app\manager\model\AgentCode::getCardTypes() as $days => $info}
|
||||
<option value='{$days}' {if isset($get.days) and $get.days==$days}selected{/if}>{$info.name}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='list-table-check-td think-checkbox'>
|
||||
<label><input data-auto-none data-check-target='.list-check-box' type='checkbox'></label>
|
||||
</th>
|
||||
<th class='text-left'>激活码</th>
|
||||
<th class='text-left'>有效天数</th>
|
||||
<th class='text-left'>代理商</th>
|
||||
<th class='text-left'>代理级别</th>
|
||||
<th class='text-left'>销售价格</th>
|
||||
<th class='text-left'>佣金</th>
|
||||
<th class='text-left'>上级佣金</th>
|
||||
<th class='text-left'>结算状态</th>
|
||||
<th class='text-left'>使用状态</th>
|
||||
<th class='text-left'>使用时间</th>
|
||||
<th class='text-left'>使用者</th>
|
||||
<th class='text-left'>生成时间</th>
|
||||
<th class='text-left'>状态</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr>
|
||||
<td class='list-table-check-td think-checkbox'>
|
||||
<label><input class="list-check-box" value='{$vo.id}' type='checkbox'></label>
|
||||
</td>
|
||||
<td class='text-left'>{$vo.code}</td>
|
||||
<td class='text-left'>{$vo.days} 天</td>
|
||||
<td class='text-left'>{$vo.agent_name|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.agent_level eq 1}
|
||||
<span class="color-blue">一级代理</span>
|
||||
{elseif $vo.agent_level eq 2}
|
||||
<span class="color-green">二级代理</span>
|
||||
{else}
|
||||
--
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.price|default='--'}</td>
|
||||
<td class='text-left'>{$vo.commission|default='--'}</td>
|
||||
<td class='text-left'>{$vo.parent_commission|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if isset($vo.settle_status)}
|
||||
{if $vo.settle_status eq 1}
|
||||
<span class="color-blue">已结算</span>
|
||||
{else}
|
||||
<span class="color-red">未结算</span>
|
||||
{/if}
|
||||
{else}
|
||||
--
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.is_used eq 1}
|
||||
<span class="color-red">已使用</span>
|
||||
{else}
|
||||
<span class="color-green">未使用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.used_at|format_datetime|default='--'}</td>
|
||||
<td class='text-left'>{$vo.used_by|default='--'}</td>
|
||||
<td class='text-left'>{$vo.created_at|format_datetime}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.status eq 1}
|
||||
<span class="color-green">正常</span>
|
||||
{else}
|
||||
<span class="color-red">已禁用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>
|
||||
{if auth("removeCode")}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除该激活码吗?" data-csrf="{:systoken('removeCode')}" data-action='{:url("removeCode")}' data-value="id#{$vo.id}">删除</a>
|
||||
{/if}
|
||||
{if auth("disableCode") && $vo.status eq 1}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要禁用该激活码吗?" data-csrf="{:systoken('disableCode')}" data-action='{:url("disableCode")}' data-value="id#{$vo.id}">禁用</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
|
||||
</div>
|
||||
|
||||
<!-- 复制弹窗 -->
|
||||
<div id="copyDialog" style="display:none;">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<textarea id="codeList" class="layui-textarea" style="height: 500px;margin:10px 0;font-size:14px;line-height:1.8;" readonly></textarea>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button class="layui-btn layui-btn-lg" onclick="copyToClipboard()">一键复制</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 批量复制按钮点击事件
|
||||
$('#batchCopyBtn').click(function(){
|
||||
var codes = [];
|
||||
$('.list-check-box:checked').each(function(){
|
||||
var tr = $(this).closest('tr');
|
||||
var code = tr.find('td:eq(1)').text(); // 激活码
|
||||
var days = tr.find('td:eq(2)').text(); // 天数
|
||||
codes.push(code + ' - ' + days);
|
||||
});
|
||||
|
||||
if(codes.length === 0){
|
||||
layer.msg('请选择要复制的激活码');
|
||||
return;
|
||||
}
|
||||
|
||||
// 格式化激活码列表
|
||||
var formattedCodes = codes.map(function(item){
|
||||
return item.trim();
|
||||
}).join('\n\n');
|
||||
|
||||
// 显示弹窗
|
||||
var index = layer.open({
|
||||
type: 1,
|
||||
title: '复制激活码',
|
||||
area: ['800px', '700px'],
|
||||
content: $('#copyDialog'),
|
||||
offset: '50px',
|
||||
fixed: false,
|
||||
resize: true,
|
||||
shadeClose: true,
|
||||
shade: [0.3, '#000'],
|
||||
maxmin: true,
|
||||
moveOut: true,
|
||||
anim: 1,
|
||||
isOutAnim: false,
|
||||
zIndex: layer.zIndex + 1000, // 增加 zIndex 确保显示在最上层
|
||||
success: function(layero, index){
|
||||
layer.setTop(layero);
|
||||
$('#codeList').val(formattedCodes);
|
||||
|
||||
// 添加自定义样式
|
||||
$(layero).css({
|
||||
'background-color': '#fff',
|
||||
'box-shadow': '0 2px 8px rgba(0,0,0,.1)'
|
||||
}).find('.layui-layer-content').css({
|
||||
'padding': '30px'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 设置最高层级
|
||||
layer.setTop($('.layui-layer[times="' + index + '"]'));
|
||||
});
|
||||
|
||||
// 复制到剪贴板
|
||||
function copyToClipboard() {
|
||||
var textarea = document.getElementById('codeList');
|
||||
textarea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
layer.msg('复制成功!', {icon: 1, zIndex: layer.zIndex + 1001});
|
||||
} catch (err) {
|
||||
layer.msg('复制失败,请手动复制', {icon: 2, zIndex: layer.zIndex + 1001});
|
||||
}
|
||||
}
|
||||
|
||||
function setDays(days) {
|
||||
document.getElementById('daysInput').value = days;
|
||||
document.querySelector('form.form-search').submit();
|
||||
}
|
||||
</script>
|
||||
{/block}
|
||||
85
app/manager/view/account/devices.html
Normal file
85
app/manager/view/account/devices.html
Normal file
@@ -0,0 +1,85 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">账号邮箱</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="email" value="{$get.email|default=''}" placeholder="请输入账号邮箱" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">设备ID</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="used_by" value="{$get.used_by|default=''}" placeholder="请输入设备ID" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='text-left'>设备ID</th>
|
||||
<th class='text-left'>账号邮箱</th>
|
||||
<th class='text-left'>代理商</th>
|
||||
<th class='text-left'>激活码数量</th>
|
||||
<th class='text-left'>总天数</th>
|
||||
<th class='text-left'>首次激活</th>
|
||||
<th class='text-left'>到期时间</th>
|
||||
<th class='text-left'>剩余天数</th>
|
||||
<th class='text-left'>缓存状态</th>
|
||||
<th class='text-left'>冷却状态</th>
|
||||
<th class='text-left'>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr>
|
||||
<td class='text-left'>{$vo.used_by|default='--'}</td>
|
||||
<td class='text-left'>{if isset($vo.email)}{$vo.email}{else}--{/if}</td>
|
||||
<td class='text-left'>{if isset($vo.agent_names)}{$vo.agent_names}{else}--{/if}</td>
|
||||
<td class='text-left'>{if isset($vo.total_codes)}{$vo.total_codes}{else}0{/if}</td>
|
||||
<td class='text-left'>{if isset($vo.total_days)}{$vo.total_days} 天{else}0 天{/if}</td>
|
||||
<td class='text-left'>{if isset($vo.first_activation)}{$vo.first_activation|format_datetime}{else}--{/if}</td>
|
||||
<td class='text-left'>{if isset($vo.expire_time)}{$vo.expire_time}{else}--{/if}</td>
|
||||
<td class='text-left'>
|
||||
{if isset($vo.days_left) && $vo.days_left > 0}
|
||||
<span class="color-green">{$vo.days_left} 天</span>
|
||||
{else}
|
||||
<span class="color-red">已过期</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>
|
||||
{if isset($vo.has_cache) && $vo.has_cache}
|
||||
<span class="color-green">已缓存</span>
|
||||
{else}
|
||||
<span class="color-gray">无缓存</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>
|
||||
{if isset($vo.in_cooldown) && $vo.in_cooldown}
|
||||
<span class="color-red">冷却中 {if isset($vo.cooldown_time)}(至 {$vo.cooldown_time}){/if}</span>
|
||||
{else}
|
||||
<span class="color-green">可用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>
|
||||
{if auth("resetDevice")}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-warm" data-confirm="确定要重置该设备的账号吗?" data-csrf="{:systoken('resetDevice')}" data-action='{:url("resetDevice")}' data-value="machine_id#{$vo.used_by}">重置账号</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
|
||||
</div>
|
||||
{/block}
|
||||
84
app/manager/view/account/generate.html
Normal file
84
app/manager/view/account/generate.html
Normal file
@@ -0,0 +1,84 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">生成数量</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="number" name="count" value="1" min="1" max="500" required placeholder="请输入生成数量(1-500)" class="layui-input">
|
||||
<tip class="help-block">单次可生成1-500个激活码</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">有效天数</label>
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-btn-group-days" style="margin-bottom: 10px;">
|
||||
{foreach :app\manager\model\AgentCode::getCardTypes() as $days => $info}
|
||||
<button type="button" class="layui-btn layui-btn-sm" data-days="{$days}">{$info.name}</button>
|
||||
{/foreach}
|
||||
</div>
|
||||
<input type="number" name="days" value="30" min="1" required placeholder="请输入有效天数" class="layui-input">
|
||||
<tip class="help-block">激活码的有效使用天数</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">选择代理</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='agent_id' class='layui-select' lay-search>
|
||||
<option value=''>不分配给代理商</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}'>{$agent.username} ({if $agent.level==1}一级代理{else}二级代理{/if})</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
<tip class="help-block">可以选择直接分配给代理商</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">销售价格</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="number" name="price" value="0" min="0" step="0.01" placeholder="请输入销售价格" class="layui-input">
|
||||
<tip class="help-block">设置激活码的销售价格,用于计算代理商佣金(选择代理商时必填)</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn" type='submit'>确定生成</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-close>取消</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
layui.use(['form'], function(){
|
||||
// 快捷选择天数
|
||||
$('.layui-btn-group-days .layui-btn').click(function(){
|
||||
var days = $(this).data('days');
|
||||
$('input[name="days"]').val(days);
|
||||
});
|
||||
|
||||
// 监听代理商选择
|
||||
layui.form.on('select(agent_id)', function(data){
|
||||
var price = $('input[name="price"]');
|
||||
if(data.value){
|
||||
price.attr('required', true);
|
||||
} else {
|
||||
price.removeAttr('required');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.layui-btn-group .layui-btn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.layui-btn-group .layui-btn:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
{/block}
|
||||
86
app/manager/view/account/index.html
Normal file
86
app/manager/view/account/index.html
Normal file
@@ -0,0 +1,86 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="button"}
|
||||
<!--{if auth("add")}-->
|
||||
<button class='layui-btn layui-btn-sm layui-btn-primary' data-modal='{:url("add")}'>添加账号</button>
|
||||
<!--{/if}-->
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">邮箱账号</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="email" value="{$get.email|default=''}" placeholder="请输入邮箱账号" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='is_used' lay-search class='layui-select' lay-filter='is_used'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='0' {if isset($get.is_used) and $get.is_used=='0'}selected{/if}>未使用</option>
|
||||
<option value='1' {if isset($get.is_used) and $get.is_used=='1'}selected{/if}>已使用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table id="AccountTable" class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='list-table-check-td think-checkbox'>
|
||||
<label><input data-auto-none data-check-target='.list-check-box' type='checkbox'></label>
|
||||
</th>
|
||||
<th class='text-left'>邮箱账号</th>
|
||||
<th class='text-left'>姓名</th>
|
||||
<th class='text-left'>机器ID</th>
|
||||
<th class='text-left'>注册时间</th>
|
||||
<th class='text-left'>使用状态</th>
|
||||
<th class='text-left'>使用时间</th>
|
||||
<th class='text-left'>使用者</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr>
|
||||
<td class='list-table-check-td think-checkbox'>
|
||||
<label><input class="list-check-box" value='{$vo.id}' type='checkbox'></label>
|
||||
</td>
|
||||
<td class='text-left'>{$vo.email|default=''}</td>
|
||||
<td class='text-left'>{$vo.first_name|default=''} {$vo.last_name|default=''}</td>
|
||||
<td class='text-left'>{$vo.machine_id|default=''}</td>
|
||||
<td class='text-left'>{$vo.registration_time|format_datetime}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.is_used eq 1}
|
||||
<span class="color-red">已使用</span>
|
||||
{else}
|
||||
<span class="color-green">未使用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.used_at|format_datetime|default='--'}</td>
|
||||
<td class='text-left'>{$vo.used_by|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if auth("state") and $vo.is_used eq 1}
|
||||
<a class="layui-btn layui-btn-sm" data-action='{:url("state")}' data-value="id#{$vo.id};state#0" data-csrf="{:systoken('state')}">标记未使用</a>
|
||||
{/if}
|
||||
{if auth("remove")}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除该账号吗?" data-csrf="{:systoken('remove')}" data-action='{:url("remove")}' data-value="id#{$vo.id}">删 除</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
|
||||
</div>
|
||||
{/block}
|
||||
70
app/manager/view/account/index_search.html
Normal file
70
app/manager/view/account/index_search.html
Normal file
@@ -0,0 +1,70 @@
|
||||
{include file='../../admin/view/search'}
|
||||
|
||||
<fieldset>
|
||||
<legend>条件搜索</legend>
|
||||
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">邮箱账号</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="email" value="{$get.email|default=''}" placeholder="请输入邮箱账号" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">姓名</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="first_name" value="{$get.first_name|default=''}" placeholder="请输入名字" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">机器ID</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="machine_id" value="{$get.machine_id|default=''}" placeholder="请输入机器ID" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select class="layui-select" name="is_used">
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='0' {if $get.is_used eq '0'}selected{/if}>未使用</option>
|
||||
<option value='1' {if $get.is_used eq '1'}selected{/if}>已使用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用者</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="used_by" value="{$get.used_by|default=''}" placeholder="请输入使用者" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">注册时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input data-date-range name="registration_time" value="{$get.registration_time|default=''}" placeholder="请选择注册时间" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">创建时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input data-date-range name="created_at" value="{$get.created_at|default=''}" placeholder="请选择创建时间" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input data-date-range name="used_at" value="{$get.used_at|default=''}" placeholder="请选择使用时间" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
106
app/manager/view/agent/add.html
Normal file
106
app/manager/view/agent/add.html
Normal file
@@ -0,0 +1,106 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="username" value='' required placeholder="请输入用户名" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="password" value='' required placeholder="请输入密码" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">昵称</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="nickname" value='' placeholder="请输入昵称" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">代理级别</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='level' class='layui-select' lay-filter='level' required>
|
||||
<option value=''>请选择代理级别</option>
|
||||
<option value='1'>一级代理</option>
|
||||
<option value='2'>二级代理</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item parent-agent" style="display:none;">
|
||||
<label class="layui-form-label">上级代理</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='parent_id' class='layui-select'>
|
||||
<option value=''>请选择上级代理</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}'>{$agent.username}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">绑定管理员</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='admin_id' class='layui-select'>
|
||||
<option value=''>请选择管理员账号</option>
|
||||
{foreach $admins as $admin}
|
||||
<option value='{$admin.id}'>{$admin.username} ({$admin.nickname})</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
<tip class="help-block">选择要绑定的管理员账号,绑定后该管理员可以作为二级代理</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">佣金比例</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="commission_rate" value='0' required placeholder="请输入佣金比例" class="layui-input">
|
||||
<tip class="help-block">请输入0-100之间的数字,表示佣金百分比</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='status' class='layui-select'>
|
||||
<option value='1'>正常</option>
|
||||
<option value='0'>禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">备注</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="remark" placeholder="请输入备注" class="layui-textarea"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn" type='submit'>保存数据</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-close>取消编辑</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
layui.form.on('select(level)', function(data){
|
||||
if(data.value == '2'){
|
||||
$('.parent-agent').show();
|
||||
}else{
|
||||
$('.parent-agent').hide();
|
||||
$('select[name=parent_id]').val('');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
63
app/manager/view/agent/assign_codes.html
Normal file
63
app/manager/view/agent/assign_codes.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">选择代理</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='agent_id' class='layui-select' lay-verify="required">
|
||||
<option value=''>请选择代理商</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}'>{$agent.username} ({if $agent.level==1}一级代理{else}二级代理{/if})</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">销售价格</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="price" value='0' required placeholder="请输入销售价格" class="layui-input">
|
||||
<tip class="help-block">设置激活码的销售价格,用于计算佣金</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">选择激活码</label>
|
||||
<div class="layui-input-block">
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='list-table-check-td think-checkbox'>
|
||||
<label><input data-auto-none data-check-target='.list-check-box' type='checkbox'></label>
|
||||
</th>
|
||||
<th>激活码</th>
|
||||
<th>有效天数</th>
|
||||
<th>生成时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $codes as $code}
|
||||
<tr>
|
||||
<td class='list-table-check-td think-checkbox'>
|
||||
<label><input class="list-check-box" name="code_ids[]" value='{$code.id}' type='checkbox'></label>
|
||||
</td>
|
||||
<td>{$code.code}</td>
|
||||
<td>{$code.days} 天</td>
|
||||
<td>{$code.created_at}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn" type='submit'>确定分配</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-close>取消</button>
|
||||
</div>
|
||||
</form>
|
||||
{/block}
|
||||
103
app/manager/view/agent/codes.html
Normal file
103
app/manager/view/agent/codes.html
Normal file
@@ -0,0 +1,103 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">代理商</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='agent_id' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}' {if isset($get.agent_id) and $get.agent_id==$agent.id}selected{/if}>
|
||||
{$agent.username} ({if $agent.level==1}一级代理{else}二级代理{/if})
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='is_used' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='0' {if isset($get.is_used) and $get.is_used=='0'}selected{/if}>未使用</option>
|
||||
<option value='1' {if isset($get.is_used) and $get.is_used=='1'}selected{/if}>已使用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">结算状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='status' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='0' {if isset($get.status) and $get.status=='0'}selected{/if}>未结算</option>
|
||||
<option value='1' {if isset($get.status) and $get.status=='1'}selected{/if}>已结算</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='text-left'>激活码</th>
|
||||
<th class='text-left'>代理商</th>
|
||||
<th class='text-left'>有效天数</th>
|
||||
<th class='text-left'>销售价格</th>
|
||||
<th class='text-left'>佣金</th>
|
||||
<th class='text-left'>上级佣金</th>
|
||||
<th class='text-left'>使用状态</th>
|
||||
<th class='text-left'>使用时间</th>
|
||||
<th class='text-left'>使用者</th>
|
||||
<th class='text-left'>结算状态</th>
|
||||
<th class='text-left'>分配时间</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr>
|
||||
<td class='text-left'>{$vo.code}</td>
|
||||
<td class='text-left'>{$vo.agent_name}</td>
|
||||
<td class='text-left'>{$vo.days} 天</td>
|
||||
<td class='text-left'>{$vo.price}</td>
|
||||
<td class='text-left'>{$vo.commission}</td>
|
||||
<td class='text-left'>{$vo.parent_commission|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.is_used eq 1}
|
||||
<span class="color-red">已使用</span>
|
||||
{else}
|
||||
<span class="color-green">未使用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.used_at|format_datetime|default='--'}</td>
|
||||
<td class='text-left'>{$vo.used_by|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.status eq 1}
|
||||
<span class="color-blue">已结算</span>
|
||||
{else}
|
||||
<span class="color-red">未结算</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.created_at|format_datetime}</td>
|
||||
<td class='text-left'>
|
||||
{if auth("settle") and $vo.status eq 0 and $vo.is_used eq 1}
|
||||
<a class="layui-btn layui-btn-sm" data-action='{:url("settle")}' data-value="id#{$vo.id}" data-csrf="{:systoken('settle')}">结 算</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
|
||||
</div>
|
||||
{/block}
|
||||
107
app/manager/view/agent/form.html
Normal file
107
app/manager/view/agent/form.html
Normal file
@@ -0,0 +1,107 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="id" type="hidden" value='{$vo.id|default=""}'>
|
||||
<input name="username" value='{$vo.username|default=""}' required placeholder="请输入用户名" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="password" value='' placeholder="请输入密码,不修改请留空" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">昵称</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="nickname" value='{$vo.nickname|default=""}' placeholder="请输入昵称" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">代理级别</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='level' class='layui-select' lay-filter='level' required>
|
||||
<option value=''>请选择代理级别</option>
|
||||
<option value='1' {if isset($vo.level) && $vo.level==1}selected{/if}>一级代理</option>
|
||||
<option value='2' {if isset($vo.level) && $vo.level==2}selected{/if}>二级代理</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item parent-agent" style="display:{if isset($vo.level) && $vo.level==2}block{else}none{/if};">
|
||||
<label class="layui-form-label">上级代理</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='parent_id' class='layui-select'>
|
||||
<option value=''>请选择上级代理</option>
|
||||
{foreach $agents as $agent}
|
||||
<option value='{$agent.id}' {if isset($vo.parent_id) && $vo.parent_id==$agent.id}selected{/if}>{$agent.username}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">绑定管理员</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='admin_id' class='layui-select'>
|
||||
<option value=''>请选择管理员账号</option>
|
||||
{foreach $admins as $admin}
|
||||
<option value='{$admin.id}' {if isset($vo.admin_id) && $vo.admin_id==$admin.id}selected{/if}>{$admin.username} ({$admin.nickname})</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
<tip class="help-block">选择要绑定的管理员账号,绑定后该管理员可以作为二级代理</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">佣金比例</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="commission_rate" value='{$vo.commission_rate|default="0"}' required placeholder="请输入佣金比例" class="layui-input">
|
||||
<tip class="help-block">请输入0-100之间的数字,表示佣金百分比</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<select name='status' class='layui-select'>
|
||||
<option value='1' {if isset($vo.status) && $vo.status==1}selected{/if}>正常</option>
|
||||
<option value='0' {if isset($vo.status) && $vo.status==0}selected{/if}>禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">备注</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="remark" placeholder="请输入备注" class="layui-textarea">{$vo.remark|default=""}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn" type='submit'>保存数据</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-close>取消编辑</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
layui.form.on('select(level)', function(data){
|
||||
if(data.value == '2'){
|
||||
$('.parent-agent').show();
|
||||
}else{
|
||||
$('.parent-agent').hide();
|
||||
$('select[name=parent_id]').val('');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
123
app/manager/view/agent/index.html
Normal file
123
app/manager/view/agent/index.html
Normal file
@@ -0,0 +1,123 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="button"}
|
||||
<!--{if auth("add")}-->
|
||||
<button class='layui-btn layui-btn-sm layui-btn-primary' data-modal='{:url("add")}'>添加代理商</button>
|
||||
<!--{/if}-->
|
||||
<!--{if auth("assignCodes")}-->
|
||||
<button class='layui-btn layui-btn-sm layui-btn-primary' data-modal='{:url("assignCodes")}'>分配激活码</button>
|
||||
<!--{/if}-->
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-inline">
|
||||
<input name="username" value="{$get.username|default=''}" placeholder="请输入用户名" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">代理级别</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='level' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='1' {if isset($get.level) and $get.level=='1'}selected{/if}>一级代理</option>
|
||||
<option value='2' {if isset($get.level) and $get.level=='2'}selected{/if}>二级代理</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name='status' lay-search class='layui-select'>
|
||||
<option value=''>-- 全部 --</option>
|
||||
<option value='1' {if isset($get.status) and $get.status=='1'}selected{/if}>正常</option>
|
||||
<option value='0' {if isset($get.status) and $get.status=='0'}selected{/if}>禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='list-table-check-td think-checkbox'>
|
||||
<label><input data-auto-none data-check-target='.list-check-box' type='checkbox'></label>
|
||||
</th>
|
||||
<th class='text-left'>用户名</th>
|
||||
<th class='text-left'>昵称</th>
|
||||
<th class='text-left'>代理级别</th>
|
||||
<th class='text-left'>上级代理</th>
|
||||
<th class='text-left'>佣金比例</th>
|
||||
<th class='text-left'>余额</th>
|
||||
<th class='text-left'>总收入</th>
|
||||
<th class='text-left'>状态</th>
|
||||
<th class='text-left'>最后登录</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr>
|
||||
<td class='list-table-check-td think-checkbox'>
|
||||
<label><input class="list-check-box" value='{$vo.id}' type='checkbox'></label>
|
||||
</td>
|
||||
<td class='text-left'>{$vo.username}</td>
|
||||
<td class='text-left'>{$vo.nickname|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.level eq 1}
|
||||
<span class="color-blue">一级代理</span>
|
||||
{else}
|
||||
<span class="color-green">二级代理</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.parent_id}
|
||||
{$vo.parent_name|default='--'}
|
||||
{else}
|
||||
--
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.commission_rate}%</td>
|
||||
<td class='text-left'>{$vo.balance}</td>
|
||||
<td class='text-left'>{$vo.total_income}</td>
|
||||
<td class='text-left'>
|
||||
{if $vo.status eq 1}
|
||||
<span class="color-green">正常</span>
|
||||
{else}
|
||||
<span class="color-red">禁用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left'>{$vo.last_login_time|format_datetime|default='--'}</td>
|
||||
<td class='text-left'>
|
||||
{if auth("edit")}
|
||||
<a class="layui-btn layui-btn-sm" data-modal='{:url("edit")}?id={$vo.id}'>编 辑</a>
|
||||
{/if}
|
||||
|
||||
{if auth("state") and $vo.status eq 1}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-warm" data-action='{:url("state")}' data-value="id#{$vo.id};status#0" data-csrf="{:systoken('state')}">禁 用</a>
|
||||
{elseif auth("state") and $vo.status eq 0}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-warm" data-action='{:url("state")}' data-value="id#{$vo.id};status#1" data-csrf="{:systoken('state')}">启 用</a>
|
||||
{/if}
|
||||
|
||||
{if auth("remove")}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除该代理商吗?" data-csrf="{:systoken('remove')}" data-action='{:url("remove")}' data-value="id#{$vo.id}">删 除</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
|
||||
</div>
|
||||
{/block}
|
||||
122
app/manager/view/version/control.html
Normal file
122
app/manager/view/version/control.html
Normal file
@@ -0,0 +1,122 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<b>版本信息</b>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">版本号</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" class="layui-input" value="{$version.version_no}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">版本名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" class="layui-input" value="{$version.version_name}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<b>权限控制</b>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary pull-right" data-open="{:url('control')}?version_id={$version.id}&mode=add">添加规则</button>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='rules'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>控制类型</th>
|
||||
<th>目标类型</th>
|
||||
<th>目标ID</th>
|
||||
<th>过期时间</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $rules as $rule}
|
||||
<tr>
|
||||
<td>{if $rule.control_type eq 'white_list'}<span class="color-green">白名单</span>{else}<span class="color-red">黑名单</span>{/if}</td>
|
||||
<td>
|
||||
{switch $rule.target_type}
|
||||
{case user}用户{/case}
|
||||
{case agent}代理{/case}
|
||||
{case device}设备{/case}
|
||||
{/switch}
|
||||
</td>
|
||||
<td>{$rule.target_id}</td>
|
||||
<td>{$rule.expire_time}</td>
|
||||
<td>{$rule.create_time}</td>
|
||||
<td>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-sm" data-confirm="确定要删除该规则吗?" data-action='{:url("removeRule")}' data-value="id#{$rule.id}">删 除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='rules'}<span class="notdata">没有记录哦</span>{/empty}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if isset($mode) and $mode eq 'add'}
|
||||
<form class="layui-form layui-card" action="{:url('control')}" data-auto="true" method="post">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">控制类型</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="control_type" value="white_list" title="白名单" checked>
|
||||
<input type="radio" name="control_type" value="black_list" title="黑名单">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">目标类型</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="target_type" class="layui-select">
|
||||
<option value="user">用户</option>
|
||||
<option value="agent">代理</option>
|
||||
<option value="device">设备</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">目标ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="target_id" required class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">过期时间</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="expire_time" required class="layui-input" id="expire_time">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
||||
<div class="layui-form-item text-center">
|
||||
<input type='hidden' value='{$version.id}' name='version_id'>
|
||||
<button class="layui-btn" type='submit'>保存数据</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-close>取消</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{block name='script'}
|
||||
<script>
|
||||
laydate.render({
|
||||
elem: '#expire_time',
|
||||
type: 'datetime'
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
{/if}
|
||||
210
app/manager/view/version/form.html
Normal file
210
app/manager/view/version/form.html
Normal file
@@ -0,0 +1,210 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="content"}
|
||||
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">版本号</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="version_no" value='{$vo.version_no|default=""}' required placeholder="请输入版本号(x.x.x)" class="layui-input">
|
||||
<tip>版本号格式为: 主版本号.次版本号.修订号,如: 1.0.0</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">版本名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="version_name" value='{$vo.version_name|default=""}' required placeholder="请输入版本名称" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">更新包</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="hidden" name="download_url" value="{$vo.download_url|default=''}" class="layui-input">
|
||||
|
||||
<div class="layui-upload-drag" id="uploadBtn">
|
||||
<i class="layui-icon layui-icon-upload"></i>
|
||||
<div class="upload-title">点击上传或拖拽文件到此处</div>
|
||||
<div class="upload-tips">支持的文件类型: zip, exe, dmg, pkg, deb, rpm</div>
|
||||
</div>
|
||||
<div class="upload-info" style="margin-top: 10px;display:none;">
|
||||
<div class="layui-progress layui-progress-big" lay-filter="uploadProgress" lay-showpercent="true">
|
||||
<div class="layui-progress-bar" lay-percent="0%"></div>
|
||||
</div>
|
||||
<div class="file-info" style="margin-top: 10px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">平台</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="platform" lay-verify="required">
|
||||
<option value="all" {if isset($vo.platform) and $vo.platform eq 'all'}selected{/if}>全平台</option>
|
||||
<option value="windows" {if isset($vo.platform) and $vo.platform eq 'windows'}selected{/if}>Windows</option>
|
||||
<option value="mac" {if isset($vo.platform) and $vo.platform eq 'mac'}selected{/if}>Mac</option>
|
||||
<option value="linux" {if isset($vo.platform) and $vo.platform eq 'linux'}selected{/if}>Linux</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">强制更新</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="is_force" value="1" title="是" {if isset($vo.is_force) and $vo.is_force eq 1}checked{/if}>
|
||||
<input type="radio" name="is_force" value="0" title="否" {if empty($vo.is_force) or $vo.is_force eq 0}checked{/if}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最低要求</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="min_version" value='{$vo.min_version|default=""}' placeholder="请输入最低要求版本号" class="layui-input">
|
||||
<tip>如果设置了最低要求版本,低于此版本的用户需要先更新到此版本</tip>
|
||||
</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">{$vo.description|default=""}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">更新日志</label>
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div id="logItems">
|
||||
{notempty name="logs"}
|
||||
{foreach $logs as $log}
|
||||
<div class="log-item layui-form-item">
|
||||
<div class="layui-inline" style="width:120px">
|
||||
<select name="logs[type][]" lay-verify="required">
|
||||
<option value="feature" {if $log.type eq 'feature'}selected{/if}>新功能</option>
|
||||
<option value="fix" {if $log.type eq 'fix'}selected{/if}>修复</option>
|
||||
<option value="optimize" {if $log.type eq 'optimize'}selected{/if}>优化</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline" style="width:calc(100% - 180px)">
|
||||
<input type="text" name="logs[content][]" value="{$log.content}" required placeholder="请输入更新内容" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline" style="width:50px">
|
||||
<a class="layui-btn layui-btn-danger layui-btn-sm" onclick="removeLogItem(this)">删除</a>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
{/notempty}
|
||||
</div>
|
||||
<div class="text-center padding-bottom-15">
|
||||
<button type="button" class="layui-btn layui-btn-sm layui-btn-primary" onclick="addLogItem()">添加更新内容</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
||||
<div class="layui-form-item text-center">
|
||||
{notempty name='id'}<input type='hidden' value='{$id}' name='id'>{/notempty}
|
||||
<button class="layui-btn" type='submit'>保存数据</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-close>取消</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{block name="script"}
|
||||
<script>
|
||||
// 添加更新日志项
|
||||
function addLogItem() {
|
||||
var html = `
|
||||
<div class="log-item layui-form-item">
|
||||
<div class="layui-inline" style="width:120px">
|
||||
<select name="logs[type][]" lay-verify="required">
|
||||
<option value="feature">新功能</option>
|
||||
<option value="fix">修复</option>
|
||||
<option value="optimize">优化</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline" style="width:calc(100% - 180px)">
|
||||
<input type="text" name="logs[content][]" required placeholder="请输入更新内容" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline" style="width:50px">
|
||||
<a class="layui-btn layui-btn-danger layui-btn-sm" onclick="removeLogItem(this)">删除</a>
|
||||
</div>
|
||||
</div>`;
|
||||
$('#logItems').append(html);
|
||||
layui.form.render();
|
||||
}
|
||||
|
||||
// 删除更新日志项
|
||||
function removeLogItem(obj) {
|
||||
$(obj).closest('.log-item').remove();
|
||||
}
|
||||
|
||||
// 上传更新包
|
||||
layui.use(['upload', 'form', 'element'], function(){
|
||||
var upload = layui.upload;
|
||||
var form = layui.form;
|
||||
var element = layui.element;
|
||||
|
||||
upload.render({
|
||||
elem: '#uploadBtn',
|
||||
url: '{:url("upload")}',
|
||||
accept: 'file',
|
||||
exts: 'zip|exe|dmg|pkg|deb|rpm',
|
||||
drag: true,
|
||||
before: function(){
|
||||
console.log('开始上传...');
|
||||
$('.upload-info').show();
|
||||
element.progress('uploadProgress', '0%');
|
||||
$('.file-info').html('');
|
||||
},
|
||||
progress: function(n){
|
||||
var percent = n + '%';
|
||||
console.log('上传进度:', percent);
|
||||
element.progress('uploadProgress', percent);
|
||||
},
|
||||
done: function(res){
|
||||
console.log('上传响应:', res);
|
||||
if(res.code == 0){
|
||||
// 更新表单值
|
||||
$('input[name=download_url]').val(res.data.url);
|
||||
|
||||
// 更新顶部输入框的值
|
||||
$('input[name="version_no"]').val(res.data.version_no);
|
||||
$('input[name="version_name"]').val(res.data.version_name);
|
||||
|
||||
// 显示文件信息
|
||||
var html = '<div class="layui-badge layui-bg-green">上传成功</div> ';
|
||||
html += '<div class="layui-inline">版本号: ' + res.data.version_no + '</div> ';
|
||||
html += '<div class="layui-inline">版本名称: ' + res.data.version_name + '</div>';
|
||||
$('.file-info').html(html);
|
||||
|
||||
// 重新渲染表单
|
||||
form.render();
|
||||
} else {
|
||||
console.error('上传失败:', res.msg);
|
||||
$('.file-info').html('<div class="layui-badge layui-bg-red">'+res.msg+'</div>');
|
||||
}
|
||||
},
|
||||
error: function(index, upload){
|
||||
console.error('上传错误:', index, upload);
|
||||
$('.file-info').html('<div class="layui-badge layui-bg-red">上传失败</div>');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.upload-title { font-size: 16px; margin-bottom: 5px; }
|
||||
.upload-tips { color: #999; font-size: 12px; }
|
||||
.layui-upload-drag { padding: 30px; }
|
||||
.layui-upload-drag .layui-icon { font-size: 50px; color: #009688; }
|
||||
.file-info .layui-inline { margin-right: 10px; }
|
||||
</style>
|
||||
{/block}
|
||||
{/block}
|
||||
79
app/manager/view/version/index.html
Normal file
79
app/manager/view/version/index.html
Normal file
@@ -0,0 +1,79 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="button"}
|
||||
<button class='layui-btn layui-btn-sm layui-btn-primary' data-open='{:url("add")}'>添加版本</button>
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<div class="think-box-shadow">
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='text-left nowrap'>版本号</th>
|
||||
<th class='text-left nowrap'>版本名称</th>
|
||||
<th class='text-left nowrap'>平台</th>
|
||||
<th class='text-left nowrap'>下载地址</th>
|
||||
<th class='text-left nowrap'>强制更新</th>
|
||||
<th class='text-left nowrap'>最低要求</th>
|
||||
<th class='text-left nowrap'>状态</th>
|
||||
<th class='text-left nowrap'>创建时间</th>
|
||||
<th class='text-left'>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr>
|
||||
<td class='text-left nowrap'>{$vo.version_no|default=''}</td>
|
||||
<td class='text-left nowrap'>{$vo.version_name|default=''}</td>
|
||||
<td class='text-left nowrap'>{$vo.platform|default='all'}</td>
|
||||
<td class='text-left nowrap'>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-normal" href="{:sysconf('site_domain')}{$vo.download_url|default=''}" target="_blank">下载</a>
|
||||
<a class="layui-btn layui-btn-sm" data-copy="{:sysconf('site_domain')}{$vo.download_url|default=''}" data-tips-text="复制下载链接">
|
||||
<i class="layui-icon"></i>
|
||||
</a>
|
||||
<input type="text" class="layui-input layui-input-inline" style="width:200px;margin-left:5px;" value="{:sysconf('site_domain')}{$vo.download_url|default=''}" readonly>
|
||||
</td>
|
||||
<td class='text-left nowrap'>{if $vo.is_force}是{else}否{/if}</td>
|
||||
<td class='text-left nowrap'>{$vo.min_version|default=''}</td>
|
||||
<td class='text-left nowrap'>
|
||||
{if $vo.status eq 1}
|
||||
<span class="color-green">已启用</span>
|
||||
{else}
|
||||
<span class="color-red">已禁用</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class='text-left nowrap'>{$vo.create_time|format_datetime}</td>
|
||||
<td class='text-left'>
|
||||
<a class="layui-btn layui-btn-sm" data-open='{:url("edit")}?id={$vo.id}'>编辑</a>
|
||||
{if $vo.status eq 1}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-warm" data-action='{:url("state")}' data-value="id#{$vo.id};status#0">禁用</a>
|
||||
{else}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-warm" data-action='{:url("state")}' data-value="id#{$vo.id};status#1">启用</a>
|
||||
{/if}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除该版本吗?" data-action='{:url("remove")}' data-value="id#{$vo.id}">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{/empty}
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{block name="script"}
|
||||
<script>
|
||||
// 注册复制事件
|
||||
$('a[data-copy]').on('click', function () {
|
||||
var text = $(this).data('copy');
|
||||
var input = document.createElement('input');
|
||||
input.value = text;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(input);
|
||||
$.msg.success('复制成功');
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
@@ -37,9 +37,9 @@ return [
|
||||
// 密码
|
||||
'password' => 'root', */
|
||||
// 用户名
|
||||
'username' => 'root',
|
||||
'username' => 'cursortools',
|
||||
// 密码
|
||||
'password' => 'root',
|
||||
'password' => 'TQ3hwykBxmARw3ya',
|
||||
// 端口
|
||||
'hostport' => '3306',
|
||||
// 数据库连接参数
|
||||
@@ -82,5 +82,12 @@ return [
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
],
|
||||
'redis' => [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'timeout' => 10,
|
||||
'prefix' => null,
|
||||
'pwd' => null
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
79
database/migrations/20240320000001_create_version_tables.php
Normal file
79
database/migrations/20240320000001_create_version_tables.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
use think\migration\db\Column;
|
||||
|
||||
class CreateVersionTables extends Migrator
|
||||
{
|
||||
public function change()
|
||||
{
|
||||
// 添加系统配置
|
||||
$this->addSystemConfig();
|
||||
|
||||
// 软件版本表
|
||||
if (!$this->hasTable('version')) {
|
||||
$this->table('version', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci'])
|
||||
->addColumn('version_no', 'string', ['limit' => 50, 'null' => false, 'comment' => '版本号'])
|
||||
->addColumn('version_name', 'string', ['limit' => 100, 'null' => false, 'comment' => '版本名称'])
|
||||
->addColumn('download_url', 'string', ['limit' => 500, 'null' => false, 'comment' => '下载地址'])
|
||||
->addColumn('is_force', 'boolean', ['null' => false, 'default' => 0, 'comment' => '是否强制更新'])
|
||||
->addColumn('min_version', 'string', ['limit' => 50, 'null' => true, 'comment' => '最低要求版本'])
|
||||
->addColumn('platform', 'string', ['limit' => 20, 'null' => false, 'default' => 'all', 'comment' => '平台(windows,mac,linux)'])
|
||||
->addColumn('status', 'boolean', ['null' => false, 'default' => 1, 'comment' => '状态(0禁用,1启用)'])
|
||||
->addColumn('description', 'text', ['null' => true, 'comment' => '版本描述'])
|
||||
->addColumn('create_time', 'datetime', ['null' => true, 'comment' => '创建时间'])
|
||||
->addColumn('update_time', 'datetime', ['null' => true, 'comment' => '更新时间'])
|
||||
->addIndex('version_no', ['name' => 'idx_version_no'])
|
||||
->create();
|
||||
}
|
||||
|
||||
// 版本更新日志表
|
||||
if (!$this->hasTable('version_log')) {
|
||||
$this->table('version_log', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci'])
|
||||
->addColumn('version_id', 'integer', ['limit' => 20, 'null' => false, 'comment' => '版本ID'])
|
||||
->addColumn('type', 'string', ['limit' => 20, 'null' => false, 'default' => 'feature', 'comment' => '更新类型(feature,fix,optimize)'])
|
||||
->addColumn('content', 'text', ['null' => false, 'comment' => '更新内容'])
|
||||
->addColumn('create_time', 'datetime', ['null' => true, 'comment' => '创建时间'])
|
||||
->addIndex('version_id', ['name' => 'idx_version_id'])
|
||||
->create();
|
||||
}
|
||||
|
||||
// 版本控制表
|
||||
if (!$this->hasTable('version_control')) {
|
||||
$this->table('version_control', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci'])
|
||||
->addColumn('version_id', 'integer', ['limit' => 20, 'null' => false, 'comment' => '版本ID'])
|
||||
->addColumn('control_type', 'string', ['limit' => 20, 'null' => false, 'comment' => '控制类型(white_list,black_list)'])
|
||||
->addColumn('target_id', 'integer', ['limit' => 20, 'null' => false, 'comment' => '目标ID'])
|
||||
->addColumn('target_type', 'string', ['limit' => 20, 'null' => false, 'comment' => '目标类型(user,agent,device)'])
|
||||
->addColumn('expire_time', 'datetime', ['null' => true, 'comment' => '过期时间'])
|
||||
->addColumn('create_time', 'datetime', ['null' => true, 'comment' => '创建时间'])
|
||||
->addColumn('update_time', 'datetime', ['null' => true, 'comment' => '更新时间'])
|
||||
->addIndex(['version_id', 'target_id', 'target_type'], ['name' => 'idx_version_target'])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加系统配置
|
||||
*/
|
||||
protected function addSystemConfig()
|
||||
{
|
||||
// 检查是否存在配置
|
||||
$exists = \think\facade\Db::name('system_config')
|
||||
->where('type', 'base')
|
||||
->where('name', 'site_domain')
|
||||
->find();
|
||||
|
||||
if (!$exists) {
|
||||
// 添加站点域名配置
|
||||
\think\facade\Db::name('system_config')->insert([
|
||||
'type' => 'base',
|
||||
'name' => 'site_domain',
|
||||
'value' => '',
|
||||
'title' => '站点域名',
|
||||
'description' => '请设置站点访问域名,例如: https://www.example.com',
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
database/migrations/20240320000002_add_version_menu.php
Normal file
82
database/migrations/20240320000002_add_version_menu.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
use think\facade\Db;
|
||||
|
||||
class AddVersionMenu extends Migrator
|
||||
{
|
||||
public function change()
|
||||
{
|
||||
// 检查是否已存在版本管理菜单
|
||||
$exists = Db::name('system_menu')->where(['node' => 'version_manage'])->find();
|
||||
if (!$exists) {
|
||||
// 添加版本管理主菜单
|
||||
$pid = Db::name('system_menu')->insertGetId([
|
||||
'pid' => 0,
|
||||
'title' => '版本管理',
|
||||
'icon' => 'layui-icon layui-icon-template-1',
|
||||
'node' => 'version_manage',
|
||||
'url' => '#',
|
||||
'sort' => 100,
|
||||
'status' => 1,
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
// 添加版本列表菜单
|
||||
$menu_id = Db::name('system_menu')->insertGetId([
|
||||
'pid' => $pid,
|
||||
'title' => '版本列表',
|
||||
'icon' => 'layui-icon layui-icon-list',
|
||||
'node' => 'version_manage/version/index',
|
||||
'url' => 'manager/version/index',
|
||||
'sort' => 100,
|
||||
'status' => 1,
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
// 添加操作权限节点(不显示在菜单中)
|
||||
Db::name('system_menu')->insertAll([
|
||||
[
|
||||
'pid' => $menu_id,
|
||||
'title' => '添加版本',
|
||||
'icon' => 'layui-icon layui-icon-add-1',
|
||||
'node' => 'version_manage/version/add',
|
||||
'url' => 'manager/version/add',
|
||||
'sort' => 100,
|
||||
'status' => 0, // 不显示在菜单中
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'pid' => $menu_id,
|
||||
'title' => '编辑版本',
|
||||
'icon' => 'layui-icon layui-icon-edit',
|
||||
'node' => 'version_manage/version/edit',
|
||||
'url' => 'manager/version/edit',
|
||||
'sort' => 100,
|
||||
'status' => 0, // 不显示在菜单中
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'pid' => $menu_id,
|
||||
'title' => '删除版本',
|
||||
'icon' => 'layui-icon layui-icon-delete',
|
||||
'node' => 'version_manage/version/remove',
|
||||
'url' => 'manager/version/remove',
|
||||
'sort' => 100,
|
||||
'status' => 0, // 不显示在菜单中
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'pid' => $menu_id,
|
||||
'title' => '版本状态',
|
||||
'icon' => 'layui-icon layui-icon-circle',
|
||||
'node' => 'version_manage/version/state',
|
||||
'url' => 'manager/version/state',
|
||||
'sort' => 100,
|
||||
'status' => 0, // 不显示在菜单中
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user