feat(vip搜索): 增强VIP客户搜索功能并添加测试
- 初始化检查allVIPCustomers数组防止未定义错误 - 优化搜索逻辑,支持姓名、手机号和车牌号搜索 - 添加手机号标准化处理,支持带格式号码匹配 - 处理空搜索和特殊字符情况,提高健壮性 - 添加转义处理防止XSS攻击 - 创建测试页面验证搜索功能
This commit is contained in:
@@ -941,6 +941,12 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
function searchVIPCustomers() {
|
||||
console.log('开始搜索VIP客户...');
|
||||
|
||||
// 确保allVIPCustomers已初始化
|
||||
if (!window.allVIPCustomers || !Array.isArray(window.allVIPCustomers)) {
|
||||
window.allVIPCustomers = [];
|
||||
console.warn('allVIPCustomers未初始化,已创建空数组');
|
||||
}
|
||||
|
||||
const searchInput = document.getElementById('vip_search');
|
||||
const searchResultsDiv = document.getElementById('vip_search_results');
|
||||
const vipSelect = document.getElementById('vip_id');
|
||||
@@ -956,6 +962,14 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
// 检查数据加载状态
|
||||
console.log('VIP客户总数:', window.allVIPCustomers.length);
|
||||
|
||||
// 立即处理空搜索情况
|
||||
if (searchTerm === '') {
|
||||
searchResultsDiv.innerHTML = '';
|
||||
searchResultsDiv.style.display = 'none';
|
||||
vipSelect.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
// 始终显示搜索结果容器
|
||||
searchResultsDiv.style.display = 'block';
|
||||
|
||||
@@ -978,43 +992,76 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
}
|
||||
|
||||
function performSearch(term) {
|
||||
if (term === '') {
|
||||
searchResultsDiv.innerHTML = '';
|
||||
searchResultsDiv.style.display = 'none';
|
||||
vipSelect.style.display = 'block';
|
||||
return;
|
||||
// 标准化手机号函数
|
||||
const normalizePhone = (phoneStr) => {
|
||||
return phoneStr ? phoneStr.replace(/[^0-9]/g, '') : '';
|
||||
};
|
||||
|
||||
// 模糊搜索:支持姓名、手机号和车牌号搜索,增强匹配逻辑
|
||||
const filteredCustomers = window.allVIPCustomers.filter(vip => {
|
||||
// 确保vip对象有效
|
||||
if (!vip || typeof vip !== 'object') return false;
|
||||
|
||||
const searchLower = term.toLowerCase();
|
||||
const name = (vip.customer_name || '').toLowerCase();
|
||||
const phone = vip.phone || '';
|
||||
const carModel = (vip.car_model || '').toLowerCase();
|
||||
const carNumber = (vip.car_number || '').toLowerCase();
|
||||
|
||||
// 基本匹配
|
||||
let isMatch = name.includes(searchLower) ||
|
||||
phone.includes(term) ||
|
||||
carModel.includes(searchLower) ||
|
||||
carNumber.includes(searchLower);
|
||||
|
||||
// 手机号标准化匹配(处理数字搜索)
|
||||
if (!isMatch && /^\d+$/.test(term)) {
|
||||
const normalizedPhone = normalizePhone(phone);
|
||||
const normalizedSearch = normalizePhone(term);
|
||||
isMatch = normalizedPhone.includes(normalizedSearch) ||
|
||||
normalizedSearch.includes(normalizedPhone);
|
||||
}
|
||||
|
||||
// 模糊搜索:支持姓名和手机号搜索
|
||||
const filteredCustomers = window.allVIPCustomers.filter(vip => {
|
||||
const name = (vip.customer_name || '').toLowerCase();
|
||||
const phone = (vip.phone || '').toLowerCase();
|
||||
const searchLower = term.toLowerCase();
|
||||
// 调试日志
|
||||
console.log('搜索项检查:', {
|
||||
id: vip.id,
|
||||
name: vip.customer_name,
|
||||
phone: phone,
|
||||
match: isMatch
|
||||
});
|
||||
|
||||
return name.includes(searchLower) || phone.includes(searchLower);
|
||||
return isMatch;
|
||||
});
|
||||
|
||||
console.log('搜索结果数量:', filteredCustomers.length);
|
||||
|
||||
// 显示搜索结果
|
||||
if (filteredCustomers.length === 0) {
|
||||
searchResultsDiv.innerHTML = '<div class="no-results">未找到匹配的VIP客户</div>';
|
||||
searchResultsDiv.innerHTML = '<div class="no-results">未找到匹配的VIP客户<br>请尝试使用姓名、手机号或车牌号搜索</div>';
|
||||
} else {
|
||||
let html = '';
|
||||
filteredCustomers.forEach(vip => {
|
||||
const name = vip.customer_name || '';
|
||||
const phone = vip.phone || '';
|
||||
// 安全获取数据并设置默认值
|
||||
const name = vip.customer_name || '未知客户';
|
||||
const phone = vip.phone || '未知手机号';
|
||||
const id = vip.id || '';
|
||||
const carModel = vip.car_model || '';
|
||||
const carNumber = vip.car_number || '';
|
||||
|
||||
// 转义引号,防止JS语法错误
|
||||
const escapedId = id.toString().replace(/'/g, "\\'");
|
||||
const escapedName = name.replace(/'/g, "\\'");
|
||||
const escapedPhone = phone.replace(/'/g, "\\'");
|
||||
|
||||
// 高亮处理
|
||||
const highlightedName = name.replace(new RegExp(`(${term})`, 'gi'), '<span class="highlight">$1</span>');
|
||||
const highlightedPhone = phone.replace(new RegExp(`(${term})`, 'gi'), '<span class="highlight">$1</span>');
|
||||
|
||||
html += `
|
||||
<div class="vip-search-item" onclick="selectVIPCustomer('${id}', '${name}', '${phone}')">
|
||||
<div class="vip-search-item" onclick="selectVIPCustomer('${escapedId}', '${escapedName}', '${escapedPhone}')">
|
||||
<div class="customer-name">${highlightedName}</div>
|
||||
<div class="customer-phone">${highlightedPhone}</div>
|
||||
${vip.car_model || vip.car_number ? `<div class="customer-car">${vip.car_model || ''} ${vip.car_number || ''}</div>` : ''}
|
||||
${carModel || carNumber ? `<div class="customer-car">${carModel} ${carNumber}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>VIP搜索功能测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
.test-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 6px;
|
||||
}
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
margin: 5px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
#test-results {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background-color: #e9f7fe;
|
||||
border-left: 4px solid #2196F3;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.success {
|
||||
color: green;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
.warning {
|
||||
color: orange;
|
||||
}
|
||||
code {
|
||||
background-color: #f0f0f0;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.debug-info {
|
||||
margin-top: 10px;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>VIP搜索功能测试</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>1. 测试数据初始化</h2>
|
||||
<button id="init-test-data">初始化测试数据</button>
|
||||
<p class="debug-info">将创建模拟的VIP客户数据用于测试</p>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>2. 测试搜索功能</h2>
|
||||
<input type="text" id="test-search-term" placeholder="输入搜索关键词" value="张">
|
||||
<button id="test-search">执行搜索</button>
|
||||
<p class="debug-info">测试修改后的搜索逻辑</p>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>3. 测试手机号标准化</h2>
|
||||
<input type="text" id="test-phone" placeholder="输入手机号" value="18699627661">
|
||||
<button id="test-phone-search">搜索手机号</button>
|
||||
<p class="debug-info">测试手机号标准化匹配功能</p>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>4. 测试边界情况</h2>
|
||||
<button id="test-empty-data">测试空数据</button>
|
||||
<button id="test-special-chars">测试特殊字符</button>
|
||||
<p class="debug-info">测试各种边界情况和异常处理</p>
|
||||
</div>
|
||||
|
||||
<div id="test-results">
|
||||
<h3>测试结果</h3>
|
||||
<div id="results-content">请点击上面的按钮开始测试...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 模拟VIP搜索函数的简化版本,用于测试
|
||||
function simulateVIPSearch(searchTerm) {
|
||||
// 标准化手机号函数
|
||||
const normalizePhone = (phoneStr) => {
|
||||
return phoneStr ? phoneStr.replace(/[^0-9]/g, '') : '';
|
||||
};
|
||||
|
||||
// 确保allVIPCustomers已初始化
|
||||
if (!window.allVIPCustomers || !Array.isArray(window.allVIPCustomers)) {
|
||||
window.allVIPCustomers = [];
|
||||
}
|
||||
|
||||
console.log('执行测试搜索,关键词:', searchTerm);
|
||||
|
||||
// 增强的搜索逻辑
|
||||
const filteredCustomers = window.allVIPCustomers.filter(vip => {
|
||||
// 确保vip对象有效
|
||||
if (!vip || typeof vip !== 'object') return false;
|
||||
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const name = (vip.customer_name || '').toLowerCase();
|
||||
const phone = vip.phone || '';
|
||||
const carModel = (vip.car_model || '').toLowerCase();
|
||||
const carNumber = (vip.car_number || '').toLowerCase();
|
||||
|
||||
// 基本匹配
|
||||
let isMatch = name.includes(searchLower) ||
|
||||
phone.includes(searchTerm) ||
|
||||
carModel.includes(searchLower) ||
|
||||
carNumber.includes(searchLower);
|
||||
|
||||
// 手机号标准化匹配(处理数字搜索)
|
||||
if (!isMatch && /^\d+$/.test(searchTerm)) {
|
||||
const normalizedPhone = normalizePhone(phone);
|
||||
const normalizedSearch = normalizePhone(searchTerm);
|
||||
isMatch = normalizedPhone.includes(normalizedSearch) ||
|
||||
normalizedSearch.includes(normalizedPhone);
|
||||
}
|
||||
|
||||
return isMatch;
|
||||
});
|
||||
|
||||
console.log('测试搜索结果数量:', filteredCustomers.length);
|
||||
return filteredCustomers;
|
||||
}
|
||||
|
||||
// 初始化测试数据
|
||||
document.getElementById('init-test-data').addEventListener('click', function() {
|
||||
// 创建模拟的VIP客户数据
|
||||
window.allVIPCustomers = [
|
||||
{
|
||||
id: '1',
|
||||
customer_name: '张三',
|
||||
phone: '18612345678',
|
||||
car_model: '奔驰C级',
|
||||
car_number: '京A12345'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
customer_name: '李四',
|
||||
phone: '13987654321',
|
||||
car_model: '宝马3系',
|
||||
car_number: '京B54321'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
customer_name: '王五',
|
||||
phone: '18699627661',
|
||||
car_model: '奥迪A4',
|
||||
car_number: '沪A12345'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
customer_name: '赵六',
|
||||
phone: '186-9962-7777',
|
||||
car_model: '特斯拉Model 3',
|
||||
car_number: '深A67890'
|
||||
}
|
||||
];
|
||||
|
||||
const resultsContent = document.getElementById('results-content');
|
||||
resultsContent.innerHTML = '<p class="success">✓ 测试数据初始化成功</p>';
|
||||
resultsContent.innerHTML += '<p>已创建 4 个模拟VIP客户数据</p>';
|
||||
resultsContent.innerHTML += '<pre>' + JSON.stringify(window.allVIPCustomers, null, 2) + '</pre>';
|
||||
});
|
||||
|
||||
// 测试搜索功能
|
||||
document.getElementById('test-search').addEventListener('click', function() {
|
||||
const searchTerm = document.getElementById('test-search-term').value;
|
||||
const results = simulateVIPSearch(searchTerm);
|
||||
|
||||
const resultsContent = document.getElementById('results-content');
|
||||
resultsContent.innerHTML = `<p>搜索关键词: <code>${searchTerm}</code></p>`;
|
||||
resultsContent.innerHTML += `<p>找到 ${results.length} 个匹配结果</p>`;
|
||||
|
||||
if (results.length > 0) {
|
||||
resultsContent.innerHTML += '<pre>' + JSON.stringify(results, null, 2) + '</pre>';
|
||||
} else {
|
||||
resultsContent.innerHTML += '<p class="warning">未找到匹配的客户</p>';
|
||||
}
|
||||
});
|
||||
|
||||
// 测试手机号搜索
|
||||
document.getElementById('test-phone-search').addEventListener('click', function() {
|
||||
const phone = document.getElementById('test-phone').value;
|
||||
const results = simulateVIPSearch(phone);
|
||||
|
||||
const resultsContent = document.getElementById('results-content');
|
||||
resultsContent.innerHTML = `<p>搜索手机号: <code>${phone}</code></p>`;
|
||||
resultsContent.innerHTML += `<p>找到 ${results.length} 个匹配结果</p>`;
|
||||
|
||||
if (results.length > 0) {
|
||||
resultsContent.innerHTML += '<pre>' + JSON.stringify(results, null, 2) + '</pre>';
|
||||
} else {
|
||||
resultsContent.innerHTML += '<p class="warning">未找到匹配的客户</p>';
|
||||
}
|
||||
});
|
||||
|
||||
// 测试空数据
|
||||
document.getElementById('test-empty-data').addEventListener('click', function() {
|
||||
// 保存当前数据
|
||||
const originalData = window.allVIPCustomers;
|
||||
// 设置为空数据
|
||||
window.allVIPCustomers = [];
|
||||
|
||||
const results = simulateVIPSearch('测试');
|
||||
|
||||
const resultsContent = document.getElementById('results-content');
|
||||
resultsContent.innerHTML = '<p>测试空数据情况</p>';
|
||||
resultsContent.innerHTML += `<p>找到 ${results.length} 个匹配结果</p>`;
|
||||
resultsContent.innerHTML += '<p class="success">✓ 空数据处理正常</p>';
|
||||
|
||||
// 恢复原数据
|
||||
window.allVIPCustomers = originalData;
|
||||
});
|
||||
|
||||
// 测试特殊字符
|
||||
document.getElementById('test-special-chars').addEventListener('click', function() {
|
||||
const specialTerms = ["张'三", '李"四', "王\\五", '赵\n六', '测试*特殊&字符'];
|
||||
let resultsHTML = '<p>测试特殊字符处理</p>';
|
||||
|
||||
specialTerms.forEach(term => {
|
||||
const results = simulateVIPSearch(term);
|
||||
resultsHTML += `<p>关键词 <code>${escapeHTML(term)}</code>: 找到 ${results.length} 个结果</p>`;
|
||||
});
|
||||
|
||||
resultsHTML += '<p class="success">✓ 特殊字符测试完成</p>';
|
||||
document.getElementById('results-content').innerHTML = resultsHTML;
|
||||
});
|
||||
|
||||
// HTML转义函数
|
||||
function escapeHTML(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\n/g, '<br>');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user