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"
|
||||
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>
|
||||
<span v-if="!isCollapsed" class="text-h6 ml-3 white--text font-weight-bold">TARS</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-divider class="border-opacity-25"></v-divider>
|
||||
|
||||
<!-- User Profile Section -->
|
||||
<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 -->
|
||||
<!-- Navigation Menu -->
|
||||
<v-list nav class="px-2">
|
||||
<v-list-item
|
||||
v-for="item in flattenedMenuItems"
|
||||
@ -48,6 +32,7 @@
|
||||
:value="item.name"
|
||||
rounded="lg"
|
||||
class="mb-1"
|
||||
@click="item.name === 'logout' ? handleLogout() : null"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon :size="22">{{ item.icon }}</v-icon>
|
||||
@ -85,32 +70,25 @@
|
||||
</template>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<!-- Updated Top Bar -->
|
||||
<!-- Top Bar -->
|
||||
<v-app-bar
|
||||
elevation="1"
|
||||
height="64"
|
||||
color="background"
|
||||
>
|
||||
<v-spacer />
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<!-- 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-icon>mdi-account</v-icon>
|
||||
</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>
|
||||
|
||||
<!-- Logout Button -->
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="tonal"
|
||||
@click="handleLogout"
|
||||
prepend-icon="mdi-logout"
|
||||
class="mr-2"
|
||||
>
|
||||
Sair
|
||||
</v-btn>
|
||||
</v-app-bar>
|
||||
|
||||
<!-- Main Content -->
|
||||
@ -118,16 +96,34 @@
|
||||
<v-container fluid class="pa-6">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
<component :is="Component"></component>
|
||||
</transition>
|
||||
</router-view>
|
||||
</v-container>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'AppLayout',
|
||||
@ -136,6 +132,11 @@ export default {
|
||||
return {
|
||||
drawer: true,
|
||||
isCollapsed: false,
|
||||
snackbar: {
|
||||
show: false,
|
||||
text: '',
|
||||
color: 'success'
|
||||
},
|
||||
flattenedMenuItems: [
|
||||
{
|
||||
name: 'home',
|
||||
@ -181,27 +182,40 @@ export default {
|
||||
icon: 'mdi-account-group',
|
||||
label: 'Usuários'
|
||||
},
|
||||
{
|
||||
name: 'logout',
|
||||
route: { name: 'login' },
|
||||
icon: 'mdi-logout',
|
||||
label: 'Sair',
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters('auth', [
|
||||
'isAuthenticated',
|
||||
'currentUser'
|
||||
])
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions('auth', ['logout']),
|
||||
|
||||
toggleDrawer() {
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
},
|
||||
|
||||
goToUserProfile() {
|
||||
this.$router.push({ name: 'user-profile' });
|
||||
|
||||
showSnackbar(text, color = 'success') {
|
||||
this.snackbar.text = text;
|
||||
this.snackbar.color = color;
|
||||
this.snackbar.show = true;
|
||||
},
|
||||
|
||||
|
||||
async handleLogout() {
|
||||
try {
|
||||
await this.logout();
|
||||
await this.$store.dispatch('auth/logout');
|
||||
this.$router.push({ name: 'login' });
|
||||
} catch (error) {
|
||||
console.error('Erro ao fazer logout:', error);
|
||||
this.showSnackbar('Erro ao fazer logout', 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,6 +236,10 @@ export default {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile-section:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.v-list-item--active {
|
||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
@ -1,115 +1,306 @@
|
||||
<template>
|
||||
<div class="testing-view">
|
||||
<v-container>
|
||||
<v-row class="mb-4">
|
||||
<!-- Filtro de Data/Hora -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="filters.date"
|
||||
label="Data/Hora"
|
||||
type="datetime-local"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-container>
|
||||
<!-- Filtros de Pesquisa -->
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-text-field
|
||||
v-model="filters.model"
|
||||
label="Buscar por modelo"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
@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 -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="filters.cameraId"
|
||||
label="Câmera ID"
|
||||
clearable
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<!-- Botões de Ação -->
|
||||
<v-row class="mb-4">
|
||||
<v-col>
|
||||
<v-btn
|
||||
color="primary"
|
||||
class="mr-2"
|
||||
@click="openTrainingDialog"
|
||||
>
|
||||
<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 -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="filters.search"
|
||||
label="Pesquisar"
|
||||
append-icon="mdi-magnify"
|
||||
clearable
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!-- Tabela de Câmeras -->
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="cameras"
|
||||
:search="filters.model"
|
||||
:custom-filter="customFilter"
|
||||
>
|
||||
<template #[`item.actions`]="{ item }">
|
||||
<v-icon
|
||||
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 -->
|
||||
<v-row class="mb-4">
|
||||
<v-col class="d-flex justify-end">
|
||||
<v-btn color="primary" class="mr-2" @click="trainModels">
|
||||
Treinar Modelos
|
||||
</v-btn>
|
||||
<v-btn color="success" @click="integrateModels">
|
||||
Integrar Modelos
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Lista de Modelos de Teste -->
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="models"
|
||||
item-value="id"
|
||||
class="elevation-1"
|
||||
<!-- Diálogo de Treinamento -->
|
||||
<v-dialog v-model="dialogs.training" max-width="500px">
|
||||
<v-card>
|
||||
<v-card-title>Treinar Modelo</v-card-title>
|
||||
<v-card-text>
|
||||
<v-select
|
||||
v-model="selectedModel"
|
||||
:items="modelOptions"
|
||||
label="Selecione o modelo"
|
||||
required
|
||||
/>
|
||||
<v-progress-linear
|
||||
v-if="training.inProgress"
|
||||
:value="training.progress"
|
||||
height="25"
|
||||
>
|
||||
<template #top>
|
||||
<v-toolbar flat>
|
||||
<v-toolbar-title>Modelos de Teste</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
</v-toolbar>
|
||||
<template v-slot:default>
|
||||
{{ training.progress }}%
|
||||
</template>
|
||||
<template #item_actions="{ item }">
|
||||
<v-btn icon color="primary" @click="viewDetails(item)">
|
||||
<v-icon>mdi-eye</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-progress-linear>
|
||||
<v-alert
|
||||
v-if="training.complete"
|
||||
:type="training.success ? 'success' : 'error'"
|
||||
class="mt-4"
|
||||
>
|
||||
{{ training.message }}
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
<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>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TestingView",
|
||||
data() {
|
||||
return {
|
||||
filters: {
|
||||
date: "",
|
||||
cameraId: "",
|
||||
search: "",
|
||||
model: '',
|
||||
status: null
|
||||
},
|
||||
headers: [
|
||||
{ text: "ID", value: "id" },
|
||||
{ text: "Nome", value: "name" },
|
||||
{ text: "Status", value: "status" },
|
||||
{ text: "Ações", value: "actions", sortable: false },
|
||||
{ title: 'Modelo', key: 'model' },
|
||||
{ title: 'Descrição', key: 'description' },
|
||||
{ title: 'Tipo', key: 'type' },
|
||||
{ title: 'Status', key: 'status' },
|
||||
{ title: 'Info', key: 'info'},
|
||||
{ title: 'Vizualização', key: 'ip' },
|
||||
{ title: 'Ações', key: 'actions', sortable: false }
|
||||
],
|
||||
models: [
|
||||
{ id: 1, name: "Modelo A", status: "Pronto" },
|
||||
{ id: 2, name: "Modelo B", status: "Em Execução" },
|
||||
{ id: 3, name: "Modelo C", status: "Concluído" },
|
||||
cameras: [
|
||||
{
|
||||
id: 1,
|
||||
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: {
|
||||
trainModels() {
|
||||
alert("Iniciando treinamento dos modelos...");
|
||||
customFilter(value, search, item) {
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
.testing-view {
|
||||
background: #f9f9f9;
|
||||
min-height: 100vh;
|
||||
.v-data-table {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user