781 lines
25 KiB
PHP
781 lines
25 KiB
PHP
<?php
|
||
/**
|
||
* 用户卡片管理页面
|
||
*/
|
||
// 注意:在检查POST请求时,我们需要尽早处理并退出,避免输出其他内容
|
||
|
||
// 首先检查是否为POST请求
|
||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
|
||
// 包含必要的文件但不要输出任何内容
|
||
include 'common.php';
|
||
|
||
// 检查权限
|
||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||
$user = Typecho_Widget::widget('Widget_User');
|
||
if (!$user->hasLogin() || !$user->pass('administrator', true)) {
|
||
header('Content-Type: application/json');
|
||
echo json_encode(['success' => false, 'message' => '无权限操作']);
|
||
exit;
|
||
}
|
||
|
||
// 设置响应头为JSON
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
|
||
if ($_POST['action'] == 'save_rss' && isset($_POST['uid'], $_POST['user_feed'])) {
|
||
$uid = intval($_POST['uid']);
|
||
$user_feed = trim($_POST['user_feed']);
|
||
$user_url = isset($_POST['user_url']) ? trim($_POST['user_url']) : '';
|
||
|
||
try {
|
||
$db = Typecho_Db::get();
|
||
$db->query($db->update('table.users')->rows(array(
|
||
'user_feed' => $user_feed,
|
||
'url' => $user_url
|
||
))->where('uid = ?', $uid));
|
||
|
||
// 清除该用户的RSS缓存
|
||
$cacheKey = 'usercard_rss_' . md5($user_feed);
|
||
$cacheFile = dirname(__FILE__) . '/cache/' . $cacheKey . '.json';
|
||
if (file_exists($cacheFile)) {
|
||
@unlink($cacheFile);
|
||
}
|
||
|
||
echo json_encode(['success' => true, 'message' => '用户信息已保存!']);
|
||
exit;
|
||
|
||
} catch (Exception $e) {
|
||
echo json_encode(['success' => false, 'message' => '保存失败:' . $e->getMessage()]);
|
||
exit;
|
||
}
|
||
|
||
} elseif ($_POST['action'] == 'batch_save') {
|
||
// 批量保存
|
||
try {
|
||
$db = Typecho_Db::get();
|
||
foreach ($_POST['user_feed'] as $uid => $user_feed) {
|
||
$uid = intval($uid);
|
||
$user_feed = trim($user_feed);
|
||
$user_url = isset($_POST['user_url'][$uid]) ? trim($_POST['user_url'][$uid]) : '';
|
||
|
||
$db->query($db->update('table.users')->rows(array(
|
||
'user_feed' => $user_feed,
|
||
'url' => $user_url
|
||
))->where('uid = ?', $uid));
|
||
|
||
// 清除该用户的RSS缓存
|
||
if (!empty($user_feed)) {
|
||
$cacheKey = 'usercard_rss_' . md5($user_feed);
|
||
$cacheFile = dirname(__FILE__) . '/cache/' . $cacheKey . '.json';
|
||
if (file_exists($cacheFile)) {
|
||
@unlink($cacheFile);
|
||
}
|
||
}
|
||
}
|
||
|
||
echo json_encode(['success' => true, 'message' => '批量保存成功!']);
|
||
exit;
|
||
|
||
} catch (Exception $e) {
|
||
echo json_encode(['success' => false, 'message' => '批量保存失败:' . $e->getMessage()]);
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 如果不是上述两种action,返回错误
|
||
echo json_encode(['success' => false, 'message' => '无效的操作']);
|
||
exit;
|
||
}
|
||
|
||
// 正常页面加载(非AJAX请求)
|
||
include 'common.php';
|
||
include 'header.php';
|
||
include 'menu.php';
|
||
|
||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||
|
||
// 检查权限
|
||
$user = Typecho_Widget::widget('Widget_User');
|
||
if (!$user->hasLogin() || !$user->pass('administrator', true)) {
|
||
exit;
|
||
}
|
||
|
||
// 获取所有用户
|
||
$db = Typecho_Db::get();
|
||
$users = $db->fetchAll($db->select('uid', 'name', 'screenName', 'url', 'user_feed')
|
||
->from('table.users')
|
||
->order('uid', Typecho_Db::SORT_ASC));
|
||
|
||
// 统计信息
|
||
$totalUsers = count($users);
|
||
$withRss = 0;
|
||
$withoutRss = 0;
|
||
$withWebsite = 0;
|
||
|
||
foreach ($users as $user) {
|
||
if (!empty($user['user_feed'])) {
|
||
$withRss++;
|
||
} else {
|
||
$withoutRss++;
|
||
}
|
||
if (!empty($user['url'])) {
|
||
$withWebsite++;
|
||
}
|
||
}
|
||
|
||
// 获取用户头像首字母函数
|
||
function getUserInitial($name) {
|
||
if (empty($name)) {
|
||
return 'U';
|
||
}
|
||
|
||
// 去除首尾空格
|
||
$name = trim($name);
|
||
|
||
// 如果是中文,获取第一个汉字
|
||
if (preg_match('/^[\x{4e00}-\x{9fa5}]/u', $name)) {
|
||
// 获取第一个字符(支持中文字符)
|
||
return mb_substr($name, 0, 1, 'UTF-8');
|
||
} else {
|
||
// 非中文,获取第一个字母或数字
|
||
$firstChar = substr($name, 0, 1);
|
||
|
||
// 如果是字母,转换为大写
|
||
if (ctype_alpha($firstChar)) {
|
||
return strtoupper($firstChar);
|
||
}
|
||
|
||
// 如果是数字,直接返回
|
||
if (ctype_digit($firstChar)) {
|
||
return $firstChar;
|
||
}
|
||
|
||
// 其他字符,返回第一个字符的大写形式
|
||
return strtoupper($firstChar);
|
||
}
|
||
}
|
||
?>
|
||
|
||
<div class="main">
|
||
<div class="body container">
|
||
<!-- 弹窗容器 -->
|
||
<div id="toast-container" style="position: fixed; top: 20px; right: 20px; z-index: 9999;"></div>
|
||
|
||
<style>
|
||
/* 弹窗样式 */
|
||
.toast {
|
||
background: #43e97b;
|
||
color: white;
|
||
padding: 12px 24px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(67, 233, 123, 0.3);
|
||
margin-bottom: 10px;
|
||
animation: slideInRight 0.3s ease, fadeOut 0.3s ease 2.7s forwards;
|
||
max-width: 300px;
|
||
word-wrap: break-word;
|
||
}
|
||
|
||
@keyframes slideInRight {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(100%);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
|
||
@keyframes fadeOut {
|
||
from {
|
||
opacity: 1;
|
||
}
|
||
to {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.user-mgmt-section {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||
}
|
||
|
||
.user-mgmt-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin-bottom: 15px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid #eee;
|
||
color: #333;
|
||
}
|
||
|
||
.user-stats-cards {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||
gap: 15px;
|
||
}
|
||
|
||
.user-stat-card {
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
padding: 20px 15px;
|
||
text-align: center;
|
||
transition: all 0.3s ease;
|
||
border: 1px solid #e9ecef;
|
||
}
|
||
|
||
.user-stat-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.user-stat-value {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
margin: 0 0 10px 0;
|
||
}
|
||
|
||
.user-stat-label {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin: 0;
|
||
}
|
||
|
||
.user-stat-card.total .user-stat-value { color: #667eea; }
|
||
.user-stat-card.with-rss .user-stat-value { color: #43e97b; }
|
||
.user-stat-card.without-rss .user-stat-value { color: #fa709a; }
|
||
.user-stat-card.with-website .user-stat-value { color: #4facfe; }
|
||
|
||
.user-table-container {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.user-mgmt-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
background: #fff;
|
||
}
|
||
|
||
.user-mgmt-table thead {
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.user-mgmt-table th {
|
||
padding: 15px;
|
||
text-align: left;
|
||
font-weight: 600;
|
||
color: #333;
|
||
border-bottom: 2px solid #e9ecef;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.user-mgmt-table td {
|
||
padding: 15px;
|
||
border-bottom: 1px solid #e9ecef;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.user-mgmt-table tbody tr:hover {
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.user-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.user-avatar {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
font-weight: bold;
|
||
font-size: 16px;
|
||
flex-shrink: 0;
|
||
font-family: "Microsoft YaHei", "Segoe UI", Arial, sans-serif;
|
||
}
|
||
|
||
.user-details {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.user-name {
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin: 0 0 3px 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.user-id {
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin: 0;
|
||
}
|
||
|
||
.website-input-container,
|
||
.rss-input-container {
|
||
position: relative;
|
||
}
|
||
|
||
.url-input,
|
||
.rss-input {
|
||
width: 100%;
|
||
padding: 10px 12px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
transition: all 0.2s ease;
|
||
background: #fff;
|
||
}
|
||
|
||
.url-input:focus,
|
||
.rss-input:focus {
|
||
border-color: #667eea;
|
||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||
outline: none;
|
||
}
|
||
|
||
.url-input.has-value,
|
||
.rss-input.has-value {
|
||
border-color: #43e97b;
|
||
background: #f8fff9;
|
||
}
|
||
|
||
.url-input.empty,
|
||
.rss-input.empty {
|
||
border-color: #ddd;
|
||
background: #fff;
|
||
}
|
||
|
||
.input-status {
|
||
position: absolute;
|
||
right: 10px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: #ddd;
|
||
}
|
||
|
||
.input-status.has-value {
|
||
background: #43e97b;
|
||
}
|
||
|
||
.input-status.empty {
|
||
background: #ddd;
|
||
}
|
||
|
||
.url-input::placeholder,
|
||
.rss-input::placeholder {
|
||
color: #999;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.form-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 15px;
|
||
margin: 30px 0 0 0;
|
||
padding: 20px;
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.btn {
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.btn-save {
|
||
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||
color: white;
|
||
}
|
||
|
||
.btn-save:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(67, 233, 123, 0.3);
|
||
}
|
||
|
||
.btn-reset {
|
||
background: linear-gradient(135deg, #ff6b6b 0%, #ffa8a8 100%);
|
||
color: white;
|
||
}
|
||
|
||
.btn-reset:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
|
||
}
|
||
|
||
.btn-refresh {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
}
|
||
|
||
.btn-refresh:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.message {
|
||
padding: 15px 20px;
|
||
margin: 20px 0;
|
||
border-radius: 8px;
|
||
border: 1px solid;
|
||
animation: slideDown 0.3s ease;
|
||
}
|
||
|
||
.message.success {
|
||
background: #f0fff4;
|
||
border-color: #c6f6d5;
|
||
color: #22543d;
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.user-stats-cards {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.user-mgmt-table {
|
||
display: block;
|
||
}
|
||
|
||
.form-actions {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.btn {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<div class="user-mgmt-section">
|
||
<div class="user-stats-cards">
|
||
<div class="user-stat-card total">
|
||
<div class="user-stat-value"><?php echo $totalUsers; ?></div>
|
||
<div class="user-stat-label">总用户数</div>
|
||
</div>
|
||
<div class="user-stat-card with-website">
|
||
<div class="user-stat-value"><?php echo $withWebsite; ?></div>
|
||
<div class="user-stat-label">有网站地址</div>
|
||
</div>
|
||
<div class="user-stat-card with-rss">
|
||
<div class="user-stat-value"><?php echo $withRss; ?></div>
|
||
<div class="user-stat-label">有RSS地址</div>
|
||
</div>
|
||
<div class="user-stat-card without-rss">
|
||
<div class="user-stat-value"><?php echo $withoutRss; ?></div>
|
||
<div class="user-stat-label">无RSS地址</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<form method="post" action="" id="batch-form">
|
||
<input type="hidden" name="action" value="batch_save">
|
||
|
||
<div class="user-mgmt-section">
|
||
<!--<div class="user-mgmt-title">RSS及网站地址管理</div>-->
|
||
|
||
<div class="user-table-container">
|
||
<table class="user-mgmt-table">
|
||
<thead>
|
||
<tr>
|
||
<th width="20%">用户信息</th>
|
||
<th width="15%">用户名</th>
|
||
<th width="30%">网站地址</th>
|
||
<th width="35%">RSS地址</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($users as $user): ?>
|
||
<?php
|
||
$displayName = !empty($user['screenName']) ? $user['screenName'] : $user['name'];
|
||
$userUrl = !empty($user['url']) ? $user['url'] : '';
|
||
$hasWebsite = !empty($user['url']);
|
||
$hasRss = !empty($user['user_feed']);
|
||
$userInitial = getUserInitial($displayName);
|
||
?>
|
||
<tr>
|
||
<td>
|
||
<div class="user-info">
|
||
<div class="user-avatar">
|
||
<?php echo htmlspecialchars($userInitial); ?>
|
||
</div>
|
||
<div class="user-details">
|
||
<div class="user-name"><?php echo htmlspecialchars($displayName); ?></div>
|
||
<div class="user-id">UID: <?php echo $user['uid']; ?></div>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<div class="user-name"><?php echo htmlspecialchars($user['name']); ?></div>
|
||
</td>
|
||
<td>
|
||
<div class="website-input-container">
|
||
<input type="text"
|
||
name="user_url[<?php echo $user['uid']; ?>]"
|
||
value="<?php echo htmlspecialchars($userUrl); ?>"
|
||
class="url-input <?php echo $hasWebsite ? 'has-value' : 'empty'; ?>"
|
||
placeholder="https://example.com/"
|
||
data-uid="<?php echo $user['uid']; ?>"
|
||
data-type="url">
|
||
<div class="input-status <?php echo $hasWebsite ? 'has-value' : 'empty'; ?>"></div>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<div class="rss-input-container">
|
||
<input type="text"
|
||
name="user_feed[<?php echo $user['uid']; ?>]"
|
||
value="<?php echo htmlspecialchars($user['user_feed']); ?>"
|
||
class="rss-input <?php echo $hasRss ? 'has-value' : 'empty'; ?>"
|
||
placeholder="https://example.com/feed/"
|
||
data-uid="<?php echo $user['uid']; ?>"
|
||
data-type="rss">
|
||
<div class="input-status <?php echo $hasRss ? 'has-value' : 'empty'; ?>"></div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="button" class="btn btn-save" onclick="saveBatch()">
|
||
批量保存
|
||
</button>
|
||
<button type="button" class="btn btn-refresh" onclick="window.location.reload()">
|
||
刷新页面
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 显示弹窗
|
||
function showToast(message, type = 'success') {
|
||
const container = document.getElementById('toast-container');
|
||
|
||
const toast = document.createElement('div');
|
||
toast.className = 'toast';
|
||
toast.textContent = message;
|
||
|
||
// 设置不同颜色的弹窗
|
||
if (type === 'error') {
|
||
toast.style.background = 'linear-gradient(135deg, #ff6b6b 0%, #ffa8a8 100%)';
|
||
toast.style.boxShadow = '0 4px 12px rgba(255, 107, 107, 0.3)';
|
||
} else if (type === 'info') {
|
||
toast.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
||
toast.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.3)';
|
||
}
|
||
|
||
container.appendChild(toast);
|
||
|
||
// 3秒后移除弹窗
|
||
setTimeout(() => {
|
||
if (toast.parentNode === container) {
|
||
toast.style.animation = 'fadeOut 0.3s ease forwards';
|
||
setTimeout(() => {
|
||
if (toast.parentNode === container) {
|
||
container.removeChild(toast);
|
||
}
|
||
}, 300);
|
||
}
|
||
}, 2500);
|
||
}
|
||
|
||
// 批量保存
|
||
function saveBatch() {
|
||
const form = document.getElementById('batch-form');
|
||
const formData = new FormData(form);
|
||
|
||
// 检查是否有数据
|
||
let hasData = false;
|
||
const urlInputs = document.querySelectorAll('.url-input');
|
||
const rssInputs = document.querySelectorAll('.rss-input');
|
||
|
||
urlInputs.forEach(input => {
|
||
if (input.value.trim()) hasData = true;
|
||
});
|
||
|
||
rssInputs.forEach(input => {
|
||
if (input.value.trim()) hasData = true;
|
||
});
|
||
|
||
if (!hasData) {
|
||
if (confirm('当前没有设置任何网站地址和RSS地址,确定要继续吗?')) {
|
||
submitBatch(formData);
|
||
}
|
||
} else {
|
||
if (confirm('确定要保存所有用户的网站地址和RSS地址吗?')) {
|
||
submitBatch(formData);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 提交批量保存
|
||
function submitBatch(formData) {
|
||
fetch('', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
// 检查响应状态
|
||
if (!response.ok) {
|
||
throw new Error('网络响应不正常: ' + response.status);
|
||
}
|
||
return response.text(); // 先获取文本
|
||
})
|
||
.then(text => {
|
||
try {
|
||
// 尝试解析为JSON
|
||
const data = JSON.parse(text);
|
||
if (data.success) {
|
||
showToast(data.message);
|
||
|
||
// 更新输入框状态
|
||
updateInputStatus();
|
||
} else {
|
||
showToast(data.message || '保存失败,请重试!', 'error');
|
||
}
|
||
} catch (e) {
|
||
// 如果不是JSON,显示原始响应以便调试
|
||
console.error('响应不是有效的JSON:', text);
|
||
showToast('服务器响应异常: ' + text.substring(0, 100), 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showToast('网络错误: ' + error.message, 'error');
|
||
});
|
||
}
|
||
|
||
// 更新输入框状态
|
||
function updateInputStatus() {
|
||
document.querySelectorAll('.url-input, .rss-input').forEach(function(input) {
|
||
const inputStatus = input.parentNode.querySelector('.input-status');
|
||
if (input.value.trim()) {
|
||
input.classList.remove('empty');
|
||
input.classList.add('has-value');
|
||
inputStatus.classList.remove('empty');
|
||
inputStatus.classList.add('has-value');
|
||
} else {
|
||
input.classList.remove('has-value');
|
||
input.classList.add('empty');
|
||
inputStatus.classList.remove('has-value');
|
||
inputStatus.classList.add('empty');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 单个保存功能(按Enter键保存)
|
||
document.querySelectorAll('.url-input, .rss-input').forEach(function(input) {
|
||
input.addEventListener('keypress', function(e) {
|
||
if (e.key === 'Enter') {
|
||
e.preventDefault();
|
||
const uid = this.getAttribute('data-uid');
|
||
const type = this.getAttribute('data-type');
|
||
const value = this.value.trim();
|
||
|
||
const formData = new FormData();
|
||
formData.append('action', 'save_rss');
|
||
formData.append('uid', uid);
|
||
formData.append('user_feed', type === 'rss' ? value : '');
|
||
if (type === 'url') {
|
||
formData.append('user_url', value);
|
||
}
|
||
|
||
fetch('', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error('网络响应不正常: ' + response.status);
|
||
}
|
||
return response.text();
|
||
})
|
||
.then(text => {
|
||
try {
|
||
const data = JSON.parse(text);
|
||
if (data.success) {
|
||
showToast(data.message);
|
||
|
||
// 更新输入框状态
|
||
const inputStatus = input.parentNode.querySelector('.input-status');
|
||
if (value) {
|
||
input.classList.remove('empty');
|
||
input.classList.add('has-value');
|
||
inputStatus.classList.remove('empty');
|
||
inputStatus.classList.add('has-value');
|
||
} else {
|
||
input.classList.remove('has-value');
|
||
input.classList.add('empty');
|
||
inputStatus.classList.remove('has-value');
|
||
inputStatus.classList.add('empty');
|
||
}
|
||
} else {
|
||
showToast(data.message || '保存失败,请重试!', 'error');
|
||
}
|
||
} catch (e) {
|
||
console.error('响应不是有效的JSON:', text);
|
||
showToast('服务器响应异常: ' + text.substring(0, 100), 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showToast('网络错误: ' + error.message, 'error');
|
||
});
|
||
}
|
||
});
|
||
|
||
// 输入时实时更新状态
|
||
input.addEventListener('input', function() {
|
||
const inputStatus = this.parentNode.querySelector('.input-status');
|
||
if (this.value.trim()) {
|
||
this.classList.remove('empty');
|
||
this.classList.add('has-value');
|
||
inputStatus.classList.remove('empty');
|
||
inputStatus.classList.add('has-value');
|
||
} else {
|
||
this.classList.remove('has-value');
|
||
this.classList.add('empty');
|
||
inputStatus.classList.remove('has-value');
|
||
inputStatus.classList.add('empty');
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<?php
|
||
include 'copyright.php';
|
||
include 'common-js.php';
|
||
include 'footer.php';
|
||
?>
|