refactor: api select value (#69)
* feat: add file * fix: wrong type * feat: select customer component * fixup! feat: select customer component * fix: char case * refactor: fn alias * chore: add space * feat: accept fetch parameter * refactor: naming * feat: add emit event create * fix: add suffix to add text * feat: add before options slot for select input comp * fix: error when label not found * fix: value type * feat: add required param * fix: wording * refactor: fix customer * feat: use new select component * chore: add note * feat: add decoration for select with creatable * feat: emit event * feat: close popup on click * feat: adjust alignment * feat: add readonly params * feat: add select branch option * feat: use new select component * feat: add disabled params * feat: adjust internal search and select * refactor: props type * feat: use new select component * feat: add lib for select component * refactor: use factory function instead * refactor: merge two lines of code * refactor: move watch inside * refactor: fix value not in list check * chore: cleanup * fix: remove test page size * chore: remove unused * feat: use new select component * fix: typo * fix: error * refactor: extract type * refactor: change ref var to normal var * refactor: force overwrite params to prevent error on render * feat: add clearable parameter * feat: make clearable
This commit is contained in:
parent
a227744131
commit
d414685fe7
13 changed files with 500 additions and 319 deletions
|
|
@ -1,9 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { QSelect } from 'quasar';
|
import SelectBranch from '../shared/select/SelectBranch.vue';
|
||||||
import { getRole } from 'src/services/keycloak';
|
|
||||||
import { selectFilterOptionRefMod } from 'stores/utils';
|
|
||||||
import { watch } from 'vue';
|
|
||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
const remark = defineModel<string>('remark');
|
const remark = defineModel<string>('remark');
|
||||||
const detail = defineModel<string>('detail');
|
const detail = defineModel<string>('detail');
|
||||||
|
|
@ -16,39 +12,16 @@ const serviceName = defineModel<string>('serviceNameTh');
|
||||||
const serviceDescription = defineModel<string>('serviceDescription');
|
const serviceDescription = defineModel<string>('serviceDescription');
|
||||||
const registeredBranchId = defineModel<string>('registeredBranchId');
|
const registeredBranchId = defineModel<string>('registeredBranchId');
|
||||||
|
|
||||||
const optionsBranch = defineModel<{ id: string; name: string }[]>(
|
|
||||||
'optionsBranch',
|
|
||||||
{ default: [] },
|
|
||||||
);
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
outlined?: boolean;
|
outlined?: boolean;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
readOnlybranchOption?: boolean;
|
branchReadonly?: boolean;
|
||||||
separator?: boolean;
|
separator?: boolean;
|
||||||
isType?: boolean;
|
isType?: boolean;
|
||||||
disableCode?: boolean;
|
disableCode?: boolean;
|
||||||
service?: boolean;
|
service?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const branchOptions = ref<Record<string, unknown>[]>([]);
|
|
||||||
let branchFilter = selectFilterOptionRefMod(
|
|
||||||
optionsBranch,
|
|
||||||
branchOptions,
|
|
||||||
'name',
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => optionsBranch.value,
|
|
||||||
() => {
|
|
||||||
branchFilter = selectFilterOptionRefMod(
|
|
||||||
optionsBranch,
|
|
||||||
branchOptions,
|
|
||||||
'name',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -80,51 +53,14 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!service" class="col-12 row q-col-gutter-sm">
|
<div v-if="!service" class="col-12 row q-col-gutter-sm">
|
||||||
<q-select
|
<SelectBranch
|
||||||
outlined
|
|
||||||
use-input
|
|
||||||
fill-input
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
hide-selected
|
|
||||||
hide-bottom-space
|
|
||||||
class="col-md-12 col-12"
|
class="col-md-12 col-12"
|
||||||
option-value="id"
|
v-model:value="registeredBranchId"
|
||||||
option-label="name"
|
:readonly
|
||||||
v-model="registeredBranchId"
|
:disabled="!readonly && branchReadonly"
|
||||||
id="input-source-nationality"
|
clearable
|
||||||
for="input-source-nationality"
|
required
|
||||||
:disable="!readonly && readOnlybranchOption"
|
/>
|
||||||
:dense="dense"
|
|
||||||
:readonly="readonly || readOnlybranchOption"
|
|
||||||
:options="branchOptions"
|
|
||||||
:hide-dropdown-icon="readonly || readOnlybranchOption"
|
|
||||||
:label="$t('productService.service.registeredBranch')"
|
|
||||||
:rules="[
|
|
||||||
(val) => {
|
|
||||||
const roles = getRole() || [];
|
|
||||||
const isSpecialRole = ['admin', 'system', 'head_of_admin'].some(
|
|
||||||
(role) => roles.includes(role),
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
isSpecialRole ||
|
|
||||||
!!val ||
|
|
||||||
$t('form.error.selectField', {
|
|
||||||
field: $t('productService.service.registeredBranch'),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
@filter="branchFilter"
|
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section class="text-grey">
|
|
||||||
{{ $t('general.noData') }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
<q-input
|
<q-input
|
||||||
for="input-code"
|
for="input-code"
|
||||||
:dense="dense"
|
:dense="dense"
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import SelectCustomer from '../shared/select/SelectCustomer.vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import SelectBranch from '../shared/select/SelectBranch.vue';
|
||||||
import useBranchStore from 'src/stores/branch';
|
|
||||||
import useCustomerStore from 'src/stores/customer';
|
|
||||||
import SelectInput from '../shared/SelectInput.vue';
|
|
||||||
import { QSelect } from 'quasar';
|
|
||||||
import { Branch } from 'src/stores/branch/types';
|
|
||||||
import { CustomerBranch } from 'src/stores/customer/types';
|
|
||||||
import useOptionStore from 'stores/options';
|
|
||||||
|
|
||||||
const { locale } = useI18n({ useScope: 'global' });
|
|
||||||
const branchStore = useBranchStore();
|
|
||||||
const customerStore = useCustomerStore();
|
|
||||||
|
|
||||||
const optionStore = useOptionStore();
|
|
||||||
const branchId = defineModel<string>('branchId');
|
const branchId = defineModel<string>('branchId');
|
||||||
const customerBranchId = defineModel<string>('customerBranchId');
|
const customerBranchId = defineModel<string>('customerBranchId');
|
||||||
const agentPrice = defineModel<boolean>('agentPrice');
|
const agentPrice = defineModel<boolean>('agentPrice');
|
||||||
const special = defineModel<boolean>('special');
|
const special = defineModel<boolean>('special');
|
||||||
|
|
||||||
const branchOption = ref<{ value: string; label: string; labelEN: string }[]>();
|
|
||||||
const customerOption =
|
|
||||||
ref<
|
|
||||||
{ value: string; label: string; labelEN: string; namePrefix: string }[]
|
|
||||||
>();
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
outlined?: boolean;
|
outlined?: boolean;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
|
|
@ -40,103 +22,6 @@ defineProps<{
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
(e: 'addCustomer'): void;
|
(e: 'addCustomer'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
async function filter(
|
|
||||||
val: string,
|
|
||||||
update: (...args: unknown[]) => void,
|
|
||||||
type: 'branch' | 'customer',
|
|
||||||
) {
|
|
||||||
update(
|
|
||||||
async () => {
|
|
||||||
await init(val, type);
|
|
||||||
},
|
|
||||||
|
|
||||||
(ref: QSelect) => {
|
|
||||||
if (val !== '' && ref.options && ref.options?.length > 0) {
|
|
||||||
ref.setOptionIndex(-1);
|
|
||||||
ref.moveOptionSelection(1, true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentSelectedBranchOption =
|
|
||||||
ref<NonNullable<typeof branchOption.value>[number]>();
|
|
||||||
const currentSelectedCustomerOption =
|
|
||||||
ref<NonNullable<typeof customerOption.value>[number]>();
|
|
||||||
|
|
||||||
async function init(val: string, type: 'branch' | 'customer') {
|
|
||||||
const res =
|
|
||||||
type === 'branch'
|
|
||||||
? await branchStore.fetchList({
|
|
||||||
query: val,
|
|
||||||
pageSize: 30,
|
|
||||||
})
|
|
||||||
: await customerStore.fetchListCustomerBranch({
|
|
||||||
query: val,
|
|
||||||
pageSize: 30,
|
|
||||||
company: true,
|
|
||||||
});
|
|
||||||
if (res) {
|
|
||||||
if (type === 'branch') {
|
|
||||||
const mapped = (res.result as Branch[]).map((v: Branch) => ({
|
|
||||||
value: v.id,
|
|
||||||
label: v.name,
|
|
||||||
labelEN: v.nameEN,
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (!mapped.find((v) => v.value === branchId.value) && branchId.value) {
|
|
||||||
if (currentSelectedBranchOption.value?.value !== branchId.value) {
|
|
||||||
const v = await branchStore.fetchById(branchId.value);
|
|
||||||
if (v) {
|
|
||||||
mapped.unshift({
|
|
||||||
value: v.id,
|
|
||||||
label: v.name,
|
|
||||||
labelEN: v.nameEN,
|
|
||||||
});
|
|
||||||
currentSelectedBranchOption.value = mapped.at(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
branchOption.value = mapped;
|
|
||||||
} else if (type === 'customer') {
|
|
||||||
const mapped = (res.result as CustomerBranch[]).map(
|
|
||||||
(v: CustomerBranch) => ({
|
|
||||||
value: v.id,
|
|
||||||
label: v.registerName || `${v.firstName} ${v.lastName}` || '-',
|
|
||||||
labelEN:
|
|
||||||
v.registerNameEN || `${v.firstNameEN} ${v.lastNameEN}` || '-',
|
|
||||||
namePrefix: v.namePrefix,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!mapped.find((v) => v.value === customerBranchId.value) &&
|
|
||||||
customerBranchId.value
|
|
||||||
) {
|
|
||||||
const v = await customerStore.fetchListCustomerBranchById(
|
|
||||||
customerBranchId.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (v) {
|
|
||||||
mapped.unshift({
|
|
||||||
value: v.id,
|
|
||||||
label: v.registerName || `${v.firstName} ${v.lastName}` || '-',
|
|
||||||
labelEN:
|
|
||||||
v.registerNameEN || `${v.firstNameEN} ${v.lastNameEN}` || '-',
|
|
||||||
namePrefix: v.namePrefix,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customerOption.value = mapped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await init('', 'branch');
|
|
||||||
await init('', 'customer');
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
@ -170,62 +55,24 @@ onMounted(async () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 row q-col-gutter-sm">
|
<div class="col-12 row q-col-gutter-sm">
|
||||||
<SelectInput
|
<SelectBranch
|
||||||
:readonly
|
v-model:value="branchId"
|
||||||
incremental
|
|
||||||
v-model="branchId"
|
|
||||||
id="quotation-branch"
|
|
||||||
class="col-md col-12"
|
|
||||||
:option="branchOption"
|
|
||||||
:label="$t('quotation.branch')"
|
:label="$t('quotation.branch')"
|
||||||
:option-label="locale === 'eng' ? 'labelEN' : 'label'"
|
class="col-md-6 col-12"
|
||||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
simple
|
||||||
@filter="(val: string, update) => filter(val, update, 'branch')"
|
required
|
||||||
/>
|
|
||||||
|
|
||||||
<SelectInput
|
|
||||||
:readonly
|
:readonly
|
||||||
incremental
|
/>
|
||||||
v-model="customerBranchId"
|
<SelectCustomer
|
||||||
class="col-md col-12"
|
v-model:value="customerBranchId"
|
||||||
id="quotation-customer"
|
|
||||||
:option="customerOption"
|
|
||||||
:label="$t('quotation.customer')"
|
:label="$t('quotation.customer')"
|
||||||
:option-label="locale === 'eng' ? 'labelEN' : 'label'"
|
@create="$emit('addCustomer')"
|
||||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
class="col-md-6 col-12"
|
||||||
@filter="(val: string, update) => filter(val, update, 'customer')"
|
creatable
|
||||||
>
|
simple
|
||||||
<template #option="{ scope }">
|
required
|
||||||
<q-item
|
:readonly
|
||||||
clickable
|
/>
|
||||||
v-if="scope.index === 0 && !hideAdd"
|
|
||||||
@click.stop="$emit('addCustomer')"
|
|
||||||
>
|
|
||||||
<q-item-section>
|
|
||||||
{{ $t('general.add', { text: $t('quotation.newCustomer') }) }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-separator v-if="scope.index === 0" />
|
|
||||||
|
|
||||||
<q-item clickable v-bind="scope.itemProps">
|
|
||||||
<q-item-section>
|
|
||||||
{{
|
|
||||||
optionStore.mapOption(scope.opt.namePrefix) ||
|
|
||||||
$t('general.company')
|
|
||||||
}}
|
|
||||||
{{ locale === 'eng' ? scope.opt.labelEN : scope.opt.label }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #noOption>
|
|
||||||
<q-item clickable @click.stop="$emit('addCustomer')">
|
|
||||||
<q-item-section>
|
|
||||||
{{ $t('general.add', { text: $t('quotation.newCustomer') }) }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</SelectInput>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup generic="T extends Record<string, unknown>">
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
import { selectFilterOptionRefMod } from 'src/stores/utils';
|
import { selectFilterOptionRefMod } from 'src/stores/utils';
|
||||||
import { QSelect } from 'quasar';
|
import { QSelect } from 'quasar';
|
||||||
|
|
@ -15,9 +15,9 @@ const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
id?: string;
|
id?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
option: Record<string, unknown>[];
|
option: T[];
|
||||||
optionLabel?: string;
|
optionLabel?: keyof T;
|
||||||
optionValue?: string;
|
optionValue?: keyof T;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
|
||||||
hideSelected?: boolean;
|
hideSelected?: boolean;
|
||||||
|
|
@ -40,14 +40,18 @@ const props = withDefaults(
|
||||||
);
|
);
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
(e: 'filter', val: string, update: void): void;
|
(
|
||||||
|
e: 'filter',
|
||||||
|
val: string,
|
||||||
|
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
||||||
|
): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
defaultFilter = selectFilterOptionRefMod(
|
defaultFilter = selectFilterOptionRefMod(
|
||||||
ref(props.option),
|
ref(props.option),
|
||||||
options,
|
options,
|
||||||
props.optionLabel,
|
typeof props.optionLabel === 'string' ? props.optionLabel : 'label',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -57,7 +61,7 @@ watch(
|
||||||
defaultFilter = selectFilterOptionRefMod(
|
defaultFilter = selectFilterOptionRefMod(
|
||||||
ref(props.option),
|
ref(props.option),
|
||||||
options,
|
options,
|
||||||
props.optionLabel,
|
typeof props.optionLabel === 'string' ? props.optionLabel : 'label',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -76,8 +80,12 @@ watch(
|
||||||
:fill-input="fillInput && !!model"
|
:fill-input="fillInput && !!model"
|
||||||
:hide-dropdown-icon="readonly"
|
:hide-dropdown-icon="readonly"
|
||||||
input-debounce="500"
|
input-debounce="500"
|
||||||
:option-value="optionValue"
|
:option-value="
|
||||||
:option-label="optionLabel"
|
typeof props.optionValue === 'string' ? props.optionValue : 'value'
|
||||||
|
"
|
||||||
|
:option-label="
|
||||||
|
typeof props.optionLabel === 'string' ? props.optionLabel : 'label'
|
||||||
|
"
|
||||||
v-model="model"
|
v-model="model"
|
||||||
dense
|
dense
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
|
@ -95,9 +103,13 @@ watch(
|
||||||
<template v-if="$slots.prepend" v-slot:prepend>
|
<template v-if="$slots.prepend" v-slot:prepend>
|
||||||
<slot name="prepend"></slot>
|
<slot name="prepend"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="$slots.append" v-slot:append>
|
||||||
|
<slot name="append"></slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-slot:no-option>
|
<template v-slot:no-option>
|
||||||
<slot name="noOption"></slot>
|
<slot name="noOption"></slot>
|
||||||
|
<slot name="no-option"></slot>
|
||||||
|
|
||||||
<q-item v-if="!$slots.noOption">
|
<q-item v-if="!$slots.noOption">
|
||||||
<q-item-section class="text-grey">
|
<q-item-section class="text-grey">
|
||||||
|
|
@ -106,12 +118,20 @@ watch(
|
||||||
</q-item>
|
</q-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="$slots.selectedItem" v-slot:selected-item="scope">
|
<template
|
||||||
<slot name="selectedItem" :scope="scope"></slot>
|
v-if="$slots.selectedItem || $slots['selected-item']"
|
||||||
|
v-slot:selected-item="scope"
|
||||||
|
>
|
||||||
|
<slot name="selectedItem" :scope="scope" :opt="scope.opt as T"></slot>
|
||||||
|
<slot name="selected-item" :scope="scope" :opt="scope.opt as T"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="$slots.option" v-slot:option="scope">
|
<template v-if="$slots.option" v-slot:option="scope">
|
||||||
<slot name="option" :scope="scope"></slot>
|
<slot name="option" :scope="scope" :opt="scope.opt as T"></slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="$slots['before-options']" #before-options>
|
||||||
|
<slot name="before-options"></slot>
|
||||||
</template>
|
</template>
|
||||||
</q-select>
|
</q-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
108
src/components/shared/select/SelectBranch.vue
Normal file
108
src/components/shared/select/SelectBranch.vue
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
|
||||||
|
import { createSelect, SelectProps } from './select';
|
||||||
|
import SelectInput from '../SelectInput.vue';
|
||||||
|
|
||||||
|
import { Branch } from 'src/stores/branch/types';
|
||||||
|
|
||||||
|
import useStore from 'src/stores/branch';
|
||||||
|
|
||||||
|
type SelectOption = Branch;
|
||||||
|
|
||||||
|
const value = defineModel<string | null | undefined>('value', {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
const valueOption = defineModel<SelectOption>('valueOption', {
|
||||||
|
required: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectOptions = ref<SelectOption[]>([]);
|
||||||
|
|
||||||
|
const { fetchList: getList, fetchById: getById } = useStore();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'create'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
type ExclusiveProps = {
|
||||||
|
selectFirstValue?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
|
||||||
|
|
||||||
|
const { getOptions, setFirstValue, getSelectedOption, filter } =
|
||||||
|
createSelect<SelectOption>(
|
||||||
|
{
|
||||||
|
value,
|
||||||
|
valueOption,
|
||||||
|
selectOptions,
|
||||||
|
getList: async (query) => {
|
||||||
|
const ret = await getList({
|
||||||
|
query,
|
||||||
|
includeCustomer: true,
|
||||||
|
...props.params,
|
||||||
|
});
|
||||||
|
if (ret) return ret.result;
|
||||||
|
},
|
||||||
|
getByValue: async (id) => {
|
||||||
|
const ret = await getById(id);
|
||||||
|
if (ret) return ret;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ valueField: 'id' },
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getOptions();
|
||||||
|
|
||||||
|
if (props.autoSelectOnSingle && selectOptions.value.length === 1) {
|
||||||
|
setFirstValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.selectFirstValue) {
|
||||||
|
setDefaultValue();
|
||||||
|
} else await getSelectedOption();
|
||||||
|
});
|
||||||
|
|
||||||
|
function setDefaultValue() {
|
||||||
|
setFirstValue();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<SelectInput
|
||||||
|
v-model="value"
|
||||||
|
incremental
|
||||||
|
:label
|
||||||
|
:placeholder
|
||||||
|
:readonly
|
||||||
|
:disable="disabled"
|
||||||
|
:option="
|
||||||
|
selectOptions.map((v) => {
|
||||||
|
const ret = {
|
||||||
|
label: (
|
||||||
|
{
|
||||||
|
['eng']: [v.nameEN, `(${v.code})`].join(' '),
|
||||||
|
['tha']: [v.name, `(${v.code})`].join(' '),
|
||||||
|
} as const
|
||||||
|
)[$i18n.locale],
|
||||||
|
value: v.id,
|
||||||
|
};
|
||||||
|
return ret;
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:hide-selected="false"
|
||||||
|
:fill-input="false"
|
||||||
|
:rules="[(v: string) => !!v || $t('form.error.required')]"
|
||||||
|
@filter="filter"
|
||||||
|
>
|
||||||
|
<template #append v-if="clearable">
|
||||||
|
<q-icon
|
||||||
|
v-if="!readonly && value"
|
||||||
|
name="mdi-close-circle"
|
||||||
|
@click.stop="value = ''"
|
||||||
|
class="cursor-pointer clear-btn"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</SelectInput>
|
||||||
|
</template>
|
||||||
128
src/components/shared/select/SelectCustomer.vue
Normal file
128
src/components/shared/select/SelectCustomer.vue
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
|
||||||
|
import { createSelect, SelectProps } from './select';
|
||||||
|
import SelectInput from '../SelectInput.vue';
|
||||||
|
import SelectCustomerItem from './SelectCustomerItem.vue';
|
||||||
|
|
||||||
|
import { Customer, CustomerBranch } from 'src/stores/customer/types';
|
||||||
|
|
||||||
|
import useStore from 'src/stores/customer';
|
||||||
|
|
||||||
|
type SelectOption = CustomerBranch & { customer: Customer };
|
||||||
|
|
||||||
|
const value = defineModel<string | null | undefined>('value', {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
const valueOption = defineModel<SelectOption>('valueOption', {
|
||||||
|
required: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectOptions = ref<SelectOption[]>([]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
fetchListCustomerBranch: getList,
|
||||||
|
fetchListCustomerBranchById: getById,
|
||||||
|
} = useStore();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'create'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
type ExclusiveProps = {
|
||||||
|
simple?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
|
||||||
|
|
||||||
|
const { getOptions, setFirstValue, getSelectedOption, filter } =
|
||||||
|
createSelect<SelectOption>(
|
||||||
|
{
|
||||||
|
value,
|
||||||
|
valueOption,
|
||||||
|
selectOptions,
|
||||||
|
getList: async (query) => {
|
||||||
|
const ret = await getList({
|
||||||
|
query,
|
||||||
|
...props.params,
|
||||||
|
includeCustomer: true,
|
||||||
|
});
|
||||||
|
if (ret) return ret.result;
|
||||||
|
},
|
||||||
|
getByValue: async (id) => {
|
||||||
|
const ret = await getById(id);
|
||||||
|
if (ret) return ret;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ valueField: 'id' },
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getOptions();
|
||||||
|
|
||||||
|
if (props.autoSelectOnSingle && selectOptions.value.length === 1) {
|
||||||
|
setFirstValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
await getSelectedOption();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<SelectInput
|
||||||
|
v-model="value"
|
||||||
|
option-value="id"
|
||||||
|
incremental
|
||||||
|
:label
|
||||||
|
:placeholder
|
||||||
|
:readonly
|
||||||
|
:disable="disabled"
|
||||||
|
:option="selectOptions"
|
||||||
|
:hide-selected="false"
|
||||||
|
:fill-input="false"
|
||||||
|
:rules="[(v: string) => !!v || $t('form.error.required')]"
|
||||||
|
@filter="filter"
|
||||||
|
>
|
||||||
|
<template #selected-item="{ opt }">
|
||||||
|
<SelectCustomerItem
|
||||||
|
v-if="typeof opt === 'object'"
|
||||||
|
:data="opt"
|
||||||
|
:simple
|
||||||
|
single-line
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #before-options v-if="creatable">
|
||||||
|
<q-item clickable v-close-popup @click.stop="$emit('create')">
|
||||||
|
<q-item-section>
|
||||||
|
<b class="row items-center">
|
||||||
|
<q-icon
|
||||||
|
name="mdi-plus-circle-outline"
|
||||||
|
class="q-mr-sm"
|
||||||
|
style="color: hsl(var(--positive-bg))"
|
||||||
|
/>
|
||||||
|
{{ $t('general.add', { text: $t('quotation.newCustomer') }) }}
|
||||||
|
</b>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-separator class="q-mx-sm" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #option="{ opt, scope }">
|
||||||
|
<q-item v-bind="scope.itemProps">
|
||||||
|
<SelectCustomerItem :data="opt" :simple />
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-separator class="q-mx-sm" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #append v-if="clearable">
|
||||||
|
<q-icon
|
||||||
|
v-if="!readonly && value"
|
||||||
|
name="mdi-close-circle"
|
||||||
|
@click.stop="value = ''"
|
||||||
|
class="cursor-pointer clear-btn"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</SelectInput>
|
||||||
|
</template>
|
||||||
100
src/components/shared/select/SelectCustomerItem.vue
Normal file
100
src/components/shared/select/SelectCustomerItem.vue
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Customer, CustomerBranch } from 'src/stores/customer/types';
|
||||||
|
import useOptionStore from 'src/stores/options';
|
||||||
|
import { formatAddress } from 'src/utils/address';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data?: CustomerBranch & { customer: Customer };
|
||||||
|
simple?: boolean;
|
||||||
|
singleLine?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const optionStore = useOptionStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="data">
|
||||||
|
<div v-if="simple" class="row items-center">
|
||||||
|
{{
|
||||||
|
{
|
||||||
|
['CORP']: {
|
||||||
|
['eng']: data.registerNameEN,
|
||||||
|
['tha']: data.registerName,
|
||||||
|
}[$i18n.locale],
|
||||||
|
['PERS']:
|
||||||
|
{
|
||||||
|
['eng']: `${optionStore.mapOption(data.namePrefix)} ${data.firstNameEN} ${data.lastNameEN}`,
|
||||||
|
['tha']: `${optionStore.mapOption(data.namePrefix)} ${data.firstName} ${data.lastName}`,
|
||||||
|
}[$i18n.locale] || '-',
|
||||||
|
}[data.customer.customerType]
|
||||||
|
}}
|
||||||
|
({{ data.code }})
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
:class="{
|
||||||
|
['row']: singleLine,
|
||||||
|
['items-center']: singleLine,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="q-mr-sm">
|
||||||
|
<span style="font-weight: 600">
|
||||||
|
{{
|
||||||
|
data.customer.customerType === 'CORP'
|
||||||
|
? $t('customer.form.registerName')
|
||||||
|
: $t('customer.form.ownerName')
|
||||||
|
}}:
|
||||||
|
</span>
|
||||||
|
{{
|
||||||
|
{
|
||||||
|
['CORP']: {
|
||||||
|
['eng']: data.registerNameEN,
|
||||||
|
['tha']: data.registerName,
|
||||||
|
}[$i18n.locale],
|
||||||
|
['PERS']:
|
||||||
|
{
|
||||||
|
['eng']: `${optionStore.mapOption(data.namePrefix)} ${data.firstNameEN} ${data.lastNameEN}`,
|
||||||
|
['tha']: `${optionStore.mapOption(data.namePrefix)} ${data.firstName} ${data.lastName}`,
|
||||||
|
}[$i18n.locale] || '-',
|
||||||
|
}[data.customer.customerType]
|
||||||
|
}}
|
||||||
|
({{ data.code }})
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-caption app-text-muted-2"
|
||||||
|
v-if="data.customer && data.province"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
`branch.form.title.${data.code.endsWith('-00') ? 'branchHQLabel' : 'branchLabel'}`,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{ !data.code.endsWith('-00') ? +data.code.split('-')[1] : '' }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-caption app-text-muted-2"
|
||||||
|
v-if="data.customer && data.province"
|
||||||
|
>
|
||||||
|
{{ $t('general.address') }}
|
||||||
|
{{
|
||||||
|
{
|
||||||
|
['eng']: formatAddress({ ...data, en: true }),
|
||||||
|
['tha']: formatAddress({ ...data, en: false }),
|
||||||
|
}[$i18n.locale]
|
||||||
|
}}
|
||||||
|
<q-tooltip v-if="data.customer && data.province">
|
||||||
|
{{ $t('customerBranch.form.title') }}:
|
||||||
|
|
||||||
|
{{ $t('general.address') }}
|
||||||
|
{{
|
||||||
|
{
|
||||||
|
['eng']: formatAddress({ ...data, en: true }),
|
||||||
|
['tha']: formatAddress({ ...data, en: false }),
|
||||||
|
}[$i18n.locale]
|
||||||
|
}}
|
||||||
|
</q-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
0
src/components/shared/select/SelectEmployee.vue
Normal file
0
src/components/shared/select/SelectEmployee.vue
Normal file
94
src/components/shared/select/select.ts
Normal file
94
src/components/shared/select/select.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { QSelect } from 'quasar';
|
||||||
|
import { Ref, watch } from 'vue';
|
||||||
|
|
||||||
|
export type SelectProps<T extends (...args: any[]) => any> = {
|
||||||
|
params?: Parameters<T>[0];
|
||||||
|
creatable?: boolean;
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
readonly?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
clearable?: boolean;
|
||||||
|
autoSelectOnSingle?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createSelect = <T extends Record<string, any>>(
|
||||||
|
state: {
|
||||||
|
value: Ref<string | null | undefined>;
|
||||||
|
valueOption: Ref<T | undefined>;
|
||||||
|
selectOptions: Ref<T[]>;
|
||||||
|
getByValue: (id: string) => Promise<T | void> | T | void;
|
||||||
|
getList: (query?: string) => Promise<T[] | void> | T[] | void;
|
||||||
|
},
|
||||||
|
opts?: {
|
||||||
|
valueField?: keyof T;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { value, valueOption, selectOptions, getList, getByValue } = state;
|
||||||
|
|
||||||
|
const valueField = opts?.valueField || 'value';
|
||||||
|
|
||||||
|
let cache: T[];
|
||||||
|
let previousSearch = '';
|
||||||
|
|
||||||
|
watch(value, (v) => {
|
||||||
|
if (!v || (cache && cache.find((opt) => opt[valueField] === v))) return;
|
||||||
|
getSelectedOption();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getOptions(query?: string) {
|
||||||
|
if (cache && selectOptions.value.length > 0 && previousSearch === query) {
|
||||||
|
selectOptions.value = JSON.parse(JSON.stringify(cache));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ret = await getList(query);
|
||||||
|
if (ret) {
|
||||||
|
cache = ret;
|
||||||
|
selectOptions.value = JSON.parse(JSON.stringify(cache));
|
||||||
|
previousSearch = query || previousSearch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setFirstValue() {
|
||||||
|
if (value.value) return;
|
||||||
|
const first = selectOptions.value.at(0);
|
||||||
|
if (first) value.value = first[valueField];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSelectedOption() {
|
||||||
|
const currentValue = value.value;
|
||||||
|
|
||||||
|
if (!currentValue) return;
|
||||||
|
if (selectOptions.value.find((v) => v[valueField] === currentValue)) return;
|
||||||
|
if (valueOption.value && valueOption.value[valueField] === currentValue) {
|
||||||
|
return selectOptions.value.unshift(valueOption.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ret = await getByValue(currentValue);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
selectOptions.value.unshift(ret);
|
||||||
|
valueOption.value = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuasarSelectUpdate = (
|
||||||
|
callback: () => void,
|
||||||
|
afterFn?: ((ref: QSelect) => void) | undefined,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
function filter(value: string, update: QuasarSelectUpdate) {
|
||||||
|
update(
|
||||||
|
() => getOptions(value),
|
||||||
|
(ref) => {
|
||||||
|
if (!!value && ref.options && ref.options.length > 0) {
|
||||||
|
ref.setOptionIndex(-1);
|
||||||
|
ref.moveOptionSelection(1, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { getOptions, setFirstValue, getSelectedOption, filter };
|
||||||
|
};
|
||||||
|
|
@ -440,7 +440,7 @@ export default {
|
||||||
issueDate: 'Issue Date',
|
issueDate: 'Issue Date',
|
||||||
passportExpiryDate: 'Passport Expiry Date',
|
passportExpiryDate: 'Passport Expiry Date',
|
||||||
|
|
||||||
ownerName: 'Owner Name',
|
ownerName: 'Customer Name',
|
||||||
firstName: 'First Name ',
|
firstName: 'First Name ',
|
||||||
lastName: 'Last Name ',
|
lastName: 'Last Name ',
|
||||||
firstNameEN: 'First Name in English',
|
firstNameEN: 'First Name in English',
|
||||||
|
|
|
||||||
|
|
@ -436,7 +436,7 @@ export default {
|
||||||
issueDate: 'วันที่ออกหนังสือ',
|
issueDate: 'วันที่ออกหนังสือ',
|
||||||
passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง',
|
passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง',
|
||||||
|
|
||||||
ownerName: 'ชื่อเจ้าของ',
|
ownerName: 'ชื่อนายจ้าง',
|
||||||
firstName: 'ชื่อ ',
|
firstName: 'ชื่อ ',
|
||||||
lastName: 'นามสกุล ',
|
lastName: 'นามสกุล ',
|
||||||
firstNameEN: 'ชื่อ ภาษาอังกฤษ',
|
firstNameEN: 'ชื่อ ภาษาอังกฤษ',
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
import { ref, watch, reactive } from 'vue';
|
import { ref, watch, reactive } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { getUserId, getRole } from 'src/services/keycloak';
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useQuasar, type QTableProps } from 'quasar';
|
import { useQuasar, type QTableProps } from 'quasar';
|
||||||
|
|
||||||
|
|
@ -38,15 +37,10 @@ import {
|
||||||
} from 'components/button';
|
} from 'components/button';
|
||||||
|
|
||||||
import useFlowStore from 'stores/flow';
|
import useFlowStore from 'stores/flow';
|
||||||
import useMyBranchStore from 'stores/my-branch';
|
|
||||||
|
|
||||||
import { dateFormat } from 'src/utils/datetime';
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
import { formatNumberDecimal, isRoleInclude } from 'stores/utils';
|
import { formatNumberDecimal, isRoleInclude } from 'stores/utils';
|
||||||
|
|
||||||
const userBranchStore = useMyBranchStore();
|
|
||||||
|
|
||||||
const { currentMyBranch } = storeToRefs(userBranchStore);
|
|
||||||
|
|
||||||
import { Status } from 'stores/types';
|
import { Status } from 'stores/types';
|
||||||
|
|
||||||
import { dialog, dialogWarningClose } from 'stores/utils';
|
import { dialog, dialogWarningClose } from 'stores/utils';
|
||||||
|
|
@ -98,7 +92,6 @@ const {
|
||||||
} = productServiceStore;
|
} = productServiceStore;
|
||||||
|
|
||||||
const { workNameItems, splitPay } = storeToRefs(productServiceStore);
|
const { workNameItems, splitPay } = storeToRefs(productServiceStore);
|
||||||
const readOnlybranchOption = ref<boolean>(false);
|
|
||||||
const allStat = ref<{ mode: string; count: number }[]>([]);
|
const allStat = ref<{ mode: string; count: number }[]>([]);
|
||||||
const stat = ref<
|
const stat = ref<
|
||||||
{
|
{
|
||||||
|
|
@ -500,13 +493,11 @@ const currentIdGroup = ref<string>('');
|
||||||
const currentIdType = ref<string>('');
|
const currentIdType = ref<string>('');
|
||||||
const currentIdService = ref<string>('');
|
const currentIdService = ref<string>('');
|
||||||
const currentIdProduct = ref<string>('');
|
const currentIdProduct = ref<string>('');
|
||||||
const currentIdGropTree = ref<string>('');
|
const currentIdGroupTree = ref<string>('');
|
||||||
|
|
||||||
const currentStatusGroupType = ref<Status>('CREATED');
|
const currentStatusGroupType = ref<Status>('CREATED');
|
||||||
const currentIdGroupType = ref('');
|
const currentIdGroupType = ref('');
|
||||||
|
|
||||||
const branchOption = ref<{ id: string; name: string }[]>([]);
|
|
||||||
|
|
||||||
const currentStatus = ref<Status | 'All'>('All');
|
const currentStatus = ref<Status | 'All'>('All');
|
||||||
|
|
||||||
// img
|
// img
|
||||||
|
|
@ -559,23 +550,6 @@ function selectAllProduct(list: Product[]) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchListOfOptionBranch() {
|
|
||||||
const uid = getUserId();
|
|
||||||
const role = getRole();
|
|
||||||
|
|
||||||
if (!uid) return;
|
|
||||||
|
|
||||||
if (role?.includes('system')) {
|
|
||||||
const result = await userBranchStore.fetchListOptionBranch();
|
|
||||||
if (result && result.total > 0) branchOption.value = result.result;
|
|
||||||
} else {
|
|
||||||
const result = await userBranchStore.fetchListMyBranch(uid);
|
|
||||||
if (result && result.total > 0) branchOption.value = result.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
formDataGroup.value.registeredBranchId = currentMyBranch.value?.id || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchListGroups() {
|
async function fetchListGroups() {
|
||||||
const res = await fetchProductGroup({
|
const res = await fetchProductGroup({
|
||||||
page: currentPageGroup.value,
|
page: currentPageGroup.value,
|
||||||
|
|
@ -863,7 +837,6 @@ function undoProductGroup() {
|
||||||
registeredBranchId: previousValue.value.registeredBranchId,
|
registeredBranchId: previousValue.value.registeredBranchId,
|
||||||
};
|
};
|
||||||
isEdit.value = false;
|
isEdit.value = false;
|
||||||
readOnlybranchOption.value = false;
|
|
||||||
flowStore.rotate();
|
flowStore.rotate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -871,7 +844,6 @@ async function assignFormDataGroup(data: ProductGroup) {
|
||||||
previousValue.value = data;
|
previousValue.value = data;
|
||||||
currentStatusGroupType.value = data.status;
|
currentStatusGroupType.value = data.status;
|
||||||
currentIdGroupType.value = data.id;
|
currentIdGroupType.value = data.id;
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
|
|
||||||
formDataGroup.value = {
|
formDataGroup.value = {
|
||||||
remark: data.remark,
|
remark: data.remark,
|
||||||
|
|
@ -881,22 +853,6 @@ async function assignFormDataGroup(data: ProductGroup) {
|
||||||
shared: data.shared,
|
shared: data.shared,
|
||||||
registeredBranchId: data.registeredBranchId,
|
registeredBranchId: data.registeredBranchId,
|
||||||
};
|
};
|
||||||
|
|
||||||
const tempValue = branchOption.value.find(
|
|
||||||
(v) => v.id === data.registeredBranchId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tempValue !== undefined) {
|
|
||||||
readOnlybranchOption.value = false;
|
|
||||||
} else {
|
|
||||||
readOnlybranchOption.value = true;
|
|
||||||
branchOption.value = [
|
|
||||||
{
|
|
||||||
id: data.registeredBranch.id,
|
|
||||||
name: data.registeredBranch.name,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevService = ref<ServiceCreate>({
|
const prevService = ref<ServiceCreate>({
|
||||||
|
|
@ -1195,8 +1151,8 @@ async function submitProduct(notClose = false) {
|
||||||
|
|
||||||
async function submitGroup() {
|
async function submitGroup() {
|
||||||
if (drawerInfo.value) {
|
if (drawerInfo.value) {
|
||||||
if (currentIdGropTree.value)
|
if (currentIdGroupTree.value)
|
||||||
await editProductGroup(currentIdGropTree.value, formDataGroup.value);
|
await editProductGroup(currentIdGroupTree.value, formDataGroup.value);
|
||||||
else await editProductGroup(currentIdGroup.value, formDataGroup.value);
|
else await editProductGroup(currentIdGroup.value, formDataGroup.value);
|
||||||
} else {
|
} else {
|
||||||
const res = await createProductGroup(formDataGroup.value);
|
const res = await createProductGroup(formDataGroup.value);
|
||||||
|
|
@ -1207,7 +1163,7 @@ async function submitGroup() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIdGropTree.value = '';
|
currentIdGroupTree.value = '';
|
||||||
drawerInfo.value = false;
|
drawerInfo.value = false;
|
||||||
await fetchListGroups();
|
await fetchListGroups();
|
||||||
clearFormGroup();
|
clearFormGroup();
|
||||||
|
|
@ -1619,20 +1575,17 @@ watch(
|
||||||
async () => {
|
async () => {
|
||||||
if (productMode === 'group') {
|
if (productMode === 'group') {
|
||||||
clearFormGroup();
|
clearFormGroup();
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
dialogInputForm = true;
|
dialogInputForm = true;
|
||||||
}
|
}
|
||||||
if (productMode === 'product') {
|
if (productMode === 'product') {
|
||||||
productTab = 1;
|
productTab = 1;
|
||||||
clearFormProduct();
|
clearFormProduct();
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
dialogProduct = true;
|
dialogProduct = true;
|
||||||
}
|
}
|
||||||
if (productMode === 'service') {
|
if (productMode === 'service') {
|
||||||
serviceTab = 1;
|
serviceTab = 1;
|
||||||
clearFormGroup();
|
clearFormGroup();
|
||||||
clearFormService();
|
clearFormService();
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
serviceTab = 1;
|
serviceTab = 1;
|
||||||
dialogService = true;
|
dialogService = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1780,7 +1733,7 @@ watch(
|
||||||
clearFormGroup();
|
clearFormGroup();
|
||||||
await assignFormDataGroup(v);
|
await assignFormDataGroup(v);
|
||||||
isEdit = false;
|
isEdit = false;
|
||||||
currentIdGropTree = v.id;
|
currentIdGroupTree = v.id;
|
||||||
drawerInfo = true;
|
drawerInfo = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1792,7 +1745,7 @@ watch(
|
||||||
clearFormGroup();
|
clearFormGroup();
|
||||||
await assignFormDataGroup(v);
|
await assignFormDataGroup(v);
|
||||||
isEdit = true;
|
isEdit = true;
|
||||||
currentIdGropTree = v.id;
|
currentIdGroupTree = v.id;
|
||||||
drawerInfo = true;
|
drawerInfo = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2803,7 +2756,6 @@ watch(
|
||||||
flat
|
flat
|
||||||
@click.stop="
|
@click.stop="
|
||||||
async () => {
|
async () => {
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
if (props.row.type === 'product') {
|
if (props.row.type === 'product') {
|
||||||
currentIdProduct = props.row.id;
|
currentIdProduct = props.row.id;
|
||||||
assignFormDataProduct(props.row);
|
assignFormDataProduct(props.row);
|
||||||
|
|
@ -2823,7 +2775,6 @@ watch(
|
||||||
:id-name="props.row.name"
|
:id-name="props.row.name"
|
||||||
@view="
|
@view="
|
||||||
async () => {
|
async () => {
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
if (props.row.type === 'product') {
|
if (props.row.type === 'product') {
|
||||||
currentIdProduct = props.row.id;
|
currentIdProduct = props.row.id;
|
||||||
assignFormDataProduct(props.row);
|
assignFormDataProduct(props.row);
|
||||||
|
|
@ -2839,7 +2790,6 @@ watch(
|
||||||
"
|
"
|
||||||
@edit="
|
@edit="
|
||||||
async () => {
|
async () => {
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
if (props.row.type === 'product') {
|
if (props.row.type === 'product') {
|
||||||
currentIdProduct = props.row.id;
|
currentIdProduct = props.row.id;
|
||||||
infoProductEdit = true;
|
infoProductEdit = true;
|
||||||
|
|
@ -2896,7 +2846,6 @@ watch(
|
||||||
"
|
"
|
||||||
@menu-view-detail="
|
@menu-view-detail="
|
||||||
async () => {
|
async () => {
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
if (row.type === 'product') {
|
if (row.type === 'product') {
|
||||||
currentIdProduct = row.id;
|
currentIdProduct = row.id;
|
||||||
assignFormDataProduct(row);
|
assignFormDataProduct(row);
|
||||||
|
|
@ -2912,7 +2861,6 @@ watch(
|
||||||
"
|
"
|
||||||
@menu-edit="
|
@menu-edit="
|
||||||
async () => {
|
async () => {
|
||||||
await fetchListOfOptionBranch();
|
|
||||||
if (row.type === 'product') {
|
if (row.type === 'product') {
|
||||||
currentIdProduct = row.id;
|
currentIdProduct = row.id;
|
||||||
infoProductEdit = true;
|
infoProductEdit = true;
|
||||||
|
|
@ -3125,7 +3073,6 @@ watch(
|
||||||
<BasicInformation
|
<BasicInformation
|
||||||
ide="form-group"
|
ide="form-group"
|
||||||
dense
|
dense
|
||||||
v-model:options-branch="branchOption"
|
|
||||||
v-model:remark="formDataGroup.remark"
|
v-model:remark="formDataGroup.remark"
|
||||||
v-model:name="formDataGroup.name"
|
v-model:name="formDataGroup.name"
|
||||||
v-model:detail="formDataGroup.detail"
|
v-model:detail="formDataGroup.detail"
|
||||||
|
|
@ -3305,8 +3252,7 @@ watch(
|
||||||
id="info-group"
|
id="info-group"
|
||||||
dense
|
dense
|
||||||
:readonly="!isEdit"
|
:readonly="!isEdit"
|
||||||
:read-onlybranch-option="!readOnlybranchOption"
|
branch-readonly
|
||||||
v-model:options-branch="branchOption"
|
|
||||||
v-model:registered-branch-id="formDataGroup.registeredBranchId"
|
v-model:registered-branch-id="formDataGroup.registeredBranchId"
|
||||||
v-model:remark="formDataGroup.remark"
|
v-model:remark="formDataGroup.remark"
|
||||||
v-model:name="formDataGroup.name"
|
v-model:name="formDataGroup.name"
|
||||||
|
|
@ -4432,6 +4378,7 @@ watch(
|
||||||
'q-py-sm q-px-lg': !$q.screen.gt.sm,
|
'q-py-sm q-px-lg': !$q.screen.gt.sm,
|
||||||
}"
|
}"
|
||||||
style="height: 100%; max-height: 100%; overflow-y: auto"
|
style="height: 100%; max-height: 100%; overflow-y: auto"
|
||||||
|
v-if="dialogServiceEdit"
|
||||||
>
|
>
|
||||||
<BasicInformation
|
<BasicInformation
|
||||||
v-if="serviceTab === 1"
|
v-if="serviceTab === 1"
|
||||||
|
|
|
||||||
|
|
@ -837,7 +837,7 @@ watch(
|
||||||
</q-item>
|
</q-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #selectedItem="{ scope }">
|
<template #selected-item="{ scope }">
|
||||||
<q-item-section v-if="scope.opt">
|
<q-item-section v-if="scope.opt">
|
||||||
{{ scope.opt.name }}
|
{{ scope.opt.name }}
|
||||||
<span class="app-text-muted text-caption">
|
<span class="app-text-muted text-caption">
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ export function selectFilterOptionRefMod(
|
||||||
destination.value = source.value.filter((v) => {
|
destination.value = source.value.filter((v) => {
|
||||||
const label = v[filterField || 'label'];
|
const label = v[filterField || 'label'];
|
||||||
|
|
||||||
|
if (!label) return;
|
||||||
if (typeof label !== 'string') {
|
if (typeof label !== 'string') {
|
||||||
throw new Error('Label must be of type string.');
|
throw new Error('Label must be of type string.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue