同步更改

This commit is contained in:
李志强 2025-08-09 23:49:50 +08:00
parent 8e96995e1c
commit 5a5f64b8c8
243 changed files with 1536 additions and 445 deletions

3
.env
View File

@ -5,7 +5,8 @@ DEFAULT_TIMEZONE = Asia/Shanghai
[DATABASE]
TYPE = mysql
HOSTNAME = 121.36.243.179
HOSTNAME = 43.133.71.191
#HOSTNAME = 127.0.0.1
DATABASE = yunzertest
USERNAME = yunzertest
PASSWORD = zKMDMEs7YP3SLDEF

1
.htaccess Normal file
View File

@ -0,0 +1 @@

1
.user.ini Normal file
View File

@ -0,0 +1 @@
open_basedir=/www/wwwroot/yunzer/:/tmp/

7
404.html Normal file
View File

@ -0,0 +1,7 @@
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

View File

@ -119,6 +119,7 @@ class ResourcesController extends BaseController
$data = [
'title' => input('post.title'),
'cate' => input('post.cate'),
'desc' => input('post.desc'),
'icon' => input('post.icon'),
'images' => input('post.images'),
'url' => input('post.url'),
@ -126,7 +127,6 @@ class ResourcesController extends BaseController
'code' => input('post.code'),
'zipcode' => input('post.zipcode'),
'uploader' => input('post.uploader'),
'desc' => input('post.desc'),
'content' => input('post.content'),
'number' => input('post.number'),
'status' => input('post.status', 1),
@ -136,7 +136,7 @@ class ResourcesController extends BaseController
$insert = Resource::insert($data);
if (empty($insert)) {
Log::record('添加资源', 0, '添加资源失败', '资源管理');
return json(['code' => 1, 'msg' => '添加失败', 'data' => []]);
$this->error('添加失败');
}
Log::record('添加资源', 1, '', '资源管理');
return json(['code' => 0, 'msg' => '添加成功', 'data' => []]);
@ -154,7 +154,11 @@ class ResourcesController extends BaseController
View::assign([
'categories' => $categories
]);
return View::fetch();
} catch (\Exception $e) {
Log::record('添加资源页面加载', 0, $e->getMessage(), '资源管理');
$this->error('页面加载失败:' . $e->getMessage());
}
}
@ -218,9 +222,9 @@ class ResourcesController extends BaseController
if (!empty($resource['images'])) {
$domain = request()->domain();
$images = explode(',', $resource['images']);
$images = array_map(function ($image) use ($domain) {
return $domain . $image;
}, $images);
// $images = array_map(function ($image) use ($domain) {
// return $domain . $image;
// }, $images);
$resource['images'] = implode(',', $images);
}

View File

@ -28,7 +28,10 @@ use app\admin\model\AdminUser;
use app\admin\model\User\Users;
use app\admin\model\User\UsersGroup;
use app\admin\model\Banner;
use app\admin\model\ContentPush;
use app\admin\model\ContentPush\ContentPush;
use app\admin\model\ContentPush\ContentPushSetting;
use app\admin\model\Resource\Resource;
use app\admin\model\Article\Articles;
class YunzeradminController extends Base
@ -684,32 +687,57 @@ class YunzeradminController extends Base
public function contentpushlist()
{
if (Request::isGet()) {
$page = intval(input('post.page', 1));
$limit = intval(input('post.limit', 10));
// 获取分页参数
$page = (int) input('get.page', 1);
$limit = (int) input('get.limit', 10);
$query = ContentPush::where('delete_time', null)
->field('id, title, type, status, sort, create_time, update_time');
// 获取总记录数
$count = $query->count();
// 获取分页数据
$lists = $query->order(['sort DESC', 'id DESC'])
->page($page, $limit)
// 1. 获取 Articles 表delete_time为空status=2
$articles = Articles::where('delete_time', null)
->where('status', 2)
->field('id, title, push, create_time')
->select()
->toArray();
// 处理数据
foreach ($lists as &$item) {
$item['create_time'] = is_numeric($item['create_time']) ? date('Y-m-d H:i:s', $item['create_time']) : $item['create_time'];
$item['update_time'] = is_numeric($item['update_time']) ? date('Y-m-d H:i:s', $item['update_time']) : $item['update_time'];
foreach ($articles as &$a) {
$a['type'] = 'article';
$a['create_time'] = is_numeric($a['create_time']) ? date('Y-m-d H:i:s', $a['create_time']) : $a['create_time'];
}
unset($a);
// 2. 获取 Resource 表delete_time为空status=1
$resources = Resource::where('delete_time', null)
->where('status', 1)
->field('id, title, push, create_time')
->select()
->toArray();
foreach ($resources as &$r) {
$r['type'] = 'resource';
$r['create_time'] = is_numeric($r['create_time']) ? date('Y-m-d H:i:s', $r['create_time']) : $r['create_time'];
}
unset($r);
// 3. 合并
$lists = array_merge($articles, $resources);
// 4. 优先显示push为0的数据再按create_time倒序排序
usort($lists, function ($a, $b) {
// push为0的排在前面
if ($a['push'] != $b['push']) {
return $a['push'] - $b['push'];
}
// push相同则按create_time倒序
return strtotime($b['create_time']) <=> strtotime($a['create_time']);
});
// 5. 分页
$total = count($lists);
$offset = ($page - 1) * $limit;
$data = array_slice($lists, $offset, $limit);
return json([
'code' => 0,
'msg' => '',
'count' => $count,
'data' => $lists
'count' => $total,
'data' => $data
]);
}
return json(['code' => 1, 'msg' => '请求方法无效']);
@ -737,8 +765,9 @@ class YunzeradminController extends Base
}
Log::record('添加内容推送', 1, '', '内容推送管理');
return json(['code' => 0, 'msg' => '添加成功']);
} else {
return View::fetch();
}
return json(['code' => 1, 'msg' => '请求方法无效']);
}
// 编辑内容推送
@ -817,6 +846,137 @@ class YunzeradminController extends Base
return json(['code' => 1, 'msg' => '请求方法无效']);
}
//推送配置列表(渲染列表)
public function contentpushsetting()
{
if (Request::isAjax() || Request::isPost()) {
$page = intval(input('get.page', 1));
$limit = intval(input('get.limit', 10));
$query = ContentPushSetting::where('delete_time', null)
->field('id, title, value, platformType, status, sort, create_time');
$count = $query->count();
$lists = $query->order(['sort' => 'DESC', 'id' => 'DESC'])
->page($page, $limit)
->select()
->toArray();
foreach ($lists as &$item) {
$item['create_time'] = is_numeric($item['create_time']) ? date('Y-m-d H:i:s', $item['create_time']) : $item['create_time'];
}
return json([
'code' => 0,
'msg' => '获取成功',
'count' => $count,
'data' => $lists
]);
} else {
return View::fetch();
}
}
//选择列出推送内容列表
public function selectpushcontent()
{
$pushcate = strtolower(trim(input('get.pushcate', '')));
$where = ['push' => 0];
$modelMap = [
'article' => Articles::class,
'resource' => Resource::class,
];
if (!isset($modelMap[$pushcate])) {
return json(['code' => 1, 'msg' => '参数错误: pushcate']);
}
$model = $modelMap[$pushcate];
$query = $model::where($where)->field('title,id');
$count = $query->count();
$list = $query->order('id', 'desc')->select()->toArray();
// 判断是接口请求还是页面请求
if (request()->isAjax() || request()->isJson()) {
return json([
'code' => 0,
'msg' => '获取成功',
'count' => $count,
'data' => $list
]);
} else {
// 这里传递变量到模板
return View::fetch('', [
'data' => $list
]);
}
}
//推送配置添加和编辑通用方法
public function contentpushsettingadd()
{
if (Request::isPost()) {
$params = input('post.');
$id = isset($params['id']) ? intval($params['id']) : 0;
if ($id > 0) {
// 编辑
$res = ContentPushSetting::update($params, ['id' => $id]);
if ($res === false) {
Log::record('编辑推送配置', 0, '编辑推送配置失败', '推送配置管理');
return json(['code' => 1, 'msg' => '编辑推送配置失败']);
}
Log::record('编辑推送配置', 1, '', '推送配置管理');
return json(['code' => 0, 'msg' => '编辑成功']);
} else {
// 添加
$res = ContentPushSetting::create($params);
if (!$res) {
Log::record('添加推送配置', 0, '添加推送配置失败', '推送配置管理');
return json(['code' => 1, 'msg' => '添加推送配置失败']);
}
Log::record('添加推送配置', 1, '', '推送配置管理');
return json(['code' => 0, 'msg' => '添加成功']);
}
} else {
$id = input('get.id', 0);
$info = [];
if ($id) {
$info = ContentPushSetting::where('id', $id)->find();
if ($info) {
$info = $info->toArray();
}
}
return View::fetch('', ['info' => $info]);
}
}
//推送配置软删除
public function contentpushsettingdel()
{
if (Request::isPost()) {
$id = intval(input('post.id', 0));
if (!$id) {
return json(['code' => 1, 'msg' => '参数错误']);
}
$setting = ContentPushSetting::where('id', $id)->find();
if (!$setting) {
return json(['code' => 1, 'msg' => '配置不存在']);
}
$res = ContentPushSetting::where('id', $id)->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($res === false) {
Log::record('删除推送配置', 0, '删除失败', '推送配置管理');
return json(['code' => 1, 'msg' => '删除失败']);
}
Log::record('删除推送配置', 1, '', '推送配置管理');
return json(['code' => 0, 'msg' => '删除成功']);
}
return json(['code' => 1, 'msg' => '请求方式错误']);
}
//素材中心
public function materialcenter()
{
@ -957,4 +1117,93 @@ class YunzeradminController extends Base
}
return json(['code' => 1, 'msg' => '请求方法无效']);
}
public function gopush()
{
$platformType = input('post.platformType');
$urls = input('post.urls/a', []);
if (empty($platformType)) {
return json(['code' => 1, 'msg' => '平台参数缺失']);
}
if (empty($urls)) {
return json(['code' => 1, 'msg' => '推送内容url为空']);
}
// 查找推送平台配置
$setting = ContentPushSetting::where('platformType', $platformType)->find();
if (!$setting) {
return json(['code' => 1, 'msg' => '未找到该平台的推送配置']);
}
if ($setting['status'] == 0) {
return json(['code' => 1, 'msg' => '该平台推送已禁用']);
}
$api = $setting['value'];
if (empty($api)) {
return json(['code' => 1, 'msg' => '推送API地址未配置']);
}
// 推送
$ch = curl_init();
$options = array(
CURLOPT_URL => $api,
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => implode("\n", $urls), // 多行文本每行一个url
CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
);
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
return json(['code' => 1, 'msg' => $error, 'result' => $result]);
}
// 解析推送返回结果
$resultArr = json_decode($result, true);
// 判断返回是否有error字段
if (is_array($resultArr) && isset($resultArr['error'])) {
return json([
'code' => 1,
'msg' => '推送失败: ' . (isset($resultArr['message']) ? $resultArr['message'] : '未知错误'),
'result' => $resultArr
]);
}
// 判断是否有success字段只有有success字段才认为推送成功
if (is_array($resultArr) && isset($resultArr['success'])) {
// 推送成功后更新对应数据的push字段为1
// 解析urls分别更新Articles或Resource表
foreach ($urls as $url) {
// 匹配文章
if (preg_match('/\/index\/articles\/detail\?id=(\d+)/', $url, $matches)) {
$id = intval($matches[1]);
if ($id > 0) {
Articles::where('id', $id)->update(['push' => 1]);
}
}
// 匹配资源
elseif (preg_match('/\/index\/resources\/detail\?id=(\d+)/', $url, $matches)) {
$id = intval($matches[1]);
if ($id > 0) {
Resource::where('id', $id)->update(['push' => 1]);
}
}
}
return json(['code' => 0, 'msg' => '推送成功', 'result' => $resultArr]);
} else {
// 没有success字段视为推送失败把失败的result反馈给前端
return json([
'code' => 1,
'msg' => '推送失败',
'result' => $resultArr !== null ? $resultArr : $result
]);
}
}
}

