212 lines
7.2 KiB
TypeScript
212 lines
7.2 KiB
TypeScript
import type { FieldsInfo, FieldsJoinMapping, JoinTable, WhereCondition } from '@/types/model';
|
||
import {
|
||
client,
|
||
isType,
|
||
type FieldType,
|
||
type App,
|
||
type Layout,
|
||
type OneOf,
|
||
type Properties,
|
||
} from './kintone-rest-api-client';
|
||
import type { ComboboxItem, DropdownItem } from 'kintone-ui-component';
|
||
import { isSpecialType, type SpecialType } from './join';
|
||
|
||
export const EMPTY_OPTION = {
|
||
value: '',
|
||
label: '--------',
|
||
} as DropdownItem;
|
||
|
||
export function generateId(): string {
|
||
const timestamp = new Date().getTime().toString(36);
|
||
const randomNum = Math.random().toString(36).substring(2, 11);
|
||
return `${timestamp}-${randomNum}`;
|
||
}
|
||
|
||
export function search(list: Array<WhereCondition | FieldsJoinMapping>, id: string) {
|
||
if (!list) return;
|
||
return list.find((item) => item.id === id);
|
||
}
|
||
|
||
export const getEmptyWhereCondition = () =>
|
||
({ field: '', condition: '', data: '', id: generateId() }) as WhereCondition;
|
||
export const getEmptyOnCondition = () => ({ leftField: '', rightField: '', id: generateId() }) as FieldsJoinMapping;
|
||
export const getEmptyFieldsMapping = () => ({ leftField: '', rightField: '', id: generateId() }) as FieldsJoinMapping;
|
||
|
||
export function createEmptyJoinTable(id = generateId()) {
|
||
return resetTable({ id, app: '' } as JoinTable);
|
||
}
|
||
|
||
export function resetTable(table: JoinTable) {
|
||
table.table = '';
|
||
return resetConditions(table);
|
||
}
|
||
|
||
export function resetConditions(table: JoinTable) {
|
||
table.onConditions = [getEmptyOnCondition()];
|
||
table.fieldsMapping = [getEmptyFieldsMapping()];
|
||
table.whereConditions = [getEmptyWhereCondition()];
|
||
return table;
|
||
}
|
||
|
||
const LIMIT = 100; // 毎回請求の最大値
|
||
export const loadApps = async (offset = 0, _apps: DropdownItem[] = []): Promise<DropdownItem[]> => {
|
||
const { apps } = await client.app.getApps({ limit: LIMIT, offset });
|
||
const allApps: DropdownItem[] = [
|
||
..._apps,
|
||
...apps.map((app: App) => ({ value: app.appId, label: app.name + '(ID: ' + app.appId + ')' })),
|
||
];
|
||
if (apps.length === LIMIT) {
|
||
return loadApps(offset + LIMIT, allApps);
|
||
}
|
||
allApps.sort((a, b) => Number(b.value) - Number(a.value));
|
||
allApps.unshift(EMPTY_OPTION);
|
||
return allApps;
|
||
};
|
||
|
||
export const loadAppFieldsAndLayout = async (appId: string | number = kintone.app.getId() as number) => {
|
||
const fields = (await client.app.getFormFields({ app: appId })).properties;
|
||
return {
|
||
fields: flatFields(fields),
|
||
layout: (await client.app.getFormLayout({ app: appId })).layout,
|
||
} as FieldsInfo;
|
||
};
|
||
|
||
function flatFields(fields: Properties) {
|
||
const subtableFields = {} as Properties;
|
||
Object.values(fields).forEach((field) => {
|
||
if (isType.SUBTABLE(field)) {
|
||
Object.values(field.fields).forEach((subField) => {
|
||
const copy = JSON.parse(JSON.stringify(subField)) as typeof subField & {originLabel:string, tableCode:string};
|
||
copy.label = '[' + field.label + '].' + subField.label;
|
||
copy.originLabel = subField.label;
|
||
copy.tableCode = field.code;
|
||
subtableFields[subField.code] = copy;
|
||
});
|
||
}
|
||
});
|
||
return { ...fields, ...subtableFields };
|
||
}
|
||
|
||
type FilterType = Array<FieldType | SpecialType>;
|
||
type Param = {
|
||
subTableCode: string | undefined;
|
||
filterType?: FilterType;
|
||
baseFilter: FieldType[] | undefined;
|
||
dependFilterField?: OneOf;
|
||
defaultLabel?: string;
|
||
defaultDisableCallback?: (field: OneOf) => boolean;
|
||
needAllSubTableField?: boolean;
|
||
};
|
||
export const getFieldsDropdownItems = (
|
||
{ fields, layout }: FieldsInfo,
|
||
{
|
||
subTableCode, // specified subTable
|
||
baseFilter, // set not allowed items hidden, undefined means no filter
|
||
defaultLabel, // label shown (default '--------')
|
||
filterType, // set not allowed items disabled, undefined and [] means no filter
|
||
dependFilterField, // used for filterType
|
||
defaultDisableCallback, // callback to control disabled items, like filterType
|
||
needAllSubTableField = false, // show all subtable fields
|
||
}: Param,
|
||
) => {
|
||
// get used field codes
|
||
const fieldOrder = extractFields(layout, baseFilter, !!needAllSubTableField, subTableCode);
|
||
const fieldMap = fields;
|
||
|
||
// create labels
|
||
const labels: ComboboxItem[] = [
|
||
{
|
||
value: EMPTY_OPTION.value,
|
||
label: defaultLabel || EMPTY_OPTION.label,
|
||
},
|
||
];
|
||
return fieldOrder.reduce((acc, fieldCode) => {
|
||
const field = fieldMap[fieldCode];
|
||
if (!fieldCode) return acc;
|
||
acc.push({
|
||
value: fieldCode,
|
||
label: field.label + '(FC: ' + fieldCode + ')',
|
||
disabled:
|
||
(defaultDisableCallback && defaultDisableCallback(field)) ||
|
||
(filterType && !checkFilterType(field, dependFilterField, filterType)),
|
||
});
|
||
return acc;
|
||
}, labels);
|
||
};
|
||
|
||
const checkFilterType = (field: OneOf, dependFilterField: OneOf | undefined, filterType: FilterType) => {
|
||
if (!filterType.length) return true; // [] means no filter
|
||
return !!filterType.find((type) => {
|
||
if (isSpecialType(type)) {
|
||
return type.check(field, dependFilterField);
|
||
}
|
||
return isType[type](field);
|
||
});
|
||
};
|
||
|
||
export const getTableFieldsDropdownItems = ({ fields }: FieldsInfo, filterType?: FieldType) => {
|
||
return Object.keys(fields).reduce(
|
||
(acc, fieldCode) => {
|
||
const field = fields[fieldCode];
|
||
if (filterType && !isType[filterType](field)) return acc;
|
||
acc.push({
|
||
value: fieldCode,
|
||
label: field.label + '(FC: ' + fieldCode + ')',
|
||
});
|
||
return acc;
|
||
},
|
||
[EMPTY_OPTION],
|
||
);
|
||
};
|
||
|
||
const extractFields = (layout: Layout, baseFilter: FieldType[] | undefined, needAllSubTableField: boolean, subTableCode?: string) => {
|
||
return layout.reduce((acc, each) => {
|
||
if (each.type === 'GROUP') {
|
||
acc.push(...extractFields(each.layout, baseFilter, needAllSubTableField, subTableCode));
|
||
} else if (each.type === 'ROW' || (!needAllSubTableField && each.code === subTableCode) || (needAllSubTableField && each.type === 'SUBTABLE')) {
|
||
acc.push(
|
||
...each.fields.map((field) => {
|
||
if (!('code' in field)) return '';
|
||
if (!baseFilter) return field.code;
|
||
return baseFilter.find((t) => t === field.type) ? field?.code || '' : '';
|
||
}),
|
||
);
|
||
}
|
||
return acc;
|
||
}, [] as string[]);
|
||
};
|
||
|
||
export function getFieldObj(fieldCode: string, { fields }: FieldsInfo, subTableCode?: string) {
|
||
const meta = getMeta(fields, subTableCode);
|
||
return meta[fieldCode];
|
||
}
|
||
|
||
export function getMeta(fields: Properties, subTableCode?: string, withNoSubTableField = true) {
|
||
if (!fields || !subTableCode) {
|
||
return fields;
|
||
}
|
||
let meta = fields;
|
||
const table = meta[subTableCode];
|
||
if (isType.SUBTABLE(table)) {
|
||
const subFields = table.fields;
|
||
Object.values(subFields).forEach((field) => {
|
||
if (typeof field === 'object' && field !== null) {
|
||
(field as Record<string, any>).subField = true;
|
||
}
|
||
});
|
||
if (withNoSubTableField) {
|
||
meta = { ...fields, ...subFields };
|
||
} else {
|
||
meta = subFields;
|
||
}
|
||
}
|
||
return meta;
|
||
}
|
||
|
||
export const isStringArray = (value: any) => {
|
||
if (Array.isArray(value) && value.every((x) => typeof x === 'string')) {
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|