Initial commit: Flutter 无书应用项目

This commit is contained in:
Developer
2026-03-30 02:35:31 +08:00
commit 9175ff9905
566 changed files with 103261 additions and 0 deletions

78
ht/inc/conn.php Normal file
View File

@@ -0,0 +1,78 @@
<?php
/**
* 数据库连接及全局配置文件
*/
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 网站全局配置
$CONFIG = [
// 网站标题
'site_title' => '简约投票简洁投票系统',
// 数据库配置
'db' => [
'host' => 'localhost',
'user' => 'toupiao',
'pass' => 'toupiao',
'name' => 'toupiao',
'port' => 3306,
'prefix' => 'tp_',
'charset' => 'utf8'
],
// 资源文件缓存控制(修改版本号可更新前端缓存)
'version' => [
'css' => '1.0.0',
'js' => '1.0.0'
]
];
// 数据库连接
function dbConnect() {
global $CONFIG;
$conn = new mysqli(
$CONFIG['db']['host'],
$CONFIG['db']['user'],
$CONFIG['db']['pass'],
$CONFIG['db']['name'],
$CONFIG['db']['port']
);
// 检查连接
if ($conn->connect_error) {
die("数据库连接失败: " . $conn->connect_error);
}
// 设置字符集
$conn->set_charset($CONFIG['db']['charset']);
return $conn;
}
// 数据表前缀函数
function getTable($table) {
global $CONFIG;
return $CONFIG['db']['prefix'] . $table;
}
// 获取带版本号的CSS路径
function getCssPath($file) {
global $CONFIG;
return "/assets/css/{$file}?v=" . $CONFIG['version']['css'];
}
// 获取带版本号的JS路径
function getJsPath($file) {
global $CONFIG;
return "/assets/js/{$file}?v=" . $CONFIG['version']['js'];
}
// 获取网站标题
function getSiteTitle() {
global $CONFIG;
return $CONFIG['site_title'];
}

495
ht/inc/css.css Normal file
View File

@@ -0,0 +1,495 @@
/**
* 公共CSS样式
*/
/* 全局重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-size: 14px;
line-height: 1.5;
color: #333;
background-color: #f5f5f5;
}
/* 顶部菜单样式 */
.header {
background-color: #2c3e50;
color: #fff;
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 100;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.header-container {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
height: 60px;
}
.logo {
font-size: 20px;
font-weight: bold;
}
.nav {
display: flex;
}
.nav-item {
margin-left: 20px;
color: #fff;
text-decoration: none;
transition: color 0.3s;
}
.nav-item:hover {
color: #3498db;
}
.active {
color: #3498db;
}
/* 主体内容 */
.main {
max-width: 1200px;
margin: 80px auto 20px;
padding: 20px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* 遮罩层样式 */
.mask-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.mask-dialog {
position: relative;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
width: 80%;
max-width: 600px;
display: flex;
flex-direction: column;
max-height: 90vh;
}
.mask-title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid #eee;
}
.mask-title h3 {
margin: 0;
font-size: 18px;
}
.mask-close {
font-size: 24px;
cursor: pointer;
color: #999;
transition: color 0.3s;
}
.mask-close:hover {
color: #333;
}
.mask-content {
padding: 20px;
overflow-y: auto;
flex: 1;
}
.mask-buttons {
padding: 15px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
position: sticky;
bottom: 0;
background-color: #fff;
border-radius: 0 0 5px 5px;
}
.mask-button {
padding: 8px 15px;
margin-left: 10px;
border: none;
border-radius: 3px;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-primary {
background-color: #3498db;
color: #fff;
}
.btn-primary:hover {
background-color: #2980b9;
}
.btn-default {
background-color: #f5f5f5;
color: #333;
}
.btn-default:hover {
background-color: #e0e0e0;
}
.btn-danger {
background-color: #e74c3c;
color: #fff;
}
.btn-danger:hover {
background-color: #c0392b;
}
/* 表格通用样式 */
.table-container {
width: 100%;
overflow-x: auto;
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
border: 1px solid #ddd;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f8f8f8;
font-weight: bold;
cursor: pointer;
position: relative;
}
th.sort-asc::after {
content: '↑';
margin-left: 5px;
}
th.sort-desc::after {
content: '↓';
margin-left: 5px;
}
tr:hover {
background-color: #f5f5f5;
}
/* 表单通用样式 */
.form-container {
max-width: 600px;
margin: 0 auto;
}
.form-group {
margin-bottom: 15px;
}
.form-label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-control:focus {
border-color: #3498db;
outline: none;
}
.form-error {
color: #e74c3c;
font-size: 12px;
margin-top: 5px;
}
.form-submit {
background-color: #3498db;
color: #fff;
border: none;
padding: 10px 15px;
border-radius: 3px;
cursor: pointer;
transition: background-color 0.3s;
}
.form-submit:hover {
background-color: #2980b9;
}
/* 分页样式 */
.pagination {
display: flex;
align-items: center;
justify-content: center;
margin: 20px 0;
}
.pagination.disabled {
display: none;
}
.page-item {
padding: 8px 12px;
margin: 0 3px;
border: 1px solid #ddd;
border-radius: 3px;
color: #333;
text-decoration: none;
transition: background-color 0.3s;
}
.page-item:hover:not(.disabled) {
background-color: #f5f5f5;
}
.page-item.disabled {
color: #ccc;
cursor: not-allowed;
background-color: #f5f5f5;
}
.page-select {
margin: 0 10px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
}
/* 提示框样式 */
.suggest-container {
position: absolute;
background-color: #fff;
border: 1px solid #ddd;
border-top: none;
border-radius: 0 0 3px 3px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
z-index: 100;
max-height: 300px;
overflow-y: auto;
}
.suggest-item {
padding: 10px;
cursor: pointer;
transition: background-color 0.3s;
}
.suggest-item:hover {
background-color: #f5f5f5;
}
.suggest-close {
padding: 10px;
text-align: right;
color: #999;
cursor: pointer;
border-bottom: 1px solid #eee;
}
/* 按钮通用样式 */
.btn {
display: inline-block;
padding: 8px 15px;
border: none;
border-radius: 3px;
cursor: pointer;
text-decoration: none;
transition: background-color 0.3s;
margin-right: 5px;
}
.btn-sm {
padding: 5px 10px;
font-size: 12px;
}
.btn-lg {
padding: 12px 20px;
font-size: 16px;
}
.btn-blue {
background-color: #3498db;
color: #fff;
}
.btn-blue:hover {
background-color: #2980b9;
}
.btn-green {
background-color: #2ecc71;
color: #fff;
}
.btn-green:hover {
background-color: #27ae60;
}
.btn-red {
background-color: #e74c3c;
color: #fff;
}
.btn-red:hover {
background-color: #c0392b;
}
.btn-gray {
background-color: #95a5a6;
color: #fff;
}
.btn-gray:hover {
background-color: #7f8c8d;
}
/* 卡片样式 */
.card {
background-color: #fff;
border-radius: 5px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
overflow: hidden;
}
.card-header {
padding: 15px;
border-bottom: 1px solid #eee;
background-color: #f8f8f8;
}
.card-title {
margin: 0;
font-size: 18px;
}
.card-body {
padding: 15px;
}
.card-footer {
padding: 15px;
border-top: 1px solid #eee;
background-color: #f8f8f8;
}
/* 投票选项样式 */
.vote-option {
border: 1px solid #ddd;
border-radius: 3px;
padding: 15px;
margin-bottom: 10px;
transition: all 0.3s;
cursor: pointer;
}
.vote-option:hover {
border-color: #3498db;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.vote-option.selected {
border-color: #3498db;
background-color: #ebf5fb;
}
.vote-option-img {
max-width: 100%;
height: auto;
margin-bottom: 10px;
}
.vote-result {
margin-top: 5px;
}
.vote-bar {
height: 20px;
background-color: #eee;
border-radius: 10px;
overflow: hidden;
margin-top: 5px;
}
.vote-bar-fill {
height: 100%;
background-color: #3498db;
border-radius: 10px;
}
/* 响应式调整 */
@media (max-width: 768px) {
.header-container {
height: auto;
padding: 10px 15px;
flex-direction: column;
}
.nav {
margin-top: 10px;
}
.main {
margin-top: 120px;
padding: 15px;
}
.mask-dialog {
width: 95%;
}
.form-container {
padding: 0 10px;
}
}

458
ht/inc/js.js Normal file
View File

@@ -0,0 +1,458 @@
/**
* 公共JavaScript函数
*/
/**
* Ajax通信函数
* @param {string} url - 请求地址
* @param {Object} data - 请求数据
* @param {Function} callback - 成功回调函数
* @param {Function} errorCallback - 错误回调函数
* @param {string} method - 请求方法GET或POST
*/
function ajaxRequest(url, data, callback, errorCallback, method) {
// 默认使用POST方法除非act为get
if (!method) {
method = (data && data.act === 'get') ? 'GET' : 'POST';
}
// 创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
// 准备发送请求
xhr.open(method, url, true);
// 设置请求头
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if (method === 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
// 处理响应
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var response;
try {
response = JSON.parse(xhr.responseText);
if (callback && typeof callback === 'function') {
callback(response);
}
} catch (e) {
console.error('解析JSON失败:', e);
if (errorCallback && typeof errorCallback === 'function') {
errorCallback('解析响应失败');
}
}
} else {
console.error('请求失败,状态码:', xhr.status);
if (errorCallback && typeof errorCallback === 'function') {
errorCallback('请求失败,状态码: ' + xhr.status);
}
}
}
};
// 处理请求超时
xhr.ontimeout = function() {
console.error('请求超时');
if (errorCallback && typeof errorCallback === 'function') {
errorCallback('请求超时');
}
};
// 处理网络错误
xhr.onerror = function() {
console.error('网络错误');
if (errorCallback && typeof errorCallback === 'function') {
errorCallback('网络错误');
}
};
// 发送请求
if (method === 'POST' && data) {
// 将对象转换为查询字符串
var params = Object.keys(data).map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]);
}).join('&');
xhr.send(params);
} else if (method === 'GET' && data) {
// 将查询参数添加到URL
var queryString = Object.keys(data).map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]);
}).join('&');
var separator = url.indexOf('?') !== -1 ? '&' : '?';
xhr.send();
} else {
xhr.send();
}
}
/**
* 分页显示函数
* @param {string} containerId - 分页容器ID
* @param {number} currentPage - 当前页码
* @param {number} totalPages - 总页数
* @param {Function} callback - 页码点击回调函数
*/
function pagination(containerId, currentPage, totalPages, callback) {
var container = document.getElementById(containerId);
if (!container) return;
// 清空容器
container.innerHTML = '';
// 如果没有数据或只有一页,不显示分页
if (totalPages <= 1) {
container.classList.add('disabled');
return;
}
container.classList.remove('disabled');
// 创建分页元素
var paginationHtml = '';
// 首页按钮
var firstDisabled = currentPage === 1 ? ' disabled' : '';
paginationHtml += '<a href="javascript:;" class="page-item first' + firstDisabled + '" data-page="1">首页</a>';
// 上一页按钮
var prevDisabled = currentPage === 1 ? ' disabled' : '';
var prevPage = Math.max(1, currentPage - 1);
paginationHtml += '<a href="javascript:;" class="page-item prev' + prevDisabled + '" data-page="' + prevPage + '">上一页</a>';
// 页码下拉选择
paginationHtml += '<select class="page-select">';
for (var i = 1; i <= totalPages; i++) {
var selected = i === currentPage ? ' selected' : '';
paginationHtml += '<option value="' + i + '"' + selected + '>第 ' + i + ' 页</option>';
}
paginationHtml += '</select>';
// 下一页按钮
var nextDisabled = currentPage === totalPages ? ' disabled' : '';
var nextPage = Math.min(totalPages, currentPage + 1);
paginationHtml += '<a href="javascript:;" class="page-item next' + nextDisabled + '" data-page="' + nextPage + '">下一页</a>';
// 尾页按钮
var lastDisabled = currentPage === totalPages ? ' disabled' : '';
paginationHtml += '<a href="javascript:;" class="page-item last' + lastDisabled + '" data-page="' + totalPages + '">尾页</a>';
// 设置HTML
container.innerHTML = paginationHtml;
// 绑定事件
// 点击页码按钮
var pageItems = container.querySelectorAll('.page-item');
for (var j = 0; j < pageItems.length; j++) {
var item = pageItems[j];
if (!item.classList.contains('disabled')) {
item.addEventListener('click', function() {
var page = parseInt(this.getAttribute('data-page'));
if (callback && typeof callback === 'function') {
callback(page);
}
});
}
}
// 下拉选择页码
var pageSelect = container.querySelector('.page-select');
if (pageSelect) {
pageSelect.addEventListener('change', function() {
var page = parseInt(this.value);
if (callback && typeof callback === 'function') {
callback(page);
}
});
}
}
/**
* 表格排序函数
* @param {string} tableId - 表格ID
* @param {number} colIndex - 排序列索引
* @param {string} type - 排序类型string, number, date
*/
function sortTable(tableId, colIndex, type) {
var table = document.getElementById(tableId);
if (!table) return;
var tbody = table.getElementsByTagName('tbody')[0];
var rows = tbody.getElementsByTagName('tr');
var sortedRows = Array.prototype.slice.call(rows, 0);
// 确定排序方向
var sortDirection = 1; // 1为升序-1为降序
if (table.getAttribute('data-sort-col') == colIndex) {
sortDirection = table.getAttribute('data-sort-dir') == 'asc' ? -1 : 1;
}
// 保存排序信息
table.setAttribute('data-sort-col', colIndex);
table.setAttribute('data-sort-dir', sortDirection == 1 ? 'asc' : 'desc');
// 排序
sortedRows.sort(function(a, b) {
var cellA = a.cells[colIndex].textContent.trim();
var cellB = b.cells[colIndex].textContent.trim();
if (type === 'number') {
return sortDirection * (parseFloat(cellA) - parseFloat(cellB));
} else if (type === 'date') {
var dateA = new Date(cellA);
var dateB = new Date(cellB);
return sortDirection * (dateA - dateB);
} else {
return sortDirection * cellA.localeCompare(cellB);
}
});
// 更新表格显示
for (var i = 0; i < sortedRows.length; i++) {
tbody.appendChild(sortedRows[i]);
}
// 更新表格头部排序指示器
var headers = table.getElementsByTagName('th');
for (var j = 0; j < headers.length; j++) {
headers[j].classList.remove('sort-asc', 'sort-desc');
}
if (headers[colIndex]) {
headers[colIndex].classList.add(sortDirection == 1 ? 'sort-asc' : 'sort-desc');
}
}
/**
* 显示遮罩层
* @param {string} title - 标题
* @param {string} content - 内容
* @param {Array} buttons - 按钮配置数组,格式:[{text: '按钮文字', callback: 回调函数, class: 'btn-class'}]
*/
function showMask(title, content, buttons) {
// 移除已存在的遮罩
closeMask();
// 创建遮罩层
var mask = document.createElement('div');
mask.className = 'mask-container';
// 创建对话框
var dialog = document.createElement('div');
dialog.className = 'mask-dialog';
// 创建标题栏
var titleBar = document.createElement('div');
titleBar.className = 'mask-title';
titleBar.innerHTML = '<h3>' + title + '</h3><span class="mask-close">&times;</span>';
// 创建内容区
var contentArea = document.createElement('div');
contentArea.className = 'mask-content';
contentArea.innerHTML = content;
// 创建按钮区
var buttonArea = document.createElement('div');
buttonArea.className = 'mask-buttons';
if (buttons && buttons.length > 0) {
buttons.forEach(function(btn) {
var button = document.createElement('button');
button.className = 'mask-button ' + (btn.class || '');
button.textContent = btn.text;
if (btn.callback && typeof btn.callback === 'function') {
button.addEventListener('click', function() {
btn.callback();
});
}
buttonArea.appendChild(button);
});
}
// 组装对话框
dialog.appendChild(titleBar);
dialog.appendChild(contentArea);
dialog.appendChild(buttonArea);
mask.appendChild(dialog);
// 添加到页面
document.body.appendChild(mask);
// 绑定关闭事件
var closeBtn = mask.querySelector('.mask-close');
if (closeBtn) {
closeBtn.addEventListener('click', closeMask);
}
// 绑定点击遮罩层关闭
mask.addEventListener('click', function(e) {
if (e.target === mask) {
closeMask();
}
});
// 禁止页面滚动
document.body.style.overflow = 'hidden';
// 调整内容区域最大高度
var contentMaxHeight = window.innerHeight * 0.7;
contentArea.style.maxHeight = contentMaxHeight + 'px';
}
/**
* 关闭遮罩层
*/
function closeMask() {
var mask = document.querySelector('.mask-container');
if (mask) {
mask.parentNode.removeChild(mask);
}
// 恢复页面滚动
document.body.style.overflow = '';
}
/**
* 输入提示功能
* @param {string} inputId - 输入框ID
* @param {string} url - 获取提示数据的URL
* @param {Object} params - 附加参数
* @param {Function} callback - 选择项后的回调函数
*/
function searchSuggest(inputId, url, params, callback) {
var input = document.getElementById(inputId);
if (!input) return;
// 创建提示容器
var suggestContainer = document.createElement('div');
suggestContainer.className = 'suggest-container';
suggestContainer.style.display = 'none';
input.parentNode.appendChild(suggestContainer);
// 定位提示容器
function positionSuggest() {
var rect = input.getBoundingClientRect();
suggestContainer.style.top = (rect.bottom + window.scrollY) + 'px';
suggestContainer.style.left = (rect.left + window.scrollX) + 'px';
suggestContainer.style.width = rect.width + 'px';
}
// 暂存上一次请求的关键词
var lastKeyword = '';
var debounceTimer = null;
// 处理输入事件
input.addEventListener('input', function() {
var keyword = input.value.trim();
// 清除上一次的定时器
if (debounceTimer) {
clearTimeout(debounceTimer);
}
// 如果关键词为空,隐藏提示
if (!keyword) {
suggestContainer.style.display = 'none';
return;
}
// 如果关键词与上一次相同,不重新请求
if (keyword === lastKeyword) {
return;
}
// 设置300ms的防抖定时器
debounceTimer = setTimeout(function() {
lastKeyword = keyword;
// 准备请求参数
var requestParams = Object.assign({}, params || {}, {
keyword: keyword
});
// 发送请求获取提示数据
ajaxRequest(url, requestParams, function(response) {
if (response.code === 0 && Array.isArray(response.data) && response.data.length > 0) {
// 清空容器
suggestContainer.innerHTML = '';
// 添加关闭按钮
var closeButton = document.createElement('div');
closeButton.className = 'suggest-close';
closeButton.textContent = '关闭';
closeButton.addEventListener('click', function() {
suggestContainer.style.display = 'none';
});
suggestContainer.appendChild(closeButton);
// 限制最多显示10条
var maxItems = Math.min(10, response.data.length);
// 添加提示项
for (var i = 0; i < maxItems; i++) {
var item = response.data[i];
var suggestionItem = document.createElement('div');
suggestionItem.className = 'suggest-item';
suggestionItem.textContent = item.text || item.name || item;
suggestionItem.setAttribute('data-value', item.value || item.id || item);
// 绑定点击事件
suggestionItem.addEventListener('click', function() {
var value = this.getAttribute('data-value');
var text = this.textContent;
// 设置输入框值
input.value = text;
// 隐藏提示
suggestContainer.style.display = 'none';
// 触发回调
if (callback && typeof callback === 'function') {
callback(value, text);
}
});
suggestContainer.appendChild(suggestionItem);
}
// 显示提示容器
positionSuggest();
suggestContainer.style.display = 'block';
} else {
suggestContainer.style.display = 'none';
}
}, function() {
suggestContainer.style.display = 'none';
});
}, 300);
});
// 点击输入框时,如果有数据则显示提示
input.addEventListener('click', function() {
if (lastKeyword && suggestContainer.childElementCount > 1) {
positionSuggest();
suggestContainer.style.display = 'block';
}
});
// 点击页面其他区域隐藏提示
document.addEventListener('click', function(e) {
if (e.target !== input && !suggestContainer.contains(e.target)) {
suggestContainer.style.display = 'none';
}
});
// 窗口调整大小时重新定位
window.addEventListener('resize', function() {
if (suggestContainer.style.display === 'block') {
positionSuggest();
}
});
}

