mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-09-22 00:20:47 +02:00
🎨 Add Relation and Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9888
This commit is contained in:
parent
0547f27839
commit
fca3bf6855
5 changed files with 215 additions and 5 deletions
|
@ -66,6 +66,8 @@ const (
|
|||
KeyTypeCreated KeyType = "created"
|
||||
KeyTypeUpdated KeyType = "updated"
|
||||
KeyTypeCheckbox KeyType = "checkbox"
|
||||
KeyTypeRelation KeyType = "relation"
|
||||
KeyTypeRollup KeyType = "rollup"
|
||||
)
|
||||
|
||||
// Key 描述了属性视图属性列的基础结构。
|
||||
|
@ -77,9 +79,23 @@ type Key struct {
|
|||
|
||||
// 以下是某些列类型的特有属性
|
||||
|
||||
Options []*KeySelectOption `json:"options,omitempty"` // 选项列表
|
||||
NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化
|
||||
Template string `json:"template"` // 模板内容
|
||||
// 单选/多选列
|
||||
Options []*KeySelectOption `json:"options,omitempty"` // 选项列表
|
||||
|
||||
// 数字列
|
||||
NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化
|
||||
|
||||
// 模板列
|
||||
Template string `json:"template"` // 模板内容
|
||||
|
||||
// 关联列
|
||||
RelationAvID string `json:"relationAvID"` // 关联的属性视图 ID
|
||||
RelationKeyID string `json:"relationKeyID"` // 关联列 ID
|
||||
IsBiRelation bool `json:"isBiRelation"` // 是否双向关联
|
||||
BackRelationKeyID string `json:"backRelationKeyID"` // 双向关联时回链关联列的 ID
|
||||
|
||||
// 汇总列
|
||||
RollupKeyID string `json:"rollupKeyID"` // 汇总列 ID
|
||||
}
|
||||
|
||||
func NewKey(id, name, icon string, keyType KeyType) *Key {
|
||||
|
|
|
@ -186,6 +186,10 @@ func (value *Value) Compare(other *Value) int {
|
|||
}
|
||||
return 0
|
||||
}
|
||||
case KeyTypeRelation:
|
||||
// TODO: relation compare
|
||||
case KeyTypeRollup:
|
||||
// TODO: rollup compare
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -567,6 +571,29 @@ func (value *Value) CompareOperator(other *Value, operator FilterOperator) bool
|
|||
return !value.Checkbox.Checked
|
||||
}
|
||||
}
|
||||
|
||||
if nil != value.Relation && nil != other.Relation {
|
||||
switch operator {
|
||||
case FilterOperatorContains:
|
||||
if "" == strings.TrimSpace(other.Relation.Content) {
|
||||
return true
|
||||
}
|
||||
return strings.Contains(value.Relation.Content, other.Relation.Content)
|
||||
case FilterOperatorDoesNotContain:
|
||||
if "" == strings.TrimSpace(other.Relation.Content) {
|
||||
return true
|
||||
}
|
||||
return !strings.Contains(value.Relation.Content, other.Relation.Content)
|
||||
case FilterOperatorIsEmpty:
|
||||
return "" == strings.TrimSpace(value.Relation.Content)
|
||||
case FilterOperatorIsNotEmpty:
|
||||
return "" != strings.TrimSpace(value.Relation.Content)
|
||||
}
|
||||
}
|
||||
|
||||
if nil != value.Rollup && nil != other.Rollup {
|
||||
// TODO: rollup filter
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -760,6 +787,10 @@ func (table *Table) CalcCols() {
|
|||
table.calcColUpdated(col, i)
|
||||
case KeyTypeCheckbox:
|
||||
table.calcColCheckbox(col, i)
|
||||
case KeyTypeRelation:
|
||||
table.calcColRelation(col, i)
|
||||
case KeyTypeRollup:
|
||||
table.calcColRollup(col, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1912,3 +1943,133 @@ func (table *Table) calcColCheckbox(col *TableColumn, colIndex int) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (table *Table) calcColRelation(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.Relation {
|
||||
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.Relation {
|
||||
for _, id := range row.Cells[colIndex].Value.Relation.BlockIDs {
|
||||
if !uniqueValues[id] {
|
||||
uniqueValues[id] = 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.Relation || 0 == len(row.Cells[colIndex].Value.Relation.BlockIDs) {
|
||||
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.Relation && 0 < len(row.Cells[colIndex].Value.Relation.BlockIDs) {
|
||||
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.Relation || 0 == len(row.Cells[colIndex].Value.Relation.BlockIDs) {
|
||||
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.Relation && 0 < len(row.Cells[colIndex].Value.Relation.BlockIDs) {
|
||||
countNotEmpty++
|
||||
}
|
||||
}
|
||||
if 0 < len(table.Rows) {
|
||||
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (table *Table) calcColRollup(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.Rollup {
|
||||
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.Rollup {
|
||||
for _, content := range row.Cells[colIndex].Value.Rollup.Contents {
|
||||
if !uniqueValues[content] {
|
||||
uniqueValues[content] = 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.Rollup || 0 == len(row.Cells[colIndex].Value.Rollup.Contents) {
|
||||
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.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) {
|
||||
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.Rollup || 0 == len(row.Cells[colIndex].Value.Rollup.Contents) {
|
||||
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.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) {
|
||||
countNotEmpty++
|
||||
}
|
||||
}
|
||||
if 0 < len(table.Rows) {
|
||||
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ type Value struct {
|
|||
Created *ValueCreated `json:"created,omitempty"`
|
||||
Updated *ValueUpdated `json:"updated,omitempty"`
|
||||
Checkbox *ValueCheckbox `json:"checkbox,omitempty"`
|
||||
Relation *ValueRelation `json:"relation,omitempty"`
|
||||
Rollup *ValueRollup `json:"rollup,omitempty"`
|
||||
}
|
||||
|
||||
func (value *Value) String() string {
|
||||
|
@ -135,6 +137,16 @@ func (value *Value) String() string {
|
|||
return "√"
|
||||
}
|
||||
return ""
|
||||
case KeyTypeRelation:
|
||||
if nil == value.Relation {
|
||||
return ""
|
||||
}
|
||||
return value.Relation.Content
|
||||
case KeyTypeRollup:
|
||||
if nil == value.Rollup {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(value.Rollup.Contents, " ")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -433,3 +445,12 @@ func NewFormattedValueUpdated(content, content2 int64, format UpdatedFormat) (re
|
|||
type ValueCheckbox struct {
|
||||
Checked bool `json:"checked"`
|
||||
}
|
||||
|
||||
type ValueRelation struct {
|
||||
Content string `json:"content"`
|
||||
BlockIDs []string `json:"blockIDs"`
|
||||
}
|
||||
|
||||
type ValueRollup struct {
|
||||
Contents []string `json:"contents"`
|
||||
}
|
||||
|
|
|
@ -1490,7 +1490,9 @@ 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, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox:
|
||||
case av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail,
|
||||
av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox,
|
||||
av.KeyTypeRelation, av.KeyTypeRollup:
|
||||
var icon string
|
||||
if nil != operation.Data {
|
||||
icon = operation.Data.(string)
|
||||
|
@ -1584,7 +1586,9 @@ 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, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox:
|
||||
case av.KeyTypeBlock, av.KeyTypeText, av.KeyTypeNumber, av.KeyTypeDate, av.KeyTypeSelect, av.KeyTypeMSelect, av.KeyTypeURL, av.KeyTypeEmail,
|
||||
av.KeyTypePhone, av.KeyTypeMAsset, av.KeyTypeTemplate, av.KeyTypeCreated, av.KeyTypeUpdated, av.KeyTypeCheckbox,
|
||||
av.KeyTypeRelation, av.KeyTypeRollup:
|
||||
for _, keyValues := range attrView.KeyValues {
|
||||
if keyValues.Key.ID == operation.ID {
|
||||
keyValues.Key.Name = strings.TrimSpace(operation.Name)
|
||||
|
|
|
@ -812,6 +812,14 @@ func FillAttributeViewTableCellNilValue(tableCell *av.TableCell, rowID, colID st
|
|||
if nil == tableCell.Value.Checkbox {
|
||||
tableCell.Value.Checkbox = &av.ValueCheckbox{}
|
||||
}
|
||||
case av.KeyTypeRelation:
|
||||
if nil == tableCell.Value.Relation {
|
||||
tableCell.Value.Relation = &av.ValueRelation{}
|
||||
}
|
||||
case av.KeyTypeRollup:
|
||||
if nil == tableCell.Value.Rollup {
|
||||
tableCell.Value.Rollup = &av.ValueRollup{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue