@yelon/abc
Version:
Common business components of ng-yunzai.
1,292 lines (1,284 loc) • 147 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, inject, TemplateRef, Input, Directive, Host, ViewContainerRef, ChangeDetectorRef, EventEmitter, Output, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, DestroyRef, input, booleanAttribute, effect, numberAttribute, ViewChild, NgModule, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
import * as i4 from '@angular/platform-browser';
import { DomSanitizer } from '@angular/platform-browser';
import { ACLService } from '@yelon/acl';
import * as i1 from '@yelon/theme';
import { YUNZAI_I18N_TOKEN, ModalHelper, DrawerHelper, YelonLocaleService, DatePipe, YNPipe } from '@yelon/theme';
import { warn, deepCopy, deepGet, deepMergeKey } from '@yelon/util/other';
import { HttpParams } from '@angular/common/http';
import { map, of, filter, finalize, catchError, throwError, isObservable, lastValueFrom } from 'rxjs';
import * as i2 from '@angular/common';
import { NgTemplateOutlet, DOCUMENT, DecimalPipe } from '@angular/common';
import * as i3 from '@yelon/util/format';
import { XlsxService } from '@yelon/abc/xlsx';
import * as i6 from '@angular/cdk/drag-drop';
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i1$1 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { CellComponent } from '@yelon/abc/cell';
import { YunzaiConfigService } from '@yelon/util/config';
import { NzBadgeComponent } from 'ng-zorro-antd/badge';
import { NzCheckboxComponent } from 'ng-zorro-antd/checkbox';
import { NzDividerComponent } from 'ng-zorro-antd/divider';
import * as i3$1 from 'ng-zorro-antd/dropdown';
import { NzDropDownModule, NzContextMenuService } from 'ng-zorro-antd/dropdown';
import { NzIconDirective } from 'ng-zorro-antd/icon';
import * as i2$1 from 'ng-zorro-antd/menu';
import { NzMenuModule } from 'ng-zorro-antd/menu';
import { NzPopconfirmDirective } from 'ng-zorro-antd/popconfirm';
import { NzRadioComponent } from 'ng-zorro-antd/radio';
import * as i5 from 'ng-zorro-antd/resizable';
import { NzResizableModule } from 'ng-zorro-antd/resizable';
import * as i4$1 from 'ng-zorro-antd/table';
import { NzTableModule } from 'ng-zorro-antd/table';
import { NzTagComponent } from 'ng-zorro-antd/tag';
import { NzTooltipDirective } from 'ng-zorro-antd/tooltip';
import { NzRangePickerComponent, NzDatePickerComponent } from 'ng-zorro-antd/date-picker';
import { NzInputDirective } from 'ng-zorro-antd/input';
import { NzInputNumberComponent } from 'ng-zorro-antd/input-number';
class STRowSource {
titles = {};
rows = {};
add(type, path, ref) {
this[type === 'title' ? 'titles' : 'rows'][path] = ref;
}
getTitle(path) {
return this.titles[path];
}
getRow(path) {
return this.rows[path];
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STRowSource, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STRowSource });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STRowSource, decorators: [{
type: Injectable
}] });
class STRowDirective {
source = inject(STRowSource, { host: true });
ref = inject(TemplateRef);
id;
type;
ngOnInit() {
this.source.add(this.type, this.id, this.ref);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.3", type: STRowDirective, isStandalone: true, selector: "[st-row]", inputs: { id: ["st-row", "id"], type: "type" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STRowDirective, decorators: [{
type: Directive,
args: [{
selector: '[st-row]'
}]
}], propDecorators: { id: [{
type: Input,
args: ['st-row']
}], type: [{
type: Input
}] } });
class STWidgetRegistry {
_widgets = {};
get widgets() {
return this._widgets;
}
register(type, widget) {
this._widgets[type] = widget;
}
has(type) {
return Object.prototype.hasOwnProperty.call(this._widgets, type);
}
get(type) {
return this._widgets[type];
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STWidgetRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STWidgetRegistry, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STWidgetRegistry, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class STColumnSource {
dom = inject(DomSanitizer);
rowSource = inject(STRowSource, { host: true });
acl = inject(ACLService, { optional: true });
i18nSrv = inject(YUNZAI_I18N_TOKEN, { optional: true });
stWidgetRegistry = inject(STWidgetRegistry);
cog;
setCog(val) {
this.cog = val;
}
fixPop(i, def) {
if (i.pop == null || i.pop === false) {
i.pop = false;
return;
}
let pop = {
okType: 'primary',
...def
};
if (typeof i.pop === 'string') {
pop.title = i.pop;
}
else if (typeof i.pop === 'object') {
pop = {
...pop,
...i.pop
};
}
if (typeof pop.condition !== 'function') {
pop.condition = () => false;
}
if (this.i18nSrv) {
if (pop.titleI18n)
pop.title = this.i18nSrv.fanyi(pop.titleI18n);
if (pop.okTextI18n)
pop.okText = this.i18nSrv.fanyi(pop.okTextI18n);
if (pop.cancelTextI18n)
pop.cancelText = this.i18nSrv.fanyi(pop.cancelTextI18n);
}
i.pop = pop;
}
btnCoerce(list) {
if (!list)
return [];
const ret = [];
const { modal, drawer, pop, btnIcon } = this.cog;
for (const item of list) {
if (this.acl && item.acl && !this.acl.can(item.acl)) {
continue;
}
if (item.type === 'modal' || item.type === 'static') {
if (item.modal == null || item.modal.component == null) {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
console.warn(`[st] Should specify modal parameter when type is modal or static`);
}
item.type = 'none';
}
else {
item.modal = { paramsName: 'record', size: 'lg', ...modal, ...item.modal };
}
}
if (item.type === 'drawer') {
if (item.drawer == null || item.drawer.component == null) {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
console.warn(`[st] Should specify drawer parameter when type is drawer`);
}
item.type = 'none';
}
else {
item.drawer = { paramsName: 'record', size: 'lg', ...drawer, ...item.drawer };
}
}
if (item.type === 'del' && typeof item.pop === 'undefined') {
item.pop = true;
}
// pop
this.fixPop(item, pop);
if (typeof item.icon !== 'function') {
item.icon = {
...btnIcon,
...(typeof item.icon === 'string' ? { type: item.icon } : item.icon)
};
}
item.children = item.children && item.children.length > 0 ? this.btnCoerce(item.children) : [];
// i18n
if (item.i18n && this.i18nSrv) {
item.text = this.i18nSrv.fanyi(item.i18n);
}
ret.push(item);
}
this.btnCoerceIf(ret);
return ret;
}
btnCoerceIf(list) {
for (const item of list) {
item.iifBehavior = item.iifBehavior || this.cog.iifBehavior;
if (item.children && item.children.length > 0) {
this.btnCoerceIf(item.children);
}
else {
item.children = [];
}
}
}
fixedCoerce(list, expand) {
const countReduce = (a, b) => a + +b.width.toString().replace('px', '');
const expandWidth = expand ? 50 : 0;
// left width
list
.filter(w => w.fixed && w.fixed === 'left' && w.width)
.forEach((item, idx) => (item._left = `${list.slice(0, idx).reduce(countReduce, 0) + expandWidth}px`));
// right width
list
.filter(w => w.fixed && w.fixed === 'right' && w.width)
.reverse()
.forEach((item, idx) => (item._right = `${idx > 0 ? list.slice(-idx).reduce(countReduce, 0) : 0}px`));
}
sortCoerce(item) {
const res = this.fixSortCoerce(item);
res.reName = {
...this.cog.sortReName,
...res.reName
};
return res;
}
fixSortCoerce(item) {
if (typeof item.sort === 'undefined') {
return { enabled: false };
}
let res = {};
if (typeof item.sort === 'string') {
if (item.sort === 'ascend' || item.sort === 'descend') {
res.directions = [item.sort, null];
}
else {
res.key = item.sort;
}
}
else if (typeof item.sort !== 'boolean') {
res = item.sort;
}
else if (typeof item.sort === 'boolean') {
res.compare = (a, b) => a[item.indexKey] - b[item.indexKey];
}
if (!res.key) {
res.key = item.indexKey;
}
if (!Array.isArray(res.directions)) {
res.directions = this.cog.sortDirections ?? ['ascend', 'descend', null];
}
res.enabled = true;
return res;
}
filterCoerce(item) {
if (item.filter == null) {
return null;
}
let res = item.filter;
res.type = res.type || 'default';
res.showOPArea = res.showOPArea !== false;
let icon = 'filter';
let iconTheme = 'fill';
let fixMenus = true;
let value = undefined;
switch (res.type) {
case 'keyword':
icon = 'search';
iconTheme = 'outline';
break;
case 'number':
icon = 'search';
iconTheme = 'outline';
res.number = {
step: 1,
min: -Infinity,
max: Infinity,
...res.number
};
break;
case 'date':
icon = 'calendar';
iconTheme = 'outline';
res.date = {
range: false,
mode: 'date',
showToday: true,
showNow: false,
...res.date
};
break;
case 'custom':
break;
default:
fixMenus = false;
break;
}
if (fixMenus && (res.menus == null || res.menus.length === 0)) {
res.menus = [{ value }];
}
if (res.menus?.length === 0) {
return null;
}
if (typeof res.multiple === 'undefined') {
res.multiple = true;
}
res.confirmText = res.confirmText || this.cog.filterConfirmText;
res.clearText = res.clearText || this.cog.filterClearText;
res.key = res.key || item.indexKey;
res.icon = res.icon || icon;
const baseIcon = { type: icon, theme: iconTheme };
if (typeof res.icon === 'string') {
res.icon = { ...baseIcon, type: res.icon };
}
else {
res.icon = { ...baseIcon, ...res.icon };
}
this.updateDefault(res);
if (this.acl) {
res.menus = res.menus?.filter(w => this.acl.can(w.acl));
}
return res.menus?.length === 0 ? null : res;
}
restoreRender(item) {
if (item.renderTitle) {
item.__renderTitle =
typeof item.renderTitle === 'string'
? this.rowSource.getTitle(item.renderTitle)
: item.renderTitle;
}
if (item.render) {
item.__render =
typeof item.render === 'string' ? this.rowSource.getRow(item.render) : item.render;
}
}
widgetCoerce(item) {
if (item.type !== 'widget')
return;
if (item.widget == null || !this.stWidgetRegistry.has(item.widget.type)) {
delete item.type;
if (typeof ngDevMode === 'undefined' || ngDevMode) {
warn(`st: No widget for type "${item.widget?.type}"`);
}
}
}
genHeaders(rootColumns) {
const rows = [];
const widths = [];
const fillRowCells = (columns, colIndex, rowIndex = 0) => {
// Init rows
rows[rowIndex] = rows[rowIndex] || [];
let currentColIndex = colIndex;
const colSpans = columns.map(column => {
const cell = {
column,
colStart: currentColIndex,
hasSubColumns: false
};
let colSpan = 1;
const subColumns = column.children;
if (Array.isArray(subColumns) && subColumns.length > 0) {
colSpan = fillRowCells(subColumns, currentColIndex, rowIndex + 1).reduce((total, count) => total + count, 0);
cell.hasSubColumns = true;
}
else {
widths.push(cell.column.width || '');
}
if ('colSpan' in column) {
colSpan = column.colSpan;
}
if ('rowSpan' in column) {
cell.rowSpan = column.rowSpan;
}
cell.colSpan = colSpan;
cell.colEnd = cell.colStart + colSpan - 1;
rows[rowIndex].push(cell);
currentColIndex += colSpan;
return colSpan;
});
return colSpans;
};
fillRowCells(rootColumns, 0);
// Handle `rowSpan`
const rowCount = rows.length;
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
rows[rowIndex].forEach(cell => {
if (!('rowSpan' in cell) && !cell.hasSubColumns) {
cell.rowSpan = rowCount - rowIndex;
}
});
}
return { headers: rows, headerWidths: rowCount > 1 ? widths : null };
}
cleanCond(list) {
const res = [];
const copyList = deepCopy(list);
for (const item of copyList) {
if (typeof item.iif === 'function' && !item.iif(item)) {
continue;
}
if (this.acl && item.acl && !this.acl.can(item.acl)) {
continue;
}
if (Array.isArray(item.children) && item.children.length > 0) {
item.children = this.cleanCond(item.children);
}
res.push(item);
}
return res;
}
mergeClass(item) {
const builtInClassNames = [];
if (item._isTruncate) {
builtInClassNames.push('text-truncate');
}
const rawClassName = item.className;
if (!rawClassName) {
const typeClass = {
number: 'text-right',
currency: 'text-right',
date: 'text-center'
}[item.type];
if (typeClass) {
builtInClassNames.push(typeClass);
}
item._className = builtInClassNames;
return;
}
const rawClassNameIsArray = Array.isArray(rawClassName);
if (!rawClassNameIsArray && typeof rawClassName === 'object') {
const objClassNames = rawClassName;
builtInClassNames.forEach(key => (objClassNames[key] = true));
item._className = objClassNames;
return;
}
const arrayClassNames = rawClassNameIsArray ? Array.from(rawClassName) : [rawClassName];
arrayClassNames.splice(0, 0, ...builtInClassNames);
item._className = [...new Set(arrayClassNames)].filter(w => !!w);
}
process(list, options) {
if (!list || list.length === 0) {
return { columns: [], headers: [], headerWidths: null };
}
const { noIndex } = this.cog;
let checkboxCount = 0;
let radioCount = 0;
const columns = [];
const processItem = (item) => {
// index
if (item.index) {
if (!Array.isArray(item.index)) {
item.index = item.index.toString().split('.');
}
item.indexKey = item.index.join('.');
}
// #region title
const tit = (typeof item.title === 'string' ? { text: item.title } : item.title) || {};
if (tit.i18n && this.i18nSrv) {
tit.text = this.i18nSrv.fanyi(tit.i18n);
}
if (tit.text) {
tit._text = this.dom.bypassSecurityTrustHtml(tit.text);
}
item.title = tit;
// #endregion
// no
if (item.type === 'no') {
item.noIndex = item.noIndex == null ? noIndex : item.noIndex;
}
// checkbox
if (item.selections == null) {
item.selections = [];
}
if (item.type === 'checkbox') {
++checkboxCount;
if (!item.width) {
item.width = `${item.selections.length > 0 ? 62 : 50}px`;
}
}
if (this.acl) {
item.selections = item.selections.filter(w => this.acl.can(w.acl));
}
// radio
if (item.type === 'radio') {
++radioCount;
item.selections = [];
if (!item.width) {
item.width = '50px';
}
}
// cell
if (item.cell != null) {
item.type = 'cell';
}
// types
if (item.type === 'yn') {
item.yn = { truth: true, ...this.cog.yn, ...item.yn };
}
// date
if (item.type === 'date') {
item.dateFormat = item.dateFormat || this.cog.date?.format;
}
if ((item.type === 'link' && typeof item.click !== 'function') ||
(item.type === 'badge' && item.badge == null) ||
(item.type === 'tag' && item.tag == null) ||
(item.type === 'enum' && item.enum == null)) {
item.type = '';
}
item._isTruncate = !!item.width && options.widthMode.strictBehavior === 'truncate' && item.type !== 'img';
// className
this.mergeClass(item);
// width
if (typeof item.width === 'number') {
item._width = item.width;
item.width = `${item.width}px`;
}
item._left = false;
item._right = false;
item.safeType = item.safeType ?? options.safeType;
// sorter
item._sort = this.sortCoerce(item);
// filter
item.filter = this.filterCoerce(item);
// buttons
item.buttons = this.btnCoerce(item.buttons);
// widget
this.widgetCoerce(item);
// restore custom row
this.restoreRender(item);
// resizable
item.resizable = {
disabled: true,
bounds: 'window',
minWidth: 60,
maxWidth: 360,
preview: true,
...options.resizable,
...(typeof item.resizable === 'boolean' ? { disabled: !item.resizable } : item.resizable)
};
return item;
};
const processList = (data) => {
for (const item of data) {
columns.push(processItem(item));
if (Array.isArray(item.children)) {
processList(item.children);
}
}
};
const copyList = this.cleanCond(list);
processList(copyList);
if (checkboxCount > 1) {
throw new Error(`[st]: just only one column checkbox`);
}
if (radioCount > 1) {
throw new Error(`[st]: just only one column radio`);
}
this.fixedCoerce(columns, options.expand);
return {
columns: columns.filter(w => !Array.isArray(w.children) || w.children.length === 0),
...this.genHeaders(copyList)
};
}
restoreAllRender(columns) {
columns.forEach(i => this.restoreRender(i));
}
updateDefault(filter) {
if (filter.menus == null)
return this;
if (filter.type === 'default') {
filter.default = filter.menus.findIndex(w => w.checked) !== -1;
}
else {
filter.default = !!filter.menus[0].value;
}
return this;
}
cleanFilter(col) {
const f = col.filter;
f.default = false;
if (f.type === 'default') {
f.menus.forEach(i => (i.checked = false));
}
else {
f.menus[0].value = undefined;
}
return this;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STColumnSource, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STColumnSource });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STColumnSource, decorators: [{
type: Injectable
}] });
class STDataSource {
http;
datePipe;
ynPipe;
numberPipe;
currencySrv;
dom;
cog;
sortTick = 0;
constructor(http, datePipe, ynPipe, numberPipe, currencySrv, dom) {
this.http = http;
this.datePipe = datePipe;
this.ynPipe = ynPipe;
this.numberPipe = numberPipe;
this.currencySrv = currencySrv;
this.dom = dom;
}
setCog(val) {
this.cog = val;
}
process(options) {
let data$;
let isRemote = false;
const { data, res, total, page, pi, ps, paginator, columns, headers } = options;
let retTotal;
let retPs;
let retList;
let retPi;
let rawData;
let showPage = page.show;
if (typeof data === 'string') {
isRemote = true;
data$ = this.getByRemote(data, options).pipe(map(result => {
rawData = result;
let ret;
if (Array.isArray(result)) {
ret = result;
retTotal = ret.length;
retPs = retTotal;
showPage = false;
}
else {
const reName = res.reName;
if (typeof reName === 'function') {
const fnRes = reName(result, { pi, ps, total });
ret = fnRes.list;
retTotal = fnRes.total;
}
else {
// list
ret = deepGet(result, reName.list, []);
if (ret == null || !Array.isArray(ret)) {
ret = [];
}
// total
const resultTotal = reName.total && deepGet(result, reName.total, null);
retTotal = resultTotal == null ? total || 0 : +resultTotal;
}
}
return deepCopy(ret);
}));
}
else if (data == null || Array.isArray(data)) {
data$ = of(data ?? []);
}
else {
// a cold observable
data$ = data;
}
if (!isRemote) {
data$ = data$.pipe(
// sort
map((result) => {
rawData = result;
let copyResult = deepCopy(result);
const sorterFn = this.getSorterFn(headers);
if (sorterFn) {
copyResult = copyResult.sort(sorterFn);
}
return copyResult;
}),
// filter
map((result) => {
columns
.filter(w => w.filter)
.forEach(c => {
const filter = c.filter;
const values = this.getFilteredData(filter);
if (values.length === 0)
return;
const onFilter = filter.fn;
if (typeof onFilter !== 'function') {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
console.warn(`[st] Muse provide the fn function in filter`);
}
return;
}
result = result.filter(record => values.some(v => onFilter(v, record)));
});
return result;
}),
// paging
map((result) => {
if (paginator && page.front) {
const maxPageIndex = Math.ceil(result.length / ps);
retPi = Math.max(1, pi > maxPageIndex ? maxPageIndex : pi);
retTotal = result.length;
if (page.show === true) {
return result.slice((retPi - 1) * ps, retPi * ps);
}
}
return result;
}));
}
// pre-process
if (typeof res.process === 'function') {
data$ = data$.pipe(map(result => res.process(result, rawData)));
}
data$ = data$.pipe(map(result => this.optimizeData({ result, columns, rowClassName: options.rowClassName })));
return data$.pipe(map(result => {
retList = result;
const realTotal = retTotal || total;
const realPs = retPs || ps;
return {
pi: retPi,
ps: retPs,
total: retTotal,
list: retList,
statistical: this.genStatistical(columns, retList, rawData),
pageShow: typeof showPage === 'undefined' ? realTotal > realPs : showPage
};
}));
}
get(item, col, idx) {
try {
const safeHtml = col.safeType === 'safeHtml';
if (col.format) {
const formatRes = col.format(item, col, idx) || '';
return {
text: formatRes,
_text: safeHtml ? this.dom.bypassSecurityTrustHtml(formatRes) : formatRes,
org: formatRes,
safeType: col.safeType
};
}
const value = deepGet(item, col.index, col.default);
let text = value;
let color;
let tooltip;
switch (col.type) {
case 'no':
text = this.getNoIndex(item, col, idx);
break;
case 'img':
text = value ? `<img src="${value}" class="img">` : '';
break;
case 'number':
text = this.numberPipe.transform(value, col.numberDigits);
break;
case 'currency':
text = this.currencySrv.format(value, col.currency?.format);
break;
case 'date':
text =
value == null || value === col.default || (typeof value === 'number' && value <= 0)
? col.default
: this.datePipe.transform(value, col.dateFormat);
break;
case 'yn':
text = this.ynPipe.transform(value === col.yn.truth, col.yn.yes, col.yn.no, col.yn.mode, false);
break;
case 'enum':
text = col.enum[value];
break;
case 'tag':
case 'badge': {
const data = col.type === 'tag' ? col.tag : col.badge;
if (data && data[text]) {
const dataItem = data[text];
text = dataItem.text;
color = dataItem.color;
tooltip = dataItem.tooltip;
}
else {
text = '';
}
break;
}
}
if (text == null)
text = '';
return {
text,
_text: safeHtml ? this.dom.bypassSecurityTrustHtml(text) : text,
org: value,
color,
tooltip,
safeType: col.safeType,
buttons: []
};
}
catch (ex) {
const text = `INVALID DATA`;
console.error(`Failed to get data`, item, col, ex);
return { text, _text: text, org: text, buttons: [], safeType: 'text' };
}
}
getByRemote(url, options) {
const { req, page, paginator, pi, ps, singleSort, multiSort, columns, headers } = options;
const method = (req.method || 'GET').toUpperCase();
let params = {};
const reName = req.reName;
if (paginator) {
if (req.type === 'page') {
params = {
[reName.pi]: page.zeroIndexed ? pi - 1 : pi,
[reName.ps]: ps
};
}
else {
params = {
[reName.skip]: (pi - 1) * ps,
[reName.limit]: ps
};
}
}
params = {
...params,
...req.params,
...this.getReqSortMap(singleSort, multiSort, headers),
...this.getReqFilterMap(columns)
};
if (options.req.ignoreParamNull == true) {
Object.keys(params).forEach(key => {
if (params[key] == null)
delete params[key];
});
}
let reqOptions = {
params,
body: req.body,
headers: req.headers
};
if (method === 'POST' && req.allInBody === true) {
reqOptions = {
body: { ...req.body, ...params },
headers: req.headers
};
}
if (typeof req.process === 'function') {
reqOptions = req.process(reqOptions);
}
if (!(reqOptions.params instanceof HttpParams)) {
reqOptions.params = new HttpParams({ fromObject: reqOptions.params });
}
if (typeof options.customRequest === 'function') {
return options.customRequest({ method, url, options: reqOptions });
}
return this.http.request(method, url, reqOptions);
}
getCell(c, item, idx) {
const onCellResult = typeof c.onCell === 'function' ? c.onCell(item, idx) : null;
const mergedColSpan = onCellResult?.colSpan ?? 1;
const mergedRowSpan = onCellResult?.rowSpan ?? 1;
return {
colSpan: mergedColSpan <= 0 ? null : mergedColSpan,
rowSpan: mergedRowSpan <= 0 ? null : mergedRowSpan
};
}
optimizeData(options) {
const { result, columns, rowClassName } = options;
for (let i = 0, len = result.length; i < len; i++) {
result[i]._values = columns.map(c => {
const props = this.getCell(c, result[i], i);
if (Array.isArray(c.buttons) && c.buttons.length > 0) {
return { buttons: this.genButtons(c.buttons, result[i], c), _text: '', props };
}
let cell;
if (typeof c.cell === 'function') {
cell = c.cell(result[i], c);
}
return { ...this.get(result[i], c, i), props, cell };
});
result[i]._rowClassName = [rowClassName ? rowClassName(result[i], i) : null, result[i].className]
.filter(w => !!w)
.join(' ');
}
return result;
}
getNoIndex(item, col, idx) {
return typeof col.noIndex === 'function' ? col.noIndex(item, col, idx) : col.noIndex + idx;
}
genButtons(_btns, item, col) {
const fn = (btns) => {
return deepCopy(btns).filter(btn => {
const result = typeof btn.iif === 'function' ? btn.iif(item, btn, col) : true;
const isRenderDisabled = btn.iifBehavior === 'disabled';
btn._result = result;
btn._disabled = !result && isRenderDisabled;
if (btn.children?.length) {
btn.children = fn(btn.children);
}
return result || isRenderDisabled;
});
};
const res = fn(_btns);
const fnText = (btns) => {
for (const btn of btns) {
btn._text = typeof btn.text === 'function' ? btn.text(item, btn) : btn.text || '';
btn._className = typeof btn.className === 'function' ? btn.className(item, btn) : btn.className;
btn._icon = typeof btn.icon === 'function' ? btn.icon(item, btn) : btn.icon;
if (btn.children?.length) {
btn.children = fnText(btn.children);
}
}
return btns;
};
return this.fixMaxMultiple(fnText(res), col);
}
fixMaxMultiple(btns, col) {
const curCog = col.maxMultipleButton;
const btnSize = btns.length;
if (curCog == null || btnSize <= 0)
return btns;
const cog = {
...this.cog.maxMultipleButton,
...(typeof curCog === 'number' ? { count: curCog } : curCog)
};
if (cog.count >= btnSize)
return btns;
const newBtns = btns.slice(0, cog.count);
newBtns.push({ _text: cog.text, children: btns.slice(cog.count) });
return newBtns;
}
// #region sort
getValidSort(headers) {
return headers.reduce((a, header) => {
const ls = header
.map(i => i.column)
.filter(item => item._sort && item._sort.enabled && item._sort.default)
.map(item => item._sort);
return a.concat(...ls);
}, []);
}
getSorterFn(headers) {
const sortList = this.getValidSort(headers);
if (sortList.length === 0) {
return;
}
const sortItem = sortList[0];
if (sortItem.compare === null) {
return;
}
if (typeof sortItem.compare !== 'function') {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
console.warn(`[st] Muse provide the compare function in sort`);
}
return;
}
return (a, b) => {
const result = sortItem.compare(a, b);
if (result !== 0) {
return sortItem.default === 'descend' ? -result : result;
}
return 0;
};
}
get nextSortTick() {
return ++this.sortTick;
}
getReqSortMap(singleSort, multiSort, headers) {
let ret = {};
const sortList = this.getValidSort(headers);
if (multiSort) {
const ms = {
key: 'sort',
separator: '-',
nameSeparator: '.',
keepEmptyKey: true,
arrayParam: false,
...multiSort
};
const sortMap = sortList
.sort((a, b) => a.tick - b.tick)
.map(item => item.key + ms.nameSeparator + ((item.reName || {})[item.default] || item.default));
ret = { [ms.key]: ms.arrayParam ? sortMap : sortMap.join(ms.separator) };
return sortMap.length === 0 && ms.keepEmptyKey === false ? {} : ret;
}
if (sortList.length === 0)
return ret;
const mapData = sortList[0];
let sortFiled = mapData.key;
let sortValue = (sortList[0].reName || {})[mapData.default] || mapData.default;
if (singleSort) {
sortValue = sortFiled + (singleSort.nameSeparator || '.') + sortValue;
sortFiled = singleSort.key || 'sort';
}
ret[sortFiled] = sortValue;
return ret;
}
// #endregion
// #region filter
getFilteredData(filter) {
return filter.type === 'default' ? filter.menus.filter(f => f.checked === true) : filter.menus.slice(0, 1);
}
getReqFilterMap(columns) {
let ret = {};
columns
.filter(w => w.filter && w.filter.default === true)
.forEach(col => {
const filter = col.filter;
const values = this.getFilteredData(filter);
let obj = {};
if (filter.reName) {
obj = filter.reName(filter.menus, col);
}
else {
obj[filter.key] = values.map(i => i.value).join(',');
}
ret = { ...ret, ...obj };
});
return ret;
}
// #endregion
// #region statistical
genStatistical(columns, list, rawData) {
const res = {};
columns.forEach((col, index) => {
res[col.key || col.indexKey || index] =
col.statistical == null ? {} : this.getStatistical(col, index, list, rawData);
});
return res;
}
getStatistical(col, index, list, rawData) {
const val = col.statistical;
const item = {
digits: 2,
currency: undefined,
...(typeof val === 'string' ? { type: val } : val)
};
let res = { value: 0 };
let currency = false;
if (typeof item.type === 'function') {
res = item.type(this.getValues(index, list), col, list, rawData);
currency = true;
}
else {
switch (item.type) {
case 'count':
res.value = list.length;
break;
case 'distinctCount':
res.value = this.getValues(index, list).filter((value, idx, self) => self.indexOf(value) === idx).length;
break;
case 'sum':
res.value = this.toFixed(this.getSum(index, list), item.digits);
currency = true;
break;
case 'average':
res.value = this.toFixed(this.getSum(index, list) / list.length, item.digits);
currency = true;
break;
case 'max':
res.value = Math.max(...this.getValues(index, list));
currency = true;
break;
case 'min':
res.value = Math.min(...this.getValues(index, list));
currency = true;
break;
}
}
if (item.currency === true || (item.currency == null && currency === true)) {
res.text = this.currencySrv.format(res.value, col.currency?.format);
}
else {
res.text = String(res.value);
}
return res;
}
toFixed(val, digits) {
if (isNaN(val) || !isFinite(val)) {
return 0;
}
return parseFloat(val.toFixed(digits));
}
getValues(index, list) {
return list.map(i => i._values[index].org).map(i => (i === '' || i == null ? 0 : i));
}
getSum(index, list) {
return this.getValues(index, list).reduce((p, i) => (p += parseFloat(String(i))), 0);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STDataSource, deps: [{ token: i1._HttpClient }, { token: i1.DatePipe, host: true }, { token: i1.YNPipe, host: true }, { token: i2.DecimalPipe, host: true }, { token: i3.CurrencyService }, { token: i4.DomSanitizer }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STDataSource });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STDataSource, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1._HttpClient }, { type: i1.DatePipe, decorators: [{
type: Host
}] }, { type: i1.YNPipe, decorators: [{
type: Host
}] }, { type: i2.DecimalPipe, decorators: [{
type: Host
}] }, { type: i3.CurrencyService }, { type: i4.DomSanitizer }] });
class STExport {
xlsxSrv = inject(XlsxService);
_stGet(item, col, index, colIndex) {
const ret = { t: 's', v: '' };
if (col.format) {
ret.v = col.format(item, col, index);
}
else {
const val = item._values ? item._values[colIndex].text : deepGet(item, col.index, '');
ret.v = val;
if (val != null) {
switch (col.type) {
case 'currency':
ret.t = 'n';
break;
case 'date':
// Can't be a empty value, it will cause `#NULL!`
// See https://github.com/SheetJS/sheetjs/blob/master/docbits/52_datatype.md
if (`${val}`.length > 0) {
ret.t = 'd';
// Number Formats: https://github.com/SheetJS/sheetjs/blob/master/docbits/63_numfmt.md
ret.z = col.dateFormat;
}
break;
case 'yn': {
const yn = col.yn;
ret.v = val === yn.truth ? yn.yes : yn.no;
break;
}
}
}
}
ret.v = ret.v ?? '';
return ret;
}
genSheet(opt) {
const sheets = {};
const sheet = (sheets[opt.sheetname || 'Sheet1'] = {});
const dataLen = opt.data.length;
const columns = opt.columens;
let validColCount = 0;
let wpx = false;
const invalidFn = (col) => col.exported === false || !col.index || !(!col.buttons || col.buttons.length === 0);
for (const [idx, col] of columns.entries()) {
if (invalidFn(col))
continue;
if (!wpx && col._width != null)
wpx = true;
++validColCount;
const columnName = this.xlsxSrv.numberToSchema(validColCount);
sheet[`${columnName}1`] = {
t: 's',
v: typeof col.title === 'object' ? col.title.text : col.title
};
for (let dataIdx = 0; dataIdx < dataLen; dataIdx++) {
sheet[`${columnName}${dataIdx + 2}`] = this._stGet(opt.data[dataIdx], col, dataIdx, idx);
}
}
if (wpx) {
// wpx: width in screen pixels https://github.com/SheetJS/sheetjs#column-properties
sheet['!cols'] = columns.filter(col => !invalidFn(col)).map(col => ({ wpx: col._width }));
}
if (validColCount > 0 && dataLen > 0) {
sheet['!ref'] = `A1:${this.xlsxSrv.numberToSchema(validColCount)}${dataLen + 1}`;
}
return sheets;
}
async export(opt) {
const sheets = this.genSheet(opt);
return this.xlsxSrv.export({
sheets,
filename: opt.filename,
callback: opt.callback
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STExport, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STExport });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STExport, decorators: [{
type: Injectable
}] });
class STWidgetHostDirective {
stWidgetRegistry = inject(STWidgetRegistry);
viewContainerRef = inject(ViewContainerRef);
record;
column;
ngOnInit() {
const widget = this.column.widget;
const componentType = this.stWidgetRegistry.get(widget.type);
this.viewContainerRef.clear();
const componentRef = this.viewContainerRef.createComponent(componentType);
const { record, column } = this;
const data = widget.params ? widget.params({ record, column }) : { record };
Object.keys(data).forEach(key => {
componentRef.instance[key] = data[key];
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STWidgetHostDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.3", type: STWidgetHostDirective, isStandalone: true, selector: "[st-widget-host]", inputs: { record: "record", column: "column" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STWidgetHostDirective, decorators: [{
type: Directive,
args: [{
selector: '[st-widget-host]'
}]
}], propDecorators: { record: [{
type: Input
}], column: [{
type: Input
}] } });
class STFilterComponent {
cdr = inject(ChangeDetectorRef);
visible = false;
col;
locale = {};
f;
n = new EventEmitter();
handle = new EventEmitter();
get icon() {
return this.f.icon;
}
stopPropagation($event) {
$event.stopPropagation();
}
checkboxChange() {
this.n.emit(this.f.menus?.filter(w => w.checked));
}
radioChange(item) {
this.f.menus.forEach(i => (i.checked = false));
item.checked = !item.checked;
this.n.emit(item);
}
close(result) {
if (result != null)
this.handle.emit(result);
this.visible = false;
this.cdr.detectChanges();
}
confirm() {
this.handle.emit(true);
return this;
}
reset() {
this.handle.emit(false);
return this;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: STFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: STFilterComponent, isStandalone: true, selector: "st-filter", inputs: { col: "col", locale: "locale", f: "f" }, outputs: { n: "n", handle: "handle" }, host: { properties: { "class.ant-table-filter-trigger-container": "true", "class.st__filter": "true", "class.ant-table-filter-trigger-container-open": "visible" } }, ngImport: i0, template: `
<span
class="ant-table-filter-trigger"
[class.active]="visible || f.default"
nz-dropdown
[nzDropdownMenu]="filterMenu"
nzTrigger="click"
[nzClickHide]="false"
[(nzVisible)]="visible"
nzOverlayClassName="st__filter-wrap"
(click)="stopPropagation($event)"
>
<nz-icon [nzType]="icon.type" [nzTheme]="icon.theme!" />
</span>
<nz-dropdown-menu #filterMenu="nzDropdownMenu">
<div class="ant-table-filter-dropdown">
@switch (f.type) {
@case ('keyword') {
<div class="st__filter-keyword">
<input
type="text"
nz-input
[attr.placeholder]="f.placeholder"
[(ngModel)]="f.menus![0]!.value"
(ngModelChange)="n.emit($event)"
(keyup.enter)="confirm()"
/>
</div>
}
@case ('number') {
<div class="p-sm st__filter-number">
<nz-input-number
[(ngModel)]="f.menus![0]!.value"
(ngModelChange)="n.emit($event)"
[nzMin]="f.number!.min!"
[nzMax]="f.number!.max!"
[nzStep]="f.number!.step!"
[nzPrecision]="f.number!.precision || null"