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:
Methapon Metanipat 2024-11-12 11:56:14 +07:00 committed by GitHub
parent a227744131
commit d414685fe7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 500 additions and 319 deletions

View file

@ -1,9 +1,5 @@
<script setup lang="ts">
import { QSelect } from 'quasar';
import { getRole } from 'src/services/keycloak';
import { selectFilterOptionRefMod } from 'stores/utils';
import { watch } from 'vue';
import { ref } from 'vue';
import SelectBranch from '../shared/select/SelectBranch.vue';
const remark = defineModel<string>('remark');
const detail = defineModel<string>('detail');
@ -16,39 +12,16 @@ const serviceName = defineModel<string>('serviceNameTh');
const serviceDescription = defineModel<string>('serviceDescription');
const registeredBranchId = defineModel<string>('registeredBranchId');
const optionsBranch = defineModel<{ id: string; name: string }[]>(
'optionsBranch',
{ default: [] },
);
defineProps<{
dense?: boolean;
outlined?: boolean;
readonly?: boolean;
readOnlybranchOption?: boolean;
branchReadonly?: boolean;
separator?: boolean;
isType?: boolean;
disableCode?: boolean;
service?: boolean;
}>();
const branchOptions = ref<Record<string, unknown>[]>([]);
let branchFilter = selectFilterOptionRefMod(
optionsBranch,
branchOptions,
'name',
);
watch(
() => optionsBranch.value,
() => {
branchFilter = selectFilterOptionRefMod(
optionsBranch,
branchOptions,
'name',
);
},
);
</script>
<template>
@ -80,51 +53,14 @@ watch(
</div>
<div v-if="!service" class="col-12 row q-col-gutter-sm">
<q-select
outlined
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
<SelectBranch
class="col-md-12 col-12"
option-value="id"
option-label="name"
v-model="registeredBranchId"
id="input-source-nationality"
for="input-source-nationality"
: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>
v-model:value="registeredBranchId"
:readonly
:disabled="!readonly && branchReadonly"
clearable
required
/>
<q-input
for="input-code"
:dense="dense"

View file

@ -1,30 +1,12 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
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';
import SelectCustomer from '../shared/select/SelectCustomer.vue';
import SelectBranch from '../shared/select/SelectBranch.vue';
const { locale } = useI18n({ useScope: 'global' });
const branchStore = useBranchStore();
const customerStore = useCustomerStore();
const optionStore = useOptionStore();
const branchId = defineModel<string>('branchId');
const customerBranchId = defineModel<string>('customerBranchId');
const agentPrice = defineModel<boolean>('agentPrice');
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<{
outlined?: boolean;
readonly?: boolean;
@ -40,103 +22,6 @@ defineProps<{
defineEmits<{
(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>
<template>
<div class="row">
@ -170,62 +55,24 @@ onMounted(async () => {
</div>
</div>
<div class="col-12 row q-col-gutter-sm">
<SelectInput
:readonly
incremental
v-model="branchId"
id="quotation-branch"
class="col-md col-12"
:option="branchOption"
<SelectBranch
v-model:value="branchId"
:label="$t('quotation.branch')"
:option-label="locale === 'eng' ? 'labelEN' : 'label'"
:rules="[(val: string) => !!val || $t('form.error.required')]"
@filter="(val: string, update) => filter(val, update, 'branch')"
/>
<SelectInput
class="col-md-6 col-12"
simple
required
:readonly
incremental
v-model="customerBranchId"
class="col-md col-12"
id="quotation-customer"
:option="customerOption"
/>
<SelectCustomer
v-model:value="customerBranchId"
:label="$t('quotation.customer')"
:option-label="locale === 'eng' ? 'labelEN' : 'label'"
:rules="[(val: string) => !!val || $t('form.error.required')]"
@filter="(val: string, update) => filter(val, update, 'customer')"
>
<template #option="{ scope }">
<q-item
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>
@create="$emit('addCustomer')"
class="col-md-6 col-12"
creatable
simple
required
:readonly
/>
</div>
</div>
</template>

View file

@ -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 { selectFilterOptionRefMod } from 'src/stores/utils';
import { QSelect } from 'quasar';
@ -15,9 +15,9 @@ const props = withDefaults(
defineProps<{
id?: string;
label?: string;
option: Record<string, unknown>[];
optionLabel?: string;
optionValue?: string;
option: T[];
optionLabel?: keyof T;
optionValue?: keyof T;
placeholder?: string;
hideSelected?: boolean;
@ -40,14 +40,18 @@ const props = withDefaults(
);
defineEmits<{
(e: 'filter', val: string, update: void): void;
(
e: 'filter',
val: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
): void;
}>();
onMounted(() => {
defaultFilter = selectFilterOptionRefMod(
ref(props.option),
options,
props.optionLabel,
typeof props.optionLabel === 'string' ? props.optionLabel : 'label',
);
});
@ -57,7 +61,7 @@ watch(
defaultFilter = selectFilterOptionRefMod(
ref(props.option),
options,
props.optionLabel,
typeof props.optionLabel === 'string' ? props.optionLabel : 'label',
);
},
);
@ -76,8 +80,12 @@ watch(
:fill-input="fillInput && !!model"
:hide-dropdown-icon="readonly"
input-debounce="500"
:option-value="optionValue"
:option-label="optionLabel"
:option-value="
typeof props.optionValue === 'string' ? props.optionValue : 'value'
"
:option-label="
typeof props.optionLabel === 'string' ? props.optionLabel : 'label'
"
v-model="model"
dense
autocomplete="off"
@ -95,9 +103,13 @@ watch(
<template v-if="$slots.prepend" v-slot:prepend>
<slot name="prepend"></slot>
</template>
<template v-if="$slots.append" v-slot:append>
<slot name="append"></slot>
</template>
<template v-slot:no-option>
<slot name="noOption"></slot>
<slot name="no-option"></slot>
<q-item v-if="!$slots.noOption">
<q-item-section class="text-grey">
@ -106,12 +118,20 @@ watch(
</q-item>
</template>
<template v-if="$slots.selectedItem" v-slot:selected-item="scope">
<slot name="selectedItem" :scope="scope"></slot>
<template
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 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>
</q-select>
</template>

View 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>

View 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>

View 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>

View 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 };
};

View file

@ -440,7 +440,7 @@ export default {
issueDate: 'Issue Date',
passportExpiryDate: 'Passport Expiry Date',
ownerName: 'Owner Name',
ownerName: 'Customer Name',
firstName: 'First Name ',
lastName: 'Last Name ',
firstNameEN: 'First Name in English',

View file

@ -436,7 +436,7 @@ export default {
issueDate: 'วันที่ออกหนังสือ',
passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง',
ownerName: 'ชื่อเจ้าของ',
ownerName: 'ชื่อนายจ้าง',
firstName: 'ชื่อ ',
lastName: 'นามสกุล ',
firstNameEN: 'ชื่อ ภาษาอังกฤษ',

View file

@ -2,7 +2,6 @@
import { ref, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue';
import { getUserId, getRole } from 'src/services/keycloak';
import { storeToRefs } from 'pinia';
import { useQuasar, type QTableProps } from 'quasar';
@ -38,15 +37,10 @@ import {
} from 'components/button';
import useFlowStore from 'stores/flow';
import useMyBranchStore from 'stores/my-branch';
import { dateFormat } from 'src/utils/datetime';
import { formatNumberDecimal, isRoleInclude } from 'stores/utils';
const userBranchStore = useMyBranchStore();
const { currentMyBranch } = storeToRefs(userBranchStore);
import { Status } from 'stores/types';
import { dialog, dialogWarningClose } from 'stores/utils';
@ -98,7 +92,6 @@ const {
} = productServiceStore;
const { workNameItems, splitPay } = storeToRefs(productServiceStore);
const readOnlybranchOption = ref<boolean>(false);
const allStat = ref<{ mode: string; count: number }[]>([]);
const stat = ref<
{
@ -500,13 +493,11 @@ const currentIdGroup = ref<string>('');
const currentIdType = ref<string>('');
const currentIdService = ref<string>('');
const currentIdProduct = ref<string>('');
const currentIdGropTree = ref<string>('');
const currentIdGroupTree = ref<string>('');
const currentStatusGroupType = ref<Status>('CREATED');
const currentIdGroupType = ref('');
const branchOption = ref<{ id: string; name: string }[]>([]);
const currentStatus = ref<Status | 'All'>('All');
// 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() {
const res = await fetchProductGroup({
page: currentPageGroup.value,
@ -863,7 +837,6 @@ function undoProductGroup() {
registeredBranchId: previousValue.value.registeredBranchId,
};
isEdit.value = false;
readOnlybranchOption.value = false;
flowStore.rotate();
}
@ -871,7 +844,6 @@ async function assignFormDataGroup(data: ProductGroup) {
previousValue.value = data;
currentStatusGroupType.value = data.status;
currentIdGroupType.value = data.id;
await fetchListOfOptionBranch();
formDataGroup.value = {
remark: data.remark,
@ -881,22 +853,6 @@ async function assignFormDataGroup(data: ProductGroup) {
shared: data.shared,
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>({
@ -1195,8 +1151,8 @@ async function submitProduct(notClose = false) {
async function submitGroup() {
if (drawerInfo.value) {
if (currentIdGropTree.value)
await editProductGroup(currentIdGropTree.value, formDataGroup.value);
if (currentIdGroupTree.value)
await editProductGroup(currentIdGroupTree.value, formDataGroup.value);
else await editProductGroup(currentIdGroup.value, formDataGroup.value);
} else {
const res = await createProductGroup(formDataGroup.value);
@ -1207,7 +1163,7 @@ async function submitGroup() {
}
}
currentIdGropTree.value = '';
currentIdGroupTree.value = '';
drawerInfo.value = false;
await fetchListGroups();
clearFormGroup();
@ -1619,20 +1575,17 @@ watch(
async () => {
if (productMode === 'group') {
clearFormGroup();
await fetchListOfOptionBranch();
dialogInputForm = true;
}
if (productMode === 'product') {
productTab = 1;
clearFormProduct();
await fetchListOfOptionBranch();
dialogProduct = true;
}
if (productMode === 'service') {
serviceTab = 1;
clearFormGroup();
clearFormService();
await fetchListOfOptionBranch();
serviceTab = 1;
dialogService = true;
}
@ -1780,7 +1733,7 @@ watch(
clearFormGroup();
await assignFormDataGroup(v);
isEdit = false;
currentIdGropTree = v.id;
currentIdGroupTree = v.id;
drawerInfo = true;
}
}
@ -1792,7 +1745,7 @@ watch(
clearFormGroup();
await assignFormDataGroup(v);
isEdit = true;
currentIdGropTree = v.id;
currentIdGroupTree = v.id;
drawerInfo = true;
}
}
@ -2803,7 +2756,6 @@ watch(
flat
@click.stop="
async () => {
await fetchListOfOptionBranch();
if (props.row.type === 'product') {
currentIdProduct = props.row.id;
assignFormDataProduct(props.row);
@ -2823,7 +2775,6 @@ watch(
:id-name="props.row.name"
@view="
async () => {
await fetchListOfOptionBranch();
if (props.row.type === 'product') {
currentIdProduct = props.row.id;
assignFormDataProduct(props.row);
@ -2839,7 +2790,6 @@ watch(
"
@edit="
async () => {
await fetchListOfOptionBranch();
if (props.row.type === 'product') {
currentIdProduct = props.row.id;
infoProductEdit = true;
@ -2896,7 +2846,6 @@ watch(
"
@menu-view-detail="
async () => {
await fetchListOfOptionBranch();
if (row.type === 'product') {
currentIdProduct = row.id;
assignFormDataProduct(row);
@ -2912,7 +2861,6 @@ watch(
"
@menu-edit="
async () => {
await fetchListOfOptionBranch();
if (row.type === 'product') {
currentIdProduct = row.id;
infoProductEdit = true;
@ -3125,7 +3073,6 @@ watch(
<BasicInformation
ide="form-group"
dense
v-model:options-branch="branchOption"
v-model:remark="formDataGroup.remark"
v-model:name="formDataGroup.name"
v-model:detail="formDataGroup.detail"
@ -3305,8 +3252,7 @@ watch(
id="info-group"
dense
:readonly="!isEdit"
:read-onlybranch-option="!readOnlybranchOption"
v-model:options-branch="branchOption"
branch-readonly
v-model:registered-branch-id="formDataGroup.registeredBranchId"
v-model:remark="formDataGroup.remark"
v-model:name="formDataGroup.name"
@ -4432,6 +4378,7 @@ watch(
'q-py-sm q-px-lg': !$q.screen.gt.sm,
}"
style="height: 100%; max-height: 100%; overflow-y: auto"
v-if="dialogServiceEdit"
>
<BasicInformation
v-if="serviceTab === 1"

View file

@ -837,7 +837,7 @@ watch(
</q-item>
</template>
<template #selectedItem="{ scope }">
<template #selected-item="{ scope }">
<q-item-section v-if="scope.opt">
{{ scope.opt.name }}
<span class="app-text-muted text-caption">

View file

@ -134,6 +134,7 @@ export function selectFilterOptionRefMod(
destination.value = source.value.filter((v) => {
const label = v[filterField || 'label'];
if (!label) return;
if (typeof label !== 'string') {
throw new Error('Label must be of type string.');
}