mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-09-22 00:20:47 +02:00
Compare commits
11 commits
fe9aba122c
...
71ac6d648b
Author | SHA1 | Date | |
---|---|---|---|
![]() |
71ac6d648b | ||
![]() |
4014d1ecdc | ||
![]() |
1344859c1e | ||
![]() |
a8b5c513ae | ||
![]() |
e2f0f241f8 | ||
![]() |
bd5dea7424 | ||
![]() |
812dadb452 | ||
![]() |
66125f4b1d | ||
![]() |
7a249761e2 | ||
![]() |
3f7421a393 | ||
![]() |
b8e67bec2d |
24 changed files with 287 additions and 81 deletions
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "سيؤدي تطهير التخزين السحابي إلى حذف جميع اللقطات غير المرجعية وعناصر البيانات ذات الصلة. <ul class='fn__list'><li>الرجاء التأكد من أن الأجهزة الأخرى توقفت عن المزامنة قبل التنفيذ</li><li>عملية التطهير تستغرق وقتاً طويلاً جداً، الرجاء التأكد من أن الشبكة مستقرة</li></ul>هل أنت متأكد من تنفيذها الآن؟",
|
"cloudStoragePurgeConfirm": "سيؤدي تطهير التخزين السحابي إلى حذف جميع اللقطات غير المرجعية وعناصر البيانات ذات الصلة. <ul class='fn__list'><li>الرجاء التأكد من أن الأجهزة الأخرى توقفت عن المزامنة قبل التنفيذ</li><li>عملية التطهير تستغرق وقتاً طويلاً جداً، الرجاء التأكد من أن الشبكة مستقرة</li></ul>هل أنت متأكد من تنفيذها الآن؟",
|
||||||
"dragFill": "اسحب عمودياً لملء القيمة",
|
"dragFill": "اسحب عمودياً لملء القيمة",
|
||||||
"switchReadonly": "تبديل وضع القراءة فقط",
|
"switchReadonly": "تبديل وضع القراءة فقط",
|
||||||
"original": "القيمة الأصلية",
|
"original": "عرض القيمة الأصلية",
|
||||||
|
"uniqueValues": "عرض القيم الفريدة",
|
||||||
"selectRelation": "الرجاء تحديد الحقل ذي الصلة أولاً",
|
"selectRelation": "الرجاء تحديد الحقل ذي الصلة أولاً",
|
||||||
"backRelation": "ثنائي الاتجاه",
|
"backRelation": "ثنائي الاتجاه",
|
||||||
"thisDatabase": "قاعدة البيانات هذه",
|
"thisDatabase": "قاعدة البيانات هذه",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Das Bereinigen des Cloud-Speichers löscht alle nicht referenzierten Schnappschüsse und zugehörigen Datenobjekte vollständig. <ul class='fn__list'><li>Bitte stellen Sie sicher, dass andere Geräte die Synchronisation pausiert haben, bevor Sie fortfahren</li><li>Der Bereinigungsvorgang kann sehr zeitaufwendig sein, bitte stellen Sie sicher, dass das Netzwerk stabil ist</li></ul>Sind Sie sicher, dass Sie es jetzt ausführen möchten?",
|
"cloudStoragePurgeConfirm": "Das Bereinigen des Cloud-Speichers löscht alle nicht referenzierten Schnappschüsse und zugehörigen Datenobjekte vollständig. <ul class='fn__list'><li>Bitte stellen Sie sicher, dass andere Geräte die Synchronisation pausiert haben, bevor Sie fortfahren</li><li>Der Bereinigungsvorgang kann sehr zeitaufwendig sein, bitte stellen Sie sicher, dass das Netzwerk stabil ist</li></ul>Sind Sie sicher, dass Sie es jetzt ausführen möchten?",
|
||||||
"dragFill": "Vertikal ziehen, um Werte zu füllen",
|
"dragFill": "Vertikal ziehen, um Werte zu füllen",
|
||||||
"switchReadonly": "In den Nur-Lesen-Modus wechseln",
|
"switchReadonly": "In den Nur-Lesen-Modus wechseln",
|
||||||
"original": "Original",
|
"original": "Originalwert anzeigen",
|
||||||
|
"uniqueValues": "Eindeutige Werte anzeigen",
|
||||||
"selectRelation": "Bitte wählen Sie zuerst die zugehörige Spalte aus",
|
"selectRelation": "Bitte wählen Sie zuerst die zugehörige Spalte aus",
|
||||||
"backRelation": "Bidirektional",
|
"backRelation": "Bidirektional",
|
||||||
"thisDatabase": "Diese Datenbank",
|
"thisDatabase": "Diese Datenbank",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Purging the cloud storage will completely delete all unreferenced snapshots and related data objects. <ul class='fn__list'><li>Please ensure that other devices have paused sync before execution</li><li>The purge operation is very time-consuming, please ensure that the network is stable</li></ul>Are you sure to execute it now?",
|
"cloudStoragePurgeConfirm": "Purging the cloud storage will completely delete all unreferenced snapshots and related data objects. <ul class='fn__list'><li>Please ensure that other devices have paused sync before execution</li><li>The purge operation is very time-consuming, please ensure that the network is stable</li></ul>Are you sure to execute it now?",
|
||||||
"dragFill": "Drag vertically to fill value",
|
"dragFill": "Drag vertically to fill value",
|
||||||
"switchReadonly": "Switch read-only mode",
|
"switchReadonly": "Switch read-only mode",
|
||||||
"original": "Original",
|
"original": "Show original values",
|
||||||
|
"uniqueValues": "Show unique values",
|
||||||
"selectRelation": "Please select the related field first",
|
"selectRelation": "Please select the related field first",
|
||||||
"backRelation": "Bidirectional",
|
"backRelation": "Bidirectional",
|
||||||
"thisDatabase": "This database",
|
"thisDatabase": "This database",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Al purgar el almacenamiento en la nube se eliminarán por completo todas las instantáneas sin referencia y los objetos de datos relacionados.<ul class='fn__list'><li>Asegúrese de que otros dispositivos hayan pausado la sincronización antes de la ejecución</li><li>La operación de purga requiere mucho tiempo; asegúrese de que la red esté estable</li></ul>¿Está seguro de ejecutarla ahora?",
|
"cloudStoragePurgeConfirm": "Al purgar el almacenamiento en la nube se eliminarán por completo todas las instantáneas sin referencia y los objetos de datos relacionados.<ul class='fn__list'><li>Asegúrese de que otros dispositivos hayan pausado la sincronización antes de la ejecución</li><li>La operación de purga requiere mucho tiempo; asegúrese de que la red esté estable</li></ul>¿Está seguro de ejecutarla ahora?",
|
||||||
"dragFill": "Arrastra verticalmente para llenar valores",
|
"dragFill": "Arrastra verticalmente para llenar valores",
|
||||||
"switchReadonly": "Cambiar modo de sólo lectura",
|
"switchReadonly": "Cambiar modo de sólo lectura",
|
||||||
"original": "Original",
|
"original": "Mostrar valor original",
|
||||||
|
"uniqueValues": "Mostrar valores únicos",
|
||||||
"selectRelation": "Seleccione primero la columna relacionada",
|
"selectRelation": "Seleccione primero la columna relacionada",
|
||||||
"backRelation": "Bidireccional",
|
"backRelation": "Bidireccional",
|
||||||
"thisDatabase": "Esta base de datos",
|
"thisDatabase": "Esta base de datos",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "La purge du stockage cloud supprimera complètement tous les instantanés non référencés et les objets de données associés. <ul class='fn__list'><li>Veuillez vous assurer que la synchronisation des autres appareils a été suspendue avant l'exécution</li><li>L'opération de purge prend beaucoup de temps, veuillez vous assurer que le réseau est stable</li></ul>Êtes-vous sûr de l'exécuter maintenant ?",
|
"cloudStoragePurgeConfirm": "La purge du stockage cloud supprimera complètement tous les instantanés non référencés et les objets de données associés. <ul class='fn__list'><li>Veuillez vous assurer que la synchronisation des autres appareils a été suspendue avant l'exécution</li><li>L'opération de purge prend beaucoup de temps, veuillez vous assurer que le réseau est stable</li></ul>Êtes-vous sûr de l'exécuter maintenant ?",
|
||||||
"dragFill": "Faites glisser verticalement pour remplir les valeurs",
|
"dragFill": "Faites glisser verticalement pour remplir les valeurs",
|
||||||
"switchReadonly": "Changer de mode lecture seule",
|
"switchReadonly": "Changer de mode lecture seule",
|
||||||
"original": "Originale",
|
"original": "Afficher la valeur d'origine",
|
||||||
|
"uniqueValues": "Afficher les valeurs uniques",
|
||||||
"selectRelation": "Veuillez d'abord sélectionner la colonne associée",
|
"selectRelation": "Veuillez d'abord sélectionner la colonne associée",
|
||||||
"backRelation": "Bidirectionnel",
|
"backRelation": "Bidirectionnel",
|
||||||
"thisDatabase": "Cette base de données",
|
"thisDatabase": "Cette base de données",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "ניקוי האחסון בענן ימחוק לחלוטין את כל הצילומים הלא מתייחסים וכל האובייקטים הנתונים הקשורים. <ul class='fn__list'><li>אנא ודא שהמכשירים האחרים הפסיקו סנכרון לפני הביצוע</li><li>הניקוי מאוד לוקח זמן, אנא ודא שהרשת יציבה</li></ul>האם אתה בטוח לבצע את זה עכשיו?",
|
"cloudStoragePurgeConfirm": "ניקוי האחסון בענן ימחוק לחלוטין את כל הצילומים הלא מתייחסים וכל האובייקטים הנתונים הקשורים. <ul class='fn__list'><li>אנא ודא שהמכשירים האחרים הפסיקו סנכרון לפני הביצוע</li><li>הניקוי מאוד לוקח זמן, אנא ודא שהרשת יציבה</li></ul>האם אתה בטוח לבצע את זה עכשיו?",
|
||||||
"dragFill": "גרור אנכית כדי למלא ערכים",
|
"dragFill": "גרור אנכית כדי למלא ערכים",
|
||||||
"switchReadonly": "עבר למצב קריאה בלבד",
|
"switchReadonly": "עבר למצב קריאה בלבד",
|
||||||
"original": "מקורי",
|
"original": "הצג ערך מקורי",
|
||||||
|
"uniqueValues": "הצג ערכים ייחודיים",
|
||||||
"selectRelation": "אנא בחר קודם את העמודה הקשורה",
|
"selectRelation": "אנא בחר קודם את העמודה הקשורה",
|
||||||
"backRelation": "דו-כיווני",
|
"backRelation": "דו-כיווני",
|
||||||
"thisDatabase": "בסיס הנתונים הזה",
|
"thisDatabase": "בסיס הנתונים הזה",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "La pulizia dello storage cloud eliminerà completamente tutti gli snapshot non referenziati e gli oggetti dati correlati. <ul class='fn__list'><li>Assicurati che gli altri dispositivi abbiano sospeso la sincronizzazione prima dell'esecuzione</li><li>L'operazione di pulizia è molto lunga, assicurati che la rete sia stabile</li></ul>Sei sicuro di voler eseguire ora?",
|
"cloudStoragePurgeConfirm": "La pulizia dello storage cloud eliminerà completamente tutti gli snapshot non referenziati e gli oggetti dati correlati. <ul class='fn__list'><li>Assicurati che gli altri dispositivi abbiano sospeso la sincronizzazione prima dell'esecuzione</li><li>L'operazione di pulizia è molto lunga, assicurati che la rete sia stabile</li></ul>Sei sicuro di voler eseguire ora?",
|
||||||
"dragFill": "Trascina verticalmente per riempire i valori",
|
"dragFill": "Trascina verticalmente per riempire i valori",
|
||||||
"switchReadonly": "Passa alla modalità di sola lettura",
|
"switchReadonly": "Passa alla modalità di sola lettura",
|
||||||
"original": "Originale",
|
"original": "Mostra valore originale",
|
||||||
|
"uniqueValues": "Mostra valori unici",
|
||||||
"selectRelation": "Seleziona prima il campo correlato",
|
"selectRelation": "Seleziona prima il campo correlato",
|
||||||
"backRelation": "Bidirezionale",
|
"backRelation": "Bidirezionale",
|
||||||
"thisDatabase": "Questo database",
|
"thisDatabase": "Questo database",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "クラウドストレージを消去すると、参照されていないスナップショットと関連データオブジェクトが完全に削除されます。<ul class='fn__list'><li>実行前に他のデバイスが同期を一時停止していることを確認してください</li><li>消去操作は非常に時間がかかるため、ネットワークが安定していることを確認してください</li></ul>今すぐ実行してもよろしいですか?",
|
"cloudStoragePurgeConfirm": "クラウドストレージを消去すると、参照されていないスナップショットと関連データオブジェクトが完全に削除されます。<ul class='fn__list'><li>実行前に他のデバイスが同期を一時停止していることを確認してください</li><li>消去操作は非常に時間がかかるため、ネットワークが安定していることを確認してください</li></ul>今すぐ実行してもよろしいですか?",
|
||||||
"dragFill": "値を埋めるために垂直にドラッグ",
|
"dragFill": "値を埋めるために垂直にドラッグ",
|
||||||
"switchReadonly": "読み取り専用モードの切り替え",
|
"switchReadonly": "読み取り専用モードの切り替え",
|
||||||
"original": "元の値",
|
"original": "元の値を表示",
|
||||||
|
"uniqueValues": "一意の値を表示",
|
||||||
"selectRelation": "最初に関連する列を選択してください",
|
"selectRelation": "最初に関連する列を選択してください",
|
||||||
"backRelation": "双方向の関連付け",
|
"backRelation": "双方向の関連付け",
|
||||||
"thisDatabase": "現在のデータベース",
|
"thisDatabase": "現在のデータベース",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Oczyszczenie pamięci w chmurze całkowicie usunie wszystkie nieodwołane zrzuty i powiązane obiekty danych. <ul class='fn__list'><li>Proszę upewnić się, że inne urządzenia wstrzymały synchronizację przed aktem</li><li>Operacja oczyszczania jest czasochłonna, proszę upewnić się, że sieć jest stabilna</li></ul>Czy na pewno chcesz to wykonać teraz?",
|
"cloudStoragePurgeConfirm": "Oczyszczenie pamięci w chmurze całkowicie usunie wszystkie nieodwołane zrzuty i powiązane obiekty danych. <ul class='fn__list'><li>Proszę upewnić się, że inne urządzenia wstrzymały synchronizację przed aktem</li><li>Operacja oczyszczania jest czasochłonna, proszę upewnić się, że sieć jest stabilna</li></ul>Czy na pewno chcesz to wykonać teraz?",
|
||||||
"dragFill": "Przeciągnij w pionie, aby wypełnić wartości",
|
"dragFill": "Przeciągnij w pionie, aby wypełnić wartości",
|
||||||
"switchReadonly": "Przełącz tryb tylko do odczytu",
|
"switchReadonly": "Przełącz tryb tylko do odczytu",
|
||||||
"original": "Oryginalny",
|
"original": "Pokaż wartość oryginalną",
|
||||||
|
"uniqueValues": "Pokaż unikalne wartości",
|
||||||
"selectRelation": "Proszę najpierw wybrać powiązaną kolumnę",
|
"selectRelation": "Proszę najpierw wybrać powiązaną kolumnę",
|
||||||
"backRelation": "Dwukierunkowa",
|
"backRelation": "Dwukierunkowa",
|
||||||
"thisDatabase": "Ta baza danych",
|
"thisDatabase": "Ta baza danych",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Limpar o armazenamento em nuvem excluirá completamente todos os instantâneos não referenciados e objetos de dados relacionados. <ul class='fn__list'><li>Certifique-se de que outros dispositivos pausaram a sincronização antes da execução</li><li>A operação de limpeza é muito demorada, certifique-se de que a rede está estável</li></ul>Tem certeza que deseja executar agora?",
|
"cloudStoragePurgeConfirm": "Limpar o armazenamento em nuvem excluirá completamente todos os instantâneos não referenciados e objetos de dados relacionados. <ul class='fn__list'><li>Certifique-se de que outros dispositivos pausaram a sincronização antes da execução</li><li>A operação de limpeza é muito demorada, certifique-se de que a rede está estável</li></ul>Tem certeza que deseja executar agora?",
|
||||||
"dragFill": "Arrastar verticalmente para preencher valor",
|
"dragFill": "Arrastar verticalmente para preencher valor",
|
||||||
"switchReadonly": "Alternar modo somente leitura",
|
"switchReadonly": "Alternar modo somente leitura",
|
||||||
"original": "Original",
|
"original": "Exibir valor original",
|
||||||
|
"uniqueValues": "Exibir valores únicos",
|
||||||
"selectRelation": "Por favor, selecione o campo relacionado primeiro",
|
"selectRelation": "Por favor, selecione o campo relacionado primeiro",
|
||||||
"backRelation": "Bidirecional",
|
"backRelation": "Bidirecional",
|
||||||
"thisDatabase": "Este banco de dados",
|
"thisDatabase": "Este banco de dados",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Очистка облачного хранилища полностью удалит все неиспользуемые снимки и связанные с ними объекты данных. <ul class='fn__list'><li>Пожалуйста, убедитесь, что другие устройства приостановили синхронизацию перед выполнением</li><li>Операция очистки занимает много времени, пожалуйста, убедитесь, что сеть стабильна</li></ul>Вы уверены, что хотите выполнить её сейчас?",
|
"cloudStoragePurgeConfirm": "Очистка облачного хранилища полностью удалит все неиспользуемые снимки и связанные с ними объекты данных. <ul class='fn__list'><li>Пожалуйста, убедитесь, что другие устройства приостановили синхронизацию перед выполнением</li><li>Операция очистки занимает много времени, пожалуйста, убедитесь, что сеть стабильна</li></ul>Вы уверены, что хотите выполнить её сейчас?",
|
||||||
"dragFill": "Перетащите вертикально, чтобы заполнить значение",
|
"dragFill": "Перетащите вертикально, чтобы заполнить значение",
|
||||||
"switchReadonly": "Переключить режим только для чтения",
|
"switchReadonly": "Переключить режим только для чтения",
|
||||||
"original": "Оригинал",
|
"original": "Показать исходное значение",
|
||||||
|
"uniqueValues": "Показать уникальные значения",
|
||||||
"selectRelation": "Пожалуйста, сначала выберите связанную колонку",
|
"selectRelation": "Пожалуйста, сначала выберите связанную колонку",
|
||||||
"backRelation": "Двустороннее",
|
"backRelation": "Двустороннее",
|
||||||
"thisDatabase": "Эта база данных",
|
"thisDatabase": "Эта база данных",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "清理雲端儲存會徹底刪除所有未引用的快照和相關資料物件。<ul class='fn__list'><li>執行前請確保其他設備已經暫停同步</li><li>清理作業非常耗時,請確保網路穩定</li></ul>確定現在就執行嗎?",
|
"cloudStoragePurgeConfirm": "清理雲端儲存會徹底刪除所有未引用的快照和相關資料物件。<ul class='fn__list'><li>執行前請確保其他設備已經暫停同步</li><li>清理作業非常耗時,請確保網路穩定</li></ul>確定現在就執行嗎?",
|
||||||
"dragFill": "垂直拖動以填充值",
|
"dragFill": "垂直拖動以填充值",
|
||||||
"switchReadonly": "唯讀模式切換",
|
"switchReadonly": "唯讀模式切換",
|
||||||
"original": "原值",
|
"original": "顯示原始值",
|
||||||
|
"uniqueValues": "顯示唯一值",
|
||||||
"selectRelation": "請先選擇關聯欄位",
|
"selectRelation": "請先選擇關聯欄位",
|
||||||
"backRelation": "雙向關聯",
|
"backRelation": "雙向關聯",
|
||||||
"thisDatabase": "目前資料庫",
|
"thisDatabase": "目前資料庫",
|
||||||
|
|
|
@ -170,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "清理云端存储会彻底删除所有未引用的快照和相关数据对象。<ul class='fn__list'><li>执行前请确保其他设备已经暂停同步</li><li>清理操作非常耗时,请确保网络稳定</li></ul> 确定现在就执行吗?",
|
"cloudStoragePurgeConfirm": "清理云端存储会彻底删除所有未引用的快照和相关数据对象。<ul class='fn__list'><li>执行前请确保其他设备已经暂停同步</li><li>清理操作非常耗时,请确保网络稳定</li></ul> 确定现在就执行吗?",
|
||||||
"dragFill": "垂直拖动以填充值",
|
"dragFill": "垂直拖动以填充值",
|
||||||
"switchReadonly": "只读模式切换",
|
"switchReadonly": "只读模式切换",
|
||||||
"original": "原值",
|
"original": "显示原始值",
|
||||||
|
"uniqueValues": "显示唯一值",
|
||||||
"selectRelation": "请先选择关联字段",
|
"selectRelation": "请先选择关联字段",
|
||||||
"backRelation": "双向关联",
|
"backRelation": "双向关联",
|
||||||
"thisDatabase": "当前数据库",
|
"thisDatabase": "当前数据库",
|
||||||
|
|
|
@ -131,6 +131,20 @@ export const openCalcMenu = async (protyle: IProtyle, calcElement: HTMLElement,
|
||||||
blockID,
|
blockID,
|
||||||
target: calcElement
|
target: calcElement
|
||||||
});
|
});
|
||||||
|
if (panelData?.data) {
|
||||||
|
// 汇总字段汇总方式中才有“显示唯一值”选项 Add "Show unique values" to the calculation of the database rollup field https://github.com/siyuan-note/siyuan/issues/15852
|
||||||
|
calcItem({
|
||||||
|
menu,
|
||||||
|
protyle,
|
||||||
|
colId,
|
||||||
|
avId,
|
||||||
|
oldOperator,
|
||||||
|
operator: "Unique values",
|
||||||
|
data: panelData?.data,
|
||||||
|
blockID,
|
||||||
|
target: calcElement
|
||||||
|
});
|
||||||
|
}
|
||||||
calcItem({
|
calcItem({
|
||||||
menu,
|
menu,
|
||||||
protyle,
|
protyle,
|
||||||
|
@ -488,6 +502,8 @@ export const getNameByOperator = (operator: string, isRollup: boolean) => {
|
||||||
case undefined:
|
case undefined:
|
||||||
case "":
|
case "":
|
||||||
return isRollup ? window.siyuan.languages.original : window.siyuan.languages.calcOperatorNone;
|
return isRollup ? window.siyuan.languages.original : window.siyuan.languages.calcOperatorNone;
|
||||||
|
case "Unique values": // 仅汇总字段的汇总方式在使用
|
||||||
|
return window.siyuan.languages.uniqueValues;
|
||||||
case "Count all":
|
case "Count all":
|
||||||
return window.siyuan.languages.calcOperatorCountAll;
|
return window.siyuan.languages.calcOperatorCountAll;
|
||||||
case "Count values":
|
case "Count values":
|
||||||
|
|
|
@ -44,10 +44,25 @@ const moveToNew = (protyle: IProtyle, sourceElements: Element[], targetElement:
|
||||||
const newSourceId = newSourceElement.getAttribute("data-node-id");
|
const newSourceId = newSourceElement.getAttribute("data-node-id");
|
||||||
const doOperations: IOperation[] = [];
|
const doOperations: IOperation[] = [];
|
||||||
const undoOperations: IOperation[] = [];
|
const undoOperations: IOperation[] = [];
|
||||||
targetElement.insertAdjacentElement(isBottom ? "afterend" : "beforebegin", newSourceElement);
|
let ignoreInsert = false;
|
||||||
|
if (isBottom &&
|
||||||
|
targetElement.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = true;
|
||||||
|
} else if (!isBottom && targetElement.previousElementSibling &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = true;
|
||||||
|
}
|
||||||
|
if (!ignoreInsert) {
|
||||||
|
targetElement.insertAdjacentElement(isBottom ? "afterend" : "beforebegin", newSourceElement);
|
||||||
|
}
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "insert",
|
action: "insert",
|
||||||
|
context: {
|
||||||
|
ignoreProcess: ignoreInsert.toString(),
|
||||||
|
},
|
||||||
data: newSourceElement.outerHTML,
|
data: newSourceElement.outerHTML,
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
previousID: targetId,
|
previousID: targetId,
|
||||||
|
@ -55,6 +70,9 @@ const moveToNew = (protyle: IProtyle, sourceElements: Element[], targetElement:
|
||||||
} else {
|
} else {
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "insert",
|
action: "insert",
|
||||||
|
context: {
|
||||||
|
ignoreProcess: ignoreInsert.toString(),
|
||||||
|
},
|
||||||
data: newSourceElement.outerHTML,
|
data: newSourceElement.outerHTML,
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
nextID: targetId,
|
nextID: targetId,
|
||||||
|
@ -139,6 +157,7 @@ const moveToNew = (protyle: IProtyle, sourceElements: Element[], targetElement:
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
ignoreInsert,
|
||||||
doOperations,
|
doOperations,
|
||||||
undoOperations,
|
undoOperations,
|
||||||
topSourceElement,
|
topSourceElement,
|
||||||
|
@ -150,9 +169,32 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
let topSourceElement;
|
let topSourceElement;
|
||||||
const doOperations: IOperation[] = [];
|
const doOperations: IOperation[] = [];
|
||||||
const undoOperations: IOperation[] = [];
|
const undoOperations: IOperation[] = [];
|
||||||
const foldHeadingIds: { id: string, parentID: string }[] = [];
|
const copyFoldHeadingIds: { newId: string, oldId: string }[] = [];
|
||||||
const targetId = targetElement.getAttribute("data-node-id");
|
const targetId = targetElement.getAttribute("data-node-id");
|
||||||
let tempTargetElement = targetElement;
|
let tempTargetElement = targetElement;
|
||||||
|
let ignoreInsert = "";
|
||||||
|
const targetPreviousId = targetElement.previousElementSibling?.getAttribute("data-node-id");
|
||||||
|
if (position === "afterend" &&
|
||||||
|
targetElement.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = targetElement.getAttribute("data-subtype").replace("h", "");
|
||||||
|
} else if (position === "beforebegin" && targetElement.previousElementSibling &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = targetElement.getAttribute("data-subtype").replace("h", "");
|
||||||
|
}
|
||||||
|
if (ignoreInsert) {
|
||||||
|
let breakIgnore = false;
|
||||||
|
sourceElements.forEach(item => {
|
||||||
|
if (item.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
parseInt(item.getAttribute("data-subtype").replace("h", "")) >= parseInt(ignoreInsert)) {
|
||||||
|
breakIgnore = true;
|
||||||
|
}
|
||||||
|
if (!breakIgnore) {
|
||||||
|
item.setAttribute("data-remove", "true");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
sourceElements.reverse().forEach((item, index) => {
|
sourceElements.reverse().forEach((item, index) => {
|
||||||
const id = item.getAttribute("data-node-id");
|
const id = item.getAttribute("data-node-id");
|
||||||
const parentID = item.parentElement.getAttribute("data-node-id") || protyle.block.rootID;
|
const parentID = item.parentElement.getAttribute("data-node-id") || protyle.block.rootID;
|
||||||
|
@ -165,17 +207,19 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
topSourceElement = targetElement;
|
topSourceElement = targetElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const copyNewId = Lute.NewNodeID();
|
||||||
if (isCopy && item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
if (isCopy && item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
||||||
item.removeAttribute("fold");
|
copyFoldHeadingIds.push({
|
||||||
foldHeadingIds.push({id, parentID});
|
newId: copyNewId,
|
||||||
|
oldId: id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let copyId;
|
|
||||||
let copyElement;
|
let copyElement;
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
copyId = Lute.NewNodeID();
|
|
||||||
undoOperations.push({
|
undoOperations.push({
|
||||||
action: "delete",
|
action: "delete",
|
||||||
id: copyId,
|
id: copyNewId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
undoOperations.push({
|
undoOperations.push({
|
||||||
|
@ -193,28 +237,41 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const needInset = !ignoreInsert || (ignoreInsert && !item.hasAttribute("data-remove"));
|
||||||
|
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
|
item.removeAttribute("data-remove");
|
||||||
copyElement = item.cloneNode(true) as HTMLElement;
|
copyElement = item.cloneNode(true) as HTMLElement;
|
||||||
copyElement.setAttribute("data-node-id", copyId);
|
copyElement.setAttribute("data-node-id", copyNewId);
|
||||||
copyElement.querySelectorAll("[data-node-id]").forEach((e) => {
|
copyElement.querySelectorAll("[data-node-id]").forEach((e) => {
|
||||||
const newId = Lute.NewNodeID();
|
const newId = Lute.NewNodeID();
|
||||||
e.setAttribute("data-node-id", newId);
|
e.setAttribute("data-node-id", newId);
|
||||||
e.setAttribute("updated", newId.split("-")[0]);
|
e.setAttribute("updated", newId.split("-")[0]);
|
||||||
});
|
});
|
||||||
tempTargetElement.insertAdjacentElement(position, copyElement);
|
if (needInset) {
|
||||||
|
tempTargetElement.insertAdjacentElement(position, copyElement);
|
||||||
|
}
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "insert",
|
action: "insert",
|
||||||
id: copyId,
|
context: {
|
||||||
|
ignoreProcess: (!needInset).toString(),
|
||||||
|
},
|
||||||
|
id: copyNewId,
|
||||||
data: copyElement.outerHTML,
|
data: copyElement.outerHTML,
|
||||||
previousID: position === "afterend" ? targetId : copyElement.previousElementSibling?.getAttribute("data-node-id"), // 不能使用常量,移动后会被修改
|
previousID: position === "afterend" ? targetId : (!needInset ? targetPreviousId : copyElement.previousElementSibling?.getAttribute("data-node-id")), // 不能使用常量,移动后会被修改
|
||||||
parentID: copyElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
parentID: copyElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tempTargetElement.insertAdjacentElement(position, item);
|
if (needInset) {
|
||||||
|
tempTargetElement.insertAdjacentElement(position, item);
|
||||||
|
}
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "move",
|
action: "move",
|
||||||
|
context: {
|
||||||
|
ignoreProcess: (!needInset).toString(),
|
||||||
|
},
|
||||||
id,
|
id,
|
||||||
previousID: position === "afterend" ? targetId : item.previousElementSibling?.getAttribute("data-node-id"), // 不能使用常量,移动后会被修改
|
previousID: position === "afterend" ? targetId : (!needInset ? targetPreviousId : item.previousElementSibling?.getAttribute("data-node-id")), // 不能使用常量,移动后会被修改
|
||||||
parentID: item.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
parentID: item.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -222,29 +279,26 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
tempTargetElement = isCopy ? copyElement : item;
|
tempTargetElement = isCopy ? copyElement : item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
undoOperations.reverse();
|
if (ignoreInsert) {
|
||||||
for (let j = 0; j < foldHeadingIds.length; j++) {
|
// 不能在上一个循环中移除,否则会影响位置的判断和 tempTargetElement
|
||||||
const childrenItem = foldHeadingIds[j];
|
sourceElements.forEach(item => {
|
||||||
const headingIds = await fetchSyncPost("/api/block/getHeadingChildrenIDs", {id: childrenItem.id});
|
if (item.hasAttribute("data-remove")) {
|
||||||
headingIds.data.reverse().forEach((headingId: string) => {
|
item.remove();
|
||||||
undoOperations.push({
|
}
|
||||||
action: "move",
|
|
||||||
id: headingId,
|
|
||||||
previousID: childrenItem.id,
|
|
||||||
parentID: childrenItem.parentID,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
undoOperations.push({
|
|
||||||
action: "foldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
data: "remove"
|
|
||||||
});
|
|
||||||
doOperations.push({
|
|
||||||
action: "unfoldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
undoOperations.reverse();
|
||||||
|
for (let j = 0; j < copyFoldHeadingIds.length; j++) {
|
||||||
|
const childrenItem = copyFoldHeadingIds[j];
|
||||||
|
const responseTransaction = await fetchSyncPost("/api/block/getHeadingInsertTransaction", {id: childrenItem.oldId});
|
||||||
|
responseTransaction.data.doOperations.splice(0, 1);
|
||||||
|
responseTransaction.data.doOperations[0].previousID = childrenItem.newId;
|
||||||
|
responseTransaction.data.undoOperations.splice(0, 1);
|
||||||
|
doOperations.push(...responseTransaction.data.doOperations);
|
||||||
|
undoOperations.push(...responseTransaction.data.undoOperations);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
|
ignoreInsert: ignoreInsert ? true : false,
|
||||||
doOperations,
|
doOperations,
|
||||||
undoOperations,
|
undoOperations,
|
||||||
topSourceElement,
|
topSourceElement,
|
||||||
|
@ -367,7 +421,7 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const foldHeadingIds: { id: string, parentID: string }[] = [];
|
const copyFoldHeadingIds: { newId: string, oldId: string }[] = [];
|
||||||
let afterPreviousID;
|
let afterPreviousID;
|
||||||
sourceElements.reverse().forEach((item, index) => {
|
sourceElements.reverse().forEach((item, index) => {
|
||||||
const id = item.getAttribute("data-node-id");
|
const id = item.getAttribute("data-node-id");
|
||||||
|
@ -384,8 +438,7 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
}
|
}
|
||||||
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
item.removeAttribute("fold");
|
copyFoldHeadingIds.push({oldId: id, newId: copyId});
|
||||||
foldHeadingIds.push({id, parentID});
|
|
||||||
}
|
}
|
||||||
hasFoldHeading = true;
|
hasFoldHeading = true;
|
||||||
}
|
}
|
||||||
|
@ -434,29 +487,17 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
undoOperations.reverse();
|
undoOperations.reverse();
|
||||||
for (let j = 0; j < foldHeadingIds.length; j++) {
|
for (let j = 0; j < copyFoldHeadingIds.length; j++) {
|
||||||
const childrenItem = foldHeadingIds[j];
|
const childrenItem = copyFoldHeadingIds[j];
|
||||||
const headingIds = await fetchSyncPost("/api/block/getHeadingChildrenIDs", {id: childrenItem.id});
|
const responseTransaction = await fetchSyncPost("/api/block/getHeadingInsertTransaction", {id: childrenItem.oldId});
|
||||||
headingIds.data.reverse().forEach((headingId: string) => {
|
responseTransaction.data.doOperations.splice(0, 1);
|
||||||
undoOperations.push({
|
responseTransaction.data.doOperations[0].previousID = childrenItem.newId;
|
||||||
action: "move",
|
responseTransaction.data.undoOperations.splice(0, 1);
|
||||||
id: headingId,
|
doOperations.push(...responseTransaction.data.doOperations);
|
||||||
previousID: childrenItem.id,
|
undoOperations.push(...responseTransaction.data.undoOperations);
|
||||||
parentID: childrenItem.parentID,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (j === 0) {
|
if (j === 0) {
|
||||||
afterPreviousID = headingIds.data[0];
|
afterPreviousID = copyFoldHeadingIds[0].newId;
|
||||||
}
|
}
|
||||||
undoOperations.push({
|
|
||||||
action: "foldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
data: "remove"
|
|
||||||
});
|
|
||||||
doOperations.push({
|
|
||||||
action: "unfoldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
sbElement.insertAdjacentElement("afterbegin", targetElement);
|
sbElement.insertAdjacentElement("afterbegin", targetElement);
|
||||||
|
@ -595,7 +636,7 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
level: "row"
|
level: "row"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ((sourceElements.length > 1 || hasFoldHeading)) {
|
if (sourceElements.length > 1 || hasFoldHeading) {
|
||||||
turnsIntoOneTransaction({
|
turnsIntoOneTransaction({
|
||||||
protyle,
|
protyle,
|
||||||
selectsElement: sourceElements.reverse(),
|
selectsElement: sourceElements.reverse(),
|
||||||
|
@ -622,6 +663,7 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
newSourceElement.insertAdjacentHTML("beforeend", `<div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div>`);
|
newSourceElement.insertAdjacentHTML("beforeend", `<div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div>`);
|
||||||
}
|
}
|
||||||
let topSourceElement: Element;
|
let topSourceElement: Element;
|
||||||
|
let ignoreInsert = false;
|
||||||
let oldSourceParentElement = sourceElements[0].parentElement;
|
let oldSourceParentElement = sourceElements[0].parentElement;
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
if (newSourceElement) {
|
if (newSourceElement) {
|
||||||
|
@ -629,11 +671,13 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
} else {
|
} else {
|
||||||
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "afterend", isCopy);
|
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "afterend", isCopy);
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newSourceElement) {
|
if (newSourceElement) {
|
||||||
|
@ -641,11 +685,13 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
} else {
|
} else {
|
||||||
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "beforebegin", isCopy);
|
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "beforebegin", isCopy);
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetElement.getAttribute("data-type") === "NodeListItem" && targetElement.getAttribute("data-subtype") === "o") {
|
if (targetElement.getAttribute("data-type") === "NodeListItem" && targetElement.getAttribute("data-subtype") === "o") {
|
||||||
|
@ -783,9 +829,9 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
/// #endif
|
/// #endif
|
||||||
}
|
}
|
||||||
if (isSameDoc || isCopy) {
|
if (isSameDoc || isCopy) {
|
||||||
transaction(protyle, doOperations, undoOperations);
|
transaction(protyle, doOperations, ignoreInsert ? undefined : undoOperations);
|
||||||
} else {
|
} else {
|
||||||
// 跨文档不支持撤销
|
// 跨文档或插入折叠标题下不支持撤销
|
||||||
transaction(protyle, doOperations);
|
transaction(protyle, doOperations);
|
||||||
}
|
}
|
||||||
let hasFoldHeading = false;
|
let hasFoldHeading = false;
|
||||||
|
@ -805,7 +851,11 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
level: "row"
|
level: "row"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
focusBlock(sourceElements[0]);
|
if (document.contains(sourceElements[0])) {
|
||||||
|
focusBlock(sourceElements[0]);
|
||||||
|
} else {
|
||||||
|
focusBlock(targetElement);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
|
export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
|
||||||
|
|
|
@ -643,6 +643,9 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, isUndo:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (operation.action === "move") {
|
if (operation.action === "move") {
|
||||||
|
if (operation.context?.ignoreProcess === "true") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
/// #if !MOBILE
|
/// #if !MOBILE
|
||||||
if (updateElements.length === 0) {
|
if (updateElements.length === 0) {
|
||||||
// 打开两个相同的文档 A、A1,从 A 拖拽块 B 到 A1,在后续 ws 处理中,无法获取到拖拽出去的 B
|
// 打开两个相同的文档 A、A1,从 A 拖拽块 B 到 A1,在后续 ws 处理中,无法获取到拖拽出去的 B
|
||||||
|
|
2
app/stage/protyle/js/lute/lute.min.js
vendored
2
app/stage/protyle/js/lute/lute.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -243,6 +243,28 @@ func getHeadingDeleteTransaction(c *gin.Context) {
|
||||||
ret.Data = transaction
|
ret.Data = transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getHeadingInsertTransaction(c *gin.Context) {
|
||||||
|
ret := gulu.Ret.NewResult()
|
||||||
|
defer c.JSON(http.StatusOK, ret)
|
||||||
|
|
||||||
|
arg, ok := util.JsonArg(c, ret)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id := arg["id"].(string)
|
||||||
|
|
||||||
|
transaction, err := model.GetHeadingInsertTransaction(id)
|
||||||
|
if err != nil {
|
||||||
|
ret.Code = -1
|
||||||
|
ret.Msg = err.Error()
|
||||||
|
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Data = transaction
|
||||||
|
}
|
||||||
|
|
||||||
func getHeadingLevelTransaction(c *gin.Context) {
|
func getHeadingLevelTransaction(c *gin.Context) {
|
||||||
ret := gulu.Ret.NewResult()
|
ret := gulu.Ret.NewResult()
|
||||||
defer c.JSON(http.StatusOK, ret)
|
defer c.JSON(http.StatusOK, ret)
|
||||||
|
|
|
@ -214,6 +214,7 @@ func ServeAPI(ginServer *gin.Engine) {
|
||||||
ginServer.Handle("POST", "/api/block/setBlockReminder", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setBlockReminder)
|
ginServer.Handle("POST", "/api/block/setBlockReminder", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setBlockReminder)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingLevelTransaction", model.CheckAuth, getHeadingLevelTransaction)
|
ginServer.Handle("POST", "/api/block/getHeadingLevelTransaction", model.CheckAuth, getHeadingLevelTransaction)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingDeleteTransaction", model.CheckAuth, getHeadingDeleteTransaction)
|
ginServer.Handle("POST", "/api/block/getHeadingDeleteTransaction", model.CheckAuth, getHeadingDeleteTransaction)
|
||||||
|
ginServer.Handle("POST", "/api/block/getHeadingInsertTransaction", model.CheckAuth, getHeadingInsertTransaction)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingChildrenIDs", model.CheckAuth, getHeadingChildrenIDs)
|
ginServer.Handle("POST", "/api/block/getHeadingChildrenIDs", model.CheckAuth, getHeadingChildrenIDs)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingChildrenDOM", model.CheckAuth, getHeadingChildrenDOM)
|
ginServer.Handle("POST", "/api/block/getHeadingChildrenDOM", model.CheckAuth, getHeadingChildrenDOM)
|
||||||
ginServer.Handle("POST", "/api/block/swapBlockRef", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, swapBlockRef)
|
ginServer.Handle("POST", "/api/block/swapBlockRef", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, swapBlockRef)
|
||||||
|
|
|
@ -33,6 +33,7 @@ type CalcOperator string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CalcOperatorNone CalcOperator = ""
|
CalcOperatorNone CalcOperator = ""
|
||||||
|
CalcOperatorUniqueValues CalcOperator = "Unique values"
|
||||||
CalcOperatorCountAll CalcOperator = "Count all"
|
CalcOperatorCountAll CalcOperator = "Count all"
|
||||||
CalcOperatorCountValues CalcOperator = "Count values"
|
CalcOperatorCountValues CalcOperator = "Count values"
|
||||||
CalcOperatorCountUniqueValues CalcOperator = "Count unique values"
|
CalcOperatorCountUniqueValues CalcOperator = "Count unique values"
|
||||||
|
@ -1709,9 +1710,35 @@ func calcFieldRollup(collection Collection, field Field, fieldIndex int) {
|
||||||
values := item.GetValues()
|
values := item.GetValues()
|
||||||
if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup {
|
if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup {
|
||||||
for _, content := range values[fieldIndex].Rollup.Contents {
|
for _, content := range values[fieldIndex].Rollup.Contents {
|
||||||
if !uniqueValues[content.String(true)] {
|
switch content.Type {
|
||||||
uniqueValues[content.String(true)] = true
|
case KeyTypeRelation:
|
||||||
countUniqueValues++
|
for _, relationVal := range content.Relation.Contents {
|
||||||
|
key := relationVal.String(true)
|
||||||
|
if !uniqueValues[key] {
|
||||||
|
uniqueValues[key] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KeyTypeMSelect:
|
||||||
|
for _, mSelectVal := range content.MSelect {
|
||||||
|
if !uniqueValues[mSelectVal.Content] {
|
||||||
|
uniqueValues[mSelectVal.Content] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KeyTypeMAsset:
|
||||||
|
for _, mAssetVal := range content.MAsset {
|
||||||
|
if !uniqueValues[mAssetVal.Content] {
|
||||||
|
uniqueValues[mAssetVal.Content] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
key := content.String(true)
|
||||||
|
if !uniqueValues[key] {
|
||||||
|
uniqueValues[key] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -834,6 +834,40 @@ func (r *ValueRollup) calcContents(calc *RollupCalc, destKey *Key) {
|
||||||
|
|
||||||
switch calc.Operator {
|
switch calc.Operator {
|
||||||
case CalcOperatorNone:
|
case CalcOperatorNone:
|
||||||
|
case CalcOperatorUniqueValues:
|
||||||
|
uniqueValues := map[string]bool{}
|
||||||
|
for _, content := range r.Contents {
|
||||||
|
switch content.Type {
|
||||||
|
case KeyTypeRelation:
|
||||||
|
var newRelationContents []*Value
|
||||||
|
for _, relationVal := range content.Relation.Contents {
|
||||||
|
key := relationVal.String(true)
|
||||||
|
if !uniqueValues[key] {
|
||||||
|
uniqueValues[key] = true
|
||||||
|
newRelationContents = append(newRelationContents, relationVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.Relation.Contents = newRelationContents
|
||||||
|
case KeyTypeMSelect:
|
||||||
|
var newMSelect []*ValueSelect
|
||||||
|
for _, mSelect := range content.MSelect {
|
||||||
|
if !uniqueValues[mSelect.Content] {
|
||||||
|
uniqueValues[mSelect.Content] = true
|
||||||
|
newMSelect = append(newMSelect, mSelect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.MSelect = newMSelect
|
||||||
|
case KeyTypeMAsset:
|
||||||
|
var newMAsset []*ValueAsset
|
||||||
|
for _, mAsset := range content.MAsset {
|
||||||
|
if !uniqueValues[mAsset.Content] {
|
||||||
|
uniqueValues[mAsset.Content] = true
|
||||||
|
newMAsset = append(newMAsset, mAsset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.MAsset = newMAsset
|
||||||
|
}
|
||||||
|
}
|
||||||
case CalcOperatorCountAll:
|
case CalcOperatorCountAll:
|
||||||
r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(float64(len(r.Contents)), NumberFormatNone)}}
|
r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(float64(len(r.Contents)), NumberFormatNone)}}
|
||||||
case CalcOperatorCountValues:
|
case CalcOperatorCountValues:
|
||||||
|
|
|
@ -8,7 +8,7 @@ require (
|
||||||
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
|
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
|
||||||
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
|
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
|
||||||
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689
|
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689
|
||||||
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa
|
github.com/88250/lute v1.7.7-0.20250915120219-5d9cd1b7574b
|
||||||
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
|
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
|
||||||
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
|
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
|
||||||
github.com/ConradIrwin/font v0.2.1
|
github.com/ConradIrwin/font v0.2.1
|
||||||
|
|
|
@ -14,8 +14,8 @@ github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceT
|
||||||
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689 h1:39y5g7vnFAIcXhTN3IXPk7h2xBhC4a9hBTykDhHJqRY=
|
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689 h1:39y5g7vnFAIcXhTN3IXPk7h2xBhC4a9hBTykDhHJqRY=
|
||||||
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689/go.mod h1:c8uVw25vW2W4dhJ/j4iYsX5H1hc19spim266jO5x2hU=
|
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689/go.mod h1:c8uVw25vW2W4dhJ/j4iYsX5H1hc19spim266jO5x2hU=
|
||||||
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa h1:kbvW8LD3yJK5hwbLdJDb+7xqj7i68Rbz47xEqnNqf4I=
|
github.com/88250/lute v1.7.7-0.20250915120219-5d9cd1b7574b h1:/x4JPITd6K2Fb6arbs84TsoPKVZqRxXmwR5blMwJD2k=
|
||||||
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
|
github.com/88250/lute v1.7.7-0.20250915120219-5d9cd1b7574b/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
|
||||||
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46 h1:Bq1JsDfVbHKUxNL/B2JXd8cC/1h6aFjrlXpGycnh0Hk=
|
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46 h1:Bq1JsDfVbHKUxNL/B2JXd8cC/1h6aFjrlXpGycnh0Hk=
|
||||||
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI=
|
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI=
|
||||||
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=
|
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=
|
||||||
|
|
|
@ -628,6 +628,46 @@ func GetHeadingDeleteTransaction(id string) (transaction *Transaction, err error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetHeadingInsertTransaction(id string) (transaction *Transaction, err error) {
|
||||||
|
tree, err := LoadTreeByBlockID(id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node := treenode.GetNodeInTree(tree, id)
|
||||||
|
if nil == node {
|
||||||
|
err = errors.New(fmt.Sprintf(Conf.Language(15), id))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.NodeHeading != node.Type {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodes []*ast.Node
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
nodes = append(nodes, treenode.HeadingChildren(node)...)
|
||||||
|
|
||||||
|
transaction = &Transaction{}
|
||||||
|
luteEngine := util.NewLute()
|
||||||
|
for _, n := range nodes {
|
||||||
|
n.ID = ast.NewNodeID()
|
||||||
|
n.SetIALAttr("id", n.ID)
|
||||||
|
|
||||||
|
op := &Operation{Context: map[string]any{"ignoreProcess": "true"}}
|
||||||
|
op.ID = n.ID
|
||||||
|
op.Action = "insert"
|
||||||
|
op.Data = luteEngine.RenderNodeBlockDOM(n)
|
||||||
|
transaction.DoOperations = append(transaction.DoOperations, op)
|
||||||
|
|
||||||
|
op = &Operation{}
|
||||||
|
op.ID = n.ID
|
||||||
|
op.Action = "delete"
|
||||||
|
transaction.UndoOperations = append(transaction.UndoOperations, op)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func GetHeadingChildrenIDs(id string) (ret []string) {
|
func GetHeadingChildrenIDs(id string) (ret []string) {
|
||||||
tree, err := LoadTreeByBlockID(id)
|
tree, err := LoadTreeByBlockID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue