843 lines
34 KiB
HTML
843 lines
34 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>微信联系人祝福管理</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
.container-main {
|
|
background: white;
|
|
border-radius: 15px;
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
|
padding: 30px;
|
|
}
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
padding-bottom: 20px;
|
|
border-bottom: 2px solid #eee;
|
|
}
|
|
.stats-bar {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 30px;
|
|
margin-bottom: 20px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.stat-item {
|
|
text-align: center;
|
|
padding: 15px 25px;
|
|
background: #f8f9fa;
|
|
border-radius: 10px;
|
|
}
|
|
.stat-number {
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
color: #667eea;
|
|
}
|
|
.search-bar {
|
|
margin-bottom: 20px;
|
|
}
|
|
.filter-bar {
|
|
margin-bottom: 20px;
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.filter-btn {
|
|
padding: 6px 16px;
|
|
border-radius: 20px;
|
|
font-size: 13px;
|
|
}
|
|
.contact-table {
|
|
font-size: 14px;
|
|
}
|
|
.contact-table th {
|
|
background: #f8f9fa;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
}
|
|
.contact-name {
|
|
cursor: pointer;
|
|
color: #0d6efd;
|
|
font-weight: 500;
|
|
}
|
|
.contact-name:hover {
|
|
text-decoration: underline;
|
|
}
|
|
.category-badge {
|
|
cursor: pointer;
|
|
padding: 2px 8px;
|
|
border-radius: 12px;
|
|
font-size: 11px;
|
|
display: inline-block;
|
|
background: #e9ecef;
|
|
color: #6c757d;
|
|
border: 1px solid transparent;
|
|
transition: all 0.15s;
|
|
margin: 2px;
|
|
}
|
|
.category-badge:hover {
|
|
transform: scale(1.05);
|
|
}
|
|
.category-badge.active {
|
|
color: #fff;
|
|
font-weight: 500;
|
|
}
|
|
.category-badge.同事.active { background: #0dcaf0; border-color: #0dcaf0; }
|
|
.category-badge.好友.active { background: #198754; border-color: #198754; }
|
|
.category-badge.同学.active { background: #17a2b8; border-color: #17a2b8; }
|
|
.category-badge.老师.active { background: #ffc107; border-color: #ffc107; color: #212529; }
|
|
.category-badge.亲戚.active { background: #dc3545; border-color: #dc3545; }
|
|
.category-badge.客户.active { background: #6f42c1; border-color: #6f42c1; }
|
|
.category-badge.供应商.active { background: #20c997; border-color: #20c997; }
|
|
.category-badge.其他.active { background: #fd7e14; border-color: #fd7e14; }
|
|
.category-tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 2px;
|
|
}
|
|
.send-badge {
|
|
cursor: pointer;
|
|
padding: 4px 12px;
|
|
border-radius: 15px;
|
|
font-size: 12px;
|
|
display: inline-block;
|
|
transition: all 0.15s;
|
|
border: 2px solid #dee2e6;
|
|
background: #fff;
|
|
color: #6c757d;
|
|
}
|
|
.send-badge:hover {
|
|
transform: scale(1.05);
|
|
}
|
|
.send-badge.active {
|
|
background: #198754;
|
|
border-color: #198754;
|
|
color: #fff;
|
|
font-weight: 500;
|
|
}
|
|
.blessing-text {
|
|
cursor: pointer;
|
|
max-width: 300px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
display: inline-block;
|
|
color: #198754;
|
|
}
|
|
.blessing-text:hover {
|
|
text-decoration: underline;
|
|
}
|
|
.pagination-container {
|
|
margin-top: 20px;
|
|
justify-content: center;
|
|
}
|
|
.loading {
|
|
text-align: center;
|
|
padding: 40px;
|
|
color: #666;
|
|
}
|
|
.toast-container {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 9999;
|
|
}
|
|
/* Modal styles */
|
|
.modal-body input, .modal-body textarea, .modal-body select {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 5px;
|
|
font-size: 14px;
|
|
}
|
|
.modal-body textarea {
|
|
resize: vertical;
|
|
min-height: 80px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-main">
|
|
<div class="header">
|
|
<h1><i class="bi bi-wechat"></i> 微信联系人祝福管理</h1>
|
|
<p class="subtitle">点击姓名/分类/祝福语可编辑,勾选发送选择框</p>
|
|
</div>
|
|
|
|
<!-- 统计栏 -->
|
|
<div class="stats-bar">
|
|
<div class="stat-item">
|
|
<div class="stat-number" id="totalCount">0</div>
|
|
<div>总联系人</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-number" id="selectedCount">0</div>
|
|
<div>已选择</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 新增联系人表单 -->
|
|
<div class="add-contact-form mb-4">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><i class="bi bi-person-plus"></i> 新增联系人</span>
|
|
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#addContactCollapse">
|
|
<i class="bi bi-chevron-down"></i>
|
|
</button>
|
|
</div>
|
|
<div class="collapse show" id="addContactCollapse">
|
|
<div class="card-body">
|
|
<div class="row g-2 align-items-end">
|
|
<div class="col-md-4">
|
|
<label class="form-label">姓名 <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" id="newNameInput" placeholder="联系人姓名">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">搜索姓名</label>
|
|
<input type="text" class="form-control" id="newSearchNameInput" placeholder="用于搜索的简化姓名(可选)">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">分类</label>
|
|
<select class="form-select" id="newCategorySelect">
|
|
<option value="">未分类</option>
|
|
<option value="同事">同事</option>
|
|
<option value="好友">好友</option>
|
|
<option value="同学">同学</option>
|
|
<option value="老师">老师</option>
|
|
<option value="亲戚">亲戚</option>
|
|
<option value="客户">客户</option>
|
|
<option value="供应商">供应商</option>
|
|
<option value="其他">其他</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button class="btn btn-primary w-100" onclick="addContact()">
|
|
<i class="bi bi-plus-lg"></i> 添加
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 搜索栏 -->
|
|
<div class="search-bar">
|
|
<div class="input-group">
|
|
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
|
<input type="text" class="form-control" id="searchInput" placeholder="搜索联系人...">
|
|
<button class="btn btn-primary" onclick="loadContacts()">搜索</button>
|
|
<select class="form-select" style="max-width: 120px;" id="pageSizeSelect" onchange="changePageSize()">
|
|
<option value="20">20条/页</option>
|
|
<option value="50">50条/页</option>
|
|
<option value="100" selected>100条/页</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 分类筛选 -->
|
|
<div class="filter-bar">
|
|
<button class="btn btn-outline-primary filter-btn active" data-category="" onclick="setFilter('')">全部</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="同事" onclick="setFilter('同事')">同事</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="好友" onclick="setFilter('好友')">好友</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="同学" onclick="setFilter('同学')">同学</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="老师" onclick="setFilter('老师')">老师</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="亲戚" onclick="setFilter('亲戚')">亲戚</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="客户" onclick="setFilter('客户')">客户</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="供应商" onclick="setFilter('供应商')">供应商</button>
|
|
<button class="btn btn-outline-primary filter-btn" data-category="其他" onclick="setFilter('其他')">其他</button>
|
|
<button class="btn btn-danger ms-3" id="batchDeleteBtn" onclick="batchDelete()" style="display:none;">
|
|
<i class="bi bi-trash"></i> 批量删除 (<span id="selectedCount2">0</span>)
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 表格 -->
|
|
<div class="table-responsive">
|
|
<table class="table table-hover contact-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 50px;"><input type="checkbox" id="selectAll" onchange="toggleSelectAll()"></th>
|
|
<th style="width: 50px;">ID</th>
|
|
<th style="min-width: 150px;">姓名</th>
|
|
<th style="min-width: 120px;">搜索姓名</th>
|
|
<th style="min-width: 100px;">分类</th>
|
|
<th style="min-width: 100px;">自定义内容</th>
|
|
<th>祝福语</th>
|
|
<th style="width: 60px;">发送</th>
|
|
<th style="width: 50px;">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="contactTable">
|
|
<tr>
|
|
<td colspan="9" class="loading">加载中...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
<nav>
|
|
<ul class="pagination pagination-container" id="pagination"></ul>
|
|
</nav>
|
|
</div>
|
|
|
|
<!-- 编辑姓名 Modal -->
|
|
<div class="modal fade" id="nameModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">编辑姓名</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="editNameId">
|
|
<div class="mb-3">
|
|
<label class="form-label">姓名</label>
|
|
<input type="text" id="editNameInput">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">搜索姓名</label>
|
|
<input type="text" id="editSearchNameInput" placeholder="用于搜索的简化姓名">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveName()">保存</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 编辑祝福语 Modal -->
|
|
<div class="modal fade" id="blessingModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">编辑祝福语</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="editBlessingId">
|
|
<div class="mb-3">
|
|
<label class="form-label">祝福语</label>
|
|
<textarea id="editBlessingInput" rows="4">马年新春快乐!愿您在新的一年里,事业腾飞,马到成功!</textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveBlessing()">保存</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 编辑自定义内容 Modal -->
|
|
<div class="modal fade" id="customContentModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">编辑自定义内容</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="editCustomContentId">
|
|
<div class="mb-3">
|
|
<label class="form-label">自定义内容</label>
|
|
<input type="text" id="editCustomContentInput" placeholder="输入自定义内容,默认为分类值">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveCustomContent()">保存</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast 提示 -->
|
|
<div class="toast-container">
|
|
<div class="toast" id="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
|
<div class="toast-body" id="toastBody"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
const API_BASE = '/api';
|
|
let currentPage = 1;
|
|
let pageSize = 100;
|
|
let currentFilter = '';
|
|
let contacts = [];
|
|
|
|
// 页面加载
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadContacts();
|
|
updateStats();
|
|
});
|
|
|
|
// 搜索回车事件
|
|
document.getElementById('searchInput').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
loadContacts(1);
|
|
}
|
|
});
|
|
|
|
let totalPages = 1;
|
|
let totalCount = 0;
|
|
|
|
// 加载联系人
|
|
async function loadContacts(page = 1) {
|
|
currentPage = page;
|
|
const search = document.getElementById('searchInput').value;
|
|
|
|
let url = `${API_BASE}/contacts?page=${page}&page_size=${pageSize}`;
|
|
if (currentFilter) url += `&category=${encodeURIComponent(currentFilter)}`;
|
|
if (search) url += `&search=${encodeURIComponent(search)}`;
|
|
|
|
try {
|
|
const response = await fetch(url);
|
|
const data = await response.json();
|
|
contacts = data.contacts;
|
|
totalPages = data.total_pages;
|
|
totalCount = data.total;
|
|
renderTable();
|
|
renderPagination();
|
|
} catch (error) {
|
|
console.error('加载失败:', error);
|
|
showToast('加载失败,请检查后端服务是否运行', 'danger');
|
|
}
|
|
}
|
|
|
|
// 渲染表格
|
|
function renderTable() {
|
|
const tbody = document.getElementById('contactTable');
|
|
const allCategories = ['同事', '好友', '同学', '老师', '亲戚', '客户', '供应商', '其他'];
|
|
|
|
if (contacts.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="9" class="text-center py-4">暂无数据</td></tr>';
|
|
updateBatchDeleteBtn();
|
|
return;
|
|
}
|
|
|
|
tbody.innerHTML = contacts.map(c => {
|
|
// 解析当前分类(支持逗号分隔的多标签)
|
|
const currentCats = (c.category || '').split(',').map(s => s.trim()).filter(s => s);
|
|
|
|
// 生成分类标签
|
|
const catTags = allCategories.map(cat => {
|
|
const isActive = currentCats.includes(cat);
|
|
return `<span class="category-badge ${cat} ${isActive ? 'active' : ''}"
|
|
onclick="toggleCategory(${c.id}, '${cat}')">${cat}</span>`;
|
|
}).join('');
|
|
|
|
// 自定义内容:如果没有则默认显示分类
|
|
const customContent = c.custom_content || c.category || '';
|
|
|
|
// 发送状态
|
|
const isSelected = c.selected;
|
|
const sendBadge = `<span class="send-badge ${isSelected ? 'active' : ''}"
|
|
onclick="toggleSend(${c.id})">${isSelected ? '✓ 发送' : '发送'}</span>`;
|
|
|
|
return `
|
|
<tr>
|
|
<td><input type="checkbox" class="contact-checkbox" data-id="${c.id}" ${c.selected ? 'checked' : ''} onchange="toggleSelect(${c.id}); updateBatchDeleteBtn()"></td>
|
|
<td>${c.id}</td>
|
|
<td><span class="contact-name" onclick="openNameModal(${c.id}, '${escapeHtml(c.name)}', '${escapeHtml(c.search_name || '')}')">${escapeHtml(c.name)}</span></td>
|
|
<td><span class="contact-name" onclick="openNameModal(${c.id}, '${escapeHtml(c.name)}', '${escapeHtml(c.search_name || '')}')">${escapeHtml(c.search_name || '')}</span></td>
|
|
<td><div class="category-tags">${catTags}</div></td>
|
|
<td><span class="contact-name" onclick="openCustomContentModal(${c.id}, '${escapeHtml(customContent)}')">${escapeHtml(customContent)}</span></td>
|
|
<td><span class="blessing-text" onclick="openBlessingModal(${c.id}, '${escapeHtml(c.blessing)}')" title="${escapeHtml(c.blessing)}">${escapeHtml(c.blessing)}</span></td>
|
|
<td>${sendBadge}</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-danger" onclick="deleteContact(${c.id})"><i class="bi bi-trash"></i></button>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}).join('');
|
|
|
|
updateBatchDeleteBtn();
|
|
}
|
|
|
|
// 渲染分页
|
|
function renderPagination() {
|
|
const pagination = document.getElementById('pagination');
|
|
|
|
if (totalPages <= 1) {
|
|
pagination.innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
let html = `
|
|
<li class="page-item ${currentPage <= 1 ? 'disabled' : ''}">
|
|
<a class="page-link" href="javascript:void(0)" onclick="loadContacts(${currentPage - 1})">上一页</a>
|
|
</li>
|
|
`;
|
|
|
|
// 显示页码
|
|
for (let i = Math.max(1, currentPage - 2); i <= Math.min(totalPages, currentPage + 2); i++) {
|
|
html += `
|
|
<li class="page-item ${i === currentPage ? 'active' : ''}">
|
|
<a class="page-link" href="javascript:void(0)" onclick="loadContacts(${i})">${i}</a>
|
|
</li>
|
|
`;
|
|
}
|
|
|
|
html += `
|
|
<li class="page-item ${currentPage >= totalPages ? 'disabled' : ''}">
|
|
<a class="page-link" href="javascript:void(0)" onclick="loadContacts(${currentPage + 1})">下一页</a>
|
|
</li>
|
|
`;
|
|
|
|
pagination.innerHTML = html;
|
|
}
|
|
|
|
// 设置筛选
|
|
function setFilter(category) {
|
|
currentFilter = category;
|
|
document.querySelectorAll('.filter-btn').forEach(btn => {
|
|
btn.classList.remove('active');
|
|
if (btn.dataset.category === category) {
|
|
btn.classList.add('active');
|
|
}
|
|
});
|
|
loadContacts(1);
|
|
}
|
|
|
|
// 改变每页显示数量
|
|
function changePageSize() {
|
|
pageSize = parseInt(document.getElementById('pageSizeSelect').value);
|
|
loadContacts(1);
|
|
}
|
|
|
|
// 新增联系人
|
|
async function addContact() {
|
|
const name = document.getElementById('newNameInput').value.trim();
|
|
const searchName = document.getElementById('newSearchNameInput').value.trim();
|
|
const category = document.getElementById('newCategorySelect').value;
|
|
|
|
if (!name) {
|
|
showToast('请输入联系人姓名', 'warning');
|
|
document.getElementById('newNameInput').focus();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/contacts`, {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({
|
|
name: name,
|
|
search_name: searchName || name,
|
|
category: category,
|
|
blessing: '马年新春快乐!愿您在新的一年里,事业腾飞,马到成功!',
|
|
selected: false
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
// 清空表单
|
|
document.getElementById('newNameInput').value = '';
|
|
document.getElementById('newSearchNameInput').value = '';
|
|
document.getElementById('newCategorySelect').value = '';
|
|
|
|
showToast('添加成功', 'success');
|
|
loadContacts(1);
|
|
updateStats();
|
|
} else {
|
|
showToast('添加失败', 'danger');
|
|
}
|
|
} catch (error) {
|
|
showToast('添加失败,请检查后端服务', 'danger');
|
|
}
|
|
}
|
|
|
|
// 回车快捷添加
|
|
document.getElementById('newNameInput').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
addContact();
|
|
}
|
|
});
|
|
document.getElementById('newSearchNameInput').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
addContact();
|
|
}
|
|
});
|
|
|
|
// 更新统计
|
|
async function updateStats() {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/stats`);
|
|
const stats = await response.json();
|
|
document.getElementById('totalCount').textContent = stats.total;
|
|
document.getElementById('selectedCount').textContent = stats.selected;
|
|
} catch (error) {
|
|
console.error('统计加载失败:', error);
|
|
}
|
|
}
|
|
|
|
// 全选/取消全选
|
|
function toggleSelectAll() {
|
|
const selectAll = document.getElementById('selectAll').checked;
|
|
document.querySelectorAll('.contact-checkbox').forEach(cb => {
|
|
cb.checked = selectAll;
|
|
const id = parseInt(cb.dataset.id);
|
|
updateSelect(id, selectAll);
|
|
});
|
|
updateStats();
|
|
updateBatchDeleteBtn();
|
|
}
|
|
|
|
// 单个选择
|
|
async function toggleSelect(id) {
|
|
const checkbox = document.querySelector(`.contact-checkbox[data-id="${id}"]`);
|
|
await updateSelect(id, checkbox.checked);
|
|
updateStats();
|
|
}
|
|
|
|
// 更新选择状态
|
|
async function updateSelect(id, selected) {
|
|
try {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {
|
|
method: 'PUT',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({selected: selected})
|
|
});
|
|
} catch (error) {
|
|
console.error('更新失败:', error);
|
|
}
|
|
}
|
|
|
|
// 打开编辑姓名Modal
|
|
function openNameModal(id, name, searchName) {
|
|
document.getElementById('editNameId').value = id;
|
|
document.getElementById('editNameInput').value = name;
|
|
document.getElementById('editSearchNameInput').value = searchName || '';
|
|
new bootstrap.Modal(document.getElementById('nameModal')).show();
|
|
}
|
|
|
|
// 保存姓名
|
|
async function saveName() {
|
|
const id = document.getElementById('editNameId').value;
|
|
const name = document.getElementById('editNameInput').value.trim();
|
|
const searchName = document.getElementById('editSearchNameInput').value.trim();
|
|
|
|
if (!name) {
|
|
showToast('姓名不能为空', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {
|
|
method: 'PUT',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({name: name, search_name: searchName})
|
|
});
|
|
bootstrap.Modal.getInstance(document.getElementById('nameModal')).hide();
|
|
showToast('保存成功', 'success');
|
|
loadContacts(currentPage);
|
|
} catch (error) {
|
|
showToast('保存失败', 'danger');
|
|
}
|
|
}
|
|
|
|
// 快速切换分类标签
|
|
async function toggleCategory(id, category) {
|
|
// 找到当前联系人
|
|
const contact = contacts.find(c => c.id === id);
|
|
if (!contact) return;
|
|
|
|
// 解析当前分类
|
|
let currentCats = (contact.category || '').split(',').map(s => s.trim()).filter(s => s);
|
|
|
|
// 切换分类
|
|
const index = currentCats.indexOf(category);
|
|
if (index > -1) {
|
|
currentCats.splice(index, 1); // 移除
|
|
} else {
|
|
currentCats.push(category); // 添加
|
|
}
|
|
|
|
const newCategory = currentCats.join(',');
|
|
|
|
try {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {
|
|
method: 'PUT',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({category: newCategory})
|
|
});
|
|
// 更新本地数据
|
|
contact.category = newCategory;
|
|
// 重新渲染表格
|
|
renderTable();
|
|
showToast('分类已更新', 'success');
|
|
updateStats();
|
|
} catch (error) {
|
|
showToast('更新失败', 'danger');
|
|
}
|
|
}
|
|
|
|
// 快速切换发送状态
|
|
async function toggleSend(id) {
|
|
const contact = contacts.find(c => c.id === id);
|
|
if (!contact) return;
|
|
|
|
const newSelected = !contact.selected;
|
|
|
|
try {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {
|
|
method: 'PUT',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({selected: newSelected})
|
|
});
|
|
contact.selected = newSelected;
|
|
renderTable();
|
|
showToast(newSelected ? '已标记发送' : '已取消发送', 'success');
|
|
updateStats();
|
|
} catch (error) {
|
|
showToast('更新失败', 'danger');
|
|
}
|
|
}
|
|
|
|
// 打开编辑祝福语Modal
|
|
function openBlessingModal(id, blessing) {
|
|
document.getElementById('editBlessingId').value = id;
|
|
document.getElementById('editBlessingInput').value = blessing;
|
|
new bootstrap.Modal(document.getElementById('blessingModal')).show();
|
|
}
|
|
|
|
// 保存祝福语
|
|
async function saveBlessing() {
|
|
const id = document.getElementById('editBlessingId').value;
|
|
const blessing = document.getElementById('editBlessingInput').value.trim();
|
|
|
|
if (!blessing) {
|
|
showToast('祝福语不能为空', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {
|
|
method: 'PUT',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({blessing: blessing})
|
|
});
|
|
bootstrap.Modal.getInstance(document.getElementById('blessingModal')).hide();
|
|
showToast('保存成功', 'success');
|
|
loadContacts(currentPage);
|
|
} catch (error) {
|
|
showToast('保存失败', 'danger');
|
|
}
|
|
}
|
|
|
|
// 打开编辑自定义内容Modal
|
|
function openCustomContentModal(id, customContent) {
|
|
document.getElementById('editCustomContentId').value = id;
|
|
document.getElementById('editCustomContentInput').value = customContent;
|
|
new bootstrap.Modal(document.getElementById('customContentModal')).show();
|
|
}
|
|
|
|
// 保存自定义内容
|
|
async function saveCustomContent() {
|
|
const id = document.getElementById('editCustomContentId').value;
|
|
const customContent = document.getElementById('editCustomContentInput').value.trim();
|
|
|
|
try {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {
|
|
method: 'PUT',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({custom_content: customContent})
|
|
});
|
|
bootstrap.Modal.getInstance(document.getElementById('customContentModal')).hide();
|
|
showToast('保存成功', 'success');
|
|
loadContacts(currentPage);
|
|
} catch (error) {
|
|
showToast('保存失败', 'danger');
|
|
}
|
|
}
|
|
|
|
// 删除联系人
|
|
async function deleteContact(id) {
|
|
if (!confirm('确定要删除这个联系人吗?')) return;
|
|
|
|
try {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {method: 'DELETE'});
|
|
showToast('删除成功', 'success');
|
|
loadContacts(currentPage);
|
|
updateStats();
|
|
} catch (error) {
|
|
showToast('删除失败', 'danger');
|
|
}
|
|
}
|
|
|
|
// 批量删除
|
|
async function batchDelete() {
|
|
const checkedIds = [];
|
|
document.querySelectorAll('.contact-checkbox:checked').forEach(cb => {
|
|
checkedIds.push(parseInt(cb.dataset.id));
|
|
});
|
|
|
|
if (checkedIds.length === 0) {
|
|
showToast('请先选择要删除的联系人', 'warning');
|
|
return;
|
|
}
|
|
|
|
if (!confirm(`确定要删除选中的 ${checkedIds.length} 个联系人吗?`)) return;
|
|
|
|
try {
|
|
let success = 0;
|
|
for (const id of checkedIds) {
|
|
await fetch(`${API_BASE}/contacts/${id}`, {method: 'DELETE'});
|
|
success++;
|
|
}
|
|
showToast(`成功删除 ${success} 个联系人`, 'success');
|
|
loadContacts(currentPage);
|
|
updateStats();
|
|
updateBatchDeleteBtn();
|
|
} catch (error) {
|
|
showToast('删除失败', 'danger');
|
|
}
|
|
}
|
|
|
|
// 更新批量删除按钮状态
|
|
function updateBatchDeleteBtn() {
|
|
const checkedCount = document.querySelectorAll('.contact-checkbox:checked').length;
|
|
const btn = document.getElementById('batchDeleteBtn');
|
|
const countSpan = document.getElementById('selectedCount2');
|
|
|
|
if (checkedCount > 0) {
|
|
btn.style.display = 'inline-block';
|
|
countSpan.textContent = checkedCount;
|
|
} else {
|
|
btn.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// 显示Toast提示
|
|
function showToast(message, type = 'info') {
|
|
const toast = document.getElementById('toast');
|
|
const toastBody = document.getElementById('toastBody');
|
|
toastBody.textContent = message;
|
|
toast.className = `toast show bg-${type} text-white`;
|
|
setTimeout(() => {
|
|
toast.className = 'toast';
|
|
}, 3000);
|
|
}
|
|
|
|
// HTML转义
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|