472 lines
12 KiB
Vue
472 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Thông tin Sale Policy -->
|
|
<div class="m-0">
|
|
<Caption v-bind="{ title: 'Thông tin chính sách', type: 'has-text-warning' }"></Caption>
|
|
</div>
|
|
|
|
<div class="columns is-multiline">
|
|
<div class="column is-1">
|
|
<div class="field">
|
|
<label class="label">Thứ tự</label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="number"
|
|
v-model.number="record.index"
|
|
placeholder="Thứ tự"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-5">
|
|
<div class="field">
|
|
<label class="label">Mã <span class="has-text-danger">*</span></label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="record.code"
|
|
id="code"
|
|
placeholder="Mã CS"
|
|
/>
|
|
</div>
|
|
<p
|
|
class="help is-danger"
|
|
v-if="errors.code"
|
|
>
|
|
{{ errors.code }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-6">
|
|
<div class="field">
|
|
<label class="label">Tên chính sách <span class="has-text-danger">*</span></label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="record.name"
|
|
placeholder="Tên"
|
|
/>
|
|
</div>
|
|
<p
|
|
class="help is-danger"
|
|
v-if="errors.name"
|
|
>
|
|
{{ errors.name }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-4">
|
|
<div class="field">
|
|
<label class="label">Đặt cọc <span class="has-text-danger">*</span></label>
|
|
<div class="control">
|
|
<InputNumber
|
|
v-bind="{
|
|
record: record,
|
|
attr: 'deposit',
|
|
defaultValue: true,
|
|
}"
|
|
@number="selected('deposit', $event)"
|
|
/>
|
|
</div>
|
|
<p
|
|
class="help is-danger"
|
|
v-if="errors.deposit"
|
|
>
|
|
{{ errors.deposit }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-4">
|
|
<div class="field">
|
|
<label class="label">PT thanh toán <span class="has-text-danger">*</span></label>
|
|
<div class="control">
|
|
<SearchBox
|
|
v-bind="{
|
|
api: 'paymentmethod',
|
|
field: 'name',
|
|
column: ['name'],
|
|
first: true,
|
|
optionid: record.method,
|
|
}"
|
|
@option="selected('method', $event)"
|
|
/>
|
|
</div>
|
|
<p
|
|
class="help is-danger"
|
|
v-if="errors.method"
|
|
>
|
|
{{ errors.method }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-3">
|
|
<div class="field">
|
|
<label class="label">% Phân bổ HĐ</label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="number"
|
|
v-model.number="record.contract_allocation_percentage"
|
|
placeholder="%"
|
|
min="0"
|
|
max="100"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-1">
|
|
<div class="field">
|
|
<label class="label">
|
|
Kích hoạt
|
|
<span class="has-text-danger">*</span>
|
|
</label>
|
|
<div class="control">
|
|
<div
|
|
class="is-flex is-align-items-center is-clickable is-gap-2"
|
|
@click="record.enable = !record.enable"
|
|
>
|
|
<SvgIcon
|
|
v-bind="{
|
|
name: record.enable ? 'checked.svg' : 'uncheck.svg',
|
|
type: record.enable ? 'primary' : 'twitter',
|
|
size: 22,
|
|
}"
|
|
/>
|
|
<span>Cho phép</span>
|
|
</div>
|
|
</div>
|
|
<p
|
|
class="help is-danger"
|
|
v-if="errors.enable"
|
|
>
|
|
{{ errors.enable }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Payment Plan -->
|
|
<div class="m-0">
|
|
<Caption v-bind="{ title: 'Kế hoạch thanh toán', type: 'has-text-warning' }"></Caption>
|
|
</div>
|
|
|
|
<div
|
|
class="p-3"
|
|
v-for="(plan, i) in paymentPlans"
|
|
:key="i"
|
|
style="position: relative"
|
|
>
|
|
<a
|
|
v-if="paymentPlans.length > 1"
|
|
class="has-text-danger is-size-7"
|
|
style="position: absolute; top: 0.5rem; right: 0.75rem; cursor: pointer"
|
|
@click="removePlan(plan, i)"
|
|
>
|
|
Xóa
|
|
</a>
|
|
<div
|
|
class="columns is-multiline is-mobile mb-0"
|
|
style="border-top: 1px solid #dbdbdb"
|
|
>
|
|
<!-- Đợt -->
|
|
<div class="column is-3-desktop is-2-mobile pb-0">
|
|
<div class="field">
|
|
<label class="label">Đợt</label>
|
|
<div class="control">
|
|
<input
|
|
class="input has-text-centered has-text-weight-bold"
|
|
type="text"
|
|
:value="i + 1"
|
|
disabled
|
|
style="background-color: #f5f5f5"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Value -->
|
|
<div class="column is-3-desktop is-5-mobile pb-0">
|
|
<div class="field">
|
|
<label class="label">Giá trị <span class="has-text-danger">*</span></label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="number"
|
|
v-model.number="plan.value"
|
|
placeholder="Giá trị"
|
|
min="0"
|
|
step="0.01"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Type -->
|
|
<div class="column is-3-desktop is-5-mobile pb-0">
|
|
<div class="field">
|
|
<label class="label">Loại <span class="has-text-danger">*</span></label>
|
|
<div class="control">
|
|
<SearchBox
|
|
v-bind="{
|
|
api: 'valuetype',
|
|
field: 'name',
|
|
column: ['name'],
|
|
first: true,
|
|
optionid: plan.type,
|
|
}"
|
|
@option="planSelected('type', $event, plan)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Days -->
|
|
<div class="column is-3-desktop is-4-mobile pb-0">
|
|
<div class="field">
|
|
<label class="label">Số ngày</label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="number"
|
|
v-model.number="plan.days"
|
|
placeholder="Ngày"
|
|
min="0"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Payment Note -->
|
|
<div class="column is-6-desktop is-6-mobile pb-0">
|
|
<div class="field">
|
|
<label class="label">Ghi chú TT</label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="plan.payment_note"
|
|
placeholder="Ghi chú thanh toán"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Due Note -->
|
|
<div class="column is-6-desktop is-10-mobile pb-0">
|
|
<div class="field mb-0">
|
|
<label class="label">Ghi chú hạn</label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="plan.due_note"
|
|
placeholder="Ghi chú hạn thanh toán"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Nút thêm -->
|
|
<button
|
|
class="button is-pulled-right is-info is-light mb-4"
|
|
@click="addPlan"
|
|
>
|
|
<span class="icon">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
height="12"
|
|
width="12"
|
|
viewBox="0 0 448 512"
|
|
>
|
|
<path
|
|
d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"
|
|
/>
|
|
</svg>
|
|
</span>
|
|
</button>
|
|
|
|
<!-- Action buttons -->
|
|
<div class="buttons">
|
|
<button
|
|
:class="`button has-text-white is-primary ${isSubmitting ? 'is-loading' : ''}`"
|
|
@click="update()"
|
|
:disabled="isSubmitting"
|
|
>
|
|
<span>Lưu</span>
|
|
</button>
|
|
|
|
<button
|
|
class="button has-text-white is-danger"
|
|
@click="emit('close')"
|
|
:disabled="isSubmitting"
|
|
>
|
|
<span>Hủy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from "vue";
|
|
|
|
const { $getdata, $copy, $resetNull, $insertapi, $updateapi, $snackbar, $remove, $deleteapi, $empty } = useNuxtApp();
|
|
|
|
const props = defineProps({
|
|
api: String,
|
|
pagename: String,
|
|
row: Object,
|
|
prefix: String,
|
|
});
|
|
|
|
const emit = defineEmits(["close", "modalevent"]);
|
|
|
|
const record = ref($copy(props.row || { enable: true }));
|
|
const paymentPlans = ref([{}]);
|
|
const errors = ref({});
|
|
const isSubmitting = ref(false);
|
|
|
|
if (props.row?.id) {
|
|
const plans = await $getdata("paymentplan", { policy: props.row.id });
|
|
if (plans && plans.length > 0) {
|
|
paymentPlans.value = plans;
|
|
}
|
|
}
|
|
|
|
function validateForm() {
|
|
errors.value = {};
|
|
|
|
if ($empty(record.value.code)) {
|
|
errors.value.code = "Mã không được để trống";
|
|
}
|
|
|
|
if ($empty(record.value.name)) {
|
|
errors.value.name = "Tên không được để trống";
|
|
}
|
|
|
|
if ($empty(record.value.deposit)) {
|
|
errors.value.deposit = "Tiền đặt cọc không được để trống";
|
|
}
|
|
|
|
if ($empty(record.value.method)) {
|
|
errors.value.method = "Phương thức thanh toán không được để trống";
|
|
}
|
|
|
|
return Object.keys(errors.value).length === 0;
|
|
}
|
|
|
|
async function update() {
|
|
if (isSubmitting.value) return;
|
|
|
|
if (!validateForm()) {
|
|
$snackbar("Vui lòng kiểm tra lại dữ liệu");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isSubmitting.value = true;
|
|
|
|
let policyData = $resetNull(record.value);
|
|
if (policyData.method && typeof policyData.method === "object") {
|
|
policyData.method = policyData.method.id;
|
|
}
|
|
|
|
const policyResult = policyData.id
|
|
? await $updateapi("salepolicy", policyData, undefined, false)
|
|
: await $insertapi("salepolicy", policyData, undefined, false);
|
|
|
|
if (policyResult === "error") {
|
|
throw new Error("Failed to save policy");
|
|
}
|
|
|
|
const plans = paymentPlans.value.filter((v) => {
|
|
return !$empty(v.value) && !$empty(v.type);
|
|
});
|
|
|
|
plans.forEach((plan, index) => {
|
|
plan.policy = policyResult.id;
|
|
plan.cycle = index + 1;
|
|
if (plan.type) {
|
|
plan.type = Number(plan.type);
|
|
}
|
|
});
|
|
|
|
if (plans.length > 0) {
|
|
if (props.row?.id) {
|
|
const oldPlans = await $getdata("paymentplan", {
|
|
policy: policyResult.id,
|
|
});
|
|
|
|
if (oldPlans && oldPlans.length > 0) {
|
|
for (const oldPlan of oldPlans) {
|
|
await $deleteapi("paymentplan", oldPlan.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
await $insertapi("paymentplan", plans, undefined, false);
|
|
}
|
|
|
|
$snackbar("Lưu dữ liệu thành công!");
|
|
|
|
emit("modalevent", { name: "dataevent", data: policyResult });
|
|
emit("close");
|
|
} catch (error) {
|
|
$snackbar("Lưu dữ liệu thất bại");
|
|
} finally {
|
|
isSubmitting.value = false;
|
|
}
|
|
}
|
|
|
|
function selected(attr, obj) {
|
|
if (obj !== null && typeof obj === "object") {
|
|
record.value[attr] = obj.id || obj;
|
|
} else {
|
|
record.value[attr] = obj;
|
|
}
|
|
}
|
|
|
|
function planSelected(attr, obj, plan) {
|
|
if (attr === "type") {
|
|
if (obj && typeof obj === "object") {
|
|
plan.type = obj.id;
|
|
plan._type = obj;
|
|
} else {
|
|
plan.type = obj;
|
|
}
|
|
} else if (attr === "value") {
|
|
plan.value = Number(obj);
|
|
} else {
|
|
plan[attr] = obj;
|
|
}
|
|
}
|
|
|
|
function addPlan() {
|
|
paymentPlans.value.push({});
|
|
}
|
|
|
|
async function removePlan(plan, index) {
|
|
if (plan.id) {
|
|
await $deleteapi("paymentplan", plan.id);
|
|
}
|
|
$remove(paymentPlans.value, index);
|
|
if (paymentPlans.value.length === 0) {
|
|
paymentPlans.value = [{}];
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
document.getElementById("code")?.focus();
|
|
});
|
|
</script>
|