Files
weixin-holiday-message/contacts_manager.html

734 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>