diff --git a/app/src/assets/scss/component/_snackbar.scss b/app/src/assets/scss/component/_snackbar.scss
index b4edf4028..cf2e75803 100644
--- a/app/src/assets/scss/component/_snackbar.scss
+++ b/app/src/assets/scss/component/_snackbar.scss
@@ -3,6 +3,7 @@
position: relative;
transition: transform 256ms cubic-bezier(.45, .05, .55, .95) 0ms;
text-align: right;
+ justify-self: end;
font-size: var(--b3-font-size);
&--hide {
diff --git a/app/src/dialog/message.ts b/app/src/dialog/message.ts
index c931c30ca..6d17e7dcd 100644
--- a/app/src/dialog/message.ts
+++ b/app/src/dialog/message.ts
@@ -32,10 +32,21 @@ export const initMessage = () => {
target = target.parentElement;
}
});
- const tempMessageElement = document.getElementById("tempMessage");
- if (tempMessageElement) {
- showMessage(tempMessageElement.innerHTML);
- tempMessageElement.remove();
+
+ const tempMessages = document.getElementById("tempMessages");
+ if (tempMessages) {
+ const items = tempMessages.querySelectorAll("[temp-message]");
+ items.forEach((item) => {
+ const timeoutString = item.getAttribute("data-timeout");
+ let timeout;
+ if (timeoutString) {
+ timeout = parseInt(timeoutString);
+ }
+ const type = item.getAttribute("data-type");
+ const messageId = item.getAttribute("data-message-id");
+ showMessage(item.innerHTML, timeout, type, messageId);
+ });
+ tempMessages.remove();
}
};
@@ -43,14 +54,25 @@ export const initMessage = () => {
export const showMessage = (message: string, timeout = 6000, type = "info", messageId?: string) => {
const messagesElement = document.getElementById("message").firstElementChild;
if (!messagesElement) {
- document.body.insertAdjacentHTML("beforeend", `
${message}
`);
+ let tempMessages = document.getElementById("tempMessages");
+ if (!tempMessages) {
+ document.body.insertAdjacentHTML("beforeend", "");
+ tempMessages = document.getElementById("tempMessages");
+ }
+
+ tempMessages.insertAdjacentHTML("beforeend", `${message}
`);
return;
}
const id = messageId || genUUID();
diff --git a/kernel/api/av.go b/kernel/api/av.go
index 84e1344d9..f981e98c3 100644
--- a/kernel/api/av.go
+++ b/kernel/api/av.go
@@ -735,3 +735,24 @@ func setAttributeViewBlockAttr(c *gin.Context) {
model.ReloadAttrView(avID)
}
+
+func batchSetAttributeViewBlockAttrs(c *gin.Context) {
+ ret := gulu.Ret.NewResult()
+ defer c.JSON(http.StatusOK, ret)
+
+ arg, ok := util.JsonArg(c, ret)
+ if !ok {
+ return
+ }
+
+ avID := arg["avID"].(string)
+ values := arg["values"].([]interface{})
+ err := model.BatchUpdateAttributeViewCells(nil, avID, values)
+ if err != nil {
+ ret.Code = -1
+ ret.Msg = err.Error()
+ return
+ }
+
+ model.ReloadAttrView(avID)
+}
diff --git a/kernel/api/router.go b/kernel/api/router.go
index 91f68bc27..628859b2b 100644
--- a/kernel/api/router.go
+++ b/kernel/api/router.go
@@ -436,6 +436,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/av/renderSnapshotAttributeView", model.CheckAuth, model.CheckAdminRole, renderSnapshotAttributeView)
ginServer.Handle("POST", "/api/av/getAttributeViewKeys", model.CheckAuth, getAttributeViewKeys)
ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setAttributeViewBlockAttr)
+ ginServer.Handle("POST", "/api/av/batchSetAttributeViewBlockAttrs", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, batchSetAttributeViewBlockAttrs)
ginServer.Handle("POST", "/api/av/searchAttributeView", model.CheckAuth, model.CheckReadonly, searchAttributeView)
ginServer.Handle("POST", "/api/av/getAttributeView", model.CheckAuth, model.CheckReadonly, getAttributeView)
ginServer.Handle("POST", "/api/av/searchAttributeViewRelationKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, searchAttributeViewRelationKey)
diff --git a/kernel/av/sort.go b/kernel/av/sort.go
index 56ca2eaa2..1dac261fc 100644
--- a/kernel/av/sort.go
+++ b/kernel/av/sort.go
@@ -397,7 +397,7 @@ func (value *Value) Compare(other *Value, attrView *AttributeView) int {
}
case KeyTypeRelation:
if nil != value.Relation && nil != other.Relation {
- if 1 < len(value.Relation.Contents) && 1 < len(other.Relation.Contents) && KeyTypeNumber == value.Relation.Contents[0].Type && KeyTypeNumber == other.Relation.Contents[0].Type {
+ if 0 < len(value.Relation.Contents) && 0 < len(other.Relation.Contents) && KeyTypeNumber == value.Relation.Contents[0].Type && KeyTypeNumber == other.Relation.Contents[0].Type {
v1, ok1 := util.Convert2Float(value.Relation.Contents[0].String(false))
v2, ok2 := util.Convert2Float(other.Relation.Contents[0].String(false))
if ok1 && ok2 {
@@ -435,7 +435,7 @@ func (value *Value) Compare(other *Value, attrView *AttributeView) int {
}
case KeyTypeRollup:
if nil != value.Rollup && nil != other.Rollup {
- if 1 < len(value.Rollup.Contents) && 1 < len(other.Rollup.Contents) && KeyTypeNumber == value.Rollup.Contents[0].Type && KeyTypeNumber == other.Rollup.Contents[0].Type {
+ if 0 < len(value.Rollup.Contents) && 0 < len(other.Rollup.Contents) && KeyTypeNumber == value.Rollup.Contents[0].Type && KeyTypeNumber == other.Rollup.Contents[0].Type {
v1, ok1 := util.Convert2Float(value.Rollup.Contents[0].String(false))
v2, ok2 := util.Convert2Float(other.Rollup.Contents[0].String(false))
if ok1 && ok2 {
diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go
index 4d20613dc..327fa0561 100644
--- a/kernel/model/attribute_view.go
+++ b/kernel/model/attribute_view.go
@@ -3955,12 +3955,58 @@ func updateAttributeViewCell(operation *Operation, tx *Transaction) (err error)
return
}
+func BatchUpdateAttributeViewCells(tx *Transaction, avID string, values []interface{}) (err error) {
+ attrView, err := av.ParseAttributeView(avID)
+ if err != nil {
+ return
+ }
+
+ for _, value := range values {
+ v := value.(map[string]interface{})
+ keyID := v["keyID"].(string)
+ rowID := v["rowID"].(string)
+ valueData := v["value"]
+ _, err = updateAttributeViewValue(tx, attrView, keyID, rowID, valueData)
+ if err != nil {
+ return
+ }
+ }
+
+ if err = av.SaveAttributeView(attrView); err != nil {
+ return
+ }
+
+ relatedAvIDs := av.GetSrcAvIDs(avID)
+ for _, relatedAvID := range relatedAvIDs {
+ ReloadAttrView(relatedAvID)
+ }
+ return
+}
+
func UpdateAttributeViewCell(tx *Transaction, avID, keyID, rowID string, valueData interface{}) (val *av.Value, err error) {
attrView, err := av.ParseAttributeView(avID)
if err != nil {
return
}
+ val, err = updateAttributeViewValue(tx, attrView, keyID, rowID, valueData)
+ if nil != err {
+ return
+ }
+
+ if err = av.SaveAttributeView(attrView); err != nil {
+ return
+ }
+
+ relatedAvIDs := av.GetSrcAvIDs(avID)
+ for _, relatedAvID := range relatedAvIDs {
+ ReloadAttrView(relatedAvID)
+ }
+ return
+}
+
+func updateAttributeViewValue(tx *Transaction, attrView *av.AttributeView, keyID, rowID string, valueData interface{}) (val *av.Value, err error) {
+ avID := attrView.ID
var blockVal *av.Value
for _, kv := range attrView.KeyValues {
if av.KeyTypeBlock == kv.Key.Type {
@@ -4203,15 +4249,6 @@ func UpdateAttributeViewCell(tx *Transaction, avID, keyID, rowID string, valueDa
}
}
}
-
- relatedAvIDs := av.GetSrcAvIDs(avID)
- for _, relatedAvID := range relatedAvIDs {
- ReloadAttrView(relatedAvID)
- }
-
- if err = av.SaveAttributeView(attrView); err != nil {
- return
- }
return
}