diff --git a/kernel/av/av.go b/kernel/av/av.go index 9c616d673..6b2a55899 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -65,6 +65,7 @@ const ( KeyTypeURL KeyType = "url" KeyTypeEmail KeyType = "email" KeyTypePhone KeyType = "phone" + KeyTypeMAsset KeyType = "mAsset" ) // Key 描述了属性视图属性列的基础结构。 @@ -107,6 +108,7 @@ type Value struct { URL *ValueURL `json:"url,omitempty"` Email *ValueEmail `json:"email,omitempty"` Phone *ValuePhone `json:"phone,omitempty"` + MAsset []*ValueAsset `json:"mAsset,omitempty"` } func (value *Value) String() string { @@ -131,6 +133,12 @@ func (value *Value) String() string { return value.Email.Content case KeyTypePhone: return value.Phone.Content + case KeyTypeMAsset: + var ret []string + for _, v := range value.MAsset { + ret = append(ret, v.Content) + } + return strings.Join(ret, " ") default: return "" } @@ -324,6 +332,19 @@ type ValuePhone struct { Content string `json:"content"` } +type AssetType string + +const ( + AssetTypeFile = "file" + AssetTypeImage = "image" +) + +type ValueAsset struct { + Type AssetType `json:"type"` + Name string `json:"name"` + Content string `json:"content"` +} + // View 描述了视图的结构。 type View struct { ID string `json:"id"` // 视图 ID diff --git a/kernel/av/table.go b/kernel/av/table.go index b774d109e..6bc97e8bc 100644 --- a/kernel/av/table.go +++ b/kernel/av/table.go @@ -132,6 +132,17 @@ func (value *Value) Compare(other *Value) int { if nil != value.Phone && nil != other.Phone { return strings.Compare(value.Phone.Content, other.Phone.Content) } + if nil != value.MAsset && nil != other.MAsset { + var v1 string + for _, v := range value.MAsset { + v1 += v.Content + } + var v2 string + for _, v := range other.MAsset { + v2 += v.Content + } + return strings.Compare(v1, v2) + } return 0 } @@ -328,6 +339,36 @@ func (value *Value) CompareOperator(other *Value, operator FilterOperator) bool } } + if nil != value.MAsset && nil != other.MAsset && 0 < len(value.MAsset) && 0 < len(other.MAsset) { + switch operator { + case FilterOperatorIsEqual, FilterOperatorContains: + contains := false + for _, v := range value.MAsset { + for _, v2 := range other.MAsset { + if v.Content == v2.Content { + contains = true + break + } + } + } + return contains + case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain: + contains := false + for _, v := range value.MAsset { + for _, v2 := range other.MAsset { + if v.Content == v2.Content { + contains = true + break + } + } + } + return !contains + case FilterOperatorIsEmpty: + return 0 == len(value.MAsset) || 1 == len(value.MAsset) && "" == value.MAsset[0].Content + case FilterOperatorIsNotEmpty: + return 0 != len(value.MAsset) && !(1 == len(value.MAsset) && "" == value.MAsset[0].Content) + } + } return true } @@ -466,6 +507,73 @@ func (table *Table) CalcCols() { table.calcColEmail(col, i) case KeyTypePhone: table.calcColPhone(col, i) + case KeyTypeMAsset: + table.calcColMAsset(col, i) + } + } +} + +func (table *Table) calcColMAsset(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.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { + countValues += len(row.Cells[colIndex].Value.MAsset) + } + } + 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.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { + for _, sel := range row.Cells[colIndex].Value.MAsset { + if _, ok := uniqueValues[sel.Content]; !ok { + uniqueValues[sel.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.MAsset || 0 == len(row.Cells[colIndex].Value.MAsset) { + 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.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { + 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.MAsset || 0 == len(row.Cells[colIndex].Value.MAsset) { + 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.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { + countNotEmpty++ + } + } + if 0 < len(table.Rows) { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} } } } @@ -807,32 +915,32 @@ func (table *Table) calcColNumber(col *TableColumn, colIndex int) { col.Calc.Result = &Value{Number: NewFormattedValueNumber(min, col.NumberFormat)} } case CalcOperatorMax: - max := -math.MaxFloat64 + maxVal := -math.MaxFloat64 for _, row := range table.Rows { if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - if row.Cells[colIndex].Value.Number.Content > max { - max = row.Cells[colIndex].Value.Number.Content + if row.Cells[colIndex].Value.Number.Content > maxVal { + maxVal = row.Cells[colIndex].Value.Number.Content } } } - if -math.MaxFloat64 != max { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(max, col.NumberFormat)} + if -math.MaxFloat64 != maxVal { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, col.NumberFormat)} } case CalcOperatorRange: - min := math.MaxFloat64 - max := -math.MaxFloat64 + minVal := math.MaxFloat64 + maxVal := -math.MaxFloat64 for _, row := range table.Rows { if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - if row.Cells[colIndex].Value.Number.Content < min { - min = row.Cells[colIndex].Value.Number.Content + if row.Cells[colIndex].Value.Number.Content < minVal { + minVal = row.Cells[colIndex].Value.Number.Content } - if row.Cells[colIndex].Value.Number.Content > max { - max = row.Cells[colIndex].Value.Number.Content + if row.Cells[colIndex].Value.Number.Content > maxVal { + maxVal = row.Cells[colIndex].Value.Number.Content } } } - if math.MaxFloat64 != min && -math.MaxFloat64 != max { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(max-min, col.NumberFormat)} + if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, col.NumberFormat)} } } }