Created
This commit is contained in:
commit
7fa42e0df2
106
.gitignore
vendored
Normal file
106
.gitignore
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
StandardExmp/
|
||||
dist/
|
||||
# tmp
|
||||
.tmp/
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# gatsby files
|
||||
.cache/
|
||||
public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
13
.vscode/launch.json
vendored
Normal file
13
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debugger",
|
||||
"type": "chrome",
|
||||
"request": "attach",
|
||||
"port": 9222,
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${cwd}/"
|
||||
}
|
||||
]
|
||||
}
|
37
.vscode/settings.json
vendored
Normal file
37
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": true,
|
||||
"files.eol": "\n",
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/node_modules/**": true,
|
||||
".tmp": true
|
||||
},
|
||||
"files.exclude": {
|
||||
".tmp": true
|
||||
},
|
||||
"search.exclude": {
|
||||
".tmp": true,
|
||||
"typings": true
|
||||
},
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"/pbiviz.json"
|
||||
],
|
||||
"url": "./node_modules/powerbi-visuals-api/schema.pbiviz.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/capabilities.json"
|
||||
],
|
||||
"url": "./node_modules/powerbi-visuals-api/schema.capabilities.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/dependencies.json"
|
||||
],
|
||||
"url": "./node_modules/powerbi-visuals-api/schema.dependencies.json"
|
||||
}
|
||||
]
|
||||
}
|
BIN
assets/icon.png
Normal file
BIN
assets/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
192
capabilities.json
Normal file
192
capabilities.json
Normal file
@ -0,0 +1,192 @@
|
||||
{
|
||||
"dataRoles": [
|
||||
{
|
||||
"displayName": "Dates",
|
||||
"name": "Dates",
|
||||
"kind": "Grouping",
|
||||
"displayNameKey": "Role_Dates_DisplayName",
|
||||
"description": "Column date",
|
||||
"descriptionKey": "Role_Dates_Discription"
|
||||
},
|
||||
{
|
||||
"displayNameKey": "Role_StartDate_DisplayName",
|
||||
"displayName": "DefaultStartDate",
|
||||
"name": "StartDate",
|
||||
"kind": "Measure",
|
||||
"descriptionKey": "Role_StartDate_Discription",
|
||||
"description": "You can use Messure to assign Default date to the date-picker"
|
||||
},
|
||||
{
|
||||
"displayNameKey": "Role_DefaultEndDate_DisplayName",
|
||||
"displayName": "DefaultEndDate",
|
||||
"name": "EndDate",
|
||||
"kind": "Measure",
|
||||
"descriptionKey": "Role_DefaultEndDate_Discription",
|
||||
"description": "You can use Messure to assign Default date to the date-picker"
|
||||
}
|
||||
],
|
||||
"objects": {
|
||||
"period": {
|
||||
"displayNameKey": "Obj_Period_DisplayName",
|
||||
"displayName": "Period",
|
||||
"descriptionKey": "Obj_Period_Discription",
|
||||
"description": "Settings for period-picker",
|
||||
"properties": {
|
||||
"defaultPeriodType": {
|
||||
"displayNameKey": "Period_DefaultPeriodType_DisplayName",
|
||||
"displayName": "Default period",
|
||||
"descriptionKey":"Period_DefaultPeriodType_Discription",
|
||||
"description": "Choose how to assign default period to the period-picker",
|
||||
"type": {
|
||||
"enumeration": [
|
||||
{
|
||||
"value": "0",
|
||||
"displayNameKey": "LastMonth",
|
||||
"displayName": "Last month"
|
||||
},
|
||||
{
|
||||
"value": "1",
|
||||
"displayNameKey": "LastEntireMonth",
|
||||
"displayName": "Last entire month"
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"displayNameKey": "LastWeek",
|
||||
"displayName": "Last week"
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"displayNameKey": "LastEntireWeek",
|
||||
"displayName": "Last entire week"
|
||||
},
|
||||
{
|
||||
"value": "4",
|
||||
"displayNameKey": "Custom",
|
||||
"displayName": "Custom(use messures)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"filterState": true
|
||||
},
|
||||
"relativeToday": {
|
||||
"type": {
|
||||
"bool": true
|
||||
},
|
||||
"displayNameKey": "Period_RelativeToday_DisplayName",
|
||||
"displayName": "Contain today",
|
||||
"descriptionKey": "Period_RelativeToday_Discription",
|
||||
"description": "Effictive when and only when Custom(use messures) not selected"
|
||||
},
|
||||
"firstDayOfWeek": {
|
||||
"descriptionKey": "Period_FirstDayOfWeek_DisplayName",
|
||||
"displayName": "FirstDayOfWeek",
|
||||
"displayNameKey": "firstDayOfWeek",
|
||||
"type": {
|
||||
"enumeration": [
|
||||
{
|
||||
"value": "0",
|
||||
"displayName": "Sunday",
|
||||
"displayNameKey": "Sunday"
|
||||
},
|
||||
{
|
||||
"value": "1",
|
||||
"displayName": "Monday",
|
||||
"displayNameKey": "Monday"
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"displayName": "Tuesday",
|
||||
"displayNameKey": "Tuesday"
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"displayName": "Wednesday",
|
||||
"displayNameKey": "Wednesday"
|
||||
},
|
||||
{
|
||||
"value": "4",
|
||||
"displayName": "Thursday",
|
||||
"displayNameKey": "Thursday"
|
||||
},
|
||||
{
|
||||
"value": "5",
|
||||
"displayName": "Friday",
|
||||
"displayNameKey": "Friday"
|
||||
},
|
||||
{
|
||||
"value": "6",
|
||||
"displayName": "Saturday",
|
||||
"displayNameKey": "Saturday"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"general": {
|
||||
"displayName": "General",
|
||||
"displayNameKey": "formattingGeneral",
|
||||
"properties": {
|
||||
"filter": {
|
||||
"type": {
|
||||
"filter": true
|
||||
}
|
||||
},
|
||||
"selfFilter": {
|
||||
"type": {
|
||||
"filter": {
|
||||
"selfFilter": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"suppressDefaultTitle": true,
|
||||
"dataViewMappings": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"Dates": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"StartDate": {
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"EndDate": {
|
||||
"max": 1,
|
||||
"min": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"categorical": {
|
||||
"categories": {
|
||||
"for": {
|
||||
"in": "Dates"
|
||||
},
|
||||
"dataReductionAlgorithm": {
|
||||
"top": {
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"values": {
|
||||
"select": [
|
||||
{
|
||||
"bind": {
|
||||
"to": "StartDate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bind": {
|
||||
"to": "EndDate"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
8646
package-lock.json
generated
Normal file
8646
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
54
package.json
Normal file
54
package.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "visual",
|
||||
"scripts": {
|
||||
"pbiviz": "pbiviz",
|
||||
"start": "pbiviz start",
|
||||
"package": "pbiviz package",
|
||||
"lint": "tslint -c tslint.json -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.6.0",
|
||||
"@babel/runtime-corejs2": "7.6.0",
|
||||
"@types/d3": "5.7.2",
|
||||
"core-js": "3.2.1",
|
||||
"d3": "5.12.0",
|
||||
"moment": "^2.24.0",
|
||||
"powerbi-models": "^1.3.1",
|
||||
"powerbi-visuals-api": "~2.6.1",
|
||||
"powerbi-visuals-utils-dataviewutils": "2.2.1",
|
||||
"powerbi-visuals-utils-interactivityutils": "^5.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@types/d3": "5.7.2",
|
||||
"@types/jasmine": "2.5.37",
|
||||
"@types/jasmine-jquery": "1.5.28",
|
||||
"@types/jquery": "2.0.41",
|
||||
"@types/karma": "3.0.0",
|
||||
"@types/lodash-es": "4.17.1",
|
||||
"coveralls": "3.0.2",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jasmine": "2.5.2",
|
||||
"jasmine-core": "2.5.2",
|
||||
"jasmine-jquery": "2.1.1",
|
||||
"jquery": "^3.4.1",
|
||||
"karma": "^4.4.1",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-coverage": "1.1.2",
|
||||
"karma-coverage-istanbul-reporter": "^2.0.4",
|
||||
"karma-jasmine": "2.0.1",
|
||||
"karma-junit-reporter": "^1.2.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-typescript": "^4.1.1",
|
||||
"karma-typescript-preprocessor": "0.4.0",
|
||||
"karma-webpack": "3.0.5",
|
||||
"puppeteer": "1.17.0",
|
||||
"style-loader": "0.23.1",
|
||||
"ts-loader": "6.1.0",
|
||||
"ts-node": "7.0.1",
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-microsoft-contrib": "^6.2.0",
|
||||
"typescript": "3.6.3",
|
||||
"webpack": "4.26.0"
|
||||
}
|
||||
}
|
25
pbiviz.json
Normal file
25
pbiviz.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"visual": {
|
||||
"name": "DatePeriodSlice",
|
||||
"displayName": "DatePeriodSlice",
|
||||
"guid": "DatePeriodSlice9771124744984CE8959D0304EEAB35E8",
|
||||
"visualClassName": "Visual",
|
||||
"version": "1.0.0",
|
||||
"description": "可设置默认值的日期段切片器",
|
||||
"supportUrl": "https://github.com/mujiannan/PbiViz",
|
||||
"gitHubUrl": "https://github.com/mujiannan/PbiViz"
|
||||
},
|
||||
"apiVersion": "2.6.0",
|
||||
"author": {
|
||||
"name": "MuJianNan",
|
||||
"email": "littlesand@outlook.com"
|
||||
},
|
||||
"assets": {
|
||||
"icon": "assets/icon.png"
|
||||
},
|
||||
"externalJS": [],
|
||||
"style": "style/visual.less",
|
||||
"capabilities": "capabilities.json",
|
||||
"dependencies": null,
|
||||
"stringResources": []
|
||||
}
|
19
src/settings/datePeriod/datePeriod.ts
Normal file
19
src/settings/datePeriod/datePeriod.ts
Normal file
@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
import * as moment from 'moment';
|
||||
export interface IPeriod {
|
||||
dateStart: Date;
|
||||
dateEnd: Date;
|
||||
}
|
||||
export class Period implements IPeriod {
|
||||
dateStart: Date;
|
||||
dateEnd: Date;
|
||||
public constructor(start: Date = null, end: Date = null) {
|
||||
this.dateStart = start;
|
||||
this.dateEnd = end;
|
||||
}
|
||||
}
|
||||
export enum DefaultPeriodType {
|
||||
LastMonth, LastEntireMonth, LastWeek, LastEntireWeek, Custom
|
||||
}
|
||||
|
||||
|
63
src/settings/datePeriod/periodSelectorManager.ts
Normal file
63
src/settings/datePeriod/periodSelectorManager.ts
Normal file
@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
import * as d3 from "d3";
|
||||
import {IPeriod,Period} from "./datePeriod";
|
||||
import * as moment from "moment";
|
||||
type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>;
|
||||
export interface IPeriodSelectorManager{
|
||||
|
||||
dateSelector_Start:Selection<HTMLInputElement>;
|
||||
dateSelector_End:Selection<HTMLInputElement>;
|
||||
period:IPeriod;
|
||||
defaultPeriod:IPeriod;
|
||||
}
|
||||
export class PeriodSelectorManager implements IPeriodSelectorManager{
|
||||
public constructor(dateSelector_Start:Selection<HTMLInputElement>,dateSelector_End:Selection<HTMLInputElement>){
|
||||
this._dateSelector_Start=dateSelector_Start;
|
||||
this._dateSelector_End=dateSelector_End;
|
||||
}
|
||||
private _dateSelector_Start:Selection<HTMLInputElement>;
|
||||
private _dateSelector_End:Selection<HTMLInputElement>;
|
||||
get dateSelector_Start():Selection<HTMLInputElement>{
|
||||
return this._dateSelector_Start;
|
||||
}
|
||||
get dateSelector_End():Selection<HTMLInputElement>{
|
||||
return this._dateSelector_End;
|
||||
}
|
||||
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 end:Date=this._dateSelector_End.property("value")?moment(this._dateSelector_End.property("value")).endOf("day").toDate():null;
|
||||
if(start&&end&&start>end){
|
||||
console.debug("dateStart>dateEnd, set dateEnd=dateStart");
|
||||
this._dateSelector_End.property("value",this._dateSelector_Start.property("value"));
|
||||
end=start;
|
||||
}
|
||||
return new Period(start,end);
|
||||
}
|
||||
private _defaultPeriod:IPeriod=new Period();
|
||||
get defaultPeriod(){
|
||||
return this._defaultPeriod;
|
||||
}
|
||||
set defaultPeriod(newDefaultPeriod:IPeriod){
|
||||
console.debug("PeriodSelectorManager set defaultPeriod start");
|
||||
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)||
|
||||
(this._defaultPeriod.dateStart&&newDefaultPeriod.dateStart&&(newDefaultPeriod.dateStart.getTime()-this._defaultPeriod.dateStart.getTime()!=0))){
|
||||
let newStartStr=moment(newDefaultPeriod.dateStart).format("YYYY-MM-DD");
|
||||
console.debug("new start",newStartStr);
|
||||
this._dateSelector_Start.property("value",newStartStr);
|
||||
console.debug("Set dateStart value="+newStartStr);
|
||||
}
|
||||
if(((!this._defaultPeriod.dateEnd)&&newDefaultPeriod.dateEnd)||
|
||||
(this._defaultPeriod.dateEnd&&newDefaultPeriod.dateEnd&&(newDefaultPeriod.dateEnd.getTime()-this._defaultPeriod.dateEnd.getTime()!=0))){
|
||||
let newEndStr=moment(newDefaultPeriod.dateEnd).format("YYYY-MM-DD");
|
||||
this._dateSelector_End.property("value",newEndStr);
|
||||
console.debug("Set dateEnd value="+newEndStr);
|
||||
}
|
||||
this._defaultPeriod=newDefaultPeriod;
|
||||
this._dateSelector_Start.dispatch("change");
|
||||
console.debug("PeriodSelectorManager set defaultPeriod end");
|
||||
}
|
||||
}
|
7
src/settings/datePeriod/periodSetting.ts
Normal file
7
src/settings/datePeriod/periodSetting.ts
Normal file
@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
import * as DatePeriod from "./datePeriod";
|
||||
export class PeriodSettings {
|
||||
public defaultPeriodType:DatePeriod.DefaultPeriodType=DatePeriod.DefaultPeriodType.LastEntireMonth;
|
||||
public relativeToday:boolean=true;
|
||||
public firstDayOfWeek:number=1;
|
||||
}
|
73
src/settings/datePeriod/relativePeriodHelper.ts
Normal file
73
src/settings/datePeriod/relativePeriodHelper.ts
Normal file
@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
import {DefaultPeriodType,IPeriod,Period} from "./datePeriod";
|
||||
import * as moment from "moment";
|
||||
class RelativePeriod {
|
||||
periodType: DefaultPeriodType;
|
||||
private _period: IPeriod;
|
||||
get period(): IPeriod {
|
||||
return this._period;
|
||||
}
|
||||
public constructor(periodType: DefaultPeriodType, period: IPeriod) {
|
||||
console.debug("RelativePeriod constructor start,preiodType:",periodType);
|
||||
this.periodType = periodType;
|
||||
this._period = period;
|
||||
console.debug(period.dateStart+"~"+period.dateEnd);
|
||||
}
|
||||
}
|
||||
export class RelativeReriodHelper {
|
||||
private _relativePeriods: RelativePeriod[] = [];
|
||||
private _baseMoment: moment.Moment;
|
||||
get baseMoment(){
|
||||
return moment(this._baseMoment);
|
||||
}
|
||||
public getPeriod(periodType: DefaultPeriodType): IPeriod {
|
||||
console.debug("RelativeReriodHelper getPeriod start");
|
||||
let result: IPeriod;
|
||||
console.log("input preiodType",periodType);
|
||||
for (const key in this._relativePeriods) {
|
||||
if (this._relativePeriods.hasOwnProperty(key)) {
|
||||
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;
|
||||
}
|
||||
public constructor(relativeToday: boolean,firstDayOfWeek:number) {
|
||||
console.debug("RelativePeriod helper constructor start");
|
||||
moment.locale("cn",{
|
||||
week:{
|
||||
dow:firstDayOfWeek,
|
||||
doy:7
|
||||
}
|
||||
});
|
||||
this._baseMoment = relativeToday ? moment() : moment().subtract(1, "day");
|
||||
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 {
|
||||
const start: Date = this.baseMoment.subtract(6, "days").startOf("day").toDate();
|
||||
const end: Date = this.baseMoment.endOf("day").toDate();
|
||||
return new RelativePeriod(DefaultPeriodType.LastWeek, new Period(start, end));
|
||||
}
|
||||
private getLastMonth(): RelativePeriod {
|
||||
const start: Date = this.baseMoment.subtract(1, "month").add(1, "day").startOf("day").toDate();
|
||||
const end: Date = this.baseMoment.endOf("day").toDate();
|
||||
return new RelativePeriod(DefaultPeriodType.LastMonth, new Period(start, end));
|
||||
}
|
||||
private getLastEntireMonth(): RelativePeriod {
|
||||
const start: Date = this.baseMoment.subtract(1, "month").startOf("month").toDate();
|
||||
const end: Date = this.baseMoment.subtract(1, "month").endOf("month").toDate();
|
||||
return new RelativePeriod(DefaultPeriodType.LastEntireMonth, new Period(start, end));
|
||||
}
|
||||
private getLastEntireWeek(): RelativePeriod {
|
||||
const start: Date = this.baseMoment.subtract(1, "week").startOf("week").toDate();
|
||||
const end: Date = this.baseMoment.subtract(1, "week").endOf("week").toDate();
|
||||
return new RelativePeriod(DefaultPeriodType.LastEntireWeek, new Period(start, end));
|
||||
}
|
||||
}
|
38
src/settings/settings.ts
Normal file
38
src/settings/settings.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Power BI Visualizations
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
* All rights reserved.
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the ""Software""), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import { dataViewObjectsParser } from "powerbi-visuals-utils-dataviewutils";
|
||||
import DataViewObjectsParser = dataViewObjectsParser.DataViewObjectsParser;
|
||||
import {PeriodSettings} from "./datePeriod/periodSetting";
|
||||
export class VisualSettings extends DataViewObjectsParser {
|
||||
public period: PeriodSettings = new PeriodSettings();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
184
src/visual.ts
Normal file
184
src/visual.ts
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Power BI Visual CLI
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
* All rights reserved.
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the ""Software""), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
"use strict";
|
||||
//powerbi and d3
|
||||
import "core-js/stable";
|
||||
import "./../style/visual.less";
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
|
||||
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
|
||||
import IVisual = powerbi.extensibility.visual.IVisual;
|
||||
import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions;
|
||||
import VisualObjectInstance = powerbi.VisualObjectInstance;
|
||||
import DataView = powerbi.DataView;
|
||||
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
|
||||
import ISelectionManager = powerbi.extensibility.ISelectionManager;
|
||||
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
|
||||
import ISelectionId = powerbi.extensibility.ISelectionId;
|
||||
import * as d3 from "d3";
|
||||
type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>;
|
||||
import { VisualSettings } from "./settings/settings";
|
||||
import { stratify } from "d3";
|
||||
import * as models from 'powerbi-models';
|
||||
import { IAdvancedFilter } from "powerbi-models";
|
||||
//custom
|
||||
import { IPeriod, Period, DefaultPeriodType } from "./settings/datePeriod/datePeriod";
|
||||
import { RelativeReriodHelper } from "./settings/datePeriod/relativePeriodHelper";
|
||||
import { IPeriodSelectorManager, PeriodSelectorManager } from "./settings/datePeriod/periodSelectorManager";
|
||||
//third party
|
||||
import * as moment from "moment";
|
||||
export class Visual implements IVisual {
|
||||
//pbi
|
||||
private host: IVisualHost;
|
||||
private settings: VisualSettings;
|
||||
private category: powerbi.DataViewCategoryColumn;
|
||||
private selectionManager: powerbi.extensibility.ISelectionManager;
|
||||
|
||||
//others
|
||||
private container: Selection<HTMLElement>;
|
||||
private periodSelectorManager: IPeriodSelectorManager;
|
||||
constructor(options: VisualConstructorOptions) {
|
||||
//pbi
|
||||
//console.log('Visual constructor', options);
|
||||
console.debug("visual constructor start");
|
||||
this.host = options.host;
|
||||
this.selectionManager=this.host.createSelectionManager();
|
||||
//others
|
||||
this.container = d3.select(options.element).classed("container", true);
|
||||
if (document) {
|
||||
const dateSelector_Start = this.container.append("input")
|
||||
.attr("type", "date")
|
||||
.style("margin", "2px 10px 2px 2px");
|
||||
const dateSelector_End = this.container.append("input")
|
||||
.attr("type", "date")
|
||||
.style("margin", "2px 0px 0px 2px");
|
||||
dateSelector_Start.on("input change", () => { this.filterPeriod(); });
|
||||
dateSelector_End.on("input change", () => { this.filterPeriod(); });
|
||||
this.periodSelectorManager = new PeriodSelectorManager(dateSelector_Start, dateSelector_End);
|
||||
//Select datePeriod when click this button
|
||||
|
||||
this.container.on('contextmenu', () => {
|
||||
const mouseEvent: MouseEvent = d3.event as MouseEvent;
|
||||
//const eventTarget: EventTarget = mouseEvent.target;
|
||||
//let dataPoint = d3.select(eventTarget).datum();
|
||||
this.selectionManager.showContextMenu({}, {
|
||||
x: mouseEvent.clientX,
|
||||
y: mouseEvent.clientY
|
||||
});
|
||||
mouseEvent.preventDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
public update(options: VisualUpdateOptions) {
|
||||
console.debug("update start");
|
||||
//pbi
|
||||
this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
|
||||
let dataView = options.dataViews[0];
|
||||
console.debug("dataView", dataView);
|
||||
//set date series
|
||||
this.category = dataView.categorical.categories[0];
|
||||
//set new default period
|
||||
let newDefaultPeriod: IPeriod = new Period();
|
||||
if (this.settings.period.defaultPeriodType == DefaultPeriodType.Custom) {
|
||||
console.debug("user select custom defaultPeriod");
|
||||
dataView.categorical.values.map((messure: powerbi.DataViewValueColumn, index: number) => {
|
||||
console.debug("1");
|
||||
console.debug(messure.values[0]);
|
||||
console.debug("2");
|
||||
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"]) {
|
||||
newDefaultPeriod.dateEnd = messureDate;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.debug("user select " + this.settings.period.defaultPeriodType.toString() + "\ defaultPeriod");
|
||||
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);
|
||||
}
|
||||
this.periodSelectorManager.defaultPeriod = newDefaultPeriod;
|
||||
console.debug("update end");
|
||||
}
|
||||
private filterPeriod() {
|
||||
//determine period from dateSelectors
|
||||
console.debug("filter start");
|
||||
let period: IPeriod = this.periodSelectorManager.period;
|
||||
let target: models.IFilterColumnTarget;
|
||||
try {
|
||||
target = {
|
||||
table: this.category.source.queryName.substr(0, this.category.source.queryName.indexOf('.')), // table
|
||||
column: this.category.source.displayName // col1
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
//filter
|
||||
let conditions: models.IAdvancedFilterCondition[] = [];
|
||||
if (period.dateStart) {
|
||||
conditions.push(
|
||||
{
|
||||
operator: "GreaterThanOrEqual",
|
||||
value: period.dateStart.toJSON()
|
||||
}
|
||||
);
|
||||
}
|
||||
if (period.dateEnd) {
|
||||
conditions.push(
|
||||
{
|
||||
operator: "LessThanOrEqual",
|
||||
value: period.dateEnd.toJSON()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let filter: IAdvancedFilter = {
|
||||
"$schema": "http://powerbi.com/product/schema#advanced",
|
||||
"target": target,
|
||||
"filterType": models.FilterType.Advanced,
|
||||
"logicalOperator": "And",
|
||||
"conditions": conditions
|
||||
};
|
||||
this.host.applyJsonFilter((period.dateStart || period.dateEnd) ? filter : null, "general", "filter", powerbi.FilterAction.merge);
|
||||
console.debug("applyFilter end");
|
||||
}
|
||||
private static parseSettings(dataView: DataView): VisualSettings {
|
||||
return <VisualSettings>VisualSettings.parse(dataView);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets called for each of the objects defined in the capabilities files and allows you to select which of the
|
||||
* objects and properties you want to expose to the users in the property pane.
|
||||
*
|
||||
*/
|
||||
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
|
||||
return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
|
||||
}
|
||||
}
|
27
stringResources/zh-CN/resources.resjson
Normal file
27
stringResources/zh-CN/resources.resjson
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"Role_Dates_DisplayName":"日期序列",
|
||||
"Role_Dates_Discription":"请放置日期列",
|
||||
"Role_StartDate_DisplayName":"默认开始日期",
|
||||
"Role_StartDate_Discription":"可以使用度量值指定切片器的默认开始日期",
|
||||
"Role_DefaultEndDate_DisplayName":"默认结束日期",
|
||||
"Role_DefaultEndDate_Discription":"可以使用度量值指定切片器的默认结束日期",
|
||||
"Obj_Period_DisplayName":"日期段",
|
||||
"Obj_Period_Discription":"日期段选项",
|
||||
"Period_DefaultPeriodType_DisplayName":"默认日期段",
|
||||
"Period_DefaultPeriodType_Discription":"选择如何确定切片器日期首尾默认值",
|
||||
"LastMonth":"最近一个月",
|
||||
"LastEntireMonth":"最近一个完整月",
|
||||
"LastWeek":"最近一周",
|
||||
"LastEntireWeek":"最近一个完整周",
|
||||
"Custom":"自定义(根据度量值)",
|
||||
"Period_RelativeToday_DisplayName":"包含今天",
|
||||
"Period_RelativeToday_Discription":"当默认日期项没有选择自定义时,此选项用于确定相对日期是否包含今日",
|
||||
"Period_FirstDayOfWeek_DisplayName":"一周的第一天",
|
||||
"Sunday":"星期天",
|
||||
"Monday":"星期一",
|
||||
"Tuesday":"星期二",
|
||||
"Wednesday":"星期三",
|
||||
"Thursday":"星期四",
|
||||
"Friday":"星期五",
|
||||
"Saturday":"星期六"
|
||||
}
|
32
stringResources/zh-CN/zh-CN.json
Normal file
32
stringResources/zh-CN/zh-CN.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"locale":"zh-CN",
|
||||
"values":{
|
||||
"Role_Dates_DisplayName":"日期序列",
|
||||
"Role_Dates_Discription":"请放置日期列",
|
||||
"Role_StartDate_DisplayName":"默认开始日期",
|
||||
"Role_StartDate_Discription":"可以使用度量值指定切片器的默认开始日期",
|
||||
"Role_DefaultEndDate_DisplayName":"默认结束日期",
|
||||
"Role_DefaultEndDate_Discription":"可以使用度量值指定切片器的默认结束日期",
|
||||
"Obj_Period_DisplayName":"日期段",
|
||||
"Obj_Period_Discription":"日期段选项",
|
||||
"Period_DefaultPeriodType_DisplayName":"默认日期段",
|
||||
"Period_DefaultPeriodType_Discription":"选择如何确定切片器日期首尾默认值",
|
||||
"LastMonth":"最近一个月",
|
||||
"LastEntireMonth":"最近一个完整月",
|
||||
"LastWeek":"最近一周",
|
||||
"LastEntireWeek":"最近一个完整周",
|
||||
"Custom":"自定义(根据度量值)",
|
||||
"Period_RelativeToday_DisplayName":"包含今天",
|
||||
"Period_RelativeToday_Discription":"当默认日期项没有选择自定义时,此选项用于确定相对日期是否包含今日",
|
||||
"Period_FirstDayOfWeek_DisplayName":"一周的第一天",
|
||||
"Sunday":"星期天",
|
||||
"Monday":"星期一",
|
||||
"Tuesday":"星期二",
|
||||
"Wednesday":"星期三",
|
||||
"Thursday":"星期四",
|
||||
"Friday":"星期五",
|
||||
"Saturday":"星期六"
|
||||
}
|
||||
}
|
||||
|
||||
|
10
style/visual.less
Normal file
10
style/visual.less
Normal file
@ -0,0 +1,10 @@
|
||||
@import (less) "node_modules/powerbi-visuals-utils-interactivityutils/lib/index.css";
|
||||
p {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
em {
|
||||
background: yellow;
|
||||
padding: 5px;
|
||||
|
||||
}
|
||||
}
|
4
test/datePeriod.js
Normal file
4
test/datePeriod.js
Normal file
@ -0,0 +1,4 @@
|
||||
import "../src/settings/datePeriod/datePeriod";
|
||||
import { RelativeReriodHelper } from "../src/settings/datePeriod/datePeriod";
|
||||
let r=new RelativeReriodHelper();
|
||||
r.getLastWeek();
|
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"outDir": "./.tmp/build/",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"./src/visual.ts"
|
||||
]
|
||||
}
|
9
tslint.json
Normal file
9
tslint.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "tslint-microsoft-contrib/recommended",
|
||||
"rulesDirectory": [
|
||||
"node_modules/tslint-microsoft-contrib"
|
||||
],
|
||||
"rules": {
|
||||
"no-relative-imports": false
|
||||
}
|
||||
}
|
64
webpack.statistics.dev.html
Normal file
64
webpack.statistics.dev.html
Normal file
File diff suppressed because one or more lines are too long
64
webpack.statistics.prod.html
Normal file
64
webpack.statistics.prod.html
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user