Complete main work, next to do the last-test

This commit is contained in:
沐见南 2020-02-07 00:04:15 +08:00
parent 7fa42e0df2
commit e7528900d1
19 changed files with 4098 additions and 330 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 659 B

View File

@ -6,36 +6,139 @@
"kind": "Grouping", "kind": "Grouping",
"displayNameKey": "Role_Dates_DisplayName", "displayNameKey": "Role_Dates_DisplayName",
"description": "Column date", "description": "Column date",
"descriptionKey": "Role_Dates_Discription" "descriptionKey": "Role_Dates_Description"
}, },
{ {
"displayNameKey": "Role_StartDate_DisplayName", "displayNameKey": "Role_StartDate_DisplayName",
"displayName": "DefaultStartDate", "displayName": "DefaultStartDate",
"name": "StartDate", "name": "StartDate",
"kind": "Measure", "kind": "Measure",
"descriptionKey": "Role_StartDate_Discription", "descriptionKey": "Role_StartDate_Description",
"description": "You can use Messure to assign Default date to the date-picker" "description": "You can use measure to assign Default date to the date-picker"
}, },
{ {
"displayNameKey": "Role_DefaultEndDate_DisplayName", "displayNameKey": "Role_DefaultEndDate_DisplayName",
"displayName": "DefaultEndDate", "displayName": "DefaultEndDate",
"name": "EndDate", "name": "EndDate",
"kind": "Measure", "kind": "Measure",
"descriptionKey": "Role_DefaultEndDate_Discription", "descriptionKey": "Role_DefaultEndDate_Description",
"description": "You can use Messure to assign Default date to the date-picker" "description": "You can use measure to assign Default date to the date-picker"
} }
], ],
"objects": { "objects": {
"period": { "datePickers": {
"displayNameKey": "Obj_DatePickers_DisplayName",
"displayName": "Date pickers",
"descriptionKey": "Obj_DatePickers_Description",
"description": "Settings for period-pickers",
"properties": {
"orientationType":{
"displayNameKey": "DatePickers_Orientation_DisplayName",
"displayName": "Orientation",
"descriptionKey": "DatePickers_Orientation_Description",
"description": "Set orientation for datePickers",
"type": {
"enumeration": [
{
"displayNameKey": "Horizontal",
"displayName": "Horizontal",
"value": "0"
},
{
"displayNameKey": "Vertical",
"displayName": "Vertical",
"value": "1"
}
]
}
},
"fontSize":{
"displayNameKey": "DatePickers_FontSize_DisplayName",
"displayName": "Font size",
"descriptionKey": "DatePickers_FontSize_Description",
"description": "Set font-size for datePickers",
"type": {
"numeric": true
}
},
"fontColor":{
"displayNameKey": "DatePickers_FontColor_DisplayName",
"displayName": "Font color",
"descriptionKey": "DatePickers_FontColor_Description",
"description": "Set font-color for datePickers",
"type": {
"fill": {"solid": {"color":true}}
}
},
"backgroundColor":{
"displayNameKey": "DatePickers_BackgroundColor_DisplayName",
"displayName": "Background color",
"descriptionKey": "DatePickers_BackgroundColor_Description",
"description": "Set background-color for datePickers",
"type": {
"fill": {
"solid": {
"color":true
}
}
}
},
"backgroundTransparency":{
"displayNameKey": "DatePickers_BackgroundTransparency_DisplayName",
"displayName":"Background transparency",
"descriptionKey": "DatePickers_BackgroundTransparency_Description",
"description": "Set background-transparency for datePickers",
"type": {
"numeric": true
}
},
"borderWidth":{
"displayNameKey": "DatePickers_BorderWidth_DisplayName",
"displayName":"Border width",
"descriptionKey": "DatePickers_BorderWidth_Description",
"description": "Set border-width for datePickers",
"type": {
"numeric": true
}
},
"borderColor":{
"displayNameKey": "DatePickers_BorderColor_DisplayName",
"displayName": "Border color",
"descriptionKey": "DatePickers_BorderColor_Description",
"description": "Set border-color for datePickers",
"type": {
"fill": {
"solid": {
"color":true
}
}
}
},
"outlineColor":{
"displayNameKey": "DatePickers_OutlineColor_DisplayName",
"displayName": "Outline color",
"descriptionKey": "DatePickers_OutlineColor_Description",
"description": "Set outline-color for datePickers",
"type": {
"fill": {
"solid": {
"color":true
}
}
}
}
}
},
"period":{
"displayNameKey": "Obj_Period_DisplayName", "displayNameKey": "Obj_Period_DisplayName",
"displayName": "Period", "displayName": "Period",
"descriptionKey": "Obj_Period_Discription", "descriptionKey": "Obj_Period_Description",
"description": "Settings for period-picker", "description": "Settings for period",
"properties": { "properties": {
"defaultPeriodType": { "defaultPeriodType": {
"displayNameKey": "Period_DefaultPeriodType_DisplayName", "displayNameKey": "Period_DefaultPeriodType_DisplayName",
"displayName": "Default period", "displayName": "Default period",
"descriptionKey":"Period_DefaultPeriodType_Discription", "descriptionKey":"Period_DefaultPeriodType_Description",
"description": "Choose how to assign default period to the period-picker", "description": "Choose how to assign default period to the period-picker",
"type": { "type": {
"enumeration": [ "enumeration": [
@ -62,7 +165,7 @@
{ {
"value": "4", "value": "4",
"displayNameKey": "Custom", "displayNameKey": "Custom",
"displayName": "Custom(use messures)" "displayName": "Custom(use measures)"
} }
] ]
}, },
@ -74,8 +177,8 @@
}, },
"displayNameKey": "Period_RelativeToday_DisplayName", "displayNameKey": "Period_RelativeToday_DisplayName",
"displayName": "Contain today", "displayName": "Contain today",
"descriptionKey": "Period_RelativeToday_Discription", "descriptionKey": "Period_RelativeToday_Description",
"description": "Effictive when and only when Custom(use messures) not selected" "description": "Effictive when and only when Custom(use measures) not selected"
}, },
"firstDayOfWeek": { "firstDayOfWeek": {
"descriptionKey": "Period_FirstDayOfWeek_DisplayName", "descriptionKey": "Period_FirstDayOfWeek_DisplayName",

3736
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,15 @@
"pbiviz": "pbiviz", "pbiviz": "pbiviz",
"start": "pbiviz start", "start": "pbiviz start",
"package": "pbiviz package", "package": "pbiviz package",
"lint": "tslint -c tslint.json -p tsconfig.json" "lint": "tslint -c tslint.json -p tsconfig.json",
"cert": "pbiviz --install-cert"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "7.6.0", "@babel/runtime": "^7.6.0",
"@babel/runtime-corejs2": "7.6.0", "@babel/runtime-corejs2": "7.6.0",
"@babel/runtime-corejs3": "^7.8.4",
"@types/d3": "5.7.2", "@types/d3": "5.7.2",
"core-js": "3.2.1", "core-js": "^3.2.1",
"d3": "5.12.0", "d3": "5.12.0",
"moment": "^2.24.0", "moment": "^2.24.0",
"powerbi-models": "^1.3.1", "powerbi-models": "^1.3.1",
@ -19,19 +21,24 @@
"powerbi-visuals-utils-interactivityutils": "^5.6.0" "powerbi-visuals-utils-interactivityutils": "^5.6.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.8.4",
"@babel/polyfill": "^7.2.5", "@babel/polyfill": "^7.2.5",
"@types/d3": "5.7.2", "@babel/preset-env": "^7.8.4",
"@types/jasmine": "2.5.37", "@types/jasmine": "2.5.37",
"@types/jasmine-jquery": "1.5.28", "@types/jasmine-jquery": "1.5.28",
"@types/jquery": "2.0.41", "@types/jquery": "2.0.41",
"@types/karma": "3.0.0", "@types/karma": "3.0.0",
"@types/lodash-es": "4.17.1", "@types/lodash-es": "4.17.1",
"babel-loader": "^8.0.6",
"base64-inline-loader": "^1.1.1",
"coveralls": "3.0.2", "coveralls": "3.0.2",
"css-loader": "^3.4.2",
"extra-watch-webpack-plugin": "^1.0.3",
"istanbul-instrumenter-loader": "^3.0.1", "istanbul-instrumenter-loader": "^3.0.1",
"jasmine": "2.5.2", "jasmine": "2.5.2",
"jasmine-core": "2.5.2", "jasmine-core": "2.5.2",
"jasmine-jquery": "2.1.1", "jasmine-jquery": "2.1.1",
"jquery": "^3.4.1", "json-loader": "^0.5.7",
"karma": "^4.4.1", "karma": "^4.4.1",
"karma-chrome-launcher": "2.2.0", "karma-chrome-launcher": "2.2.0",
"karma-coverage": "1.1.2", "karma-coverage": "1.1.2",
@ -42,13 +49,19 @@
"karma-typescript": "^4.1.1", "karma-typescript": "^4.1.1",
"karma-typescript-preprocessor": "0.4.0", "karma-typescript-preprocessor": "0.4.0",
"karma-webpack": "3.0.5", "karma-webpack": "3.0.5",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"puppeteer": "1.17.0", "puppeteer": "1.17.0",
"style-loader": "0.23.1", "style-loader": "0.23.1",
"ts-loader": "6.1.0", "ts-loader": "^6.2.1",
"ts-node": "7.0.1", "ts-node": "7.0.1",
"tslint": "^5.18.0", "tslint": "^5.20.1",
"tslint-microsoft-contrib": "^6.2.0", "tslint-microsoft-contrib": "^6.2.0",
"typescript": "3.6.3", "typescript": "3.6.3",
"webpack": "4.26.0" "webpack": "^4.41.5",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.3"
} }
} }

View File

@ -1,13 +1,13 @@
{ {
"visual": { "visual": {
"name": "DatePeriodSlice", "name": "Period Slice - Syinpo",
"displayName": "DatePeriodSlice", "displayName": "Period Slicer - Syinpo",
"guid": "DatePeriodSlice9771124744984CE8959D0304EEAB35E8", "guid": "SyinpoPeriodSlice9771124744984CE8959D0304EEAB35E8",
"visualClassName": "Visual", "visualClassName": "Visual",
"version": "1.0.0", "version": "1.0.0",
"description": "可设置默认值的日期段切片器", "description": "A period slicer, which can be initialized by messures.",
"supportUrl": "https://github.com/mujiannan/PbiViz", "supportUrl": "https://github.com/mujiannan/PeriodSlicer-Syinpo",
"gitHubUrl": "https://github.com/mujiannan/PbiViz" "gitHubUrl": "https://github.com/mujiannan/PeriodSlicer-Syinpo"
}, },
"apiVersion": "2.6.0", "apiVersion": "2.6.0",
"author": { "author": {

View File

@ -15,5 +15,7 @@ export class Period implements IPeriod {
export enum DefaultPeriodType { export enum DefaultPeriodType {
LastMonth, LastEntireMonth, LastWeek, LastEntireWeek, Custom LastMonth, LastEntireMonth, LastWeek, LastEntireWeek, Custom
} }
export enum OrientationType{
Horizontal,Vertical
}

View File

@ -0,0 +1,12 @@
"use strict";
import { OrientationType } from "./datePeriod";
export class DatePickerSetting {
public orientationType: OrientationType = OrientationType.Horizontal;
public fontSize: number = 12;
public fontColor: string = "black";
public backgroundColor: string = "white";
public backgroundTransparency: number = 0;
public borderWidth: number = 1;
public borderColor: string = "gray";
public outlineColor: string = "blue";
}

View File

@ -4,7 +4,6 @@ import {IPeriod,Period} from "./datePeriod";
import * as moment from "moment"; import * as moment from "moment";
type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>; type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>;
export interface IPeriodSelectorManager{ export interface IPeriodSelectorManager{
dateSelector_Start:Selection<HTMLInputElement>; dateSelector_Start:Selection<HTMLInputElement>;
dateSelector_End:Selection<HTMLInputElement>; dateSelector_End:Selection<HTMLInputElement>;
period:IPeriod; period:IPeriod;
@ -24,11 +23,10 @@ export class PeriodSelectorManager implements IPeriodSelectorManager{
return this._dateSelector_End; return this._dateSelector_End;
} }
get period():IPeriod{ get period():IPeriod{
console.debug(this._dateSelector_Start.property("value"));
let start:Date=this._dateSelector_Start.property("value")?moment(this._dateSelector_Start.property("value")).startOf("day").toDate():null; let start:Date=this._dateSelector_Start.property("value")?moment(this._dateSelector_Start.property("value")).startOf("day").toDate():null;
let end:Date=this._dateSelector_End.property("value")?moment(this._dateSelector_End.property("value")).endOf("day").toDate():null; let end:Date=this._dateSelector_End.property("value")?moment(this._dateSelector_End.property("value")).endOf("day").toDate():null;
if(start&&end&&start>end){ if(start&&end&&start>end){
console.debug("dateStart>dateEnd, set dateEnd=dateStart");
this._dateSelector_End.property("value",this._dateSelector_Start.property("value")); this._dateSelector_End.property("value",this._dateSelector_Start.property("value"));
end=start; end=start;
} }
@ -39,25 +37,24 @@ export class PeriodSelectorManager implements IPeriodSelectorManager{
return this._defaultPeriod; return this._defaultPeriod;
} }
set defaultPeriod(newDefaultPeriod:IPeriod){ set defaultPeriod(newDefaultPeriod:IPeriod){
console.debug("PeriodSelectorManager set defaultPeriod start"); let defaultPeriodChanged:boolean=false;
console.debug((!this._defaultPeriod.dateStart)&&newDefaultPeriod.dateStart);
console.debug(this._defaultPeriod.dateStart&&newDefaultPeriod.dateStart&&(newDefaultPeriod.dateStart.getTime()-this._defaultPeriod.dateStart.getTime()!=0));
console.debug("test");
if(((!this._defaultPeriod.dateStart)&&newDefaultPeriod.dateStart)|| if(((!this._defaultPeriod.dateStart)&&newDefaultPeriod.dateStart)||
(this._defaultPeriod.dateStart&&newDefaultPeriod.dateStart&&(newDefaultPeriod.dateStart.getTime()-this._defaultPeriod.dateStart.getTime()!=0))){ (this._defaultPeriod.dateStart&&newDefaultPeriod.dateStart&&(newDefaultPeriod.dateStart.getTime()-this._defaultPeriod.dateStart.getTime()!=0))){
defaultPeriodChanged=true;
let newStartStr=moment(newDefaultPeriod.dateStart).format("YYYY-MM-DD"); let newStartStr=moment(newDefaultPeriod.dateStart).format("YYYY-MM-DD");
console.debug("new start",newStartStr);
this._dateSelector_Start.property("value",newStartStr); this._dateSelector_Start.property("value",newStartStr);
console.debug("Set dateStart value="+newStartStr);
} }
if(((!this._defaultPeriod.dateEnd)&&newDefaultPeriod.dateEnd)|| if(((!this._defaultPeriod.dateEnd)&&newDefaultPeriod.dateEnd)||
(this._defaultPeriod.dateEnd&&newDefaultPeriod.dateEnd&&(newDefaultPeriod.dateEnd.getTime()-this._defaultPeriod.dateEnd.getTime()!=0))){ (this._defaultPeriod.dateEnd&&newDefaultPeriod.dateEnd&&(newDefaultPeriod.dateEnd.getTime()-this._defaultPeriod.dateEnd.getTime()!=0))){
defaultPeriodChanged=true;
let newEndStr=moment(newDefaultPeriod.dateEnd).format("YYYY-MM-DD"); let newEndStr=moment(newDefaultPeriod.dateEnd).format("YYYY-MM-DD");
this._dateSelector_End.property("value",newEndStr); this._dateSelector_End.property("value",newEndStr);
console.debug("Set dateEnd value="+newEndStr);
} }
this._defaultPeriod=newDefaultPeriod; this._defaultPeriod=newDefaultPeriod;
this._dateSelector_Start.dispatch("change"); if(defaultPeriodChanged){
console.debug("PeriodSelectorManager set defaultPeriod end"); this._dateSelector_Start.dispatch("change");
}
} }
} }

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
import * as DatePeriod from "./datePeriod"; import * as DatePeriod from "./datePeriod";
export class PeriodSettings { export class PeriodSetting {
public defaultPeriodType:DatePeriod.DefaultPeriodType=DatePeriod.DefaultPeriodType.LastEntireMonth; public defaultPeriodType:DatePeriod.DefaultPeriodType=DatePeriod.DefaultPeriodType.Custom;
public relativeToday:boolean=true; public relativeToday:boolean=true;
public firstDayOfWeek:number=1; public firstDayOfWeek:number=1.00;
} }

View File

@ -1,54 +1,41 @@
"use strict"; "use strict";
import {DefaultPeriodType,IPeriod,Period} from "./datePeriod"; import { DefaultPeriodType, IPeriod, Period } from "./datePeriod";
import * as moment from "moment"; import * as moment from "moment";
class RelativePeriod { export class RelativePeriod {
periodType: DefaultPeriodType; periodType: DefaultPeriodType;
private _period: IPeriod; private _period: IPeriod;
get period(): IPeriod { get period(): IPeriod {
return this._period; return this._period;
} }
public constructor(periodType: DefaultPeriodType, period: IPeriod) { public constructor(periodType: DefaultPeriodType, period: IPeriod) {
console.debug("RelativePeriod constructor start,preiodType:",periodType);
this.periodType = periodType; this.periodType = periodType;
this._period = period; this._period = period;
console.debug(period.dateStart+"~"+period.dateEnd);
} }
} }
export class RelativeReriodHelper { export class RelativePeriodHelper {
private _relativePeriods: RelativePeriod[] = []; private _relativePeriods: RelativePeriod[] = [];
private _baseMoment: moment.Moment; private _baseMoment: moment.Moment;
get baseMoment(){ get baseMoment() {
return moment(this._baseMoment); return moment(this._baseMoment);
} }
public getPeriod(periodType: DefaultPeriodType): IPeriod { public getPeriod(periodType: DefaultPeriodType): IPeriod {
console.debug("RelativeReriodHelper getPeriod start");
let result: IPeriod; let result: IPeriod;
console.log("input preiodType",periodType); for (const relativePeriod of this._relativePeriods) {
for (const key in this._relativePeriods) { if (relativePeriod.periodType == periodType) {
if (this._relativePeriods.hasOwnProperty(key)) { result = relativePeriod.period;
console.debug("key",key);
let relativePeriod = this._relativePeriods[key];
console.debug(relativePeriod.periodType);
if (relativePeriod.periodType == periodType) {
result = relativePeriod.period;
console.debug("period found",result.dateStart+"~"+result.dateEnd);
}
} }
} }
return result; return result;
} }
public constructor(relativeToday: boolean,firstDayOfWeek:number) { public constructor(relativeToday: boolean, firstDayOfWeek: number) {
console.debug("RelativePeriod helper constructor start"); moment.locale("cn", {
moment.locale("cn",{ week: {
week:{ dow: firstDayOfWeek,
dow:firstDayOfWeek, doy: 7
doy:7
} }
}); });
this._baseMoment = relativeToday ? moment() : moment().subtract(1, "day"); this._baseMoment = relativeToday ? moment() : moment().subtract(1, "day");
this._relativePeriods.push(this.getLastWeek(), this.getLastMonth(), this.getLastEntireWeek(), this.getLastEntireMonth()); this._relativePeriods.push(this.getLastWeek(), this.getLastMonth(), this.getLastEntireWeek(), this.getLastEntireMonth());
console.debug("this._baseMoment:" + this.baseMoment.format());
console.debug("RelativePeriodHelper constuctor end");
} }
private getLastWeek(): RelativePeriod { private getLastWeek(): RelativePeriod {
const start: Date = this.baseMoment.subtract(6, "days").startOf("day").toDate(); const start: Date = this.baseMoment.subtract(6, "days").startOf("day").toDate();

View File

@ -28,10 +28,12 @@
import { dataViewObjectsParser } from "powerbi-visuals-utils-dataviewutils"; import { dataViewObjectsParser } from "powerbi-visuals-utils-dataviewutils";
import DataViewObjectsParser = dataViewObjectsParser.DataViewObjectsParser; import DataViewObjectsParser = dataViewObjectsParser.DataViewObjectsParser;
import {PeriodSettings} from "./datePeriod/periodSetting"; import { DatePickerSetting } from "./datePeriod/datePickerSetting";
import { PeriodSetting } from "./datePeriod/periodSetting";
export class VisualSettings extends DataViewObjectsParser { export class VisualSettings extends DataViewObjectsParser {
public period: PeriodSettings = new PeriodSettings(); public datePickers: DatePickerSetting = new DatePickerSetting();
} public period: PeriodSetting = new PeriodSetting();
}

View File

@ -40,22 +40,25 @@ import IVisualHost = powerbi.extensibility.visual.IVisualHost;
import ISelectionId = powerbi.extensibility.ISelectionId; import ISelectionId = powerbi.extensibility.ISelectionId;
import * as d3 from "d3"; import * as d3 from "d3";
type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>; type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>;
import { VisualSettings } from "./settings/settings"; import { VisualSettings } from "./settings/visualSettings";
import { stratify } from "d3"; import { stratify, rgb } from "d3";
import * as models from 'powerbi-models'; import * as models from 'powerbi-models';
import { IAdvancedFilter } from "powerbi-models"; import { IAdvancedFilter } from "powerbi-models";
import IVisualEventService=powerbi.extensibility.IVisualEventService;
//custom //custom
import { IPeriod, Period, DefaultPeriodType } from "./settings/datePeriod/datePeriod"; import { IPeriod, Period, DefaultPeriodType,OrientationType } from "./settings/datePeriod/datePeriod";
import { RelativeReriodHelper } from "./settings/datePeriod/relativePeriodHelper"; import { RelativePeriodHelper } from "./settings/datePeriod/relativePeriodHelper";
import { IPeriodSelectorManager, PeriodSelectorManager } from "./settings/datePeriod/periodSelectorManager"; import { IPeriodSelectorManager, PeriodSelectorManager } from "./settings/datePeriod/periodSelectorManager";
//third party //third party
import * as moment from "moment"; import * as moment from "moment";
export class Visual implements IVisual { export class Visual implements IVisual {
//pbi //pbi
private host: IVisualHost; private host: IVisualHost;
private settings: VisualSettings; private settings: VisualSettings;
private category: powerbi.DataViewCategoryColumn; private category: powerbi.DataViewCategoryColumn;
private selectionManager: powerbi.extensibility.ISelectionManager; private selectionManager: powerbi.extensibility.ISelectionManager;
private events: IVisualEventService;
//others //others
private container: Selection<HTMLElement>; private container: Selection<HTMLElement>;
@ -63,25 +66,24 @@ export class Visual implements IVisual {
constructor(options: VisualConstructorOptions) { constructor(options: VisualConstructorOptions) {
//pbi //pbi
//console.log('Visual constructor', options); //console.log('Visual constructor', options);
console.debug("visual constructor start");
this.host = options.host; this.host = options.host;
this.selectionManager=this.host.createSelectionManager(); this.selectionManager = this.host.createSelectionManager();
this.events = options.host.eventService;
//others //others
this.container = d3.select(options.element).classed("container", true); this.container = d3.select(options.element).classed("container", true).append("div");
if (document) { if (document) {
const dateSelector_Start = this.container.append("input") const dateSelector_Start = this.container.append("input")
.attr("type", "date") .attr("type", "date")
.style("margin", "2px 10px 2px 2px"); .style("margin", "4px");
const dateSelector_End = this.container.append("input") const dateSelector_End = this.container.append("input")
.attr("type", "date") .attr("type", "date")
.style("margin", "2px 0px 0px 2px"); .style("margin", "4px");
dateSelector_Start.on("input change", () => { this.filterPeriod(); }); dateSelector_Start.on("input change", () => { this.filterPeriod(); });
dateSelector_End.on("input change", () => { this.filterPeriod(); }); dateSelector_End.on("input change", () => { this.filterPeriod(); });
this.periodSelectorManager = new PeriodSelectorManager(dateSelector_Start, dateSelector_End); this.periodSelectorManager = new PeriodSelectorManager(dateSelector_Start, dateSelector_End);
//Select datePeriod when click this button //Select datePeriod when click this button
this.container.on('contextmenu', () => { this.container.on('contextmenu', () => {
const mouseEvent: MouseEvent = d3.event as MouseEvent; const mouseEvent: MouseEvent = <MouseEvent>d3.event;
//const eventTarget: EventTarget = mouseEvent.target; //const eventTarget: EventTarget = mouseEvent.target;
//let dataPoint = d3.select(eventTarget).datum(); //let dataPoint = d3.select(eventTarget).datum();
this.selectionManager.showContextMenu({}, { this.selectionManager.showContextMenu({}, {
@ -93,53 +95,66 @@ export class Visual implements IVisual {
} }
} }
public update(options: VisualUpdateOptions) { public update(options: VisualUpdateOptions) {
console.debug("update start");
//pbi //pbi
this.events.renderingStarted(options);
this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]); this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
let width: number = options.viewport.width;
if(this.settings.datePickers.backgroundTransparency<0||this.settings.datePickers.backgroundTransparency>1){
this.settings.datePickers.backgroundTransparency=0;
}
let backgroundColor=d3.rgb(this.settings.datePickers.backgroundColor).rgb();
let backgroundColorWithTransparency=rgb(backgroundColor.r,backgroundColor.g,backgroundColor.b,1-this.settings.datePickers.backgroundTransparency).toString();
//Set style for these date-pickers one-by-one, but not group by class
let datePickerWidth:number=(this.settings.datePickers.orientationType==OrientationType.Horizontal)?width/2-8-4*this.settings.datePickers.borderWidth:width-8-2*this.settings.datePickers.borderWidth;
this.periodSelectorManager.dateSelector_Start
.style("width", datePickerWidth + "px")
.style("font-size",this.settings.datePickers.fontSize+"px")
.style("color",this.settings.datePickers.fontColor)
.style("background-color",backgroundColorWithTransparency)
.style("border-width",this.settings.datePickers.borderWidth+"px")
.style("border-color",this.settings.datePickers.borderColor)
.style("outline-color",this.settings.datePickers.outlineColor)
.style("display", (this.settings.datePickers.orientationType==OrientationType.Horizontal)?"inline":"block");
this.periodSelectorManager.dateSelector_End
.style("width", datePickerWidth+"px")
.style("font-size",this.settings.datePickers.fontSize+"px")
.style("color",this.settings.datePickers.fontColor)
.style("background-color",backgroundColorWithTransparency)
.style("border-width",this.settings.datePickers.borderWidth+"px")
.style("border-color",this.settings.datePickers.borderColor)
.style("display", (this.settings.datePickers.orientationType==OrientationType.Horizontal)?"inline":"block");
let dataView = options.dataViews[0]; let dataView = options.dataViews[0];
console.debug("dataView", dataView);
//set date series //set date series
this.category = dataView.categorical.categories[0]; this.category = dataView.categorical.categories[0];
//set new default period //set new default period
let newDefaultPeriod: IPeriod = new Period(); let newDefaultPeriod: IPeriod = new Period();
if (this.settings.period.defaultPeriodType == DefaultPeriodType.Custom) { if (this.settings.period.defaultPeriodType == DefaultPeriodType.Custom) {
console.debug("user select custom defaultPeriod");
dataView.categorical.values.map((messure: powerbi.DataViewValueColumn, index: number) => { dataView.categorical.values.map((measure: powerbi.DataViewValueColumn, index: number) => {
console.debug("1"); let measureDate: Date = moment(measure.values[0].toString()).startOf("day").toDate();
console.debug(messure.values[0]); if (measure.source.roles["StartDate"]) {
console.debug("2"); newDefaultPeriod.dateStart = measureDate;
let messureDate: Date = moment(messure.values[0].toString()).startOf("day").toDate();
console.debug("messureDate", messureDate);
if (messure.source.roles["StartDate"]) {
newDefaultPeriod.dateStart = messureDate;
} }
if (messure.source.roles["EndDate"]) { if (measure.source.roles["EndDate"]) {
newDefaultPeriod.dateEnd = messureDate; newDefaultPeriod.dateEnd = measureDate;
} }
}); });
} else { } else {
console.debug("user select " + this.settings.period.defaultPeriodType.toString() + "\ defaultPeriod"); let relativePeriodHelper: RelativePeriodHelper = new RelativePeriodHelper(this.settings.period.relativeToday, this.settings.period.firstDayOfWeek);
console.debug("firstDayOfWeek", this.settings.period.firstDayOfWeek);
let relativePeriodHelper: RelativeReriodHelper = new RelativeReriodHelper(this.settings.period.relativeToday, this.settings.period.firstDayOfWeek);
newDefaultPeriod = relativePeriodHelper.getPeriod(this.settings.period.defaultPeriodType); newDefaultPeriod = relativePeriodHelper.getPeriod(this.settings.period.defaultPeriodType);
} }
this.periodSelectorManager.defaultPeriod = newDefaultPeriod; this.periodSelectorManager.defaultPeriod = newDefaultPeriod;
console.debug("update end"); this.events.renderingFinished(options);
} }
private filterPeriod() { private filterPeriod() {
//determine period from dateSelectors //determine period from dateSelectors
console.debug("filter start");
let period: IPeriod = this.periodSelectorManager.period; let period: IPeriod = this.periodSelectorManager.period;
let target: models.IFilterColumnTarget; let target: models.IFilterColumnTarget;
try { target = {
target = { table: this.category.source.queryName.substr(0, this.category.source.queryName.indexOf('.')), // table
table: this.category.source.queryName.substr(0, this.category.source.queryName.indexOf('.')), // table column: this.category.source.displayName // col1
column: this.category.source.displayName // col1 };
};
}
catch (e) {
console.log(e);
}
//filter //filter
let conditions: models.IAdvancedFilterCondition[] = []; let conditions: models.IAdvancedFilterCondition[] = [];
if (period.dateStart) { if (period.dateStart) {
@ -158,8 +173,8 @@ export class Visual implements IVisual {
} }
); );
} }
let filter: IAdvancedFilter = { let filter: IAdvancedFilter = {
// tslint:disable-next-line: no-http-string
"$schema": "http://powerbi.com/product/schema#advanced", "$schema": "http://powerbi.com/product/schema#advanced",
"target": target, "target": target,
"filterType": models.FilterType.Advanced, "filterType": models.FilterType.Advanced,
@ -167,7 +182,7 @@ export class Visual implements IVisual {
"conditions": conditions "conditions": conditions
}; };
this.host.applyJsonFilter((period.dateStart || period.dateEnd) ? filter : null, "general", "filter", powerbi.FilterAction.merge); this.host.applyJsonFilter((period.dateStart || period.dateEnd) ? filter : null, "general", "filter", powerbi.FilterAction.merge);
console.debug("applyFilter end");
} }
private static parseSettings(dataView: DataView): VisualSettings { private static parseSettings(dataView: DataView): VisualSettings {
return <VisualSettings>VisualSettings.parse(dataView); return <VisualSettings>VisualSettings.parse(dataView);

View File

@ -1,21 +1,21 @@
{ {
"Role_Dates_DisplayName":"日期序列", "Role_Dates_DisplayName":"日期序列",
"Role_Dates_Discription":"请放置日期列", "Role_Dates_Description":"请放置日期列",
"Role_StartDate_DisplayName":"默认开始日期", "Role_StartDate_DisplayName":"默认开始日期",
"Role_StartDate_Discription":"可以使用度量值指定切片器的默认开始日期", "Role_StartDate_Description":"可以使用度量值指定切片器的默认开始日期",
"Role_DefaultEndDate_DisplayName":"默认结束日期", "Role_DefaultEndDate_DisplayName":"默认结束日期",
"Role_DefaultEndDate_Discription":"可以使用度量值指定切片器的默认结束日期", "Role_DefaultEndDate_Description":"可以使用度量值指定切片器的默认结束日期",
"Obj_Period_DisplayName":"日期段", "Obj_Period_DisplayName":"日期段",
"Obj_Period_Discription":"日期段选项", "Obj_Period_Description":"日期段选项",
"Period_DefaultPeriodType_DisplayName":"默认日期段", "Period_DefaultPeriodType_DisplayName":"默认日期段",
"Period_DefaultPeriodType_Discription":"选择如何确定切片器日期首尾默认值", "Period_DefaultPeriodType_Description":"选择如何确定切片器日期首尾默认值",
"LastMonth":"最近一个月", "LastMonth":"最近一个月",
"LastEntireMonth":"最近一个完整月", "LastEntireMonth":"最近一个完整月",
"LastWeek":"最近一周", "LastWeek":"最近一周",
"LastEntireWeek":"最近一个完整周", "LastEntireWeek":"最近一个完整周",
"Custom":"自定义(根据度量值)", "Custom":"自定义(根据度量值)",
"Period_RelativeToday_DisplayName":"包含今天", "Period_RelativeToday_DisplayName":"包含今天",
"Period_RelativeToday_Discription":"当默认日期项没有选择自定义时,此选项用于确定相对日期是否包含今日", "Period_RelativeToday_Description":"当默认日期项没有选择自定义时,此选项用于确定相对日期是否包含今日",
"Period_FirstDayOfWeek_DisplayName":"一周的第一天", "Period_FirstDayOfWeek_DisplayName":"一周的第一天",
"Sunday":"星期天", "Sunday":"星期天",
"Monday":"星期一", "Monday":"星期一",
@ -23,5 +23,25 @@
"Wednesday":"星期三", "Wednesday":"星期三",
"Thursday":"星期四", "Thursday":"星期四",
"Friday":"星期五", "Friday":"星期五",
"Saturday":"星期六" "Saturday":"星期六",
"DatePickers_Orientation_DisplayName":"方向",
"DatePickers_Orientation_Description":"设置日期选择器的排列方向",
"Horizontal":"水平",
"Vertical":"垂直",
"Obj_DatePickers_DisplayName":"日期选择器外观",
"Obj_DatePickers_Description":"设置两个日期选择器的外观",
"DatePickers_FontSize_DisplayName":"字体",
"DatePickers_FontSize_Description":"设置字体大小单位px无大小限制但请注意保持合适",
"DatePickers_FontColor_DisplayName":"字体颜色",
"DatePickers_FontColor_Description":"设置字体颜色",
"DatePickers_BackgroundColor_DisplayName":"背景色",
"DatePickers_BackgroundColor_Description":"设置背景色",
"DatePickers_BackgroundTransparency_DisplayName":"背景透明度",
"DatePickers_BackgroundTransparency_Description":"设置背景透明度",
"DatePickers_BorderWidth_DisplayName":"边框宽度",
"DatePickers_BorderWidth_Description":"设置边框的宽度",
"DatePickers_BorderColor_DisplayName":"边框颜色",
"DatePickers_BorderColor_Description":"设置常规状态下边框的颜色",
"DatePickers_OutlineColor_DisplayName":"轮廓颜色",
"DatePickers_OutlineColor_Description":"设置选中状态下日期选择器的边缘颜色"
} }

View File

@ -2,22 +2,22 @@
"locale":"zh-CN", "locale":"zh-CN",
"values":{ "values":{
"Role_Dates_DisplayName":"日期序列", "Role_Dates_DisplayName":"日期序列",
"Role_Dates_Discription":"请放置日期列", "Role_Dates_Description":"请放置日期列",
"Role_StartDate_DisplayName":"默认开始日期", "Role_StartDate_DisplayName":"默认开始日期",
"Role_StartDate_Discription":"可以使用度量值指定切片器的默认开始日期", "Role_StartDate_Description":"可以使用度量值指定切片器的默认开始日期",
"Role_DefaultEndDate_DisplayName":"默认结束日期", "Role_DefaultEndDate_DisplayName":"默认结束日期",
"Role_DefaultEndDate_Discription":"可以使用度量值指定切片器的默认结束日期", "Role_DefaultEndDate_Description":"可以使用度量值指定切片器的默认结束日期",
"Obj_Period_DisplayName":"日期段", "Obj_Period_DisplayName":"日期段",
"Obj_Period_Discription":"日期段选项", "Obj_Period_Description":"日期段选项",
"Period_DefaultPeriodType_DisplayName":"默认日期段", "Period_DefaultPeriodType_DisplayName":"默认日期段",
"Period_DefaultPeriodType_Discription":"选择如何确定切片器日期首尾默认值", "Period_DefaultPeriodType_Description":"选择如何确定切片器日期首尾默认值",
"LastMonth":"最近一个月", "LastMonth":"最近一个月",
"LastEntireMonth":"最近一个完整月", "LastEntireMonth":"最近一个完整月",
"LastWeek":"最近一周", "LastWeek":"最近一周",
"LastEntireWeek":"最近一个完整周", "LastEntireWeek":"最近一个完整周",
"Custom":"自定义(根据度量值)", "Custom":"自定义(根据度量值)",
"Period_RelativeToday_DisplayName":"包含今天", "Period_RelativeToday_DisplayName":"包含今天",
"Period_RelativeToday_Discription":"当默认日期项没有选择自定义时,此选项用于确定相对日期是否包含今日", "Period_RelativeToday_Description":"当默认日期项没有选择自定义时,此选项用于确定相对日期是否包含今日",
"Period_FirstDayOfWeek_DisplayName":"一周的第一天", "Period_FirstDayOfWeek_DisplayName":"一周的第一天",
"Sunday":"星期天", "Sunday":"星期天",
"Monday":"星期一", "Monday":"星期一",

219
test.webpack.config.js Normal file
View File

@ -0,0 +1,219 @@
const path = require('path');
const fs = require("fs");
// werbpack plugin
const webpack = require("webpack");
const PowerBICustomVisualsWebpackPlugin = require('powerbi-visuals-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const Visualizer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin');
// api configuration
const powerbiApi = require("powerbi-visuals-api");
// visual configuration json path
const pbivizPath = "./pbiviz.json";
const pbivizFile = require(path.join(__dirname, pbivizPath));
// the visual capabilities content
const capabilitiesPath = "./capabilities.json";
const capabilitiesFile = require(path.join(__dirname, capabilitiesPath));
const pluginLocation = './.tmp/precompile/visualPlugin.ts'; // path to visual plugin file, the file generates by the plugin
// string resources
const resourcesFolder = path.join(".","stringResources");
const localizationFolders = fs.existsSync(resourcesFolder) && fs.readdirSync(resourcesFolder);
const statsLocation = "../../webpack.statistics.html";
// babel options to support IE11
let babelOptions = {
"presets": [
[
require.resolve('@babel/preset-env'),
{
"targets": {
"ie": "11"
},
useBuiltIns: "entry",
corejs: 3,
modules: false
}
]
],
sourceType: "unambiguous", // tell to babel that the project can contains different module types, not only es2015 modules
cacheDirectory: path.join(".tmp", "babelCache") // path for chace files
};
module.exports = {
entry: {
"visual.js": pluginLocation
},
optimization: {
concatenateModules: false,
minimize: true // enable minimization for create *.pbiviz file less than 2 Mb, can be disabled for dev mode
},
devtool: 'source-map',
mode: "development",
module: {
rules: [
{
parser: {
amd: false
}
},
{
test: /(\.ts)x|\.ts$/,
include: /powerbi-visuals-|src|precompile\\visualPlugin.ts/,
use: [
{
loader: require.resolve('babel-loader'),
options: babelOptions
},
{
loader: require.resolve('ts-loader'),
options: {
transpileOnly: false,
experimentalWatchApi: false
}
}
]
},
{
test: /(\.js)x|\.js$/,
use: [
{
loader: require.resolve('babel-loader'),
options: babelOptions
}
]
},
{
test: /\.json$/,
loader: require.resolve('json-loader'),
type: "javascript/auto"
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: require.resolve('css-loader')
},
{
loader: require.resolve('less-loader'),
options: {
paths: [path.resolve(__dirname, "..", 'node_modules')]
}
}
]
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: require.resolve('css-loader')
}
]
},
{
test: /\.(woff|ttf|ico|woff2|jpg|jpeg|png|webp)$/i,
use: [
{
loader: 'base64-inline-loader'
}
]
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '.css']
},
output: {
path: path.join(__dirname, "/.tmp","drop"),
publicPath: 'assets',
filename: "[name]",
},
devServer: {
disableHostCheck: true,
contentBase: path.join(__dirname, ".tmp", "drop"), // path with assets for dev server, they are generated by webpack plugin
compress: true,
port: 8080, // dev server port
hot: false,
inline: false,
// cert files for dev server
https: {
pfx: fs.readFileSync("C:\\Users\\littl\\AppData\\Roaming\\npm\\node_modules\\powerbi-visuals-tools\\certs\\PowerBICustomVisualTest_public.pfx"), // for windows
passphrase: "5573455193282877"
// keep it commented to use webpack generated certificate
// key: path.join(__dirname, "certs","PowerBICustomVisualTest_public.key"), // for darwin, linux
// cert: path.join(__dirname, "certs", "PowerBICustomVisualTest_public.cer"), // for darwin, linux
// pfx: fs.readFileSync(path.join(__dirname, "certs", "PowerBICustomVisualTest_public.pfx")), // for windows
// passphrase: "5031595470751755"
},
headers: {
"access-control-allow-origin": "*",
"cache-control": "public, max-age=0"
},
},
externals: {
"powerbi-visuals-api": 'null',
"fakeDefine": 'false',
"corePowerbiObject": "Function('return this.powerbi')()",
"realWindow": "Function('return this')()"
},
plugins: [
new MiniCssExtractPlugin({
filename: "visual.css",
chunkFilename: "[id].css"
}),
new Visualizer({
reportFilename: statsLocation,
openAnalyzer: false,
analyzerMode: `static`
}),
// visual plugin regenerates with the visual source, but it does not require relaunching dev server
new webpack.WatchIgnorePlugin([
path.join(__dirname, pluginLocation),
"./.tmp/**/*.*"
]),
// custom visuals plugin instance with options
new PowerBICustomVisualsWebpackPlugin({
...pbivizFile,
capabilities: capabilitiesFile,
stringResources: localizationFolders && localizationFolders.map(localization => path.join(
resourcesFolder,
localization,
"resources.resjson"
)),
apiVersion: powerbiApi.version,
capabilitiesSchema: powerbiApi.schemas.capabilities,
pbivizSchema: powerbiApi.schemas.pbiviz,
stringResourcesSchema: powerbiApi.schemas.stringResources,
dependenciesSchema: powerbiApi.schemas.dependencies,
devMode: false,
generatePbiviz: true,
generateResources: true,
modules: true,
visualSourceLocation: "../../src/visual",
pluginLocation: pluginLocation,
packageOutPath: path.join(__dirname, "dist")
}),
new ExtraWatchWebpackPlugin({
files: [
pbivizPath,
capabilitiesPath
]
}),
new webpack.ProvidePlugin({
//window: 'realWindow',//for global window.jquery
define: 'fakeDefine',
powerbi: 'corePowerbiObject'
}),
]
};

View File

@ -14,6 +14,7 @@
] ]
}, },
"files": [ "files": [
"./src/visual.ts" "./src/visual.ts",
"./src/settings/datePeriod/relativePeriodHelper.ts"
] ]
} }

File diff suppressed because one or more lines are too long

67
webpack.statistics.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long