Files
teams-alias/popup.js
2026-05-20 18:51:49 -04:00

153 lines
4.1 KiB
JavaScript

const REQUIRED_ID_FIELD = '账号ID';
const REQUIRED_ALIAS_FIELD = '最终输出';
function setStatus(message) {
document.getElementById('status').textContent = message;
}
function parseCsv(text) {
const rows = [];
let row = [];
let cell = '';
let inQuotes = false;
for (let i = 0; i < text.length; i++) {
const char = text[i];
const nextChar = text[i + 1];
if (char === '"') {
if (inQuotes && nextChar === '"') {
cell += '"';
i++;
} else {
inQuotes = !inQuotes;
}
continue;
}
if (char === ',' && !inQuotes) {
row.push(cell);
cell = '';
continue;
}
if ((char === '\n' || char === '\r') && !inQuotes) {
if (char === '\r' && nextChar === '\n') i++;
row.push(cell);
if (row.some(value => value.trim() !== '')) rows.push(row);
row = [];
cell = '';
continue;
}
cell += char;
}
row.push(cell);
if (row.some(value => value.trim() !== '')) rows.push(row);
return rows;
}
function normalizeHeader(value) {
return value.replace(/^\uFEFF/, '').trim();
}
function aliasesFromCsv(text) {
const rows = parseCsv(text);
if (rows.length === 0) {
throw new Error('CSV 文件为空。');
}
const headers = rows[0].map(normalizeHeader);
const idIndex = headers.indexOf(REQUIRED_ID_FIELD);
const aliasIndex = headers.indexOf(REQUIRED_ALIAS_FIELD);
if (idIndex === -1 || aliasIndex === -1) {
throw new Error(`CSV 必须包含“${REQUIRED_ID_FIELD}”和“${REQUIRED_ALIAS_FIELD}”两列。`);
}
const aliases = {};
rows.slice(1).forEach(row => {
const id = (row[idIndex] || '').trim();
const alias = (row[aliasIndex] || '').trim();
if (id && alias) {
aliases[id] = alias;
}
});
return aliases;
}
function decodeCsvBuffer(buffer, encoding) {
return new TextDecoder(encoding).decode(buffer);
}
function readAliasesFromCsvBuffer(buffer) {
const encodings = ['utf-8', 'gb18030', 'gbk'];
let lastError = null;
for (const encoding of encodings) {
try {
return aliasesFromCsv(decodeCsvBuffer(buffer, encoding));
} catch (error) {
lastError = error;
}
}
throw lastError;
}
// 导入 CSV
document.getElementById('import').addEventListener('click', () => {
document.getElementById('fileInput').click();
});
document.getElementById('fileInput').addEventListener('change', async (event) => {
const file = event.target.files[0];
if (!file) return;
const btn = document.getElementById('import');
const originalText = btn.textContent;
btn.textContent = '导入中...';
btn.disabled = true;
setStatus('');
try {
const buffer = await file.arrayBuffer();
const aliases = readAliasesFromCsvBuffer(buffer);
await chrome.storage.local.set({ aliases, lastImport: Date.now(), sourceFileName: file.name });
setStatus(`已导入 ${Object.keys(aliases).length}`);
alert(`导入成功!共导入 ${Object.keys(aliases).length} 条数据。`);
} catch (error) {
setStatus('导入失败');
alert(error.message || '导入失败,请检查 CSV 文件。');
console.error(error);
} finally {
btn.textContent = originalText;
btn.disabled = false;
event.target.value = '';
}
});
// 导出数据
document.getElementById('export').addEventListener('click', async () => {
const { aliases } = await chrome.storage.local.get('aliases');
const blob = new Blob([JSON.stringify(aliases || {}, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const timestamp = new Date()
.toLocaleString('sv-SE') // ISO-like 格式
.replace(' ', '_')
.replace(/:/g, '-'); // 避免非法文件名
const a = document.createElement('a');
a.href = url;
a.download = `teams-alias-${timestamp}.json`;
a.click();
URL.revokeObjectURL(url);
});