refactor: quotation
This commit is contained in:
parent
3bead87e03
commit
a4ff3052fe
5 changed files with 348 additions and 140 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import useBranchStore from 'src/stores/branch';
|
import useBranchStore from 'src/stores/branch';
|
||||||
import useCustomerStore from 'src/stores/customer';
|
import useCustomerStore from 'src/stores/customer';
|
||||||
|
|
@ -10,8 +10,8 @@ const { locale } = useI18n({ useScope: 'global' });
|
||||||
const branchStore = useBranchStore();
|
const branchStore = useBranchStore();
|
||||||
const customerStore = useCustomerStore();
|
const customerStore = useCustomerStore();
|
||||||
|
|
||||||
const branch = defineModel<string>('branch');
|
const branchId = defineModel<string>('branchId');
|
||||||
const customer = defineModel<string>('customer');
|
const customerBranchId = defineModel<string>('customerBranchId');
|
||||||
const agentPrice = defineModel<boolean>('agentPrice');
|
const agentPrice = defineModel<boolean>('agentPrice');
|
||||||
|
|
||||||
const branchOption = ref();
|
const branchOption = ref();
|
||||||
|
|
@ -23,7 +23,7 @@ defineProps<{
|
||||||
separator?: boolean;
|
separator?: boolean;
|
||||||
employee?: boolean;
|
employee?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
prefixId: string;
|
inputOnly?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
|
|
@ -37,37 +37,7 @@ async function filter(
|
||||||
) {
|
) {
|
||||||
update(
|
update(
|
||||||
async () => {
|
async () => {
|
||||||
const res =
|
await init(val, type);
|
||||||
type === 'branch'
|
|
||||||
? await branchStore.fetchList({
|
|
||||||
query: val,
|
|
||||||
pageSize: 30,
|
|
||||||
})
|
|
||||||
: await customerStore.fetchList({
|
|
||||||
query: val,
|
|
||||||
pageSize: 30,
|
|
||||||
});
|
|
||||||
if (res) {
|
|
||||||
if (type === 'branch') {
|
|
||||||
branchOption.value = res.result.map((v) => ({
|
|
||||||
value: v.id,
|
|
||||||
label: v.name,
|
|
||||||
labelEN: v.nameEN,
|
|
||||||
}));
|
|
||||||
} else if (type === 'customer') {
|
|
||||||
customerOption.value = res.result.map((v) => ({
|
|
||||||
value: v.id,
|
|
||||||
label:
|
|
||||||
v.customerType === 'CORP'
|
|
||||||
? v.branch[0].registerName
|
|
||||||
: `${v.branch[0].firstName} ${v.branch[0].lastName}`,
|
|
||||||
labelEN:
|
|
||||||
v.customerType === 'CORP'
|
|
||||||
? v.branch[0].registerNameEN
|
|
||||||
: `${v.branch[0].firstNameEN} ${v.branch[0].lastNameEN}`,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
(ref: QSelect) => {
|
(ref: QSelect) => {
|
||||||
|
|
@ -78,10 +48,56 @@ async function filter(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function init(val: string, type: 'branch' | 'customer') {
|
||||||
|
const res =
|
||||||
|
type === 'branch'
|
||||||
|
? await branchStore.fetchList({
|
||||||
|
query: val,
|
||||||
|
pageSize: 30,
|
||||||
|
})
|
||||||
|
: await customerStore.fetchListCustomeBranch({
|
||||||
|
query: val,
|
||||||
|
registeredBranchId: branchId.value || undefined,
|
||||||
|
pageSize: 30,
|
||||||
|
});
|
||||||
|
if (res) {
|
||||||
|
if (type === 'branch') {
|
||||||
|
branchOption.value = res.result.map((v) => ({
|
||||||
|
value: v.id,
|
||||||
|
label: v.name,
|
||||||
|
labelEN: v.nameEN,
|
||||||
|
}));
|
||||||
|
} else if (type === 'customer') {
|
||||||
|
customerOption.value = res.result.map((v) => ({
|
||||||
|
value: v.id,
|
||||||
|
label: v.registerName || `${v.firstName} ${v.lastName}` || '-',
|
||||||
|
labelEN: v.registerNameEN || `${v.firstNameEN} ${v.lastNameEN}` || '-',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await init('', 'branch');
|
||||||
|
await init('', 'customer');
|
||||||
|
});
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => branchId.value,
|
||||||
|
// async (v) => {
|
||||||
|
// if (v) {
|
||||||
|
// customerBranchId.value = '';
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// );
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="row col-12">
|
<div class="row">
|
||||||
<div class="col-12 row items-center q-pb-sm text-weight-bold text-body1">
|
<div
|
||||||
|
v-if="!inputOnly"
|
||||||
|
class="col-12 row items-center q-pb-sm text-weight-bold text-body1"
|
||||||
|
>
|
||||||
<q-icon
|
<q-icon
|
||||||
flat
|
flat
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|
@ -104,7 +120,7 @@ async function filter(
|
||||||
<SelectInput
|
<SelectInput
|
||||||
:readonly
|
:readonly
|
||||||
incremental
|
incremental
|
||||||
v-model="branch"
|
v-model="branchId"
|
||||||
id="quotation-branch"
|
id="quotation-branch"
|
||||||
class="col-md col-12"
|
class="col-md col-12"
|
||||||
:option="branchOption"
|
:option="branchOption"
|
||||||
|
|
@ -117,7 +133,7 @@ async function filter(
|
||||||
<SelectInput
|
<SelectInput
|
||||||
:readonly
|
:readonly
|
||||||
incremental
|
incremental
|
||||||
v-model="customer"
|
v-model="customerBranchId"
|
||||||
class="col-md col-12"
|
class="col-md col-12"
|
||||||
id="quotation-customer"
|
id="quotation-customer"
|
||||||
:option="customerOption"
|
:option="customerOption"
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,9 @@ defineEmits<{
|
||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
rows: {
|
rows: {
|
||||||
|
[key: string]: any;
|
||||||
code: string;
|
code: string;
|
||||||
list: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
pricePerUnit: number;
|
pricePerUnit: number;
|
||||||
|
|
@ -48,10 +49,10 @@ const columns = [
|
||||||
field: 'code',
|
field: 'code',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'list',
|
name: 'name',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
label: 'productService.service.list',
|
label: 'productService.service.list',
|
||||||
field: 'list',
|
field: 'name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'amount',
|
name: 'amount',
|
||||||
|
|
@ -100,7 +101,7 @@ const columns = [
|
||||||
button-delete
|
button-delete
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
imgColumn="list"
|
imgColumn="name"
|
||||||
:customColumn="['amount', 'pricePerUnit', 'discount', 'tax', 'sumPrice']"
|
:customColumn="['amount', 'pricePerUnit', 'discount', 'tax', 'sumPrice']"
|
||||||
@delete="$emit('delete')"
|
@delete="$emit('delete')"
|
||||||
>
|
>
|
||||||
|
|
@ -108,16 +109,8 @@ const columns = [
|
||||||
<q-avatar class="q-mr-sm" size="md">
|
<q-avatar class="q-mr-sm" size="md">
|
||||||
<q-icon
|
<q-icon
|
||||||
class="full-width full-height"
|
class="full-width full-height"
|
||||||
:name="
|
name="mdi-shopping-outline"
|
||||||
props.row.type === 'product'
|
:style="`color: var(--teal-10); background: hsla(var(--teal-${$q.dark.isActive ? '8' : '10'}-hsl)/0.15)`"
|
||||||
? 'mdi-shopping-outline'
|
|
||||||
: 'mdi-server-outline'
|
|
||||||
"
|
|
||||||
:style="`color: var(--${props.row.type === 'product' ? 'teal-10' : 'orange-5'}); background: ${
|
|
||||||
props.row.type === 'product'
|
|
||||||
? `hsla(var(--teal-${$q.dark.isActive ? '8' : '10'}-hsl)/0.15)`
|
|
||||||
: `hsla(var(--orange-${$q.dark.isActive ? '6' : '5'}-hsl)/0.15)`
|
|
||||||
}`"
|
|
||||||
/>
|
/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { pageTabs, fieldSelectedOption } from './constants';
|
import { pageTabs, fieldSelectedOption } from './constants';
|
||||||
|
import { useQuotationForm } from './form';
|
||||||
|
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
@ -35,6 +37,7 @@ import ItemCard from 'src/components/ItemCard.vue';
|
||||||
import DialogForm from 'components/DialogForm.vue';
|
import DialogForm from 'components/DialogForm.vue';
|
||||||
import { AddButton } from 'src/components/button';
|
import { AddButton } from 'src/components/button';
|
||||||
import SideMenu from 'components/SideMenu.vue';
|
import SideMenu from 'components/SideMenu.vue';
|
||||||
|
import ButtonAddComponent from 'components/ButtonAddCompoent.vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
uploadFileListEmployee,
|
uploadFileListEmployee,
|
||||||
|
|
@ -71,17 +74,7 @@ import {
|
||||||
import QuotationView from './QuotationView.vue';
|
import QuotationView from './QuotationView.vue';
|
||||||
import { watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
|
|
||||||
type Node = {
|
const quotationForm = useQuotationForm();
|
||||||
[key: string]: any;
|
|
||||||
opened?: boolean;
|
|
||||||
checked?: boolean;
|
|
||||||
bg?: string;
|
|
||||||
fg?: string;
|
|
||||||
icon?: string;
|
|
||||||
displayDropIcon?: boolean;
|
|
||||||
children?: Node[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const customerFormStore = useCustomerForm();
|
const customerFormStore = useCustomerForm();
|
||||||
const employeeFormStore = useEmployeeForm();
|
const employeeFormStore = useEmployeeForm();
|
||||||
const employeeStore = useEmployeeStore();
|
const employeeStore = useEmployeeStore();
|
||||||
|
|
@ -90,12 +83,14 @@ const flowStore = useFlowStore();
|
||||||
const userBranch = useMyBranch();
|
const userBranch = useMyBranch();
|
||||||
const ocrStore = useOcrStore();
|
const ocrStore = useOcrStore();
|
||||||
|
|
||||||
|
const { currentFormData: quotationFormData } = storeToRefs(quotationForm);
|
||||||
const { state: customerFormState, currentFormData: customerFormData } =
|
const { state: customerFormState, currentFormData: customerFormData } =
|
||||||
storeToRefs(customerFormStore);
|
storeToRefs(customerFormStore);
|
||||||
const { state: employeeFormState, currentFromDataEmployee } =
|
const { state: employeeFormState, currentFromDataEmployee } =
|
||||||
storeToRefs(employeeFormStore);
|
storeToRefs(employeeFormStore);
|
||||||
const { currentMyBranch } = storeToRefs(userBranch);
|
const { currentMyBranch } = storeToRefs(userBranch);
|
||||||
|
|
||||||
|
const branchId = ref('');
|
||||||
const currentCustomerId = ref<string | undefined>();
|
const currentCustomerId = ref<string | undefined>();
|
||||||
const refreshImageState = ref(false);
|
const refreshImageState = ref(false);
|
||||||
const emptyCreateDialog = ref(false);
|
const emptyCreateDialog = ref(false);
|
||||||
|
|
@ -360,7 +355,10 @@ function triggerAddQuotationDialog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function triggerQuotationDialog() {
|
function triggerQuotationDialog() {
|
||||||
window.open('/quotation/add-quotation', '_blank');
|
window.open(
|
||||||
|
`/quotation/add-quotation?branchId=${branchId.value}&customerBranchId=${quotationFormData.value.customerBranchId}&date=${Date.now()}`,
|
||||||
|
'_blank',
|
||||||
|
);
|
||||||
// TODO: form and state controll
|
// TODO: form and state controll
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -484,7 +482,12 @@ watch(() => pageState.currentTab, fetchQuotationList);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
{{ convertToTree() }}
|
<ButtonAddComponent
|
||||||
|
hide-icon
|
||||||
|
style="z-index: 999"
|
||||||
|
@click.stop="triggerAddQuotationDialog"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="column full-height no-wrap">
|
<div class="column full-height no-wrap">
|
||||||
<!-- SEC: stat -->
|
<!-- SEC: stat -->
|
||||||
<section class="text-body-2 q-mb-xs flex items-center">
|
<section class="text-body-2 q-mb-xs flex items-center">
|
||||||
|
|
@ -721,7 +724,7 @@ watch(() => pageState.currentTab, fetchQuotationList);
|
||||||
</nav>
|
</nav>
|
||||||
<!-- SEC: body content -->
|
<!-- SEC: body content -->
|
||||||
<article
|
<article
|
||||||
v-if="false"
|
v-if="!quotationData || quotationData.length === 0"
|
||||||
class="col surface-2 flex items-center justify-center"
|
class="col surface-2 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
<CreateButton
|
<CreateButton
|
||||||
|
|
@ -832,6 +835,13 @@ watch(() => pageState.currentTab, fetchQuotationList);
|
||||||
submit-icon="mdi-check"
|
submit-icon="mdi-check"
|
||||||
height="auto"
|
height="auto"
|
||||||
width="60vw"
|
width="60vw"
|
||||||
|
:submit="() => triggerQuotationDialog()"
|
||||||
|
:close="
|
||||||
|
() => {
|
||||||
|
branchId = '';
|
||||||
|
quotationFormData.customerBranchId = '';
|
||||||
|
}
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<header class="q-mx-lg q-mt-lg">
|
<header class="q-mx-lg q-mt-lg">
|
||||||
<ProfileBanner
|
<ProfileBanner
|
||||||
|
|
@ -852,7 +862,8 @@ watch(() => pageState.currentTab, fetchQuotationList);
|
||||||
style="height: 100%; max-height: 100%; overflow-y: auto"
|
style="height: 100%; max-height: 100%; overflow-y: auto"
|
||||||
>
|
>
|
||||||
<FormAbout
|
<FormAbout
|
||||||
prefixId="zxc"
|
v-model:branch-id="branchId"
|
||||||
|
v-model:customer-branch-id="quotationFormData.customerBranchId"
|
||||||
@add-customer="triggerSelectTypeCustomerd()"
|
@add-customer="triggerSelectTypeCustomerd()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
import { computed, reactive, ref, watch } from 'vue';
|
||||||
import useOptionStore from 'stores/options';
|
|
||||||
import { moveItemUp, moveItemDown, deleteItem } from 'stores/utils';
|
import { moveItemUp, moveItemDown, deleteItem } from 'stores/utils';
|
||||||
|
import useOptionStore from 'stores/options';
|
||||||
|
|
||||||
import DialogForm from 'src/components/DialogForm.vue';
|
import DialogForm from 'src/components/DialogForm.vue';
|
||||||
import TreeView from 'src/components/shared/TreeView.vue';
|
import TreeView from 'src/components/shared/TreeView.vue';
|
||||||
|
|
@ -32,6 +32,7 @@ const optionStore = useOptionStore();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'selectGroup', id: string): void;
|
(e: 'selectGroup', id: string): void;
|
||||||
|
(e: 'submit', nodes: Node[]): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const model = defineModel<boolean>();
|
const model = defineModel<boolean>();
|
||||||
|
|
@ -186,6 +187,9 @@ function mapNode() {
|
||||||
subtitle: p.product.code || ' ',
|
subtitle: p.product.code || ' ',
|
||||||
detail: p.product.detail,
|
detail: p.product.detail,
|
||||||
remark: p.product.remark,
|
remark: p.product.remark,
|
||||||
|
price: p.product.price,
|
||||||
|
agentPrice: p.product.agentPrice,
|
||||||
|
serviceCharge: p.product.serviceCharge,
|
||||||
icon: 'mdi-shopping-outline',
|
icon: 'mdi-shopping-outline',
|
||||||
bg: 'hsla(var(--teal-10-hsl)/0.1)',
|
bg: 'hsla(var(--teal-10-hsl)/0.1)',
|
||||||
fg: 'var(--teal-10)',
|
fg: 'var(--teal-10)',
|
||||||
|
|
@ -207,6 +211,9 @@ function mapNode() {
|
||||||
subtitle: p.product.code || ' ',
|
subtitle: p.product.code || ' ',
|
||||||
detail: p.product.detail,
|
detail: p.product.detail,
|
||||||
remark: p.product.remark,
|
remark: p.product.remark,
|
||||||
|
price: p.product.price,
|
||||||
|
agentPrice: p.product.agentPrice,
|
||||||
|
serviceCharge: p.product.serviceCharge,
|
||||||
type: 'product',
|
type: 'product',
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
@ -220,6 +227,9 @@ function mapNode() {
|
||||||
subtitle: v.code,
|
subtitle: v.code,
|
||||||
detail: v.detail,
|
detail: v.detail,
|
||||||
remark: v.remark,
|
remark: v.remark,
|
||||||
|
price: v.price,
|
||||||
|
agentPrice: v.agentPrice,
|
||||||
|
serviceCharge: v.serviceCharge,
|
||||||
type: 'product',
|
type: 'product',
|
||||||
icon: 'mdi-shopping-outline',
|
icon: 'mdi-shopping-outline',
|
||||||
bg: 'hsla(var(--teal-10-hsl)/0.1)',
|
bg: 'hsla(var(--teal-10-hsl)/0.1)',
|
||||||
|
|
@ -285,6 +295,7 @@ watch(
|
||||||
:title="$t('general.list', { msg: $t('productService.title') })"
|
:title="$t('general.list', { msg: $t('productService.title') })"
|
||||||
:submit-label="$t('general.select', { msg: $t('productService.title') })"
|
:submit-label="$t('general.select', { msg: $t('productService.title') })"
|
||||||
submit-icon="mdi-check"
|
submit-icon="mdi-check"
|
||||||
|
:submit="() => $emit('submit', nodes)"
|
||||||
>
|
>
|
||||||
<q-splitter
|
<q-splitter
|
||||||
v-model="splitterModel"
|
v-model="splitterModel"
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,57 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { setLocale } from 'src/utils/datetime';
|
||||||
import { QSelect, useQuasar } from 'quasar';
|
import { QSelect, useQuasar } from 'quasar';
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import ProductServiceForm from './ProductServiceForm.vue';
|
||||||
import WorkerItem from 'components/05_quotation/WorkerItem.vue';
|
import WorkerItem from 'components/05_quotation/WorkerItem.vue';
|
||||||
import QuotationFormInfo from './QuotationFormInfo.vue';
|
import QuotationFormInfo from './QuotationFormInfo.vue';
|
||||||
import { AddButton, SaveButton } from 'components/button';
|
|
||||||
import ToggleButton from 'src/components/button/ToggleButton.vue';
|
import ToggleButton from 'src/components/button/ToggleButton.vue';
|
||||||
import ProductItem from 'src/components/05_quotation/ProductItem.vue';
|
import ProductItem from 'src/components/05_quotation/ProductItem.vue';
|
||||||
import { setLocale } from 'src/utils/datetime';
|
import FormAbout from 'src/components/05_quotation/FormAbout.vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import DialogForm from 'src/components/DialogForm.vue';
|
||||||
|
import SelectZone from 'src/components/shared/SelectZone.vue';
|
||||||
|
import PersonCard from 'src/components/shared/PersonCard.vue';
|
||||||
|
import { AddButton, SaveButton } from 'components/button';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useQuotationForm } from './form';
|
||||||
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
|
import { Employee } from 'src/stores/employee/types';
|
||||||
|
import {
|
||||||
|
ProductGroup,
|
||||||
|
ProductList,
|
||||||
|
Service,
|
||||||
|
} from 'src/stores/product-service/types';
|
||||||
|
import useProductServiceStore from 'src/stores/product-service';
|
||||||
|
|
||||||
|
type Node = {
|
||||||
|
[key: string]: any;
|
||||||
|
opened?: boolean;
|
||||||
|
checked?: boolean;
|
||||||
|
bg?: string;
|
||||||
|
fg?: string;
|
||||||
|
icon?: string;
|
||||||
|
children?: Node[];
|
||||||
|
};
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const productServiceStore = useProductServiceStore();
|
||||||
|
const quotationForm = useQuotationForm();
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
|
const { currentFormData: quotationFormData } = storeToRefs(quotationForm);
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
||||||
|
const date = ref();
|
||||||
|
const rows = ref<Node[]>([]);
|
||||||
|
const selectedEmployee = ref<Employee[]>([]);
|
||||||
const selectedBranchIssuer = ref('');
|
const selectedBranchIssuer = ref('');
|
||||||
const selectedCustomer = ref('');
|
const selectedCustomer = ref('');
|
||||||
const toggleWorker = ref(true);
|
const toggleWorker = ref(true);
|
||||||
|
const branchId = ref('');
|
||||||
|
|
||||||
const quotationNo = ref('');
|
const quotationNo = ref('');
|
||||||
const actor = ref('');
|
const actor = ref('');
|
||||||
|
|
@ -32,6 +64,89 @@ const payType = ref('');
|
||||||
const paySplitCount = ref(1);
|
const paySplitCount = ref(1);
|
||||||
const payBank = ref('');
|
const payBank = ref('');
|
||||||
|
|
||||||
|
const pageState = reactive({
|
||||||
|
hideStat: false,
|
||||||
|
inputSearch: '',
|
||||||
|
statusFilter: 'all',
|
||||||
|
fieldSelected: [],
|
||||||
|
gridView: false,
|
||||||
|
|
||||||
|
currentTab: 'all',
|
||||||
|
addModal: false,
|
||||||
|
quotationModal: false,
|
||||||
|
employeeModal: false,
|
||||||
|
productServiceModal: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
type ProductGroupId = string;
|
||||||
|
|
||||||
|
const productGroup = ref<ProductGroup[]>([]);
|
||||||
|
const productList = ref<Partial<Record<ProductGroupId, ProductList[]>>>({});
|
||||||
|
const serviceList = ref<Partial<Record<ProductGroupId, Service[]>>>({});
|
||||||
|
|
||||||
|
type Id = string;
|
||||||
|
const product = ref<Record<Id, ProductList>>({});
|
||||||
|
const service = ref<Record<Id, Service>>({});
|
||||||
|
|
||||||
|
const selectedGroup = ref<ProductGroup | null>(null);
|
||||||
|
const selectedGroupSub = ref<'product' | 'service' | null>(null);
|
||||||
|
const selectedProductServiceId = ref('');
|
||||||
|
|
||||||
|
async function getAllProduct(
|
||||||
|
groupId: string,
|
||||||
|
opts?: { force?: false; page?: number; pageSize?: number },
|
||||||
|
) {
|
||||||
|
selectedGroupSub.value = 'product';
|
||||||
|
if (!opts?.force && productList.value[groupId] !== undefined) return;
|
||||||
|
const ret = await productServiceStore.fetchListProduct({
|
||||||
|
page: opts?.page ?? 1,
|
||||||
|
pageSize: opts?.pageSize ?? 9999,
|
||||||
|
productGroupId: groupId,
|
||||||
|
});
|
||||||
|
if (ret) productList.value[groupId] = ret.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAllService(
|
||||||
|
groupId: string,
|
||||||
|
opts?: { force?: false; page?: number; pageSize?: number },
|
||||||
|
) {
|
||||||
|
selectedGroupSub.value = 'service';
|
||||||
|
if (!opts?.force && serviceList.value[groupId] !== undefined) return;
|
||||||
|
const ret = await productServiceStore.fetchListService({
|
||||||
|
page: opts?.page ?? 1,
|
||||||
|
pageSize: opts?.pageSize ?? 9999,
|
||||||
|
productGroupId: groupId,
|
||||||
|
fullDetail: true,
|
||||||
|
});
|
||||||
|
if (ret) serviceList.value[groupId] = ret.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerSelectEmployeeDialog() {
|
||||||
|
pageState.employeeModal = true;
|
||||||
|
// TODO: form and state controll
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerProductServiceDialog() {
|
||||||
|
pageState.productServiceModal = true;
|
||||||
|
// TODO: form and state controll
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToTable(nodes: Node[]): Node[] {
|
||||||
|
const products: Node[] = [];
|
||||||
|
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (node.type === 'product' && node.checked) {
|
||||||
|
products.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.children && node.children.length !== 0) {
|
||||||
|
products.push(...convertToTable(node.children)); // Correctly push the converted children
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return products;
|
||||||
|
}
|
||||||
|
|
||||||
function changeMode(mode: string) {
|
function changeMode(mode: string) {
|
||||||
if (mode === 'light') {
|
if (mode === 'light') {
|
||||||
localStorage.setItem('currentTheme', 'light');
|
localStorage.setItem('currentTheme', 'light');
|
||||||
|
|
@ -60,6 +175,19 @@ function changeMode(mode: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
const ret = await productServiceStore.fetchListProductService({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 9999,
|
||||||
|
});
|
||||||
|
if (ret) productGroup.value = ret.result;
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const queryDate = urlParams.get('date');
|
||||||
|
date.value = queryDate && new Date(Number(queryDate));
|
||||||
|
branchId.value = urlParams.get('branchId') || '';
|
||||||
|
quotationFormData.value.customerBranchId =
|
||||||
|
urlParams.get('customerBranchId') || '';
|
||||||
|
|
||||||
const getCurLang = localStorage.getItem('currentLanguage');
|
const getCurLang = localStorage.getItem('currentLanguage');
|
||||||
if (getCurLang === 'English') {
|
if (getCurLang === 'English') {
|
||||||
locale.value = 'eng';
|
locale.value = 'eng';
|
||||||
|
|
@ -97,30 +225,20 @@ onMounted(async () => {
|
||||||
<span class="column text-h6 text-bold q-ml-md">
|
<span class="column text-h6 text-bold q-ml-md">
|
||||||
{{ $t('quotation.title') }}
|
{{ $t('quotation.title') }}
|
||||||
<span class="text-caption text-regular app-text-muted">
|
<span class="text-caption text-regular app-text-muted">
|
||||||
{{ $t('quotation.processOn', { msg: '27 มีนาคม 2567 16:00:01' }) }}
|
{{
|
||||||
|
$t('quotation.processOn', {
|
||||||
|
msg: `${dateFormat(date, true)} ${dateFormat(date, true, true)}`,
|
||||||
|
})
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<q-select
|
<FormAbout
|
||||||
v-model="selectedBranchIssuer"
|
class="col-4"
|
||||||
:options="[{ label: 'Issuer 1', value: 'Issuer 1' }]"
|
input-only
|
||||||
:label="$t('quotation.branch')"
|
v-model:branch-id="branchId"
|
||||||
id="select-branch-issuer"
|
v-model:customer-branch-id="quotationFormData.customerBranchId"
|
||||||
for="select-branch-issuer"
|
@add-customer="triggerSelectTypeCustomerd()"
|
||||||
style="width: 300px"
|
|
||||||
class="q-mr-md"
|
|
||||||
outlined
|
|
||||||
dense
|
|
||||||
/>
|
|
||||||
<q-select
|
|
||||||
v-model="selectedCustomer"
|
|
||||||
:options="[{ label: 'Customer 1', value: 'Customer 1' }]"
|
|
||||||
:label="$t('quotation.customer')"
|
|
||||||
id="select-customer"
|
|
||||||
for="select-customer"
|
|
||||||
style="width: 300px"
|
|
||||||
outlined
|
|
||||||
dense
|
|
||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -164,7 +282,11 @@ onMounted(async () => {
|
||||||
toggleWorker ? $t('general.specify') : $t('general.noSpecify')
|
toggleWorker ? $t('general.specify') : $t('general.noSpecify')
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<AddButton icon-only class="q-ml-auto" @click.stop="" />
|
<AddButton
|
||||||
|
icon-only
|
||||||
|
class="q-ml-auto"
|
||||||
|
@click.stop="triggerSelectEmployeeDialog"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="surface-1 q-pa-md full-width">
|
<div class="surface-1 q-pa-md full-width">
|
||||||
|
|
@ -203,23 +325,22 @@ onMounted(async () => {
|
||||||
{{ $t('quotation.productList') }}
|
{{ $t('quotation.productList') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<AddButton icon-only class="q-ml-auto" @click.stop="" />
|
<AddButton
|
||||||
|
icon-only
|
||||||
|
class="q-ml-auto"
|
||||||
|
@click.stop="triggerProductServiceDialog"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div class="surface-1 q-pa-md full-width">
|
<div class="surface-1 q-pa-md full-width">
|
||||||
<ProductItem
|
<ProductItem
|
||||||
:rows="[
|
:rows="
|
||||||
{
|
rows.map((p) => ({
|
||||||
code: '123456',
|
code: p.subtitle,
|
||||||
list: 'aaa',
|
name: p.title,
|
||||||
type: 'product',
|
}))
|
||||||
amount: 0,
|
"
|
||||||
pricePerUnit: 0,
|
|
||||||
discount: 0,
|
|
||||||
tax: 0,
|
|
||||||
sumPrice: 0,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
/>
|
/>
|
||||||
|
{{ console.log(rows) }}
|
||||||
</div>
|
</div>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
|
||||||
|
|
@ -267,38 +388,6 @@ onMounted(async () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
<!-- <AppBox bordered style="padding: var(--size-3)">
|
|
||||||
<div class="row items-center q-mb-sm">
|
|
||||||
<span style="flex: 1">
|
|
||||||
{{ $t('quotation.employeeList') }}
|
|
||||||
<q-toggle
|
|
||||||
v-model="toggleWorker"
|
|
||||||
id="toggle-status"
|
|
||||||
size="md"
|
|
||||||
padding="none"
|
|
||||||
class="q-ml-md"
|
|
||||||
dense
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<AddButton icon-only />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AppBox class="surface-2 worker-list" v-if="toggleWorker" bordered>
|
|
||||||
<WorkerItem
|
|
||||||
v-for="_ in Array(20)"
|
|
||||||
:data="{
|
|
||||||
no: 1,
|
|
||||||
refNo: 'CC6613334',
|
|
||||||
nationality: 'Thai',
|
|
||||||
birthDate: '1 May 2001',
|
|
||||||
age: '23 Years',
|
|
||||||
fullName: 'Methapon Metanipat',
|
|
||||||
docExpireDate: '16 May 2025',
|
|
||||||
}"
|
|
||||||
color="male"
|
|
||||||
/>
|
|
||||||
</AppBox>
|
|
||||||
</AppBox> -->
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -335,6 +424,94 @@ onMounted(async () => {
|
||||||
<SaveButton solid />
|
<SaveButton solid />
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- SEC: Dialog -->
|
||||||
|
<!-- add employee quotation -->
|
||||||
|
<DialogForm
|
||||||
|
:title="$t('general.select', { msg: $t('quotation.employeeList') })"
|
||||||
|
v-model:modal="pageState.employeeModal"
|
||||||
|
:submit-label="$t('general.select', { msg: $t('quotation.employee') })"
|
||||||
|
submit-icon="mdi-check"
|
||||||
|
height="75vh"
|
||||||
|
>
|
||||||
|
<section class="col row scroll">
|
||||||
|
<SelectZone
|
||||||
|
v-model:selected-item="selectedEmployee"
|
||||||
|
:items="[
|
||||||
|
{
|
||||||
|
name: 'มิเคล่า สุวรรณดี',
|
||||||
|
gender: 'female',
|
||||||
|
telephoneNo: '0621249602',
|
||||||
|
code: 'CORP000000-1-01-240001',
|
||||||
|
birthDate: '16 ปี 11 เดือน 5 วัน',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'มิลิเซนต์ สุวรรณดี',
|
||||||
|
gender: 'female',
|
||||||
|
telephoneNo: '0621249666',
|
||||||
|
code: 'CORP000000-1-01-240002',
|
||||||
|
birthDate: '19 ปี 2 เดือน 2 วัน',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ไรคาร์ด พวงศรี',
|
||||||
|
gender: 'male',
|
||||||
|
telephoneNo: '0621249777',
|
||||||
|
code: 'CORP000000-1-01-240003',
|
||||||
|
birthDate: '39 ปี 5 เดือน 2 วัน',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #top>
|
||||||
|
<AddButton
|
||||||
|
icon-only
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
triggerCreateEmployee();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #data="{ item }">
|
||||||
|
<PersonCard
|
||||||
|
noAction
|
||||||
|
prefixId="asda"
|
||||||
|
class="full-width"
|
||||||
|
:data="{
|
||||||
|
name: item.name,
|
||||||
|
code: item.code,
|
||||||
|
female: item.gender === 'female',
|
||||||
|
male: item.gender === 'male',
|
||||||
|
img: '/images/employee-avatar.png',
|
||||||
|
detail: [
|
||||||
|
{ icon: 'mdi-phone-outline', value: item.telephoneNo },
|
||||||
|
{ icon: 'mdi-clock-outline', value: item.birthDate },
|
||||||
|
],
|
||||||
|
}"
|
||||||
|
></PersonCard>
|
||||||
|
</template>
|
||||||
|
</SelectZone>
|
||||||
|
</section>
|
||||||
|
</DialogForm>
|
||||||
|
|
||||||
|
<!-- add product service -->
|
||||||
|
<ProductServiceForm
|
||||||
|
v-model="pageState.productServiceModal"
|
||||||
|
v-model:product-group="productGroup"
|
||||||
|
v-model:product-list="productList"
|
||||||
|
v-model:service-list="serviceList"
|
||||||
|
@submit="
|
||||||
|
(nodes) => {
|
||||||
|
rows = convertToTable(nodes);
|
||||||
|
pageState.productServiceModal = false;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@select-group="
|
||||||
|
async (id) => {
|
||||||
|
await getAllService(id);
|
||||||
|
await getAllProduct(id);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
></ProductServiceForm>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue