init repo
This commit is contained in:
78
src/App.vue
Normal file
78
src/App.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<q-layout view="lHh lpr lff">
|
||||
<q-header v-if="!isLoginPage" reveal class="bg-primary text-white">
|
||||
<q-toolbar>
|
||||
<Button dense flat round icon="mdi-menu" @click="toggleLeftDrawer" />
|
||||
<q-toolbar-title> {{ currentRouteTitle }} </q-toolbar-title>
|
||||
<Toggle v-model="darkMode" @toggle="toggleDarkMode" />
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
|
||||
<q-drawer v-if="!isLoginPage" show-if-above v-model="leftDrawerOpen" side="left" bordered>
|
||||
<div class="row q-pa-md items-center">
|
||||
<div class="col q-ml-xs title">
|
||||
<span>Clipper AI</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-separator />
|
||||
|
||||
<q-list>
|
||||
<q-item v-for="route in routes" clickable v-ripple :to="route.path">
|
||||
<q-item-section>{{ route.title }}</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
|
||||
<q-page-container>
|
||||
<router-view />
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Dark } from "quasar";
|
||||
|
||||
import Button from "@components/Button";
|
||||
import Toggle from "@components/Toggle";
|
||||
|
||||
import { routes } from "./auth/router";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Button,
|
||||
Toggle,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
leftDrawerOpen: false,
|
||||
darkMode: Dark.isActive,
|
||||
routes,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleLeftDrawer() {
|
||||
this.leftDrawerOpen = !this.leftDrawerOpen;
|
||||
},
|
||||
toggleDarkMode() {
|
||||
Dark.toggle();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currentRouteTitle() {
|
||||
const route = this.$route;
|
||||
return route.meta?.title || route.name || 'Clipper AI';
|
||||
},
|
||||
isLoginPage() {
|
||||
return this.$route.path === '/login';
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
font-size: 24px;
|
||||
color: $primary;
|
||||
}
|
||||
</style>
|
||||
BIN
src/assets/logo_clipperai.png
Normal file
BIN
src/assets/logo_clipperai.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
1
src/assets/vue.svg
Normal file
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
3
src/auth/roles.js
Normal file
3
src/auth/roles.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export default {
|
||||
VIDEOS_LIST: "role_videos_6550",
|
||||
};
|
||||
100
src/auth/router.js
Normal file
100
src/auth/router.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
import { createWebHistory, createRouter } from "vue-router";
|
||||
|
||||
import roles from "@/auth/roles";
|
||||
|
||||
import Videos from "@/routes/videos";
|
||||
import Login from "@/routes/auth/Login.vue";
|
||||
|
||||
const getUserRoles = () => {
|
||||
const rolesFromCookie = Cookies.get("user_roles"); // TODO: Tirar as permissões do usuário
|
||||
|
||||
return rolesFromCookie ? JSON.parse(rolesFromCookie) : [];
|
||||
};
|
||||
|
||||
export const routes = [
|
||||
{
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
component: Login,
|
||||
meta: {
|
||||
guest: true,
|
||||
title: "Login",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/videos",
|
||||
component: Videos,
|
||||
name: "Videos",
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "Vídeos",
|
||||
permissions: [roles.VIDEOS_LIST],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
redirect: (to) => {
|
||||
return Cookies.get("token") ? "/videos" : "/login";
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
redirect: (to) => {
|
||||
console.log(Cookies);
|
||||
return Cookies.get("token") ? "/videos" : "/login";
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
export const isAuthenticated = () => {
|
||||
return !!Cookies.get("token");
|
||||
};
|
||||
|
||||
const hasPermission = (requiredPermissions = []) => {
|
||||
if (!requiredPermissions.length) return true;
|
||||
|
||||
const userRoles = getUserRoles();
|
||||
|
||||
return requiredPermissions.some((permission) =>
|
||||
userRoles.includes(permission)
|
||||
);
|
||||
};
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.meta.title) {
|
||||
document.title = `${to.meta.title} | ClipperIA`;
|
||||
}
|
||||
|
||||
if (to.matched.some((record) => record.meta.requiresAuth)) {
|
||||
if (!isAuthenticated()) {
|
||||
next({
|
||||
path: "/login",
|
||||
query: { redirect: to.fullPath },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const requiredPermissions = to.meta.permissions || [];
|
||||
|
||||
if (requiredPermissions.length > 0 && !hasPermission(requiredPermissions)) {
|
||||
next({ path: "/unauthorized" });
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (to.matched.some((record) => record.meta.guest) && isAuthenticated()) {
|
||||
next({ path: "/" });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
50
src/components/Button/index.vue
Normal file
50
src/components/Button/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<q-btn
|
||||
:icon="icon"
|
||||
:label="label"
|
||||
:loading="loading"
|
||||
:color="color"
|
||||
:type="type"
|
||||
:disable="loading || disabled"
|
||||
:text-color="textColor"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Button",
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: "primary",
|
||||
validator: (value) => ["primary", "secondary"].includes(value),
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "button",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
default: "white",
|
||||
},
|
||||
fullWidth: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
92
src/components/Table/index.vue
Normal file
92
src/components/Table/index.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<q-card class="q-pa-md" flat bordered>
|
||||
<q-table
|
||||
:title="title"
|
||||
:rows="rows"
|
||||
:columns="columns"
|
||||
:row-key="rowKey"
|
||||
:loading="loading"
|
||||
row-key="name"
|
||||
flat
|
||||
bordered
|
||||
virtual-scroll
|
||||
hide-pagination
|
||||
>
|
||||
<template v-slot:loading>
|
||||
<q-inner-loading color="primary" />
|
||||
</template>
|
||||
|
||||
<template v-slot:no-data>
|
||||
<slot name="no-data" />
|
||||
</template>
|
||||
</q-table>
|
||||
|
||||
<div class="row q-mt-md">
|
||||
<div class="col-8 flex justify-end">
|
||||
<q-pagination
|
||||
v-model="pagination.currentPage"
|
||||
color="primary"
|
||||
:max="pagination.totalPages"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-4 flex justify-end items-center">
|
||||
<span class="text-grey-5"
|
||||
>Mostrando {{ pagination.currentPage }} de
|
||||
{{ pagination.totalItems }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import has from "lodash/has";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (value) => value.some((item) => has(item, ["name", "label"])),
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (value) => value.some((item) => has(item, ["name", "label"])),
|
||||
},
|
||||
rowName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "name",
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
pagination: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {
|
||||
sortBy: "desc",
|
||||
descending: false,
|
||||
currentPage: 1,
|
||||
totalPages: 1,
|
||||
totalItems: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
return {
|
||||
columns: props.columns,
|
||||
rows: props.rows,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
31
src/components/TextField/index.vue
Normal file
31
src/components/TextField/index.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="q-pa-sm">
|
||||
<div>
|
||||
<span>{{ label }} {{ required ? "*" : "" }}</span>
|
||||
</div>
|
||||
|
||||
<q-input outlined v-model="text" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from "vue";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
text: ref(""),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
30
src/components/Toggle/index.vue
Normal file
30
src/components/Toggle/index.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<q-toggle
|
||||
v-model="value"
|
||||
v-bind="$attrs"
|
||||
:color="color"
|
||||
@update:model-value="toggle"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Toggle",
|
||||
model: {
|
||||
prop: "value",
|
||||
event: "update:model-value",
|
||||
},
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: "secondary",
|
||||
validator: (value) => ["primary", "secondary"].includes(value),
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$emit("toggle", this.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
30
src/main.js
Normal file
30
src/main.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { createApp } from "vue";
|
||||
import { Quasar } from "quasar";
|
||||
|
||||
import quasarLang from "quasar/lang/pt-BR";
|
||||
|
||||
import "@quasar/extras/roboto-font/roboto-font.css";
|
||||
import "@quasar/extras/material-icons/material-icons.css";
|
||||
import "@quasar/extras/material-icons-outlined/material-icons-outlined.css";
|
||||
import "@quasar/extras/material-symbols-outlined/material-symbols-outlined.css";
|
||||
import "@quasar/extras/mdi-v7/mdi-v7.css";
|
||||
|
||||
import "quasar/src/css/index.sass";
|
||||
|
||||
import App from "./App.vue";
|
||||
|
||||
import { router } from "./auth/router";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(Quasar, {
|
||||
config: {
|
||||
dark: "auto",
|
||||
},
|
||||
plugins: {},
|
||||
lang: quasarLang,
|
||||
});
|
||||
|
||||
app.use(router);
|
||||
|
||||
app.mount("#app");
|
||||
11
src/quasar-variables.sass
Normal file
11
src/quasar-variables.sass
Normal file
@@ -0,0 +1,11 @@
|
||||
$primary : #8200ff
|
||||
$secondary : #eabdff
|
||||
$accent : #8200ff
|
||||
|
||||
$dark : #1c1e21
|
||||
$dark-page : #1c1e21
|
||||
|
||||
$success : #31cb00
|
||||
$error : #ee2e31
|
||||
$info : #226ce0
|
||||
$warning : #fcdc4d
|
||||
166
src/routes/auth/Login.vue
Normal file
166
src/routes/auth/Login.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<q-layout view="hHh lpr fFf">
|
||||
<q-drawer
|
||||
v-model="leftDrawerOpen"
|
||||
show-if-above
|
||||
:width="400"
|
||||
:breakpoint="700"
|
||||
bordered
|
||||
side="right"
|
||||
class="bg-primary text-white"
|
||||
>
|
||||
<q-scroll-area class="fit">
|
||||
<div class="q-pa-md">
|
||||
<q-form @submit.prevent="handleLogin" class="q-mt-lg">
|
||||
<q-input
|
||||
v-model="email"
|
||||
type="email"
|
||||
label="E-mail"
|
||||
lazy-rules
|
||||
:rules="[(val) => !!val || 'Campo obrigatório']"
|
||||
class="q-mb-md"
|
||||
dark
|
||||
filled
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="mail" />
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-input
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
label="Senha"
|
||||
lazy-rules
|
||||
:rules="[(val) => !!val || 'Campo obrigatório']"
|
||||
class="q-mb-lg"
|
||||
dark
|
||||
filled
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="lock" />
|
||||
</template>
|
||||
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="showPassword ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showPassword = !showPassword"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
label="Entrar"
|
||||
color="white"
|
||||
textColor="primary"
|
||||
full-width
|
||||
:loading="loading"
|
||||
/>
|
||||
</q-form>
|
||||
|
||||
<p v-if="error" class="text-negative q-mt-md">{{ error }}</p>
|
||||
</div>
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
|
||||
<q-page-container>
|
||||
<q-page class="flex flex-center bg-black-1">
|
||||
<div class="text-center q-pa-md">
|
||||
<div class="text-h4 q-mb-md">Clipper IA</div>
|
||||
|
||||
<p class="text-grey-8">Cortes automaticos de vídeos</p>
|
||||
</div>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Button from "@components/Button";
|
||||
|
||||
export default {
|
||||
name: "LoginView",
|
||||
components: {
|
||||
Button,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
leftDrawerOpen: true,
|
||||
email: "",
|
||||
password: "",
|
||||
showPassword: false,
|
||||
error: "",
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleLogin() {
|
||||
this.error = "";
|
||||
|
||||
if (!this.email || !this.password) {
|
||||
this.error = "Por favor, preencha todos os campos";
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
// setTimeout(() => {
|
||||
// try {
|
||||
// localStorage.setItem("auth_token", "dummy_token");
|
||||
|
||||
// const redirectPath = this.$route.query.redirect || "/";
|
||||
// this.$router.push(redirectPath);
|
||||
// } catch (err) {
|
||||
// this.error = err.message || "Erro ao fazer login. Tente novamente.";
|
||||
// } finally {
|
||||
// this.loading = false;
|
||||
// }
|
||||
// }, 1000);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (window.innerWidth < 700) {
|
||||
this.leftDrawerOpen = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Estilos personalizados para o formulário de login */
|
||||
.q-drawer {
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Ajustes para responsividade */
|
||||
@media (max-width: 700px) {
|
||||
.q-drawer {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.q-page-container {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Estilo para o botão de login */
|
||||
.q-btn--actionable {
|
||||
transition: transform 0.2s, opacity 0.2s;
|
||||
}
|
||||
|
||||
.q-btn--actionable:active:not(.disabled) {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* Melhorias na acessibilidade */
|
||||
.q-field--filled .q-field__control {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Efeito de hover nos inputs */
|
||||
.q-field--filled:not(.q-field--readonly):hover .q-field__control:before {
|
||||
border-color: rgba(255, 255, 255, 0.7) !important;
|
||||
}
|
||||
</style>
|
||||
192
src/routes/videos/index.vue
Normal file
192
src/routes/videos/index.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="user-list q-pa-md">
|
||||
<q-card flat bordered class="q-pa-sm q-mb-lg">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<TextField label="Nome" required />
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
|
||||
<Table :columns="columns" :rows="rows" :pagination="pagination">
|
||||
<template #no-data>
|
||||
<div
|
||||
class="full-width row flex-center q-gutter-sm"
|
||||
style="font-size: 1.3em"
|
||||
>
|
||||
<span> Infelizmente, não há dados para serem mostrados </span>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Table from "@components/Table";
|
||||
import TextField from "@components/TextField";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: "name",
|
||||
required: true,
|
||||
label: "Dessert (100g serving)",
|
||||
align: "left",
|
||||
field: (row) => row.name,
|
||||
format: (val) => `${val}`,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: "calories",
|
||||
align: "center",
|
||||
label: "Calories",
|
||||
field: "calories",
|
||||
sortable: true,
|
||||
},
|
||||
{ name: "fat", label: "Fat (g)", field: "fat", sortable: true },
|
||||
{ name: "carbs", label: "Carbs (g)", field: "carbs" },
|
||||
{ name: "protein", label: "Protein (g)", field: "protein" },
|
||||
{ name: "sodium", label: "Sodium (mg)", field: "sodium" },
|
||||
{
|
||||
name: "calcium",
|
||||
label: "Calcium (%)",
|
||||
field: "calcium",
|
||||
sortable: true,
|
||||
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10),
|
||||
},
|
||||
{
|
||||
name: "iron",
|
||||
label: "Iron (%)",
|
||||
field: "iron",
|
||||
sortable: true,
|
||||
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10),
|
||||
},
|
||||
];
|
||||
|
||||
const rows = [
|
||||
{
|
||||
name: "Frozen Yogurt",
|
||||
calories: 159,
|
||||
fat: 6.0,
|
||||
carbs: 24,
|
||||
protein: 4.0,
|
||||
sodium: 87,
|
||||
calcium: "14%",
|
||||
iron: "1%",
|
||||
},
|
||||
{
|
||||
name: "Ice cream sandwich",
|
||||
calories: 237,
|
||||
fat: 9.0,
|
||||
carbs: 37,
|
||||
protein: 4.3,
|
||||
sodium: 129,
|
||||
calcium: "8%",
|
||||
iron: "1%",
|
||||
},
|
||||
{
|
||||
name: "Eclair",
|
||||
calories: 262,
|
||||
fat: 16.0,
|
||||
carbs: 23,
|
||||
protein: 6.0,
|
||||
sodium: 337,
|
||||
calcium: "6%",
|
||||
iron: "7%",
|
||||
},
|
||||
{
|
||||
name: "Cupcake",
|
||||
calories: 305,
|
||||
fat: 3.7,
|
||||
carbs: 67,
|
||||
protein: 4.3,
|
||||
sodium: 413,
|
||||
calcium: "3%",
|
||||
iron: "8%",
|
||||
},
|
||||
{
|
||||
name: "Gingerbread",
|
||||
calories: 356,
|
||||
fat: 16.0,
|
||||
carbs: 49,
|
||||
protein: 3.9,
|
||||
sodium: 327,
|
||||
calcium: "7%",
|
||||
iron: "16%",
|
||||
},
|
||||
{
|
||||
name: "Jelly bean",
|
||||
calories: 375,
|
||||
fat: 0.0,
|
||||
carbs: 94,
|
||||
protein: 0.0,
|
||||
sodium: 50,
|
||||
calcium: "0%",
|
||||
iron: "0%",
|
||||
},
|
||||
{
|
||||
name: "Lollipop",
|
||||
calories: 392,
|
||||
fat: 0.2,
|
||||
carbs: 98,
|
||||
protein: 0,
|
||||
sodium: 38,
|
||||
calcium: "0%",
|
||||
iron: "2%",
|
||||
},
|
||||
{
|
||||
name: "Honeycomb",
|
||||
calories: 408,
|
||||
fat: 3.2,
|
||||
carbs: 87,
|
||||
protein: 6.5,
|
||||
sodium: 562,
|
||||
calcium: "0%",
|
||||
iron: "45%",
|
||||
},
|
||||
{
|
||||
name: "Donut",
|
||||
calories: 452,
|
||||
fat: 25.0,
|
||||
carbs: 51,
|
||||
protein: 4.9,
|
||||
sodium: 326,
|
||||
calcium: "2%",
|
||||
iron: "22%",
|
||||
},
|
||||
{
|
||||
name: "KitKat",
|
||||
calories: 518,
|
||||
fat: 26.0,
|
||||
carbs: 65,
|
||||
protein: 7,
|
||||
sodium: 54,
|
||||
calcium: "12%",
|
||||
iron: "6%",
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "UserList",
|
||||
components: {
|
||||
Table,
|
||||
TextField,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
pagination: {
|
||||
currentPage: 1,
|
||||
totalPages: 1,
|
||||
totalItems: 10,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-list {
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user