versão 18h32 13/02
This commit is contained in:
parent
d36d7f76d3
commit
923d6b0655
104
src/App.vue
104
src/App.vue
@ -16,30 +16,14 @@
|
|||||||
:size="isCollapsed ? 40 : 36"
|
:size="isCollapsed ? 40 : 36"
|
||||||
class="gradient-avatar"
|
class="gradient-avatar"
|
||||||
>
|
>
|
||||||
<v-icon size="24" color="white">mdi-security</v-icon>
|
<v-icon size="22" color="white">mdi-security</v-icon>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
<span v-if="!isCollapsed" class="text-h6 ml-3 white--text font-weight-bold">TARS</span>
|
<span v-if="!isCollapsed" class="text-h6 ml-3 white--text font-weight-bold">TARS</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-divider class="border-opacity-25"></v-divider>
|
<v-divider class="border-opacity-25"></v-divider>
|
||||||
|
|
||||||
<!-- User Profile Section -->
|
<!-- Navigation Menu -->
|
||||||
<div class="profile-section px-4 py-3" @click="goToUserProfile">
|
|
||||||
<div class="d-flex align-center" :class="{ 'justify-center': isCollapsed }">
|
|
||||||
<v-avatar color="primary" size="40">
|
|
||||||
<v-icon>mdi-account</v-icon>
|
|
||||||
</v-avatar>
|
|
||||||
<div v-if="!isCollapsed" class="ml-3">
|
|
||||||
<div class="text-subtitle-2 font-weight-medium">Admin User</div>
|
|
||||||
<div class="text-caption text-grey-lighten-1">Administrador</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-divider class="border-opacity-25"></v-divider>
|
|
||||||
|
|
||||||
<!-- Flattened Navigation Menu -->
|
|
||||||
<v-list nav class="px-2">
|
<v-list nav class="px-2">
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-for="item in flattenedMenuItems"
|
v-for="item in flattenedMenuItems"
|
||||||
@ -48,6 +32,7 @@
|
|||||||
:value="item.name"
|
:value="item.name"
|
||||||
rounded="lg"
|
rounded="lg"
|
||||||
class="mb-1"
|
class="mb-1"
|
||||||
|
@click="item.name === 'logout' ? handleLogout() : null"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon :size="22">{{ item.icon }}</v-icon>
|
<v-icon :size="22">{{ item.icon }}</v-icon>
|
||||||
@ -85,32 +70,25 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
|
|
||||||
<!-- Updated Top Bar -->
|
<!-- Top Bar -->
|
||||||
<v-app-bar
|
<v-app-bar
|
||||||
elevation="1"
|
elevation="1"
|
||||||
height="64"
|
height="64"
|
||||||
color="background"
|
color="background"
|
||||||
>
|
>
|
||||||
<v-spacer />
|
<v-spacer></v-spacer>
|
||||||
|
|
||||||
<!-- Admin User Profile -->
|
<!-- Admin User Profile -->
|
||||||
<div class="d-flex align-center mr-2">
|
<div
|
||||||
|
class="d-flex align-center mr-2 profile-section pa-2 rounded"
|
||||||
|
style="cursor: pointer;"
|
||||||
|
@click="$router.push({ name: 'user-profile' })"
|
||||||
|
>
|
||||||
<v-avatar color="primary" size="32" class="mr-2">
|
<v-avatar color="primary" size="32" class="mr-2">
|
||||||
<v-icon>mdi-account</v-icon>
|
<v-icon>mdi-account</v-icon>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
<span class="text-subtitle-2 font-weight-medium">Admin User</span>
|
<span class="text-subtitle-2 font-weight-medium">{{ currentUser?.name || 'Admin User' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Logout Button -->
|
|
||||||
<v-btn
|
|
||||||
color="error"
|
|
||||||
variant="tonal"
|
|
||||||
@click="handleLogout"
|
|
||||||
prepend-icon="mdi-logout"
|
|
||||||
class="mr-2"
|
|
||||||
>
|
|
||||||
Sair
|
|
||||||
</v-btn>
|
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
@ -118,16 +96,34 @@
|
|||||||
<v-container fluid class="pa-6">
|
<v-container fluid class="pa-6">
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<component :is="Component" />
|
<component :is="Component"></component>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-main>
|
</v-main>
|
||||||
|
|
||||||
|
<!-- Status Snackbar -->
|
||||||
|
<v-snackbar
|
||||||
|
v-model="snackbar.show"
|
||||||
|
:color="snackbar.color"
|
||||||
|
:timeout="3000"
|
||||||
|
>
|
||||||
|
{{ snackbar.text }}
|
||||||
|
<template v-slot:actions>
|
||||||
|
<v-btn
|
||||||
|
color="white"
|
||||||
|
variant="text"
|
||||||
|
@click="snackbar.show = false"
|
||||||
|
>
|
||||||
|
Fechar
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
</v-app>
|
</v-app>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AppLayout',
|
name: 'AppLayout',
|
||||||
@ -136,6 +132,11 @@ export default {
|
|||||||
return {
|
return {
|
||||||
drawer: true,
|
drawer: true,
|
||||||
isCollapsed: false,
|
isCollapsed: false,
|
||||||
|
snackbar: {
|
||||||
|
show: false,
|
||||||
|
text: '',
|
||||||
|
color: 'success'
|
||||||
|
},
|
||||||
flattenedMenuItems: [
|
flattenedMenuItems: [
|
||||||
{
|
{
|
||||||
name: 'home',
|
name: 'home',
|
||||||
@ -181,27 +182,40 @@ export default {
|
|||||||
icon: 'mdi-account-group',
|
icon: 'mdi-account-group',
|
||||||
label: 'Usuários'
|
label: 'Usuários'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'logout',
|
||||||
|
route: { name: 'login' },
|
||||||
|
icon: 'mdi-logout',
|
||||||
|
label: 'Sair',
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters('auth', [
|
||||||
|
'isAuthenticated',
|
||||||
|
'currentUser'
|
||||||
|
])
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions('auth', ['logout']),
|
|
||||||
|
|
||||||
toggleDrawer() {
|
toggleDrawer() {
|
||||||
this.isCollapsed = !this.isCollapsed;
|
this.isCollapsed = !this.isCollapsed;
|
||||||
},
|
},
|
||||||
|
|
||||||
goToUserProfile() {
|
showSnackbar(text, color = 'success') {
|
||||||
this.$router.push({ name: 'user-profile' });
|
this.snackbar.text = text;
|
||||||
|
this.snackbar.color = color;
|
||||||
|
this.snackbar.show = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async handleLogout() {
|
async handleLogout() {
|
||||||
try {
|
try {
|
||||||
await this.logout();
|
await this.$store.dispatch('auth/logout');
|
||||||
this.$router.push({ name: 'login' });
|
this.$router.push({ name: 'login' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao fazer logout:', error);
|
this.showSnackbar('Erro ao fazer logout', 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,6 +236,10 @@ export default {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.profile-section:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
.v-list-item--active {
|
.v-list-item--active {
|
||||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,115 +1,306 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="testing-view">
|
<v-container>
|
||||||
<v-container>
|
<!-- Filtros de Pesquisa -->
|
||||||
<v-row class="mb-4">
|
<v-row>
|
||||||
<!-- Filtro de Data/Hora -->
|
<v-col cols="6">
|
||||||
<v-col cols="12" md="4">
|
<v-text-field
|
||||||
<v-text-field
|
v-model="filters.model"
|
||||||
v-model="filters.date"
|
label="Buscar por modelo"
|
||||||
label="Data/Hora"
|
prepend-inner-icon="mdi-magnify"
|
||||||
type="datetime-local"
|
variant="outlined"
|
||||||
></v-text-field>
|
density="compact"
|
||||||
</v-col>
|
@input="filterModels"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-select
|
||||||
|
v-model="filters.status"
|
||||||
|
:items="statusOptions"
|
||||||
|
label="Status"
|
||||||
|
variant="outlined"
|
||||||
|
density="compact"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
<!-- Filtro de Câmera ID -->
|
<!-- Botões de Ação -->
|
||||||
<v-col cols="12" md="4">
|
<v-row class="mb-4">
|
||||||
<v-text-field
|
<v-col>
|
||||||
v-model="filters.cameraId"
|
<v-btn
|
||||||
label="Câmera ID"
|
color="primary"
|
||||||
clearable
|
class="mr-2"
|
||||||
></v-text-field>
|
@click="openTrainingDialog"
|
||||||
</v-col>
|
>
|
||||||
|
<v-icon left>mdi-brain</v-icon>
|
||||||
|
Treinar
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
color="success"
|
||||||
|
class="mr-2"
|
||||||
|
:disabled="!hasTrained"
|
||||||
|
@click="integrate"
|
||||||
|
>
|
||||||
|
<v-icon left>mdi-connection</v-icon>
|
||||||
|
Integrar
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
color="info"
|
||||||
|
:disabled="!hasIntegrated"
|
||||||
|
@click="test"
|
||||||
|
>
|
||||||
|
<v-icon left>mdi-test-tube</v-icon>
|
||||||
|
Testar
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
<!-- Campo de Pesquisa -->
|
<!-- Tabela de Câmeras -->
|
||||||
<v-col cols="12" md="4">
|
<v-data-table
|
||||||
<v-text-field
|
:headers="headers"
|
||||||
v-model="filters.search"
|
:items="cameras"
|
||||||
label="Pesquisar"
|
:search="filters.model"
|
||||||
append-icon="mdi-magnify"
|
:custom-filter="customFilter"
|
||||||
clearable
|
>
|
||||||
></v-text-field>
|
<template #[`item.actions`]="{ item }">
|
||||||
</v-col>
|
<v-icon
|
||||||
</v-row>
|
size="small"
|
||||||
|
class="mr-2"
|
||||||
|
@click="editCamera(item)"
|
||||||
|
>
|
||||||
|
mdi-pencil
|
||||||
|
</v-icon>
|
||||||
|
<v-icon
|
||||||
|
size="small"
|
||||||
|
@click="confirmDelete(item)"
|
||||||
|
>
|
||||||
|
mdi-delete
|
||||||
|
</v-icon>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
|
||||||
<!-- Botões de Treino e Integração -->
|
<!-- Diálogo de Treinamento -->
|
||||||
<v-row class="mb-4">
|
<v-dialog v-model="dialogs.training" max-width="500px">
|
||||||
<v-col class="d-flex justify-end">
|
<v-card>
|
||||||
<v-btn color="primary" class="mr-2" @click="trainModels">
|
<v-card-title>Treinar Modelo</v-card-title>
|
||||||
Treinar Modelos
|
<v-card-text>
|
||||||
</v-btn>
|
<v-select
|
||||||
<v-btn color="success" @click="integrateModels">
|
v-model="selectedModel"
|
||||||
Integrar Modelos
|
:items="modelOptions"
|
||||||
</v-btn>
|
label="Selecione o modelo"
|
||||||
</v-col>
|
required
|
||||||
</v-row>
|
/>
|
||||||
|
<v-progress-linear
|
||||||
<!-- Lista de Modelos de Teste -->
|
v-if="training.inProgress"
|
||||||
<v-row>
|
:value="training.progress"
|
||||||
<v-col>
|
height="25"
|
||||||
<v-data-table
|
|
||||||
:headers="headers"
|
|
||||||
:items="models"
|
|
||||||
item-value="id"
|
|
||||||
class="elevation-1"
|
|
||||||
>
|
>
|
||||||
<template #top>
|
<template v-slot:default>
|
||||||
<v-toolbar flat>
|
{{ training.progress }}%
|
||||||
<v-toolbar-title>Modelos de Teste</v-toolbar-title>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
</v-toolbar>
|
|
||||||
</template>
|
</template>
|
||||||
<template #item_actions="{ item }">
|
</v-progress-linear>
|
||||||
<v-btn icon color="primary" @click="viewDetails(item)">
|
<v-alert
|
||||||
<v-icon>mdi-eye</v-icon>
|
v-if="training.complete"
|
||||||
</v-btn>
|
:type="training.success ? 'success' : 'error'"
|
||||||
</template>
|
class="mt-4"
|
||||||
</v-data-table>
|
>
|
||||||
</v-col>
|
{{ training.message }}
|
||||||
</v-row>
|
</v-alert>
|
||||||
</v-container>
|
</v-card-text>
|
||||||
</div>
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
text
|
||||||
|
@click="closeTrainingDialog"
|
||||||
|
>
|
||||||
|
Fechar
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
:disabled="!selectedModel || training.inProgress"
|
||||||
|
@click="startTraining"
|
||||||
|
>
|
||||||
|
Iniciar Treinamento
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
<!-- Diálogo de Confirmação de Exclusão -->
|
||||||
|
<v-dialog v-model="dialogs.delete" max-width="400px">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>Confirmar Exclusão</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
Tem certeza que deseja excluir esta câmera?
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn
|
||||||
|
color="error"
|
||||||
|
text
|
||||||
|
@click="deleteCamera"
|
||||||
|
>
|
||||||
|
Excluir
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
text
|
||||||
|
@click="dialogs.delete = false"
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "TestingView",
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
date: "",
|
model: '',
|
||||||
cameraId: "",
|
status: null
|
||||||
search: "",
|
|
||||||
},
|
},
|
||||||
headers: [
|
headers: [
|
||||||
{ text: "ID", value: "id" },
|
{ title: 'Modelo', key: 'model' },
|
||||||
{ text: "Nome", value: "name" },
|
{ title: 'Descrição', key: 'description' },
|
||||||
{ text: "Status", value: "status" },
|
{ title: 'Tipo', key: 'type' },
|
||||||
{ text: "Ações", value: "actions", sortable: false },
|
{ title: 'Status', key: 'status' },
|
||||||
|
{ title: 'Info', key: 'info'},
|
||||||
|
{ title: 'Vizualização', key: 'ip' },
|
||||||
|
{ title: 'Ações', key: 'actions', sortable: false }
|
||||||
],
|
],
|
||||||
models: [
|
cameras: [
|
||||||
{ id: 1, name: "Modelo A", status: "Pronto" },
|
{
|
||||||
{ id: 2, name: "Modelo B", status: "Em Execução" },
|
id: 1,
|
||||||
{ id: 3, name: "Modelo C", status: "Concluído" },
|
model: 'AXIS Q6128',
|
||||||
|
description: 'Câmera HD para ambientes internos',
|
||||||
|
type: 'HTTP',
|
||||||
|
status: 'Ativo',
|
||||||
|
ip: '192.168.1.100',
|
||||||
|
port: '8080'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
model: 'Hikvision DS-2CD',
|
||||||
|
description: 'Câmera HD para ambientes internos',
|
||||||
|
type: 'HTTP',
|
||||||
|
status: 'Ativo',
|
||||||
|
ip: '192.168.1.100',
|
||||||
|
port: '8080'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
model: 'AK-31548',
|
||||||
|
description: 'Câmera HD para ambientes internos',
|
||||||
|
type: 'RTSP',
|
||||||
|
status: 'Ativo',
|
||||||
|
ip: '192.168.1.100',
|
||||||
|
port: '8080'
|
||||||
|
},
|
||||||
|
// Adicione mais câmeras conforme necessário
|
||||||
],
|
],
|
||||||
};
|
statusOptions: ['Ativo', 'Inativo', 'Em Manutenção'],
|
||||||
|
typeOptions: ['HTTP','RTSP'],
|
||||||
|
modelOptions: ['Modelo AXIS Q6128', 'Modelo Hikvision DS-2CD', 'Modelo AK-31548'],
|
||||||
|
selectedModel: null,
|
||||||
|
selectedCamera: null,
|
||||||
|
dialogs: {
|
||||||
|
training: false,
|
||||||
|
delete: false
|
||||||
|
},
|
||||||
|
training: {
|
||||||
|
inProgress: false,
|
||||||
|
progress: 0,
|
||||||
|
complete: false,
|
||||||
|
success: false,
|
||||||
|
message: ''
|
||||||
|
},
|
||||||
|
hasTrained: false,
|
||||||
|
hasIntegrated: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
trainModels() {
|
customFilter(value, search, item) {
|
||||||
alert("Iniciando treinamento dos modelos...");
|
if (search == null || search === '') return true
|
||||||
|
|
||||||
|
const modelMatch = item.model.toLowerCase().includes(search.toLowerCase())
|
||||||
|
const statusMatch = !this.filters.status || item.status === this.filters.status
|
||||||
|
|
||||||
|
return modelMatch && statusMatch
|
||||||
},
|
},
|
||||||
integrateModels() {
|
|
||||||
alert("Integrando modelos...");
|
openTrainingDialog() {
|
||||||
|
this.training = {
|
||||||
|
inProgress: false,
|
||||||
|
progress: 0,
|
||||||
|
complete: false,
|
||||||
|
success: false,
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
this.dialogs.training = true
|
||||||
},
|
},
|
||||||
viewDetails(item) {
|
|
||||||
alert(`Visualizando detalhes do modelo: ${item.name}`);
|
closeTrainingDialog() {
|
||||||
|
this.dialogs.training = false
|
||||||
|
this.selectedModel = null
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
async startTraining() {
|
||||||
|
this.training.inProgress = true
|
||||||
|
this.training.progress = 0
|
||||||
|
|
||||||
|
// Simulação do progresso de treinamento
|
||||||
|
for (let i = 0; i <= 100; i += 10) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
this.training.progress = i
|
||||||
|
}
|
||||||
|
|
||||||
|
this.training.inProgress = false
|
||||||
|
this.training.complete = true
|
||||||
|
this.training.success = true
|
||||||
|
this.training.message = 'Treinamento concluído com sucesso!'
|
||||||
|
this.hasTrained = true
|
||||||
|
},
|
||||||
|
|
||||||
|
integrate() {
|
||||||
|
// Lógica de integração
|
||||||
|
this.hasIntegrated = true
|
||||||
|
// Exibir mensagem de sucesso
|
||||||
|
},
|
||||||
|
|
||||||
|
test() {
|
||||||
|
// Lógica de teste
|
||||||
|
// Exibir resultados do teste
|
||||||
|
},
|
||||||
|
|
||||||
|
editCamera(camera) {
|
||||||
|
this.selectedCamera = camera
|
||||||
|
// Implementar lógica de edição
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmDelete(camera) {
|
||||||
|
this.selectedCamera = camera
|
||||||
|
this.dialogs.delete = true
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteCamera() {
|
||||||
|
const index = this.cameras.findIndex(c => c.id === this.selectedCamera.id)
|
||||||
|
if (index > -1) {
|
||||||
|
this.cameras.splice(index, 1)
|
||||||
|
}
|
||||||
|
this.dialogs.delete = false
|
||||||
|
this.selectedCamera = null
|
||||||
|
// Exibir mensagem de sucesso
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.testing-view {
|
.v-data-table {
|
||||||
background: #f9f9f9;
|
width: 100%;
|
||||||
min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user