View File

@ -16,7 +16,7 @@
* 3. 禁止转售或分发
*/
namespace app\admin\model;
namespace app\admin\model\ContentPush;
use think\Model;

View File

@ -0,0 +1,34 @@
<?php
/**
* 商业使用授权协议
*
* Copyright (c) 2025 [云泽网]. 保留所有权利.
*
* 本软件仅供评估使用。任何商业用途必须获得书面授权许可。
* 未经授权商业使用本软件属于侵权行为,将承担法律责任。
*
* 授权购买请联系: 357099073@qq.com
* 官方网站: https://www.yunzer.cn
*
* 评估用户须知:
* 1. 禁止移除版权声明
* 2. 禁止用于生产环境
* 3. 禁止转售或分发
*/
namespace app\admin\model\ContentPush;
use think\Model;
class ContentPushSetting extends Model
{
protected $name = 'content_push_setting';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
// 定义时间戳字段名
protected $createTime = 'create_time';
protected $updateTime = 'update_time';
protected $deleteTime = 'delete_time';
}

View File

@ -317,7 +317,7 @@ function getUserCounts() {
fetch('{:url("users/counts")}')
.then(response => response.json())
.then(res => {
console.log('用户统计接口返回数据:', res);
// console.log('用户统计接口返回数据:', res);
if (res.code === 0 && res.data) {
// 更新用户总数
document.querySelector('.stat-card:nth-child(1) .stat-value').textContent = res.data.total.toLocaleString();
@ -353,7 +353,7 @@ function getArticleCounts() {
fetch('{:url("articles/counts")}')
.then(response => response.json())
.then(res => {
console.log('文章统计接口返回数据:', res);
// console.log('文章统计接口返回数据:', res);
if (res.code === 0 && res.data) {
// 更新文章总数
document.querySelector('.stat-card:nth-child(3) .stat-value').textContent = res.data.total.toLocaleString();
@ -389,7 +389,7 @@ function getResourcesCounts() {
fetch('{:url("resources/counts")}')
.then(response => response.json())
.then(res => {
console.log('资源统计接口返回数据:', res);
// console.log('资源统计接口返回数据:', res);
if (res.code === 0 && res.data) {
// 更新资源总数
document.querySelector('.stat-card:nth-child(4) .stat-value').textContent = res.data.total.toLocaleString();

View File

@ -97,29 +97,41 @@
</button>
<div class="image-preview-container" id="imagePreview">
{if condition="isset($resource.images) && !empty($resource.images)"}
{if condition="strpos($resource.images, ',') !== false"}
{volist name="resource.images|explode=',',true" id="image"}
<div class="image-preview-item" data-src="{$image}">
<img src="{$image}" alt="已上传图片">
<div class="image-preview-overlay">
<button type="button" class="delete-image">
<i class="fas fa-trash"></i>
<div class="image-list-table">
<table class="layui-table">
<thead>
<tr>
<th width="100" align="center">缩略图</th>
<th width="40" align="center">操作</th>
</tr>
</thead>
<tbody>
{php}
// 处理图片字符串,正确拆分逗号分隔的路径
$imageArray = [];
if (isset($resource['images']) && !empty($resource['images'])) {
$imageArray = explode(',', $resource['images']);
$imageArray = array_map('trim', $imageArray); // 去除每个路径的前后空格
$imageArray = array_filter($imageArray); // 过滤空值
}
{/php}
{volist name="imageArray" id="image"}
<tr>
<td>
<div class="image-thumbnail" onclick="previewImage('<?php echo (strpos($image, 'http') === 0 ? $image : request()->domain() . $image); ?>')">
<img src="<?php echo (strpos($image, 'http') === 0 ? $image : request()->domain() . $image); ?>" alt="缩略图">
</div>
</td>
<td>
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs delete-image" data-src="{$image}">
<i class="fas fa-trash"></i> 删除
</button>
</div>
<p class="image-filename">{$image|basename}</p>
</div>
</td>
</tr>
{/volist}
{else}
<div class="image-preview-item" data-src="{$resource.images}">
<img src="{$resource.images}" alt="已上传图片">
<div class="image-preview-overlay">
<button type="button" class="delete-image">
<i class="fas fa-trash"></i>
</button>
</tbody>
</table>
</div>
<p class="image-filename">{$resource.images|basename}</p>
</div>
{/if}
{/if}
</div>
<div class="upload-progress" id="imageProgress" style="display: none;">
@ -685,6 +697,19 @@
updateImagesInput();
}
});
// 图片预览功能
window.previewImage = function(imageUrl) {
layer.photos({
photos: {
"data": [{
"src": imageUrl,
"alt": "图片预览"
}]
},
anim: 5
});
};
});
</script>
@ -820,4 +845,28 @@
.container-right {
width: 65%;
}
/* 图片列表表格样式 */
.image-list-table {
margin-top: 15px;
}
.image-thumbnail {
width: 60px;
height: 60px;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
transition: transform 0.2s;
}
.image-thumbnail:hover {
transform: scale(1.05);
}
.image-thumbnail img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>

View File

@ -50,8 +50,10 @@
</div>
</div>
<script>
layui.use(['jquery'], function () {
layui.use(['form', 'jquery', 'layer'], function () {
var $ = layui.jquery;
var form = layui.form;
var layer = layui.layer;
var $input = $('#icon_class_input');
var $dropdown = $('#iconDropdown');
var $search = $('#iconSearchInput');
@ -101,6 +103,41 @@
}
});
}
//保存
window.save = function () {
var data = {};
// 获取表单数据
data.smid = $('input[name="smid"]').val();
data.label = $('input[name="label"]').val();
data.icon_class = $('input[name="icon_class"]').val();
data.sort = $('input[name="sort"]').val();
data.status = $('input[name="status"]:checked').val();
// 简单校验
if (!data.label) {
layer.msg('菜单名不能为空', { icon: 2 });
return;
}
$.post(
'{$config["admin_route"]}yunzer/menuedit',
data,
function (res) {
if (res.code == 0) {
layer.msg(res.msg, { icon: 1 });
// 1秒后关闭弹窗并刷新父页面
setTimeout(function () {
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
parent.location.reload();
}, 1000);
} else {
layer.alert(res.msg, { icon: 2 });
}
},
'json'
);
}
});
</script>
<style>

View File

@ -8,8 +8,14 @@
</div>
<div style="display: flex;align-items: flex-start;flex-direction: column;gap: 15px;margin-bottom: 10px;">
<div>
<button class="layui-btn layui-bg-blue" onclick="add()">
<!-- <button class="layui-btn layui-bg-blue" onclick="add()">
<i class="layui-icon layui-icon-add-1"></i>添加推送
</button> -->
<button class="layui-btn layui-bg-blue" onclick="setting()">
<i class="layui-icon layui-icon-set-fill"></i>推送配置
</button>
<button type="button" class="layui-btn layui-btn-primary layui-border-green" onclick="pushSelected()">
<i class="layui-icon layui-icon-release"></i>推送
</button>
<button type="button" class="layui-btn layui-btn-primary layui-border-blue" onclick="refresh()">
<i class="layui-icon layui-icon-refresh"></i>刷新
@ -33,19 +39,35 @@
url: '{$config["admin_route"]}yunzeradmin/contentpushlist',
page: true,
cols: [[
{field: 'id', title: 'ID', width: 80, sort: true},
{field: 'title', title: '标题', width: 200},
{field: 'type', title: '类型', width: 100, templet: function(d){
return d.type == 1 ? '普通推送' : '重要推送';
}},
{field: 'status', title: '状态', width: 100, templet: function(d){
return d.status == 1 ?
'<span class="layui-badge layui-bg-green">启用</span>' :
'<span class="layui-badge">禁用</span>';
}},
{field: 'sort', title: '排序', width: 100, sort: true},
{ type: 'checkbox', fixed: 'left' }, // 多选全选
// {field: 'id', title: 'ID', width: 80, sort: true},
{
field: 'type', title: '类型', width: 80, templet: function (d) {
return d.type == 'article' ? '文章' : '资源';
}
},
{ field: 'title', title: '标题' },
{
field: 'url', title: '链接', templet: function (d) {
if (d.type == 'article') {
return '<a href="https://www.yunzer.cn/index/articles/detail?id=' + d.id + '" target="_blank">https://www.yunzer.cn/index/articles/detail?id=' + d.id + '</a>';
} else if (d.type == 'resource') {
return '<a href="https://www.yunzer.cn/index/resources/detail?id=' + d.id + '" target="_blank">https://www.yunzer.cn/index/resources/detail?id=' + d.id + '</a>';
} else {
return '';
}
}
},
{
field: 'push', title: '推送状态', width: 100, templet: function (d) {
return d.push == 1 ?
'<span class="layui-badge layui-bg-green">已推送</span>' :
'<span class="layui-badge">未推送</span>';
}
},
// {field: 'sort', title: '排序', width: 100, sort: true},
{ field: 'create_time', title: '创建时间', width: 180 },
{title: '操作', width: 200, toolbar: '#tableBar', fixed: 'right'}
// {title: '操作', width: 200, toolbar: '#tableBar', fixed: 'right'}
]],
limit: 10,
limits: [10, 20, 30, 50]
@ -119,6 +141,81 @@
}, 'json');
}
//配置
function setting() {
layer.open({
type: 2,
title: '推送配置',
shade: 0.3,
area: ['1000px', '800px'],
content: "{$config['admin_route']}yunzeradmin/contentpushsetting"
});
}
//推送数据
function pushSelected() {
var checkStatus = layui.table.checkStatus('contentPushTable');
var data = checkStatus.data;
if (data.length === 0) {
layer.msg('请先选择要推送的数据', { icon: 2 });
return;
}
$.getJSON("{$config['admin_route']}yunzeradmin/contentpushsetting", function (res) {
if (res.code !== 0 || !res.data || res.data.length === 0) {
layer.msg('暂无可用推送平台');
return;
}
var html = '<div style="padding:10px;"><table class="layui-table"><thead><tr><th>ID</th><th>平台名称</th><th>类型标识</th></tr></thead><tbody>';
res.data.forEach(function (item) {
if (item.status == 1) {
html += '<tr style="cursor:pointer" data-type="' + item.platformType + '" data-title="' + item.title.replace(/'/g, "\\'") + '">';
html += '<td>' + item.id + '</td>';
html += '<td>' + item.title + '</td>';
html += '<td>' + item.platformType + '</td>';
html += '</tr>';
}
});
html += '</tbody></table></div>';
layer.open({
type: 1,
title: '选择推送平台',
area: ['500px', '350px'],
content: html,
success: function (layero, index) {
// 事件委托,避免全局变量
$(layero).find('tr[data-type]').on('click', function () {
var platformType = $(this).data('type');
var platformTitle = $(this).data('title');
layer.close(index);
var urls = [];
data.forEach(function (item) {
urls.push('https://www.yunzer.cn/index/' + (item.type === 'resource' ? 'resources' : 'articles') + '/detail?id=' + item.id);
});
$.ajax({
url: "{$config['admin_route']}yunzeradmin/gopush",
type: "POST",
contentType: "application/json",
data: JSON.stringify({
platformType: platformType,
urls: urls
}),
dataType: "json",
success: function (res) {
layer.msg(res.msg, { icon: res.code === 0 ? 1 : 2, time: 5000 });
layui.table.reload('contentPushTable');
}
});
});
}
});
});
}
// 刷新列表
function refresh() {
layui.table.reload('contentPushTable');
@ -126,10 +223,9 @@
</script>
<!-- 表格操作列模板 -->
<script type="text/html" id="tableBar">
<!-- <script type="text/html" id="tableBar">
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</a>
<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="status">状态</a>
</script>
</script> -->
{include file="public/tail" /}

View File

@ -1,26 +1,35 @@
{include file="public/header" /}
<div class="config-container">
<form class="layui-form" action="{$config['admin_route']}yunzeradmin/contentpushsave" method="post" lay-filter="contentPushForm">
<form class="layui-form" action="{$config['admin_route']}yunzeradmin/contentpushsave" method="post"
lay-filter="contentPushForm">
<div class="layui-form-item">
<label class="layui-form-label">推送标题</label>
<label class="layui-form-label">推送平台</label>
<div class="layui-input-block">
<input type="text" name="title" required lay-verify="required" placeholder="请输入推送标题" autocomplete="off" class="layui-input">
<select name="push_platform" id="pushPlatformSelect" required lay-verify="required">
<option value="">请选择推送平台</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">推送分类</label>
<div class="layui-input-block">
<select name="pushcate" required lay-verify="required">
<option value="article">文章分类</option>
<option value="resource">资源分类</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">推送内容</label>
<div class="layui-input-block">
<textarea name="content" placeholder="请输入推送内容" class="layui-textarea" required lay-verify="required"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">推送图片</label>
<div class="layui-input-block">
<input type="text" name="image" id="image" placeholder="请上传推送图片" autocomplete="off" class="layui-input">
<button type="button" class="layui-btn" id="uploadImage">
<i class="layui-icon">&#xe67c;</i>上传图片
<div class="layui-input-block" style="display: flex; align-items: center;">
<input type="text" name="title" id="pushTitleInput" required lay-verify="required" placeholder="请输入推送标题"
autocomplete="off" class="layui-input" style="flex:1;">
<button type="button" class="layui-btn layui-btn-primary" id="selectPushContent"
style="margin-left: 8px;">
<i class="layui-icon layui-icon-search"></i>
</button>
</div>
</div>
@ -32,28 +41,38 @@
</div>
</div>
<div class="layui-form-item">
<!-- <div class="layui-form-item">
<label class="layui-form-label">推送图片</label>
<div class="layui-input-block">
<input type="text" name="image" id="image" placeholder="请上传推送图片" autocomplete="off" class="layui-input">
<button type="button" class="layui-btn" id="uploadImage">
<i class="layui-icon">&#xe67c;</i>上传图片
</button>
</div>
</div> -->
<!-- <div class="layui-form-item">
<label class="layui-form-label">推送类型</label>
<div class="layui-input-block">
<input type="radio" name="type" value="1" title="普通推送" checked>
<input type="radio" name="type" value="2" title="重要推送">
</div>
</div>
</div> -->
<div class="layui-form-item">
<!-- <div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="radio" name="status" value="1" title="启用" checked>
<input type="radio" name="status" value="0" title="禁用">
</div>
</div>
</div> -->
<div class="layui-form-item">
<!-- <div class="layui-form-item">
<label class="layui-form-label">排序</label>
<div class="layui-input-block">
<input type="number" name="sort" value="0" placeholder="请输入排序值" autocomplete="off" class="layui-input">
</div>
</div>
</div> -->
<div class="layui-form-item">
<div class="layui-input-block">
@ -64,12 +83,54 @@
</form>
</div>
<script type="text/javascript">
layui.use(['form', 'upload', 'layer'], function(){
var form = layui.form;
var upload = layui.upload;
var layer = layui.layer;
var $ = layui.jquery;
<script>
layui.use(['layer', 'jquery', 'form', 'upload'], function () {
var $ = layui.jquery,
layer = layui.layer,
form = layui.form,
upload = layui.upload;
// 动态加载推送平台
$.getJSON("{$config['admin_route']}yunzeradmin/contentpushsetting", function (res) {
if (res.data.length > 0) {
var $select = $('#pushPlatformSelect');
res.data.forEach(function (item) {
if (item.status == 1) {
$select.append(
$('<option>', {
value: item.id,
text: item.title
})
);
}
});
form.render('select');
}
});
// 选择推送内容
$('#selectPushContent').on('click', function () {
var pushCate = $('select[name="pushcate"]').val();
// 弹窗选择内容
layer.open({
type: 2,
title: '选择推送内容',
area: ['800px', '500px'],
content: "{$config['admin_route']}yunzeradmin/selectpushcontent?pushcate=" + pushCate,
success: function (layero, index) {
// 可在弹窗页面通过父页面回调选中内容
window.setPushContent = function (title, url) {
$('#pushTitleInput').val(title);
// 如果需要同步设置跳转链接
if (url) {
$('input[name="url"]').val(url);
}
layer.close(index);
}
}
});
});
// 图片上传
upload.render({

View File

@ -0,0 +1,109 @@
{include file="public/header" /}
<div class="config-container">
<div class="config-header" style="display: flex;flex-direction: column;flex-wrap: wrap;align-items: flex-start;">
<div class="maintitle">
<i class="layui-icon layui-icon-set-fill"></i>
<span>推送配置列表</span>
</div>
<div style="display: flex;align-items: flex-start;flex-direction: column;gap: 15px;margin-bottom: 10px;">
<div>
<button class="layui-btn layui-bg-blue" onclick="addSetting()">
<i class="layui-icon layui-icon-add-1"></i>添加配置
</button>
<button type="button" class="layui-btn layui-btn-primary layui-border-blue" onclick="refreshSetting()">
<i class="layui-icon layui-icon-refresh"></i>刷新
</button>
</div>
</div>
</div>
<table id="contentPushSettingTable" lay-filter="contentPushSettingTable"></table>
</div>
<script type="text/html" id="settingTableBar">
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<script type="text/javascript">
layui.use(['table', 'layer'], function () {
var table = layui.table;
var layer = layui.layer;
var $ = layui.jquery;
// 渲染表格
table.render({
elem: '#contentPushSettingTable',
url: '{$config["admin_route"]}yunzeradmin/contentpushsetting',
page: true,
cols: [[
{field: 'id', title: 'ID', width: 80, sort: true},
{field: 'title', title: '配置标题', width: 200},
{field: 'value', title: '配置值', width: 300},
{field: 'status', title: '状态', width: 100, templet: function(d){
return d.status == 1 ?
'<span class="layui-badge layui-bg-green">启用</span>' :
'<span class="layui-badge">禁用</span>';
}},
{field: 'sort', title: '排序', width: 100, sort: true},
{field: 'create_time', title: '创建时间', width: 180},
{title: '操作', width: 160, toolbar: '#settingTableBar', fixed: 'right'}
]],
limit: 10,
limits: [10, 20, 30, 50]
});
// 工具条事件
table.on('tool(contentPushSettingTable)', function(obj){
var data = obj.data;
if(obj.event === 'edit'){
editSetting(data.id);
} else if(obj.event === 'del'){
layer.confirm('确定要删除该配置吗?', function(index){
$.post('{$config["admin_route"]}yunzeradmin/contentpushsettingdel', {id: data.id}, function(res){
if(res.code == 0){
layer.msg('删除成功', {icon: 1});
table.reload('contentPushSettingTable');
} else {
layer.msg(res.msg, {icon: 2});
}
});
layer.close(index);
});
}
});
// 刷新
window.refreshSetting = function(){
table.reload('contentPushSettingTable');
};
// 添加
window.addSetting = function(){
layer.open({
type: 2,
title: '添加推送配置',
area: ['800px', '600px'],
content: '{$config["admin_route"]}yunzeradmin/contentpushsettingadd',
end: function(){
table.reload('contentPushSettingTable');
}
});
};
// 编辑
window.editSetting = function(id){
layer.open({
type: 2,
title: '编辑推送配置',
area: ['600px', '400px'],
content: '{$config["admin_route"]}yunzeradmin/contentpushsettingadd?id=' + id,
end: function(){
table.reload('contentPushSettingTable');
}
});
};
});
</script>
{include file="public/tail" /}

View File

@ -0,0 +1,75 @@
{include file="public/header" /}
<div class="config-container">
<form class="layui-form" action="{$config['admin_route']}yunzeradmin/contentpushsettingadd" method="post" lay-filter="contentPushForm">
{if isset($info.id)}
<input type="hidden" name="id" value="{$info.id}">
{/if}
<div class="layui-form-item">
<label class="layui-form-label">配置标题</label>
<div class="layui-input-block">
<input type="text" name="title" required lay-verify="required" placeholder="请输入配置标题" autocomplete="off" class="layui-input" value="{$info.title|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">配置值</label>
<div class="layui-input-block">
<textarea name="value" placeholder="请输入配置值" class="layui-textarea" required lay-verify="required">{$info.value|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="radio" name="status" value="1" title="启用" {if !isset($info.status) || $info.status==1}checked{/if}>
<input type="radio" name="status" value="0" title="禁用" {if isset($info.status) && $info.status==0}checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">排序</label>
<div class="layui-input-block">
<input type="number" name="sort" value="{$info.sort|default='0'}" placeholder="请输入排序值" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="contentPushForm">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
<script type="text/javascript">
layui.use(['form', 'upload', 'layer'], function(){
var form = layui.form;
var upload = layui.upload;
var layer = layui.layer;
var $ = layui.jquery;
// 表单提交
form.on('submit(contentPushForm)', function(data){
$.ajax({
url: data.form.action,
type: 'POST',
data: data.field,
success: function(res){
if(res.code == 0){
layer.msg(res.msg, {icon: 1}, function(){
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
parent.layui.table.reload('contentPushTable');
});
} else {
layer.msg(res.msg, {icon: 2});
}
}
});
return false;
});
});
</script>
{include file="public/tail" /}

View File

@ -0,0 +1,29 @@
{include file="public/header" /}
<div style="padding: 20px;">
<table class="layui-table">
<thead>
<tr>
<th>标题</th>
</tr>
</thead>
<tbody>
<?php foreach ($data as $item): ?>
<tr>
<td>
<a href="javascript:;" onclick="selectThis('<?php echo addslashes($item['title']); ?>', '<?php echo isset($item['url']) ? addslashes($item['url']) : ''; ?>')">
<?php echo htmlspecialchars($item['title']); ?>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script>
function selectThis(title, url) {
if (window.parent && window.parent.setPushContent) {
window.parent.setPushContent(title, url || '');
}
}
</script>
{include file="public/tail" /}

View File

@ -17,7 +17,7 @@
*/
/**
* 游戏下载控制器
* 资源下载控制器
*/
namespace app\index\controller;
use app\index\controller\BaseController;
@ -27,6 +27,8 @@ use think\facade\Request;
use app\index\model\Resources\Resources;
use app\index\model\Resources\ResourcesCategory;
use app\index\model\Attachments;
use app\index\model\Users;
use app\index\model\Articles\Articles;
class ResourcesController extends BaseController
{
@ -66,7 +68,7 @@ class ResourcesController extends BaseController
return View::fetch();
}
// 游戏列表页
// 资源列表页
public function list()
{
$cid = input('cid/d', 0);
@ -99,60 +101,84 @@ class ResourcesController extends BaseController
return View::fetch('list');
}
// 游戏详情页
// 资源详情页
public function detail()
{
$id = Request::param('id/d', 0);
$game = Resources::where('id', $id)->find();
$resources = Resources::where('id', $id)->find();
if (!$game) {
return json(['code' => 0, 'msg' => '游戏不存在或已被删除']);
if (!$resources) {
return json(['code' => 0, 'msg' => '资源不存在或已被删除']);
}
// 如果size没有从附件表中获取
if (empty($game['size']) && !empty($game['fileurl'])) {
$attachment = Attachments::where('src', $game['fileurl'])
if (empty($resources['size']) && !empty($resources['fileurl'])) {
$attachment = Attachments::where('src', $resources['fileurl'])
->find();
if ($attachment && !empty($attachment['size'])) {
$size = $attachment['size'];
// 转换文件大小为合适的单位
if ($size >= 1073741824) { // 1GB = 1024MB = 1024*1024KB = 1024*1024*1024B
$game['size'] = round($size / 1073741824, 2) . 'GB';
$resources['size'] = round($size / 1073741824, 2) . 'GB';
} elseif ($size >= 1048576) { // 1MB = 1024KB = 1024*1024B
$game['size'] = round($size / 1048576, 2) . 'MB';
$resources['size'] = round($size / 1048576, 2) . 'MB';
} else {
$game['size'] = round($size / 1024, 2) . 'KB';
$resources['size'] = round($size / 1024, 2) . 'KB';
}
}
}
// 获取分类名称
$cateName = ResourcesCategory::where('id', $game['cate'])
$cateName = ResourcesCategory::where('id', $resources['cate'])
->value('name');
// 获取上一个和下一个游戏
$prevGame = Resources::where('id', '<', $id)
// 获取作者信息
$authorInfo = Users::where('name', $resources['uploader'])->find();
// var_dump($authorInfo);
if ($authorInfo) {
// 统计作者的文章数
$resourcesCount = Articles::where('author', $resources['uploader'])->count();
// 统计作者的资源数
$resourceCount = Resources::where('uploader', $resources['uploader'])->count();
$authorData = [
'avatar' => $authorInfo['avatar'] ?: '/static/images/avatar.png',
'name' => $authorInfo['name'],
'resource_count' => $resourceCount,
'article_count' => $resourcesCount
];
} else {
$authorData = [
'avatar' => '/static/images/avatar.png',
'name' => $resources['author'],
'resource_count' => 0,
'article_count' => 0
];
}
// 获取上一个和下一个资源
$prevResources = Resources::where('id', '<', $id)
->where('delete_time', null)
->where('status', 1)
->where('cate', $game['cate'])
->where('cate', $resources['cate'])
->field(['id', 'title'])
->order('id DESC')
->find();
$nextGame = Resources::where('id', '>', $id)
$nextResources = Resources::where('id', '>', $id)
->where('delete_time', null)
->where('status', 1)
->where('cate', $game['cate'])
->where('cate', $resources['cate'])
->field(['id', 'title'])
->order('id ASC')
->find();
// 获取相关游戏(同分类的其他游戏)
$relatedGames = Db::table('yz_resources')
// 获取相关资源(同分类的其他资源
$relatedResourcess = Db::table('yz_resources')
->alias('g')
->join('yz_resources_category c', 'g.cate = c.id')
->where('g.cate', $game['cate'])
->where('g.cate', $resources['cate'])
->where('g.id', '<>', $id)
->where('g.delete_time', null)
->where('g.status', 1)
@ -162,7 +188,7 @@ class ResourcesController extends BaseController
'IF(g.icon IS NULL OR g.icon = "", c.icon, g.icon) as icon'
])
->order('g.id DESC')
->limit(3)
->limit(5)
->select()
->toArray();
@ -172,28 +198,29 @@ class ResourcesController extends BaseController
'code' => 1,
'msg' => '获取成功',
'data' => [
'game' => $game,
'resources' => $resources,
'cateName' => $cateName,
'prevGame' => $prevGame,
'nextGame' => $nextGame,
'relatedGames' => $relatedGames
'prevResources' => $prevResources,
'nextResources' => $nextResources,
'relatedResourcess' => $relatedResourcess
]
]);
}
// 非 AJAX 请求返回视图
View::assign([
'game' => $game,
'resources' => $resources,
'cateName' => $cateName,
'prevGame' => $prevGame,
'nextGame' => $nextGame,
'relatedGames' => $relatedGames
'authorInfo' => $authorData,
'prevResources' => $prevResources,
'nextResources' => $nextResources,
'relatedResourcess' => $relatedResourcess
]);
return View::fetch('detail');
}
// 游戏下载
// 资源下载
public function downloadurl()
{
if (!Request::isAjax()) {
@ -202,13 +229,13 @@ class ResourcesController extends BaseController
$id = Request::param('id/d', 0);
// 获取游戏信息
$game = Resources::where('id', $id)
// 获取资源信息
$resources = Resources::where('id', $id)
->where('delete_time', null)
->find();
if (!$game) {
return json(['code' => 0, 'msg' => '游戏不存在']);
if (!$resources) {
return json(['code' => 0, 'msg' => '资源不存在']);
}
// 更新下载次数
@ -222,7 +249,7 @@ class ResourcesController extends BaseController
'code' => 1,
'msg' => '下载成功',
'data' => [
'url' => $game['url']
'url' => $resources['url']
]
]);
} else {
@ -248,7 +275,7 @@ class ResourcesController extends BaseController
}
/**
* 更新游戏访问次数
* 更新资源访问次数
*/
public function updateViews()
{
@ -263,9 +290,9 @@ class ResourcesController extends BaseController
try {
// 更新访问次数
$game = Resources::where('id', $id)->find();
if (!$game) {
return json(['code' => 0, 'msg' => '游戏不存在']);
$resources = Resources::where('id', $id)->find();
if (!$resources) {
return json(['code' => 0, 'msg' => '资源不存在']);
}
// 更新访问次数

View File

@ -23,7 +23,8 @@
</h3>
<ul class="category-menu">
{volist name="cate.subCategories" id="subCategory"}
<li class="menu-item {$cate.id == $subCategory.id ? 'active' : ''}" data-cateid="{$subCategory.id}">
<li class="menu-item {$cate.id == $subCategory.id ? 'active' : ''}"
data-cateid="{$subCategory.id}">
<span>{$subCategory.name}</span>
<i class="layui-icon layui-icon-right"></i>
</li>
@ -47,16 +48,20 @@
</div>
<div class="card-content">
<div class="meta-info">
<span class="category-tag">${article.category_name || '未分类'}</span>
<time class="publish-date">${article.create_time || ''}</time>
<!-- <span class="category-tag">${article.category_name || '未分类'}</span> -->
<time class="publish-date">{:date('Y-m-d', $article['create_time'])}</time>
</div>
<a href="/index/articles/detail?id=${article.id}">
<h3 class="article-title">${article.title}</h3>
</a>
<div class="card-footer">
<div class="stats">
<span class="views"><i class="layui-icon layui-icon-eye"></i> ${article.views || 0}</span>
<span class="likes"><i class="layui-icon layui-icon-praise"></i> ${article.likes || 0}</span>
<span class="views"><i class="layui-icon layui-icon-eye"></i> ${article.views ||
0}</span>
<span class="likes"><i class="layui-icon layui-icon-praise"></i> ${article.likes ||
0}</span>
</div>
<a href="/index/articles/detail?id=${article.id}" class="read-more">阅读更多</a>
</div>
</div>
</article>
@ -131,16 +136,19 @@
</div>
<div class="card-content">
<div class="meta-info">
<span class="category-tag">${article.category_name || '未分类'}</span>
<time class="publish-date">${article.create_time || ''}</time>
</div>
<a href="/index/articles/detail?id=${article.id}" class="read-more">
<h3 class="article-title">${article.title}</h3>
</a>
<div class="card-footer">
<div class="stats">
<span class="views"><i class="layui-icon layui-icon-eye"></i> ${article.views || 0}</span>
<span class="likes"><i class="layui-icon layui-icon-praise"></i> ${article.likes || 0}</span>
</div>
<a href="/index/articles/detail?id=${article.id}" class="read-more">阅读更多</a>
<div class="times">
<time class="publish-date">${article.create_time ? new Date(article.create_time * 1000).toLocaleDateString() : ''}</time>
</div>
</div>
</div>
</article>`;
@ -318,7 +326,7 @@
}
.card-image {
height: 180px;
height: 135px;
position: relative;
overflow: hidden;
}
@ -350,10 +358,16 @@
flex-direction: column;
}
.card-content .read-more h3:hover {
color: #1E9FFF !important;
transition: all ease .5s;
font-weight:700;
}
.meta-info {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
/* margin-bottom: 12px; */
font-size: 0.85rem;
color: #666;
}
@ -398,12 +412,16 @@
}
.stats {
font-size: 0.85rem;
color: #999;
display: flex;
gap: 15px;
}
.times,
.stats {
font-size: 0.85rem;
color: #999;
}
.stats i {
margin-right: 3px;
}
@ -491,8 +509,13 @@
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 响应式设计 */

View File

@ -496,7 +496,7 @@
});
return `
<div class="opencourse product-item" onclick="window.open('/index/program/detail?id=${program.id || ''}', '_blank')">
<div class="opencourse product-item" onclick="window.open('/index/resources/detail?id=${program.id || ''}', '_blank')">
<div class="video">
<img src="${program.icon || ''}" alt="" class="cover">
</div>
@ -576,7 +576,7 @@
if (!game) return '';
return `
<div class="opencourse product-item" onclick="window.open('/index/game/detail?id=${game.id || ''}', '_blank')">
<div class="opencourse product-item" onclick="window.open('/index/resources/detail?id=${game.id || ''}', '_blank')">
<div class="video">
<img src="${game.icon || '/static/images/default-game.png'}" alt="" class="cover">
</div>

View File

@ -44,11 +44,17 @@
<div class="game-info">
<div class="title">Free</div>
<div class="infos">
<div class="infoitem"><span>更新时间:</span><?php echo date('Y-m-d', $game['create_time']); ?></div>
<div class="infoitem"><span>所属分类:</span><?php echo $cateName; ?></div>
<div class="infoitem"><span>程序编号:</span><?php echo $game['number']; ?></div>
<div class="infoitem"><span>查看:</span><?php echo $game['views']; ?></div>
<div class="infoitem"><span>下载:</span><?php echo $game['downloads']; ?></div>
<div class="infoitem"><span>更新时间:</span><span
class="infoitem-value"><?php echo date('Y-m-d', $game['create_time']); ?></span>
</div>
<div class="infoitem"><span>所属分类:</span><span
class="infoitem-value"><?php echo $cateName; ?></span></div>
<div class="infoitem"><span>程序编号:</span><span
class="infoitem-value"><?php echo $game['number']; ?></span></div>
<div class="infoitem"><span>查看:</span><span
class="infoitem-value"><?php echo $game['views']; ?></span></div>
<div class="infoitem"><span>下载:</span><span
class="infoitem-value"><?php echo $game['downloads']; ?></span></div>
</div>
</div>
</div>
@ -705,6 +711,14 @@
.disclaimer-content p {
margin-bottom: 0;
}
.infoitem-value {
border: 1px #0d6efd dashed;
padding: 3px 6px;
font-size: 13px;
background-color: #0081ff12;
border-radius: 5px;
}
</style>
{include file="component/foot" /}

View File

@ -1,17 +1,22 @@
{include file="component/head" /}
<link href="__STATIC__/css/lightbox.min.css" rel="stylesheet">
<link href="__CSS__/swiper-bundle.min.css" rel="stylesheet">
<script src="__JS__/jquery.min.js"></script>
<script src="__JS__/lightbox.min.js"></script>
<script src="__JS__/swiper-bundle.min.js"></script>
{include file="component/header-simple" /}
<div class="main">
<div class="main-top">
<div class="main-top-main">
<div class="main-title">
<?php echo $game['title']; ?>
<?php echo $resources['title']; ?>
</div>
<div class="location">
<div class="container">
<div class="location-item">
<a href="/">首页</a>
<span>></span>
<a href="/index/game/list" id="cateLink"><?php echo $cateName; ?></a>
<a href="/index/resources/list" id="cateLink"><?php echo $cateName; ?></a>
</div>
</div>
</div>
@ -20,17 +25,46 @@
<div class="detail-main">
<div class="detail-top">
<div class="detail-top-left">
<div class="detail-top-card">
<div class="detail-top-card-left">
<div class="article-cover">
<img src="<?php echo $game['icon'] ?: '/static/images/default-game.png'; ?>">
<!-- <img src="https://www.yunzer.cn/storage/uploads/20250523/b75a51fa606fd3a18261a6ea283d35fe.jpg" alt=""> -->
<div class="swiper resources-swiper">
<div class="swiper-wrapper">
{php}
// 兼容字符串和数组
$images = isset($resources['images']) ? $resources['images'] : [];
if (is_string($images)) {
$images = explode(',', $images);
}
$images = array_filter($images); // 移除空值
if (empty($images) && !empty($resources['icon'])) {
$images = [$resources['icon']];
}
{/php}
{volist name="images" id="image"}
<div class="swiper-slide">
<a href="<?php $img = trim($image, ', ');
echo (strpos($img, 'http') === 0 ? $img : request()->domain() . $img); ?>"
data-lightbox="resources-gallery">
<img src="<?php $img = trim($image, ', ');
echo (strpos($img, 'http') === 0 ? $img : request()->domain() . $img); ?>"
alt="<?php echo $resources['title']; ?>">
</a>
</div>
{/volist}
</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<div class="swiper-pagination"></div>
</div>
</div>
</div>
<div class="detail-top-card-right">
<div class="detail-top-card-right-top">
<!-- <div class="detail-top-card-right-top">
<div class="collect-btn">
<button class="btn btn-primary" id="collectBtn" data-game-id="<?php echo $game['id']; ?>">
<button class="btn btn-primary" id="collectBtn"
data-resources-id="<?php echo $resources['id']; ?>">
<i class="fa-solid fa-heart"></i> 收藏
</button>
</div>
@ -39,27 +73,49 @@
<i class="fa-solid fa-flag"></i> 举报
</button>
</div>
</div>
</div> -->
<div class="detail-top-card-right-middle">
<div class="game-info">
<div class="resources-info">
<div class="title">Free</div>
<div class="infos">
<div class="infoitem"><span>更新时间:</span><?php echo date('Y-m-d', $game['create_time']); ?></div>
<div class="infoitem"><span>所属分类:</span><?php echo $cateName; ?></div>
<div class="infoitem"><span>程序编号:</span><?php echo $game['number']; ?></div>
<div class="infoitem"><span>查看:</span><?php echo $game['views']; ?></div>
<div class="infoitem"><span>下载:</span><?php echo $game['downloads']; ?></div>
<div style="display: flex;">
<div class="infoitem">
<span>程序编号:</span>
<span class="infoitem-value"><?php echo $resources['number']; ?></span>
</div>
<div class="infoitem">
<span>所属分类:</span>
<span class="infoitem-value"><?php echo $cateName; ?></span>
</div>
</div>
<div style="display: flex;">
<div class="infoitem">
<span>更新时间:</span>
<span
class="infoitem-value"><?php echo date('Y-m-d', $resources['create_time']); ?></span>
</div>
<div class="infoitem">
<span>查看:</span>
<span class="infoitem-value"><?php echo $resources['views']; ?></span>
<span></span>
</div>
<div class="infoitem">
<span>下载:</span>
<span class="infoitem-value"><?php echo $resources['downloads']; ?></span>
<span></span>
</div>
</div>
</div>
</div>
</div>
<div class="detail-top-card-right-bottom">
<div class="game-actions1">
<div class="resources-actions1">
<div style="display: flex;gap: 30px;}">
<button id="downloadBtn" class="btn btn-primary">
<i class="fa-solid fa-download"></i> 立即下载
</button>
<button id="codeBtn" class="codebtn">
<i class="fa-solid fa-download"></i> 分享码:<?php echo $game['code']; ?>
<i class="fa-solid fa-download"></i> 分享码:<?php echo $resources['code']; ?>
</button>
</div>
</div>
@ -67,22 +123,62 @@
</div>
</div>
</div>
<div class="detail-top-right">
<div class="aboutauthor-main">
<div class="aboutauthor-main-top">
<div class="aboutauthor-avatar">
<img src="{$authorInfo.avatar}" alt="作者头像">
</div>
<div class="aboutauthor-info">
<div class="author-name">{$authorInfo.name}</div>
</div>
</div>
<div class="aboutauthor-main-middle">
<div class="author-stats">
<div class="author-stats-item">
<h6>资源</h6>
<span class="count">{$authorInfo.resource_count}</span>
</div>
<div class="author-stats-item">
<h6>文章</h6>
<span class="count">{$authorInfo.article_count}</span>
</div>
<div class="author-stats-item">
<h6>粉丝</h6>
<span class="count">
0
</span>
</div>
</div>
</div>
</div>
<div class="aboutauthor-btn">
<button class="follow-btn">
<i class="fa fa-user-plus"></i> 关注他
</button>
<button class="message-btn">
<i class="fa fa-envelope"></i> 发私信
</button>
</div>
</div>
</div>
<div class="detail-middle">
<div class="detial-middle-left">
<div class="game-detail">
<div class="game-info">
<div class="game-content">
<div class="game-desc">
<?php echo $game['content']; ?>
<div class="resources-detail">
<div class="resources-info">
<div class="resources-content">
<div class="resources-desc">
<?php echo $resources['content']; ?>
</div>
</div>
<div class="game-actions">
<div class="resources-actions">
<div style="display: flex;gap: 30px;}">
<button id="downloadBtn" class="btn btn-primary">
<i class="fa-solid fa-download"></i> 立即下载
</button>
<button id="codeBtn" class="codebtn">
<i class="fa-solid fa-download"></i> 分享码:<?php echo $game['code']; ?>
<i class="fa-solid fa-download"></i> 分享码:<?php echo $resources['code']; ?>
</button>
</div>
</div>
@ -97,27 +193,27 @@
</div>
</div>
<div class="game-navigation">
<div class="prev-game" id="prevGame">
<div class="resources-navigation">
<div class="prev-resources" id="prevResources">
</div>
<div class="next-game" id="nextGame">
<div class="next-resources" id="nextResources">
</div>
</div>
<!-- 相关游戏 -->
<?php if (!empty($relatedGames)): ?>
<div class="related-games">
<h3>相关游戏</h3>
<div class="game-list">
<?php foreach ($relatedGames as $related): ?>
<div class="game-item"
onclick="window.location.href='/index/game/detail?id=<?php echo $related['id']; ?>'">
<div class="game-cover">
<img src="<?php echo $related['icon'] ?: '/static/images/default-game.png'; ?>"
<!-- 相关资源 -->
<?php if (!empty($relatedResourcess)): ?>
<div class="related-resourcess">
<h3>相关资源</h3>
<div class="resources-list">
<?php foreach ($relatedResourcess as $related): ?>
<div class="resources-item"
onclick="window.location.href='/index/resources/detail?id=<?php echo $related['id']; ?>'">
<div class="resources-cover">
<img src="<?php echo $related['icon'] ?: '/static/images/default-resources.png'; ?>"
alt="<?php echo $related['title']; ?>">
</div>
<div class="game-info">
<h4 class="game-title-1"><?php echo $related['title']; ?></h4>
<div class="resources-info">
<h4 class="resources-title-1"><?php echo $related['title']; ?></h4>
</div>
</div>
<?php endforeach; ?>
@ -144,15 +240,16 @@
<script>
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function () {
// 获取游戏ID
const gameId = new URLSearchParams(window.location.search).get('id');
if (!gameId) {
alert('游戏ID不存在');
// 获取资源ID
const resourcesId = new URLSearchParams(window.location.search).get('id');
if (!resourcesId) {
alert('资源ID不存在');
return;
}
// 获取游戏详情
fetch('/index/game/detail?id=' + gameId, {
// 获取资源详情
fetch('/index/resources/detail?id=' + resourcesId, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
@ -161,42 +258,42 @@
.then(result => {
if (result.code === 1) {
// 渲染上一篇
const prevGame = document.getElementById('prevGame');
if (result.data.prevGame) {
prevGame.innerHTML = `
<a href="/index/game/detail?id=${result.data.prevGame.id}">
<i class="fa fa-arrow-left"></i> 上一篇:${result.data.prevGame.title}
const prevResources = document.getElementById('prevResources');
if (result.data.prevResources) {
prevResources.innerHTML = `
<a href="/index/resources/detail?id=${result.data.prevResources.id}">
<i class="fa fa-arrow-left"></i> 上一篇:${result.data.prevResources.title}
</a>
`;
} else {
prevGame.innerHTML = '<span class="disabled"><i class="fa fa-arrow-left"></i> 没有上一篇了</span>';
prevResources.innerHTML = '<span class="disabled"><i class="fa fa-arrow-left"></i> 没有上一篇了</span>';
}
// 渲染下一篇
const nextGame = document.getElementById('nextGame');
if (result.data.nextGame) {
nextGame.innerHTML = `
<a href="/index/game/detail?id=${result.data.nextGame.id}">
下一篇:${result.data.nextGame.title} <i class="fa fa-arrow-right"></i>
const nextResources = document.getElementById('nextResources');
if (result.data.nextResources) {
nextResources.innerHTML = `
<a href="/index/resources/detail?id=${result.data.nextResources.id}">
下一篇:${result.data.nextResources.title} <i class="fa fa-arrow-right"></i>
</a>
`;
} else {
nextGame.innerHTML = '<span class="disabled">没有下一篇了 <i class="fa fa-arrow-right"></i></span>';
nextResources.innerHTML = '<span class="disabled">没有下一篇了 <i class="fa fa-arrow-right"></i></span>';
}
}
})
.catch(error => {
console.error('获取游戏详情失败:', error);
console.error('获取资源详情失败:', error);
});
// 更新访问次数
updateGameViews(gameId);
updateResourcesViews(resourcesId);
// 下载功能
const downloadBtn = document.getElementById('downloadBtn');
if (downloadBtn) {
downloadBtn.addEventListener('click', function () {
fetch('/index/game/downloadurl?id=' + gameId, {
fetch('/index/resources/downloadurl?id=' + resourcesId, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
@ -210,7 +307,7 @@
})
.then(data => {
if (data.code === 1) {
const downloadsElement = document.getElementById('gameDownloads');
const downloadsElement = document.getElementById('resourcesDownloads');
if (downloadsElement) {
let downloads = parseInt(downloadsElement.textContent);
downloadsElement.textContent = downloads + 1;
@ -233,11 +330,45 @@
});
}
const swiper = new Swiper('.resources-swiper', {
slidesPerView: 1,
spaceBetween: 30,
loop: true,
autoplay: {
delay: 3000,
disableOnInteraction: false,
},
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
// 初始化 Lightbox
lightbox.option({
'resizeDuration': 200,
'wrapAround': true,
'albumLabel': "图片 %1 / %2",
'fadeDuration': 300,
'imageFadeDuration': 300,
'positionFromTop': 100,
'maxWidth': 1200,
'maxHeight': 800,
'disableScrolling': true,
'showImageNumberLabel': true,
'alwaysShowNavOnTouchDevices': true
});
//复制分享码
const codeBtn = document.getElementById('codeBtn');
if (codeBtn) {
codeBtn.addEventListener('click', function () {
const code = '<?php echo $game['code']; ?>';
const code = '<?php echo $resources['code']; ?>';
if (code) {
// 创建一个临时输入框
const tempInput = document.createElement('input');
@ -264,8 +395,7 @@
// 返回顶部功能
const goToTop = document.getElementById('goToTop');
// 监听滚动事件
if (goToTop) {
window.addEventListener('scroll', function () {
if (window.pageYOffset > 300) {
goToTop.classList.add('show');
@ -273,30 +403,29 @@
goToTop.classList.remove('show');
}
});
// 点击返回顶部
goToTop.addEventListener('click', function () {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
});
// 更新游戏访问次数
function updateGameViews(gameId) {
fetch('/index/game/updateViews', {
// 更新资源访问次数
function updateResourcesViews(resourcesId) {
fetch('/index/resources/updateViews', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: 'id=' + gameId
body: 'id=' + resourcesId
})
.then(response => response.json())
.then(result => {
if (result.code === 1) {
const viewsElement = document.querySelector('.game-views');
const viewsElement = document.querySelector('.resources-views');
if (viewsElement) {
viewsElement.innerHTML = `<i class="fa-solid fa-eye"></i> ${result.data.views}`;
}
@ -356,6 +485,17 @@
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
}
.detail-top-left {
width: 70%;
}
.detail-top-right {
width: 30%;
border-left: 1px solid #eee;
margin: 20px;
}
.detail-top-card {
@ -368,9 +508,10 @@
.detail-top-card img {
width: 400px;
height: auto;
width: 350px;
height: 260px;
border-radius: 8px;
object-fit: cover;
overflow: hidden;
}
@ -384,35 +525,36 @@
.detail-top-card-right-top {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 25px;
}
.detail-top-card-right-middle {}
.detail-top-card-right-middle .game-info {
.detail-top-card-right-middle .resources-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
}
.detail-top-card-right-middle .game-info .title {
font-size: 40px;
.detail-top-card-right-middle .resources-info .title {
font-size: 30px;
font-weight: 700;
color: #42d697;
margin-bottom: 15px;
}
.detail-top-card-right-middle .game-info .infos {
.detail-top-card-right-middle .resources-info .infos {
display: flex;
flex-direction: column;
gap: 20px;
}
.detail-top-card-right-middle .game-info .infos .infoitem {
margin-right: 60px;
.detail-top-card-right-middle .resources-info .infos .infoitem {
margin-right: 40px;
}
.detail-top-card-right-middle .game-info .infos .infoitem span {
.detail-top-card-right-middle .resources-info .infos .infoitem span {
color: #7d879c;
}
@ -439,20 +581,20 @@
color: #fff !important;
}
.game-detail {
.resources-detail {
padding: 50px;
background: #fff;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.game-header {
.resources-header {
margin-bottom: 30px;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
}
.game-title {
.resources-title {
font-size: 30px;
font-weight: 700;
color: #333;
@ -464,7 +606,7 @@
overflow: hidden;
}
.game-title-1 {
.resources-title-1 {
font-size: 16px;
font-weight: 700;
color: #333;
@ -476,7 +618,7 @@
overflow: hidden;
}
.game-meta {
.resources-meta {
display: flex;
flex-wrap: wrap;
gap: 20px;
@ -484,38 +626,38 @@
font-size: 14px;
}
.game-meta span {
.resources-meta span {
display: flex;
align-items: center;
}
.game-meta i {
.resources-meta i {
margin-right: 5px;
}
.game-content {
.resources-content {
line-height: 1.8;
color: #333;
font-size: 16px;
margin-bottom: 30px;
}
.game-cover {
.resources-cover {
margin-bottom: 20px;
}
.game-cover img {
.resources-cover img {
width: 100%;
height: 300px;
object-fit: cover;
border-radius: 8px;
}
.game-desc {
.resources-desc {
margin-bottom: 30px;
}
.game-actions {
.resources-actions {
display: flex;
justify-content: center;
gap: 40px;
@ -525,30 +667,30 @@
border-bottom: 1px solid #eee;
}
.game-actions1 {
.resources-actions1 {
display: flex;
margin: 20px 0;
}
.game-navigation {
.resources-navigation {
display: flex;
justify-content: space-between;
margin: 30px 0;
}
.prev-game,
.next-game {
.prev-resources,
.next-resources {
max-width: 45%;
}
.prev-game a,
.next-game a {
.prev-resources a,
.next-resources a {
color: #333 !important;
text-decoration: none;
}
.prev-game a:hover,
.next-game a:hover {
.prev-resources a:hover,
.next-resources a:hover {
color: #f57005 !important;
transition: all 0.3s ease;
}
@ -581,11 +723,11 @@
transform: translateY(-2px);
}
.related-games {
.related-resourcess {
margin: 40px 0;
}
.related-games h3 {
.related-resourcess h3 {
font-size: 20px;
font-weight: 600;
margin-bottom: 20px;
@ -601,35 +743,35 @@
border-bottom: 1px solid #eee;
}
.game-list {
.resources-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(5, 1fr);
gap: 20px;
}
.game-item {
.resources-item {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.game-item:hover {
.resources-item:hover {
transform: translateY(-5px);
}
.game-item a {
.resources-item a {
text-decoration: none;
color: inherit;
}
.game-cover img {
.resources-cover img {
width: 100%;
height: 150px;
object-fit: cover;
}
.game-info {
.resources-info {
padding: 10px;
}
@ -664,15 +806,15 @@
}
@media (max-width: 768px) {
.game-title {
.resources-title {
font-size: 24px;
}
.game-list {
.resources-list {
grid-template-columns: repeat(1, 1fr);
}
.game-meta {
.resources-meta {
gap: 10px;
}
@ -705,6 +847,110 @@
.disclaimer-content p {
margin-bottom: 0;
}
.infoitem-value {
border: 1px #0d6efd dashed;
padding: 3px 6px;
font-size: 13px;
background-color: #0081ff12;
border-radius: 5px;
}
.detail-top-card-left {
width: 350px;
}
.swiper .swiper-button-prev,
.swiper .swiper-button-next {
color: #3881fd;
background: rgba(255, 255, 255, 0.9);
width: 40px;
height: 40px;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.swiper .swiper-button-prev:after,
.swiper .swiper-button-next:after {
font-size: 18px;
}
/* about author css */
.detail-top-right .aboutauthor-main {
display: flex;
flex-direction: column;
padding: 20px;
}
.detail-top-right .aboutauthor-main .aboutauthor-main-top {
display: flex;
align-items: center;
padding-left: 20px !important;
padding: 20px 0;
border-bottom: 1px solid #efefef;
margin-bottom: 20px;
}
.detail-top-right .aboutauthor-main .aboutauthor-main-top .aboutauthor-avatar {
margin-right: 12px;
}
.detail-top-right .aboutauthor-main .aboutauthor-main-top .aboutauthor-info .author-name {
font-size: 20px;
font-weight: 700;
}
.detail-top-right .aboutauthor-main .aboutauthor-main-top .aboutauthor-avatar img {
width: 60px;
height: 60px;
border-radius: 4px;
box-sizing: border-box;
margin: 0px;
min-width: 0px;
max-width: 100%;
background-color: #fff;
}
.detail-top-right .aboutauthor-main .aboutauthor-main-middle {
/* margin-left: 20px; */
}
.detail-top-right .aboutauthor-main .aboutauthor-main-middle .author-stats {
display: flex;
justify-content: space-evenly;
}
.detail-top-right .aboutauthor-main .aboutauthor-main-middle .author-stats .author-stats-item {
display: flex;
flex-direction: column;
align-items: center;
}
.detail-top-right .aboutauthor-main .aboutauthor-main-middle .author-stats .author-stats-item .count {
/* font-size: 30px; */
font-weight: 700;
}
.detail-top-right .aboutauthor-btn {
display: flex;
justify-content: space-evenly;
padding: 20px 0;
}
.detail-top-right .aboutauthor-btn .follow-btn {
background-color: #0081ff;
color: #fff;
padding: 10px 20px;
border-radius: 8px;
border: none;
}
.detail-top-right .aboutauthor-btn .message-btn {
color: #0081ff;
padding: 10px 20px;
border-radius: 8px;
border: 1px solid #eee;
}
</style>
{include file="component/foot" /}

View File

@ -19,7 +19,7 @@
<div class="resource-card">
<div class="card-image">
<img src="{$resource.icon|default='/static/images/default-resource.jpg'}"
alt="{$resource.name}">
alt="{$resource.title}">
<div class="image-overlay"></div>
</div>
<div class="card-content">
@ -27,7 +27,7 @@
<span class="resource-number">{$resource.number}</span>
<time class="create-date">{$resource.create_time|date="Y-m-d"}</time>
</div>
<h3 class="resource-title">{$resource.name}</h3>
<h3 class="resource-title">{$resource.title}</h3>
<div class="card-footer">
<div class="resource-stats">
<span class="stat-item">
@ -186,6 +186,15 @@
margin: 0 0 15px;
color: #333;
line-height: 1.4;
border-top: 1px solid #f0f0f0;
padding-top: 10px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
max-height: 3.2em;
}
.card-footer {

1
public/.user.ini Normal file
View File

@ -0,0 +1 @@
open_basedir=/www/wwwroot/yunzer/:/tmp/

View File

@ -0,0 +1 @@
lI0pTvvpEPLRA8mlO-8Xppk2istc_8r7qhzXTqKYdCw.6eZtzN3ZriJCP7Qg_ZX7TmIAYjnic6ZfRahS65CaOnk

View File

@ -0,0 +1 @@
5X2Sd9gRT0S0OX2W

View File

@ -0,0 +1 @@
{"openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","scene_str":"03fd16235b7bd87691aef1ad5069cacb","ticket":"gQEg8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyQi1lVFFQbjNjUkYxdEFLMjFFY2wAAgQ47UJoAwQsAQAA","scan_time":1749216575,"event":"scan","raw_data":"<xml><ToUserName><![CDATA[gh_f77178d95f62]]><\/ToUserName>\n<FromUserName><![CDATA[oV8M16Um-4DkoAOlbTrIleYEOkFw]]><\/FromUserName>\n<CreateTime>1749216574<\/CreateTime>\n<MsgType><![CDATA[event]]><\/MsgType>\n<Event><![CDATA[SCAN]]><\/Event>\n<EventKey><![CDATA[03fd16235b7bd87691aef1ad5069cacb]]><\/EventKey>\n<Ticket><![CDATA[gQEg8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyQi1lVFFQbjNjUkYxdEFLMjFFY2wAAgQ47UJoAwQsAQAA]]><\/Ticket>\n<\/xml>"}

View File

@ -0,0 +1 @@
{"uid":2,"name":"云朵_765957","avatar":"\/storage\/avatar\/20250527\\1ef4d2905dfcc9d6aa0859ff5c144730.webp","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"357026310@qq.com","user_password":"412b7ee19961676b97a89cb04a22d7d3","expire_time":1755358505,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"uid":11,"name":"wx_48533dd9","avatar":"\/static\/images\/avatar.png","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"wx_48533dd9","user_password":"4f23966a15cae894373a08a4106b02ca","expire_time":1749823469,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"uid":12,"name":"wx_48533dd9","avatar":"\/static\/images\/avatar.png","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"wx_48533dd9","user_password":"77cfdb8aaca36b01160b25ec595f9806","expire_time":1749823595,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","scene_str":"5491c392b09abb032c037de1f4fe53f0","ticket":"gQH87zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyWG5ZelJWbjNjUkYxdlNLMjFFY3IAAgTK7UJoAwQsAQAA","scan_time":1749216726,"event":"scan","raw_data":"<xml><ToUserName><![CDATA[gh_f77178d95f62]]><\/ToUserName>\n<FromUserName><![CDATA[oV8M16Um-4DkoAOlbTrIleYEOkFw]]><\/FromUserName>\n<CreateTime>1749216720<\/CreateTime>\n<MsgType><![CDATA[event]]><\/MsgType>\n<Event><![CDATA[SCAN]]><\/Event>\n<EventKey><![CDATA[5491c392b09abb032c037de1f4fe53f0]]><\/EventKey>\n<Ticket><![CDATA[gQH87zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyWG5ZelJWbjNjUkYxdlNLMjFFY3IAAgTK7UJoAwQsAQAA]]><\/Ticket>\n<\/xml>"}

View File

@ -0,0 +1 @@
{"openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","scene_str":"5eff05cb6092e044fee8561872f62862","ticket":"gQE58DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyYWJOOVFNbjNjUkYxcTlBM2hFYzUAAgRdo0NoAwQsAQAA","scan_time":1749263202,"event":"scan","raw_data":"<xml><ToUserName><![CDATA[gh_f77178d95f62]]><\/ToUserName>\n<FromUserName><![CDATA[oV8M16Um-4DkoAOlbTrIleYEOkFw]]><\/FromUserName>\n<CreateTime>1749263201<\/CreateTime>\n<MsgType><![CDATA[event]]><\/MsgType>\n<Event><![CDATA[SCAN]]><\/Event>\n<EventKey><![CDATA[5eff05cb6092e044fee8561872f62862]]><\/EventKey>\n<Ticket><![CDATA[gQE58DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyYWJOOVFNbjNjUkYxcTlBM2hFYzUAAgRdo0NoAwQsAQAA]]><\/Ticket>\n<\/xml>"}

View File

@ -0,0 +1 @@
{"openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","scene_str":"615459fab9bb9dc01071ac6bfdd0a957","ticket":"gQGV8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyMUJCX1E3bjNjUkYxdkxKMmhFY1cAAgTD7EJoAwQsAQAA","scan_time":1749216468,"event":"scan","raw_data":"<xml><ToUserName><![CDATA[gh_f77178d95f62]]><\/ToUserName>\n<FromUserName><![CDATA[oV8M16Um-4DkoAOlbTrIleYEOkFw]]><\/FromUserName>\n<CreateTime>1749216468<\/CreateTime>\n<MsgType><![CDATA[event]]><\/MsgType>\n<Event><![CDATA[SCAN]]><\/Event>\n<EventKey><![CDATA[615459fab9bb9dc01071ac6bfdd0a957]]><\/EventKey>\n<Ticket><![CDATA[gQGV8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyMUJCX1E3bjNjUkYxdkxKMmhFY1cAAgTD7EJoAwQsAQAA]]><\/Ticket>\n<\/xml>"}

View File

@ -0,0 +1 @@
{"uid":12,"name":"","avatar":"\/static\/images\/avatar.png","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"wx_48533dd9","user_password":"77cfdb8aaca36b01160b25ec595f9806","expire_time":1749823808,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"uid":13,"name":"","avatar":"\/static\/images\/avatar.png","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"wx_48533dd9","user_password":"2120e0715b79df8750bd750b52728c3c","expire_time":1749824017,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"uid":2,"name":"云朵_765957","avatar":"\/storage\/avatar\/20250527\\1ef4d2905dfcc9d6aa0859ff5c144730.webp","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"357026310@qq.com","user_password":"412b7ee19961676b97a89cb04a22d7d3","expire_time":1755358650,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"uid":13,"name":"","avatar":"\/static\/images\/avatar.png","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"wx_48533dd9","user_password":"2120e0715b79df8750bd750b52728c3c","expire_time":1749823904,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","scene_str":"cf42988370572ea9bcf87385fb90ecf7","ticket":"gQGf7zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySjdUbVJ6bjNjUkYxdWNJMk5FY0QAAgRg60JoAwQsAQAA","scan_time":1749216107,"event":"scan","raw_data":"<xml><ToUserName><![CDATA[gh_f77178d95f62]]><\/ToUserName>\n<FromUserName><![CDATA[oV8M16Um-4DkoAOlbTrIleYEOkFw]]><\/FromUserName>\n<CreateTime>1749216104<\/CreateTime>\n<MsgType><![CDATA[event]]><\/MsgType>\n<Event><![CDATA[SCAN]]><\/Event>\n<EventKey><![CDATA[cf42988370572ea9bcf87385fb90ecf7]]><\/EventKey>\n<Ticket><![CDATA[gQGf7zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySjdUbVJ6bjNjUkYxdWNJMk5FY0QAAgRg60JoAwQsAQAA]]><\/Ticket>\n<\/xml>"}

View File

@ -0,0 +1 @@
{"openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","scene_str":"d14ab9057231cd55da0f0654476b79d5","ticket":"gQHA7zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySHY1ZVJabjNjUkYxdHJKMk5FY04AAgQv7EJoAwQsAQAA","scan_time":1749216327,"event":"scan","raw_data":"<xml><ToUserName><![CDATA[gh_f77178d95f62]]><\/ToUserName>\n<FromUserName><![CDATA[oV8M16Um-4DkoAOlbTrIleYEOkFw]]><\/FromUserName>\n<CreateTime>1749216325<\/CreateTime>\n<MsgType><![CDATA[event]]><\/MsgType>\n<Event><![CDATA[SCAN]]><\/Event>\n<EventKey><![CDATA[d14ab9057231cd55da0f0654476b79d5]]><\/EventKey>\n<Ticket><![CDATA[gQHA7zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySHY1ZVJabjNjUkYxdHJKMk5FY04AAgQv7EJoAwQsAQAA]]><\/Ticket>\n<\/xml>"}

View File

@ -0,0 +1 @@
{"uid":13,"name":"","avatar":"\/static\/images\/avatar.png","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"wx_48533dd9","user_password":"2120e0715b79df8750bd750b52728c3c","expire_time":1749824337,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"uid":2,"name":"云朵_765957","avatar":"\/storage\/avatar\/20250527\\1ef4d2905dfcc9d6aa0859ff5c144730.webp","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"357026310@qq.com","user_password":"412b7ee19961676b97a89cb04a22d7d3","expire_time":1755358447,"is_auto_login":true,"login_status":"success"}

View File

@ -0,0 +1 @@
{"uid":2,"name":"云朵_765957","avatar":"\/storage\/avatar\/20250527\\1ef4d2905dfcc9d6aa0859ff5c144730.webp","openid":"oV8M16Um-4DkoAOlbTrIleYEOkFw","user_account":"357026310@qq.com","user_password":"412b7ee19961676b97a89cb04a22d7d3","expire_time":1755358386,"is_auto_login":true,"login_status":"success"}

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 KiB

Some files were not shown because too many files have changed in this diff Show More