support export
This commit is contained in:
133
content.js
133
content.js
@@ -312,6 +312,96 @@ function toggleAddPanel() {
|
|||||||
renderChatOrganizer();
|
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) {
|
function renderChatCard(chatId, categoryId) {
|
||||||
const meta = getChatMeta(chatId) || { id: chatId, name: chatId, avatarUrl: "" };
|
const meta = getChatMeta(chatId) || { id: chatId, name: chatId, avatarUrl: "" };
|
||||||
const avatar = meta.avatarUrl
|
const avatar = meta.avatarUrl
|
||||||
@@ -502,6 +592,17 @@ function ensureChatOrganizerUI() {
|
|||||||
font-weight: 600;
|
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-category-list,
|
||||||
.teams-alias-visible-list,
|
.teams-alias-visible-list,
|
||||||
.teams-alias-chat-list {
|
.teams-alias-chat-list {
|
||||||
@@ -635,13 +736,6 @@ function ensureChatOrganizerUI() {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.teams-alias-main-title {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #334155;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.teams-alias-modal {
|
.teams-alias-modal {
|
||||||
left: 12px;
|
left: 12px;
|
||||||
@@ -649,6 +743,11 @@ function ensureChatOrganizerUI() {
|
|||||||
width: auto;
|
width: auto;
|
||||||
top: 56px;
|
top: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.teams-alias-toolbar {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
@@ -704,6 +803,16 @@ function ensureChatOrganizerUI() {
|
|||||||
return;
|
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") {
|
if (action === "toggle-category") {
|
||||||
toggleCategoryExpanded(actionElement.getAttribute("data-category-id"));
|
toggleCategoryExpanded(actionElement.getAttribute("data-category-id"));
|
||||||
return;
|
return;
|
||||||
@@ -747,6 +856,12 @@ function ensureChatOrganizerUI() {
|
|||||||
if (target.matches('[data-role="category-select"]')) {
|
if (target.matches('[data-role="category-select"]')) {
|
||||||
chatOrganizerState.selectedCategoryId = target.value;
|
chatOrganizerState.selectedCategoryId = target.value;
|
||||||
renderChatOrganizer();
|
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>
|
<button class="teams-alias-close" data-action="close-modal">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="teams-alias-toolbar">
|
<div class="teams-alias-toolbar">
|
||||||
<p class="teams-alias-main-title">分类是主视图,添加和识别列表作为辅助面板使用。</p>
|
|
||||||
<div class="teams-alias-toolbar-actions">
|
<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-create-panel">${chatOrganizerState.createPanelOpen ? "收起新建" : "新建分类"}</button>
|
||||||
<button class="teams-alias-primary-button" data-action="toggle-add-panel">${chatOrganizerState.addPanelOpen ? "收起添加" : "添加聊天"}</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>
|
</div>
|
||||||
|
<input type="file" accept="application/json,.json" data-role="import-categories-input" class="teams-alias-collapsed">
|
||||||
</div>
|
</div>
|
||||||
<div class="teams-alias-modal-body">
|
<div class="teams-alias-modal-body">
|
||||||
<div class="teams-alias-panel ${chatOrganizerState.createPanelOpen ? "" : "teams-alias-collapsed"}">
|
<div class="teams-alias-panel ${chatOrganizerState.createPanelOpen ? "" : "teams-alias-collapsed"}">
|
||||||
|
|||||||
Reference in New Issue
Block a user