Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
2c73687083
7 changed files with 243 additions and 19 deletions
|
@ -26,6 +26,51 @@ import (
|
|||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getAttributeView(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, _ := util.JsonArg(c, ret)
|
||||
if nil == arg {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
av := model.GetAttributeView(id)
|
||||
ret.Data = map[string]interface{}{
|
||||
"av": av,
|
||||
}
|
||||
}
|
||||
|
||||
func searchAttributeView(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, _ := util.JsonArg(c, ret)
|
||||
if nil == arg {
|
||||
return
|
||||
}
|
||||
|
||||
keyword := arg["keyword"].(string)
|
||||
page := 1
|
||||
pageArg := arg["page"]
|
||||
if nil != pageArg {
|
||||
page = int(pageArg.(float64))
|
||||
}
|
||||
|
||||
pageSize := 10
|
||||
pageSizeArg := arg["pageSize"]
|
||||
if nil != pageSizeArg {
|
||||
pageSize = int(pageSizeArg.(float64))
|
||||
}
|
||||
|
||||
results, total := model.SearchAttributeView(keyword, page, pageSize)
|
||||
ret.Data = map[string]interface{}{
|
||||
"results": results,
|
||||
"total": total,
|
||||
}
|
||||
}
|
||||
|
||||
func renderSnapshotAttributeView(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
|
|
@ -386,6 +386,8 @@ func ServeAPI(ginServer *gin.Engine) {
|
|||
ginServer.Handle("POST", "/api/av/renderSnapshotAttributeView", model.CheckAuth, renderSnapshotAttributeView)
|
||||
ginServer.Handle("POST", "/api/av/getAttributeViewKeys", model.CheckAuth, getAttributeViewKeys)
|
||||
ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, model.CheckReadonly, setAttributeViewBlockAttr)
|
||||
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/ai/chatGPT", model.CheckAuth, chatGPT)
|
||||
ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction)
|
||||
|
|
|
@ -80,7 +80,7 @@ type Key struct {
|
|||
// 以下是某些列类型的特有属性
|
||||
|
||||
// 单选/多选列
|
||||
Options []*KeySelectOption `json:"options,omitempty"` // 选项列表
|
||||
Options []*SelectOption `json:"options,omitempty"` // 选项列表
|
||||
|
||||
// 数字列
|
||||
NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化
|
||||
|
@ -89,13 +89,10 @@ type Key struct {
|
|||
Template string `json:"template"` // 模板内容
|
||||
|
||||
// 关联列
|
||||
RelationAvID string `json:"relationAvID"` // 关联的属性视图 ID
|
||||
RelationKeyID string `json:"relationKeyID"` // 关联列 ID
|
||||
IsBiRelation bool `json:"isBiRelation"` // 是否双向关联
|
||||
BackRelationKeyID string `json:"backRelationKeyID"` // 双向关联时回链关联列的 ID
|
||||
Relation *Relation `json:"relation,omitempty"` // 关联信息
|
||||
|
||||
// 汇总列
|
||||
RollupKeyID string `json:"rollupKeyID"` // 汇总列 ID
|
||||
Rollup *Rollup `json:"rollup,omitempty"` // 汇总信息
|
||||
}
|
||||
|
||||
func NewKey(id, name, icon string, keyType KeyType) *Key {
|
||||
|
@ -107,7 +104,17 @@ func NewKey(id, name, icon string, keyType KeyType) *Key {
|
|||
}
|
||||
}
|
||||
|
||||
type KeySelectOption struct {
|
||||
type Rollup struct {
|
||||
KeyID string `json:"keyID"` // 汇总列 ID
|
||||
}
|
||||
|
||||
type Relation struct {
|
||||
AvID string `json:"avID"` // 关联的属性视图 ID
|
||||
IsTwoWay bool `json:"isTwoWay"` // 是否双向关联
|
||||
BackKeyID string `json:"backKeyID"` // 双向关联时回链关联列的 ID
|
||||
}
|
||||
|
||||
type SelectOption struct {
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
|
|
@ -651,9 +651,11 @@ type TableColumn struct {
|
|||
|
||||
// 以下是某些列类型的特有属性
|
||||
|
||||
Options []*KeySelectOption `json:"options,omitempty"` // 选项列表
|
||||
NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化
|
||||
Template string `json:"template"` // 模板内容
|
||||
Options []*SelectOption `json:"options,omitempty"` // 选项列表
|
||||
NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化
|
||||
Template string `json:"template"` // 模板内容
|
||||
Relation *Relation `json:"relation,omitempty"` // 关联列
|
||||
Rollup *Rollup `json:"rollup,omitempty"` // 汇总列
|
||||
}
|
||||
|
||||
type TableCell struct {
|
||||
|
|
|
@ -37,6 +37,70 @@ import (
|
|||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func GetAttributeView(avID string) (ret *av.AttributeView) {
|
||||
waitForSyncingStorages()
|
||||
|
||||
ret, _ = av.ParseAttributeView(avID)
|
||||
return
|
||||
}
|
||||
|
||||
type SearchAttributeViewResult struct {
|
||||
AvID string `json:"avID"`
|
||||
AvName string `json:"avName"`
|
||||
BlockID string `json:"blockID"`
|
||||
}
|
||||
|
||||
func SearchAttributeView(keyword string, page int, pageSize int) (ret []*SearchAttributeViewResult, pageCount int) {
|
||||
waitForSyncingStorages()
|
||||
|
||||
ret = []*SearchAttributeViewResult{}
|
||||
blocks, _, _, pageCount := FullTextSearchBlock(keyword, nil, nil, map[string]bool{"databaseBlock": true}, 0, 7, 0, page, pageSize)
|
||||
trees := map[string]*parse.Tree{}
|
||||
for _, block := range blocks {
|
||||
tree := trees[block.RootID]
|
||||
if nil == tree {
|
||||
tree, _ = loadTreeByBlockID(block.ID)
|
||||
if nil != tree {
|
||||
trees[block.RootID] = tree
|
||||
}
|
||||
}
|
||||
if nil == tree {
|
||||
continue
|
||||
}
|
||||
|
||||
node := treenode.GetNodeInTree(tree, block.ID)
|
||||
if nil == node {
|
||||
continue
|
||||
}
|
||||
|
||||
if "" == node.AttributeViewID {
|
||||
continue
|
||||
}
|
||||
|
||||
avID := node.AttributeViewID
|
||||
attrView, _ := av.ParseAttributeView(avID)
|
||||
if nil == attrView {
|
||||
continue
|
||||
}
|
||||
|
||||
exist := false
|
||||
for _, result := range ret {
|
||||
if result.AvID == avID {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
ret = append(ret, &SearchAttributeViewResult{
|
||||
AvID: avID,
|
||||
AvName: attrView.Name,
|
||||
BlockID: block.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type BlockAttributeViewKeys struct {
|
||||
AvID string `json:"avID"`
|
||||
AvName string `json:"avName"`
|
||||
|
@ -489,6 +553,8 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a
|
|||
Options: key.Options,
|
||||
NumberFormat: key.NumberFormat,
|
||||
Template: key.Template,
|
||||
Relation: key.Relation,
|
||||
Rollup: key.Rollup,
|
||||
Wrap: col.Wrap,
|
||||
Hidden: col.Hidden,
|
||||
Width: col.Width,
|
||||
|
@ -655,6 +721,102 @@ func getRowBlockValue(keyValues []*av.KeyValues) (ret *av.Value) {
|
|||
return
|
||||
}
|
||||
|
||||
func (tx *Transaction) doUpdateAttrViewColRelation(operation *Operation) (ret *TxErr) {
|
||||
err := updateAttributeViewColRelation(operation)
|
||||
if nil != err {
|
||||
return &TxErr{code: TxErrWriteAttributeView, id: operation.AvID, msg: err.Error()}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func updateAttributeViewColRelation(operation *Operation) (err error) {
|
||||
// operation.AvID 源 avID
|
||||
// operation.ID 目标 avID
|
||||
// operation.KeyID 源 av 关联列 ID
|
||||
// operation.IsTwoWay 是否双向关联
|
||||
// operation.BackRelationKeyID 双向关联的目标关联列 ID
|
||||
// operation.Name 双向关联的目标关联列名称
|
||||
|
||||
srcAv, err := av.ParseAttributeView(operation.AvID)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
destAv, err := av.ParseAttributeView(operation.ID)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
isSameAv := srcAv.ID == destAv.ID
|
||||
|
||||
for _, keyValues := range srcAv.KeyValues {
|
||||
if keyValues.Key.ID == operation.KeyID {
|
||||
// 已经设置过双向关联的话需要先断开双向关联
|
||||
if nil != keyValues.Key.Relation && keyValues.Key.Relation.IsTwoWay {
|
||||
oldDestAv, parseErr := av.ParseAttributeView(keyValues.Key.Relation.AvID)
|
||||
if nil == parseErr {
|
||||
isOldSameAv := oldDestAv.ID == destAv.ID
|
||||
if isOldSameAv {
|
||||
oldDestAv = destAv
|
||||
}
|
||||
|
||||
oldDestKey, _ := oldDestAv.GetKey(keyValues.Key.Relation.BackKeyID)
|
||||
if nil != oldDestKey && nil != oldDestKey.Relation && oldDestKey.Relation.AvID == srcAv.ID && oldDestKey.Relation.IsTwoWay {
|
||||
oldDestKey.Relation.IsTwoWay = false
|
||||
oldDestKey.Relation.BackKeyID = ""
|
||||
}
|
||||
|
||||
if !isOldSameAv {
|
||||
err = av.SaveAttributeView(oldDestAv)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyValues.Key.Relation = &av.Relation{
|
||||
AvID: operation.ID,
|
||||
IsTwoWay: operation.IsTwoWay,
|
||||
BackKeyID: operation.BackRelationKeyID,
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
destAdded := false
|
||||
for _, keyValues := range destAv.KeyValues {
|
||||
if keyValues.Key.ID == operation.BackRelationKeyID {
|
||||
keyValues.Key.Relation = &av.Relation{
|
||||
AvID: operation.AvID,
|
||||
IsTwoWay: operation.IsTwoWay,
|
||||
BackKeyID: operation.KeyID,
|
||||
}
|
||||
destAdded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destAdded {
|
||||
destAv.KeyValues = append(destAv.KeyValues, &av.KeyValues{
|
||||
Key: &av.Key{
|
||||
ID: operation.BackRelationKeyID,
|
||||
Name: operation.Name,
|
||||
Type: av.KeyTypeRelation,
|
||||
Relation: &av.Relation{AvID: operation.AvID, IsTwoWay: operation.IsTwoWay, BackKeyID: operation.KeyID},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
err = av.SaveAttributeView(srcAv)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
if !isSameAv {
|
||||
err = av.SaveAttributeView(destAv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tx *Transaction) doSortAttrViewView(operation *Operation) (ret *TxErr) {
|
||||
avID := operation.AvID
|
||||
attrView, err := av.ParseAttributeView(avID)
|
||||
|
@ -1948,7 +2110,7 @@ func updateAttributeViewColumnOptions(operation *Operation) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
options := []*av.KeySelectOption{}
|
||||
options := []*av.SelectOption{}
|
||||
if err = gulu.JSON.UnmarshalJSON(jsonData, &options); nil != err {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -258,6 +258,8 @@ func performTx(tx *Transaction) (ret *TxErr) {
|
|||
ret = tx.doDuplicateAttrViewView(op)
|
||||
case "sortAttrViewView":
|
||||
ret = tx.doSortAttrViewView(op)
|
||||
case "updateAttrViewColRelation":
|
||||
ret = tx.doUpdateAttrViewColRelation(op)
|
||||
}
|
||||
|
||||
if nil != ret {
|
||||
|
@ -1181,14 +1183,16 @@ type Operation struct {
|
|||
|
||||
DeckID string `json:"deckID"` // 用于添加/删除闪卡
|
||||
|
||||
AvID string `json:"avID"` // 属性视图 ID
|
||||
SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中
|
||||
IsDetached bool `json:"isDetached"` // 用于标识是否是脱离块,仅存在于属性视图中
|
||||
Name string `json:"name"` // 属性视图列名
|
||||
Typ string `json:"type"` // 属性视图列类型
|
||||
Format string `json:"format"` // 属性视图列格式化
|
||||
KeyID string `json:"keyID"` // 属性视列 ID
|
||||
RowID string `json:"rowID"` // 属性视图行 ID
|
||||
AvID string `json:"avID"` // 属性视图 ID
|
||||
SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中
|
||||
IsDetached bool `json:"isDetached"` // 用于标识是否是脱离块,仅存在于属性视图中
|
||||
Name string `json:"name"` // 属性视图列名
|
||||
Typ string `json:"type"` // 属性视图列类型
|
||||
Format string `json:"format"` // 属性视图列格式化
|
||||
KeyID string `json:"keyID"` // 属性视列 ID
|
||||
RowID string `json:"rowID"` // 属性视图行 ID
|
||||
IsTwoWay bool `json:"isTwoWay"` // 属性视图关联列是否是双向关系
|
||||
BackRelationKeyID string `json:"backRelationKeyID"` // 属性视图关联列回链关联列的 ID
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
|
|
|
@ -614,6 +614,8 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a
|
|||
Options: key.Options,
|
||||
NumberFormat: key.NumberFormat,
|
||||
Template: key.Template,
|
||||
Relation: key.Relation,
|
||||
Rollup: key.Rollup,
|
||||
Wrap: col.Wrap,
|
||||
Hidden: col.Hidden,
|
||||
Width: col.Width,
|
||||
|
|
Loading…
Add table
Reference in a new issue