# 7.5 组件样例

# 文件概览

├── cards                  // 固定目录
    └── cardName              // 组件名称
        └── panel                 // 组件自定义配置面板
            ├── Config.vue            // 组件配置内容
            ├── index.js              // 组件配置入口
        ├── static                    // 组件静态资源
        ├── card.json             // 组件元数据文件
        ├── Card.vue              // 组件vue代码
        ├── index.js              // 组件入口
        ├── README.md             // 组件介绍文档
├── manifest.json          // 资源包元数据文件
└── README.md              // 资源包内容介绍文件

# 图表类规范示例(曲线图为例)

曲线图全

# 样例1:新增组件交互

下面以新增标题的点击事件为例,点击后将标题内容作为入参,朝指定url跳转

# 1、在card.json中配置交互事件

找到cardEvents数组,可以看到组件已经有一个交互事件了,为图表点击,其中包含有两个可传递参数的变量,分别是x和y。于是在cardEvents中新增一个对象

    "cardEvents": [         // 组件交互配置
      {
        "name": "图表点击",     // 交互事件名称
        "id": "event-one",     // 交互事件id, 同一组件内唯一
        "variable": [          // 交互事件参数列表, 自定义字段名称/类型
          {
            "field": "x",         // 交互事件参数字段, 自定义
            "type": "string"      // 交互事件参数类型, 自定义
          },
          {
            "field": "y",
            "type": "number"
          }
        ]
      }
    ]
"cardEvents": [       
  {
...    // 图表点击
  },
  // 新增如下内容
  {
    "name": "标题点击",
    "id": "click-title",
    "variable": [        
      {
        "field": "param",       
        "type": "string"      
      }
    ]
  }
]

# 2、在Card.vue中通过message消息发布,传递参数

2.1、找到卡片标题的DOM结构

  <div class="wemax-card-container">
    <div class="inner">
      <CardTitle :options="options"/>
      <div ref="chartRef" class="weMax-container"></div>
    </div>
  </div>

新增click点击事件,使用native是防止点击无效,代码如下所示

  <CardTitle :options="options" @click.native="handleTitleClick"/>

2.2、在methods中,新增标题点击函数handleTitleClick的处理逻辑

前面说到,该点击事件需要传入当前卡片标题的内容,由于之前考虑国际化差异,原先在card.json的配置如下

      "i18n": {
        "messages": {
          "zh_CN": {
            "cardTitle": "曲线图",
            "rightTitle": ""
          },
          "en_US": {
            "cardTitle": "Line Chart",
            "rightTitle": ""
          }
        }
      },

可以得到卡片标题的字段为 cardTitle,WeMax平台插件中,已经将卡片标题等文本进行了国际化处理,都包含在textContentObj对象中,可以使用该字段获得标题,代码如下

  handleTitleClick(){
      const cardEvent = this.options.cardEvents[1]  // 获取card.json文件中新增的标题点击事件对象
      const publishParam = {
        i: this.options.i || '',     // 组件唯一标识i -- 固定值, 不可修改
        id: cardEvent.id,            // 交互唯一标识id
        param: {                     // 交互参数
          [cardEvent.variable[0].field]: this.textContentObj.cardTitle // 即对象key为param
        },
        interactionList: this.options.interactionList || [] // 独立运行wemax卡片交互必传参数
      }
      this.$service.message.publish({    // 发送message消息
        topic: 'cardEventDistribution',  // cardEventDistribution消息名称 -- 固定值, 不可修改
        data: publishParam                   // 交互参数,按照实际情况修改
      })
    },

2.3 在mouted中订阅消息

若该卡片card.json中已有事件列表cardEvents,则Card.vue中应该有订阅消息的函数了,此步骤则可跳过。若卡片是第一次设置事件交互或vue文件中没有该类函数,则需要添加消息订阅,代码如下

// 在Card.vue中订阅message事件,刷新数据
  // subscribeMessageFun 和 handlerSubscribeMsg 均为EventMixin 混合内方法, 使用时需提前从公共插件引入
this.subscribeMessageFun(topic, data => {
  if (topic === 'variablesSubscribption') { // 订阅全局变量。variablesSubscribption名称为固定值,修改无效
    this.handlerSubscribeMsg(data, this.options.apis)
  }
})

至此,代码部分就全部完结。此时需要将卡片版本号加1,并上传到WeMax平台,具体上传步骤可参考。

# 3、在WeMax平台上配置跳转交互

在平台右边配置项中找到 交互--标题点击--设置变量,可以看到动态变量下面有个字段param,这跟卡片代码中写相吻合,此时可按如下步骤操作。

a.在绑定变量那一列填入param,表示将该动态变量绑定到param

b.将绑定变量的开关打开,表示向全局暴露

截图986

然后在交互--标题点击--图形交互设置成下图,具体步骤如下:

截图8986

a.交互方式有关联显示/隐藏、跳转、弹出窗口等,此处选择跳转

b.打开方式可选新窗口

c.跳转到,选择目标地址,如百度地址

d.在参数一栏,需要设置入参,点击+号,弹出参数选择表,设置如下

截图321

此时WeMax平台上配置完毕,可点击右上角的保存,再点击预览,则会看到曲线图的预览效果

截图111

此时点击曲线图左上角的标题,则会跳转前往新窗口,地址显示如下

截图1111

至此,交互功能完成。

# 样例2:新增系列1配置项

新增需求,给曲线图系列1--系列类型配置项下面,加一个线类型的配置项,具体步骤如下:

# 1、在SeriesItem.vue中新增配置项

由于卡片工程中图表类配置都很常用,因此此类配置一般都在公共组件中,并不属于该卡片私有。为了找到配置线类型的地方,可选择该面板中一个比较独特的字段,比如默认边框宽度,然后去编辑器中,搜索该字段,即可找到SeriesItem.vue文件,若搜索出多个文件,则需要排除,确认后才可进去修改。

截图6

a.先在文件的HTML中找到系列类型的配置代码,如下所示

    <aui-form-item label="系列类型">
      <aui-select v-model="seriesType">
        <aui-option
          v-for="item in typeOp"
          :key="item.value"
          :label="item.text"
          :value="item.value"
        >
        </aui-option>
      </aui-select>
    </aui-form-item>

b.然后按照上述代码的格式,在它下面加入线类型的配置代码,从echarts官网得到线类型的表示方法为series.itemStyle.normal.lineStyle.type,于是用v-model绑定如下

      <aui-form-item label="线类型">
        <aui-select v-model="data.itemStyle.normal.lineStyle.type">
          <aui-option
            v-for="item in lineTypeOp"
            :key="item.value"
            :label="item.text"
            :value="item.value"
          >
          </aui-option>
        </aui-select>
      </aui-form-item>

其中data是该文件的父组件传过来的表示系列的对象。

c.将lineTypeOp定义如下

    lineTypeOp: [
        { value: 'dotted', text: '点线' },
        { value: 'solid', text: '实线' },
        { value: 'dashed', text: '虚线' }
    ],

d.引入AUI组件Select和Option

import { Select, Option} from '@aurora/vue'
export default {
  components: {
    AuiSelect: Select,
    AuiOption: Option,()
  },

# 2、在card.json中配置series属性

大多数的卡片工程的图表类代码中,这些series默认配置项都是公共的,其对应的控制字段都统一放在插件中,所以card.json中series属性一般是空数组,如果想要设置不一样的效果,只需在series中设置对应的字段即可,如果它的配置和插件中的配置有重复,则会以card.json中的为主,因此此处可设置为

series: [
    {
        itemStyle: {
          normal: {
            lineStyle: {
              type: 'solid'
            }
          }
        }
	 }
]

至此,新增线类型配置项的功能开发完成,该功能不需要上传到WeMax平台,在本地即可验证,效果如下

截图8

# 样例3、新增api服务

如需新增一个数据服务,将返回的文本内容显示到标题下面,则可按以下步骤

# 1、在card.json中新增服务设置

    "apis": [
        {
             ...之前的服务设置
        },
        // 新增服务
        {
          "id": "top-text",
          "name": "文本数据服务",
          "title": "文本数据服务",
          "refreshSwitch": false,
          "refreshTime": 60,
          "variables": {},
          "showAggregation": false,
          "type": "static",
          "supportTypes": ["static", "api"],
          "isInItGetData": true,
          "isInitServeI18n": false,
          "fields": [
            {
              "id": "text-name",
              "field": "text",
              "mapField": "text",
              "desc": "文本数据服务",
              "type": "string",
              "category": "dimension"
            }
          ],
          "source": [
            {
              "text": ""
            }
          ]
        }
      ]    

# 2、在card.vue中配置

a.在计算属性中新增该api

    optionsApi1() {
      return this.options.apis[1]
    },

b.将该服务加入监听,如果该服务有变化,便会重新获取服务数据。

const watchers = {
  {
    ...
  },
  optionsApi1: {
    deep: true,
    handler(val) {
      if (val) {
        this.getTextData()
        this.isRefresh(this.getTextData, val.refreshSwitch, val.refreshTime)
      }
    }
  },

c.,定义变量,初始化时调用服务获取数据

  data() {
    text: ""
  },
   mounted() {
    this.getTextData()
  }

d.在DOM结构中加入需要展示的结构,样式自写。

<div>{{text}}</div>

e.编写服务数据获取函数

    async getTextData(){
      const textData = await this.getDataByID('top-text') // 根据JSON文件的api字段定义的ID获取数据,其中getDataByID来自ApiMixin混合
      const fields = this.apis[1].fields
      const res = this.buildRemodelData(textData, fields) // 将数据转换处理,来自ApiMixin混合
      if (res.length) {
        this.text = res[0].text     // 固定格式,从定义的source中取出text字段
      }
    },

最终面板如下,只需将数据源类型由静态数据改为REST URL,就可以填入服务地址了

截图87

# 表格类规范示例(表格组件为例)

# 文件概览

├── cards                  // 固定目录
    └── cardName              // 组件名称
        └── panel                 // 组件自定义配置面板
            ├── Config.vue            // 组件配置内容
            ├── index.js              // 组件配置入口
            ├── components            // 组件配置文件夹
            	├── columnConfig              // 列配置文件夹
            		├── ColumnConfig.vue            // 列总的配置文件
            		├── ColumnSettingItem.vue        // 各列设置项
            		├── ColumnSettings.vue            // 列设置
            		├── SelfHeadItem.vue             // 自定义表头具体各项
            		├── SeqConfig.vue               // 序号列配置
            		├── TableSelfHead.vue            // 自定义表头
            	├── conditionConfig           // 列条件配置项文件夹
            		├── ColumnConditionConfig.vue    // 列条件总配置     
            		├── ColumnTextStySetting.vue     // 列文本样式配置    
            		├── ConditionConfig.vue          // 列条件配置
            		├── ConditionItem.vue           // 各列的条件配置
            		├── DialogSetting.vue           // 对话框配置
            		├── ImageSetting.vue            // 图片项配置
            		├── LinkTextSetting.vue         // 链接项配置 
            		├── ProgressStySetting.vue      // 进度条样式设置
            		├── RowConditionConfig.vue      // 行条件配置
            		├── RowConditionItem.vue        // 各行的条件配置
            		├── RowConditionList.vue        // 行条件配置
            		├── RowConditionPanel.vue         // 行条件面板
            		├── RowConditions.vue           
            	├── rowConfig                 // 行配置文件夹
            		├── IconConfig.vue        // 图标配置
            		├── RowConfig.vue         // 行配置     
            		├── TableHeadConfig.vue   // 表头配置           
            		├── TableRowConfig.vue    // 表行配置         
            		├── rowConfig             // 分组行设置
            	├── BorderConfig.vue          // 边框配置文件
            	├── EventConfig.vue           // 事件配置文件
            	├── EventStyleConfig.vue      // 事件样式配置文件
            	├── FontConfig.vue            // 字体配置文件
            	├── PagerConfig.vue           // 页面配置文件
            	├── TableConfig.vue           // 表格配置文件
        ├── static                    // 组件静态资源
        ├── compontents                  // 组件文件
         	├── ConditionedCell.vue      // 单元格组件
         	├── PageSizeSetting.vue      // 分页页码设置
         	├── Sorter.vue				 // 排序组件
         	├── TableDialog.vue          // 表格弹窗组件
         	├── TablePager.vue           // 分页组件
        ├── card.json             // 组件元数据文件
        ├── Card.vue              // 组件vue代码
        ├── index.js              // 组件入口
        ├── README.md             // 组件介绍文档
├── manifest.json          // 资源包元数据文件
└── README.md              // 资源包内容介绍文件

# index.js规范

输出入口文件为Card.vue。

import Card from './Card.vue'
export default Card

# card.json规范

# 总体描述

属性 类型 必填 描述
name string 组件名称,在资源包内唯一,必须使用英文,数字和连字符。
code string 组件编码,要求全局唯一,{组件name}
title string 组件标题
version string 组件版本号
main string 组件运行代码路径
initProps object 组件初始化时加载参数
dnd object 组件默认大小配置
description string 组件功能描述
keywords string[] 组件资源标签,可以设置多个
customPanel string 组件自定义面板路径
apis object 组件数据接入配置
cardEvents object 组件交互配置
alarmMsgConfig object 组件消息预警配置

# 组件元数据规范示例

{
  "code": "wemax.template.table",
  "name": "wemax.template.table",
  "title": "表格",
  "description": "表格", 
  "version": "1.0.0",
  "type": "create:card",
  "category": "组件",
  "keywords": ["wemax"],
  "icon": {
    "default": "./static/default.svg",
    "preview": "./static/preview.svg"
  },
  "customPanel": "./panel/index.js",
  "initProps": {
    "options": {
      "alarmMsgConfig": {                                 // 消息预警配置, 不必填
        "conditionConfig": false,                         // 消息预警开关
        "messageList": [                                  // 消息预警列表,最多可配置5条
          {
            "conditionCode": "item.column1 > 8000",       // 消息预警条件
            "messageTitle": "表格预警消息",                 // 消息推送标题
            "messageContent": "警告信息",                  // 消息推动内容
            "warnStyle": [                                // 组件预警样式
              {
                "color": "#FFFFFF",                       // 预警时的文字颜色
                "des": "文字颜色"                          // 对应预警的描述
              },
              {
                "cellColor": "#FF171A",
                "des": "单元格填充"
              }
            ]
          }
        ]
      },
      // weMax平台提供的公共插件 @aurora/wemax-card-plugin 已提供国际化组件I18nInput,可以直接引入使用,i18n配置如下:
      "i18n": {                                           // 国际化配置,不必填
        "messages": {
          "zh_CN": {
            "cardTitle": "表格",
            "rightTitle": ""
          },
          "en_US": {
            "cardTitle": "WeMax table",
            "rightTitle": ""
          }
        }
      },
      "isUsePager": false,                                 // 是否使用分页
      "pagerStyleConfig": {                                // 分页样式
        "iconbtnColor": "#000000",                         // 翻页图标颜色
        "pagerDataColor": "#000000"                        // 页签颜色
      },
      "cardTitle": {                                       // 卡片标题
        "isShow": true,
        "contentConfig": true,
        "textContent": "cardTitle",
        "color": "transparent",
        "themeColor": "var(--wemax-base-color-info-normal, #fff)",
        "fontSize": 16,
        "scale": true,
        "fontWeight": 400
      },
      "rightTitle": {                                       // 右侧标题
        "isShow": false,
        "contentConfig": true,
        "textContent": "rightTitle",
        "color": "transparent",
        "themeColor": "var(--wemax-base-color-gray-normal, #999)",
        "fontSize": 14,
        "scale": true,
        "fontWeight": 400
      },
      "sortConfig": {                                        // 表格排序设置
        "order": "",                                         // 降序还是升序
        "field": ""                                          // 排序的列
      },                                           
      "seqConfig": {                                         // 第一列序号列配置
        "name": "序号",
        "show": true,
        "backgroundColor": "transparent",
        "scale": true,
        "fontSize": 12,
        "color": "transparent",
        "themeColor": "var(--wemax-table-tbody-color, #ffffff)",
        "fontWeight": 400,
        "borderRadius": 10,
        "size": 12,
        "width": 10
      },
      "eventStyleConfig": {                                   // 行样式
        "isUsed": false,                                      // 是否启用样式
        "click": {                                            // 点击时
          "rowBgColor": "transparent",                        // 行背景颜色
          "rowColor": "#ffffff",                              // 行字体颜色
          "cellBgColor": "transparent",                       // 单元格背景颜色
          "cellColor": "#ffffff"                              // 单元格字体颜色
        },
        "hover": {                                            // 鼠标悬浮样式  
          "rowBgColor": "transparent",
          "rowColor": "#ffffff",
          "cellBgColor": "transparent",
          "cellColor": "#ffffff"
        }
      },
      "tableConfig": {                                        // 表格外边距
        "top": "0%",
        "bottom": "0%",
        "left": "1%",
        "right": "1%"
      },
      "tableHeadConfig": {                                    // 表头样式配置
        "show": true,
        "backgroundColor": "transparent",
        "themeBgColor": "var(--wemax-table-thead-bg-color, #1C61C1)",
        "gradientBackground": "",                    
        "gradientFlag": false,                                // 开启渐变
        "gradientDirection": 0,                               // 渐变角度
        "gradientStartColor": "transparent",
        "gradientStopColor": "transparent",
        "fontSize": 12,
        "color": "transparent",
        "themeColor": "var(--wemax-table-thead-color, #ffffff)",
        "scale": true,
        "fontWeight": 400,
        "height": 30,
        "align": "center",
        "borderWidth": 1,
        "borderColor": "transparent",
        "themeBorderColor": "var(--wemax-table-thead-border-color, #0C5092)",
        "borderStyle": "solid",
        "selfDefineHeads": [],                                // 自定义表头
        "icon": {
          "size": 16,
          "color": "#ffffff"
        }
      },
      "tableRowConfig": {                                      // 行样式配置
        "oddRowBackground": "transparent",                     // 奇数行背景色
        "themeOddBgColor": "var(--wemax-table-tbody-odd-col-bg-color, #0E2B5A)",
        "evenRowBackground": "transparent",                    // 偶数行背景色
        "themeEvenBgColor": "var(--wemax-table-tbody-even-col-bg-color, #0F214E)",
        "fontSize": 12,
        "color": "transparent",
        "themeColor": "var(--wemax-table-tbody-color, #ffffff)",
        "scale": true,
        "fontWeight": 400,
        "height": 30,
        "align": "center",
        "borderWidth": 1,
        "borderColor": "transparent",
        "themeBorderColor": "var(--wemax-table-tbody-col-border-color, #0C5092)",
        "borderStyle": "solid"
      },
      "columnSettings": [],                          // 列配置
      "pageSize": 5,                                 // 分页条数
      "apis": [                                 // 组件数据接入配置,不必填,可配置多条
        {
          "id": "table-data",                   // 数据接入id, 同一组件内唯一
          "name": "表格数据",                     // 数据接入名称
          "title": "表格数据",                    // 数据接入名称
          "showAggregation": true,              // 是否展示聚合开关(用于数据模型方式接入)
          "defaultAggregation": false,          // 聚合开关(用于数据模型方式接入)
          "type": "static",                     // 数据接入类型
          "params": [],                         // 数据获取时查询参数
          "supportTypes": ["static", "api"],    // 默认支持的数据接入类型
          "refreshSwitch": false,               // 定时调度开关
          "refreshTime": 60,                    // 定时调度时间
          "isInItGetData": true,                // 是否初始时请求表格数据
          "isInitServeI18n": false,             // 是否国际化
          "fields": [                           // 组件数据字段映射 -- 用于转换真实数据字段关联当前组件代码中使用字段
            {
              "id": "table-column-1",           // 字段定义唯一标识
              "field": "column1",               // 字段名
              "mapField": "",              // 数据映射字段名, 如未选择默认取field字段
              "desc": "列1",                    // 字段分类描述 列1
              "type": "string",                 // 字段类型 string number
              "category": "dimension"           // 维度dimension/度量measure 用于数据模型接入时, 拖入字段类型识别
            },
            ...... // 省略类似结构
            {
              "id": "table-column-20",
              "field": "column20",
              "mapField": "",
              "desc": "列20",
              "type": "string",
              "category": "dimension"
            }
          ],
          "source": [                            // 组件初始化静态数据, 自定义
            {
              "column1": 800,
              "column2": "福州",
              "column3": "2015-04-30 00:56:12",
              "column4": "1"
            },
            ...... // 省略类似结构
            {
              "column1": 952,
              "column2": "上海",
              "column3": "2018-04-30 00:56:13",
              "column4": "8"
            }
          ]
        }
      ],
      "cardEvents": [                  // 组件交互配置,不必填,可配置多条
        {
          "name": "表格点击",          // 交互事件名称
          "id": "event-table-click",   // 交互事件id, 同一组件内唯一
          "variable": [                // 交互事件参数列表, 自定义字段名称/类型
            {
              "field": "column1",      // 交互事件参数字段, 自定义
              "type": "string"         // 交互事件参数类型, 自定义
            },
           ...... // 省略类似结构
            {
              "field": "column20",
              "type": "string"
            }
          ]
        }
      ],
      "eventConfig": {                   // 行点击是发送单元格的值还是行字段
        "isPublishCellValue": true       // 为true,表示发送单元格的值
      },
      "colHeads": [],                    // 列头配置
      "rowConditions": [                 // 给行设置条件,满足时触发样式
        {
          "bgColor": "transparent",      
          "color": "#ffffff",
          "conditionArr": [             // 多个条件组成的数组
            {
              "logical": "&&",          // 逻辑运算符
              "colName": "",            // 当前设置条件的列名
              "operator": "=",          // 比较运算符
              "colVal": ""              // 拿来比较的列的值
            }
          ]
        }
      ],
      "columnConditionSetting": []      // 给列设置条件,满足时触发样式
    }
  },
  "main": "./index.js",                 // 组件运行路径             
  "dnd": {                              // 组件拖入容器**初始化时默认大小**
    "height": 12,                       // 高度
    "width": 10,                        // 宽度
    "minHeight": 12,                    // 最小高度
    "minWidth": 8                       // 最小宽度
  }
}

# Card.vue规范

# html模板规范

<template>
  <div class="wemax-component-container">
    <div class="inner">
      <CardTitle :options="options" />
      <div
        :style="[paddingStyl]"
        class="table-wrapper"
      >
        <table
          ref="wemaxTable"
          v-clickoutside="handleOutsideClick"
          class="wemax-table"
          @mouseover="handleTableHover"
          @mouseout="handleTableHoverEnd"
        >
         
          <thead
            v-show="tableHeadConfig.show"
            class="wemax-table-thead"
          >
          // 自定义表头
            <tr
              v-if="selfDefineHeads.length"
              class="wemax-table-tr"
            >
              <th
                v-show="seqConfig.show"
                :style="[headerStyle, seqWidth]"
                class="wemax-thead-th"
              ></th>
              <th
                v-for="(head, index) in selfDefineHeads"
                :key="index"
                :colspan="head.colspan"
                :style="[headerStyle, getColColumnWidth(index, head)]"
                class="wemax-thead-th"
              >
                {{ head.name }}
              </th>
            </tr>
            // 表格数据的表头
            <tr
              v-if="tableColumns.length"
              class="wemax-table-tr"
            >
              <th
                v-show="seqConfig.show"
                :style="[headerStyle, seqWidth]"
                class="wemax-thead-th"
              >
                {{ seqConfig.name }}
              </th>
              <th
                v-for="(item, index) in tableColumns"
                :key="item.field + (Math.random() + 1)"
                :style="[headerStyle, getColumnWidth(index)]"
                class="table-head wemax-thead-th"
              >
                {{ getColumnName(index) }}
                <sorter
                  v-show="getColumnSortable(index)"
                  v-if="!isSqlModel && hasTableData"
                  :sortObj="item"
                  :styleObj="tableHeadConfig"
                  class="head-sorter"
                  @setSort="
                    (obj) => {
                      getSortingData(obj, index)
                    }
                  "
                >
                </sorter>
              </th>
            </tr>
          </thead>
          // 表格主体
          <tbody
            class="wemax-table-tbody"
            :style="tbodyStyle"
          >
          // 表格行
            <tr
              v-for="(data, index) in tableData"
              :key="index + (Math.random() + 1)"
              :style="[getRowBackground(index), getRowConditionStyle(data)]"
              class="wemax-table-tbody-tr"
              @click="setClickStyle($event)"
            >
              // 序号列
              <td
                v-show="seqConfig.show"
                :style="[rowStyle, seqWidth]"
                class="wemax-table-tbody-td-seq"
              >
                <span
                  :style="transStyleObj('seqConfig')"
                  class="wemax-table-sequence"
                >{{ getSeq(index) }}</span>
              </td>
               // 数据列
              <td
                v-for="(item, colIndex) in tableColumns"
                :key="item.field + (Math.random() + 1)"
                :style="[rowStyle, getColumnWidth(colIndex), showAlarmStyle(index)]"
                :field="item.field"
                class="wemax-table-tbody-td"
                @click="colClickStyle(data, item.field, colIndex)"
              >
                // 单元格
                <conditioned-cell
                  :cellVal="data[item.field]"
                  :endUnit="getColumnEndUnit(colIndex)"
                  :field="item.field"
                  :type="getConditonType(item.field, data[item.field])"
                  :cellStyle="
                    getColConditionStyle(item.field, data[item.field])
                  "
                  @handleCellClick="
                    handleCellClick(item.field, data[item.field])
                  "
                />
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      // 分页器
      <TablePager
        v-if="isUsePager"
        :curPage="curPage"
        :pageSize="pageSize"
        :total="totalRows"
        :options="options"
        @pageSizeChange="currentChange"
      />
    </div>
    <TableDialog
      ref="tableDialog"
      :iframeUrl="iframeUrl"
    />
  </div>
</template>

# js脚本规范

import cardMeta from './card.json'
import {
  ApiMixin,
  EventMixin,
  ClickoutsideMixin,
  Watcher
} from '@aurora/wemax-card-plugin'    // 引入公共插件
import Sorter from './components/Sorter.vue'
import ConditionedCell from './components/ConditionedCell.vue'
import getDefaultColumnSetting from './static/columnConfig.js'
import TablePager from './components/TablePager.vue'
import TableDialog from './components/TableDialog.vue'
import _ from 'lodash'
import CardTitle from '@/components/CardTitle'

// 设计时和运行时watch分离
const watchers = {
  apis: {                             // 组件数据监听, 包含定时调度设置
    deep: true,
    handler(val) {
      if (val) {
        this.fetchTableData()         // 获取表格数据
        const { refreshSwitch, refreshTime } = val[0]
        this.isRefresh(this.fetchTableData, refreshSwitch, refreshTime)
      }
    }
  }
}
const designWatchers = {
  isUsePager(val, oldVal) {           // 分页获取数据
    if (val !== oldVal) {
      this.berforeGetTableData()
    }
  },
  'options.alarmMsgConfig': {         // 预警配置有变化,将重新渲染表格
    handler(val) {
      this.berforeGetTableData()
    },
    deep: true
  },
  'options.pageSize'(val, oldVal) {   //  分页条数有变化,重新获取数据
    if (val !== oldVal) {
      this.pageSize = val
      this.curPage = 1
      this.berforeGetTableData()
    }
  }
}
Watcher.init(watchers, designWatchers)

export default {
  name: 'WeMaxTable',
  components: {
    Sorter,
    ConditionedCell,
    CardTitle,
    TablePager,
    TableDialog
  },
  mixins: [ApiMixin, EventMixin, ClickoutsideMixin],  // 使用插件内混合方法
  props: {
    options: {                                        // 固定使用方式
      type: Object,
      default: () => cardMeta.initProps.options
    }
  },
  data() {
    return {
      tableColumns: [],      // 包含每一列的映射字段、列名和排序标识
      tableData: [],         // 服务获取的表格数据
      tableDataBackup: [],   // 排序后的表格数据
      curPage: 1,            // 分页时的当前页码
      totalRows: 0,          // 数据总数目
      pageSize: 10,          // 每页的分页条目
      curSortIndex: 0,       // 当前使用排序的列索引
      curClickTarget: null,  // 当前点击目标
      curHoverTarget: null,  // 当前鼠标悬浮的目标
      originalStyle: null,   // 原始样式
      fieldsObj: {},         // 列映射
      rowItemObj: {},
      iframeUrl: ''
    }
  },
  computed: {
    tbodyStyle() {                       // 表行样式
      if (!this._isMounted) {
        return
      }
      const { clientHeight } = this.$refs.wemaxTable
      const { height } = this.tableHeadConfig
      return {
        height: clientHeight - height + 'px'
      }
    },
    hasTableData() {                    // 表格是否有数据
      return this.tableData && this.tableData.length && this.tableData.length > 1
    },
    seqConfig() {
      return this.options.seqConfig
    },
    seqWidth() {                       // 序号列的宽度
      const { width } = this.seqConfig
      return {
        width: width + '%'
      }
    },
    apis() {
      return this.options.apis         // 组件服务数据配置
    },
    isUsePager() {                     // 是否使用分页
      return this.options.isUsePager
    },
    tableHeadConfig() {                // 表头配置
      return this.options.tableHeadConfig
    },
    headerStyle() {                    // 表头样式
      return {
        ...this.transStyleObj('tableHeadConfig'),
        ...this.getBorderStyle('tableHeadConfig')
      }
    },
    rowStyle() {                       // 行样式
      return {
        ...this.transStyleObj('tableRowConfig'),
        ...this.getBorderStyle('tableRowConfig')
      }
    },
    paddingStyl() {                     // 页面内边距
      const { top, bottom, left, right } = this.options.tableConfig
      return {
        'padding-top': top,
        'padding-bottom': bottom,
        'padding-left': left,
        'padding-right': right
      }
    },
    selfDefineHeads() {              // 自定义列头
      return this.tableHeadConfig.selfDefineHeads
    },
    selfDefineHeadsTotal() {         // 自定义列头的总宽度
      let total = 0
      this.selfDefineHeads &&
        this.selfDefineHeads.forEach((el) => {
          total += el.colspan
        })
      return total
    },
    apiType() {                      // 第一个服务的数据接入类型
      return this.apis[0].type
    },
    isSqlModel() {                   // 是否是sql模型
      return this.apiType === 'sqlmodel'
    },
    columnSettings() {               // 列配置,包含是否显示,分组列、排序、排名、列宽等功能
      return this.options.columnSettings
    }
  },
  watch: watchers,
  created() {
    this.pageSize =  this.options.pageSize  // 从配置项里得到分页条数
    const { refreshSwitch, refreshTime, isInItGetData = null } = this.apis[0]
    isInItGetData && this.fetchTableData()  // 是否初始化时获取表格数据
    this.isRefresh(this.fetchTableData, refreshSwitch, refreshTime)
    if (Watcher.isDesignMode()) {       // 组件修改属性时 添加防抖. 降低刷新频率
      this.setColumnSettings = _.debounce(this.setColumnSettings, 300)
    }
  },
  mounted() {
    this.subscribe('variablesSubscribption')  // 订阅全局变量
  },
  methods: {

    /**
     * @description 获取二级表头宽度
     * @param {Number} index 列下标
     * @param {Object} obj 配置项
     * @returns {Object}
     */
    getColColumnWidth(index, obj) {
      const itemCol = this.columnSettings[index]
      const styleObj = {
        width: itemCol && itemCol.width + '%' // 初始默认列宽
      }
      let total = 0                     
      for (let k = 0; k <= index; k++) {  // 计算所有自定义列头所占的列数,colspan表示几列,默认是1,表示一列
        total += this.selfDefineHeads[k].colspan
      }
      let totalWidth = 0
      // 根据列数和列宽,计算该表头的宽度
      this.columnSettings.forEach((el, inx) => {
        if (inx >= total - obj.colspan && inx < total) {
          totalWidth += el.width
        }
      })
      styleObj.width = totalWidth + '%'
      // 当自定义表头的总列数超过原有表头的列数时,样式不再变化
      total >= this.columnSettings.length && (styleObj['flex-grow'] = 1)
      return styleObj
    },

    /**
     * @description 展示预警样式
     * @param {Number } index 行的索引
     */
    showAlarmStyle(index){
      if(this.getAlertStyle && this.getAlertStyle[0]){ // getAlertStyle为插件中的方法,可得到满足alarmMsgConfig预警条件的表格行及该条件下对应的预警样式
        const el = this.getAlertStyle && Object.values(this.getAlertStyle)[0] // 取出里面的预警对象el,包含index和style字段,index是一个满足预警条件的行索引的数组,style是该行对应的样式
        if(el.index.includes(index)){           // 如果表格行在满足预警条件的索引数组中,则添加预警样式
          const { color } = el.style[index].filter((item)=>item.color)[0] // 取出该行对应的预警样式
          const { cellColor } = el.style[index].filter((item)=>item.cellColor)[0]
          const obj = {
            color,
            "background-color": cellColor
          }
          return obj
        }
      }
    },

    /**
     * @description 获取列宽度
     * @param {Number} index 列下标
     * @returns {Object}
     */
    getColumnWidth(index) {
      const itemCol = this.columnSettings[index]
      const styleObj = {
        width: itemCol && itemCol.width + '%'   // 初始默认列宽
      }
      // 索引过界,样式不再变化
      index + 1 === this.columnSettings.length && (styleObj['flex-grow'] = 1)
      return styleObj
    },
    getColumnEndUnit(index) {  // 从列配置项中获取单位
      const itemCol = this.columnSettings[index]
      return itemCol && itemCol.endUnit || ''
    },

    /**
     * @description 获取列可排序设置参数
     * @param {Number} index 列下标
     * @returns {Boolean}
     */
    getColumnSortable(index) {  // 是否开启排序功能
      const itemCol = this.columnSettings[index]
      return itemCol ? itemCol.sortable : false
    },

    /**
     * @description 获取列头名称
     * @param {Number} index 列下标
     * @returns {String}
     */
    getColumnName(index) {   // 从列配置项中获取列名
      const itemCol = this.columnSettings[index]
      const { title } = this.tableColumns[index]
      const name = itemCol ? itemCol.name : ''
      return this.apis[0].isInitServeI18n // 根据国际化场景显示
        ? this.$t(name || title)
        : name || title
    },

    /**
     * @description 获取条件类型,一共有text|progress|image三种
     * @param {String} colName 添加条件的列名
     * @param {String | Number} val 单元格的值
     * @returns {String}
     */
    getConditonType(colName, val) {  
      const conditions = this.options.columnConditionSetting
      if (!conditions.length) {       
        return 'text'                 // 单元格内默认是文本格式
      }
      let conditionType = ''
      const fitteds = conditions.filter((condition) => {
        return condition.colName === colName  // 根据列名定位到当前列
      })                              
      if (!fitteds.length) {
        return 'text'
      }
      fitteds.forEach((fitObj) => {
        const isSatisfied = this.checkColumnCondition(fitObj, val)
        if (!isSatisfied) {       // 判断该单元格是否满足条件
          conditionType = 'text'
          return
        }
        conditionType = fitObj.type
      })
      return conditionType
    },

    /**
     * @description 点击事件,如果设置了列条件并且类型为link,点击会跳转
     * @param {String} colName 列名
     * @param {String} val 单元格的值
     */
    handleCellClick(colName, val) {
      const conditions = this.options.columnConditionSetting
      if (!conditions.length) {
        return
      }
      let res = null
      const fitteds = conditions.filter((condition) => {
        return condition.colName === colName   // 根据列名定位到当前列
      })
      fitteds.forEach((fitObj) => {
        const isSatisfied = this.checkColumnCondition(fitObj, val)
        if (!isSatisfied) {
          return                     // 当前值不满足条件,返回
        }
        res = fitObj
      })
      if (res.operator === '--') {   // 运算符默认是--
        res.colVal = val
      }
      if (res.type === 'link') {     // 若列条件设置为a链接
        this.openLink(res)
      } else {
        this.openDialogBox(res)      // 否则是对话框
      }
    },

    openDialogBox(obj) {
      const { dialogUrl } = obj
      if (!dialogUrl) {
        return
      }
      this.iframeUrl = this.getOpenLinkUrl(obj)
      this.$refs.tableDialog.openDialog()  // 打开对话框
    },

    openLink(res) {
      const { linkType, linkUrl } = res
      if (linkType === 'none' || !linkUrl) {
        return
      }
      const openLinkUrl = this.getOpenLinkUrl(res)
      window.open(openLinkUrl, linkType)  // 打开链接
    },

    getOpenLinkUrl(obj) {                 // 获取链接的url
      const { type, linkUrl, dialogUrl, colName, colVal } = obj
      const urlParam = `${colName}=${colVal}`  //  默认将当前值作为url参数
      let url = type === 'link' ? linkUrl : dialogUrl
      url += (url.indexOf('?') === -1 ? '?' : '&') + urlParam
      return url
    },

    /**
     * @description 获取列条件配置中的样式
     * @param {String} colName 列名
     * @param {String | Number} val 单元格的值
     * @returns {Object}
     */
    getColConditionStyle(colName, val) {
      const conditions = this.options.columnConditionSetting
      if (!conditions.length) {
        return
      }
      let res = null
      const fitteds = conditions.filter((condition) => {
        return condition.colName === colName
      })
      fitteds.forEach((fitObj) => {
        const isSatisfied = this.checkColumnCondition(fitObj, val)
        if (!isSatisfied) {
          return
        }
        res = this.getFittedStyle(fitObj)
      })
      return res
    },

    /**
     * @description 获取匹配的列条件样式
     * @param {Object} fitObj 匹配的列条件配置对象
     * @returns {Object}
     */
    getFittedStyle(fitObj) {
      let res = null
      if (fitObj.type === 'text') {
        res = this.transToTextStyle(fitObj)
      } else if (fitObj.type === 'link' || fitObj.type === 'dialogBox') {
        res = this.transToLinkStyle(fitObj)
      } else if (fitObj.type === 'progress') {
        res = this.transToProgressStyle(fitObj)
      } else {
        res = this.transToImageStyle(fitObj)
      }
      return res
    },

    /**
     * @description 转换为进度条样式
     * @param {Object} fitObj 匹配的列条件配置对象
     * @returns {Object}
     */
    transToProgressStyle(fitObj) {
      const { progressWidth, progressLength, progressBg } = fitObj
      return { progressWidth, progressLength, progressBg }
    },

    /**
     * @description 转换为图片样式
     * @param {Object} fitObj 匹配的列条件配置对象
     * @returns {Object}
     */
    transToImageStyle(fitObj) {
      const { imageUrl, imageSize, borderRadius, position, hideText } = fitObj
      return { imageUrl, imageSize, borderRadius, position, hideText }
    },

    /**
     * @description 转换为文字样式
     * @param {Object} fitObj 匹配的列条件配置对象
     * @returns {Object}
     */
    transToTextStyle(fitObj) {
      const { color, fontSize, fontWeight, useUnderLine, useItalic } = fitObj
      return {
        color,
        'font-size': fontSize + 'px',
        'font-weight': fontWeight,
        'text-decoration': useUnderLine ? 'underline' : 'unset',
        'font-style': useItalic ? 'italic' : 'unset'
      }
    },

    /**
     * @description 转换为链接样式
     * @param {Object} fitObj 匹配的列条件配置对象
     * @returns {Object}
     */
    transToLinkStyle(obj) {
      const { color, fontSize, fontWeight, useItalic } = obj
      return {
        color,
        'font-size': fontSize + 'px',
        'font-weight': fontWeight,
        'text-decoration': 'underline',
        cursor: 'pointer',
        'font-style': useItalic ? 'italic' : 'unset'
      }
    },

    /**
     * @description 将单元格的值与条件做比较,检测是否满足列条件
     * @param {Object} condition 列条件对象
     * @param {String | Number} columnVal 单元格的值
     * @returns {Boolean} 
     */
    checkColumnCondition(condition, columnVal) {
      let result = false
      const { operator, colVal } = condition  // 从条件配置中取出运算符和被比较的列值
      const checkVal = String(columnVal)
      if (checkVal) {
        switch (operator) {
          case '>':
            result = checkVal > colVal
            break
          case '>=':
            result = checkVal >= colVal
            break
          case '!=':
            result = checkVal != colVal
            break
          case '<=':
            result = checkVal <= colVal
            break
          case '<':
            result = checkVal < colVal
            break
          case '=':
            result = checkVal === colVal
            break
          default:
            result = true
            break
        }
      }
      return result
    },

    /**
     * @description 获取行条件配置样式
     * @param {Object} rowData 行的值
     * @returns {Object}
     */
    getRowConditionStyle(rowData) {
      const fitIndex = this.getFitConditionIndex(rowData)
      const fitCondition = this.options.rowConditions[fitIndex]
      if (fitCondition) {
        const { bgColor, color } = fitCondition
        return {
          'background-color': bgColor,
          color: color
        }
      }
    },

    /**
     * @description 获取匹配条件的行下标
     * @param {Object} rowVal 行值
     * @returns {Number}
     */
    getFitConditionIndex(rowVal) {
      let fitIndex = -1
      const { rowConditions } = this.options
      for (let i = 0, len = rowConditions.length; i < len; i++) {
        const { conditionArr } = rowConditions[i]
        const isSatisfied = this.checkRowConditions(conditionArr, rowVal)
        if (isSatisfied) {
          fitIndex = i
          break
        }
      }
      return fitIndex
    },

    /**
     * @description 获取匹配条件的行下标
     * @param {Array} conditions 行条件数组
     * @param {Object} rowVal 单元格的值
     * @returns {boolean}
     */
    checkRowConditions(conditions, rowVal) {
      let isFitted = true
      if (!conditions.length) {
        return !isFitted
      }
      conditions.forEach((item) => { // 分别比较该行中选中的几列是否符合条件,再将结果进行逻辑与、或
        const { colName, operator, colVal, logical } = item
        const stringedVal = String(rowVal[colName])
        const isOk = this.checkItemCondition(stringedVal, operator, colVal)
        isFitted = this.getFittedByLogical(logical, isFitted, isOk) 
      })
      return isFitted
    },
    // 将每个列是否满足条件的结果进行逻辑与、或,得到该行是否满足条件
    getFittedByLogical(logical, boolVal1, boolVal2) {
      switch (logical) {
        case '&&':
          return boolVal1 && boolVal2
        case '||':
          return boolVal1 || boolVal2
        default:
          return false
      }
    },
    
    /**
     * @description 将单元格的值与条件做比较,检测是否满足列条件
     * @param {String | Number} realVal 单元格的值
     * @param {String | Number} operator 运算符
     * @param {String | Number} conditionVal 条件中要比较的值
     * @returns {Boolean} 
     */
    checkItemCondition(realVal, operator, conditionVal) {
      let result = true
      switch (operator) {
        case '>':
          result = realVal > conditionVal
          break
        case '>=':
          result = realVal >= conditionVal
          break
        case '!=':
          result = realVal != conditionVal
          break
        case '<=':
          result = realVal <= conditionVal
          break
        case '<':
          result = realVal < conditionVal
          break
        default:
          result = realVal === conditionVal
          break
      }
      return result
    },

    /**
     * @description 表格外点击处理
     */
    handleOutsideClick() { 
      this.removePreTargetStyle() // 去除上一个被点击dom元素的事件样式
      this.removePreHoverTargetStyle() // 去除上一个被hover的dom元素的事件样式
    },

    /**
     * @description 获取第一列-序列号
     * @param {Number} index 列数据下标
     */
    getSeq(index) {
      return index + 1 + (this.curPage - 1) * this.pageSize
    },

    /**
     * @description 点击发送表格单元格或者行字段
     * @param {Object} event 点击事件对象
     */
    publishValue(event) {
      const isSeqCell = this.hasClassName(  // 序号列的类名
        event.target,
        'wemax-table-tbody-td-seq'
      )
      const isCell = this.hasClassName(event.target, 'conditioned-cell') // 单元格的类名
      const { isPublishCellValue } = this.options.eventConfig
      // 当点击了序号列或者不在单元格中点击,则返回
      if (isSeqCell && isPublishCellValue || !isCell) {
        return
      }
      const publishParam = this.getPublishParam(event) // 获取发送参数
      this.publishEvent(publishParam)                  // 发布消息
    },

    /**
     * @description 获取发送参数
     * @param {Object} event 点击事件对象
     * @returns {Object}
     */
    getPublishParam(event) {
      const { isPublishCellValue } = this.options.eventConfig
      const realTarget = isPublishCellValue
        ? event.target       // 行的类名
        : this.filterDomFromPath(this.getPath(event), 'wemax-table-tbody-tr')
      const cardEvent = this.options.cardEvents[0]  // 对应json文件中第一个事件(表格点击)
      return {
        i: this.options.i || '',                  // 组件唯一标识i
        id: cardEvent.id,                         // 交互唯一标识id
        screenId: this.options.screenId || '',
        param: this.getParam(realTarget),         // 具体交互参数
        interactionList: this.options.interactionList || []  // 独立运行wemax卡片交互必传参数
      }
    },

    /**
     * @description 获取点击事件对象中path字段
     * event.composedPath是为了兼容firefox与safari浏览器
     * @param {Object} event 点击事件对象
     */
    getPath(event) {
      return event.path || event.composedPath && event.composedPath()
    },

    /**
     * @description 从点击事件对象path字段中获取具有className的dom元素
     * @param {Array} path 点击事件对象中的path字段
     * @param {String} className 类名
     * @returns {Object}
     */
    filterDomFromPath(path, className) {
      let targetDom = null
      if (!className) {
        return
      }
      for (let i = 0, len = path.length; i < len; i++) {
        const dom = path[i]
        if (!dom.className) {
          continue
        }
        if (this.hasClassName(dom, className)) {
          targetDom = dom
          break
        }
      }
      return targetDom
    },

    /**
     * @description 获取param字段的值
     * @param {Object}
     */
    getParam(target) {
      const { isPublishCellValue } = this.options.eventConfig
      if (isPublishCellValue) {
        return this.getCellParam(target)  // 得到单元格的参数
      }
      return this.getRowParam(target)     // 得到行的参数
    },

    /**
     * @description 获取单元格中的值
     * @param {Object} target 单元格dom元素对象
     * @returns {Object}
     */
    getCellParam(target) {
      const targetField = target.getAttribute('field')
      const targetVal = target.innerText
      let valField = ''
      Object.keys(this.fieldsObj).forEach((key) => {
        // key为initprops内的options中定义的apis中的fields字段的值,如column1
        const tempField = this.fieldsObj[key]
        if (targetField && tempField === targetField) {
          valField = key
        }
      })
      return { [valField]: targetVal }
    },

    /**
     * @description 获取行中的值
     * @param {Object} target 行dom元素对象
     * @returns {Object}
     */
    getRowParam(rowEl) {
      const res = {}
      const cellArr = Array.prototype.slice.call(rowEl.children)
      cellArr.forEach((cell) => {
        if (this.hasClassName(cell, 'wemax-table-tbody-td-seq')) {
          return
        }
        Object.assign(res, this.getCellParam(cell)) // 将每个单元格的值拼成一个对象
      })
      return res
    },

    /**
     * @description 表格点击发布事件
     * @param {String} value 发布消息数据
     */
    publishEvent(param) {
      this.$service.message.publish({        // 发送message消息
        topic: 'cardEventDistribution',      // message消息名称 -- 固定值, 不可修改
        data: param                          // 消息参数,按照实际情况修改
      })
    },

    /**
     * @description 设置点击后效果
     * @param {Object} event 事件对象
     */
    setClickStyle(event) {
      if (!this.options.eventStyleConfig.isUsed) {
        return               // 是否启用了事件效果的开关
      }
      this.removePreHoverTargetStyle()  // 去除上一个被hover的dom元素的事件样式
      const clickTarget = this.filterDomFromPath( // 从点击事件对象path字段中获取行的列元素
        this.getPath(event),
        'wemax-table-tbody-td'
      )
      if (this.curClickTarget === clickTarget) {
        return
      }
      if (this.curClickTarget !== clickTarget) {
        this.removePreTargetStyle()  // 去除上一个被点击dom元素的事件样式
      }
      this.setClickTargetStyle(event) // 否则去设置该dom元素的事件样式
    },

    /**
     * @description 列点击事件
     * @param {Object} rowObj 行数据
     * @param {Object} field 服务中映射的字段名,这里就是列名
     * @param {Number} index 列索引
     */
    colClickStyle(rowObj, field, index) {
      const { isPublishCellValue } = this.options.eventConfig // 从options设置中确定当前点击是行点击还是单元格点击
      const cardEvent = this.options.cardEvents[0]
      let param = {}
      if (isPublishCellValue) {   // 如果是单元格点击
        param = {
          [cardEvent.variable[index].field]: rowObj[field] // 将当前单元格的值传给事件变量
        }
      } else { // 否则是行点击,将整行的值都传过去
        Object.values(rowObj).forEach((el, index) => {
          param[cardEvent.variable[index].field] = el
        })
      }
      const publishParam = {
        i: this.options.i || '',            // 组件唯一标识i -- 固定值, 不可修改
        id: cardEvent.id,                   // 交互唯一标识id
        param: param,
        screenId: this.options.screenId || '',
        interactionList: this.options.interactionList || []  // 独立运行wemax卡片交互必传参数
      }
      this.publishEvent(publishParam)      // 发送消息
    },

    /**
     * @description 判断dom元素是否具有某个类名
     * @param {Object} target dom元素
     * @param {String} name 类名
     * @returns {Boolean}
     */
    hasClassName(target, name) {
      return target.className && target.className.indexOf(name) !== -1
    },

    /**
     * @description 设置被点击对象的样式
     * @param {Object} event 点击事件对象
     */
    setClickTargetStyle(event) {
      const { rowBgColor, rowColor, cellBgColor, cellColor } =
        this.options.eventStyleConfig.click
      const parentEl = this.filterDomFromPath(
        this.getPath(event),
        'wemax-table-tbody-tr'
      )
      const realTarget = this.filterDomFromPath(
        this.getPath(event),
        'wemax-table-tbody-td'
      )
      this.saveOriStyle(realTarget, parentEl)
      realTarget.style.backgroundColor = cellBgColor
      realTarget.style.color = cellColor
      parentEl.style.backgroundColor = rowBgColor
      parentEl.style.color = rowColor
      this.curClickTarget = realTarget
    },

    /**
     * @description 缓存样式
     * @param {Object} 点击或hover事件对象
     * @param {Object} 点击或hover事件对象的父级dome元素
     */
    saveOriStyle(target, parentEl) {
      this.originalStyle = {
        targetBgColor: target.style.backgroundColor,
        targetColor: target.style.color,
        parentBgColor: parentEl.style.backgroundColor,
        parentColor: parentEl.style.color
      }
    },

    /**
     * @description 去除上一个被点击dom元素的事件样式
     */
    removePreTargetStyle() {
      if (!this.curClickTarget) {
        return
      }
      const { targetBgColor, targetColor, parentBgColor, parentColor } =
        this.originalStyle
      this.curClickTarget.style.backgroundColor = targetBgColor
      this.curClickTarget.style.color = targetColor
      this.curClickTarget.parentElement.style.backgroundColor = parentBgColor
      this.curClickTarget.parentElement.style.color = parentColor
      this.curClickTarget = null
    },

    /**
     * @description 去除上一个被hover的dom元素的事件样式
     */
    removePreHoverTargetStyle() {
      if (!this.curHoverTarget) {
        return
      }
      const { targetBgColor, targetColor, parentBgColor, parentColor } =
        this.originalStyle
      this.curHoverTarget.style.backgroundColor = targetBgColor
      this.curHoverTarget.style.color = targetColor
      this.curHoverTarget.parentElement.style.backgroundColor = parentBgColor
      this.curHoverTarget.parentElement.style.color = parentColor
      this.curHoverTarget = null
    },

    /**
     * @description 表格的mouseout事件处理回调方法
     * @param {Obejct} event mouseout事件对象
     */
    handleTableHoverEnd(event) {
      this.removePreHoverTargetStyle()
    },

    /**
     * @description 表格的mouseover事件处理回调方法
     * @param {Object} event mouseover事件对象
     */
    handleTableHover(event) {
      if (!this.options.eventStyleConfig.isUsed) {
        return
      }
      const hoverTarget = this.filterDomFromPath(
        this.getPath(event),
        'wemax-table-tbody-td'
      )
      if (this.curHoverTarget === hoverTarget) {
        return
      }
      if (this.curHoverTarget !== hoverTarget) {
        this.removePreHoverTargetStyle()
      }
      this.setHoverTargetStyle(event)
    },

    /**
     * @description 设置被hover元素效果样式
     * @param {Object} event hover事件对象
     */
    setHoverTargetStyle(event) {
      const { rowBgColor, rowColor, cellBgColor, cellColor } =
        this.options.eventStyleConfig.hover
      const parentEl = this.filterDomFromPath(
        this.getPath(event),
        'wemax-table-tbody-tr'
      )
      const realTarget = this.filterDomFromPath(
        this.getPath(event),
        'wemax-table-tbody-td'
      )
      this.saveOriStyle(realTarget, parentEl)
      realTarget.style.backgroundColor = cellBgColor
      realTarget.style.color = cellColor
      parentEl.style.backgroundColor = rowBgColor
      parentEl.style.color = rowColor
      this.curHoverTarget = realTarget
    },

    /**
     * @description 订阅消息
     * @param {String} topic 被订阅的消息名称
     */
    subscribe(topic) {
      this.subscribeMessageFun(topic, (msgData) => {
        if (topic === 'variablesSubscribption') {
          this.handlerSubscribeMsg(msgData, this.options.apis)
        }
      })
    },

    /**
     * @description sorter组件向上emit的setSort事件
     * @param {Object} sortObj 排序对象
     * @param {Number} index 排序字段下标
     */
    getSortingData(sortObj, index) {
      this.options.sortConfig = sortObj
      this.curSortIndex = index
      this.berforeGetTableData()
    },

    /**
     * @description 设置边框样式
     * @param {String} type 设置类型
     * @returns {Object}
     */
    getBorderStyle(type) {
      const { borderWidth, borderColor, themeBorderColor, borderStyle } = this.options[type]
      return {
        'border-width': borderWidth + 'px',
        'border-color':
          borderColor === 'transparent' ? themeBorderColor : borderColor,
        'border-style': borderStyle
      }
    },

    /**
     * @description 设置奇偶行背景色
     * @param {Number} index 行下标
     * @returns {Object}
     */
    getRowBackground(index) {
      const rowIndex = index + 1
      const isEvenCol = rowIndex % 2 === 0
      const colBgColor = isEvenCol
        ? this.getEvenColBgColor()
        : this.getOddColBgColor()
      return {
        'background-color': colBgColor
      }
    },
    getEvenColBgColor() {
      const { evenRowBackground, themeEvenBgColor } =
        this.options.tableRowConfig
      return evenRowBackground === 'transparent'
        ? themeEvenBgColor
        : evenRowBackground
    },

    getOddColBgColor() {
      const { oddRowBackground, themeOddBgColor } = this.options.tableRowConfig
      return oddRowBackground === 'transparent'
        ? themeOddBgColor
        : oddRowBackground
    },

    /**
     * @description 转换样式对象
     * @param {String} type 要转换的类型
     * @returns {Object}
     */
    transStyleObj(type) {
      return {}
    },

    getBgColor(type) {
      const { backgroundColor, themeBgColor } = this.options[type]
      if (!backgroundColor) {
        return
      }
      return backgroundColor === 'transparent' ? themeBgColor : backgroundColor
    },

    /**
     * @description 页码改变
     * @param {Number} val 当前页码
     */
    currentChange(val) {
      this.curPage = val
      this.berforeGetTableData()
    },

    /**
     * @description 表格排序
     * @param {Array} data 需要排序的数据
     * @returns {Array}
     */
    sortData(data) {
      if (!data || data.length === 0) {
        return []
      }
      const { order, field } = this.options.sortConfig
      if (!field || !order) {
        return data
      }
      const filterWord = this.getFilterWord(field)
      data.sort(function (a, b) {
        if (order === 'asc') {
          return a[filterWord] > b[filterWord] ? 1 : -1
        }
        return b[filterWord] > a[filterWord] ? 1 : -1
      })
      return data
    },

    /**
     * @description 获取排序的表头名称
     * @param {String} field 排序字段
     * @returns {String}
     */
    getFilterWord(field) {
      const filtered = this.tableColumns.filter((column) => {
        return column.title.indexOf(field) > -1
      })[0]
      return filtered ? filtered.field : ''
    },

    berforeGetTableData() {
      // 设计时或者sql模型
      if (Watcher.isDesignMode() || this.isSqlModel) {
        this.fetchTableData()
        return
      }
      // 静态 rest 类型 使用了分页
      if (this.isUsePager) {
        this.setTableColumns()
        const sortData = this.sortData(this.tableDataBackup)
        this.tableData = this.getResultByPager(sortData)
        return
      }
      this.fetchTableData()  // 获取数据
    },

    /**
     * @description 获取新的apis配置对象
     * @returns {Object}
     */
    getNewApis() {
      const newApis = _.cloneDeep(this.apis)
      if (this.isUsePager) {
        // returnAll设置为true 让sql模型返回分页字段pageVO
        newApis[0].returnAll = true
        newApis[0].source.pager = {
          curPage: this.curPage,
          pageSize: this.pageSize
        }
      }
      return newApis
    },

    /**
     * @description 获取服务数据
     * @param {Object} apis apis配置对象
     * @param {Function} callBack 回调函数
     */
    fetchTableData() {
      const apis = this.isSqlModel ? this.getNewApis() : this.apis
      this.getData(apis, res => {
        this.getAlarmStatus(res.tableData)
        const type = Object.prototype.toString.call(res).slice(8, -1)
        if (type === 'Object') {
          this.totalRows = res.totalRows
          if (this.isSqlModel) {
            this.tableData = res.tableData
            return
          }
          const sortData = this.sortData(res.tableData)
          this.tableDataBackup = sortData
          this.tableData = this.getResultByPager(sortData)
          return
        }
        this.tableData = []
      })
    },

    /**
     * @description 获取分页之后的数据
     * @param {Array} data 原始数据
     * @returns {Array}
     */
    getResultByPager(data) {
      const offset = (this.curPage - 1) * this.pageSize
      return this.isUsePager ? data.slice(offset, offset + this.pageSize) : data
    },

    /**
     * @description 通过数据映射构建各个系列所需要的数据
     * @param {Array|Object} data 需要进行数据映射处理的原始数据
     * @param {Object} fields 数据源中fields对象
     * @returns {Array}
     */
    buildSeriesData(res, fields, isInitServeI18n) {
      let data = res
      if (this.isSqlModel && this.isUsePager) {
        this.totalRows = res.pageVO.totalRows || 0
        data = res.result
      }
      const fieldsObj = this.getFieldMap(fields)
      let datas = []
      if (Array.isArray(data) && data.length) {
        datas = data.map(item => {
          return this.buildNewData(item, fieldsObj)
        })
        this.rowItemObj =  datas[0] || {}
        this.fieldsObj = fieldsObj
        this.setTableColumns()
        this.setColHeads()
        this.setColumnSettings()
      }
      return {
        totalRows: this.isSqlModel && this.isUsePager ? res.pageVO.totalRows || 0 : datas.length,
        tableData: datas
      }
    },

    buildNewData(item, fieldsObj) {
      const newItem = {}
      Object.keys(fieldsObj).forEach((key) => {
        const realKey = fieldsObj[key]
        const hasKey = Object.prototype.hasOwnProperty.call(item, realKey)
        if (hasKey) {
          newItem[realKey] = item[realKey]
        }
      })
      return newItem
    },
    /**
     * 设置表格列配置项,包括标题、映射后的字段、排序标识
     */
    setTableColumns() {
      const res = []
      Object.keys(this.fieldsObj).forEach((key) => {
        const realKey = this.fieldsObj[key]
        const hasKey =
          realKey !== key || Object.prototype.hasOwnProperty.call(this.rowItemObj, key)
        if (hasKey) {
          res.push({
            title: realKey,
            field: realKey,
            order: this.getOrder(realKey)
          })
        }
      })
      this.tableColumns = res
    },
    /**
     * 从行数据里拿到映射的列名
     */
    setColHeads() {
      this.options.colHeads = Object.keys(this.rowItemObj).map((key) => {
        return {
          text: key,
          value: key
        }
      })
    },

    /**
     * @description 配置列设置columnSettings的值
     */
    setColumnSettings() {
      const list = []
      const { columnSettings } = this.options
      this.tableColumns.forEach((col, index) => {
        const defaultConf = getDefaultColumnSetting() // 获取一部分默认的配置项
        const curConfig = columnSettings.length && columnSettings[index]
        defaultConf.name = col.field
        if (curConfig) {
          Object.assign(defaultConf, curConfig)    // 若有配置项,则覆盖默认的
        }
        list.push(defaultConf)
      })
      this.options.columnSettings = list
    },

    /**
     * @description 获取列头排序类型
     * @param {String} name 列名
     * @returns {String}
     */
    getOrder(name) {
      const { field, order } = this.options.sortConfig
      return name === field ? order : ''
    }
  }
}

# 配置项详解

下面根据面板配置项来专项讲解表格,表格面板配置项跟图表类有相似之处,其中的数据服务、属性配置中的标题和全局都很类似,因此下面重点讲解不同之处

# 行--表头

截图99

表格组件的表头配置项是TableHeadConfig.vue文件,

<template>
  <aui-form>      // 表单组件
    <aui-form-item label="显示">  // 表单项
        // 复选框
      <aui-checkbox v-model="options.tableHeadConfig.show"></aui-checkbox> 
    </aui-form-item>
    <aui-form-item label="背景颜色">
       // 颜色选择器
      <color-picker             
        v-if="!gradientFlag"
        v-model="options.tableHeadConfig.backgroundColor"
        :color="options.tableHeadConfig.backgroundColor"
      ></color-picker>
    </aui-form-item>
    // 渐变组件,包含渐变角度、起始颜色、结束颜色
    <GradientConfig      
      :data="options.tableHeadConfig"
      :isChartGradient="false"
      defaultBgColorField="gradientBackground"></GradientConfig>
    <aui-form-item label="行高">
       // 计数器
      <aui-numeric  
        v-model="options.tableHeadConfig.height"
        :min="0"
        :max="100"
        size="normal"
      >
      </aui-numeric>
    </aui-form-item>
    <aui-form-item label="水平对齐">
       // 下拉选择器
      <aui-select
        v-model="options.tableHeadConfig.align">
        <aui-option
          v-for="item in alignList"
          :key="item.value"
          :label="item.text"
          :value="item.value"
        ></aui-option>
      </aui-select>
    </aui-form-item>
     // 通用字体设置组件,包含字号同步、字号大小、字体颜色、字体粗细
    <FontConfig :options="options.tableHeadConfig" /> 
     // 通用边框组件,包括边框宽度、边框颜色、边框样式
    <BorderConfig :options="options.tableHeadConfig" />
    <div class="wemax-config-subtitle">排序图标</div>
     // 排序图标组件,包括排序图标的尺寸和颜色
    <IconConfig :options="options.tableHeadConfig" />
  </aui-form>
</template>
<script>
import { Form, FormItem, Checkbox, Select, Option, Numeric } from '@aurora/vue' // 引入AUI组件
import BorderConfig from '../BorderConfig'  // 边框设置
import FontConfig from '../FontConfig.vue'  // 字体设置
import IconConfig from './IconConfig.vue'   // 图标设置
import GradientConfig from '@/components/echarts/GradientConfig' // 渐变设置
import { ColorPicker } from '@aurora/wemax-card-plugin'  // 颜色选择器
export default {
  name: 'TableHeadConfig',
  components: {
    AuiForm: Form,
    AuiFormItem: FormItem,
    AuiCheckbox: Checkbox,
    AuiSelect: Select,
    AuiOption: Option,
    AuiNumeric: Numeric,
    ColorPicker,
    BorderConfig,
    FontConfig,
    GradientConfig,
    IconConfig
  },
  props: {
    options: Object
  },
  data() {
    return {
      alignList: [   // 水平对齐方式
        { text: '左对齐', value: 'left' },
        { text: '居中', value: 'center' },
        { text: '右对齐', value: 'right' }
      ]
    }
  },
  computed: {
    gradientFlag: { // 控制背景颜色的显隐,如果开启了渐变开关,则需关闭背景颜色选择器,防止两者冲突
      get() {
        return this.options.tableHeadConfig.gradientFlag
      }
    }
  }
}
</script>
<style lang="less" scoped>
.wemax-config-subtitle {
  font-weight: bold;
  font-size: 14px;
  margin-bottom: 0.3rem;
}
</style>

# 行-表行

截图10

表行配置项对应的组件为TableRowConfig.vue文件,由于该面板和表头的面板大为相似,因此不做重复讲解,重点讲解一下添加行条件这个配置项,该项对应的文件为RowConditionConfig.vue

<template>
  <div>
    <AuiButton @click="showConditionPanel">添加行条件</AuiButton>
    <RowConditionPanel :isShow="isShowPanel" :options="options" @closePanel="closeConditionPanel"></RowConditionPanel>
  </div>
</template>

该文件中引入按钮组件AuiButton和条件面板组件RowConditionPanel,接下来讲解后者

代码如下。

<template>
  // aui弹框组件
  <aui-dialog-box  
    v-model="showDialog"
    draggable
    title="新增行条件"
    :close-on-click-modal="false"
    width="950px"
    class="rowConditionDialog"
  >
    // 页签组件,可新增多个条件
    <aui-tabs
      v-if="rowConditionList.length"
      v-model="activeName"
      :with-add="true"
      :with-close="true"
      @add="addCondition"
      @close="deleteCondition"
    >
      // 每个页签项的配置
      <aui-tab-item
        v-for="(item, index) in rowConditionList"
        :key="index"
        :title="'条件' + (index + 1)"
        :name="String(index)"
      >
        // 行条件配置组件
        <RowConditions
          :rowTabConditionObj="item"
          :colHeads="colHeads"
        >
        </RowConditions>
      </aui-tab-item>
    </aui-tabs>
    <template #footer>  // 底部的确认和取消按钮
      <aui-button @click="showDialog = false">取消</aui-button>
      <aui-button
        type="primary"
        @click="setRowConditions"
      >确定</aui-button>
    </template>
  </aui-dialog-box>
</template>

点击添加行条件,弹出如下面板,该面板展示的条件为:当column1的值等于800并且column2的值等600时,这一行的文字颜色为#ffffff,背景颜色为transparent,该样式可以自行设置。

截图11

具体行条件配置的代码如下

<template>
  <div class="condition-row">
    <div class="item-condition-col handle-icons">
       // 对应上图左边的加号图标,可新加条件
      <IconPlusCircle    
        class="aui-svg-icon icon-plus-circle"
        @click="addRow"
      ></IconPlusCircle>
       // 对应上图左边的减号图标,可删除条件
      <IconMinusCircle
        class="aui-svg-icon icon-minus-circle"
        @click="deleteRow"
      ></IconMinusCircle>
    </div>
    <div class="item-condition-col relation">
       // 一个值不存在逻辑运算,需两个及以上
      <aui-select
        v-show="criteriaIndex !== 0"
        v-model="conditionItem.logical"
      >
        // 条件的逻辑运算,包括与、或
        <aui-option
          v-for="item in logicalOp" 
          :key="item.value"
          :label="item.label"
          :value="item.value"
        >
        </aui-option>
      </aui-select>
    </div>
    <div class="item-condition-col filedName">
      <aui-select
        v-model="conditionItem.colName"
        filterable
      >
        // 在字段中选择列名
        <aui-option
          v-for="item in colHeads" 
          :key="item.value"
          :label="item.label"
          :value="item.value"
        >
        </aui-option>
      </aui-select>
    </div>
    <div class="item-condition-col operation-symbol">
      <aui-select
        v-model="conditionItem.operator" 
        filterable
      >
       // 运算符,包括大于、等于、小于等常用运算符
        <aui-option
          v-for="item in operators"
          :key="item.value"
          :label="item.text"
          :value="item.value"
        >
        </aui-option>
      </aui-select>
    </div>
    <div class="item-condition-col operation-value">
        //  输入框组件,输入一个值与列值进行比较
      <aui-input  
        v-model="conditionItemValue"
        clearable
      ></aui-input>
    </div>
    // 只当第一个条件生成时才出现颜色选择器,最终颜色由多个条件进行逻辑运算后决定
    <div class="item-condition-col text-color">
      // 文字颜色选择器
      <color-picker
        v-if="criteriaIndex === 0"
        v-model="rowTabConditionObj.color"
        :color="rowTabConditionObj.color"
      ></color-picker>
    </div>
    <div class="item-condition-col background-color">
      // 背景颜色选择器
      <color-picker
        v-if="criteriaIndex === 0"
        v-model="rowTabConditionObj.bgColor"
        :color="rowTabConditionObj.bgColor"
      ></color-picker>
    </div>
  </div>
</template>
<script>
import { Select, Option, Input } from '@aurora/vue' // 引入aui组件
import { IconPlusCircle, IconMinusCircle } from '@aurora/vue-icon' // 引入加号、减号图标
import { ColorPicker, $xss } from '@aurora/wemax-card-plugin'  // 引入颜色选择器以及XSS攻击输出控制
export default {
  name: 'RowConditionItem',
  components: {
    AuiSelect: Select,
    AuiOption: Option,
    AuiInput: Input,
    IconPlusCircle: IconPlusCircle(),
    IconMinusCircle: IconMinusCircle(),
    ColorPicker
  },
  props: {
    conditionItem: Object,
    rowTabConditionObj: Object,
    colHeads: Array,
    criteriaIndex: {
      type: Number,
      default: -1
    }
  },
  data() {
    return {
      logicalOp: [  // 逻辑运算符,用于各个条件间的运算
        { label: '与', value: '&&' },
        { label: '或', value: '||' }
      ],
      operators: [  // 运算符,用于判断输入的值与列值的比较关系
        {
          text: '大于', value: '>'
        },
        {
          text: '大于等于', value: '>='
        },
        {
          text: '等于', value: '='
        },
        {
          text: '小于等于', value: '<='
        },
        {
          text: '小于', value: '<'
        },
        {
          text: '不等于', value: '!='
        }
      ]
    }
  },
  computed: {
    conditionItemValue: {
      get() {
        return this.conditionItem.colVal
      },
      set(val) {
        this.conditionItem.colVal = $xss.process(val)
      }
    }
  },
  methods: {
    /**
     * @description 新增行
     */
    addRow() {
      this.$emit('addRow', this.criteriaIndex)
    },

    /**
     * @description 移除行
     */
    deleteRow() {
      this.$emit('deleteRow', this.criteriaIndex)
    }
  }
}
 </script>

# 行-触发样式

行的触发样式配置面板如下

截图632

(1)当点击表格时,提供了两种选择,一种是可发送单元格的值,另一种点击可发送一整行的值。具体区别体现在:当在WeMax平台上选择交互选项时,若选择了前者,则当点击第二列的某一单元格时,就只有column2变量会有值,其他变量为空;若选择发送一整行的值时,当点击第二列的某一单元格时,则下图存在的column系列变量都会接收到值。

截图13

(2)事件效果的开关默认关闭,当打开该开关时,此时表格点击或者划过便会出现背景颜色和文字颜色的变化,文件为EventStyleConfig.vue,如下为鼠标滑过单元格的效果图

截图1012

代码如下

<template>
  <aui-form>
    <aui-form-item size="mini">
      <template #label>
        <span>启用</span>
      </template>
      <aui-switch v-model="options.eventStyleConfig.isUsed"></aui-switch>
    </aui-form-item>
    <div v-if="options.eventStyleConfig.isUsed">
      <div class="wemax-config-subtitle">行点击效果</div>
      <aui-form-item label="背景颜色">
        <color-picker
          v-model="options.eventStyleConfig.click.rowBgColor"
          :color="options.eventStyleConfig.click.rowBgColor"
        >
        </color-picker>
      </aui-form-item>
      <aui-form-item label="字体颜色">
        <color-picker
          v-model="options.eventStyleConfig.click.rowColor"
          :color="options.eventStyleConfig.click.rowColor"
        >
        </color-picker>
      </aui-form-item>
      <div class="wemax-config-subtitle">单元格点击效果</div>
      <aui-form-item label="背景颜色">
        <color-picker
          v-model="options.eventStyleConfig.click.cellBgColor"
          :color="options.eventStyleConfig.click.cellBgColor"
        >
        </color-picker>
      </aui-form-item>
      <aui-form-item label="字体颜色">
        <color-picker
          v-model="options.eventStyleConfig.click.cellColor"
          :color="options.eventStyleConfig.click.cellColor"
        >
        </color-picker>
      </aui-form-item>
      <div class="wemax-config-subtitle">行滑过效果</div>
      <aui-form-item label="背景颜色">
        <color-picker
          v-model="options.eventStyleConfig.hover.rowBgColor"
          :color="options.eventStyleConfig.hover.rowBgColor"
        >
        </color-picker>
      </aui-form-item>
      <aui-form-item label="字体颜色">
        <color-picker
          v-model="options.eventStyleConfig.hover.rowColor"
          :color="options.eventStyleConfig.hover.rowColor"
        >
        </color-picker>
      </aui-form-item>
      <div class="wemax-config-subtitle">单元格滑过效果</div>
      <aui-form-item label="背景颜色">
        <color-picker
          v-model="options.eventStyleConfig.hover.cellBgColor"
          :color="options.eventStyleConfig.hover.cellBgColor"
        >
        </color-picker>
      </aui-form-item>
      <aui-form-item label="字体颜色">
        <color-picker
          v-model="options.eventStyleConfig.hover.cellColor"
          :color="options.eventStyleConfig.hover.cellColor"
        >
        </color-picker>
      </aui-form-item>
    </div>
  </aui-form>
</template>
<script>
import { Form, FormItem, Switch } from '@aurora/vue'
import { ColorPicker } from '@aurora/wemax-card-plugin'
export default {
  name: 'EventStyleConfig',
  components: {
    AuiForm: Form,
    AuiFormItem: FormItem,
    AuiSwitch: Switch,
    ColorPicker
  },
  props: {
    options: Object
  }
}
</script>
<style lang="less" scoped>
.wemax-config-subtitle {
  font-weight: bold;
  font-size: 14px;
  margin-bottom: 0.3rem;
  &:not(:first-child) {
    margin-top: 0.8rem;
  }
}
</style>

相关的card.json配置如下

      "eventStyleConfig": {
        "isUsed": false,
        "click": {
          "rowBgColor": "transparent",
          "rowColor": "#ffffff",
          "cellBgColor": "transparent",
          "cellColor": "#ffffff"
        },
        "hover": {
          "rowBgColor": "transparent",
          "rowColor": "#ffffff",
          "cellBgColor": "transparent",
          "cellColor": "#ffffff"
        }
      },

card.vue中,hover状态的样式代码如下

   /**
     * @description 设置被hover元素效果样式
     * @param {Object} event hover事件对象
     */
    setHoverTargetStyle(event) {
      const { rowBgColor, rowColor, cellBgColor, cellColor } =
        this.options.eventStyleConfig.hover   // 从option配置项中得到hover状态的样式字段
      // 获取点击事件所在的行
      const parentEl = this.filterDomFromPath(
        this.getPath(event),
        'wemax-table-tbody-tr'
      )
      // 获取点击事件所在的单元格
      const realTarget = this.filterDomFromPath(
        this.getPath(event),
        'wemax-table-tbody-td'
      )
      this.saveOriStyle(realTarget, parentEl) // 保存原始的样式
      // 设置hover样式
      realTarget.style.backgroundColor = cellBgColor
      realTarget.style.color = cellColor
      parentEl.style.backgroundColor = rowBgColor
      parentEl.style.color = rowColor
      this.curHoverTarget = realTarget  // 保存当前事件所在的单元格
    },

# 列-基础

列配置项的基础栏包括序号以及各列具体的配置,包括修改列名、列宽,显示或隐藏该列等。文件为ColumnSettingItem.vue

截图66

代码为

<template>
  <div>
     // 序号列不用排序
    <aui-form-item
      v-if="hasSortableAttr"  
      label="可排序"
    >
      </aui-checkbox>
    </aui-form-item>
    <aui-form-item label="列名">
      <aui-input
        v-model="columnName"
        type="text"
      >
      </aui-input>
    </aui-form-item>
    <aui-form-item
      v-if="notSeqCol"
      label="后缀"
    >
      <aui-input
        v-model="endUnit"
        type="text"
      >
      </aui-input>
    </aui-form-item>
    <aui-form-item label="列宽(%)">
      <aui-numeric
        v-model="config.width"
        :min="1"
        :max="100"
        :precision="0"
      >
      </aui-numeric>
    </aui-form-item>
  </div>
</template>

<script>
import { Input, FormItem, Numeric, Checkbox } from '@aurora/vue'
import { $xss } from '@aurora/wemax-card-plugin'
import _ from 'lodash'
export default {
  name: 'ColumnSettingItem',
  components: {
    AuiInput: Input,
    AuiFormItem: FormItem,
    AuiNumeric: Numeric,
    AuiCheckbox: Checkbox
  },
  props: {
    config: Object,
    colIndex: Number,
    columns: Array,
    seqCol: Object
  },
  data() {
    return {
      columnName: '',
      endUnit: '',
      columnWidth: 0
    }
  },
  computed: {
    hasSortableAttr() {  // 序号列不用排序
      const hasOwnProperty = Object.prototype.hasOwnProperty
      return hasOwnProperty.call(this.config, 'sortable')
    },
    notSeqCol() {   // 不是序号列
      return typeof this.colIndex === 'number'
    }
  },
  watch: {
    columnName(value) {
      this.setColumnName(value)
    },
    endUnit(val) {
      this.setColumnUnit(val)
    }
  },
  mounted() {
    this.setColumnName = _.debounce(this.setColumnName, 500)
    this.setColumnUnit = _.debounce(this.setColumnUnit, 500)
    this.columnName = this.config.name
    this.endUnit = this.config.endUnit
  },
  methods: {
    checkIfLastColumn() {
      const isSeq = typeof this.colIndex !== 'number'
      return !isSeq && this.colIndex === this.columns.length - 1
    },
    setColumnName(val) {
      this.config.name = $xss.process(val)
    },
    setColumnUnit(val) {
      this.config.endUnit = $xss.process(val)
    }
  }
}
</script>

# 列-列头合并

列头合并这一项,可以添加列头。点击添加列头,在左边表头之上便会新增一个列头,可以配置名称以及所占的列数。文件为SelfHeadItem.vue。此处配置简单,代码不做详细讲解。

截图998

# 列-条件

该配置项可以给每一列添加条件,如下图所示,该条件表示,当column1的值大于800、效果类型是文字时,对应的单元格的字号、颜色、粗细等就会如配置所示,其中,效果类型还可以选链接、图片、弹框等。

# 截图879

其中条件的代码如下

<template>
  <aui-form class="column-condition-list">
    <div
      class="add-condition"
      @click="addCondition"
    >添加条件</div>
     // columnConditions为条件列表,可设置多个条件
    <template v-for="(condition,index) in columnConditions"> 
      <div
        :key="index"
        class="list-item"
      >
        <div>
          <span class="item-title">
            {{`条件${index + 1}`}} // 条件名称:条件1、条件2
            <icon-close-square
              class="remove-condition"
              title="删除条件"
              @click="removeCondition(index)"
            ></icon-close-square>
          </span>
          <condition-item
            :config="condition"
            :operators="operators"
          ></condition-item>
          <aui-form-item label="效果类型">
            <aui-select v-model="condition.type">
              <aui-option
                v-for="item in styleTypes"
                :key="item.value"
                :label="item.text"
                :value="item.value"
              >
              </aui-option>
            </aui-select>
          </aui-form-item>
          <column-text-sty-setting
            v-if="condition.type === 'text'" // 文本
            :options="condition"
          >
          </column-text-sty-setting>
          <link-text-setting
            v-else-if="condition.type === 'link'"  // 链接
            :options="condition"
          >
          </link-text-setting>
          <dialog-setting
            v-else-if="condition.type === 'dialogBox'" // 弹框
            :options="condition"
          >
          </dialog-setting>
          <progress-sty-setting
            v-else-if="condition.type === 'progress'"  // 进度条
            :options="condition"
          >
          </progress-sty-setting>
          <image-setting  // 图片
            v-else
            :options="condition"
            @changePosType="(val) => changePos(val, index)"
          ></image-setting>
        </div>
      </div>
    </template>
  </aui-form>
</template>
<script>
import { Form, FormItem, Select, Option } from '@aurora/vue'
import ColumnTextStySetting from './ColumnTextStySetting.vue'
import ConditionItem from './ConditionItem.vue'
import ProgressStySetting from './ProgressStySetting.vue'
import ImageSetting from './ImageSetting.vue'
import LinkTextSetting from './LinkTextSetting.vue'
import DialogSetting from './DialogSetting.vue'
import { IconCloseSquare } from '@aurora/vue-icon'
export default {
  name: 'ColumnConditionConfig',
  components: {
    AuiForm: Form,
    AuiFormItem: FormItem,
    AuiSelect: Select,
    AuiOption: Option,
    ConditionItem,
    IconCloseSquare: IconCloseSquare(),
    ColumnTextStySetting,
    ProgressStySetting,
    ImageSetting,
    LinkTextSetting,
    DialogSetting
  },
  props: {
    options: Object
  },
  data() {
    return {
      columnConditions: [],
      operators: [
        {
          text: '无', value: '--'
        },
        {
          text: '大于', value: '>'
        },
        {
          text: '大于等于', value: '>='
        },
        {
          text: '等于', value: '='
        },
        {
          text: '小于等于', value: '<='
        },
        {
          text: '小于', value: '<'
        },
        {
          text: '不等于', value: '!='
        }
      ],
      styleTypes: [
        {
          text: '文字', value: 'text'
        },
        {
          text: '链接', value: 'link'
        },
        {
          text: '弹窗', value: 'dialogBox'
        },
        {
          text: '图片', value: 'image'
        },
        {
          text: '进度条', value: 'progress'
        }
      ]
    }
  },
  watch: {
    columnConditions: { // 条件有变化,及时更新option中的条件列表,保持两者相同
      deep: true,
      handler(value) {
        if (value) {
          this.options.columnConditionSetting = this.columnConditions
        }
      }
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    changePos(val, index) {
      this.columnConditions[index].position = val
    },

    /**
     * @description 初始化列条件
     */
    init() {
      const { columnConditionSetting } = this.options
      this.columnConditions = JSON.parse(JSON.stringify(columnConditionSetting))
    },

    /**
     * @description 添加新条件,条件的默认配置
     */
    addCondition() {
      const newCondition = {
        colName: '',
        operator: '--',
        colVal: '',
        type: 'text',
        color: "#ffffff",
        fontSize: 16,
        fontWeight: 400,
        useItalic: false,
        useUnderLine: false,
        imageUrl: "",
        imageSize: 10,
        position: 'row',
        hideText: false,
        borderRadius: 5,
        progressWidth: 5,
        progressLength: 1000,
        progressBg: "transparent",
        dialogUrl: '',
        linkUrl: '',
        linkType: '_blank'
      }
      this.columnConditions.push(newCondition)
    },

    /**
     * @description 移除对应下标条件
     * @param {Number} index 条件下标
     */
    removeCondition(index) {
      this.columnConditions.splice(index, 1)
    }
  }
}
</script>

# 实操讲解(新增列显隐)

在列-基础栏中,在每一列的配置项里加一个显示的选项,表示该列是否隐藏,原型图如下,默认勾选。当取消勾选时该列隐藏

截图9999

具体步骤为:

# 1.新增配置项

在ColumnSettingItem.vue文件中,找到可排序配置项的上方,新增一个aui-form-item标签如下

  <aui-form-item label="显示">
      <aui-checkbox v-model="config.show"></aui-checkbox>
  </aui-form-item>

# 2.在columnSettings数组中,新增show属性

在card.vue文件中,找到setColumnSettings函数,该函数是设置columnSettings的值

    setColumnSettings() {
      const list = []
      const { columnSettings } = this.options
      this.tableColumns.forEach((col, index) => {
        const defaultConf = getDefaultColumnSetting()
        const curConfig = columnSettings.length && columnSettings[index]
        defaultConf.name = col.field
        // 重点:新增显隐控制属性
        defaultConf.show = true  // 默认为true,显示列
        if (curConfig) {
          Object.assign(defaultConf, curConfig)
        }
        list.push(defaultConf)
      })
      this.options.columnSettings = list
    },

# 3.card.vue文件中修改DOM和新增对应函数

在card.vue文件的DOM结构中,找到设置列的标签元素,它是包括列头和列普通单元格,下面是列头的对应标签。

 <th
     v-for="(item, index) in tableColumns"
     :key="item.field + (Math.random() + 1)"
     :style="[headerStyle, getColumnWidth(index)]"
     class="table-head wemax-thead-th"
  >

由此可以看出只需要在tableColumns数组的每一项中加入一个控制显隐的show字段就行了,这个show字段需来源于columnSettings,即列条件数组。而且当columnSettings变化时,应及时更新,因此需新增监听如下

  columnSettings: {
    deep: true,
    handler(val) {
      this.updateTableColumns()
    }
  }

updateTableColumns函数如下

    updateTableColumns() {
      this.tableColumns = this.tableColumns.map((item,index) => {
        item.show = this.columnSettings[index].show
        return item
      })
    }

于是在列头标签处新增v-show

    // 列头新增显隐控制
	<th
         v-for="(item, index) in tableColumns"
         v-show="item.show"
         :key="item.field + (Math.random() + 1)"
         :style="[headerStyle, getColumnWidth(index)]"
         class="table-head wemax-thead-th"
      >

然后在列单元格标签处新增v-show

   // 列单元格新增显隐控制
	<td
        v-for="(item, colIndex) in tableColumns"
        v-show="item.show"
        :key="item.field + (Math.random() + 1)"
        :style="[rowStyle, getColumnWidth(colIndex)]"
        :field="item.field"
        class="wemax-table-tbody-td"
        @click="colClickStyle(data, item.field, colIndex)"
        >

至此,列控制显隐的需求就全部完成,当点击去掉勾选的列配置中的显示复选框时,表格该列便会隐藏。

截图888

更新时间: 7/1/2024, 6:12:44 PM