147
ht/inc/pubs.php Normal file
View File

@@ -0,0 +1,147 @@
<?php
/**
* 公共函数文件
*/
// 引入数据库连接
require_once 'conn.php';
/**
* 输出JSON格式响应
* @param int $code 状态码0表示成功非0表示失败
* @param string $msg 提示信息
* @param array $data 返回数据
*/
function ajaxReturn($code = 0, $msg = '', $data = []) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'code' => $code,
'msg' => $msg,
'data' => $data
]);
exit;
}
/**
* 检查用户登录状态
* @return array|bool 已登录返回用户信息数组未登录返回false
*/
function checkLogin() {
session_start();
if (isset($_SESSION['user']) && !empty($_SESSION['user']['id'])) {
return $_SESSION['user'];
}
return false;
}
/**
* 检查管理员权限
* @return bool 是否具有管理员权限
*/
function checkAdmin() {
$user = checkLogin();
if ($user && $user['irole'] == 1) {
return true;
}
return false;
}
/**
* 输入安全过滤
* @param mixed $data 需要过滤的数据
* @return mixed 过滤后的数据
*/
function safeFilter($data) {
if (is_array($data)) {
foreach ($data as $key => $val) {
$data[$key] = safeFilter($val);
}
} else {
// 去除空格
$data = trim($data);
// 转义特殊字符
$data = htmlspecialchars($data, ENT_QUOTES);
// 防止SQL注入
$data = addslashes($data);
}
return $data;
}
/**
* 系统日志记录
* @param int $userId 用户ID
* @param string $action 操作类型
* @param string $content 操作内容
*/
function writeLog($userId, $action, $content = '') {
$conn = dbConnect();
$table = getTable('logs');
$userId = (int)$userId;
$action = $conn->real_escape_string($action);
$content = $conn->real_escape_string($content);
$ip = $_SERVER['REMOTE_ADDR'];
$time = date('Y-m-d H:i:s');
$sql = "INSERT INTO {$table} (user_id, action, idesc, ip, logtime) VALUES ($userId, '$action', '$content', '$ip', '$time')";
$conn->query($sql);
$conn->close();
}
/**
* 权限检查函数
* @param string $action 操作名称
* @param bool $adminRequired 是否需要管理员权限
* @return bool|array 成功返回用户信息失败返回false
*/
function checkAuth($action, $adminRequired = false) {
$user = checkLogin();
// 未登录
if (!$user) {
ajaxReturn(403, '请先登录后再操作');
return false;
}
// 需要管理员权限但不是管理员
if ($adminRequired && $user['irole'] != 1) {
ajaxReturn(403, '权限不足,需要管理员权限');
return false;
}
return $user;
}
/**
* 获取分页参数
* @param int $total 总记录数
* @param int $page 当前页码
* @param int $pageSize 每页记录数
* @return array 分页信息
*/
function getPagination($total, $page = 1, $pageSize = 10) {
$page = max(1, intval($page));
$pageSize = max(1, intval($pageSize));
$totalPage = ceil($total / $pageSize);
$totalPage = max(1, $totalPage);
$page = min($page, $totalPage);
$offset = ($page - 1) * $pageSize;
return [
'total' => $total,
'page' => $page,
'pageSize' => $pageSize,
'totalPage' => $totalPage,
'offset' => $offset
];
}
/**
* 获取当前时间
* @return string 格式化的时间字符串
*/
function getCurrentTime() {
return date('Y-m-d H:i:s');
}

281
ht/inc/sqls.php Normal file
View File

@@ -0,0 +1,281 @@
<?php
/**
* 数据库操作类
*/
require_once 'conn.php';
class DB {
private $conn;
private $prefix;
/**
* 构造函数,初始化数据库连接
*/
public function __construct() {
global $CONFIG;
$this->conn = dbConnect();
$this->prefix = $CONFIG['db']['prefix'];
}
/**
* 析构函数,关闭数据库连接
*/
public function __destruct() {
if ($this->conn) {
$this->conn->close();
}
}
/**
* 转义字符串
* @param string $str 需要转义的字符串
* @return string 转义后的字符串
*/
public function escape($str) {
return $this->conn->real_escape_string($str);
}
/**
* 获取带前缀的表名
* @param string $table 表名
* @return string 带前缀的表名
*/
public function table($table) {
return $this->prefix . $table;
}
/**
* 执行SQL查询
* @param string $sql SQL语句
* @return mysqli_result|bool 查询结果
*/
public function query($sql) {
return $this->conn->query($sql);
}
/**
* 获取一条记录
* @param string $table 表名(不带前缀)
* @param string|array $where 查询条件
* @param string $fields 返回字段
* @return array|null 查询结果
*/
public function getOne($table, $where = '', $fields = '*') {
$table = $this->table($table);
$whereStr = $this->parseWhere($where);
$sql = "SELECT {$fields} FROM {$table} {$whereStr} LIMIT 1";
$result = $this->query($sql);
if ($result && $result->num_rows > 0) {
return $result->fetch_assoc();
}
return null;
}
/**
* 获取多条记录
* @param string $table 表名(不带前缀)
* @param string|array $where 查询条件
* @param string $fields 返回字段
* @param string $order 排序方式
* @param string $limit 限制条数
* @return array 查询结果
*/
public function getAll($table, $where = '', $fields = '*', $order = '', $limit = '') {
$table = $this->table($table);
$whereStr = $this->parseWhere($where);
$orderStr = $order ? "ORDER BY {$order}" : '';
$limitStr = $limit ? "LIMIT {$limit}" : '';
$sql = "SELECT {$fields} FROM {$table} {$whereStr} {$orderStr} {$limitStr}";
$result = $this->query($sql);
$rows = [];
if ($result) {
while ($row = $result->fetch_assoc()) {
$rows[] = $row;
}
}
return $rows;
}
/**
* 插入数据
* @param string $table 表名(不带前缀)
* @param array $data 数据数组
* @return int|bool 成功返回插入ID失败返回false
*/
public function insert($table, $data) {
$table = $this->table($table);
$fields = [];
$values = [];
foreach ($data as $key => $value) {
$fields[] = "`{$key}`";
if ($value === null) {
$values[] = "NULL";
} else {
$values[] = "'" . $this->escape($value) . "'";
}
}
$fieldsStr = implode(', ', $fields);
$valuesStr = implode(', ', $values);
$sql = "INSERT INTO {$table} ({$fieldsStr}) VALUES ({$valuesStr})";
$result = $this->query($sql);
if ($result) {
return $this->conn->insert_id;
}
return false;
}
/**
* 更新数据
* @param string $table 表名(不带前缀)
* @param array $data 数据数组
* @param string|array $where 更新条件
* @return bool 更新结果
*/
public function update($table, $data, $where) {
$table = $this->table($table);
$whereStr = $this->parseWhere($where);
$set = [];
foreach ($data as $key => $value) {
if ($value === null) {
$set[] = "`{$key}` = NULL";
} else {
$set[] = "`{$key}` = '" . $this->escape($value) . "'";
}
}
$setStr = implode(', ', $set);
$sql = "UPDATE {$table} SET {$setStr} {$whereStr}";
$result = $this->query($sql);
return $result !== false;
}
/**
* 删除数据
* @param string $table 表名(不带前缀)
* @param string|array $where 删除条件
* @return bool 删除结果
*/
public function delete($table, $where) {
$table = $this->table($table);
$whereStr = $this->parseWhere($where);
if (empty($whereStr)) {
return false; // 防止误删除全表
}
$sql = "DELETE FROM {$table} {$whereStr}";
$result = $this->query($sql);
return $result !== false;
}
/**
* 获取记录数量
* @param string $table 表名(不带前缀)
* @param string|array $where 查询条件
* @return int 记录数量
*/
public function count($table, $where = '') {
$table = $this->table($table);
$whereStr = $this->parseWhere($where);
$sql = "SELECT COUNT(*) AS count FROM {$table} {$whereStr}";
$result = $this->query($sql);
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
return (int) $row['count'];
}
return 0;
}
/**
* 开始事务
*/
public function startTransaction() {
$this->conn->autocommit(false);
}
/**
* 提交事务
*/
public function commit() {
$this->conn->commit();
$this->conn->autocommit(true);
}
/**
* 回滚事务
*/
public function rollback() {
$this->conn->rollback();
$this->conn->autocommit(true);
}
/**
* 解析查询条件
* @param string|array $where 查询条件
* @return string 解析后的WHERE子句
*/
private function parseWhere($where) {
if (empty($where)) {
return '';
}
// 如果是字符串,直接返回
if (is_string($where)) {
return "WHERE {$where}";
}
// 如果是数组,解析为查询条件
if (is_array($where)) {
$conditions = [];
foreach ($where as $key => $value) {
if ($value === null) {
$conditions[] = "`{$key}` IS NULL";
} else {
$conditions[] = "`{$key}` = '" . $this->escape($value) . "'";
}
}
return "WHERE " . implode(' AND ', $conditions);
}
return '';
}
/**
* 获取最后的错误信息
* @return string 错误信息
*/
public function getError() {
return $this->conn->error;
}
/**
* 获取最后执行的SQL影响行数
* @return int 影响行数
*/
public function affectedRows() {
return $this->conn->affected_rows;
}
}