mirror of
https://github.com/woodchen-ink/obsidian-publish-to-discourse.git
synced 2025-07-17 21:32:05 +08:00
支持标签组功能,支持直接从列表多选标签。
This commit is contained in:
parent
87a903b4b5
commit
951eba6bc5
35
src/api.ts
35
src/api.ts
@ -303,12 +303,41 @@ export class DiscourseAPI {
|
||||
|
||||
if (data && data.tags) {
|
||||
const canCreateTags = await this.checkCanCreateTags();
|
||||
|
||||
// 处理所有标签(包括tag_groups中的标签)
|
||||
const allTags = new Map<string, { name: string; count: number }>();
|
||||
|
||||
// 添加普通标签
|
||||
data.tags.forEach((tag: any) => {
|
||||
tags.push({
|
||||
allTags.set(tag.name, { name: tag.name, count: tag.count || 0 });
|
||||
});
|
||||
|
||||
// 添加tag_groups中的标签
|
||||
if (data.extras && data.extras.tag_groups) {
|
||||
data.extras.tag_groups.forEach((group: any) => {
|
||||
if (group.tags) {
|
||||
group.tags.forEach((tag: any) => {
|
||||
// 如果标签已存在,取较大的count值
|
||||
const existing = allTags.get(tag.name);
|
||||
if (existing) {
|
||||
existing.count = Math.max(existing.count, tag.count || 0);
|
||||
} else {
|
||||
allTags.set(tag.name, { name: tag.name, count: tag.count || 0 });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 按count数量排序,转换为最终格式
|
||||
const sortedTags = Array.from(allTags.values())
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.map(tag => ({
|
||||
name: tag.name,
|
||||
canCreate: canCreateTags
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
tags.push(...sortedTags);
|
||||
}
|
||||
|
||||
return tags;
|
||||
|
54
src/ui.ts
54
src/ui.ts
@ -82,6 +82,7 @@ export class SelectCategoryModal extends Modal {
|
||||
removeBtn.onclick = () => {
|
||||
selectedTags.delete(tag);
|
||||
updateSelectedTags();
|
||||
showDefaultTags(); // 重新显示网格,让移除的标签重新出现
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -101,18 +102,47 @@ export class SelectCategoryModal extends Modal {
|
||||
// 创建标签建议容器
|
||||
const tagSuggestions = tagInputContainer.createEl('div', { cls: 'tag-suggestions' });
|
||||
|
||||
// 创建默认标签网格容器
|
||||
const defaultTagsGrid = tagInputContainer.createEl('div', { cls: 'default-tags-grid' });
|
||||
|
||||
// 显示默认标签网格
|
||||
const showDefaultTags = () => {
|
||||
defaultTagsGrid.empty();
|
||||
const availableTags = this.tags.filter(tag => !selectedTags.has(tag.name)).slice(0, 20);
|
||||
|
||||
if (availableTags.length > 0) {
|
||||
availableTags.forEach(tag => {
|
||||
const tagEl = defaultTagsGrid.createEl('span', {
|
||||
cls: 'grid-tag',
|
||||
text: tag.name
|
||||
});
|
||||
tagEl.onclick = () => {
|
||||
selectedTags.add(tag.name);
|
||||
updateSelectedTags();
|
||||
showDefaultTags(); // 重新显示网格,移除已选标签
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化显示默认标签网格
|
||||
showDefaultTags();
|
||||
|
||||
// 处理输入事件,显示匹配的标签
|
||||
tagInput.oninput = () => {
|
||||
const value = tagInput.value.toLowerCase();
|
||||
tagSuggestions.empty();
|
||||
|
||||
if (value) {
|
||||
// 有输入时隐藏默认网格,显示搜索结果
|
||||
defaultTagsGrid.style.display = 'none';
|
||||
tagSuggestions.empty();
|
||||
|
||||
const matches = this.tags
|
||||
.filter(tag =>
|
||||
tag.name.toLowerCase().includes(value) &&
|
||||
!selectedTags.has(tag.name)
|
||||
)
|
||||
.slice(0, 10);
|
||||
.slice(0, 20); // 搜索结果显示更多
|
||||
|
||||
if (matches.length > 0) {
|
||||
// 获取输入框位置和宽度
|
||||
@ -126,6 +156,7 @@ export class SelectCategoryModal extends Modal {
|
||||
tagSuggestions.style.top = `${inputRect.bottom + 4}px`;
|
||||
tagSuggestions.style.left = `${inputRect.left}px`;
|
||||
tagSuggestions.style.width = `${Math.min(inputRect.width, maxWidth)}px`;
|
||||
tagSuggestions.style.display = 'block';
|
||||
|
||||
matches.forEach(tag => {
|
||||
const suggestion = tagSuggestions.createEl('div', {
|
||||
@ -135,11 +166,20 @@ export class SelectCategoryModal extends Modal {
|
||||
suggestion.onclick = () => {
|
||||
selectedTags.add(tag.name);
|
||||
tagInput.value = '';
|
||||
tagSuggestions.empty();
|
||||
tagSuggestions.style.display = 'none';
|
||||
defaultTagsGrid.style.display = 'grid';
|
||||
updateSelectedTags();
|
||||
showDefaultTags();
|
||||
};
|
||||
});
|
||||
} else {
|
||||
tagSuggestions.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
// 无输入时显示默认网格,隐藏搜索结果
|
||||
tagSuggestions.style.display = 'none';
|
||||
defaultTagsGrid.style.display = 'grid';
|
||||
showDefaultTags();
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,16 +193,19 @@ export class SelectCategoryModal extends Modal {
|
||||
if (existingTag) {
|
||||
selectedTags.add(existingTag.name);
|
||||
updateSelectedTags();
|
||||
showDefaultTags();
|
||||
} else if (this.canCreateTags) {
|
||||
selectedTags.add(value);
|
||||
updateSelectedTags();
|
||||
showDefaultTags();
|
||||
} else {
|
||||
// 显示权限提示
|
||||
new Notice(t('PERMISSION_ERROR'), 3000);
|
||||
}
|
||||
}
|
||||
tagInput.value = '';
|
||||
tagSuggestions.empty();
|
||||
tagSuggestions.style.display = 'none';
|
||||
defaultTagsGrid.style.display = 'grid';
|
||||
}
|
||||
};
|
||||
|
||||
@ -170,7 +213,8 @@ export class SelectCategoryModal extends Modal {
|
||||
tagInput.onblur = () => {
|
||||
// 延迟隐藏,以便可以点击建议
|
||||
setTimeout(() => {
|
||||
tagSuggestions.empty();
|
||||
tagSuggestions.style.display = 'none';
|
||||
defaultTagsGrid.style.display = 'grid';
|
||||
}, 200);
|
||||
};
|
||||
|
||||
|
75
styles.css
75
styles.css
@ -199,42 +199,91 @@ If your plugin does not need CSS, delete this file.
|
||||
.discourse-sync-modal .tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
padding: 4px 8px;
|
||||
border-radius: var(--radius-s);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid var(--interactive-accent);
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.discourse-sync-modal .tag:hover {
|
||||
background-color: var(--interactive-accent-hover);
|
||||
border-color: var(--interactive-accent-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-s);
|
||||
}
|
||||
|
||||
.discourse-sync-modal .remove-tag {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
line-height: 1;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--background-modifier-hover);
|
||||
margin-left: 4px;
|
||||
transition: all 0.2s ease;
|
||||
opacity: 0.7;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.discourse-sync-modal .remove-tag:hover {
|
||||
background-color: var(--background-modifier-error);
|
||||
color: var(--text-on-accent);
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 默认标签网格样式 */
|
||||
.discourse-sync-modal .default-tags-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: var(--radius-s);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.discourse-sync-modal .grid-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
padding: 6px 8px;
|
||||
border-radius: var(--radius-s);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
text-align: center;
|
||||
min-height: 28px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.discourse-sync-modal .grid-tag:hover {
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border-color: var(--interactive-accent);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-s);
|
||||
}
|
||||
|
||||
.discourse-sync-modal .grid-tag:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-xs);
|
||||
}
|
||||
|
||||
.discourse-sync-modal input[type="text"] {
|
||||
|
Loading…
x
Reference in New Issue
Block a user