Add database lineNumber field type (#11008)

* 🎨 add database lineNumber type

* 🎨 fix https://github.com/siyuan-note/siyuan/issues/10896

* 🎨 Improve mobile app appearance language https://github.com/siyuan-note/siyuan/issues/11009

* 🎨 Improve database template field calc https://github.com/siyuan-note/siyuan/issues/11011

* ⬆️ Upgrade kernel deps

* ⬆️ Upgrade kernel deps

* 🎨 fix https://github.com/siyuan-note/siyuan/issues/10896

* 🎨 fix https://github.com/siyuan-note/siyuan/issues/10896

* 🐛 https://github.com/siyuan-note/siyuan/issues/11015

* 🐛 https://github.com/siyuan-note/siyuan/issues/11018

* 🐛 https://github.com/siyuan-note/siyuan/issues/11018

* 🎨 刚创建时无 id,更新需和 oldValue 保持一致

* 🐛 Database date field between filter calculation error Fix https://github.com/siyuan-note/siyuan/issues/10979

* 🐛 Primary key value unexpectedly updated when database adds row https://github.com/siyuan-note/siyuan/issues/11018

* 🎨 https://github.com/siyuan-note/siyuan/issues/11013

* 🎨 搜索可汇总字段时排除行号类型字段 https://github.com/siyuan-note/siyuan/pull/11008

* 🎨 lineNumber no need to join the calc op and remove useless todo tag

---------

Co-authored-by: Vanessa <lly219@gmail.com>
Co-authored-by: Daniel <845765@qq.com>
This commit is contained in:
Silent Lee 2024-04-15 00:49:48 +08:00 committed by GitHub
parent afeea6a80e
commit 8a354da50a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 224 additions and 108 deletions

1
.gitignore vendored
View file

@ -24,6 +24,7 @@ electron/dist
# IDE # IDE
.idea/ .idea/
.vscode/
# Log # Log
logs logs

View file

@ -119,6 +119,7 @@
"goToEditTabPrev": "Go to previous edited tab", "goToEditTabPrev": "Go to previous edited tab",
"createdTime": "Created time", "createdTime": "Created time",
"updatedTime": "Updated time", "updatedTime": "Updated time",
"lineNumber": "Line number",
"removeBookmark": "Remove bookmark from ${x}?", "removeBookmark": "Remove bookmark from ${x}?",
"defaultMargin": "Default", "defaultMargin": "Default",
"noneMargin": "None", "noneMargin": "None",

View file

@ -119,6 +119,7 @@
"goToEditTabPrev": "Ir a la pestaña editada anteriormente", "goToEditTabPrev": "Ir a la pestaña editada anteriormente",
"createdTime": "Hora de creación", "createdTime": "Hora de creación",
"updatedTime": "Hora actualizada", "updatedTime": "Hora actualizada",
"lineNumber": "Número de línea",
"removeBookmark": "¿Eliminar marcador de ${x}?", "removeBookmark": "¿Eliminar marcador de ${x}?",
"lockEdit": "Hacer que el documento sea de sólo lectura", "lockEdit": "Hacer que el documento sea de sólo lectura",
"unlockEdit": "Hacer que el documento sea escribible", "unlockEdit": "Hacer que el documento sea escribible",

View file

@ -119,6 +119,7 @@
"goToEditTabPrev": "Aller à l'onglet modifié précédent", "goToEditTabPrev": "Aller à l'onglet modifié précédent",
"createdTime": "Heure de création", "createdTime": "Heure de création",
"updatedTime": "Heure mise à jour", "updatedTime": "Heure mise à jour",
"lineNumber": "Numéro de ligne",
"removeBookmark": "Supprimer le signet de ${x} ?", "removeBookmark": "Supprimer le signet de ${x} ?",
"lockEdit": "Rendre le document en lecture seule", "lockEdit": "Rendre le document en lecture seule",
"unlockEdit": "Rendre le document accessible en écriture", "unlockEdit": "Rendre le document accessible en écriture",

View file

@ -119,6 +119,7 @@
"goToEditTabPrev": "跳到上一個編輯頁籤", "goToEditTabPrev": "跳到上一個編輯頁籤",
"createdTime": "建立時間", "createdTime": "建立時間",
"updatedTime": "更新時間", "updatedTime": "更新時間",
"lineNumber": "行號",
"removeBookmark": "移除 ${x} 中的書籤?", "removeBookmark": "移除 ${x} 中的書籤?",
"lockEdit": "鎖定編輯", "lockEdit": "鎖定編輯",
"unlockEdit": "解除鎖定", "unlockEdit": "解除鎖定",

View file

@ -119,6 +119,7 @@
"goToEditTabPrev": "跳转到上一个编辑页签", "goToEditTabPrev": "跳转到上一个编辑页签",
"createdTime": "创建时间", "createdTime": "创建时间",
"updatedTime": "更新时间", "updatedTime": "更新时间",
"lineNumber": "行号",
"removeBookmark": "移除 ${x} 中的书签?", "removeBookmark": "移除 ${x} 中的书签?",
"lockEdit": "锁定编辑", "lockEdit": "锁定编辑",
"unlockEdit": "解除锁定", "unlockEdit": "解除锁定",

View file

@ -251,7 +251,8 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
return; return;
} }
const type = getTypeByCellElement(target); const type = getTypeByCellElement(target);
if (type === "updated" || type === "created" || (type === "block" && !target.getAttribute("data-detached"))) { // TODO 点击单元格的时候, lineNumber 选中整行
if (type === "updated" || type === "created" || type === "lineNumber" || (type === "block" && !target.getAttribute("data-detached"))) {
selectRow(rowElement.querySelector(".av__firstcol"), "toggle"); selectRow(rowElement.querySelector(".av__firstcol"), "toggle");
} else { } else {
scrollElement.querySelectorAll(".av__row--select").forEach(item => { scrollElement.querySelectorAll(".av__row--select").forEach(item => {

View file

@ -644,7 +644,7 @@ export const renderCellAttr = (cellElement: Element, value: IAVCellValue) => {
} }
}; };
export const renderCell = (cellValue: IAVCellValue) => { export const renderCell = (cellValue: IAVCellValue, rowIndex = 0) => {
let text = ""; let text = "";
if (["text", "template"].includes(cellValue.type)) { if (["text", "template"].includes(cellValue.type)) {
text = `<span class="av__celltext">${cellValue ? (cellValue[cellValue.type as "text"].content || "") : ""}</span>`; text = `<span class="av__celltext">${cellValue ? (cellValue[cellValue.type as "text"].content || "") : ""}</span>`;
@ -683,6 +683,9 @@ export const renderCell = (cellValue: IAVCellValue) => {
text += dayjs(dataValue.content).format("YYYY-MM-DD HH:mm"); text += dayjs(dataValue.content).format("YYYY-MM-DD HH:mm");
} }
text += "</span>"; text += "</span>";
} else if (["lineNumber"].includes(cellValue.type)) {
// 渲染行号
text = `<span class="av__celltext" data-value='${rowIndex + 1}'>${rowIndex + 1}</span>`;
} else if (cellValue.type === "mAsset") { } else if (cellValue.type === "mAsset") {
cellValue?.mAsset?.forEach((item) => { cellValue?.mAsset?.forEach((item) => {
if (item.type === "image") { if (item.type === "image") {
@ -713,8 +716,9 @@ export const renderCell = (cellValue: IAVCellValue) => {
text = text.substring(0, text.length - 2); text = text.substring(0, text.length - 2);
} }
} }
if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated"].includes(cellValue.type) &&
cellValue && cellValue[cellValue.type as "url"].content) { if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated", "lineNumber"].includes(cellValue.type) &&
( cellValue.type === "lineNumber" || (cellValue && cellValue[cellValue.type as "url"].content))) {
text += `<span ${cellValue.type !== "number" ? "" : 'style="right:auto;left:5px"'} data-type="copy" class="block__icon"><svg><use xlink:href="#iconCopy"></use></svg></span>`; text += `<span ${cellValue.type !== "number" ? "" : 'style="right:auto;left:5px"'} data-type="copy" class="block__icon"><svg><use xlink:href="#iconCopy"></use></svg></span>`;
} }
return text; return text;

View file

@ -252,6 +252,7 @@ export const getEditHTML = (options: {
${genUpdateColItem("template", colData.type)} ${genUpdateColItem("template", colData.type)}
${genUpdateColItem("relation", colData.type)} ${genUpdateColItem("relation", colData.type)}
${genUpdateColItem("rollup", colData.type)} ${genUpdateColItem("rollup", colData.type)}
${genUpdateColItem("lineNumber", colData.type)}
${genUpdateColItem("created", colData.type)} ${genUpdateColItem("created", colData.type)}
${genUpdateColItem("updated", colData.type)} ${genUpdateColItem("updated", colData.type)}
</div>`; </div>`;
@ -482,6 +483,8 @@ export const getColNameByType = (type: TAVCol) => {
return window.siyuan.languages.checkbox; return window.siyuan.languages.checkbox;
case "block": case "block":
return window.siyuan.languages["_attrView"].key; return window.siyuan.languages["_attrView"].key;
case "lineNumber":
return window.siyuan.languages.lineNumber;
} }
}; };
@ -518,6 +521,8 @@ export const getColIconByType = (type: TAVCol) => {
return "iconMath"; return "iconMath";
case "checkbox": case "checkbox":
return "iconCheck"; return "iconCheck";
case "lineNumber":
return "iconSpreadOdd";
} }
}; };
@ -694,6 +699,9 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
} }
}); });
menu.addSeparator(); menu.addSeparator();
// 行号 类型不参与 排序和筛选
if (type !== "lineNumber") {
menu.addItem({ menu.addItem({
icon: "iconUp", icon: "iconUp",
label: window.siyuan.languages.asc, label: window.siyuan.languages.asc,
@ -778,6 +786,7 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
}); });
} }
menu.addSeparator(); menu.addSeparator();
}
menu.addItem({ menu.addItem({
icon: "iconInsertLeft", icon: "iconInsertLeft",
@ -1429,6 +1438,44 @@ export const addCol = (protyle: IProtyle, blockElement: Element, previousID?: st
blockElement.setAttribute("updated", newUpdated); blockElement.setAttribute("updated", newUpdated);
} }
}); });
// 在创建时间前插入 lineNumber
menu.addItem({
icon: "iconSpreadOdd",
label: window.siyuan.languages.lineNumber,
click() {
const id = Lute.NewNodeID();
const newUpdated = dayjs().format("YYYYMMDDHHmmss");
transaction(protyle, [{
action: "addAttrViewCol",
name: window.siyuan.languages.lineNumber,
avID,
type: "lineNumber",
id,
previousID
}, {
action: "doUpdateUpdated",
id: blockId,
data: newUpdated,
}], [{
action: "removeAttrViewCol",
id,
avID,
}, {
action: "doUpdateUpdated",
id: blockId,
data: blockElement.getAttribute("updated")
}]);
addAttrViewColAnimation({
blockElement: blockElement,
protyle: protyle,
type: "lineNumber",
name: window.siyuan.languages.lineNumber,
id,
previousID
});
blockElement.setAttribute("updated", newUpdated);
}
});
menu.addItem({ menu.addItem({
icon: "iconClock", icon: "iconClock",
label: window.siyuan.languages.createdTime, label: window.siyuan.languages.createdTime,

View file

@ -566,7 +566,8 @@ export const addFilter = (options: {
return true; return true;
} }
}); });
if (!filter && column.type !== "mAsset") { // 该列是行号类型列,则不允许添加到过滤器
if (!filter && column.type !== "mAsset" && column.type !== "lineNumber") {
menu.addItem({ menu.addItem({
label: column.name, label: column.name,
iconHTML: column.icon ? unicode2Emoji(column.icon, "b3-menu__icon", true) : `<svg class="b3-menu__icon"><use xlink:href="#${getColIconByType(column.type)}"></use></svg>`, iconHTML: column.icon ? unicode2Emoji(column.icon, "b3-menu__icon", true) : `<svg class="b3-menu__icon"><use xlink:href="#${getColIconByType(column.type)}"></use></svg>`,

View file

@ -854,6 +854,46 @@ export const openMenuPanel = (options: {
name, name,
type: target.dataset.oldType as TAVCol, type: target.dataset.oldType as TAVCol,
}]); }]);
// 需要取消 lineNumber 列的排序和过滤
if (target.dataset.newType === "lineNumber") {
const sortExist = data.view.sorts.find((sort) => sort.column === options.colId);
if (sortExist) {
const oldSorts = Object.assign([], data.view.sorts);
const newSorts = data.view.sorts.filter((sort) => sort.column !== options.colId);
transaction(options.protyle, [{
action: "setAttrViewSorts",
avID: data.id,
data: newSorts,
blockID,
}], [{
action: "setAttrViewSorts",
avID: data.id,
data: oldSorts,
blockID,
}]);
}
const filterExist = data.view.filters.find((filter) => filter.column === options.colId);
if (filterExist) {
const oldFilters = JSON.parse(JSON.stringify(data.view.filters));
const newFilters = data.view.filters.filter((filter) => filter.column !== options.colId);
transaction(options.protyle, [{
action: "setAttrViewFilters",
avID: data.id,
data: newFilters,
blockID
}], [{
action: "setAttrViewFilters",
avID: data.id,
data: oldFilters,
blockID
}]);
}
}
} }
avPanelElement.remove(); avPanelElement.remove();
event.preventDefault(); event.preventDefault();

