conflict-nuxt-4/app/components/auto-import/MySelect.vue
2026-02-14 15:44:00 +03:30

193 lines
5.4 KiB
Vue
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="my-select">
<USelect
v-model="selectedValue"
:items="formattedItems"
:placeholder="selectSchema.placeholder || 'انتخاب کنید'"
:multiple="selectSchema.multiple || false"
:disabled="selectSchema.disabled || loading"
:loading="loading"
@update:modelValue="onModelUpdate"
:class="[gridColumnClass, 'cursor-pointer']"
:style="selectWidthStyle"
:ui="{ wrapper: 'w-full', base: 'w-full' }"
dir="rtl"
color="primary"
size="xl"
highlight
/>
</div>
</template>
<script setup>
import { ref, computed, onMounted, watch } from "vue";
import { useCachedRequest } from "@/composables/useCachedRequest";
const props = defineProps({
selectSchema: { type: Object, default: () => ({}) },
gridColumnClass: { type: String, default: "" },
});
const emit = defineEmits(["my-select-action"]);
const emitAction = (action, payload) => {
// console.log("payloadیییی ==> ", payload);
// console.log("actionیییی ==> ", action);
emit("my-select-action", { action, payload });
};
const selectedValue = ref(null);
const rawItems = ref([]);
const loading = ref(false);
const lastEmittedValue = ref(null);
const { fetchRequest } = useCachedRequest();
const mode = computed(() => props.selectSchema.mode ?? "local");
const formattedItems = computed(() => {
const labelKey = props.selectSchema.labelKey || "name";
const valueKey = props.selectSchema.valueKey || "id";
return rawItems.value.map((item) => {
if (typeof item === "string") return { label: item, value: item };
return {
label:
item[labelKey] ?? item.name ?? item.title ?? item.label ?? "بدون عنوان",
value: item[valueKey] ?? item.id ?? item._id ?? item.value,
};
});
});
const selectWidthStyle = computed(() => {
return props.selectSchema.width ? { width: props.selectSchema.width } : {};
});
// مقایسه امن برای جلوگیری از emit تکراری
const isSameValue = (a, b) => {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false;
return a.every((v, i) => v === b[i]);
}
return a === b;
};
const emitSelected = (val) => {
if (isSameValue(val, lastEmittedValue.value)) return;
lastEmittedValue.value = Array.isArray(val) ? [...val] : val;
const selectedItem = formattedItems.value.find((i) =>
props.selectSchema.multiple ? val?.includes(i.value) : i.value === val,
);
emitAction("selected", selectedItem);
};
const syncDefaultSelection = () => {
if (props.selectSchema.value != null) return;
if (!formattedItems.value.length) return;
const isEmpty =
selectedValue.value == null ||
(Array.isArray(selectedValue.value) && !selectedValue.value.length);
if (isEmpty) {
const firstItem = formattedItems.value[0];
const newVal = props.selectSchema.multiple
? [firstItem.value]
: firstItem.value;
selectedValue.value = newVal;
emitSelected(newVal);
}
};
const onModelUpdate = (val) => {
emitSelected(val);
};
const fetchItems = async () => {
if (mode.value !== "api") {
rawItems.value =
props.selectSchema.options ?? props.selectSchema.items ?? [];
syncDefaultSelection();
return;
}
const apiConfig = props.selectSchema.apiConfig || {};
if (!apiConfig.url) return;
loading.value = true;
try {
const data = await fetchRequest(apiConfig);
rawItems.value = Array.isArray(data) ? data : (data?.data ?? []);
syncDefaultSelection();
emitAction("fetch:success", { items: rawItems.value });
} catch (error) {
emitAction("fetch:error", { error: error?.message ?? error });
} finally {
loading.value = false;
}
};
watch(
() => [props.selectSchema.items, props.selectSchema.options],
() => {
if (mode.value !== "api") {
rawItems.value =
props.selectSchema.options ?? props.selectSchema.items ?? [];
syncDefaultSelection();
}
},
{ immediate: true, deep: true },
);
watch(
() => props.selectSchema.value,
(val) => {
selectedValue.value = val ?? null;
emitSelected(val);
},
{ immediate: true },
);
onMounted(fetchItems);
</script>
<style lang="scss">
.my-select {
button[role="combobox"] {
direction: rtl; /* راست‌چین کردن متن و placeholder */
text-align: right; /* ترازبندی متن داخل button */
}
/* 2. placeholder */
button[role="combobox"] [data-slot="placeholder"] {
text-align: right;
direction: rtl;
}
/* 3. آیکون پایین (chevron) */
button[role="combobox"] [data-slot="trailing"] {
right: auto; /* اگر نیاز باشد موقعیت icon را اصلاح کنید */
left: 0; /* icon به سمت چپ می‌رود */
}
/* 4. منوی بازشونده (اگر teleport شده باشد) */
/* کلاس واقعی منو را از inspector پیدا کنید و به جای .u-select__menu بنویسید */
.u-select__menu {
direction: rtl !important;
text-align: right !important;
}
/* 5. آیتم‌های منو */
.u-select__menu [data-slot="item"] {
text-align: right;
direction: rtl;
display: flex;
flex-direction: row-reverse; /* اگر icon ها در آیتم هستند آنها را برعکس می‌کند */
}
}
.i-lucide\:check {
display: none;
}
</style>