// ==UserScript==
// @name 纪念币预约辅助工具
// @namespace http://tampermonkey.net/
// @version 1.1
// @description 纪念币预约辅助工具,可以在预约前提前录入人员信息,在预约时点击复制,快速录入预约人的信息
// @author jiangbkvir
// @include *://*.icbc.com.cn/*
// @include *://*.ccb.com/*
// @include *://*.bankcomm.com/*
// @include *://*.hxb.com.cn/*
// @include *://*.spdb.com.cn/*
// @include *://*.psbc.com/*
// @include *://*.abchina.com/*
// @include *://*.boc.cn/*
// @include *://*.szbk.com/*
// @include *://*.hxb.com.cn/*
// @include *://*.csbbank.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// 页面加载完成后执行
window.addEventListener('load', () => {
// 创建表单容器
const container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '50%';
container.style.right = '0';
container.style.transform = 'translateY(-50%)';
container.style.width = '350px';
container.style.maxHeight = '90%';
container.style.overflowY = 'auto';
container.style.padding = '5px';
container.style.background = 'rgba(249, 249, 249, .8)';
container.style.border = '1px solid #ddd';
container.style.borderRadius = '10px';
container.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
container.style.zIndex = '9999';
container.id = 'formContainer';
// 增加顶部内边距,避免按钮遮挡卡片
container.style.paddingTop = '50px'; // 给顶部留出空间
// 创建提示框
const notification = document.createElement('div');
notification.style.position = 'fixed';
notification.style.top = '10%'; // 靠近页面顶部显示
notification.style.left = '50%';
notification.style.transform = 'translateX(-50%)';
notification.style.minWidth = '300px';
notification.style.padding = '10px';
notification.style.background = '#4caf50';
notification.style.color = '#fff';
notification.style.borderRadius = '5px';
notification.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.1)';
notification.style.fontSize = '14px';
notification.style.textAlign = 'center';
notification.style.opacity = '0';
notification.style.transition = 'opacity 0.3s ease';
document.body.appendChild(notification);
// 显示提示信息
function showNotification(message, type = 'success') {
notification.innerText = message;
notification.style.background = type === 'success' ? '#4caf50' : '#f44336';
notification.style.opacity = '1';
setTimeout(() => {
notification.style.opacity = '0';
}, 2000); // 提示2秒后消失
}
// 存储表单数据
const data = [];
// 验证手机号
function validateMobile(mobile) {
return /^1[3-9]\d{9}$/.test(mobile);
}
// 验证身份证号
function validateIDCard(idCard) {
return /^\d{15}$|^\d{17}[0-9Xx]$/.test(idCard);
}
// 创建单个表单(卡片样式)
function createForm(entry = { name: '', mobile: '', idCard: '' }, index) {
const card = document.createElement('div');
card.style.marginBottom = '10px';
card.style.border = '1px solid #ddd';
card.style.borderRadius = '8px';
card.style.padding = '10px 40px 0px 10px';
card.style.background = 'rgba(249, 249, 249, .4)';
card.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.1)';
card.style.position = 'relative';
// 删除按钮放到右上角
const deleteButton = document.createElement('button');
deleteButton.innerText = 'X';
deleteButton.style.position = 'absolute';
deleteButton.style.top = '5px';
deleteButton.style.right = '5px';
deleteButton.style.padding = '5px 8px';
deleteButton.style.background = '#dc3545';
deleteButton.style.color = '#fff';
deleteButton.style.border = 'none';
deleteButton.style.borderRadius = '50%';
deleteButton.style.cursor = 'pointer';
deleteButton.style.fontSize = '14px';
deleteButton.addEventListener('click', () => {
data.splice(index, 1); // 删除对应数据
card.remove(); // 删除表单
showNotification('表单已删除!');
saveData(); // 删除后更新保存数据
});
const fields = ['name', 'idCard', 'mobile'];
const labels = ['姓名', '身份证号', '手机号'];
fields.forEach((field, idx) => {
const row = document.createElement('div');
row.style.display = 'flex';
row.style.alignItems = 'center';
row.style.marginBottom = '8px';
const label = document.createElement('label');
label.innerText = `${labels[idx]}:`;
label.style.width = '80px'; // 优化标签宽度,减少空间浪费
label.style.fontSize = '12px';
const input = document.createElement('input');
input.type = 'text';
input.value = entry[field];
input.style.flex = '1';
input.style.padding = '4px';
input.style.border = '1px solid #ccc';
input.style.borderRadius = '4px';
input.style.marginRight = '10px';
input.style.fontSize = '12px';
input.style.maxWidth = '100%'; // 确保所有输入框宽度一致
input.addEventListener('blur', () => {
let valid = true;
// 校验
if (field === 'mobile' && !validateMobile(input.value)) {
showNotification('手机号格式不正确!', 'error');
input.value = ''; // 清空输入框
valid = false;
}
if (field === 'idCard' && !validateIDCard(input.value)) {
showNotification('身份证号格式不正确!', 'error');
input.value = ''; // 清空输入框
valid = false;
}
// 如果校验通过
if (valid) {
entry[field] = input.value; // 更新数据
showNotification('数据已保存!');
} else {
entry[field] = ''; // 保证无效数据清空
}
saveData(); // 每次输入框失去焦点时自动保存
});
// 创建复制按钮并放置在输入框后
const copyButton = document.createElement('button');
copyButton.innerText = '复制';
copyButton.style.marginLeft = '8px';
copyButton.style.padding = '4px 8px';
copyButton.style.background = '#007bff';
copyButton.style.color = '#fff';
copyButton.style.border = 'none';
copyButton.style.borderRadius = '5px';
copyButton.style.cursor = 'pointer';
copyButton.style.fontSize = '12px';
copyButton.addEventListener('click', () => {
const inputValue = input.value; // 获取当前输入框的值
navigator.clipboard.writeText(inputValue) // 将输入框的值复制到剪贴板
.then(() => {
const name = entry.name || '姓名';
let message = `${name} 已复制`;
if (field === 'idCard') {
message = `${name} 身份证已复制`;
} else if (field === 'mobile') {
message = `${name} 手机号已复制`;
}
showNotification(message);
})
.catch(() => showNotification('复制失败!', 'error'));
});
row.appendChild(label);
row.appendChild(input);
row.appendChild(copyButton); // 将复制按钮放到输入框后面
card.appendChild(row);
// 动态更新数据
input.addEventListener('input', () => {
entry[field] = input.value;
});
});
card.appendChild(deleteButton);
container.appendChild(card);
}
// 保存数据
function saveData() {
// localStorage.setItem('formData', JSON.stringify(data));
GM_setValue('formData', JSON.stringify(data));
}
// 添加按钮组(放置按钮)
function addButtons() {
const buttonsContainer = document.createElement('div');
buttonsContainer.id = 'buttonsContainer';
buttonsContainer.style.marginBottom = '10px';
buttonsContainer.style.display = 'flex';
buttonsContainer.style.flexDirection = 'row'; // 保持在同一行显示按钮
buttonsContainer.style.alignItems = 'center';
buttonsContainer.style.position = 'absolute';
buttonsContainer.style.top = '10px'; // 确保按钮在顶部固定
// 添加表单按钮
const addButton = document.createElement('button');
addButton.innerText = '添加';
addButton.style.marginRight = '8px';
addButton.style.padding = '6px 10px';
addButton.style.background = '#28a745';
addButton.style.color = '#fff';
addButton.style.border = 'none';
addButton.style.borderRadius = '5px';
addButton.style.cursor = 'pointer';
addButton.style.fontSize = '12px';
addButton.addEventListener('click', () => {
const newEntry = { name: '', mobile: '', idCard: '' };
data.push(newEntry);
createForm(newEntry, data.length - 1);
saveData(); // 自动保存
});
// 导出按钮
const exportButton = document.createElement('button');
exportButton.innerText = '导出';
exportButton.style.marginRight = '8px';
exportButton.style.padding = '6px 10px';
exportButton.style.background = '#007bff';
exportButton.style.color = '#fff';
exportButton.style.border = 'none';
exportButton.style.borderRadius = '5px';
exportButton.style.cursor = 'pointer';
exportButton.style.fontSize = '12px';
exportButton.addEventListener('click', () => {
const jsonData = JSON.stringify(data, null, 2);
const blob = new Blob([jsonData], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'form_data.json';
a.click();
});
// 导入按钮
const importButton = document.createElement('button');
importButton.innerText = '导入';
importButton.style.marginRight = '8px';
importButton.style.padding = '6px 10px';
importButton.style.background = '#ffc107';
importButton.style.color = '#000';
importButton.style.border = 'none';
importButton.style.borderRadius = '5px';
importButton.style.cursor = 'pointer';
importButton.style.fontSize = '12px';
importButton.addEventListener('click', () => {
if (confirm('导入会覆盖现有数据,确定导入吗?')) {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const importedData = JSON.parse(e.target.result);
data.splice(0, data.length, ...importedData); // 更新数据
container.innerHTML = ''; // 清空现有表单
addButtons(); // 重新添加按钮组
importedData.forEach(entry => {
createForm(entry, data.length - 1);
});
saveData(); // 导入后自动保存
} catch (error) {
showNotification('导入的文件格式不正确!', 'error');
}
};
reader.readAsText(file);
}
});
input.click();
}
});
// 收起按钮
const collapseButton = document.createElement('button');
collapseButton.innerText = '收起';
collapseButton.style.marginRight = '8px';
collapseButton.style.padding = '6px 10px';
collapseButton.style.background = '#6c757d';
collapseButton.style.color = '#fff';
collapseButton.style.border = 'none';
collapseButton.style.borderRadius = '5px';
collapseButton.style.cursor = 'pointer';
collapseButton.style.fontSize = '12px';
collapseButton.addEventListener('click', () => {
container.style.display = 'none'; // 收起表单容器
expandButton.style.display = 'inline-block'; // 显示展开按钮
});
buttonsContainer.appendChild(addButton);
buttonsContainer.appendChild(exportButton);
buttonsContainer.appendChild(importButton);
buttonsContainer.appendChild(collapseButton);
container.appendChild(buttonsContainer);
}
// 展开按钮
const expandButton = document.createElement('button');
expandButton.innerText = '展开';
expandButton.style.marginRight = '8px';
expandButton.style.padding = '6px 10px';
expandButton.style.background = '#28a745';
expandButton.style.color = '#fff';
expandButton.style.border = 'none';
expandButton.style.borderRadius = '5px';
expandButton.style.cursor = 'pointer';
expandButton.style.fontSize = '12px';
expandButton.style.display = 'none'; // 默认隐藏展开按钮
expandButton.style.position = 'fixed';
expandButton.style.top = '50%';
expandButton.style.right = '0';
expandButton.style.transform = 'translateY(-50%)';
expandButton.addEventListener('click', () => {
container.style.display = 'block'; // 展开表单容器
expandButton.style.display = 'none'; // 隐藏展开按钮
});
// 添加展开按钮到 body
document.body.appendChild(expandButton);
// 从 Tampermonkey 存储空间获取数据
let savedData = GM_getValue('formData', []); // 如果没有保存数据,返回空字符串
if (!savedData) {
console.log('没有找到保存的数据');
return
}
if (savedData) {
try {
const parsedData = JSON.parse(savedData);
parsedData.forEach(entry => {
data.push(entry); // 将保存的数据添加到数据数组
createForm(entry, data.length - 1); // 创建表单
});
showNotification('已加载保存的数据!');
} catch (error) {
showNotification('加载保存的数据失败!', 'error');
}
}
addButtons(); // 添加按钮组
document.body.appendChild(container); // 将容器添加到页面
});
})();