support export
This commit is contained in:
133
content.js
133
content.js
@@ -312,6 +312,96 @@ function toggleAddPanel() {
|
||||
renderChatOrganizer();
|
||||
}
|
||||
|
||||
function normalizeImportedCategories(rawCategories) {
|
||||
if (!Array.isArray(rawCategories)) return [];
|
||||
|
||||
return rawCategories
|
||||
.map(category => {
|
||||
const name = String(category?.name || "").trim();
|
||||
if (!name) return null;
|
||||
|
||||
const chatIds = Array.isArray(category?.chatIds)
|
||||
? Array.from(new Set(category.chatIds.map(chatId => normalizeChatId(chatId)).filter(Boolean)))
|
||||
: [];
|
||||
|
||||
return {
|
||||
id: String(category?.id || createCategoryId()),
|
||||
name,
|
||||
chatIds
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function normalizeImportedChatCache(rawChatCache) {
|
||||
if (!rawChatCache || typeof rawChatCache !== "object") return {};
|
||||
|
||||
const nextCache = {};
|
||||
Object.entries(rawChatCache).forEach(([rawChatId, meta]) => {
|
||||
const chatId = normalizeChatId(rawChatId);
|
||||
if (!chatId) return;
|
||||
|
||||
nextCache[chatId] = {
|
||||
id: chatId,
|
||||
name: String(meta?.name || chatId),
|
||||
avatarUrl: String(meta?.avatarUrl || ""),
|
||||
updatedAt: Number(meta?.updatedAt || Date.now())
|
||||
};
|
||||
});
|
||||
return nextCache;
|
||||
}
|
||||
|
||||
function exportChatCategories() {
|
||||
const payload = {
|
||||
version: 1,
|
||||
exportedAt: new Date().toISOString(),
|
||||
categories: chatOrganizerState.categories,
|
||||
chatCache: chatOrganizerState.chatCache
|
||||
};
|
||||
|
||||
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = url;
|
||||
anchor.download = `teams-chat-categories-${timestamp}.json`;
|
||||
anchor.click();
|
||||
URL.revokeObjectURL(url);
|
||||
setOrganizerFeedback("分类已导出。");
|
||||
}
|
||||
|
||||
async function importChatCategoriesFromFile(file) {
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
const content = await file.text();
|
||||
const parsed = JSON.parse(content);
|
||||
const importedCategories = normalizeImportedCategories(parsed?.categories);
|
||||
const importedChatCache = normalizeImportedChatCache(parsed?.chatCache);
|
||||
|
||||
if (!importedCategories.length) {
|
||||
setOrganizerFeedback("导入失败:文件里没有有效分类。");
|
||||
return;
|
||||
}
|
||||
|
||||
chatOrganizerState.categories = importedCategories;
|
||||
chatOrganizerState.chatCache = {
|
||||
...chatOrganizerState.chatCache,
|
||||
...importedChatCache
|
||||
};
|
||||
chatOrganizerState.expandedCategoryIds = {};
|
||||
importedCategories.forEach(category => {
|
||||
chatOrganizerState.expandedCategoryIds[category.id] = true;
|
||||
});
|
||||
chatOrganizerState.selectedCategoryId = importedCategories[0]?.id || "";
|
||||
scheduleOrganizerStateSave();
|
||||
setOrganizerFeedback(`已导入 ${importedCategories.length} 个分类。`);
|
||||
} catch (error) {
|
||||
console.error("Teams Alias: 导入分类失败", error);
|
||||
setOrganizerFeedback("导入失败:文件格式不是有效的 JSON。");
|
||||
}
|
||||
}
|
||||
|
||||
function renderChatCard(chatId, categoryId) {
|
||||
const meta = getChatMeta(chatId) || { id: chatId, name: chatId, avatarUrl: "" };
|
||||
const avatar = meta.avatarUrl
|
||||
@@ -502,6 +592,17 @@ function ensureChatOrganizerUI() {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.teams-alias-secondary-button {
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 10px;
|
||||
padding: 9px 14px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
color: #0f172a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.teams-alias-category-list,
|
||||
.teams-alias-visible-list,
|
||||
.teams-alias-chat-list {
|
||||
@@ -635,13 +736,6 @@ function ensureChatOrganizerUI() {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.teams-alias-main-title {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.teams-alias-modal {
|
||||
left: 12px;
|
||||
@@ -649,6 +743,11 @@ function ensureChatOrganizerUI() {
|
||||
width: auto;
|
||||
top: 56px;
|
||||
}
|
||||
|
||||
.teams-alias-toolbar {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
@@ -704,6 +803,16 @@ function ensureChatOrganizerUI() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "export-categories") {
|
||||
exportChatCategories();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "trigger-import-categories") {
|
||||
root.querySelector('[data-role="import-categories-input"]')?.click();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "toggle-category") {
|
||||
toggleCategoryExpanded(actionElement.getAttribute("data-category-id"));
|
||||
return;
|
||||
@@ -747,6 +856,12 @@ function ensureChatOrganizerUI() {
|
||||
if (target.matches('[data-role="category-select"]')) {
|
||||
chatOrganizerState.selectedCategoryId = target.value;
|
||||
renderChatOrganizer();
|
||||
return;
|
||||
}
|
||||
if (target.matches('[data-role="import-categories-input"]')) {
|
||||
const file = target.files?.[0];
|
||||
importChatCategoriesFromFile(file);
|
||||
target.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -813,11 +928,13 @@ function renderChatOrganizer() {
|
||||
<button class="teams-alias-close" data-action="close-modal">×</button>
|
||||
</div>
|
||||
<div class="teams-alias-toolbar">
|
||||
<p class="teams-alias-main-title">分类是主视图,添加和识别列表作为辅助面板使用。</p>
|
||||
<div class="teams-alias-toolbar-actions">
|
||||
<button class="teams-alias-primary-button" data-action="toggle-create-panel">${chatOrganizerState.createPanelOpen ? "收起新建" : "新建分类"}</button>
|
||||
<button class="teams-alias-primary-button" data-action="toggle-add-panel">${chatOrganizerState.addPanelOpen ? "收起添加" : "添加聊天"}</button>
|
||||
<button class="teams-alias-secondary-button" data-action="export-categories">导出分类</button>
|
||||
<button class="teams-alias-secondary-button" data-action="trigger-import-categories">导入分类</button>
|
||||
</div>
|
||||
<input type="file" accept="application/json,.json" data-role="import-categories-input" class="teams-alias-collapsed">
|
||||
</div>
|
||||
<div class="teams-alias-modal-body">
|
||||
<div class="teams-alias-panel ${chatOrganizerState.createPanelOpen ? "" : "teams-alias-collapsed"}">
|
||||
|
||||
Reference in New Issue
Block a user