mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-09-22 00:20:47 +02:00
550 lines
18 KiB
TypeScript
550 lines
18 KiB
TypeScript
import {Menu} from "../../../plugin/Menu";
|
|
import {transaction} from "../../wysiwyg/transaction";
|
|
import {hasClosestBlock, hasClosestByClassName} from "../../util/hasClosest";
|
|
import {fetchSyncPost} from "../../../util/fetch";
|
|
import {getFieldsByData} from "./view";
|
|
|
|
const calcItem = (options: {
|
|
menu: Menu,
|
|
protyle: IProtyle,
|
|
operator: string,
|
|
oldOperator: string,
|
|
colId: string,
|
|
data?: IAV, // rollup
|
|
target: HTMLElement,
|
|
avId: string,
|
|
blockID: string
|
|
}) => {
|
|
options.menu.addItem({
|
|
iconHTML: "",
|
|
label: getNameByOperator(options.operator, !!options.data),
|
|
click() {
|
|
if (!options.data) {
|
|
transaction(options.protyle, [{
|
|
action: "setAttrViewColCalc",
|
|
avID: options.avId,
|
|
id: options.colId,
|
|
data: {
|
|
operator: options.operator
|
|
},
|
|
blockID: options.blockID
|
|
}], [{
|
|
action: "setAttrViewColCalc",
|
|
avID: options.avId,
|
|
id: options.colId,
|
|
data: {
|
|
operator: options.oldOperator
|
|
},
|
|
blockID: options.blockID
|
|
}]);
|
|
} else {
|
|
options.target.querySelector(".b3-menu__accelerator").textContent = getNameByOperator(options.operator, true);
|
|
const colData = getFieldsByData(options.data).find((item) => {
|
|
if (item.id === options.colId) {
|
|
if (!item.rollup) {
|
|
item.rollup = {};
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
colData.rollup.calc = {
|
|
operator: options.operator
|
|
};
|
|
transaction(options.protyle, [{
|
|
action: "updateAttrViewColRollup",
|
|
id: options.colId,
|
|
avID: options.avId,
|
|
parentID: colData.rollup.relationKeyID,
|
|
keyID: colData.rollup.keyID,
|
|
data: {
|
|
calc: colData.rollup.calc,
|
|
},
|
|
}], [{
|
|
action: "updateAttrViewColRollup",
|
|
id: options.colId,
|
|
avID: options.avId,
|
|
parentID: colData.rollup.relationKeyID,
|
|
keyID: colData.rollup.keyID,
|
|
data: {
|
|
calc: {
|
|
operator: options.oldOperator
|
|
},
|
|
}
|
|
}]);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
export const openCalcMenu = async (protyle: IProtyle, calcElement: HTMLElement, panelData?: {
|
|
data: IAV,
|
|
colId: string,
|
|
blockID: string
|
|
}, x?: number) => {
|
|
let rowElement: HTMLElement | false;
|
|
let type;
|
|
let colId: string;
|
|
let avId: string;
|
|
let oldOperator: string;
|
|
let blockID: string;
|
|
if (panelData) {
|
|
avId = panelData.data.id;
|
|
type = calcElement.dataset.colType as TAVCol;
|
|
oldOperator = calcElement.dataset.calc;
|
|
colId = panelData.colId;
|
|
blockID = panelData.blockID;
|
|
} else {
|
|
const blockElement = hasClosestBlock(calcElement);
|
|
if (!blockElement) {
|
|
return;
|
|
}
|
|
rowElement = hasClosestByClassName(calcElement, "av__row--footer");
|
|
if (!rowElement) {
|
|
return;
|
|
}
|
|
rowElement.classList.add("av__row--show");
|
|
type = calcElement.dataset.dtype as TAVCol;
|
|
colId = calcElement.dataset.colId;
|
|
avId = blockElement.dataset.avId;
|
|
oldOperator = calcElement.dataset.operator;
|
|
blockID = blockElement.dataset.nodeId;
|
|
}
|
|
if (type === "lineNumber") {
|
|
return;
|
|
}
|
|
const menu = new Menu("av-calc", () => {
|
|
if (rowElement) {
|
|
rowElement.classList.remove("av__row--show");
|
|
}
|
|
});
|
|
if (menu.isOpen) {
|
|
return;
|
|
}
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
if (panelData?.data && type !== "checkbox") {
|
|
// 汇总字段汇总方式中才有“显示唯一值”选项 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({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Count all",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
if (type !== "checkbox") {
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Count empty",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Count not empty",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Count values",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Count unique values",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Percent empty",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Percent not empty",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Percent unique values",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
} else {
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Checked",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Unchecked",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Percent checked",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Percent unchecked",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
}
|
|
let rollupIsNumber = false;
|
|
if (type === "rollup") {
|
|
let relationKeyID: string;
|
|
let keyID: string;
|
|
let avData = panelData?.data;
|
|
if (!avData) {
|
|
const avResponse = await fetchSyncPost("api/av/renderAttributeView", {id: avId});
|
|
avData = avResponse.data;
|
|
}
|
|
|
|
getFieldsByData(avData).find((item) => {
|
|
if (item.id === colId) {
|
|
relationKeyID = item.rollup?.relationKeyID;
|
|
keyID = item.rollup?.keyID;
|
|
return true;
|
|
}
|
|
});
|
|
if (relationKeyID && keyID) {
|
|
let relationAvId: string;
|
|
getFieldsByData(avData).find((item) => {
|
|
if (item.id === relationKeyID) {
|
|
relationAvId = item.relation?.avID;
|
|
return true;
|
|
}
|
|
});
|
|
if (relationAvId) {
|
|
const colResponse = await fetchSyncPost("api/av/getAttributeView", {id: relationAvId});
|
|
colResponse.data.av.keyValues.find((item: { key: { id: string, name: string, type: TAVCol } }) => {
|
|
if (item.key.id === keyID) {
|
|
rollupIsNumber = item.key.type === "number";
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if (["number", "template"].includes(type) || rollupIsNumber) {
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Sum",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Average",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Median",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Min",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Max",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Range",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
} else if (["date", "created", "updated"].includes(type)) {
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Earliest",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Latest",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
calcItem({
|
|
menu,
|
|
protyle,
|
|
colId,
|
|
avId,
|
|
oldOperator,
|
|
operator: "Range",
|
|
data: panelData?.data,
|
|
blockID,
|
|
target: calcElement
|
|
});
|
|
}
|
|
const calcRect = calcElement.getBoundingClientRect();
|
|
menu.open({x: Math.max(x || 0, calcRect.left), y: calcRect.bottom, h: calcRect.height});
|
|
};
|
|
|
|
export const getCalcValue = (column: IAVColumn) => {
|
|
if (!column.calc || !column.calc.result) {
|
|
return "";
|
|
}
|
|
let resultCalc: any = column.calc.result.number;
|
|
if (column.calc.operator === "Earliest" || column.calc.operator === "Latest" ||
|
|
(column.calc.operator === "Range" && ["date", "created", "updated"].includes(column.type))) {
|
|
resultCalc = column.calc.result[column.type as "date"];
|
|
}
|
|
let value = "";
|
|
switch (column.calc.operator) {
|
|
case "Count all":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultCountAll}</small>`;
|
|
break;
|
|
case "Count values":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultCountValues}</small>`;
|
|
break;
|
|
case "Count unique values":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultCountUniqueValues}</small>`;
|
|
break;
|
|
case "Count empty":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultCountEmpty}</small>`;
|
|
break;
|
|
case "Count not empty":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultCountNotEmpty}</small>`;
|
|
break;
|
|
case "Percent empty":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultPercentEmpty}</small>`;
|
|
break;
|
|
case "Percent not empty":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultPercentNotEmpty}</small>`;
|
|
break;
|
|
case "Percent unique values":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultPercentUniqueValues}</small>`;
|
|
break;
|
|
case "Sum":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultSum}</small>`;
|
|
break;
|
|
case "Average":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultAverage}</small>`;
|
|
break;
|
|
case "Median":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultMedian}</small>`;
|
|
break;
|
|
case "Min":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultMin}</small>`;
|
|
break;
|
|
case "Max":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultMax}</small>`;
|
|
break;
|
|
case "Range":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcResultRange}</small>`;
|
|
break;
|
|
case "Earliest":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcOperatorEarliest}</small>`;
|
|
break;
|
|
case "Latest":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.calcOperatorLatest}</small>`;
|
|
break;
|
|
case "Checked":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.checked}</small>`;
|
|
break;
|
|
case "Unchecked":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.unchecked}</small>`;
|
|
break;
|
|
case "Percent checked":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.percentChecked}</small>`;
|
|
break;
|
|
case "Percent unchecked":
|
|
value = `<span>${resultCalc.formattedContent}</span><small>${window.siyuan.languages.percentUnchecked}</small>`;
|
|
break;
|
|
}
|
|
return value;
|
|
};
|
|
|
|
export const getNameByOperator = (operator: string, isRollup: boolean) => {
|
|
switch (operator) {
|
|
case undefined:
|
|
case "":
|
|
return isRollup ? window.siyuan.languages.original : window.siyuan.languages.calcOperatorNone;
|
|
case "Unique values": // 仅汇总字段的汇总方式在使用
|
|
return window.siyuan.languages.uniqueValues;
|
|
case "Count all":
|
|
return window.siyuan.languages.calcOperatorCountAll;
|
|
case "Count values":
|
|
return window.siyuan.languages.calcOperatorCountValues;
|
|
case "Count unique values":
|
|
return window.siyuan.languages.calcOperatorCountUniqueValues;
|
|
case "Count empty":
|
|
return window.siyuan.languages.calcOperatorCountEmpty;
|
|
case "Count not empty":
|
|
return window.siyuan.languages.calcOperatorCountNotEmpty;
|
|
case "Percent empty":
|
|
return window.siyuan.languages.calcOperatorPercentEmpty;
|
|
case "Percent not empty":
|
|
return window.siyuan.languages.calcOperatorPercentNotEmpty;
|
|
case "Percent unique values":
|
|
return window.siyuan.languages.calcOperatorPercentUniqueValues;
|
|
case "Checked":
|
|
return window.siyuan.languages.checked;
|
|
case "Unchecked":
|
|
return window.siyuan.languages.unchecked;
|
|
case "Percent checked":
|
|
return window.siyuan.languages.percentChecked;
|
|
case "Percent unchecked":
|
|
return window.siyuan.languages.percentUnchecked;
|
|
case "Sum":
|
|
return window.siyuan.languages.calcOperatorSum;
|
|
case "Average":
|
|
return window.siyuan.languages.calcOperatorAverage;
|
|
case "Median":
|
|
return window.siyuan.languages.calcOperatorMedian;
|
|
case "Min":
|
|
return window.siyuan.languages.calcOperatorMin;
|
|
case "Max":
|
|
return window.siyuan.languages.calcOperatorMax;
|
|
case "Range":
|
|
return window.siyuan.languages.calcOperatorRange;
|
|
case "Earliest":
|
|
return window.siyuan.languages.calcOperatorEarliest;
|
|
case "Latest":
|
|
return window.siyuan.languages.calcOperatorLatest;
|
|
default:
|
|
return "";
|
|
}
|
|
};
|