| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- <template>
- <q-table
- v-model:fullscreen="fullscreen"
- flat
- :pagination="{ rowsPerPage }"
- :pagination-label="getPaginationLabel"
- row-key="id"
- :rows="rows"
- :rows-per-page-label="$t('general.rows_per_page')"
- :columns="props.columns"
- :visible-columns="visibleColumns"
- :filter="filter"
- :grid="$q.screen.lt.sm"
- class="softpar-table q-pa-sm"
- @row-click="onRowClick"
- >
- <template #top>
- <div
- class="flex full-width justify-between align-center q-mb-md q-pl-sm"
- style="gap: 1rem"
- >
- <q-input
- v-if="mostrarCampoPesquisa"
- v-model="filter"
- debounce="250"
- :placeholder="$t('general.search')"
- clearable
- autofocus
- class=""
- color="primary"
- >
- <template #append>
- <q-icon name="mdi-magnify" />
- </template>
- </q-input>
- <q-select
- v-if="mostrarSelecaoDeColunas"
- v-model="visibleColumns"
- multiple
- dense
- outlined
- options-outlined
- :display-value="$q.lang.table.columns"
- emit-value
- map-options
- :options="mapColuns"
- option-value="name"
- style="width: 150px"
- options-selected-class="text-bold"
- />
- <q-btn
- v-if="mostrarBotaoFullscreen"
- flat
- @click="fullscreen = !fullscreen"
- >
- <q-icon name="mdi-fullscreen" />
- </q-btn>
- <q-space />
- <q-btn-dropdown
- v-if="props.dropDown"
- color="primary"
- :label="$t('general.options')"
- />
- <q-btn
- v-if="props.addItem"
- color="primary"
- padding="10px 16px"
- :outline="props.outlineAdd"
- :label="$t('general.add')"
- @click="onAddItem"
- >
- </q-btn>
- </div>
- </template>
- <template #body-cell-status="{ value, row }">
- <q-td style="width: 8%">
- <q-item-section>
- <span class="text-center">
- <div v-if="row.status && value" class="ativo body2 text-positive">
- {{ $t("general.active") }}
- </div>
- <div v-if="!row.status" class="inativo body2 text-accent">
- {{ $t("general.inactive") }}
- </div>
- </span>
- </q-item-section>
- </q-td>
- </template>
- <template #body-cell-ativo="{ value, row }">
- <q-td style="width: 8%">
- <q-item-section>
- <span class="text-center">
- <div v-if="row.ativo && value" class="ativo body2 text-positive">
- {{ $t("general.active") }}
- </div>
- <div v-if="row.ativo && !value" class="ativo body2 text-positive">
- {{ $t("general.active") }}
- </div>
- <div v-if="!row.ativo" class="inativo body2 text-accent">
- {{ $t("general.active") }}
- </div>
- </span>
- </q-item-section>
- </q-td>
- </template>
- <template v-if="!props.hideNoDataLabel" #no-data>
- <div class="q-my-md row justify-center full-width">
- <q-spinner v-if="loading" color="primary" size="30px" />
- <div v-else class="q-pa-md body2">
- {{ $t("errors.no_records_found") }}
- </div>
- </div>
- </template>
- <template v-for="(index, name) in $slots" #[name]="data">
- <slot :name="name" v-bind="data"></slot>
- </template>
- </q-table>
- </template>
- <script setup>
- import { ref, onMounted, toRaw, watch } from "vue";
- import { useRouter } from "vue-router";
- const emit = defineEmits(["onRowClick", "onAddItem", "noRows"]);
- const props = defineProps({
- // colunas de configuração da tabela
- columns: {
- type: Array,
- required: true,
- },
- // rota da api, ex: /clientes
- apiCall: {
- type: Function,
- required: true,
- },
- // botao de adicionar com aparencia de outline
- outlineAdd: {
- type: Boolean,
- default: false,
- },
- // ir para sub pagina on row click
- openItem: {
- type: Boolean,
- default: false,
- },
- // rota da sub page
- openItemRoute: {
- type: String,
- default: "",
- },
- // botao de adicionar
- addItem: {
- type: Boolean,
- default: true,
- },
- // botao de opcoes
- dropDown: {
- type: Boolean,
- default: false,
- },
- // botao de adicionar route
- addItemRoute: {
- type: String,
- default: "",
- },
- // quantidade de items por pagina
- rowsPerPage: {
- type: Number,
- default: 10,
- },
- comecarDesativado: {
- type: Boolean,
- default: false,
- },
- mostrarSelecaoDeColunas: {
- type: Boolean,
- default: false,
- },
- mostrarBotaoFullscreen: {
- type: Boolean,
- default: false,
- },
- // mostrarToggleInativos: {
- // type: Boolean,
- // default: false,
- // },
- mostrarCampoPesquisa: {
- type: Boolean,
- default: true,
- },
- noApiCall: {
- type: Boolean,
- default: false,
- },
- hideNoDataLabel: {
- type: Boolean,
- default: false,
- },
- // labelInativo: {
- // type: String,
- // default: "Exibir inativos",
- // },
- });
- const router = useRouter();
- const rows = ref([]);
- const filter = ref("");
- const loading = ref(true);
- const fullscreen = ref(false);
- const showInativos = ref(false);
- const inativos = ref([]);
- const getPaginationLabel = (from, to, last) => {
- return `${from}-${to} de ${last}`;
- };
- watch(showInativos, () => {
- if (showInativos.value) {
- rows.value = rows.value.concat(inativos.value);
- } else {
- inativos.value = rows.value.filter(
- (row) => row.status === false || row.ativo === false,
- );
- rows.value = rows.value.filter((row) => row.ativo);
- }
- });
- watch(
- () => props.apiCall,
- async () => {
- await onRequest();
- },
- );
- // remove as colunas obrigatórias do filtro de colunas
- const mapColuns = props.columns.reduce((accm, column) => {
- if (!column.required) {
- accm.push(column);
- }
- return accm;
- }, []);
- // as colunas que serão carregadas
- const visibleColumns = ref(mapColuns.map((column) => column.name));
- const onRowClick = (evt, row, index) => {
- const item = toRaw(row);
- if (props.openItem) {
- if (props.openItemRoute) {
- router.push({ name: props.openItemRoute, params: { id: item.id } });
- } else {
- emit("onRowClick", { evt, row, index });
- }
- }
- };
- const onAddItem = () => {
- if (props.addItem) {
- if (props.addItemRoute) {
- router.push({ name: props.addItemRoute });
- } else {
- emit("onAddItem");
- }
- }
- };
- // busca os dados do banco com filtros e pagination
- const onRequest = async () => {
- // const filter = params.filter;
- if (props.noApiCall) {
- loading.value = false;
- return;
- }
- // inicia o loading
- loading.value = true;
- // pega os dados do servidor
- const response = await props.apiCall();
- // limpa os dados atuais e adiciona os novos
- rows.value.splice(0, rows.value.length, ...response);
- // if (props.mostrarToggleInativos && !showInativos.value) {
- // inativos.value = rows.value.filter(
- // (row) => row.status === false || row.ativo === false,
- // );
- // rows.value = rows.value.filter((row) => row.ativo || row.status === true);
- // }
- if (rows.value.length == 0) {
- emit("noRows");
- }
- // finaliza o loading
- loading.value = false;
- };
- onMounted(async () => {
- // faz a primeira requisição
- await onRequest({
- filter: undefined,
- });
- if (props.comecarDesativado) {
- visibleColumns.value = mapColuns
- .map((column) => column.required)
- .map((column) => column.name);
- }
- });
- </script>
- <style lang="scss">
- @import "src/css/table.scss";
- </style>
|