OpenTiny组件--表格组件使用指南
一 相关资料
https://opentiny.design/
二 示例代码
2.1 statistics.component.html
<div class="sj-stac-item-container">
<div class="sj-stac-item-title">
未跟进清单
</div>
<div class="sj-stac-item-label">
统计规则:1)话务员录入1小时后未更新进度的;2)一个月未跟进的。
</div>
<button class="sj-stac-filter-button" tiButton (click)="notTracked.exportLists()" id="export">导出excel</button>
<ti-table [srcData]="notTracked.srcData" [(displayedData)]="notTracked.displayedData">
<table>
<thead>
<tr>
<th class="sj-shangji-filter-table-th" *ngFor="let column of notTracked.columns">
<ti-cell-text>{{column.title}}</ti-cell-text>
<ti-head-sort *ngIf="column.sortKey" [sortKey]="column.sortKey"></ti-head-sort>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of notTracked.displayedData">
<td tiOverflow>{{row.source}}</td>
<td tiOverflow>{{row.time | date:'yyyy.MM.dd HH:mm'}}</td>
<td tiOverflow>{{row.province}}</td>
<td tiOverflow>{{row.contactInformation}}</td>
<td tiOverflow>{{row.type}}</td>
<td tiOverflow>{{row.accessPeoples}}</td>
<td tiOverflow>{{row.regionalManager}}</td>
<td tiOverflow>{{row.companyName}}</td>
<td tiOverflow>{{row.customerLevel}}</td>
<td tiOverflow>{{row.salesType}}</td>
<td tiOverflow>{{row.intendedproduct}}</td>
<td tiOverflow>{{row.status}}</td>
<td tiOverflow>{{row.followUpDate | date:'yyyy.MM.dd HH:mm'}}</td>
<td tiOverflow [ngStyle]="{ color: row.color }">{{row.notTracked}}</td>
</tr>
</tbody>
</table>
<ti-pagination [(currentPage)]="notTracked.pagination.currentPage"
[pageSize]="notTracked.pagination.pageSize"
[(totalNumber)]="notTracked.pagination.totalNumber">
</ti-pagination>
</ti-table>
</div>
<div class="sj-stac-item-container">
<div class="sj-stac-item-title">
成交率统计
</div>
<div class="sj-stac-item-label">
月份成交率计算方法:当月接入的商机中,在当月成交的数量。 统计对象:区域经理。
</div>
<button class="sj-stac-filter-button" tiButton (click)="dealRate.exportLists()" id="export1">导出excel</button>
<div class="sj-stac-item-filter">
<div class="sj-stac-item-filter-label">过滤条件:</div>
<ti-select
id="select-multiple"
class="sj-stac-item-filter-item"
[options]="dealRate.filter.time.options"
[(ngModel)]="dealRate.filter.time.value"
(select)="dealRate.filter.time.onSelect($event)"
(clear)="dealRate.filter.time.clear($event)"
[clearable]="true"
[multiple]="true"
placeholder="请选择月份">
</ti-select>
<ti-select
id="select-multiple1"
class="sj-stac-item-filter-item"
[options]="dealRate.filter.regionalManager.options"
[(ngModel)]="dealRate.filter.regionalManager.value"
[multiple]="true"
(select)="dealRate.filter.regionalManager.onSelect($event)"
(clear)="dealRate.filter.time.clear($event)"
[clearable]="true"
placeholder="请选择人员">
</ti-select>
</div>
<ti-table [srcData]="dealRate.srcData" [(displayedData)]="dealRate.displayedData">
<table>
<thead>
<tr>
<th class="sj-shangji-filter-table-th" *ngFor="let column of dealRate.columns">
<ti-cell-text>{{column.title}}</ti-cell-text>
<ti-head-sort *ngIf="column.sortKey" [sortKey]="column.sortKey"></ti-head-sort>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of dealRate.displayedData">
<td tiOverflow>{{row.time}}</td>
<td tiOverflow>{{row.regionalManager}}</td>
<td tiOverflow>{{row.shangjiNum}}</td>
<td tiOverflow>{{row.dealNum}}</td>
<td tiOverflow>{{row.dealRate}}</td>
</tr>
</tbody>
</table>
<ti-pagination [(currentPage)]="dealRate.pagination.currentPage"
[pageSize]="dealRate.pagination.pageSize"
[(totalNumber)]="dealRate.pagination.totalNumber">
</ti-pagination>
</ti-table>
<div class="sj-stac-shangji-label">商机数统计:</div>
<ng-container *ngFor="let list of dealRate.shangjiNum.lists; index as i">
<div style="margin-top: 10px;background-color: #f7f9fd;">
{{i}}.
<div class="sj-stac-shangji-item">{{list.name}}:</div>
<div class="sj-stac-shangji-item">{{list.num}}</div>
</div>
</ng-container>
</div>
<div class="sj-stac-item-container">
<div class="sj-stac-item-title">
商机来源统计
</div>
<div class="sj-stac-item-label">
商机来源统计方法:将商机列表汇总所有商机来源按照月份进行统计。
</div>
<button class="sj-stac-filter-button" tiButton (click)="source.exportLists()" id="export2">导出excel</button>
<div class="sj-stac-item-filter">
<div class="sj-stac-item-filter-label">过滤条件:</div>
<ti-select
id="select-multiple2"
class="sj-stac-item-filter-item"
[options]="source.filter.time.options"
[(ngModel)]="source.filter.time.value"
(select)="source.filter.time.onSelect($event)"
(clear)="source.filter.time.clear($event)"
[clearable]="true"
[multiple]="true"
placeholder="请选择月份">
</ti-select>
<ti-select
id="select-multiple3"
class="sj-stac-item-filter-item"
[options]="source.filter.source.options"
[(ngModel)]="source.filter.source.value"
[multiple]="true"
(select)="source.filter.source.onSelect($event)"
(clear)="source.filter.time.clear($event)"
[clearable]="true"
placeholder="请选择来源">
</ti-select>
</div>
<ti-table [srcData]="source.srcData" [(displayedData)]="source.displayedData">
<table>
<thead>
<tr>
<th class="sj-shangji-filter-table-th" *ngFor="let column of source.columns">
<ti-cell-text>{{column.title}}</ti-cell-text>
<ti-head-sort *ngIf="column.sortKey" [sortKey]="column.sortKey"></ti-head-sort>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of source.displayedData">
<td tiOverflow>{{row.time}}</td>
<td tiOverflow>{{row.source}}</td>
<td tiOverflow>{{row.num}}</td>
</tr>
</tbody>
</table>
<ti-pagination [(currentPage)]="source.pagination.currentPage"
[pageSize]="source.pagination.pageSize"
[(totalNumber)]="source.pagination.totalNumber">
</ti-pagination>
</ti-table>
</div>
2.2 statistics.component.ts
import {Component, Inject, LOCALE_ID, OnInit} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {formatDate} from "@angular/common";
import {expoerExcel} from "../shangji/excelOpt";
import {Constants} from "../constants";
@Component({
selector: 'app-statistics',
templateUrl: './statistics.component.html',
styleUrls: ['./statistics.component.less']
})
export class StatisticsComponent implements OnInit{
public notTracked: any;
public dealRate: any;
public source: any;
constructor(
@Inject(LOCALE_ID) public locale: string,
private http: HttpClient,
) {
}
ngOnInit(): void {
this.initNotTrackedTable();
this.initDealRateTable();
this.initSourceTable();
this.http.get('./sj/shangji/list', {
params: { name : 11, id: 233 }
}).subscribe((data: any) => {
this.setNotTrackedData(data);
this.setDealRateData(data);
this.setSourceData(data);
}, (error: any) => {
});
}
private initNotTrackedTable() {
this.notTracked = {
displayedData: [],
srcData: {
data: [
],
state: undefined
},
columns: [
{
title: '来源',
show: true,
sortKey: 'source',
width: '60px',
},
{
title: '首次接入日期',
show: true,
sortKey: 'time',
width: '100px',
},
{
title: '省',
show: true,
sortKey: 'province',
width: '70px',
},
{
title: '联系方式',
show: true,
sortKey: 'contactInformation',
width: '100px',
},
{
title: '分类',
show: true,
sortKey: 'type',
width: '60px',
},
{
title: '接入人',
show: true,
sortKey: 'accessPeoples',
width: '60px',
},
{
title: '区域经理',
show: true,
sortKey: 'regionalManager',
width: '60px',
},
{
title: '公司名称',
show: true,
sortKey: 'companyName',
width: '100px',
},
{
title: '客户等级',
show: true,
sortKey: 'customerLevel',
width: '60px',
},
{
title: '类型',
show: true,
sortKey: 'salesType',
width: '100px',
},
{
title: '意向产品',
show: true,
sortKey: 'intendedproduct',
width: '60px',
},
{
title: '商机状态',
show: true,
sortKey: 'status',
width: '100px',
},
{
title: '跟进日期',
show: true,
sortKey: 'followUpDate',
width: '100px',
},
{
title: '未跟踪状态',
show: true,
sortKey: 'notTracked',
width: '100px',
},
],
pagination: {
currentPage: 1,
totalNumber: 0,
pageSize: {
options: [5, 10, 20, 40, 50, 100],
size: 10
}
},
exportLists: ()=>{
const header:any= this.getExportTableHeader(this.notTracked);
const body:any = this.getExportTableBody(this.notTracked);
const name:any = formatDate(Date.now(), 'yyyy.MM.dd HH:mm', this.locale);
expoerExcel(header, body, name);
}
};
}
private initDealRateTable() {
this.dealRate = {
allData: [],
displayedData: [],
srcData: {
data: [
],
state: undefined
},
columns: [
{
title: '时间',
show: true,
sortKey: 'time',
width: '60px',
},
{
title: '人员',
show: true,
sortKey: 'regionalManager',
width: '100px',
},
{
title: '商机数量',
show: true,
sortKey: 'shangjiNum',
width: '70px',
},
{
title: '成交数量',
show: true,
sortKey: 'dealNum',
width: '100px',
},
{
title: '成交率',
show: true,
sortKey: 'dealRate',
width: '60px',
},
],
pagination: {
currentPage: 1,
totalNumber: 0,
pageSize: {
options: [5, 10, 20, 40, 50, 100],
size: 10
}
},
exportLists: ()=>{
const header:any= this.getExportTableHeader(this.dealRate);
const body:any = this.getExportTableBody(this.dealRate);
const name:any = formatDate(Date.now(), 'yyyy.MM.dd HH:mm', this.locale);
expoerExcel(header, body, name);
},
filter: {
time: {
options: [],
value: [],
onSelect: (option: any) => {
this.filterDealRateTable();
console.log(11111);
console.log(this.dealRate.filter.time.value)
},
clear: (option: any) => {
this.filterDealRateTable();
},
},
regionalManager: {
options: [],
value: [],
onSelect: (option: any) => {
this.filterDealRateTable();
console.log(option);
},
clear: (option: any) => {
this.filterDealRateTable();
},
},
},
shangjiNum: {
lists: []
}
};
}
private initSourceTable() {
this.source = {
allData: [],
displayedData: [],
srcData: {
data: [
],
state: undefined
},
columns: [
{
title: '时间',
show: true,
sortKey: 'time',
width: '60px',
},
{
title: '来源',
show: true,
sortKey: 'source',
width: '100px',
},
{
title: '数量',
show: true,
sortKey: 'num',
width: '70px',
},
],
pagination: {
currentPage: 1,
totalNumber: 0,
pageSize: {
options: [5, 10, 20, 40, 50, 100],
size: 10
}
},
exportLists: ()=>{
const header:any= this.getExportTableHeader(this.source);
const body:any = this.getExportTableBody(this.source);
const name:any = formatDate(Date.now(), 'yyyy.MM.dd HH:mm', this.locale);
expoerExcel(header, body, name);
},
filter: {
time: {
options: Constants.time,
value: [],
onSelect: (option: any) => {
this.filterSourceTable();
console.log(11111);
console.log(this.dealRate.filter.time.value)
},
clear: (option: any) => {
this.filterSourceTable();
},
},
source: {
options: Constants.source,
value: [],
onSelect: (option: any) => {
this.filterSourceTable();
console.log(option);
},
clear: (option: any) => {
this.filterSourceTable();
},
},
},
};
}
public getExportTableHeader(options: any) {
const header = [];
for (let i = 0; i < options.columns.length; i++) {
header.push(options.columns[i].title);
}
return header;
}
public getExportTableBody(options: any) {
const body = [];
const columns = options.columns;
const data = options.srcData.data;
for (let i = 0; i < data.length; i++) {
const row = [];
for (let j = 0; j < columns.length; j++) {
row.push(data[i][columns[j].sortKey]);
}
body.push(row);
}
return body;
}
private setNotTrackedData(data: any) {
const destData: any = [];
data.forEach((d: any) => {
if (d.status !== '已成交') {
// 一个月未跟踪
let flT = d.followUpDate === "" ? d.time : d.followUpDate;
if (Date.now() - Date.parse(flT) > 30*24*60*60*1000) {
d.notTracked = '一个月未跟踪';
d.color = '#f8d802';
destData.push(d);
} else {
if (d.followUpDate === '') {
// 一小时没更新
if (Date.now() - Date.parse(d.time) > 60*60*1000) {
d.notTracked = '一小时未更新';
d.color = '#ee0a0a';
destData.push(d);
}
}
}
}
});
this.notTracked.srcData.data = destData;
this.notTracked.displayedData = destData;
this.notTracked.pagination.totalNumber = destData.length;
}
private setDealRateData(data: any) {
const destData: any = [];
const dataMap = new Map();
data.forEach((d: any) => {
const dataStr = formatDate(d.time, 'yyyy.MM', this.locale);
const curMap = dataMap.get(dataStr);
if (curMap) {
const managerData = curMap.get(d.regionalManager);
if (managerData) {
managerData.shangjiNum += 1;
managerData.dealNum += this.isCurMonthDeal(d);
} else {
curMap.set(d.regionalManager, {
shangjiNum: 1,
dealNum: this.isCurMonthDeal(d),
});
}
} else {
const managerMap = new Map();
managerMap.set(d.regionalManager, {
shangjiNum: 1,
dealNum: this.isCurMonthDeal(d),
});
dataMap.set(dataStr, managerMap);
}
});
for (let d of dataMap.keys()) {
const mMap = dataMap.get(d);
for (let m of mMap.keys()) {
const mData = mMap.get(m);
destData.push({
time: d,
regionalManager: m,
shangjiNum: mData.shangjiNum,
dealNum: mData.dealNum,
dealRate: (mData.dealNum / mData.shangjiNum * 100 ).toFixed(2) + '%',
});
}
}
this.setDealRateTimeFilter(dataMap);
this.setDealRateRegionalManagerFilter(destData);
this.setShangjiNum(destData);
console.log(destData);
this.dealRate.srcData.data = destData;
this.dealRate.displayedData = destData;
this.dealRate.pagination.totalNumber = destData.length;
this.dealRate.allData = destData;
}
private setSourceData(data: any) {
this.source.allData = data;
}
private isCurMonthDeal(d: any) {
if (d.status !== '已成交') {
return 0;
}
if (d.time && d.dealTime) {
const dateStr = formatDate(d.time, 'yyyy.MM', this.locale);
const dealDateStr = formatDate(d.dealTime, 'yyyy.MM', this.locale);
if (dateStr === dealDateStr) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
private setDealRateTimeFilter(data: any) {
const options = [];
for (let d of data.keys()) {
options.push({
label: d,
});
}
this.dealRate.filter.time.options = options;
}
private setDealRateRegionalManagerFilter(data: any) {
const options = [];
const oM = new Map();
for (let d of data) {
if (!oM.get(d.regionalManager)) {
oM.set(d.regionalManager, true);
}
}
for (let o of oM.keys()) {
options.push({
label: o,
});
}
this.dealRate.filter.regionalManager.options = options;
}
private filterDealRateTable() {
let destData = [];
const time = this.dealRate.filter.time.value;
const regionalManager = this.dealRate.filter.regionalManager.value;
if (time.length === 0 && regionalManager.length === 0) {
destData = this.dealRate.allData;
} else if (time.length === 0 && regionalManager.length !== 0) {
for (let d of this.dealRate.allData) {
for (let r of regionalManager) {
if (r.label === d.regionalManager) {
destData.push(d);
}
}
}
} else if (time.length !== 0 && regionalManager.length === 0) {
for (let d of this.dealRate.allData) {
for (let t of time) {
if (t.label === d.time) {
destData.push(d);
}
}
}
} else {
for (let d of this.dealRate.allData) {
for (let t of time) {
if (t.label === d.time) {
for (let r of regionalManager) {
if (r.label === d.regionalManager) {
destData.push(d);
}
}
}
}
}
}
this.dealRate.srcData.data = destData;
this.dealRate.displayedData = destData;
this.dealRate.pagination.totalNumber = destData.length;
}
private filterSourceTable() {
let destData = [];
const time = this.source.filter.time.value;
const source = this.source.filter.source.value;
if (time.length === 0 && source.length === 0) {
destData = [];
} else if (time.length === 0 && source.length !== 0) {
destData = [];
} else if (time.length !== 0 && source.length === 0) {
destData = [];
} else {
for (let t of time) {
for (let s of source) {
destData.push({
time: t.label,
source: s.label,
num: 0,
});
}
}
for (let d of this.source.allData) {
for (let dd of destData) {
if (dd.time === formatDate(d.time, 'yyyy.MM', this.locale) && dd.source === d.source) {
++dd.num;
}
}
}
}
console.log(destData)
this.source.srcData.data = destData;
this.source.displayedData = destData;
this.source.pagination.totalNumber = destData.length;
}
private setShangjiNum(data: any) {
const destData = [];
let allNum = 0;
const oM = new Map();
for (let d of data) {
if (oM.get(d.regionalManager)) {
oM.set(d.regionalManager, oM.get(d.regionalManager) + d.shangjiNum);
} else {
oM.set(d.regionalManager, d.shangjiNum);
}
}
for (let o of oM.keys()) {
destData.push({
name: o,
num: oM.get(o)
});
allNum += oM.get(o);
}
destData.push({
name: '总计:',
num: allNum
});
this.dealRate.shangjiNum.lists = destData;
}
}
2.3 statistics.component.less
.sj-stac-item-container {
width: 92%;
height: 100%;
border-radius: 5px;
border: solid 1px #8A8E99;
padding: 20px;
margin-bottom: 20px;
}
.sj-stac-item-title {
width: 100%;
height: 30px;
font-size: 20px;
font-weight: bold;
margin-bottom: 20px;
}
.sj-stac-item-label {
margin-bottom: 20px;
}
.sj-stac-filter-button {
display: block;
margin-bottom: 20px;
margin-right: 20px;
}
.sj-stac-item-filter-item {
display: inline-block;
width: 400px;
margin-bottom: 20px;
margin-right: 20px;
}
::ng-deep .ti3-icon-clear:before {
display: none !important;
}
::ng-deep .ti3-icon-close-staic {
height: 1px !important;
}
.sj-stac-item-filter {
display: block;
}
.sj-stac-item-filter-label{
display: inline-block;
font-weight: bold;
font-size: 14px;
}
.sj-stac-shangji-label {
height: 25px;
font-size: 16px;
font-weight: bold;
margin-bottom: 5px;
margin-top: 60px;
}
.sj-stac-shangji-item {
display: inline-block;
margin-right: 40px;
width: 100px;
}
.sj-shangji-filter-table-th {
background-color: #7495e0 !important;
color: #0a0a0a !important;
font-size: 14px;
}