diff --git a/app/src/assets/scss/business/_av.scss b/app/src/assets/scss/business/_av.scss
index e042695e1..de33d0a37 100644
--- a/app/src/assets/scss/business/_av.scss
+++ b/app/src/assets/scss/business/_av.scss
@@ -191,6 +191,13 @@
opacity: 1;
background-color: var(--b3-theme-background-light) !important;
}
+
+ .av__cellassetimg {
+ max-height: 24px;
+ border-radius: var(--b3-border-radius);
+ margin: 1px 2px;
+ max-width: none;
+ }
}
&__cellheader {
diff --git a/app/src/protyle/render/av/asset.ts b/app/src/protyle/render/av/asset.ts
new file mode 100644
index 000000000..ee05aac25
--- /dev/null
+++ b/app/src/protyle/render/av/asset.ts
@@ -0,0 +1,185 @@
+import {Menu} from "../../../plugin/Menu";
+import {transaction} from "../../wysiwyg/transaction";
+import {updateAttrViewCellAnimation} from "./action";
+import {isMobile} from "../../../util/functions";
+import {Constants} from "../../../constants";
+import {uploadFiles} from "../../upload";
+import {pathPosix} from "../../../util/pathName";
+
+
+export const bindAssetEvent = (options: {
+ protyle: IProtyle,
+ data: IAV,
+ menuElement: HTMLElement,
+ cellElements: HTMLElement[]
+}) => {
+ options.menuElement.querySelector("input").addEventListener("change", (event: InputEvent & {
+ target: HTMLInputElement
+ }) => {
+ if (event.target.files.length === 0) {
+ return;
+ }
+ uploadFiles(options.protyle, event.target.files, event.target, (res) => {
+ const resData = JSON.parse(res)
+ const value: IAVCellAssetValue[] = []
+ Object.keys(resData.data.succMap).forEach((key) => {
+ value.push({
+ name: key,
+ content: resData.data.succMap[key],
+ type: Constants.SIYUAN_ASSETS_IMAGE.includes(pathPosix().extname(resData.data.succMap[key]).toLowerCase()) ? "image" : "file"
+ })
+ })
+ updateAssetCell({
+ protyle: options.protyle,
+ data: options.data,
+ cellElements: options.cellElements,
+ value
+ })
+ });
+ });
+};
+
+export const getAssetHTML = (data: IAVTable, cellElements: HTMLElement[]) => {
+ const cellId = cellElements[0].dataset.id;
+ const rowId = cellElements[0].parentElement.dataset.id;
+ let cellData: IAVCell
+ data.rows.find(row => {
+ if (row.id === rowId) {
+ row.cells.find(cell => {
+ if (cell.id === cellId) {
+ cellData = cell
+ return true
+ }
+ })
+ return true
+ }
+ })
+ let html = ""
+ if (cellData?.value?.mAsset) {
+ cellData.value.mAsset.forEach(item => {
+ if (!item.content) {
+ return
+ }
+ let contentHTML
+ if (item.type === "image") {
+ contentHTML = `
`
+ } else {
+ contentHTML = ``
+ }
+
+ html += ``
+ })
+ }
+ return `
`;
+};
+
+const updateAssetCell = (options: {
+ protyle: IProtyle,
+ data: IAV,
+ cellElements: HTMLElement[],
+ value: IAVCellAssetValue[]
+}) => {
+ if (!options.value || options.value.length === 0 || !options.value[0].content) {
+ return;
+ }
+ let cellIndex = 0;
+ Array.from(options.cellElements[0].parentElement.querySelectorAll(".av__cell")).find((item: HTMLElement, index) => {
+ if (item.dataset.id === options.cellElements[0].dataset.id) {
+ cellIndex = index;
+ return true;
+ }
+ });
+
+ const colId = options.cellElements[0].dataset.colId;
+ const cellDoOperations: IOperation[] = [];
+ const cellUndoOperations: IOperation[] = [];
+ options.cellElements.forEach(item => {
+ let cellData: IAVCell;
+ const rowID = item.parentElement.dataset.id;
+ options.data.view.rows.find(row => {
+ if (row.id === rowID) {
+ cellData = row.cells[cellIndex];
+ // 为空时 cellId 每次请求都不一致
+ cellData.id = item.dataset.id;
+ if (!cellData.value || !cellData.value.mAsset) {
+ cellData.value = {mAsset: []} as IAVCellValue;
+ }
+ return true;
+ }
+ });
+ const oldValue = Object.assign([], cellData.value.mAsset);
+ options.value.forEach(item => {
+ cellData.value.mAsset.push(item);
+ })
+ cellDoOperations.push({
+ action: "updateAttrViewCell",
+ id: cellData.id,
+ keyID: colId,
+ rowID,
+ avID: options.data.id,
+ data: cellData.value
+ });
+ cellUndoOperations.push({
+ action: "updateAttrViewCell",
+ id: cellData.id,
+ keyID: colId,
+ rowID,
+ avID: options.data.id,
+ data: {
+ mAsset: oldValue
+ }
+ });
+ updateAttrViewCellAnimation(item);
+ });
+ transaction(options.protyle, cellDoOperations, cellUndoOperations);
+}
+
+export const addAssetLink = (protyle: IProtyle, data: IAV, cellElements: HTMLElement[], target: HTMLElement) => {
+ const menu = new Menu("av-asset-link", () => {
+ const textElements = menu.element.querySelectorAll("textarea")
+ if (!textElements[0].value) {
+ return
+ }
+ updateAssetCell({
+ protyle,
+ data,
+ cellElements,
+ value: [{
+ type: "file",
+ name: textElements[1].value,
+ content: textElements[0].value,
+ }]
+ })
+ })
+
+ menu.addItem({
+ iconHTML: "",
+ label: ``,
+ });
+ menu.addItem({
+ iconHTML: "",
+ label: ``,
+ });
+ const rect = target.getBoundingClientRect();
+ menu.open({
+ x: rect.right,
+ y: rect.bottom,
+ w: rect.width,
+ h: rect.height,
+ });
+}
diff --git a/app/src/protyle/render/av/cell.ts b/app/src/protyle/render/av/cell.ts
index 1ffacafc3..09f74ba94 100644
--- a/app/src/protyle/render/av/cell.ts
+++ b/app/src/protyle/render/av/cell.ts
@@ -356,6 +356,9 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[]) => {
} else if (["select", "mSelect"].includes(type) && blockElement) {
openMenuPanel({protyle, blockElement, type: "select", cellElements});
return;
+ } else if (type === "mAsset" && blockElement) {
+ openMenuPanel({protyle, blockElement, type: "asset", cellElements});
+ return;
} else if (type === "date" && blockElement) {
openMenuPanel({protyle, blockElement, type: "date", cellElements});
return;
diff --git a/app/src/protyle/render/av/openMenuPanel.ts b/app/src/protyle/render/av/openMenuPanel.ts
index c4b16493f..276496db0 100644
--- a/app/src/protyle/render/av/openMenuPanel.ts
+++ b/app/src/protyle/render/av/openMenuPanel.ts
@@ -10,11 +10,12 @@ import {addSort, bindSortsEvent, getSortsHTML} from "./sort";
import {bindDateEvent, getDateHTML, setDateValue} from "./date";
import {formatNumber} from "./number";
import {removeAttrViewColAnimation} from "./action";
+import {addAssetLink, bindAssetEvent, getAssetHTML} from "./asset";
export const openMenuPanel = (options: {
protyle: IProtyle,
blockElement: Element,
- type: "select" | "properties" | "config" | "sorts" | "filters" | "edit" | "date",
+ type: "select" | "properties" | "config" | "sorts" | "filters" | "edit" | "date" | "asset",
colId?: string, // for edit
cellElements?: HTMLElement[] // for select & date
}) => {
@@ -42,6 +43,8 @@ export const openMenuPanel = (options: {
html = getFiltersHTML(data.view);
} else if (options.type === "select") {
html = getSelectHTML(data.view, options.cellElements);
+ } else if (options.type === "asset") {
+ html = getAssetHTML(data.view, options.cellElements);
} else if (options.type === "edit") {
html = getEditHTML({protyle: options.protyle, data, colId: options.colId});
} else if (options.type === "date") {
@@ -55,20 +58,21 @@ export const openMenuPanel = (options: {
avPanelElement = document.querySelector(".av__panel");
const menuElement = avPanelElement.lastElementChild as HTMLElement;
const tabRect = options.blockElement.querySelector(".layout-tab-bar").getBoundingClientRect();
- if (options.type === "select") {
+ if (["select", "date", "asset"].includes(options.type)) {
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
- bindSelectEvent(options.protyle, data, menuElement, options.cellElements);
- const inputElement = menuElement.querySelector("input");
- inputElement.select();
- inputElement.focus();
- } else if (options.type === "date") {
- const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
- setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
- bindDateEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
- const inputElement = menuElement.querySelector("input");
- inputElement.select();
- inputElement.focus();
+ if (options.type === "select") {
+ bindSelectEvent(options.protyle, data, menuElement, options.cellElements);
+ } else if (options.type === "date") {
+ bindDateEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
+ } else if (options.type === "asset") {
+ bindAssetEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
+ }
+ if (["select", "date"].includes(options.type)) {
+ const inputElement = menuElement.querySelector("input");
+ inputElement.select();
+ inputElement.focus();
+ }
} else {
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
if (options.type === "sorts") {
@@ -630,6 +634,11 @@ export const openMenuPanel = (options: {
event.preventDefault();
event.stopPropagation();
break;
+ } else if (type === "addAssetLink") {
+ addAssetLink(options.protyle, data, options.cellElements, target)
+ event.preventDefault();
+ event.stopPropagation();
+ break;
} else if (type === "clearDate") {
setDateValue({
cellElements: options.cellElements,
diff --git a/app/src/protyle/render/av/render.ts b/app/src/protyle/render/av/render.ts
index 1a0839a30..057d02af2 100644
--- a/app/src/protyle/render/av/render.ts
+++ b/app/src/protyle/render/av/render.ts
@@ -101,7 +101,7 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '