滑动选中
This commit is contained in:
@@ -1208,10 +1208,16 @@ export default {
|
|||||||
hasChange: false,
|
hasChange: false,
|
||||||
hasInit: false,
|
hasInit: false,
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
|
isMouseSelecting: false,
|
||||||
|
_selectionSyncToCheckboxesTimer: null,
|
||||||
|
_onDocumentSelectionChange: null,
|
||||||
|
_onDocumentMouseUp: null,
|
||||||
|
_onManuscriptMouseDown: null,
|
||||||
|
|
||||||
displayList: [],
|
displayList: [],
|
||||||
currentTypeText: '',
|
currentTypeText: '',
|
||||||
tinymceId: this.id || 'vue-tinymce-' + +new Date()
|
tinymceId: this.id || 'vue-tinymce-' + +new Date()
|
||||||
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(newVal));
|
// this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(newVal));
|
||||||
@@ -1274,51 +1280,60 @@ export default {
|
|||||||
});
|
});
|
||||||
this.$refs.scrollDiv.addEventListener('scroll', this.divOnScroll, { passive: true });
|
this.$refs.scrollDiv.addEventListener('scroll', this.divOnScroll, { passive: true });
|
||||||
|
|
||||||
document.addEventListener('selectionchange', () => {
|
// document.addEventListener('selectionchange', () => {
|
||||||
if(this.isPreview)return;
|
// if(this.isPreview)return;
|
||||||
const selection = window.getSelection();
|
// const selection = window.getSelection();
|
||||||
if (selection.rangeCount === 0) return;
|
// if (selection.rangeCount === 0) return;
|
||||||
|
|
||||||
const range = selection.getRangeAt(0);
|
// const range = selection.getRangeAt(0);
|
||||||
// 依然保留 trim() 后的文本判断,用来决定是否显示气泡
|
// // 依然保留 trim() 后的文本判断,用来决定是否显示气泡
|
||||||
const plainText = selection.toString().trim();
|
// const plainText = selection.toString().trim();
|
||||||
|
|
||||||
if (plainText !== '' && selection.rangeCount > 0) {
|
// if (plainText !== '' && selection.rangeCount > 0) {
|
||||||
// --- 1. 获取包含标签的 HTML 内容 ---
|
// // --- 1. 获取包含标签的 HTML 内容 ---
|
||||||
const fragment = range.cloneContents();
|
// const fragment = range.cloneContents();
|
||||||
const tempDiv = document.createElement('div');
|
// const tempDiv = document.createElement('div');
|
||||||
tempDiv.appendChild(fragment);
|
// tempDiv.appendChild(fragment);
|
||||||
// 关键点:这个 label 变量现在包含了完整的 HTML 结构(如 <myfigure>)
|
// // 关键点:这个 label 变量现在包含了完整的 HTML 结构(如 <myfigure>)
|
||||||
const htmlLabel = tempDiv.innerHTML;
|
// const htmlLabel = tempDiv.innerHTML;
|
||||||
const allPMainElements = this.getInvolvedPMain(range);
|
// const allPMainElements = this.getInvolvedPMain(range);
|
||||||
const allIds = [...new Set(allPMainElements.map((el) => el.getAttribute('data-id')))];
|
// const allIds = [...new Set(allPMainElements.map((el) => el.getAttribute('data-id')))];
|
||||||
if (allIds.length > 0) {
|
// if (allIds.length > 0) {
|
||||||
this.updateBubblePosition(range);
|
// this.updateBubblePosition(range);
|
||||||
const rootItem = this.wordList.find((item) => item.am_id == allIds[0]);
|
// const rootItem = this.wordList.find((item) => item.am_id == allIds[0]);
|
||||||
|
|
||||||
this.currentSelection = {
|
// this.currentSelection = {
|
||||||
// 将 label 设置为包含标签的 HTML 字符串
|
// // 将 label 设置为包含标签的 HTML 字符串
|
||||||
label: htmlLabel,
|
// label: htmlLabel,
|
||||||
mainId: allIds[0],
|
// mainId: allIds[0],
|
||||||
index: this.wordList.indexOf(rootItem),
|
// index: this.wordList.indexOf(rootItem),
|
||||||
|
|
||||||
content: rootItem ? rootItem.content : ''
|
// content: rootItem ? rootItem.content : ''
|
||||||
};
|
// };
|
||||||
|
|
||||||
|
// this.currentId = allIds[0];
|
||||||
|
// this.currentData = rootItem;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// this.currentTag = '';
|
||||||
|
// this.currentTagData = {};
|
||||||
|
// this.currentSelection = {
|
||||||
|
// label: '',
|
||||||
|
// mainId: '',
|
||||||
|
// index: 0,
|
||||||
|
// content: {}
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
this._onDocumentSelectionChange = this.handleDocumentSelectionChange.bind(this);
|
||||||
|
this._onDocumentMouseUp = this.handleDocumentMouseUp.bind(this);
|
||||||
|
this._onManuscriptMouseDown = this.handleManuscriptMouseDown.bind(this);
|
||||||
|
document.addEventListener('selectionchange', this._onDocumentSelectionChange);
|
||||||
|
document.addEventListener('mouseup', this._onDocumentMouseUp);
|
||||||
|
if (this.$refs.scroll) {
|
||||||
|
this.$refs.scroll.addEventListener('mousedown', this._onManuscriptMouseDown);
|
||||||
|
}
|
||||||
|
|
||||||
this.currentId = allIds[0];
|
|
||||||
this.currentData = rootItem;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.currentTag = '';
|
|
||||||
this.currentTagData = {};
|
|
||||||
this.currentSelection = {
|
|
||||||
label: '',
|
|
||||||
mainId: '',
|
|
||||||
index: 0,
|
|
||||||
content: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
// 主动触发 MathJax 渲染
|
// 主动触发 MathJax 渲染
|
||||||
@@ -1378,6 +1393,115 @@ export default {
|
|||||||
|
|
||||||
return rangePs;
|
return rangePs;
|
||||||
},
|
},
|
||||||
|
handleManuscriptMouseDown(event) {
|
||||||
|
if (this.isPreview) return;
|
||||||
|
const root = this.$refs && this.$refs.scroll;
|
||||||
|
if (!root) return;
|
||||||
|
if (root.contains(event.target)) {
|
||||||
|
this.isMouseSelecting = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDocumentMouseUp() {
|
||||||
|
this.isMouseSelecting = false;
|
||||||
|
},
|
||||||
|
handleDocumentSelectionChange() {
|
||||||
|
if (this.isPreview) return;
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (!selection || selection.rangeCount === 0) return;
|
||||||
|
|
||||||
|
const range = selection.getRangeAt(0);
|
||||||
|
const plainText = selection.toString().trim();
|
||||||
|
|
||||||
|
if (plainText !== '' && selection.rangeCount > 0) {
|
||||||
|
const fragment = range.cloneContents();
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.appendChild(fragment);
|
||||||
|
const htmlLabel = tempDiv.innerHTML;
|
||||||
|
const allPMainElements = this.getInvolvedPMain(range);
|
||||||
|
const allIds = [...new Set(allPMainElements.map((el) => el.getAttribute('data-id')))];
|
||||||
|
if (allIds.length > 0) {
|
||||||
|
this.updateBubblePosition(range);
|
||||||
|
const rootItem = this.wordList.find((item) => item.am_id == allIds[0]);
|
||||||
|
|
||||||
|
this.currentSelection = {
|
||||||
|
label: htmlLabel,
|
||||||
|
mainId: allIds[0],
|
||||||
|
index: this.wordList.indexOf(rootItem),
|
||||||
|
content: rootItem ? rootItem.content : ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this.currentId = allIds[0];
|
||||||
|
this.currentData = rootItem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.currentTag = '';
|
||||||
|
this.currentTagData = {};
|
||||||
|
this.currentSelection = {
|
||||||
|
label: '',
|
||||||
|
mainId: '',
|
||||||
|
index: 0,
|
||||||
|
content: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.scheduleSyncSelectedIdsFromRange();
|
||||||
|
},
|
||||||
|
scheduleSyncSelectedIdsFromRange() {
|
||||||
|
if (this._selectionSyncToCheckboxesTimer) {
|
||||||
|
clearTimeout(this._selectionSyncToCheckboxesTimer);
|
||||||
|
}
|
||||||
|
this._selectionSyncToCheckboxesTimer = setTimeout(() => {
|
||||||
|
this.syncSelectedIdsFromRangeInternal();
|
||||||
|
}, 80);
|
||||||
|
},
|
||||||
|
syncSelectedIdsFromRangeInternal() {
|
||||||
|
if (this.isPreview || this.isInternalAction) return;
|
||||||
|
const scrollRoot = this.$refs && this.$refs.scroll;
|
||||||
|
if (!scrollRoot) return;
|
||||||
|
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
|
||||||
|
const range = selection.getRangeAt(0);
|
||||||
|
const common = range.commonAncestorContainer;
|
||||||
|
const commonElement = common && common.nodeType === 1 ? common : common && common.parentElement;
|
||||||
|
if (!commonElement || !scrollRoot.contains(commonElement)) return;
|
||||||
|
|
||||||
|
const nodes = Array.from(scrollRoot.querySelectorAll('.drop-target[main-id]'));
|
||||||
|
const touchedMainIds = [];
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
const id = node.getAttribute('main-id');
|
||||||
|
if (!id || id === 'References') return;
|
||||||
|
try {
|
||||||
|
if (range.intersectsNode(node)) {
|
||||||
|
touchedMainIds.push(id);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
if (!touchedMainIds.length) return;
|
||||||
|
|
||||||
|
const indexMap = {};
|
||||||
|
this.wordList.forEach((item, idx) => {
|
||||||
|
if (item && item.am_id != null) {
|
||||||
|
indexMap[String(item.am_id)] = idx;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const indices = [...new Set(touchedMainIds.map((id) => indexMap[String(id)]).filter((v) => Number.isInteger(v)))];
|
||||||
|
if (!indices.length) return;
|
||||||
|
const lo = Math.min(...indices);
|
||||||
|
const hi = Math.max(...indices);
|
||||||
|
const rangeIds = this.wordList
|
||||||
|
.slice(lo, hi + 1)
|
||||||
|
.map((item) => (item && item.am_id != null ? item.am_id : null))
|
||||||
|
.filter((id) => id != null);
|
||||||
|
if (!rangeIds.length) return;
|
||||||
|
|
||||||
|
const existingSet = new Set(this.selectedIds || []);
|
||||||
|
rangeIds.forEach((id) => existingSet.add(id));
|
||||||
|
const ordered = this.wordList
|
||||||
|
.map((item) => (item && item.am_id != null ? item.am_id : null))
|
||||||
|
.filter((id) => id != null && existingSet.has(id));
|
||||||
|
this.selectedIds = ordered;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
|
||||||
handleUnbindLink(type) {
|
handleUnbindLink(type) {
|
||||||
const rootItem = this.wordList.find((item) => item.am_id == this.currentTagData.main_id);
|
const rootItem = this.wordList.find((item) => item.am_id == this.currentTagData.main_id);
|
||||||
|
|||||||
Reference in New Issue
Block a user