diff --git a/kernel/av/av.go b/kernel/av/av.go index 2d50e2848..a8794f382 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -55,16 +55,17 @@ type KeyValues struct { type KeyType string const ( - KeyTypeBlock KeyType = "block" - KeyTypeText KeyType = "text" - KeyTypeNumber KeyType = "number" - KeyTypeDate KeyType = "date" - KeyTypeSelect KeyType = "select" - KeyTypeMSelect KeyType = "mSelect" - KeyTypeURL KeyType = "url" - KeyTypeEmail KeyType = "email" - KeyTypePhone KeyType = "phone" - KeyTypeMAsset KeyType = "mAsset" + KeyTypeBlock KeyType = "block" + KeyTypeText KeyType = "text" + KeyTypeNumber KeyType = "number" + KeyTypeDate KeyType = "date" + KeyTypeSelect KeyType = "select" + KeyTypeMSelect KeyType = "mSelect" + KeyTypeURL KeyType = "url" + KeyTypeEmail KeyType = "email" + KeyTypePhone KeyType = "phone" + KeyTypeMAsset KeyType = "mAsset" + KeyTypeTemplate KeyType = "template" ) // Key 描述了属性视图属性列的基础结构。 @@ -100,15 +101,16 @@ type Value struct { Type KeyType `json:"type,omitempty"` IsDetached bool `json:"isDetached,omitempty"` - Block *ValueBlock `json:"block,omitempty"` - Text *ValueText `json:"text,omitempty"` - Number *ValueNumber `json:"number,omitempty"` - Date *ValueDate `json:"date,omitempty"` - MSelect []*ValueSelect `json:"mSelect,omitempty"` - URL *ValueURL `json:"url,omitempty"` - Email *ValueEmail `json:"email,omitempty"` - Phone *ValuePhone `json:"phone,omitempty"` - MAsset []*ValueAsset `json:"mAsset,omitempty"` + Block *ValueBlock `json:"block,omitempty"` + Text *ValueText `json:"text,omitempty"` + Number *ValueNumber `json:"number,omitempty"` + Date *ValueDate `json:"date,omitempty"` + MSelect []*ValueSelect `json:"mSelect,omitempty"` + URL *ValueURL `json:"url,omitempty"` + Email *ValueEmail `json:"email,omitempty"` + Phone *ValuePhone `json:"phone,omitempty"` + MAsset []*ValueAsset `json:"mAsset,omitempty"` + Template *ValueTemplate `json:"template,omitempty"` } func (value *Value) String() string { @@ -139,6 +141,8 @@ func (value *Value) String() string { ret = append(ret, v.Content) } return strings.Join(ret, " ") + case KeyTypeTemplate: + return value.Template.RenderedContent default: return "" } @@ -345,6 +349,11 @@ type ValueAsset struct { Content string `json:"content"` } +type ValueTemplate struct { + Content string `json:"content"` + RenderedContent string `json:"renderedContent"` +} + // View 描述了视图的结构。 type View struct { ID string `json:"id"` // 视图 ID diff --git a/kernel/av/table.go b/kernel/av/table.go index 6bc97e8bc..f8116e691 100644 --- a/kernel/av/table.go +++ b/kernel/av/table.go @@ -509,6 +509,71 @@ func (table *Table) CalcCols() { table.calcColPhone(col, i) case KeyTypeMAsset: table.calcColMAsset(col, i) + case KeyTypeTemplate: + table.calcColTemplate(col, i) + } + } +} + +func (table *Table) calcColTemplate(col *TableColumn, colIndex int) { + switch col.Calc.Operator { + case CalcOperatorCountAll: + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.RenderedContent { + countValues++ + } + } + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.RenderedContent { + if !uniqueValues[row.Cells[colIndex].Value.Template.RenderedContent] { + uniqueValues[row.Cells[colIndex].Value.Template.RenderedContent] = true + countUniqueValues++ + } + } + } + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, row := range table.Rows { + if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Template || "" == row.Cells[colIndex].Value.Template.RenderedContent { + countEmpty++ + } + } + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.RenderedContent { + countNotEmpty++ + } + } + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, row := range table.Rows { + if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Template || "" == row.Cells[colIndex].Value.Template.RenderedContent { + countEmpty++ + } + } + if 0 < len(table.Rows) { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.RenderedContent { + countNotEmpty++ + } + } + if 0 < len(table.Rows) { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} } } } diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index ea549adbe..9278b8458 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -788,7 +788,7 @@ func addAttributeViewColumn(operation *Operation) (err error) { keyType := av.KeyType(operation.Typ) switch keyType { - case av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail, av.KeyTypePhone, av.KeyTypeMAsset: + case av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail, av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate: key := av.NewKey(operation.ID, operation.Name, keyType) attrView.KeyValues = append(attrView.KeyValues, &av.KeyValues{Key: key}) @@ -847,7 +847,7 @@ func updateAttributeViewColumn(operation *Operation) (err error) { colType := av.KeyType(operation.Typ) switch colType { - case av.KeyTypeBlock, av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail, av.KeyTypePhone, av.KeyTypeMAsset: + case av.KeyTypeBlock, av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail, av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate: for _, keyValues := range attrView.KeyValues { if keyValues.Key.ID == operation.ID { keyValues.Key.Name = operation.Name