hesabixCore/webUI/src/components/forms/Htabletreeselect.vue

188 lines
5 KiB
Vue
Raw Normal View History

<template>
<v-menu v-model="menu" :close-on-content-click="false">
<template v-slot:activator="{ props }">
<v-text-field
v-bind="props"
:model-value="selectedItem ? decodeLabel(selectedItem.label) + ` (${selectedItem.id})` : ''"
readonly
variant="outlined"
hide-details
density="compact"
:label="label"
class=""
prepend-inner-icon="mdi-table"
clearable
@click:clear="clearSelection"
>
<template v-slot:append-inner>
<v-icon>{{ menu ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
</template>
</v-text-field>
</template>
<v-card min-width="300" max-width="400">
<v-card-text class="pa-2">
<v-text-field
v-model="searchQuery"
density="compact"
variant="outlined"
hide-details
class="mb-2"
placeholder="جستجو..."
prepend-inner-icon="mdi-magnify"
clearable
@click:clear="searchQuery = ''"
></v-text-field>
<v-treeview
:items="filteredItems"
item-children="children"
item-title="label"
item-value="id"
:loading="loading"
density="compact"
class="tree-container rtl-tree"
hoverable
>
<template v-slot:prepend="{ item }">
<div class="d-flex align-center">
<v-radio
:model-value="selectedItem?.id"
:value="item.id"
hide-details
density="compact"
@click.stop
@change="selectItem(item)"
></v-radio>
<v-icon size="small" class="mr-1">
{{ item.children && item.children.length > 0 ? 'mdi-folder' : 'mdi-file-outline' }}
</v-icon>
</div>
</template>
<template v-slot:label="{ item }">
{{ decodeLabel(item.label) }} ({{ item.id }})
</template>
</v-treeview>
</v-card-text>
</v-card>
</v-menu>
</template>
<script>
import axios from 'axios';
export default {
name: 'CostTreeSelect',
props: {
modelValue: {
type: [Object, Number],
default: null
},
label: {
type: String,
default: 'جدول حساب'
},
returnObject: {
type: Boolean,
default: false
}
},
data() {
return {
selectedItem: null,
treeItems: [],
loading: false,
menu: false,
searchQuery: ''
};
},
computed: {
filteredItems() {
if (!this.searchQuery) return this.treeItems;
const searchTerm = this.searchQuery.toLowerCase();
return this.filterNodes(this.treeItems, (node) => {
const labelMatch = this.decodeLabel(node.label).toLowerCase().includes(searchTerm);
const idMatch = node.id.toString().includes(searchTerm);
return labelMatch || idMatch;
});
}
},
watch: {
modelValue: {
handler(newVal) {
if (this.returnObject) {
this.selectedItem = newVal;
} else {
this.selectedItem = this.findItemById(this.treeItems, newVal);
}
},
immediate: true
}
},
methods: {
findItemById(items, id) {
for (const item of items) {
if (item.id === id) return item;
if (item.children?.length) {
const found = this.findItemById(item.children, id);
if (found) return found;
}
}
return null;
},
async fetchTreeData() {
this.loading = true;
try {
const response = await axios.get('/api/accounting/table/childs/cost');
this.treeItems = response.data;
} catch (error) {
console.error('خطا در دریافت داده‌ها:', error);
this.$toast.error('خطا در بارگذاری داده‌ها');
} finally {
this.loading = false;
}
},
decodeLabel(label) {
try {
return decodeURIComponent(JSON.parse(`"${label}"`));
} catch (e) {
return label;
}
},
selectItem(item) {
this.selectedItem = item;
this.$emit('update:modelValue', this.returnObject ? item : item.id);
this.menu = false;
},
clearSelection() {
this.selectedItem = null;
this.$emit('update:modelValue', null);
},
filterNodes(nodes, predicate) {
return nodes.reduce((filtered, node) => {
const clone = { ...node };
if (predicate(clone)) {
if (clone.children?.length) {
clone.children = this.filterNodes(clone.children, predicate);
}
filtered.push(clone);
} else if (clone.children?.length) {
clone.children = this.filterNodes(clone.children, predicate);
if (clone.children.length) {
filtered.push(clone);
}
}
return filtered;
}, []);
}
},
created() {
this.fetchTreeData();
},
};
</script>