页面树结构
转至元数据结尾
转至元数据起始

该宏示例在 V10.5上 验证通过

本文档的示例代码仅适用于本文档中的示例报表/场景。若实际报表/场景与示例代码无法完全适配(如使用功能不一致,或多个宏代码冲突等),需根据实际需求开发代码。

1. 示例效果

筛选器选择"年月日",图表的数据范围发生改变。

  • 当选择频度为“”时,图表显示区间为 [ "当前设置的'日期'往前 num 天" , "当前设置的'日期'" ]。例如宏代码内“num”的预设值为40,则显示当前日期前40天内的数据。设置“日期”筛选器的值为“2017-05-06”(截止日期),则图表显示区间为[ "2017-03-27", "2017-05-06" ]
  • 当选择频度为“”时,图表显示区间为 [ "当前设置的'日期'往前 num 月" , "当前设置的'日期'" ],数据根据月汇总。例如宏代码内“num”的预设值为12,即显示当前日期前12个月内的数据。设置“日期”筛选器的值为“2017-05-06”(截止日期),则图表显示区间为[ "2016-05-06", "2017-05-06" ]
  • 当选择频度为“”时,图表显示区间为 [ "当前设置的'日期'往前 num 年" , "当前设置的'日期'" ],数据根据年汇总。例如宏代码内“num”的预设值为1,即显示当前日期前1年内的数据。设置“日期”筛选器的值为“2017-05-06”(截止日期),则图表显示区间为[ "2016-05-06", "2017-05-06" ]

由于当前所使用的“订单日期”字段的数据截止到2017年5月6日,因此预设的日期为“2017-05-06”

2. 操作步骤

2.1. 新建空白仪表盘

2.2. 设计图形

  • 选择数据模型“销售综合数据”,接着拖入一个线图组件到画布中

  • 从数据区内拖拽相应字段到指定区域,如下图所示

  • 右键选择该图表,点击“组件设置”

  • 在【坐标轴】>【分类轴】下,开启显示“缩略轴”

2.3. 添加筛选器

  • 在数据区内的维度内的拖拽四次字段“OrderDate”到线图的筛选框内,分别对其组件标题进行重命名为“日期”、“年”、“月”、“日”,并应用于图表2。
  • 接着拖拽“参数”筛选器到画布上,修改组件标题为“选择器”,该参数控件不作用于任何组件。

  • 接着右键选中筛选器“日期”,点击“筛选器设置

  • 修改 筛选器“日期” 的操作符为“小于等于”,设置默认值为“2017-05-06”

  • 其它筛选器的操作同理,各筛选器的详细配置如下:
筛选器标题筛选器设置是否显示组件
日期设置操作符为“小于等于”,默认值为“2017年5月6日”显示组件
设置操作符为“大于等于”隐藏组件
设置操作符为“大于等于”隐藏组件
设置操作符为“大于等于”隐藏组件

2.4. 编写宏代码

  • 选中名为“选择器”的筛选器,右键进入“宏管理”页面

  • ①:通过点击时间粒度选择器,刷新图形数据。在该名为“选择器组件上新建宏,事件为“onBeforeRender(组件渲染前)”,把下面宏代码复制到代码区域,接着保存该宏代码。
function main(page: IPage, portlet: IFilterPortlet) {
    init(page, portlet)
}

/**
 * 场景:通过点击时间粒度选择器,刷新图形数据
 * 实现:
 * 1、添加一个echarts的曲线图:"图表",用于展示数据,并通过【组件设置-自定义属性】添加dataZoom控制条(具体请看资源)。
 * 2、数据模型添加一个无用的参数,作为筛选器:”选择器“(列表单选:日、月、年),用于选择时间粒度,并通过宏设置样式、监听选择器值变化函数,触发时间范围变化,且不作用于任何组件;
 * 3、拖拽一个时间字段作为筛选器:”日期“,用于作为当前日期即时间粒度中的结束日期,并通过宏添加监听值变化函数,触发时间范围变化,且作用于echarts图形组件;
 * 4、拖拽上面的那个时间字段三个,分别作为筛选器:”日“、”月“、”年“,用于作为时间粒度范围中的开始日期,且作用于echarts图形组件。
 * (由于只是为了让图形组件请求数据,所以这三个筛选器最终作图后可以选择隐藏组件,不展示在仪表盘中。)
 */

// 初始化选中
function init(page: IPage, portlet: IFilterPortlet) {
    
    // 选择器点击修改日期的开始时间
    doSelectTime(page, portlet)
    // 初始化选择器第一次选中
    let selectorValue: any = portlet.getValue()
    method.doAction(page, selectorValue[0])
}
// 点击选择器时更新年月日的值
function doSelectTime(page: IPage, startDatePortlet: IFilterPortlet) {
    startDatePortlet.setFilterValueChangeHandler(value => {
        if (value && value[0]) {
            method.doAction(page, value[0])
        }
        return value
    })
    // 日期选择器修改日期时也需要更新开始时间
    let endDateFilter: IFilterPortlet = method.getPortlet(page, method.dc.endDatePortletTitle)
    if (!endDateFilter) return
    endDateFilter.setFilterValueChangeHandler(value => {
        setTimeout(() => { 
            let selectorValue = startDatePortlet.getValue()
            if (value && value[0]) {
                method.doAction(page, selectorValue[0])
            }
        }, 0)
        return value
    })
}

var method = {
    // 字段信息常量
    fc: {
        DATEINDEX: 0,
        VALUEINDEX: 1
    },
    // 组件标题常量
    dc: {
        endDatePortletTitle: '日期',
        chartPortletTitle: '图表',
        textSelectorPortlet: '选择器'
    },
    // 定义选择器
    getSelectors() {
        return [
            { label: '年', calc: method.calcYear, total: method.calcTotalYear },
            { label: '月', calc: method.calcMonth, total: method.calcTotalMonth },
            { label: '日', calc: method.calcDate, total: method.calcTotalDay, checked: true /**默认高亮 */ }
        ]
    },
    // 点击事件处理
    doAction(page: IPage, type: string | any) {
        let selectors: any = method.getSelectors()
        let selector = selectors.find((el: any) => el.label === type)
        if (!selector) return
        let endDateValue = method.getBaseDateValue(page)
        let endDate: Date = new Date(endDateValue)
        let beginDate: Date = selector.calc(new Date(endDateValue))
        method.setValues(page, beginDate, selector.label)
        let bdate = SmartbiXMacro.utils.formatDate(beginDate, 'yyyy-MM-dd')
        let edate = SmartbiXMacro.utils.formatDate(endDate, 'yyyy-MM-dd')
        // ECharts图形
        let portlet: IEChartsPortlet = method.getPortlet(page, method.dc.chartPortletTitle)
        if (!portlet) return
        let flag = false
        page.doPortletRendered(portlet.getId(), () => {
            if (flag) return
            flag = true
            let options = portlet.getChartOptions()
            if (!options) return
            options = method.updateSeries(options, bdate, edate, selector)
            portlet.setChartOptions(options)
        })
    },
    // 更新Series(做合计)
    updateSeries(options: object | any, bdate: string, edate: string, selector: any) {
        selector.total(options, bdate, edate)
        return options
    },
    // 不足两位补全0
    zeroFn(num: number | string) {
        return num < 10 ? '0' + num : num + ''
    },
    // 计算合计:年
    calcTotalYear(options: any, bdate: string, edate: string) {
        let { tooltipInfos } = options
        let keys = Object.keys(tooltipInfos) || []
        let da = { start: new Date(bdate), end: new Date(edate) }
        let y = { start: da.start.getFullYear(), end: da.end.getFullYear() }
        let m = { start: da.start.getMonth() + 1, end: da.end.getMonth() + 1 }
        let d = { start: da.start.getDate(), end: da.end.getDate() }

        let zeroFn = method.zeroFn

        let dataObj: any = {}
        let curTotal = 0 // 合计
        let newAxisData: string[] = [] // 新的Axis的data
        let newTooltipInfos: any = {} // 新的tooltipInfos
        let newSeriesData: any = [] // 新的series的data

        keys.forEach((key, index) => {
            let item = tooltipInfos[key]
            let dateValue = item[method.fc.DATEINDEX].value
            let dataValue = item[method.fc.VALUEINDEX].realValue
            let date = new Date(dateValue)
            let year = date.getFullYear()
            let startDateValue = `${year}-01-01`
            let startDate = new Date(startDateValue)
            let endDateValue = `${year}-${zeroFn(m.end)}-${zeroFn(d.end)}`
            let endDate = new Date(endDateValue)
            let isInDate = date >= startDate && date <= endDate // 一年中指定的[开始时间(year-01-01),结束时间]

            if (!dataObj[year]) {
                dataObj[year] = true
                curTotal = dataValue
                // xAsix的data数据
                newAxisData.push(endDateValue)
            } else if (isInDate) {
                curTotal += dataValue
            }
            let parseD = SmartbiXMacro.utils.formatDate(date, 'yyyy-MM-dd')
            let parseEndD = SmartbiXMacro.utils.formatDate(endDate, 'yyyy-MM-dd')
            // if (isInDate) { 
            // console.warn('isInDate', isInDate, date, startDate, endDate)
            // console.log(parseD === parseEndD, (isInDate && index === keys.length - 1))
            // }
            if (parseD === parseEndD || (isInDate && index === keys.length - 1)) {
                let newIndex = newAxisData.indexOf(parseD)
                let find = newSeriesData.find(el => el.displayValue[0] === endDateValue)
                // console.warn(date, curTotal, newIndex, newAxisData)
                if (!find) {
                    // series的data数据
                    newSeriesData.push({
                        value: [newIndex, curTotal],
                        displayValue: [endDateValue, curTotal],
                        rowIndex: [newIndex, newIndex],
                        colIndex: 1
                    })
                    // tooltip的数据
                    item[method.fc.DATEINDEX].value = endDateValue
                    item[method.fc.DATEINDEX].realValue = new Date(endDateValue).getTime()
                    item[method.fc.VALUEINDEX].value = curTotal + ''
                    item[method.fc.VALUEINDEX].realValue = curTotal
                    let newTooltipInfosKey = newIndex + '_1'
                    if (!newTooltipInfos[newTooltipInfosKey]) {
                        newTooltipInfos[newTooltipInfosKey] = []
                    }
                    newTooltipInfos[newTooltipInfosKey][method.fc.DATEINDEX] = item[method.fc.DATEINDEX]
                    newTooltipInfos[newTooltipInfosKey][method.fc.VALUEINDEX] = item[method.fc.VALUEINDEX]
                }
            }

        })

        options.xAxis[0].data = newAxisData
        options.series[0].data = newSeriesData
        options.tooltipInfos = newTooltipInfos
        // console.warn('newAxisData', newAxisData)
        // console.warn('newSeriesData', newSeriesData)
        // console.warn('tooltipInfos', tooltipInfos)
        return options
    },
    // 计算合计:月
    calcTotalMonth(options: any, bdate: string, edate: string) {
        let { tooltipInfos } = options
        let keys = Object.keys(tooltipInfos) || []
        let startMonth = new Date(bdate).getMonth()
        let endDay = new Date(edate).getDate()

        let zeroFn = method.zeroFn

        let dataObj: any = {}
        let newAxisData: string[] = []
        let newTooltipInfos: any = {}
        let newSeriesData: any = []

        let curTotal = 0 // 合计
        keys.forEach((key) => {
            let item = tooltipInfos[key]
            let dateValue = item[method.fc.DATEINDEX].value
            let dataValue = item[method.fc.VALUEINDEX].realValue
            let date = new Date(dateValue)
            let year = date.getFullYear()
            let month: number | string = zeroFn(date.getMonth() + 1)
            let axisKey = `${year}-${month}`
            let endDateValue = axisKey + '-' + zeroFn(endDay) // 每个月的同一天为合计截止日期
            let isEndDay = method.isEndDay(dateValue, endDateValue) // 获取指定每个月的最后一天
            // 月份总计total
            if (!dataObj[axisKey]) {
                dataObj[axisKey] = true
                curTotal = dataValue
            } else {
                if (isEndDay || !(new Date(dateValue) > new Date(endDateValue))) {
                    curTotal += dataValue
                }
            }
            // 结束时间(每个月最后一天不一样,所以要做判断,如果不一致需要更新endDateValue)
            if (isEndDay) {
                endDateValue = dateValue
                // xAsix的data数据
                let flag = newAxisData.indexOf(endDateValue) !== -1
                if (flag) return
                newAxisData.push(endDateValue)

                // tooltip的数据
                item[method.fc.DATEINDEX].value = endDateValue
                item[method.fc.DATEINDEX].realValue = new Date(endDateValue).getTime()
                item[method.fc.VALUEINDEX].value = curTotal + ''
                item[method.fc.VALUEINDEX].realValue = curTotal
                let newIndex = newAxisData.length - 1
                let newTooltipInfosKey = newIndex + '_1'
                if (!newTooltipInfos[newTooltipInfosKey]) {
                    newTooltipInfos[newTooltipInfosKey] = []
                }
                newTooltipInfos[newTooltipInfosKey][method.fc.DATEINDEX] = item[method.fc.DATEINDEX]
                newTooltipInfos[newTooltipInfosKey][method.fc.VALUEINDEX] = item[method.fc.VALUEINDEX]
                // series的data数据
                newSeriesData.push({
                    value: [newIndex, curTotal],
                    displayValue: [item[method.fc.DATEINDEX].value, item[method.fc.VALUEINDEX].value],
                    rowIndex: [newIndex, newIndex],
                    colIndex: 1
                })
            }
        })

        options.xAxis[0].data = newAxisData
        options.series[0].data = newSeriesData
        options.tooltipInfos = newTooltipInfos
        // console.warn('newAxisData', newAxisData)
        // console.warn('newSeriesData', newSeriesData)
        // console.warn('tooltipInfos', tooltipInfos)
        return options
    },
    // 计算合计:日,这里不需要计算,写个空方法直接返回options
    calcTotalDay(options: any, bdate: string, edate: string) {
        return options
    },
    isEndDay(currentDate: string, endDate: string) {
        let monthLastDay = method.getMonthEndDay(currentDate)
        if (currentDate === endDate) return true
        let cdate = new Date(currentDate)
        let edate = new Date(endDate)
        if (cdate > edate) {
            return false
        }
        // 当前判断的日期
        let cDay = cdate.getDate()
        if (cDay === monthLastDay) return true
        return false
    },
    // 根据标题获取组件
    getPortlet<T>(page: IPage, title: string): T {
        let portlets: T[] = page.getPortletsByTitle(title)
        let portlet: T = portlets[0]
        return portlet
    },
    // 获取自定义当前时间筛选器的值
    getBaseDateValue(page: IPage) {
        let filter: IFilterPortlet = method.getPortlet(page, method.dc.endDatePortletTitle)
        if (!filter) return ''
        let value = filter.getValue()
        if (!value || !value.length) return ''
        return value[0]
    },
    /**
     * 选择“年”:
     * 数据区间选取 当前日期 至 前一年,且根据当前所选的日,
     * 每年选取年的第一天 至 当日(如2020-02-29,2019-02-28),
     * 展示所选日期对应每年的当月日,根据年汇总。
     */
    calcYear(baseDate: Date) {
        let year = baseDate.getFullYear();
        year -= 1
        let resultDate = new Date(`${year}-01-01`)
        return resultDate
    },
    /**
     * 选择“月”:
     * 数据区间选取 当前月份 至 往前12个月,
     * 且根据所选日期的日(如30号),
     * 每个月选取 月的第1天 至 当日(如2月则是1-28号),
     * 展示为所选日期对应每月的当日(如选2020-03-30,展示2020-02-29,2020-01-30),
     * 根据月汇总。
     */
    calcMonth(baseDate: Date) {
        let month = baseDate.getMonth();
        let num = 12
        month -= num
        method.setMonth(baseDate, month, num)
        baseDate.setDate(1);
        return baseDate
    },
    /**
     * 选择“日”:
     * 数据区间选取 当前日期 至 往前40天。
     */
    calcDate(baseDate: Date) {
        let date = baseDate.getDate();
        date -= 40;
        baseDate.setDate(date)
        return baseDate
    },
    setMonth(currentDate: Date, month: number, num: number) {
        let day = currentDate.getDate();
        currentDate.setMonth(month);
        if (num && day !== currentDate.getDate()) {
            let currentMonthLastDay = new Date(currentDate.getTime() - 1000 * 60 * 60 * 24 * currentDate.getDate());
            currentDate.setYear(currentMonthLastDay.getFullYear());
            currentDate.setMonth(currentMonthLastDay.getMonth());
            currentDate.setDate(currentMonthLastDay.getDate());
        }
    },
    // 获取每个月最后一天
    getMonthEndDay(currentDate: string) {
        let clastdate = new Date(currentDate)
        let year = clastdate.getFullYear()
        let month = clastdate.getMonth() + 1
        if (month > 12) {
            month -= 1
            year += 1
        }
        let lateDate = new Date(year, month, 1);
        let lastDay = (new Date(lateDate.getTime() - 1000 * 60 * 60 * 24)).getDate();
        return lastDay
    },
    // 单选设置值
    setValues(page: IPage, currentDate: Date, selectorLabel: any) {
        let selectors = method.getSelectors()
        let value = SmartbiXMacro.utils.formatDate(currentDate, 'yyyy-MM-dd') + ' 00:00:00'
        selectors.forEach(el => {
            let portlet: IFilterPortlet = method.getPortlet(page, el.label)
            if (!portlet) return
            if (el.label === selectorLabel) {
                setTimeout(() => { portlet.setValue([value]) }, 0)
            } else {
                setTimeout(() => { portlet.setValue([]) }, 0)
            }
        })
    }
}

  • ②:修改选择器样式。在该名为“选择器组件上新建宏,事件为“onAfterRender(组件渲染后)”,把下面宏代码复制到代码区域,接着保存该宏代码。
function main(page: IPage, portlet: IFilterPortlet) {
    // 修改选择器样式
    setTimeout(() => {
        doRenderStyle(portlet)
    }, 800)
}

function doRenderStyle(portlet: IFilterPortlet) {
    // 水平布局
    portlet.appendCss('.filter-list__table>div.ps', `{display: inline-flex; width: 100%;}`)
    // 隐藏参数默认显示的“全部”选项
    portlet.appendCss('.filter-list__table > tr:nth-child(1)', `{display: none;}`)
    // // 去除选项之间的边距
    portlet.appendCss('.filter-list__td', `{padding: 0px;}`)
    // // 设置单个选项样式
    portlet.appendCss(`.filter-wrapper .el-radio-group .el-radio`, `{
        background-color: #eeeeee;
        width: 30px;
        height: 30px;
        justify-content: center;
        padding: 0px;
    }`)
    portlet.appendCss('.filter-wrapper .el-radio-group .el-radio.is-checked', `{background-color: rgb(188, 139, 42);}`)
    portlet.appendCss('.filter-wrapper .el-radio-group .el-radio.is-checked .el-radio__label', `{color: #fff;}`)
    portlet.appendCss('.el-radio .el-radio__input', `{display: none; }`)
    portlet.appendCss('.el-radio__label', `{padding-left: 0px;}`)
}
  • 点击 保存 保存该宏代码重新访问报表,可看到效果已实现

3. 下载资源

通过筛选不同范围的日期数据来展示不同日期区间的数据.xml

  • 无标签