This commit is contained in:
166
src/components/Config.vue
Normal file
166
src/components/Config.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<h2 class="settings-heading">{{ $t('config.title') }}</h2>
|
||||
<p class="kintoneplugin-desc">{{ $t('config.desc') }}</p>
|
||||
|
||||
<plugin-row class="header-row border">
|
||||
<plugin-input v-model="data.buttonName" placeholder="ボタン名を入力してください" label="集約ボタン名" />
|
||||
</plugin-row>
|
||||
|
||||
<div id="main-area" ref="mainArea">
|
||||
<plugin-table-area v-for="joinTable in data.joinTables" :table="joinTable" :key="joinTable.id" />
|
||||
</div>
|
||||
|
||||
<plugin-row class="footer-row border">
|
||||
<kuc-button text="キャンセル" type="normal" @click="cancel" />
|
||||
<kuc-button :disabled="!canSave" text="保存する" class="save-btn" type="submit" @click="save" />
|
||||
</plugin-row>
|
||||
|
||||
<kuc-spinner :container="mainArea" ref="spinner"></kuc-spinner>
|
||||
<error-dialog :message="errMessage" :show="showError" @update:show="(value) => showError = value"></error-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
createEmptyJoinTable,
|
||||
loadApps,
|
||||
loadAppFieldsAndLayout,
|
||||
EMPTY_OPTION,
|
||||
getEmptyOnCondition,
|
||||
getMeta,
|
||||
} from '@/js/helper';
|
||||
import { isType, type OneOf, type Properties } from '@/js/kintone-rest-api-client';
|
||||
import type { CachedData, FieldsInfo, JoinTable, SavedData } from '@/types/model';
|
||||
import type { Spinner } from 'kintone-ui-component';
|
||||
|
||||
import { onMounted, watch, provide, reactive, ref, shallowRef, nextTick } from 'vue';
|
||||
|
||||
const props = defineProps<{ pluginId: string }>();
|
||||
const loading = ref(false);
|
||||
const canSave = ref(true);
|
||||
const data: SavedData = reactive({
|
||||
buttonName: '',
|
||||
joinTables: [createEmptyJoinTable()],
|
||||
});
|
||||
const showError = ref(false);
|
||||
const errMessage = ref("");
|
||||
|
||||
const cachedData: CachedData = reactive({
|
||||
apps: [EMPTY_OPTION],
|
||||
currentAppFields: { fields: {}, layout: [] } as FieldsInfo,
|
||||
});
|
||||
|
||||
provide('savedData', data);
|
||||
provide('canSave', (data: boolean) => {
|
||||
canSave.value = data;
|
||||
});
|
||||
provide('cachedData', cachedData);
|
||||
|
||||
const mainArea = shallowRef<HTMLElement | null>(null);
|
||||
const spinner = shallowRef<Spinner | null>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
nextTick(async () => {
|
||||
spinner.value?.close(); // fix bug: kuc-spinner will not auto amount to target HTML element when init loading
|
||||
const savedData = kintone.plugin.app.getConfig(props.pluginId);
|
||||
loading.value = true;
|
||||
cachedData.apps = await loadApps();
|
||||
cachedData.currentAppFields = await loadAppFieldsAndLayout();
|
||||
if (savedData?.joinTablesForConfig) {
|
||||
const newJoinTables = JSON.parse(savedData.joinTablesForConfig);
|
||||
data.joinTables.length = 0;
|
||||
data.joinTables.push(...newJoinTables);
|
||||
}
|
||||
data.buttonName = savedData?.buttonName || '集約';
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
|
||||
watch(loading, (load) => {
|
||||
load ? spinner.value?.open() : spinner.value?.close();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => data.joinTables.length,
|
||||
(newLength) => {
|
||||
console.log(data.joinTables);
|
||||
if (newLength === 1) {
|
||||
data.joinTables[0].onConditions = [getEmptyOnCondition()];
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 保存データのバリデーション関数
|
||||
*/
|
||||
function validate(data: SavedData<string>): boolean {
|
||||
// ボタン名が空の場合、エラーを表示
|
||||
if (!data.buttonName.trim()) {
|
||||
errMessage.value = 'ボタン名を入力してください。';
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const joinTable of data.joinTables) {
|
||||
// 取得元アプリが空の場合、エラーを表示
|
||||
if (!joinTable.app.trim()) {
|
||||
errMessage.value = '取得元アプリを指定してください。';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 取得フィールドのマッピングが1つ未満の場合、エラーを表示
|
||||
if (
|
||||
joinTable.fieldsMapping.length < 1 ||
|
||||
!joinTable.fieldsMapping[0].leftField?.trim() ||
|
||||
!joinTable.fieldsMapping[0].rightField?.trim()
|
||||
) {
|
||||
errMessage.value = '取得フィールドを1つ以上設定してください。';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function save() {
|
||||
if(!validate(data)){
|
||||
showError.value=true;
|
||||
return;
|
||||
}
|
||||
const currentAppMeta = cachedData.currentAppFields.fields;
|
||||
const convertJoinTables = JSON.parse(JSON.stringify(data.joinTables)) as JoinTable<OneOf | string>[];
|
||||
convertJoinTables.forEach((item) => {
|
||||
const meta = getMeta(item.meta as Properties, item.table);
|
||||
if (!meta) return;
|
||||
|
||||
// Process onConditions
|
||||
item.onConditions.forEach((condition) => {
|
||||
condition.leftField = meta[condition.leftField as string] || condition.leftField;
|
||||
condition.rightField = currentAppMeta[condition.rightField as string] || condition.rightField;
|
||||
});
|
||||
|
||||
// Process fieldsMapping
|
||||
item.fieldsMapping.forEach((mapping) => {
|
||||
mapping.leftField = meta[mapping.leftField as string] || mapping.leftField;
|
||||
mapping.rightField = currentAppMeta[mapping.rightField as string] || mapping.rightField;
|
||||
});
|
||||
|
||||
// Process whereConditions
|
||||
item.whereConditions.forEach((condition) => {
|
||||
condition.field = meta[condition.field as string] || condition.field;
|
||||
});
|
||||
delete item.meta;
|
||||
});
|
||||
|
||||
data.joinTables.forEach((item) => {
|
||||
delete item.meta;
|
||||
});
|
||||
|
||||
kintone.plugin.app.setConfig({
|
||||
buttonName: data.buttonName,
|
||||
joinTables: JSON.stringify(convertJoinTables),
|
||||
joinTablesForConfig: JSON.stringify(data.joinTables || []),
|
||||
});
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
window.location.href = `../../${kintone.app.getId()}/plugin/`;
|
||||
}
|
||||
</script>
|
||||
79
src/components/basic/ErrorDialog.vue
Normal file
79
src/components/basic/ErrorDialog.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
<template>
|
||||
<div ref="dialogContainer">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, Dialog } from "kintone-ui-component";
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
message: string;
|
||||
show: boolean;
|
||||
}>();
|
||||
|
||||
const isDialogVisible = ref(props.show);
|
||||
|
||||
watch(
|
||||
() => props.message,
|
||||
(newMessage) => {
|
||||
if (dialog.value) {
|
||||
dialog.value.content=newMessage;
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.show,
|
||||
(newValue) => {
|
||||
if (dialog.value) {
|
||||
if (newValue) {
|
||||
dialog.value.open();
|
||||
} else {
|
||||
dialog.value.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(["update:show"]);
|
||||
|
||||
const dialog = ref<Dialog | null>(null);
|
||||
const dialogContainer = ref<HTMLDivElement | null>(null);
|
||||
|
||||
const closeDialog = () => {
|
||||
if (dialog.value) {
|
||||
dialog.value.close();
|
||||
}
|
||||
emit("update:show", false);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!dialogContainer.value) return;
|
||||
|
||||
const okButton = new Button({ text: "OK", type: "normal" });
|
||||
okButton.addEventListener("click", closeDialog);
|
||||
|
||||
const footerDiv = document.createElement("div");
|
||||
footerDiv.className="dialog-action-bar";
|
||||
footerDiv.appendChild(okButton);
|
||||
|
||||
// 创建 Dialog 实例
|
||||
dialog.value = new Dialog({
|
||||
header: "エラー情報",
|
||||
content: props.message,
|
||||
icon: "error",
|
||||
container: dialogContainer.value,
|
||||
footer: footerDiv,
|
||||
footerVisible:true
|
||||
});
|
||||
|
||||
if (props.show) {
|
||||
dialog.value.open();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
27
src/components/basic/PluginDropdown.vue
Normal file
27
src/components/basic/PluginDropdown.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="kintoneplugin-input-container flex-row">
|
||||
<plugin-label v-if="label" :label="label" />
|
||||
<kuc-combobox className="kuc-text-input" :items="items" :value="modelValue" @change="updateValue" :disabled="disabled"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import type { ComboboxChangeEventDetail, DropdownItem } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
label: string;
|
||||
disabled: boolean;
|
||||
items: DropdownItem[];
|
||||
modelValue: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
const updateValue = ({ detail }: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
emit('update:modelValue', detail.value || '');
|
||||
};
|
||||
</script>
|
||||
26
src/components/basic/PluginInput.vue
Normal file
26
src/components/basic/PluginInput.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="kintoneplugin-input-container flex-row">
|
||||
<plugin-label v-if="label" :label="label" />
|
||||
<kuc-text :placeholder="placeholder" className="kuc-text-input" :value="modelValue" @change="updateValue" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import type { TextInputEventDetail } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
label: string;
|
||||
modelValue: string;
|
||||
placeholder: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
const updateValue = ({ detail }: KucEvent<TextInputEventDetail>) => {
|
||||
emit('update:modelValue', detail.value || '');
|
||||
};
|
||||
</script>
|
||||
11
src/components/basic/PluginLabel.vue
Normal file
11
src/components/basic/PluginLabel.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div class="kintoneplugin-label">{{ label }}</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
label: string;
|
||||
}>();
|
||||
</script>
|
||||
7
src/components/basic/PluginRow.vue
Normal file
7
src/components/basic/PluginRow.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="kintoneplugin-row">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
23
src/components/basic/PluginTableActionIcon.vue
Normal file
23
src/components/basic/PluginTableActionIcon.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<button :class="['kuc-action-button', isAdd ? 'add' : 'remove']" :title="title">
|
||||
<svg fill="none" width="18" height="18" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path :d="dPath" fill-rule="evenodd" clip-rule="evenodd" :fill="fillPath"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{ isAdd: boolean; title: string }>();
|
||||
|
||||
const dAdd =
|
||||
'M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM12.0355 8.49997V7.49997H8.50008V3.96454H7.50008V7.49997H3.96443V8.49997H7.50008V12.0356H8.50008V8.49997H12.0355Z';
|
||||
const fillAdd = '#3498db';
|
||||
const dRemove =
|
||||
'M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM12.0355 7.49997V8.49997L3.96443 8.49997V7.49997H12.0355Z';
|
||||
const fillRemove = '#b5b5b5';
|
||||
|
||||
const dPath = computed(() => (props.isAdd ? dAdd : dRemove));
|
||||
const fillPath = computed(() => (props.isAdd ? fillAdd : fillRemove));
|
||||
</script>
|
||||
38
src/components/basic/PluginTableActionIconGroup.vue
Normal file
38
src/components/basic/PluginTableActionIconGroup.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<plugin-table-action-icon v-if="canAdd" :is-add="true" title="Add Row" @click="onClick('add')" />
|
||||
<plugin-table-action-icon v-if="canDelete" :is-add="false" title="Delete this row" @click="onClick('remove')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { createEmptyJoinTable } from '@/js/helper';
|
||||
import type { SavedData } from '@/types/model';
|
||||
import { defineProps, withDefaults, inject } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
canAdd?: boolean;
|
||||
canDelete?: boolean;
|
||||
tableId: string;
|
||||
}>(),
|
||||
{
|
||||
canAdd: true,
|
||||
canDelete: true,
|
||||
},
|
||||
);
|
||||
|
||||
const savedData = inject<SavedData>('savedData') as SavedData;
|
||||
const onClick = (type: 'add' | 'remove') => {
|
||||
if (type === 'add') {
|
||||
const currentIndex = savedData.joinTables.findIndex((t) => t.id === props.tableId);
|
||||
if (currentIndex !== -1) {
|
||||
savedData.joinTables.splice(currentIndex + 1, 0, createEmptyJoinTable());
|
||||
} else {
|
||||
savedData.joinTables.push(createEmptyJoinTable());
|
||||
}
|
||||
} else if (savedData.joinTables.length > 1) {
|
||||
savedData.joinTables = savedData.joinTables.filter((t) => t.id !== props.tableId);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
110
src/components/basic/PluginTableArea.vue
Normal file
110
src/components/basic/PluginTableArea.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<plugin-row class="table-area flex-row border">
|
||||
<div class="table-main-area">
|
||||
<plugin-row>
|
||||
<plugin-dropdown :disabled="false" label="取得元アプリ" :items="apps" v-model="table.app" />
|
||||
</plugin-row>
|
||||
<plugin-row>
|
||||
<plugin-dropdown
|
||||
:disabled="selectedAppData.loading"
|
||||
label="テーブル"
|
||||
:items="tableOptions"
|
||||
v-model="table.table"
|
||||
@change="selectTable"
|
||||
:key="componentKey"
|
||||
/>
|
||||
</plugin-row>
|
||||
<plugin-row class="flex-row" v-if="isJoinConditionShown(table)">
|
||||
<plugin-label label="連結条件" />
|
||||
<plugin-table-connect-row connector="=" type="connect" :modelValue="table.onConditions" />
|
||||
</plugin-row>
|
||||
<plugin-row class="flex-row">
|
||||
<plugin-label label="取得フィールド" />
|
||||
<plugin-table-connect-row connector="→" type="mapping" :modelValue="table.fieldsMapping" />
|
||||
</plugin-row>
|
||||
<plugin-row class="flex-row">
|
||||
<plugin-label label="絞込条件" />
|
||||
<plugin-table-condition-row :modelValue="table.whereConditions" :table="table"
|
||||
@update:modelValue="(newData:any) => table.whereConditions = newData"/>
|
||||
</plugin-row>
|
||||
</div>
|
||||
<div class="table-action-area">
|
||||
<plugin-table-action-icon-group :can-delete="savedData.joinTables.length > 1" :table-id="table.id" />
|
||||
</div>
|
||||
</plugin-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
EMPTY_OPTION,
|
||||
getTableFieldsDropdownItems,
|
||||
loadAppFieldsAndLayout,
|
||||
resetConditions,
|
||||
resetTable,
|
||||
} from '@/js/helper';
|
||||
import { types } from '@/js/kintone-rest-api-client';
|
||||
import type { CachedData, CachedSelectedAppData, FieldsInfo, JoinTable, SavedData } from '@/types/model';
|
||||
import { computed, inject, provide, reactive, ref, watch } from 'vue';
|
||||
|
||||
const savedData = inject<SavedData>('savedData') as SavedData;
|
||||
const cachedData = inject<CachedData>('cachedData') as CachedData;
|
||||
|
||||
const props = defineProps<{
|
||||
table: JoinTable;
|
||||
}>();
|
||||
const apps = computed(() => cachedData.apps);
|
||||
const tableOptions = ref([EMPTY_OPTION]);
|
||||
const loading = ref(false);
|
||||
|
||||
const selectedAppData: CachedSelectedAppData = reactive({
|
||||
appFields: { fields: {}, layout: [] } as FieldsInfo,
|
||||
table: props.table,
|
||||
loading: false,
|
||||
});
|
||||
provide('selectedAppData', selectedAppData);
|
||||
|
||||
const componentKey = ref(0);
|
||||
// fix-bug: force select saved data when load config
|
||||
watch(
|
||||
() => tableOptions.value,
|
||||
() => {
|
||||
if (!props.table.table) return;
|
||||
componentKey.value += 1;
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.table.app,
|
||||
async (newVal, oldVal) => {
|
||||
if (!newVal) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
const fields = await loadAppFieldsAndLayout(newVal);
|
||||
tableOptions.value = getTableFieldsDropdownItems(fields, types.SUBTABLE);
|
||||
selectedAppData.appFields = fields;
|
||||
props.table.meta = fields.fields;
|
||||
!!oldVal && resetTable(props.table);
|
||||
loading.value = false;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const selectTable = () => {
|
||||
resetConditions(props.table);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => !props.table.app || loading.value,
|
||||
(val) => {
|
||||
selectedAppData.loading = val;
|
||||
},
|
||||
);
|
||||
|
||||
const isJoinConditionShown = (table: JoinTable) => {
|
||||
return savedData.joinTables[0].id !== table.id;
|
||||
};
|
||||
</script>
|
||||
123
src/components/basic/PluginTableConditionRow.vue
Normal file
123
src/components/basic/PluginTableConditionRow.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<kuc-table className='plugin-kuc-table condition-table'
|
||||
:columns="columns"
|
||||
:data="modelValue"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { CachedData, CachedSelectedAppData, JoinTable, SavedData, WhereCondition } from '@/types/model';
|
||||
import { defineProps, inject, computed, render, h, reactive, watch } from 'vue';
|
||||
import TableCombobox from './TableCombobox.vue';
|
||||
import { generateId, getFieldsDropdownItems, search } from '@/js/helper';
|
||||
import TableCondition from './conditions/TableCondition.vue';
|
||||
import TableConditionValue from './conditions/TableConditionValue.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: WhereCondition[];
|
||||
table:JoinTable;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: WhereCondition[]): void;
|
||||
}>();
|
||||
|
||||
const savedData = inject<SavedData>('savedData') as SavedData;
|
||||
const cachedData = inject<CachedData>('cachedData') as CachedData;
|
||||
const selectedAppData = inject<CachedSelectedAppData>('selectedAppData') as CachedSelectedAppData;
|
||||
// const table = computed(() => selectedAppData.table.table);
|
||||
|
||||
const canSave = inject<(canSave: boolean) => void>('canSave') as (canSave: boolean) => void;
|
||||
|
||||
watch(
|
||||
()=>props.modelValue,
|
||||
(newValue,oldValue)=>{
|
||||
console.log(newValue);
|
||||
console.log(oldValue);
|
||||
},{
|
||||
deep:true,
|
||||
immediate:true
|
||||
}
|
||||
)
|
||||
|
||||
const columns = reactive([
|
||||
{
|
||||
title: '取得元アプリのフィールド',
|
||||
field: 'field',
|
||||
render: (cellData: string, rowData: WhereCondition) => {
|
||||
if (!rowData.id) {
|
||||
rowData.id = generateId();
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
const vnode = h(TableCombobox, {
|
||||
items: computed(() =>
|
||||
getFieldsDropdownItems(selectedAppData.appFields, {
|
||||
subTableCode: '', //table.value,
|
||||
baseFilter: undefined,
|
||||
defaultLabel: 'すべてのレコード',
|
||||
needAllSubTableField: true,
|
||||
}),
|
||||
),
|
||||
modelValue: computed(() => (search(props.modelValue, rowData.id) as WhereCondition)?.field || ''),
|
||||
selectedAppData,
|
||||
dataList: props.modelValue,
|
||||
id: rowData.id,
|
||||
'onUpdate:modelValue': (data) => {
|
||||
const obj = (data.obj as WhereCondition);
|
||||
if (obj) {
|
||||
obj.field = data.value;
|
||||
obj.condition = '',
|
||||
obj.data = '';
|
||||
}
|
||||
},
|
||||
});
|
||||
render(vnode, container);
|
||||
return container;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
field: 'condition',
|
||||
render: (cellData: string, rowData: WhereCondition) => {
|
||||
const container = document.createElement('div');
|
||||
const vnode = h(TableCondition, {
|
||||
modelValue: computed(() => (search(props.modelValue, rowData.id) as WhereCondition)?.condition || ''),
|
||||
selectedAppData,
|
||||
id: rowData.id,
|
||||
whereConditions: props.modelValue,
|
||||
'onUpdate:modelValue': ({obj, value}) => {
|
||||
obj && (obj.condition = value);
|
||||
},
|
||||
});
|
||||
render(vnode, container);
|
||||
return container;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
field: 'data',
|
||||
render: (cellData: string, rowData: WhereCondition) => {
|
||||
const container = document.createElement('div');
|
||||
const vnode = h(TableConditionValue, {
|
||||
modelValue: computed(() => (search(props.modelValue, rowData.id) as WhereCondition)?.data || ''),
|
||||
selectedAppData,
|
||||
canSave,
|
||||
id: rowData.id,
|
||||
whereConditions: props.modelValue,
|
||||
'onUpdate:modelValue': ({obj, value}) => {
|
||||
if(obj){
|
||||
obj.data = value;
|
||||
const newData = props.modelValue.map((item) =>
|
||||
item.id === obj.id ? { ...item, data: value } : item
|
||||
);
|
||||
emit('update:modelValue', newData);
|
||||
}
|
||||
},
|
||||
});
|
||||
render(vnode, container);
|
||||
return container;
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
</script>
|
||||
118
src/components/basic/PluginTableConnectRow.vue
Normal file
118
src/components/basic/PluginTableConnectRow.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<kuc-table className='plugin-kuc-table' :columns="columns" :data="modelValue"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { CachedData, CachedSelectedAppData, FieldsJoinMapping, WhereCondition } from '@/types/model';
|
||||
import { defineProps, inject, computed, reactive, render, h } from 'vue';
|
||||
import { generateId, getFieldObj, getFieldsDropdownItems, search } from '@/js/helper';
|
||||
import { getLeftAvailableJoinType, getRightAvailableJoinType, isLeftJoinForceDisable, isRightJoinForceDisable, } from '@/js/join';
|
||||
import { getLeftAvailableMappingType, getRightAvailableMappingType } from '@/js/mapping';
|
||||
import TableCombobox from './TableCombobox.vue';
|
||||
import { type FieldType, type OneOf } from '@/js/kintone-rest-api-client';
|
||||
|
||||
const props = defineProps<{
|
||||
connector: string;
|
||||
modelValue: FieldsJoinMapping[];
|
||||
type: 'connect' | 'mapping';
|
||||
}>();
|
||||
|
||||
const cachedData = inject<CachedData>('cachedData') as CachedData;
|
||||
const selectedAppData = inject<CachedSelectedAppData>('selectedAppData') as CachedSelectedAppData;
|
||||
const table = computed(() => selectedAppData.table.table);
|
||||
|
||||
const filterFunc = {
|
||||
connect: {
|
||||
left: (right?: OneOf | '') => getLeftAvailableJoinType(right),
|
||||
right: (left?: OneOf | '') => getRightAvailableJoinType(left),
|
||||
},
|
||||
mapping: {
|
||||
left: (right?: OneOf | '') => getLeftAvailableMappingType(right),
|
||||
right: (left?: OneOf | '') => getRightAvailableMappingType(left),
|
||||
},
|
||||
};
|
||||
|
||||
const columns = reactive([
|
||||
{
|
||||
title: '取得元アプリのフィールド',
|
||||
field: 'leftField',
|
||||
render: (cellData: string, rowData: WhereCondition) => {
|
||||
if (!rowData.id) {
|
||||
rowData.id = generateId();
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
const vnode = h(TableCombobox, {
|
||||
items: computed(() => {
|
||||
const dependFilterField = getField('rightField', rowData.id);
|
||||
return getFieldsDropdownItems(selectedAppData.appFields, {
|
||||
subTableCode: table.value,
|
||||
baseFilter: filterFunc[props.type].left() as FieldType[],
|
||||
filterType: filterFunc[props.type].left(dependFilterField),
|
||||
dependFilterField,
|
||||
defaultDisableCallback: isLeftJoinForceDisable,
|
||||
});
|
||||
}),
|
||||
modelValue: computed(() => (search(props.modelValue, rowData.id) as FieldsJoinMapping)?.leftField || ''),
|
||||
selectedAppData,
|
||||
dataList: props.modelValue,
|
||||
id: rowData.id,
|
||||
'onUpdate:modelValue': (data) => {
|
||||
if (data.obj) {
|
||||
(data.obj as FieldsJoinMapping).leftField = data.value;
|
||||
}
|
||||
},
|
||||
});
|
||||
render(vnode, container);
|
||||
return container;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
field: 'connector',
|
||||
render: () => {
|
||||
return props.connector;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'このアプリのフィールド',
|
||||
field: 'rightField',
|
||||
render: (cellData: string, rowData: WhereCondition) => {
|
||||
if (!rowData.id) {
|
||||
rowData.id = generateId();
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
const vnode = h(TableCombobox, {
|
||||
items: computed(() => {
|
||||
const dependFilterField = getField('leftField', rowData.id);
|
||||
return getFieldsDropdownItems(cachedData.currentAppFields, {
|
||||
subTableCode: '', // subtable not allowed for current app
|
||||
baseFilter: filterFunc[props.type].right() as FieldType[],
|
||||
filterType: filterFunc[props.type].right(dependFilterField),
|
||||
dependFilterField,
|
||||
defaultDisableCallback: props.type === 'connect' ? isRightJoinForceDisable : undefined,
|
||||
});
|
||||
}),
|
||||
modelValue: computed(() => (search(props.modelValue, rowData.id) as FieldsJoinMapping)?.rightField || ''),
|
||||
selectedAppData,
|
||||
dataList: props.modelValue,
|
||||
id: rowData.id,
|
||||
'onUpdate:modelValue': (data) => {
|
||||
if (data.obj) {
|
||||
(data.obj as FieldsJoinMapping).rightField = data.value;
|
||||
}
|
||||
},
|
||||
});
|
||||
render(vnode, container);
|
||||
return container;
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
function getField(key: 'leftField' | 'rightField', id: string) {
|
||||
const dataRow = search(props.modelValue, id) as FieldsJoinMapping | undefined;
|
||||
const fieldCode = dataRow ? dataRow[key] || '' : '';
|
||||
const targetFieldMap = key === 'leftField' ? selectedAppData.appFields : cachedData.currentAppFields;
|
||||
const targetTable = key === 'leftField' ? table.value : '';
|
||||
return getFieldObj(fieldCode, targetFieldMap, targetTable);
|
||||
}
|
||||
</script>
|
||||
52
src/components/basic/TableCombobox.vue
Normal file
52
src/components/basic/TableCombobox.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<kuc-combobox
|
||||
className="kuc-text-input"
|
||||
:items="items.value"
|
||||
:value="modelValue.value"
|
||||
@change="updateValue"
|
||||
:key="componentKey"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { search } from '@/js/helper';
|
||||
import type { CachedSelectedAppData } from '@/types/model';
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import type { ComboboxChangeEventDetail, DropdownItem } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits, type Ref, watch, ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
items: Ref<DropdownItem[]>;
|
||||
modelValue: Ref<string>;
|
||||
dataList: any[];
|
||||
id: string;
|
||||
selectedAppData: CachedSelectedAppData;
|
||||
}>();
|
||||
|
||||
const componentKey = ref(0);
|
||||
// fix-bug: force select saved data when load config
|
||||
watch(
|
||||
() => props.items.value,
|
||||
() => {
|
||||
if (!props.modelValue.value) return;
|
||||
componentKey.value += 1;
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
},
|
||||
);
|
||||
|
||||
type EmitData = {
|
||||
obj?: any;
|
||||
value: string;
|
||||
};
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', data: EmitData): void;
|
||||
}>();
|
||||
|
||||
const updateValue = ({ detail }: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
emit('update:modelValue', { obj: search(props.dataList, props.id), value: detail.value || '' });
|
||||
};
|
||||
</script>
|
||||
22
src/components/basic/conditions/CellInput.vue
Normal file
22
src/components/basic/conditions/CellInput.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<kuc-text className="kuc-text-input" :value="modelValue" @change="updateValue" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import type { TextInputEventDetail } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits, type Ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
const updateValue = ({ detail }: KucEvent<TextInputEventDetail>) => {
|
||||
emit('update:modelValue', detail.value || '');
|
||||
};
|
||||
</script>
|
||||
|
||||
66
src/components/basic/conditions/TableCondition.vue
Normal file
66
src/components/basic/conditions/TableCondition.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<kuc-combobox
|
||||
v-if="items?.length"
|
||||
:items="items"
|
||||
:value="value"
|
||||
@change.stop="updateValue"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
className="condition-combobox-short"
|
||||
:data-val="value"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getAvailableCondition, type ConditionValue } from '@/js/conditions';
|
||||
import { search } from '@/js/helper';
|
||||
import type { CachedSelectedAppData, WhereCondition } from '@/types/model';
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import type { ComboboxChangeEventDetail } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits, computed, watch, ref, type Ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Ref<string>;
|
||||
selectedAppData: CachedSelectedAppData;
|
||||
whereConditions: WhereCondition[];
|
||||
id: string;
|
||||
}>();
|
||||
|
||||
const whereCondition = computed(() => search(props.whereConditions, props.id) as WhereCondition | undefined);
|
||||
|
||||
const items = computed(() =>
|
||||
getAvailableCondition(
|
||||
whereCondition.value?.field || '',
|
||||
props.selectedAppData.appFields,
|
||||
props.selectedAppData.table.table,
|
||||
),
|
||||
);
|
||||
|
||||
const value = ref(props.modelValue.value);
|
||||
|
||||
watch(
|
||||
() => items,
|
||||
() => {
|
||||
if (whereCondition.value?.condition === '') {
|
||||
// select first option
|
||||
const option = items.value?.[0] || { value: '' };
|
||||
value.value = option.value;
|
||||
updateValue({ detail: option });
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
type EmitData = {
|
||||
obj?: WhereCondition;
|
||||
value: ConditionValue;
|
||||
};
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', data: EmitData): void;
|
||||
}>();
|
||||
|
||||
const updateValue = ({ detail }: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
value.value = detail.value || '';
|
||||
emit('update:modelValue', { obj: whereCondition.value, value: detail.value as ConditionValue });
|
||||
};
|
||||
</script>
|
||||
150
src/components/basic/conditions/TableConditionValue.vue
Normal file
150
src/components/basic/conditions/TableConditionValue.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<kuc-text
|
||||
v-if="valueType === 'kuc-text'"
|
||||
:value="modelValue.value"
|
||||
@change="updateValue"
|
||||
:className="needPlaceholderWidthClass"
|
||||
:placeholder="placeholder"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
/>
|
||||
<kuc-combobox
|
||||
v-else-if="valueType === 'kuc-combobox'"
|
||||
:value="modelValue.value"
|
||||
@change="updateValue"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
/>
|
||||
<kuc-time-picker
|
||||
v-else-if="valueType === 'kuc-time'"
|
||||
:value="modelValue.value"
|
||||
@change="updateValue"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
/>
|
||||
<table-condition-value-date-time
|
||||
v-else-if="valueType === 'datetime' || valueType === 'date'"
|
||||
:time="valueType === 'datetime'"
|
||||
:value="modelValue.value as string"
|
||||
@change="updateValue"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
/>
|
||||
<kuc-multi-choice
|
||||
v-else-if="valueType === 'kuc-multichoice'"
|
||||
:value="multiValue"
|
||||
:items="multiChoiceItems"
|
||||
:requiredIcon="true"
|
||||
@change="updateMultiValue"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
/>
|
||||
<table-condition-value-multi-input
|
||||
v-else-if="valueType === 'multi-input'"
|
||||
:value="multiInput"
|
||||
@change="updateTableValue"
|
||||
:disabled="selectedAppData.loading == undefined ? false : selectedAppData.loading"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getComponent } from '@/js/conditions';
|
||||
import { getFieldObj, isStringArray, search } from '@/js/helper';
|
||||
import { isType } from '@/js/kintone-rest-api-client';
|
||||
import { isSelectType } from '@/js/mapping';
|
||||
import type { CachedSelectedAppData, StringValue, WhereCondition } from '@/types/model';
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import type { ComboboxChangeEventDetail, TextInputEventDetail, MultiChoiceChangeEventDetail } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits, computed, type Ref, inject, provide, ref, watch, watchEffect } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Ref<StringValue>;
|
||||
selectedAppData: CachedSelectedAppData;
|
||||
whereConditions: WhereCondition[];
|
||||
id: string;
|
||||
canSave: (canSave: boolean) => void;
|
||||
}>();
|
||||
|
||||
provide('canSave', props.canSave);
|
||||
|
||||
const whereCondition = computed(() => search(props.whereConditions, props.id) as WhereCondition | undefined);
|
||||
|
||||
const needPlaceholderWidthClass = computed(() => (placeholder.value ? 'kuc-text-input-placeholder-width' : ''));
|
||||
|
||||
const placeholder = computed(() => {
|
||||
const field = getFieldObj(whereCondition.value?.field || '', props.selectedAppData.appFields, '');
|
||||
if (isType.GROUP_SELECT(field)) {
|
||||
return 'グループコードをカンマ区切りで指定';
|
||||
} else if (isType.ORGANIZATION_SELECT(field)) {
|
||||
return '組織コードをカンマ区切りで指定';
|
||||
} else if (isType.USER_SELECT(field) || isType.CREATOR(field) || isType.MODIFIER(field)) {
|
||||
return 'ログイン名をカンマ区切りで指定';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
const multiChoiceItems = computed(() => {
|
||||
const field = getFieldObj(whereCondition.value?.field || '', props.selectedAppData.appFields, '');
|
||||
const items = [{
|
||||
label: '--',
|
||||
value: '',
|
||||
}];
|
||||
if (field && isSelectType(field)) {
|
||||
const opts = field.options;
|
||||
const multiOpts = Object.values(opts).map((opt) => {
|
||||
return {
|
||||
label: opt.label,
|
||||
value: opt.label,
|
||||
};
|
||||
});
|
||||
items.push(...multiOpts);
|
||||
}
|
||||
return items;
|
||||
});
|
||||
|
||||
const valueType = computed(() => {
|
||||
const field = getFieldObj(whereCondition.value?.field || '', props.selectedAppData.appFields, '');
|
||||
return getComponent(whereCondition.value?.condition || '', field);
|
||||
});
|
||||
|
||||
type EmitData = {
|
||||
obj?: WhereCondition;
|
||||
value: StringValue;
|
||||
};
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', data: EmitData): void;
|
||||
}>();
|
||||
|
||||
const updateValue = (event: KucEvent<ComboboxChangeEventDetail | TextInputEventDetail>) => {
|
||||
emit('update:modelValue', { obj: whereCondition.value, value: event.detail.value || '' });
|
||||
};
|
||||
|
||||
const multiValue = ref(isStringArray(props.modelValue.value) ? props.modelValue.value : []);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
const field = getFieldObj(whereCondition.value?.field || '', props.selectedAppData.appFields, '');
|
||||
const vType = valueType.value;
|
||||
const moduleValue = props.modelValue.value;
|
||||
if (field && isSelectType(field) && vType === 'kuc-multichoice') {
|
||||
multiValue.value = isStringArray(moduleValue) ? moduleValue : [];
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const multiInput = ref(isStringArray(props.modelValue.value) ? (props.modelValue.value as string[]) : ['', '']);
|
||||
watchEffect(() => {
|
||||
const vType = valueType.value;
|
||||
const moduleValue = props.modelValue.value;
|
||||
if (vType === 'multi-input') {
|
||||
multiInput.value = isStringArray(moduleValue) ? (moduleValue as string[]) : ['', ''];
|
||||
}
|
||||
});
|
||||
|
||||
const updateMultiValue = (event: KucEvent<MultiChoiceChangeEventDetail>) => {
|
||||
emit('update:modelValue', { obj: whereCondition.value, value: event.detail.value || [] });
|
||||
};
|
||||
|
||||
const updateTableValue = (event: KucEvent<string[]>) => {
|
||||
let value = event.detail || ['', ''];
|
||||
multiInput.value = value;
|
||||
emit('update:modelValue', { obj: whereCondition.value, value: value });
|
||||
};
|
||||
</script>
|
||||
267
src/components/basic/conditions/TableConditionValueDateTime.vue
Normal file
267
src/components/basic/conditions/TableConditionValueDateTime.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<div style="display: flex; position: relative">
|
||||
<kuc-combobox
|
||||
:value="funcValue"
|
||||
:items="options"
|
||||
@change.stop="updateFuncValue"
|
||||
:disabled="disabled"
|
||||
:className="shortConditionClass"
|
||||
:key="time"
|
||||
/>
|
||||
|
||||
<template v-if="isInput()">
|
||||
<kuc-datetime-picker v-if="time" :value="inputValue" @change.stop="updateValue" :disabled="disabled" />
|
||||
<kuc-date-picker v-else :value="inputValue" @change.stop="updateValue" :disabled="disabled" />
|
||||
</template>
|
||||
<kuc-combobox
|
||||
v-else-if="isWeek()"
|
||||
:items="weekOptions"
|
||||
:value="selectValue"
|
||||
@change.stop="updateValue"
|
||||
:disabled="disabled"
|
||||
:className="weekClassName"
|
||||
/>
|
||||
<kuc-combobox
|
||||
v-else-if="isMonth()"
|
||||
:items="monthOptions"
|
||||
:value="selectValue"
|
||||
@change.stop="updateValue"
|
||||
:disabled="disabled"
|
||||
:className="monthClassName"
|
||||
/>
|
||||
|
||||
<template v-if="isFromToday()">
|
||||
<kuc-text
|
||||
:error="fromTodayError"
|
||||
:value="inputValue"
|
||||
@change.stop="updateFromTodayValue"
|
||||
:disabled="disabled"
|
||||
:className="fromTodayError ? 'from-today-input input error' : 'from-today-input input'"
|
||||
/>
|
||||
<kuc-combobox
|
||||
:items="fromOptions"
|
||||
:value="selectValue"
|
||||
@change.stop="updateFromTodaySelectValue"
|
||||
:disabled="disabled"
|
||||
className="from-today-input"
|
||||
/>
|
||||
<kuc-combobox
|
||||
:items="additionOptions"
|
||||
:value="additionSelectValue"
|
||||
@change.stop="updateFromTodayAdditionValue"
|
||||
:disabled="disabled"
|
||||
className="from-today-input addition"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFuncMap, getDateFuncList, type DateFuncKey } from '@/js/conditions';
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import type { ComboboxChangeEventDetail } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits, computed, ref, watch, inject, type Ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
value: string;
|
||||
time: boolean;
|
||||
disabled: boolean;
|
||||
}>();
|
||||
|
||||
const canSave = inject<(canSave: boolean) => void>('canSave');
|
||||
|
||||
const inputValue = ref('');
|
||||
const selectValue = ref('');
|
||||
const additionSelectValue = ref('');
|
||||
const funcValue = ref('');
|
||||
|
||||
const fromTodayError = ref('');
|
||||
|
||||
watch(
|
||||
() => fromTodayError.value,
|
||||
() => {
|
||||
canSave?.(!fromTodayError.value);
|
||||
},
|
||||
);
|
||||
|
||||
const isInput = (func = funcValue.value) => func === dateFuncMap[''].value;
|
||||
const isWeek = (func = funcValue.value) => func.includes('WEEK');
|
||||
const isMonth = (func = funcValue.value) => func.includes('MONTH');
|
||||
const isFromToday = (func = funcValue.value) => func.includes('FROM_TODAY');
|
||||
|
||||
const weekClassName = computed(() => {
|
||||
if (isWeek()) {
|
||||
return selectValue.value === DEFAULT_WEEK_MONTH ? 'week-all-combobox' : 'week-combobox';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
const monthClassName = computed(() => {
|
||||
if (isMonth()) {
|
||||
return selectValue.value === DEFAULT_WEEK_MONTH ? 'month-all-combobox' : 'month-combobox';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
const shortConditionClass = computed(() => {
|
||||
const className = 'datetime-condition-combobox';
|
||||
if (isInput()) { return className }
|
||||
if (isFromToday() || funcValue.value === dateFuncMap['NOW'].value) {
|
||||
return className + ' mid';
|
||||
} else {
|
||||
return className + ' short';
|
||||
}
|
||||
});
|
||||
|
||||
const regex = /^(?<func>\w+)\((?<val>.*)\)$/;
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(current, before) => {
|
||||
if (props.value === '') {
|
||||
// select default one when empty
|
||||
funcValue.value = dateFuncMap[''].value;
|
||||
selectValue.value = '';
|
||||
inputValue.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const match = props.value.match(regex);
|
||||
|
||||
funcValue.value = dateFuncMap[(match?.groups?.func || '') as DateFuncKey].value;
|
||||
|
||||
const value = match?.groups?.val || (props.value.includes('%') ? '' : props.value);
|
||||
|
||||
// TODO set values is this method but isFromToday
|
||||
if (isInput()) {
|
||||
inputValue.value = value;
|
||||
selectValue.value = '';
|
||||
} else if (isWeek() || isMonth()) {
|
||||
inputValue.value = '';
|
||||
selectValue.value = value || DEFAULT_WEEK_MONTH;
|
||||
} else if (isFromToday() && !(before && isFromToday(before))) {
|
||||
// only called when first open page
|
||||
const split = value.split(', ');
|
||||
inputValue.value = split[0] === '0' ? '' : split[0].replace('-', '');
|
||||
selectValue.value = split[1] || fromOptions[0].value;
|
||||
additionSelectValue.value = split[0] ? (split[0].startsWith('-') ? '-' : '+') : '+';
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const options = computed(() => {
|
||||
return getDateFuncList(props.time).map((item) => {
|
||||
return { label: typeof item.label === 'function' ? item.label(props.time) : item.label, value: item.value };
|
||||
});
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'change', data: KucEvent<{ value: string }>): void;
|
||||
}>();
|
||||
|
||||
const updateValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
emit('change', { detail: { value: buildResult({ value: event.detail.value }) } });
|
||||
};
|
||||
|
||||
const updateFuncValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
selectValue.value = '';
|
||||
inputValue.value = '';
|
||||
emit('change', { detail: { value: buildResult({ func: event.detail.value }) } });
|
||||
};
|
||||
|
||||
const updateFromTodayValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
const value = buildFromTodayResult({ value: event.detail.value });
|
||||
if (!value) return;
|
||||
inputValue.value = event.detail.value as string;
|
||||
emit('change', { detail: { value } });
|
||||
};
|
||||
|
||||
const updateFromTodaySelectValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
const value = buildFromTodayResult({ select: event.detail.value });
|
||||
if (!value) return;
|
||||
selectValue.value = event.detail.value as string;
|
||||
emit('change', { detail: { value } });
|
||||
};
|
||||
|
||||
const updateFromTodayAdditionValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
|
||||
const value = buildFromTodayResult({ addition: event.detail.value });
|
||||
if (!value) return;
|
||||
additionSelectValue.value = event.detail.value as string;
|
||||
emit('change', { detail: { value } });
|
||||
};
|
||||
|
||||
type FromTodayParam = {
|
||||
value?: string;
|
||||
select?: string;
|
||||
addition?: string;
|
||||
};
|
||||
|
||||
function buildFromTodayResult({
|
||||
value = inputValue.value || '0',
|
||||
select = selectValue.value || 'DAYS',
|
||||
addition = additionSelectValue.value || '-',
|
||||
}: FromTodayParam) {
|
||||
if (value?.match(/^-?[0-9]+$/)) {
|
||||
fromTodayError.value = '';
|
||||
let res = value;
|
||||
if (value && value !== '0' && addition === '-') {
|
||||
if (value.startsWith('-')) {
|
||||
res = res.replace('-', '');
|
||||
} else {
|
||||
res = '-' + res;
|
||||
}
|
||||
}
|
||||
return funcValue.value.replace('%s', select).replace('%d', res);
|
||||
} else if (!fromTodayError.value) {
|
||||
inputValue.value = value || '';
|
||||
fromTodayError.value = '整数値を指定してください。';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
type Param = {
|
||||
func?: string;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
function buildResult({ func = funcValue.value, value }: Param) {
|
||||
let val = value || inputValue.value;
|
||||
if (isWeek(func) || isMonth(func)) {
|
||||
val = value || selectValue.value;
|
||||
val = val === DEFAULT_WEEK_MONTH ? '' : val;
|
||||
} else if (isFromToday(func)) {
|
||||
return func.replace('%d', val || '0').replace('%s', val || 'DAYS');
|
||||
}
|
||||
return func.replace('%s', val || '');
|
||||
}
|
||||
|
||||
const DEFAULT_WEEK_MONTH = '_';
|
||||
|
||||
const fromOptions = [
|
||||
{ label: '日', value: 'DAYS' },
|
||||
{ label: '周', value: 'WEEKS' },
|
||||
{ label: '月', value: 'MONTHS' },
|
||||
{ label: '年', value: 'YEARS' },
|
||||
];
|
||||
|
||||
const additionOptions = [
|
||||
{ label: '前', value: '-' },
|
||||
{ label: '後', value: '+' },
|
||||
];
|
||||
|
||||
const weekOptions = [
|
||||
{ label: 'すべての曜日', value: '_' },
|
||||
{ label: '日', value: 'SUNDAY' },
|
||||
{ label: '月', value: 'MONDAY' },
|
||||
{ label: '火', value: 'TUESDAY' },
|
||||
{ label: '水', value: 'WEDNESDAY' },
|
||||
{ label: '木', value: 'THURSDAY' },
|
||||
{ label: '金', value: 'FRIDAY' },
|
||||
{ label: '土', value: 'SATURDAY' },
|
||||
];
|
||||
|
||||
const monthOptions = [{ label: 'すべて', value: '_' }];
|
||||
for (let i = 1; i <= 31; i++) {
|
||||
monthOptions.push({ label: i.toString() + '日', value: i.toString() });
|
||||
}
|
||||
monthOptions.push({ label: '末日', value: 'LAST' });
|
||||
</script>
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
<template>
|
||||
<!-- <kuc-table className='table-option'
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
@change="updateValue"
|
||||
:headerVisible="false"
|
||||
ref="table"/> -->
|
||||
<div ref="tableContainer"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { KucEvent } from '@/types/my-kintone';
|
||||
import { Table, Text, type TableChangeEventDetail } from 'kintone-ui-component';
|
||||
import { defineProps, defineEmits, computed, ref, watch, inject, type Ref, onMounted, onUnmounted } from 'vue';
|
||||
interface MuiltItem{
|
||||
value:string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
value: string[];
|
||||
}>();
|
||||
|
||||
const tableContainer = ref<HTMLDivElement>();
|
||||
const table = ref<Table | null>(null);
|
||||
const data = ref<MuiltItem[]>((props.value || ['', '']).map(x => ({ value: x })));
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue)=>{
|
||||
data.value =(newValue || ['', '']).map((x) => ({ value: x }));
|
||||
if (table.value) {
|
||||
table.value.data = data.value; // 更新 Table 数据
|
||||
}
|
||||
},
|
||||
{
|
||||
deep:true,immediate:true
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'change', data: KucEvent<string[]>): void;
|
||||
(e: 'update:modelValue', value: string[]): void;
|
||||
}>();
|
||||
|
||||
|
||||
const updateValue=(event:KucEvent<TableChangeEventDetail<MuiltItem>>)=>{
|
||||
data.value = event.detail.data||[{value:''},{value:''}];
|
||||
if (table.value) {
|
||||
table.value.data = data.value;
|
||||
}
|
||||
const muiltData = event.detail.data ? event.detail.data.map(x=>x.value) :[];
|
||||
emit('change', { detail: [...muiltData] });
|
||||
// emit('update:modelValue', [...muiltData] );
|
||||
// emit('change', muiltData);
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
table.value = new Table({
|
||||
className:'table-option',
|
||||
headerVisible:false,
|
||||
actionButton:true,
|
||||
columns:[
|
||||
{
|
||||
field:"value",
|
||||
render:(cellData:any)=>{
|
||||
const text = new Text({value:cellData});
|
||||
return text;
|
||||
}
|
||||
},
|
||||
],
|
||||
data:data.value
|
||||
});
|
||||
table.value.addEventListener('change', updateValue);
|
||||
tableContainer.value?.appendChild(table.value);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (table.value) {
|
||||
table.value.removeEventListener('change', updateValue);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user