Files
weixin-holiday-message/contacts_manager.html

734 lines
27 KiB
HTML
Raw Normal View History

<!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">
<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;
}
.header h1 {
color: #333;
font-weight: bold;
}
.header .subtitle {
color: #666;
margin-top: 10px;
}
.stats-bar {
display: flex;
justify-content: center;
gap: 30px;
margin-bottom: 20px;
}
.stat-item {
text-align: center;
padding: 15px 25px;
background: #f8f9fa;
border-radius: 10px;
}
.stat-number {
font-size: 28px;
font-weight: bold;
color: #667eea;
}
.stat-label {
color: #666;
font-size: 14px;
}
.toolbar {
margin-bottom: 20px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.contact-table {
width: 100%;
}
.contact-row {
display: grid;
grid-template-columns: 40px 1fr 120px 1fr 60px;
gap: 15px;
padding: 12px 15px;
border-bottom: 1px solid #eee;
align-items: center;
transition: background 0.2s;
}
.contact-row:hover {
background: #f8f9fa;
}
.contact-row.header-row {
background: #667eea;
color: white;
font-weight: bold;
border-radius: 8px;
}
.contact-name {
cursor: pointer;
color: #333;
font-weight: 500;
}
.contact-name:hover {
color: #667eea;
text-decoration: underline;
}
.category-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.category-badge.同事 { background: #e3f2fd; color: #1976d2; }
.category-badge.好友 { background: #e8f5e9; color: #388e3c; }
.category-badge.老师 { background: #fff3e0; color: #f57c00; }
.category-badge.亲戚 { background: #fce4ec; color: #c2185b; }
.category-badge.客户 { background: #f3e5f5; color: #7b1fa2; }
.category-badge.其他 { background: #f5f5f5; color: #616161; }
.category-badge:hover {
transform: scale(1.05);
}
.blessing-text {
color: #666;
cursor: pointer;
font-style: italic;
}
.blessing-text:hover {
color: #667eea;
}
.action-btn {
padding: 5px 15px;
border-radius: 5px;
border: none;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
}
.btn-send {
background: #667eea;
color: white;
}
.btn-send:hover {
background: #5a6fd6;
}
.btn-select-all {
background: #28a745;
color: white;
}
.btn-export {
background: #17a2b8;
color: white;
}
.modal-content {
border-radius: 15px;
}
.modal-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 15px 15px 0 0;
}
.modal-header .btn-close {
filter: brightness(0) invert(1);
}
.category-select {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.category-option {
padding: 8px 20px;
border: 2px solid #ddd;
border-radius: 20px;
cursor: pointer;
transition: all 0.2s;
}
.category-option:hover {
border-color: #667eea;
}
.category-option.selected {
background: #667eea;
color: white;
border-color: #667eea;
}
.filter-tabs {
display: flex;
gap: 10px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.filter-tab {
padding: 6px 15px;
border-radius: 20px;
background: #f0f0f0;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
}
.filter-tab:hover, .filter-tab.active {
background: #667eea;
color: white;
}
.search-box {
margin-bottom: 15px;
}
.checkbox-wrapper {
display: flex;
align-items: center;
justify-content: center;
}
.checkbox-wrapper input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.empty-state {
text-align: center;
padding: 50px;
color: #999;
}
.pagination {
display: flex;
justify-content: center;
gap: 5px;
margin-top: 20px;
}
.page-btn {
padding: 8px 15px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 5px;
}
.page-btn:hover, .page-btn.active {
background: #667eea;
color: white;
border-color: #667eea;
}
</style>
</head>
<body>
<div class="container container-main">
<div class="header">
<h1>🧧 微信联系人新年祝福管理</h1>
<div class="subtitle">马年新春,为您的联系人送上一份温暖的祝福</div>
</div>
<div class="stats-bar">
<div class="stat-item">
<div class="stat-number" id="totalCount">0</div>
<div class="stat-label">总联系人数</div>
</div>
<div class="stat-item">
<div class="stat-number" id="selectedCount">0</div>
<div class="stat-label">已选择发送</div>
</div>
<div class="stat-item">
<div class="stat-number" id="categorizedCount">0</div>
<div class="stat-label">已分类</div>
</div>
</div>
<div class="toolbar">
<button class="action-btn btn-select-all" onclick="toggleSelectAll()">全选/取消</button>
<button class="action-btn btn-send" onclick="sendBlessings()">发送祝福</button>
<button class="action-btn btn-export" onclick="exportData()">导出数据</button>
<button class="action-btn" style="background:#6c757d;color:white" onclick="importData()">导入数据</button>
</div>
<div class="search-box">
<input type="text" class="form-control" id="searchInput" placeholder="搜索联系人..." oninput="filterContacts()">
</div>
<div class="filter-tabs">
<div class="filter-tab active" onclick="filterByCategory('all')">全部</div>
<div class="filter-tab" onclick="filterByCategory('同事')">同事</div>
<div class="filter-tab" onclick="filterByCategory('好友')">好友</div>
<div class="filter-tab" onclick="filterByCategory('老师')">老师</div>
<div class="filter-tab" onclick="filterByCategory('亲戚')">亲戚</div>
<div class="filter-tab" onclick="filterByCategory('客户')">客户</div>
<div class="filter-tab" onclick="filterByCategory('其他')">其他</div>
<div class="filter-tab" onclick="filterByCategory('未分类')">未分类</div>
</div>
<div id="contactList">
<!-- 联系人列表将在这里动态生成 -->
</div>
<div class="pagination" id="pagination">
<!-- 分页将在这里动态生成 -->
</div>
</div>
<!-- 编辑联系人名称模态框 -->
<div class="modal fade" id="editNameModal" 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="editContactId">
<div class="mb-3">
<label class="form-label">联系人名称</label>
<input type="text" class="form-control" id="editNameInput">
</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>
<!-- 设置分类模态框 -->
<div class="modal fade" id="categoryModal" 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="categoryContactId">
<div class="category-select" id="categorySelect">
<div class="category-option" data-category="同事" onclick="selectCategory(this)">同事</div>
<div class="category-option" data-category="好友" onclick="selectCategory(this)">好友</div>
<div class="category-option" data-category="老师" onclick="selectCategory(this)">老师</div>
<div class="category-option" data-category="亲戚" onclick="selectCategory(this)">亲戚</div>
<div class="category-option" data-category="客户" onclick="selectCategory(this)">客户</div>
<div class="category-option" data-category="其他" onclick="selectCategory(this)">其他</div>
</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="saveCategory()">保存</button>
</div>
</div>
</div>
</div>
<!-- 编辑祝福语模态框 -->
<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="blessingContactId">
<div class="mb-3">
<label class="form-label">选择祝福语模板</label>
<select class="form-select" id="blessingTemplate" onchange="applyBlessingTemplate()">
<option value="">自定义</option>
<option value="马年新春快乐!愿您在新的一年里,事业腾飞,马到成功!">马年祝福1</option>
<option value="新春佳节,祝您马年大吉,万事如意,阖家幸福!">马年祝福2</option>
<option value="马年到来,愿您龙马精神,身体健康,财源广进!">马年祝福3</option>
<option value="新春快乐!祝您马年行大运,心想事成!">马年祝福4</option>
<option value="新年好!愿您马年一切顺利,步步高升!">马年祝福5</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">祝福语内容</label>
<textarea class="form-control" id="blessingInput" rows="3"></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>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 数据存储
let contacts = [];
let currentPage = 1;
const pageSize = 20;
let currentFilter = 'all';
// 初始化
document.addEventListener('DOMContentLoaded', function() {
loadData();
});
// 从LocalStorage加载数据
function loadData() {
const saved = localStorage.getItem('wechatContacts');
if (saved) {
contacts = JSON.parse(saved);
renderContacts();
updateStats();
} else {
// 如果没有保存的数据尝试加载JSON文件
loadFromJsonFile();
}
}
// 从JSON文件加载
async function loadFromJsonFile() {
try {
const response = await fetch('contacts_data.json');
if (response.ok) {
contacts = await response.json();
saveData();
renderContacts();
updateStats();
alert('已加载联系人数据!');
} else {
// 使用示例数据
loadSampleData();
}
} catch (e) {
// 文件不存在或加载失败,使用示例数据
loadSampleData();
}
}
// 加载示例数据
function loadSampleData() {
const sampleNames = [
"深信服刘峻委", "深信服-刘艳彬", "深信服-刘英杰", "深信服马杰",
"沈阳营业部陈亮", "深圳分公司-姜沣原", "深圳通晓鹏", "神州数码刘月华"
];
contacts = sampleNames.map((name, index) => ({
id: index + 1,
name: name,
category: '',
blessing: '马年新春快乐!愿您在新的一年里,事业腾飞,马到成功!',
selected: false
}));
saveData();
renderContacts();
updateStats();
}
// 保存数据到LocalStorage
function saveData() {
localStorage.setItem('wechatContacts', JSON.stringify(contacts));
updateStats();
}
// 渲染联系人列表
function renderContacts() {
const listContainer = document.getElementById('contactList');
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
// 过滤
let filtered = contacts.filter(c => {
const matchSearch = c.name.toLowerCase().includes(searchTerm);
const matchCategory = currentFilter === 'all' ||
(currentFilter === '未分类' && !c.category) ||
c.category === currentFilter;
return matchSearch && matchCategory;
});
// 分页
const totalPages = Math.ceil(filtered.length / pageSize);
const start = (currentPage - 1) * pageSize;
const pageData = filtered.slice(start, start + pageSize);
if (pageData.length === 0) {
listContainer.innerHTML = '<div class="empty-state">暂无联系人数据</div>';
document.getElementById('pagination').innerHTML = '';
return;
}
// 表头
let html = `
<div class="contact-row header-row">
<div class="checkbox-wrapper">
<input type="checkbox" onchange="togglePageSelect(this.checked)">
</div>
<div>联系人名称</div>
<div>分类</div>
<div>祝福语</div>
<div>发送</div>
</div>
`;
// 数据行
pageData.forEach(contact => {
html += `
<div class="contact-row" data-id="${contact.id}">
<div class="checkbox-wrapper">
<input type="checkbox" ${contact.selected ? 'checked' : ''}
onchange="toggleContact(${contact.id}, this.checked)">
</div>
<div class="contact-name" onclick="editName(${contact.id})">${contact.name}</div>
<div>
<span class="category-badge ${contact.category || '其他'}"
onclick="editCategory(${contact.id})">
${contact.category || '点击分类'}
</span>
</div>
<div class="blessing-text" onclick="editBlessing(${contact.id})">
${contact.blessing || '点击设置祝福语'}
</div>
<div>
<input type="checkbox" class="send-checkbox"
${contact.selected ? 'checked' : ''}
onchange="toggleContact(${contact.id}, this.checked)">
</div>
</div>
`;
});
listContainer.innerHTML = html;
// 分页
renderPagination(totalPages);
}
// 渲染分页
function renderPagination(totalPages) {
const pagination = document.getElementById('pagination');
if (totalPages <= 1) {
pagination.innerHTML = '';
return;
}
let html = '';
for (let i = 1; i <= totalPages; i++) {
html += `<button class="page-btn ${i === currentPage ? 'active' : ''}"
onclick="goToPage(${i})">${i}</button>`;
}
pagination.innerHTML = html;
}
// 跳转页面
function goToPage(page) {
currentPage = page;
renderContacts();
}
// 更新统计
function updateStats() {
document.getElementById('totalCount').textContent = contacts.length;
document.getElementById('selectedCount').textContent = contacts.filter(c => c.selected).length;
document.getElementById('categorizedCount').textContent = contacts.filter(c => c.category).length;
}
// 切换联系人选择
function toggleContact(id, checked) {
const contact = contacts.find(c => c.id === id);
if (contact) {
contact.selected = checked;
saveData();
renderContacts();
}
}
// 全选/取消
function toggleSelectAll() {
const allSelected = contacts.every(c => c.selected);
contacts.forEach(c => c.selected = !allSelected);
saveData();
renderContacts();
}
// 当前页全选
function togglePageSelect(checked) {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
let filtered = contacts.filter(c => {
const matchSearch = c.name.toLowerCase().includes(searchTerm);
const matchCategory = currentFilter === 'all' ||
(currentFilter === '未分类' && !c.category) ||
c.category === currentFilter;
return matchSearch && matchCategory;
});
const start = (currentPage - 1) * pageSize;
const pageIds = filtered.slice(start, start + pageSize).map(c => c.id);
contacts.filter(c => pageIds.includes(c.id)).forEach(c => c.selected = checked);
saveData();
renderContacts();
}
// 搜索过滤
function filterContacts() {
currentPage = 1;
renderContacts();
}
// 分类过滤
function filterByCategory(category) {
currentFilter = category;
currentPage = 1;
document.querySelectorAll('.filter-tab').forEach(tab => {
tab.classList.remove('active');
});
event.target.classList.add('active');
renderContacts();
}
// 编辑名称
function editName(id) {
const contact = contacts.find(c => c.id === id);
if (contact) {
document.getElementById('editContactId').value = id;
document.getElementById('editNameInput').value = contact.name;
new bootstrap.Modal(document.getElementById('editNameModal')).show();
}
}
// 保存名称
function saveName() {
const id = parseInt(document.getElementById('editContactId').value);
const newName = document.getElementById('editNameInput').value.trim();
if (newName) {
const contact = contacts.find(c => c.id === id);
if (contact) {
contact.name = newName;
saveData();
renderContacts();
}
}
bootstrap.Modal.getInstance(document.getElementById('editNameModal')).hide();
}
// 编辑分类
function editCategory(id) {
const contact = contacts.find(c => c.id === id);
if (contact) {
document.getElementById('categoryContactId').value = id;
document.querySelectorAll('.category-option').forEach(opt => {
opt.classList.remove('selected');
if (opt.dataset.category === contact.category) {
opt.classList.add('selected');
}
});
new bootstrap.Modal(document.getElementById('categoryModal')).show();
}
}
// 选择分类
function selectCategory(element) {
document.querySelectorAll('.category-option').forEach(opt => {
opt.classList.remove('selected');
});
element.classList.add('selected');
}
// 保存分类
function saveCategory() {
const id = parseInt(document.getElementById('categoryContactId').value);
const selected = document.querySelector('.category-option.selected');
if (selected) {
const contact = contacts.find(c => c.id === id);
if (contact) {
contact.category = selected.dataset.category;
saveData();
renderContacts();
}
}
bootstrap.Modal.getInstance(document.getElementById('categoryModal')).hide();
}
// 编辑祝福语
function editBlessing(id) {
const contact = contacts.find(c => c.id === id);
if (contact) {
document.getElementById('blessingContactId').value = id;
document.getElementById('blessingInput').value = contact.blessing || '';
document.getElementById('blessingTemplate').value = '';
new bootstrap.Modal(document.getElementById('blessingModal')).show();
}
}
// 应用祝福语模板
function applyBlessingTemplate() {
const template = document.getElementById('blessingTemplate').value;
if (template) {
document.getElementById('blessingInput').value = template;
}
}
// 保存祝福语
function saveBlessing() {
const id = parseInt(document.getElementById('blessingContactId').value);
const blessing = document.getElementById('blessingInput').value.trim();
const contact = contacts.find(c => c.id === id);
if (contact) {
contact.blessing = blessing;
saveData();
renderContacts();
}
bootstrap.Modal.getInstance(document.getElementById('blessingModal')).hide();
}
// 发送祝福(模拟)
function sendBlessings() {
const selected = contacts.filter(c => c.selected);
if (selected.length === 0) {
alert('请先选择要发送祝福的联系人!');
return;
}
let message = `即将向 ${selected.length} 位联系人发送祝福:\n\n`;
selected.forEach(c => {
message += `${c.name}: ${c.blessing}\n`;
});
message += '\n\n此为模拟功能实际发送需要在微信中操作';
alert(message);
}
// 导出数据
function exportData() {
const dataStr = JSON.stringify(contacts, null, 2);
const blob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'wechat_contacts_backup.json';
a.click();
URL.revokeObjectURL(url);
}
// 导入数据
function importData() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = function(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function(e) {
try {
contacts = JSON.parse(e.target.result);
saveData();
renderContacts();
alert('导入成功!');
} catch (err) {
alert('导入失败:文件格式错误');
}
};
reader.readAsText(file);
};
input.click();
}
</script>
</body>
</html>