StateSelect.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <template>
  2. <q-select
  3. v-model="selectedState"
  4. v-bind="$attrs"
  5. use-input
  6. hide-selected
  7. fill-input
  8. clearable
  9. :options="stateOptions"
  10. :label
  11. :loading
  12. :placeholder="$t('common.actions.search') + ' ' + $t('ui.navigation.state')"
  13. :rules
  14. :error
  15. :error-message
  16. @filter="filterFn"
  17. >
  18. <template #no-option>
  19. <q-item>
  20. <q-item-section class="text-grey">
  21. {{ $t("http.errors.no_records_found") }}
  22. </q-item-section>
  23. </q-item>
  24. </template>
  25. </q-select>
  26. </template>
  27. <script setup>
  28. import { getStates } from "src/api/state";
  29. import { ref, onMounted, watch } from "vue";
  30. import { normalizeString } from "src/helpers/utils";
  31. import { useI18n } from "vue-i18n";
  32. const emit = defineEmits(["selectedCountryId"]);
  33. const { country, label, rules, initialId } = defineProps({
  34. country: {
  35. type: Object,
  36. required: false,
  37. default: () => {
  38. return {
  39. label: "Brasil",
  40. value: 1,
  41. };
  42. },
  43. },
  44. label: {
  45. type: String,
  46. default: () => useI18n().t("ui.navigation.state"),
  47. },
  48. rules: {
  49. type: Array,
  50. default: () => [],
  51. },
  52. initialId: {
  53. type: Number,
  54. required: false,
  55. default: null,
  56. },
  57. error: {
  58. type: Boolean,
  59. default: false,
  60. },
  61. errorMessage: {
  62. type: String,
  63. default: "",
  64. },
  65. });
  66. const selectedState = defineModel({ type: Object });
  67. const loading = ref(false);
  68. const baseOptions = ref([]);
  69. const stateOptions = ref([]);
  70. const filterFn = (val, update) => {
  71. ensureOnlyPossibleOptions(country?.value);
  72. const needle = normalizeString(val);
  73. if(val) {
  74. stateOptions.value = stateOptions.value.filter((v) => {
  75. return (
  76. normalizeString(v.label).includes(needle) ||
  77. normalizeString(v.code).includes(needle)
  78. );
  79. });
  80. } else {
  81. stateOptions.value = baseOptions.value;
  82. }
  83. update();
  84. };
  85. const selectStateById = async (id) => {
  86. if (selectedState.value?.value === id) {
  87. return;
  88. }
  89. selectedState.value = baseOptions.value.find((state) => state.value === id);
  90. };
  91. const selectStateByName = (name) => {
  92. if (selectedState.value?.label === name) {
  93. return;
  94. }
  95. selectedState.value = baseOptions.value.find((state) => state.label === name);
  96. };
  97. const selectStateByCode = (code) => {
  98. if (selectedState.value?.code === code) {
  99. return;
  100. }
  101. selectedState.value = baseOptions.value.find((state) => state.code === code);
  102. };
  103. const ensureOnlyPossibleOptions = (country_id) => {
  104. if (country_id) {
  105. stateOptions.value = baseOptions.value.filter(
  106. (state) => state.country_id === country_id,
  107. );
  108. } else {
  109. stateOptions.value = baseOptions.value;
  110. }
  111. };
  112. watch(
  113. () => country,
  114. (value, oldValue) => {
  115. if (
  116. value?.value != oldValue?.value &&
  117. value?.value != selectedState.value?.country_id
  118. ) {
  119. selectedState.value = null;
  120. }
  121. if (value) {
  122. ensureOnlyPossibleOptions(value.value);
  123. }
  124. },
  125. { immediate: true },
  126. );
  127. watch(selectedState, () => {
  128. if (selectedState.value?.country_id) {
  129. emit("selectedCountryId", selectedState.value.country_id);
  130. }
  131. });
  132. onMounted(async () => {
  133. try {
  134. loading.value = true;
  135. const baseStates = await getStates();
  136. baseOptions.value = baseStates.map((state) => ({
  137. label: state.name,
  138. value: state.id,
  139. code: state.code,
  140. country_id: state.country_id,
  141. }));
  142. stateOptions.value = baseOptions.value;
  143. if (initialId) {
  144. selectStateById(initialId);
  145. }
  146. } catch (e) {
  147. console.log(e);
  148. } finally {
  149. loading.value = false;
  150. }
  151. });
  152. defineExpose({
  153. selectStateById,
  154. selectStateByName,
  155. selectStateByCode,
  156. });
  157. </script>