hsueh chiahao
2025-10-13 11:14:10 +08:00
commit cea4d4eea1
57 changed files with 20980 additions and 0 deletions

211
src/js/helper.ts Normal file
View File

@@ -0,0 +1,211 @@
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;
};