View file

@ -129,8 +129,16 @@ style="width: ${column.width || "200px"};">
if (pinIndex === index) { if (pinIndex === index) {
tableHTML += "</div>"; tableHTML += "</div>";
} }
// lineNumber type 不参与计算操作
if (column.type === "lineNumber") {
calcHTML += `<div data-col-id="${column.id}" data-dtype="${column.type}"
style="display: flex; width: ${index === 0 ? ((parseInt(column.width || "200") + 24) + "px") : (column.width || "200px")}">&nbsp;</div>`;
} else {
calcHTML += `<div class="av__calc${column.calc && column.calc.operator !== "" ? " av__calc--ashow" : ""}" data-col-id="${column.id}" data-dtype="${column.type}" data-operator="${column.calc?.operator || ""}" calcHTML += `<div class="av__calc${column.calc && column.calc.operator !== "" ? " av__calc--ashow" : ""}" data-col-id="${column.id}" data-dtype="${column.type}" data-operator="${column.calc?.operator || ""}"
style="width: ${index === 0 ? ((parseInt(column.width || "200") + 24) + "px") : (column.width || "200px")}">${getCalcValue(column) || '<svg><use xlink:href="#iconDown"></use></svg>' + window.siyuan.languages.calc}</div>`; style="width: ${index === 0 ? ((parseInt(column.width || "200") + 24) + "px") : (column.width || "200px")}">${getCalcValue(column) || '<svg><use xlink:href="#iconDown"></use></svg>' + window.siyuan.languages.calc}</div>`;
}
if (pinIndex === index) { if (pinIndex === index) {
calcHTML += "</div>"; calcHTML += "</div>";
} }
@ -142,7 +150,7 @@ style="width: ${index === 0 ? ((parseInt(column.width || "200") + 24) + "px") :
</div> </div>
</div>`; </div>`;
// body // body
data.rows.forEach((row: IAVRow) => { data.rows.forEach((row: IAVRow, rowIndex: number) => {
tableHTML += `<div class="av__row" data-id="${row.id}">`; tableHTML += `<div class="av__row" data-id="${row.id}">`;
if (pinIndex > -1) { if (pinIndex > -1) {
tableHTML += '<div class="av__colsticky"><div class="av__firstcol"><svg><use xlink:href="#iconUncheck"></use></svg></div>'; tableHTML += '<div class="av__colsticky"><div class="av__firstcol"><svg><use xlink:href="#iconUncheck"></use></svg></div>';
@ -165,7 +173,7 @@ ${cell.value?.isDetached ? ' data-detached="true"' : ""}
style="width: ${data.columns[index].width || "200px"}; style="width: ${data.columns[index].width || "200px"};
${cell.valueType === "number" ? "text-align: right;" : ""} ${cell.valueType === "number" ? "text-align: right;" : ""}
${cell.bgColor ? `background-color:${cell.bgColor};` : ""} ${cell.bgColor ? `background-color:${cell.bgColor};` : ""}
${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value)}</div>`; ${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex)}</div>`;
if (pinIndex === index) { if (pinIndex === index) {
tableHTML += "</div>"; tableHTML += "</div>";

View file

@ -16,12 +16,19 @@ export const addSort = (options: {
const menu = new Menu("av-add-sort"); const menu = new Menu("av-add-sort");
options.data.view.columns.forEach((column) => { options.data.view.columns.forEach((column) => {
let hasSort = false; let hasSort = false;
// 如果该列是行号类型列,不允许添加排序
if (column.type === "lineNumber") {
hasSort = true;
} else {
options.data.view.sorts.find((sort) => { options.data.view.sorts.find((sort) => {
if (sort.column === column.id) { if (sort.column === column.id) {
hasSort = true; hasSort = true;
return true; return true;
} }
}); });
}
if (!hasSort) { if (!hasSort) {
menu.addItem({ menu.addItem({
label: column.name, label: column.name,

View file

@ -82,6 +82,7 @@ type TAVCol =
| "created" | "created"
| "updated" | "updated"
| "checkbox" | "checkbox"
| "lineNumber"
type THintSource = "search" | "av" | "hint"; type THintSource = "search" | "av" | "hint";
type TAVFilterOperator = type TAVFilterOperator =
"=" "="

View file

@ -80,6 +80,7 @@ const (
KeyTypeCheckbox KeyType = "checkbox" KeyTypeCheckbox KeyType = "checkbox"
KeyTypeRelation KeyType = "relation" KeyTypeRelation KeyType = "relation"
KeyTypeRollup KeyType = "rollup" KeyTypeRollup KeyType = "rollup"
KeyTypeLineNumber KeyType = "lineNumber"
) )
// Key 描述了属性视图属性列的基础结构。 // Key 描述了属性视图属性列的基础结构。

View file

@ -145,7 +145,7 @@ func SearchAttributeViewNonRelationKey(avID, keyword string) (ret []*av.Key) {
} }
for _, keyValues := range attrView.KeyValues { for _, keyValues := range attrView.KeyValues {
if av.KeyTypeRelation != keyValues.Key.Type && av.KeyTypeRollup != keyValues.Key.Type && av.KeyTypeTemplate != keyValues.Key.Type && av.KeyTypeCreated != keyValues.Key.Type && av.KeyTypeUpdated != keyValues.Key.Type { if av.KeyTypeRelation != keyValues.Key.Type && av.KeyTypeRollup != keyValues.Key.Type && av.KeyTypeTemplate != keyValues.Key.Type && av.KeyTypeCreated != keyValues.Key.Type && av.KeyTypeUpdated != keyValues.Key.Type && av.KeyTypeLineNumber != keyValues.Key.Type {
if strings.Contains(strings.ToLower(keyValues.Key.Name), strings.ToLower(keyword)) { if strings.Contains(strings.ToLower(keyValues.Key.Name), strings.ToLower(keyword)) {
ret = append(ret, keyValues.Key) ret = append(ret, keyValues.Key)
} }
@ -2654,7 +2654,7 @@ func AddAttributeViewKey(avID, keyID, keyName, keyType, keyIcon, previousKeyID s
switch keyTyp { switch keyTyp {
case av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail, case av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail,
av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox, av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox,
av.KeyTypeRelation, av.KeyTypeRollup: av.KeyTypeRelation, av.KeyTypeRollup, av.KeyTypeLineNumber:
key := av.NewKey(keyID, keyName, keyIcon, keyTyp) key := av.NewKey(keyID, keyName, keyIcon, keyTyp)
if av.KeyTypeRollup == keyTyp { if av.KeyTypeRollup == keyTyp {
@ -2766,7 +2766,7 @@ func updateAttributeViewColumn(operation *Operation) (err error) {
switch colType { switch colType {
case av.KeyTypeBlock, av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail, case av.KeyTypeBlock, av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail,
av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox, av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox,
av.KeyTypeRelation, av.KeyTypeRollup: av.KeyTypeRelation, av.KeyTypeRollup, av.KeyTypeLineNumber:
for _, keyValues := range attrView.KeyValues { for _, keyValues := range attrView.KeyValues {
if keyValues.Key.ID == operation.ID { if keyValues.Key.ID == operation.ID {
keyValues.Key.Name = strings.TrimSpace(operation.Name) keyValues.Key.Name = strings.TrimSpace(operation.Name)