feat: task order (#141)
* feat: task order => routes * feat: Page * refactor: pagination * refactor: taskOrder => table, card and constants * feat: add structure select request list comp * fix: re-export type * refactor: edit path route of task order * feat: trigger task order * refactor: edit type task statss * feat: table select request list * feat: i18n * refactor: quasar expansion chevron color * refactor: type * refactor: state btn status done * feat: task order => order view layout * feat: task order => remark expansion * fix: task order => rename attachment to additional file * feat: upload file section optional layout * feat: task order => additional file expansion * feat: task order => payment expansion * feat: conditionally add urgent * feat: send id together with link * refactor: edit type * feat: new form.ts * refactor: edit url * refactor: edit id trigger * feat: select institution component * feat: task order code i18n * feat: task order => document expansion form * feat: fallback address on null * refactor: add type for table * feat: add filter parameter * refactor: edit name routes * refactor: add type of task order payload * refactor: by value form * refactor: responsive quotation form info * refactor: submit form * refactor: add i18n * refactor: status canceled * refactor: handle task status * refactor: handle mode view * refactor: addtaskstatus * refactor: i18n & constants * refactor: table employee * refactor: select ready request work * refactor: handle save form * refactor: edit layout btn * feat: undo() * cleanup delete import * feat: closetab * refactor: handle readonly * fix: body edit * refactor: handle readonly uploadfile * feat: import manage attachment * refactor: quotation/task-order => type * refactor: select ready request work * refactor: i18n & constants * chore: clean duplicate i18n * refactor: type according to backend relation * refactor: edit base url * feat: upload file * feat: fetch file list * feat: get url file * refactor: set default opened * refactor: type * feat: removefile * feat: task order => select product * feat: add parameter only active branch is selectable * refactor: add i18n * feat: set layout * feat: add info product expansion * refactor: new info messenger * refactor: add slot name value * refactor: add i18n * refactor: edit type task status * refactor: use date format * refactor: value can null * refactor: add i18n * cleanup * feat: productlistinput * refactor: edit i18n * refactor: edit redo * refactor: add slot * feat: task order => i18n * refactor: task order => constant * refactor: taskOrder => status type and index * feat: taskOrder => ReceiveDialog * refactor: wording * refactor: table employee due date * refactor: receive task i18n * feat: trigger receive & task stat in receive page * refactor: receive dialog task in cart i18n * fix: remove task-order/receive/add * feat: receivetabletaskorder * refactor: fetch task on receive dialog * feat: add separate api get user task * refactor: receive fetch (messenger) * refactor: edit layout table * refactor: task order i18n & constant * refactor: task order change tab and stat (messenger and !messenger) * fix: task order status display & receive badge color (card) * refactor: trigger receive view * fix: add receive task condition * feat: total count * feat: prepare information * fix: i18n error task order not found * refacor: value * feat: select worker * refactor: status i18n & constant * refactor: table employee props (check box, step) * fix: order => select ready task * refactor: order => toggle status * refactor: receive => receive dialog * feat: featch value * refactor: task status display components * refactor: status active can is null * feat: update status tab * refactor: data display * refactor: i18n & fullTaskOrder variable * refactor: task receive view * refactor: add type responsible user * refactor: set group messenger * cleanup: * refactor: i18n / clone full task order / service => workflow type * refactor: receive view * refactor: show info messenger * refactor: handle flow step * refactor: receive view => opacity when pending * feat: add workflow template name and step name * feat: display workflow data on table * feat: add template step identifier * fix: edit does not change workflow id if changed * feat: detect if same template and step * refactor: handle template * refactor: add slot name product * refactor: map step in list product * refactor: bind data messenger list group * refactor: change endpoint name * chore: add helper package * feat: changetaskstatus * refactor: update type * refactor: set color btn * refactor: add step * refactor: add resposible institution * feat: disabled * refactor: map responsible institution * fix: order view => readonly * chore: clean * refactor: edit url api * refactor: edit name type * refactor: add slots action * refactor: add type row * refactor: add opts of task status * refactor: add select status * refactor: handle btn * refactor: add btn change task status * refactor: edit i18n redo th * refactor: sort status opts * feat: receive & order banner img * refactor: fetch status after submit * refactor: handle create only * refactor: task order status type Accept (messenger only) * feat: receive messenger profile * refactor: receive toggle status (display only) * fix: document expansion readonly * feat: confirmsendingbtn * refactor: constant and task order status * feat: receive task list count * refactor: post or get * refactor: define props institution group * refactor: fetch status after submit * refactor: handle create * refactor: handle query * refactor: update endpoint to support accept multiple order * refactor: change function name * feat: receive => functional accept task order * feat: task status count * feat: receive stat card count * refactor: order messenger profile * refactor: edit value to be task status * refactor: handle status of type order * refactor: use componet task status * refactor: handle show btn saving status * refactor: order => task status * refactor: edit selectStatus => changeStatus * refactor: edit @click btn confirmssending * refactor: add i18n * refactor: add function get template data * refactor: add change status * refactor: handle type receive * feat: order => auto change tab by status * refactor: fetch task after change status * feat: fail remark dialog * refactor: display step order (table employee) * refactor: fail remark dialog * refactor: order => open ready request dialog map selected * refactor: task list type & change status param * refactor: table task order, td background when selected * refactor: order => change status param * refactor: order => selectedEmployee variable type * refactor: task status component => shield btn * refactor: receive => change status * refactor: order => step btn waiting * fix: step btn waiting condition * refactor: filter selectable task (Failed) * refactor: find index condition on check * refactor: no request list available * refactor: fail btn no-wrap * refactor: fail dialog readonly * fix: reset state on open dialog * fix: wrong title position * refactor: hide task status drop down icon * fix: handle check condition * refactor: add userTask type and status * feat: submit task order function * refactor: table employee checkbox display condition * refactor: main layout * fix: task order validate i18n * refactor: table task order add submit status * refactor: status list * refactor: info product => user task status * feat: receive => submit task & step * refactor: i18n * feat: complete task oder function * refactor: task status component no action props * refactor: info messenger status * refactor: receive and order view * refactor: order complete view * refactor: order => complete color and title * refactor: calc price on table * refactor: quotation table i18n + product image * refactor: remove urgent checkbox * refactor: task status color * feat: calc summary price * fix: data is not available * feat: add doc view structure * refactor: format address text * feat: fetch document data from api * fix: value is null * fix: regression cannot edit package * feat: add document view for task order * feat: add view document button * feat: update type add discount * feat: readonly on cancel * feat: add discount from relation * refactor: add taskProduct on submit order * refactor: order => task product discount * refactor: order => date, task status count, view example * refactor: receive date * refactor: receive task status count --------- Co-authored-by: puriphatt <puriphat@frappet.com> Co-authored-by: nwpptrs <jay02499@gmail.com> Co-authored-by: Thanaphon Frappet <thanaphon@frappet.com> Co-authored-by: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Co-authored-by: oat_dev <nattapon@frappet.com>
This commit is contained in:
parent
cd0831bac1
commit
9eff614dbd
56 changed files with 6981 additions and 361 deletions
519
src/pages/09_task-order/document_view/MainPage.vue
Normal file
519
src/pages/09_task-order/document_view/MainPage.vue
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onMounted, nextTick, ref } from 'vue';
|
||||
import ThaiBahtText from 'thai-baht-text';
|
||||
|
||||
// NOTE: Import stores
|
||||
import { formatNumberDecimal } from 'stores/utils';
|
||||
import { useConfigStore } from 'stores/config';
|
||||
import { precisionRound } from 'src/utils/arithmetic';
|
||||
|
||||
// NOTE Import Types
|
||||
import { Branch } from 'stores/branch/types';
|
||||
|
||||
// NOTE: Import Components
|
||||
import ViewHeader from './ViewHeader.vue';
|
||||
import ViewFooter from './ViewFooter.vue';
|
||||
import PrintButton from 'src/components/button/PrintButton.vue';
|
||||
import {
|
||||
TaskOrder,
|
||||
TaskOrderStatus,
|
||||
TaskStatus,
|
||||
} from 'src/stores/task-order/types';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useTaskOrderStore } from 'src/stores/task-order';
|
||||
import { RequestWork } from 'src/stores/request-list';
|
||||
|
||||
const route = useRoute();
|
||||
const taskOrder = useTaskOrderStore();
|
||||
const configStore = useConfigStore();
|
||||
const config = storeToRefs(configStore).data;
|
||||
|
||||
type Data = TaskOrder;
|
||||
|
||||
type ProductItem = {
|
||||
id: string;
|
||||
code: string;
|
||||
detail: string;
|
||||
amount: number;
|
||||
priceUnit: number;
|
||||
discount: number;
|
||||
vat: number;
|
||||
value: number;
|
||||
};
|
||||
|
||||
type SummaryPrice = {
|
||||
totalPrice: number;
|
||||
totalDiscount: number;
|
||||
vat: number;
|
||||
vatExcluded: number;
|
||||
finalPrice: number;
|
||||
};
|
||||
|
||||
const branch = ref<Branch>();
|
||||
const product = ref<ProductItem[]>([]);
|
||||
|
||||
const elements = ref<HTMLElement[]>([]);
|
||||
const chunks = ref<ProductItem[][]>([[]]);
|
||||
const data = ref<Data>();
|
||||
|
||||
const summaryPrice = ref<SummaryPrice>({
|
||||
totalPrice: 0,
|
||||
totalDiscount: 0,
|
||||
vat: 0,
|
||||
vatExcluded: 0,
|
||||
finalPrice: 0,
|
||||
});
|
||||
|
||||
async function assignData() {
|
||||
for (let i = 0; i < product.value.length; i++) {
|
||||
let el = elements.value.at(-1);
|
||||
|
||||
if (!el) return;
|
||||
|
||||
if (getHeight(el) < 500) {
|
||||
chunks.value.at(-1)?.push(product.value[i]);
|
||||
} else {
|
||||
chunks.value.push([]);
|
||||
i--;
|
||||
}
|
||||
|
||||
await nextTick();
|
||||
}
|
||||
}
|
||||
|
||||
function getHeight(el: HTMLElement) {
|
||||
const shadow = document.createElement('div');
|
||||
|
||||
shadow.style.opacity = '0';
|
||||
shadow.style.position = 'absolute';
|
||||
shadow.style.top = '-999999px';
|
||||
shadow.style.left = '-999999px';
|
||||
shadow.style.pointerEvents = 'none';
|
||||
|
||||
document.body.appendChild(shadow);
|
||||
|
||||
shadow.appendChild(el.cloneNode(true));
|
||||
|
||||
const height = shadow.offsetHeight;
|
||||
|
||||
document.body.removeChild(shadow);
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'task-order-preview';
|
||||
|
||||
onMounted(async () => {
|
||||
if (route.params['id'] && typeof route.params['id'] === 'string') {
|
||||
const jsonObject = await taskOrder.getTaskOrderById(route.params['id']);
|
||||
|
||||
if (!jsonObject) return;
|
||||
|
||||
data.value = jsonObject;
|
||||
branch.value = jsonObject.registeredBranch;
|
||||
} else {
|
||||
let jsonString: string | null;
|
||||
|
||||
jsonString = localStorage.getItem(STORAGE_KEY);
|
||||
jsonString = jsonString || sessionStorage.getItem(STORAGE_KEY);
|
||||
|
||||
if (!jsonString) return;
|
||||
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
|
||||
const jsonObject: Data = JSON.parse(jsonString);
|
||||
|
||||
if (jsonObject) sessionStorage.setItem(STORAGE_KEY, jsonString);
|
||||
|
||||
data.value = jsonObject;
|
||||
branch.value = jsonObject.registeredBranch;
|
||||
}
|
||||
|
||||
const taskListGroup = data.value?.taskList.reduce<
|
||||
{
|
||||
product: RequestWork['productService']['product'];
|
||||
list: RequestWork[];
|
||||
}[]
|
||||
>((acc, curr) => {
|
||||
if (
|
||||
data.value?.taskOrderStatus === TaskOrderStatus.Complete &&
|
||||
curr.taskStatus !== TaskStatus.Complete
|
||||
) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const task = curr.requestWorkStep;
|
||||
|
||||
if (!task) return acc;
|
||||
|
||||
if (task.requestWork) {
|
||||
let exist = acc.find(
|
||||
(item) => task.requestWork.productService.productId == item.product.id,
|
||||
);
|
||||
|
||||
if (exist) {
|
||||
exist.list.push(task.requestWork);
|
||||
} else {
|
||||
acc.push({
|
||||
product: task.requestWork.productService.product,
|
||||
list: [task.requestWork],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
product.value = [];
|
||||
summaryPrice.value = taskListGroup
|
||||
.map((v) => ({
|
||||
product: v.product,
|
||||
pricePerUnit: v.product.serviceCharge,
|
||||
discount:
|
||||
data.value?.taskProduct.find(
|
||||
({ productId }) => productId === v.product.id,
|
||||
)?.discount || 0,
|
||||
amount: v.list.length,
|
||||
}))
|
||||
.reduce(
|
||||
(a, c) => {
|
||||
const price = precisionRound(c.pricePerUnit * c.amount);
|
||||
const vat =
|
||||
precisionRound(
|
||||
(c.pricePerUnit * (c.discount ? c.amount : 1) - c.discount) *
|
||||
(config.value?.vat || 0.07),
|
||||
) * (!c.discount ? c.amount : 1);
|
||||
|
||||
product.value.push({
|
||||
id: c.product.id,
|
||||
code: c.product.code,
|
||||
detail: c.product.name,
|
||||
priceUnit: c.pricePerUnit,
|
||||
amount: c.amount,
|
||||
discount: c.discount,
|
||||
vat,
|
||||
value: price * c.amount + vat,
|
||||
});
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
|
||||
a.vat = c.product.calcVat ? precisionRound(a.vat + vat) : a.vat;
|
||||
a.vatExcluded = c.product.calcVat
|
||||
? a.vatExcluded
|
||||
: precisionRound(a.vat + vat);
|
||||
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat);
|
||||
return a;
|
||||
},
|
||||
{
|
||||
totalPrice: 0,
|
||||
totalDiscount: 0,
|
||||
vat: 0,
|
||||
vatExcluded: 0,
|
||||
finalPrice: 0,
|
||||
},
|
||||
);
|
||||
|
||||
assignData();
|
||||
});
|
||||
|
||||
function print() {
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="toolbar">
|
||||
<PrintButton solid @click="print" />
|
||||
</div>
|
||||
<div class="row justify-between container color-task">
|
||||
<section class="content" v-for="chunk in chunks">
|
||||
<ViewHeader
|
||||
v-if="!!branch && data"
|
||||
:branch="branch"
|
||||
:institution="data.institution"
|
||||
:details="{
|
||||
code: data.code,
|
||||
name: data.taskName,
|
||||
contactName: data.contactName,
|
||||
contactTel: data.contactTel,
|
||||
}"
|
||||
/>
|
||||
|
||||
<span
|
||||
class="q-mb-sm q-mt-md"
|
||||
style="
|
||||
font-weight: 800;
|
||||
font-size: 16px;
|
||||
color: var(--main);
|
||||
display: block;
|
||||
border-bottom: 2px solid var(--main);
|
||||
"
|
||||
>
|
||||
{{ $t('taskOrder.goodReceipt') }}
|
||||
</span>
|
||||
|
||||
<table ref="elements" class="q-mb-sm" cellpadding="0" style="width: 100%">
|
||||
<tbody class="color-tr">
|
||||
<tr>
|
||||
<th>{{ $t('preview.rank') }}</th>
|
||||
<th>{{ $t('preview.productCode') }}</th>
|
||||
<th>{{ $t('general.detail') }}</th>
|
||||
<th>{{ $t('general.amount') }}</th>
|
||||
<th>{{ $t('preview.pricePerUnit') }}</th>
|
||||
<th>{{ $t('preview.discount') }}</th>
|
||||
<th>{{ $t('preview.vat') }}</th>
|
||||
<th>{{ $t('preview.value') }}</th>
|
||||
</tr>
|
||||
<tr v-for="(v, i) in chunk">
|
||||
<td class="text-center">{{ i + 1 }}</td>
|
||||
<td>{{ v.code }}</td>
|
||||
<td>{{ v.detail }}</td>
|
||||
<td style="text-align: right">{{ v.amount }}</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.priceUnit, 2) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.discount, 2) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.vat, 2) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.value, 2) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table
|
||||
style="width: 40%; margin-left: auto"
|
||||
class="q-mb-md"
|
||||
cellpadding="0"
|
||||
>
|
||||
<tbody class="color-tr">
|
||||
<tr>
|
||||
<td>{{ $t('general.total') }}</td>
|
||||
<td class="text-right">
|
||||
{{ formatNumberDecimal(summaryPrice.totalPrice, 2) }}
|
||||
฿
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{{ $t('general.discount') }}</td>
|
||||
<td class="text-right">
|
||||
{{ formatNumberDecimal(summaryPrice.totalDiscount, 2) || 0 }} ฿
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('general.totalAfterDiscount') }}</td>
|
||||
<td class="text-right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
summaryPrice.totalPrice - summaryPrice.totalDiscount,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
฿
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{{ $t('general.totalVatExcluded') }}</td>
|
||||
<td class="text-right">
|
||||
{{ formatNumberDecimal(summaryPrice.vatExcluded, 2) || 0 }}
|
||||
฿
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('general.vat', { msg: '7%' }) }}</td>
|
||||
<td class="text-right">
|
||||
{{ formatNumberDecimal(summaryPrice.vat, 2) }} ฿
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('general.totalVatIncluded') }}</td>
|
||||
<td class="text-right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
summaryPrice.totalPrice -
|
||||
summaryPrice.totalDiscount +
|
||||
summaryPrice.vat,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
฿
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="row justify-between q-mb-md" style="width: 100%">
|
||||
<div
|
||||
class="column set-width bg-color full-height"
|
||||
style="padding: 12px"
|
||||
>
|
||||
({{ ThaiBahtText(summaryPrice.finalPrice) }})
|
||||
</div>
|
||||
<div
|
||||
class="row text-right border-5 items-center"
|
||||
style="width: 40%; background: var(--main); padding: 8px"
|
||||
>
|
||||
<span style="color: white; font-weight: 600">ยอดรวมสุทธิ</span>
|
||||
<span
|
||||
class="border-5"
|
||||
style="
|
||||
width: 70%;
|
||||
margin-left: auto;
|
||||
background: white;
|
||||
padding: 4px;
|
||||
"
|
||||
>
|
||||
{{
|
||||
formatNumberDecimal(Math.max(summaryPrice.finalPrice, 0), 2) || 0
|
||||
}}
|
||||
฿
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content" v-if="data">
|
||||
<ViewHeader
|
||||
v-if="!!branch"
|
||||
:branch="branch"
|
||||
:institution="data.institution"
|
||||
:details="{
|
||||
code: data.code,
|
||||
name: data.taskName,
|
||||
contactName: data.contactName,
|
||||
contactTel: data.contactTel,
|
||||
}"
|
||||
/>
|
||||
|
||||
<article style="height: 5.8in"></article>
|
||||
|
||||
<ViewFooter
|
||||
:data="{
|
||||
name: '',
|
||||
company: branch?.name || '',
|
||||
buyer: '',
|
||||
buyDate: '',
|
||||
approveDate: '',
|
||||
approver: '',
|
||||
}"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.color-task {
|
||||
--main: var(--yellow-6);
|
||||
--main-hsl: var(--yellow-6-hsl);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
width: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
background: white;
|
||||
border-bottom: 1px solid var(--gray-3);
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th {
|
||||
background: var(--main);
|
||||
color: white;
|
||||
|
||||
padding: 4px;
|
||||
}
|
||||
td {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.border-5 {
|
||||
border-radius: 5px;
|
||||
}
|
||||
.set-width {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.bg-color {
|
||||
background-color: hsla(var(--main-hsl) / 0.1);
|
||||
}
|
||||
|
||||
.color-tr > tr:nth-child(odd) {
|
||||
background-color: hsla(var(--main-hsl) / 0.1);
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-inline: auto;
|
||||
background: var(--gray-3);
|
||||
width: calc(8.3in + 1rem);
|
||||
}
|
||||
|
||||
.container :deep(*) {
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
padding: 0.5in;
|
||||
align-items: center;
|
||||
background: white;
|
||||
page-break-after: always;
|
||||
break-after: page;
|
||||
height: 11.7in;
|
||||
max-height: 11.7in;
|
||||
}
|
||||
|
||||
.position-bottom {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.detail-note {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
& > * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
border-style: solid;
|
||||
border-color: var(--main);
|
||||
}
|
||||
|
||||
@media print {
|
||||
.toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
width: 100%;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0;
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
98
src/pages/09_task-order/document_view/ViewFooter.vue
Normal file
98
src/pages/09_task-order/document_view/ViewFooter.vue
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
data?: {
|
||||
name: string;
|
||||
buyer: string;
|
||||
buyDate: string;
|
||||
|
||||
company: string;
|
||||
approver: string;
|
||||
approveDate: string;
|
||||
};
|
||||
}>();
|
||||
</script>
|
||||
<template>
|
||||
<div class="footer-container">
|
||||
<div class="footer-top">
|
||||
<div>ในนาม {{ data?.name || '-' }}</div>
|
||||
<div>ในนาม {{ data?.company || '-' }}</div>
|
||||
</div>
|
||||
|
||||
<img src="/images/jws-stamp.png" alt="${0}" />
|
||||
|
||||
<div class="footer-bottom">
|
||||
<section>
|
||||
<div>
|
||||
<span class="data-placeholder"></span>
|
||||
<span>ผู้สั่งซื้อสินค้า</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="data-placeholder"></span>
|
||||
<span>วันที่</span>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div>
|
||||
<span class="data-placeholder"></span>
|
||||
<span>ผู้อนุมัติ</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="data-placeholder"></span>
|
||||
<span>วันที่</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.footer-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
height: 1.5in;
|
||||
}
|
||||
|
||||
.footer-top {
|
||||
position: absolute;
|
||||
width: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
& > * {
|
||||
width: 38%;
|
||||
}
|
||||
}
|
||||
.footer-bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
width: 38%;
|
||||
justify-content: space-around;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-placeholder {
|
||||
display: block;
|
||||
min-width: 1.2in;
|
||||
border-bottom: 1px dotted black;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
</style>
|
||||
148
src/pages/09_task-order/document_view/ViewHeader.vue
Normal file
148
src/pages/09_task-order/document_view/ViewHeader.vue
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<script lang="ts" setup>
|
||||
import { dateFormat } from 'src/utils/datetime';
|
||||
|
||||
// NOTE: Import stores
|
||||
import { formatAddress } from 'src/utils/address';
|
||||
|
||||
// NOTE Import Types
|
||||
import { Branch } from 'src/stores/branch/types';
|
||||
import { Institution } from 'src/stores/institution/types';
|
||||
|
||||
// NOTE: Import Components
|
||||
|
||||
defineProps<{
|
||||
branch: Branch;
|
||||
institution: Institution;
|
||||
details: {
|
||||
code: string;
|
||||
name: string;
|
||||
contactName: string;
|
||||
contactTel: string;
|
||||
};
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="row items-center q-mb-lg">
|
||||
<div class="column" style="width: 50%">
|
||||
<img src="/logo.png" width="192px" style="object-fit: scale-down" />
|
||||
</div>
|
||||
<div
|
||||
class="column"
|
||||
style="text-align: center; width: 50%; font-weight: 800; font-size: 24px"
|
||||
>
|
||||
{{ $t('taskOrder.goodReceipt') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<article class="detail-card">
|
||||
<section class="detail-customer-info">
|
||||
<article>
|
||||
<b>
|
||||
{{ !!branch.virtual ? '' : $t('general.company') }} {{ branch.name }}
|
||||
</b>
|
||||
|
||||
<span v-if="branch.province && branch.district && branch.subDistrict">
|
||||
{{
|
||||
formatAddress({
|
||||
address: branch.address,
|
||||
addressEN: branch.addressEN,
|
||||
moo: branch.moo,
|
||||
mooEN: branch.mooEN,
|
||||
soi: branch.soi,
|
||||
soiEN: branch.soiEN,
|
||||
street: branch.street,
|
||||
streetEN: branch.streetEN,
|
||||
province: branch.province,
|
||||
district: branch.district,
|
||||
subDistrict: branch.subDistrict,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ branch.taxNo }}</span>
|
||||
<span>เบอร์โทร {{ branch.telephoneNo }}</span>
|
||||
<span>{{ branch.webUrl }}</span>
|
||||
</article>
|
||||
<article>
|
||||
<b>ลูกค้า</b>
|
||||
<span>
|
||||
{{
|
||||
formatAddress({
|
||||
address: institution.address,
|
||||
addressEN: institution.addressEN,
|
||||
moo: institution.moo || '-',
|
||||
mooEN: institution.mooEN || '-',
|
||||
soi: institution.soi || '-',
|
||||
soiEN: institution.soiEN || '-',
|
||||
street: institution.street || '-',
|
||||
streetEN: institution.streetEN || '-',
|
||||
province: institution.province,
|
||||
district: institution.district,
|
||||
subDistrict: institution.subDistrict,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</article>
|
||||
</section>
|
||||
<section class="detail-quotation-info">
|
||||
<div>
|
||||
<div>
|
||||
{{ $t('general.itemNo', { msg: `${$t('preview.taskOrder')}` }) }}
|
||||
</div>
|
||||
<div>{{ details.code }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>ชื่องาน</div>
|
||||
<div>{{ details.name }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>ผู้ติดต่อ</div>
|
||||
<div>{{ details.contactName }}</div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.detail-card {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
& > * {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
& > :first-child {
|
||||
max-width: 57.5%;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-customer-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& > :first-child {
|
||||
color: var(--main);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-quotation-info {
|
||||
& > * {
|
||||
display: flex;
|
||||
|
||||
& > :first-child {
|
||||
color: var(--main);
|
||||
}
|
||||
|
||||
& > * {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue