Initial commit
This commit is contained in:
189
app/components/modal/UserMainTab.vue
Normal file
189
app/components/modal/UserMainTab.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div class="columns mx-0 px-0 py-2">
|
||||
<!-- Tab Navigation -->
|
||||
<div
|
||||
:class="`column is-narrow p-0 pr-4 ${viewport < 2 ? 'px-0' : ''}`"
|
||||
:style="`${viewport < 2 ? '' : 'border-right: 1px solid #B0B0B0;'}`"
|
||||
>
|
||||
<div
|
||||
:class="['is-clickable p-3', i !== 0 && 'mt-2', getStyle(v)]"
|
||||
style="width: 120px; border-radius: 4px;"
|
||||
v-for="(v, i) in tabsArray"
|
||||
:key="i"
|
||||
@click="changeTab(v)"
|
||||
>
|
||||
{{ isVietnamese ? v.name : v.en }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div
|
||||
:class="`column pl-4 ${viewport < 2 ? 'px-0' : 'pr-0 py-0'}`"
|
||||
style="min-width: 0"
|
||||
>
|
||||
<div v-if="isLoading" class="has-text-centered">
|
||||
<span class="icon is-large">
|
||||
<SvgIcon v-bind="{ name: 'loading.svg', type: 'primary', size: 18 }" />
|
||||
</span>
|
||||
</div>
|
||||
<component
|
||||
:is="getComponent(activeComponent.component)"
|
||||
v-else-if="activeComponent && activeComponent.component"
|
||||
v-bind="componentProps"
|
||||
@update="handleUpdate"
|
||||
@close="emit('close')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, defineAsyncComponent, watch } from "vue";
|
||||
import { useStore } from "@/stores/index";
|
||||
import { useNuxtApp } from "#app";
|
||||
|
||||
const props = defineProps({
|
||||
tabs: [Array, Object],
|
||||
row: Object,
|
||||
pagename: String,
|
||||
application: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["modalevent", "close"]);
|
||||
const store = useStore();
|
||||
const nuxtApp = useNuxtApp();
|
||||
const { $dialog, $calculate, $findapi, $getapi, $copy } = nuxtApp;
|
||||
|
||||
const tabsArray = computed(() => {
|
||||
if (Array.isArray(props.tabs)) {
|
||||
return props.tabs;
|
||||
}
|
||||
if (typeof props.tabs === 'object' && props.tabs !== null) {
|
||||
return Object.keys(props.tabs)
|
||||
.sort()
|
||||
.map(key => props.tabs[key]);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const modules = import.meta.glob("@/components/**/*.vue");
|
||||
function getComponent(path) {
|
||||
if (!path || typeof path !== "string" || !path.includes("/")) return null;
|
||||
const moduleKey = Object.keys(modules).find((key) => key.endsWith(`${path}.vue`));
|
||||
if (moduleKey) return defineAsyncComponent(modules[moduleKey]);
|
||||
console.warn(`Component not found: ${path}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const lang = computed(() => store.lang);
|
||||
const isVietnamese = computed(() => lang.value === "vi");
|
||||
const viewport = computed(() => store.viewport);
|
||||
|
||||
const record = ref(props.row || {});
|
||||
const activeTab = ref(tabsArray.value.find(t => t.active === true || t.active === 'true')?.code || tabsArray.value[0]?.code || '');
|
||||
const tabData = ref(null);
|
||||
const isLoading = ref(false);
|
||||
|
||||
const activeComponent = computed(() => tabsArray.value.find(t => t.code === activeTab.value));
|
||||
|
||||
const componentProps = computed(() => {
|
||||
const baseProps = {
|
||||
row: record.value,
|
||||
data: tabData.value,
|
||||
pagename: props.pagename,
|
||||
application: props.application,
|
||||
};
|
||||
|
||||
const tabSpecificProps = activeComponent.value?.vbind || {};
|
||||
const processedProps = {};
|
||||
|
||||
if (record.value) {
|
||||
for (const key in tabSpecificProps) {
|
||||
const value = tabSpecificProps[key];
|
||||
if (typeof value === 'string' && value.startsWith('row.')) {
|
||||
const recordKey = value.substring(4);
|
||||
processedProps[key] = record.value[recordKey];
|
||||
} else {
|
||||
processedProps[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { ...baseProps, ...processedProps };
|
||||
});
|
||||
|
||||
watch(activeTab, async (newTabCode) => {
|
||||
const tabConfig = tabsArray.value.find(t => t.code === newTabCode);
|
||||
if (tabConfig && tabConfig.api) {
|
||||
await fetchTabData(tabConfig);
|
||||
} else {
|
||||
tabData.value = null;
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
async function fetchTabData(tabConfig) {
|
||||
isLoading.value = true;
|
||||
tabData.value = null;
|
||||
try {
|
||||
let conn = $findapi(tabConfig.api.name);
|
||||
if (!conn) {
|
||||
console.error(`API connection '${tabConfig.api.name}' not found.`);
|
||||
return;
|
||||
}
|
||||
conn = $copy(conn);
|
||||
|
||||
if (tabConfig.api.params) {
|
||||
let params = $copy(tabConfig.api.params);
|
||||
for (const key in params.filter) {
|
||||
const value = params.filter[key];
|
||||
if (typeof value === 'string' && value.startsWith('record.')) {
|
||||
const recordKey = value.substring(7);
|
||||
params.filter[key] = record.value[recordKey];
|
||||
}
|
||||
}
|
||||
conn.params = params;
|
||||
}
|
||||
|
||||
const result = await $getapi([conn]);
|
||||
const apiResult = result.find(r => r.name === tabConfig.api.name);
|
||||
if (apiResult && apiResult.data) {
|
||||
tabData.value = apiResult.data.rows;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching tab data:", error);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function getStyle(tab) {
|
||||
const canEnter = checkCondition(tab, false);
|
||||
return tab.code === activeTab.value
|
||||
? "has-background-primary has-text-white"
|
||||
: `has-background-light ${canEnter ? "" : "has-text-grey-light"}`;
|
||||
}
|
||||
|
||||
function checkCondition(tab, showAlert = true) {
|
||||
if (!tab.condition) return true;
|
||||
const context = { record: record.value };
|
||||
const result = $calculate(context, [], tab.condition);
|
||||
if (!result.success || !result.value) {
|
||||
if (showAlert) {
|
||||
$dialog("Vui lòng <b>lưu dữ liệu</b> hoặc hoàn tất các bước trước khi chuyển sang mục này.", "Thông báo");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function changeTab(tab) {
|
||||
if (activeTab.value === tab.code) return;
|
||||
if (!checkCondition(tab)) return;
|
||||
activeTab.value = tab.code;
|
||||
}
|
||||
|
||||
function handleUpdate(updatedRecord) {
|
||||
record.value = { ...record.value, ...updatedRecord };
|
||||
emit("modalevent", { name: "dataevent", data: record.value });
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user