diff --git a/CHANGELOG.md b/CHANGELOG.md index 7709ddb37dd7f06eb113d1fd48be0bf870de3e26..56c99738c5048155d9379d241f348e474216510a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ ## [Unreleased] +### Added + +- 新增BI报表 + ### Changed - 更新文本框ai按钮样式 diff --git a/src/control/report-panel/index.ts b/src/control/report-panel/index.ts index a334774669574d0b127f145ac0d3b60ca6ff4157..e6b2a8445f38e8f9f7c8828107a4caf2acb1a9ac 100644 --- a/src/control/report-panel/index.ts +++ b/src/control/report-panel/index.ts @@ -4,6 +4,7 @@ import { App } from 'vue'; import { ReportPanelControl } from './report-panel'; import { ReportPanelProvider } from './report-panel.provider'; import { + IBizBIReport, IBizBIReportPanel, IBizUser2ReportPanel, IBizUserReportPanel, @@ -13,6 +14,7 @@ import { export const IBizReportPanelControl: any = withInstall( ReportPanelControl, function (v: App) { + v.use(IBizBIReport); v.use(IBizUserReportPanel); v.use(IBizUser2ReportPanel); v.use(IBizBIReportPanel); diff --git a/src/control/report-panel/report-detail/bi-report/bi-report.scss b/src/control/report-panel/report-detail/bi-report/bi-report.scss new file mode 100644 index 0000000000000000000000000000000000000000..a2d58f4d1ff6997e71207e80ab93caee29620531 --- /dev/null +++ b/src/control/report-panel/report-detail/bi-report/bi-report.scss @@ -0,0 +1,116 @@ +@include b(bi-report) { + width: 100%; + height: 100%; + .#{bem(control-grid)}.is-single-select { + .el-table__body-wrapper .el-table__body tbody > tr td { + padding-left: getCssVar(spacing, tight) !important; + } + } + + // 表格样式 + .#{bem(control-grid)} { + .el-table { + .el-table__header { + .el-table__cell { + font-style: var(--ibiz-control-grid-header-font-style); + text-align: var(--ibiz-control-grid-header-align); + + .cell { + #{getCssVarName(grid-column, justify-content)}: getCssVar( + control-grid, + header, + align + ); + } + } + } + + .ibiz-grid-field-column__text { + overflow: visible; + font-size: var(--ibiz-control-grid-content-font-size); + font-style: var(--ibiz-control-grid-content-font-style); + font-weight: var(--ibiz-control-grid-content-font-weight); + color: var(--ibiz-control-grid-content-text-color); + } + + .ibiz-grid-field-column__script { + line-height: 23px; + } + } + + // 只读、边框样式 + .el-table { + --el-table-border-color: var(--ibiz-color-border); + --ibiz-control-grid-row-hover-color: transparent; + --el-table-current-row-bg-color: transparent; + --ibiz-control-grid-row-bg-color-2: var(--ibiz-control-grid-row-bg-color); + + tr td { + pointer-events: none; + } + + .el-table__header-wrapper { + .el-table__header thead > tr th:nth-child(1) { + padding-left: 0; + } + } + + // 表格body样式 + .el-table__body-wrapper { + .el-table__body tbody > tr td:nth-child(1) { + padding-left: 0; + } + } + } + + .el-table--border { + .el-table__cell { + border-right: var(--el-table-border); + } + } + + // 去除底部border + .el-table td.el-table__cell, + .el-table th.el-table__cell.is-leaf { + border-bottom: var(--el-table-border); + } + + &.el-table--scroll-header { + .el-table__inner-wrapper { + overflow: auto; + } + + .el-table__header-wrapper { + position: sticky; + top: 0; + } + + .el-table__body-wrapper { + flex: none; + height: auto; + } + + .el-scrollbar__bar { + display: none; + } + } + + .el-table--top-agg { + .el-table__header-wrapper { + order: 0; + } + + .el-table__footer-wrapper { + order: 1; + + .el-table__footer { + height: var(--ibiz-control-grid-content-row-height); + } + } + + .el-table__body-wrapper { + order: 2; + } + } + } +} diff --git a/src/control/report-panel/report-detail/bi-report/bi-report.tsx b/src/control/report-panel/report-detail/bi-report/bi-report.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bcba6c01e7d10daa8c4dd9ebc84a26ec23ef8dff --- /dev/null +++ b/src/control/report-panel/report-detail/bi-report/bi-report.tsx @@ -0,0 +1,54 @@ +import { defineComponent, PropType } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { + ReportPanelController, + BIReportPanelGenerator, +} from '@ibiz-template/runtime'; +import { IBizBINumberReport } from './commons'; +import './bi-report.scss'; + +export const BIReport = defineComponent({ + name: 'IBizBIReport', + props: { + controller: { + type: Object as PropType, + required: true, + }, + }, + setup(props) { + const c = props.controller; + const ns = useNamespace('bi-report'); + + const generator = c.generator as BIReportPanelGenerator; + + const reportType = generator.reportType; + + return { + c, + ns, + reportType, + }; + }, + render() { + if (!this.c.state.biReport) return; + return ( +
+ {this.reportType === 'NUMBER' ? ( + + ) : ( + + )} +
+ ); + }, +}); diff --git a/src/control/report-panel/report-detail/bi-report/commons/bi-number-report/bi-number-report.scss b/src/control/report-panel/report-detail/bi-report/commons/bi-number-report/bi-number-report.scss new file mode 100644 index 0000000000000000000000000000000000000000..c340b9494f29abc7ed719e55b38a8e4ef801da37 --- /dev/null +++ b/src/control/report-panel/report-detail/bi-report/commons/bi-number-report/bi-number-report.scss @@ -0,0 +1,106 @@ +@include b('bi-number-report') { + width: 100%; + height: 100%; + + @include e('content') { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + + @include m('number-text') { + cursor: pointer; + } + + @include m('yoy') { + display: flex; + } + + @include m('qoq') { + display: flex; + } + + @include m('compare-number') { + display: flex; + align-items: baseline; + width: 150px; + } + + @include m('yoy-value') { + display: none; + margin-left: 10px; + + @include when('show') { + display: block; + } + } + + @include m('yoy-lastyearMonth') { + display: none; + flex: 1; + margin-left: 10px; + + @include when('show') { + display: block; + } + } + + @include m('qoq-lastMonth') { + display: none; + flex: 1; + margin-left: 10px; + + @include when('show') { + display: block; + } + } + + @include m('qoq-value') { + display: none; + margin-left: 10px; + + @include when('show') { + display: block; + } + } + + @include m('yoy-yoyTotal') { + display: none; + @include when('show') { + display: block; + } + } + @include m('qoq-qoqTotal') { + display: none; + @include when('show') { + display: block; + } + } + } + + @include e('down') { + display: flex; + gap: 10px; + align-items: center; + justify-content: center; + margin-left: 10px; + + svg { + color: getCssVar(color, danger, active); + } + } + + @include e('up') { + display: flex; + gap: 10px; + align-items: center; + justify-content: center; + margin-left: 10px; + + svg { + color: getCssVar(color, success, light, active); + } + } +} diff --git a/src/control/report-panel/report-detail/bi-report/commons/bi-number-report/bi-number-report.tsx b/src/control/report-panel/report-detail/bi-report/commons/bi-number-report/bi-number-report.tsx new file mode 100644 index 0000000000000000000000000000000000000000..25698f07fa7d3d84d5b2538f56c1fa5bebd509b6 --- /dev/null +++ b/src/control/report-panel/report-detail/bi-report/commons/bi-number-report/bi-number-report.tsx @@ -0,0 +1,263 @@ +/* eslint-disable camelcase */ +import { useNamespace } from '@ibiz-template/vue3-util'; +import { IAppBIReportMeasure } from '@ibiz/model-core'; +import { computed, defineComponent, PropType, ref } from 'vue'; +import './bi-number-report.scss'; + +export const IBizBINumberReport = defineComponent({ + name: 'IBizBINumberReport', + props: { + model: { type: Object as PropType, required: true }, + data: { type: Array, required: true }, + }, + setup(props) { + const ns = useNamespace('bi-number-report'); + + // 统一管理所有响应式数据 + const uiState = ref({ + visible: false, // 查看明细是否显示 + yoy: 0, // 同比差异值,年与年比较 + qoq: 0, // 环比差异值,月与月比较 + currentTotal: 0, // 当前总数 + yoyTotal: 0, // 同比总数 + qoqTotal: 0, // 环比总数 + }); + + // 处理比较数据 + const handleCompareData = () => { + const codeName = ( + props.model.measure as IAppBIReportMeasure + )?.measureTag?.toLowerCase(); + if (codeName) { + props.data.forEach((item: IData) => { + if (item && item.srfperiodtype === 'PoP1') { + uiState.value.qoqTotal = Number.isNaN(Number(item[codeName])) + ? 0 + : Number(item[codeName]); + } else if (item && item.srfperiodtype === 'YoY1') { + uiState.value.yoyTotal = Number.isNaN(Number(item[codeName])) + ? 0 + : Number(item[codeName]); + } else if (item && item[codeName]) { + uiState.value.currentTotal = Number.isNaN(Number(item[codeName])) + ? 0 + : Number(item[codeName]); + } + }); + uiState.value.qoq = uiState.value.currentTotal - uiState.value.qoqTotal; + uiState.value.yoy = uiState.value.currentTotal - uiState.value.yoyTotal; + } + }; + handleCompareData(); + + // 数字的样式 + const style = computed(() => { + const tempStyle: IData = {}; + const { number_fontstyle, number_fontsize, number_fontcolor } = + props.model; + if (number_fontstyle) { + if (number_fontstyle.includes('bold')) { + tempStyle.fontWeight = 'bold'; + } else { + tempStyle.fontStyle = number_fontstyle; + } + } + if (number_fontsize) tempStyle.fontSize = `${number_fontsize}px`; + if (number_fontcolor) tempStyle.color = number_fontcolor; + return tempStyle; + }); + + // 处理值格式化 + const handleFormat = (value: number) => { + const format = (props.model.measure as IAppBIReportMeasure)?.jsonFormat; + if (format) return ibiz.util.text.format(String(value), format); + return value; + }; + + // 绘制上升图标 + const renderUpIcon = () => { + return ( + + + + + + ); + }; + + // 绘制下降图标 + const renderDownIcon = () => { + return ( + + + + + + ); + }; + + // 绘制升降图标与升降数值 + const renderUpDownResult = (baseValue: number, targetValue: number) => { + if (baseValue === 0) { + if (targetValue === 0) { + return -; + } + return ( +
+ {renderUpIcon()} + 100% +
+ ); + } + const temp = targetValue - baseValue; + const percentage = ((Math.abs(temp) / baseValue) * 100).toFixed(0); + if (temp < 0) { + return ( +
+ {renderDownIcon()} + {percentage}% +
+ ); + } + if (temp === 0) { + return -; + } + return ( +
+ {renderUpIcon()} + {percentage}% +
+ ); + }; + + return { + ns, + style, + uiState, + handleFormat, + renderUpDownResult, + }; + }, + render() { + return ( +
+
+
+ + {this.handleFormat(this.uiState.currentTotal)} + +
+
+ {this.model.number_yoy_show === 1 && ( +
+
+ 同比 + + {this.uiState.yoyTotal} + + + ({this.uiState.yoy > 0 ? '+' : ''} + {this.uiState.yoy}) + +
+
+ {this.renderUpDownResult( + this.uiState.yoyTotal, + this.uiState.currentTotal, + )} +
+
+ )} + + {this.model.number_qoq_show === 1 && ( +
+
+ 环比 + + {this.uiState.qoqTotal} + + + ({this.uiState.qoq > 0 ? '+' : ''} + {this.uiState.qoq}) + +
+
+ {this.renderUpDownResult( + this.uiState.qoqTotal, + this.uiState.currentTotal, + )} +
+
+ )} +
+
+
+ ); + }, +}); diff --git a/src/control/report-panel/report-detail/bi-report/commons/index.ts b/src/control/report-panel/report-detail/bi-report/commons/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3aebbc7892e030ff1596e4b83bbdea09f4af68e --- /dev/null +++ b/src/control/report-panel/report-detail/bi-report/commons/index.ts @@ -0,0 +1 @@ +export { IBizBINumberReport } from './bi-number-report/bi-number-report'; diff --git a/src/control/report-panel/report-detail/bi-report/index.ts b/src/control/report-panel/report-detail/bi-report/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..686d2ec955b3f92e129d0fc6d1c0f6f9ac676b10 --- /dev/null +++ b/src/control/report-panel/report-detail/bi-report/index.ts @@ -0,0 +1,8 @@ +import { withInstall } from '@ibiz-template/vue3-util'; +import { BIReport } from './bi-report'; + +export const IBizBIReport = withInstall(BIReport, v => { + v.component(BIReport.name!, BIReport); +}); + +export default IBizBIReport; diff --git a/src/control/report-panel/report-detail/index.ts b/src/control/report-panel/report-detail/index.ts index d3e5231270b867277b00a5463da0ca1829718966..932144f4e7f3ece73efdb6ef1faddbfa260e9752 100644 --- a/src/control/report-panel/report-detail/index.ts +++ b/src/control/report-panel/report-detail/index.ts @@ -1,3 +1,4 @@ export * from './user-report-panel/index'; export * from './user2-report-panel/index'; export * from './bi-report-panel/index'; +export * from './bi-report/index'; diff --git a/src/control/report-panel/report-panel.tsx b/src/control/report-panel/report-panel.tsx index dfbe2b63ea709f61fa40ec948431abfc54a1f6ce..b10e6bcb0121fdf7d61172ec3a370003b00e50ab 100644 --- a/src/control/report-panel/report-panel.tsx +++ b/src/control/report-panel/report-panel.tsx @@ -36,7 +36,7 @@ export const ReportPanelControl = defineComponent({ noLoadDefault: { type: Boolean, default: false }, }, setup() { - const c = useControlController( + const c: ReportPanelController = useControlController( (...args) => new ReportPanelController(...args), ); const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); @@ -44,17 +44,17 @@ export const ReportPanelControl = defineComponent({ // 绘制内容 const renderContent = (): VNode | false => { // 未加载不显示无数据 - const { reportType } = c.state; - switch (reportType) { + switch (c.model.appDEReport?.reportType) { case 'USER': return ; case 'USER2': return ; + case 'SYSBIREPORT': + return ; case 'DESYSBIREPORTS': case 'SYSBICUBE': case 'DESYSBICUBES': case 'ALLSYSBICUBES': - case 'SYSBIREPORT': case 'SYSBICUBEREPORTS': case 'ALLSYSBIREPORTS': return ; @@ -70,9 +70,7 @@ export const ReportPanelControl = defineComponent({ }; }, render() { - if (!this.c.state.isCreated) { - return; - } + if (!this.c.state.isCreated) return; return ( {this.renderContent()}