Remove CDN dependencies to ensure the app works without internet access. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
739 lines
35 KiB
HTML
739 lines
35 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>АСУ Инвентаризация</title>
|
||
<link rel="stylesheet" href="/app/bootstrap.min.css" />
|
||
<link rel="stylesheet" href="/app/styles.css" />
|
||
</head>
|
||
<body>
|
||
<header class="no-print">
|
||
<h1>
|
||
<a href="/app/">АСУ Инвентаризация</a>
|
||
</h1>
|
||
</header>
|
||
|
||
<div id="app">
|
||
<div class="row no-print">
|
||
<nav class="no-print navbar navbar-expand-lg navbar-light">
|
||
<div class="container-fluid">
|
||
<a class="navbar-brand" href="#" @click.prevent="showHome">Главная</a>
|
||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||
<span class="navbar-toggler-icon"></span>
|
||
</button>
|
||
<div class="collapse navbar-collapse" id="navbarNav">
|
||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||
<li class="nav-item"><a class="nav-link" href="#" @click.prevent="showHome">Главная</a></li>
|
||
<li class="nav-item dropdown">
|
||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Оборудование</a>
|
||
<ul class="dropdown-menu">
|
||
<li><a class="dropdown-item" href="#" @click.prevent="view='byAud'">По аудитории</a></li>
|
||
<li><a class="dropdown-item" href="#" @click.prevent="showAllEquipment">Всё оборудование</a></li>
|
||
<li><a class="dropdown-item" href="#" @click.prevent="showUnassigned">Не распределено</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-item" v-if="isAuth"><a class="nav-link" href="#" @click.prevent="showInspection">Проверка</a></li>
|
||
<li class="nav-item dropdown" v-if="isAdmin">
|
||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Просмотр</a>
|
||
<ul class="dropdown-menu">
|
||
<li><a class="dropdown-item" href="#" @click.prevent="view='users'">Пользователи</a></li>
|
||
<li><a class="dropdown-item" href="#" @click.prevent="view='audManage'">Аудитории</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-item"><a class="nav-link" href="#" @click.prevent="showZametki">Заметки</a></li>
|
||
<li class="nav-item dropdown" v-if="canEdit">
|
||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Добавить</a>
|
||
<ul class="dropdown-menu">
|
||
<li><a class="dropdown-item" href="#" @click.prevent="view='addEquipment'">Оборудование</a></li>
|
||
<li><a class="dropdown-item" href="#" @click.prevent="view='addAuditory'">Аудиторию</a></li>
|
||
<li><a class="dropdown-item" href="#" @click.prevent="view='addType'">Тип оборудования</a></li>
|
||
<li><a class="dropdown-item" href="#" @click.prevent="view='addOwner'">Владельца</a></li>
|
||
<li v-if="isAdmin"><hr class="dropdown-divider"></li>
|
||
<li v-if="isAdmin"><a class="dropdown-item" href="#" @click.prevent="view='addUser'">Пользователя</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-item" v-if="isAuth"><a class="nav-link" href="/docs" target="_blank">API Docs</a></li>
|
||
</ul>
|
||
<span class="navbar-text ms-auto" v-if="isAuth">Роль: {{ role }}</span>
|
||
<a class="btn btn-outline-primary ms-2" v-if="!isAuth" href="/login">Войти</a>
|
||
<button class="btn btn-outline-secondary ms-2" v-else @click.prevent="logout">Выйти</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="container-fluid px-4">
|
||
|
||
<!-- Главная страница -->
|
||
<div v-if="view==='home'">
|
||
<div class="row">
|
||
<div class="card col-md-6 col-10">
|
||
<div class="card-body">
|
||
<form @submit.prevent="doHomeSearch">
|
||
<div class="d-flex align-items-center gap-2">
|
||
<input type="text" class="form-control w-auto" v-model="homeSearch" placeholder="инвентарный номер" />
|
||
<button class="btn btn-primary" type="submit">Найти</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-3">
|
||
<div class="card col-md-10 col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Нераспределённые</h3>
|
||
<div class="status" :class="{error: !!error}">{{ status }}</div>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">Инв. номер</th>
|
||
<th scope="col">Название</th>
|
||
<th scope="col">Аудитория</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="it in homeUnassigned" :key="it.id">
|
||
<td>{{ it.invNumber ?? '' }}</td>
|
||
<td>{{ it.nazvanie ?? '' }}</td>
|
||
<td>
|
||
<template v-if="canEdit">
|
||
<select class="form-select form-select-sm d-inline w-auto" v-model="it.selectedAudId">
|
||
<option value="">— выберите —</option>
|
||
<option v-for="a in auditories" :key="a.id" :value="a.id">{{ a.audnazvanie }}</option>
|
||
</select>
|
||
<button class="btn btn-sm btn-primary ms-2" @click="assignToAuditory(it)">Назначить</button>
|
||
</template>
|
||
<template v-else>—</template>
|
||
</td>
|
||
</tr>
|
||
<tr v-if="homeUnassigned.length === 0">
|
||
<td colspan="3" class="text-muted text-center">Нераспределённого оборудования нет</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-3" v-if="homeSearchResults.length > 0">
|
||
<div class="card col-md-10 col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Распределённые</h3>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">Инв. номер</th>
|
||
<th scope="col">Название</th>
|
||
<th scope="col">Аудитория исходная</th>
|
||
<th scope="col" v-if="canEdit">Аудитория переноса</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="it in homeSearchResults" :key="it.id">
|
||
<td>{{ it.invNumber ?? '' }}</td>
|
||
<td>{{ it.nazvanie ?? '' }}</td>
|
||
<td>{{ getAuditoryName(it.aud_id) }}</td>
|
||
<td v-if="canEdit">
|
||
<select class="form-select form-select-sm d-inline w-auto" v-model="it.selectedAudId">
|
||
<option value="">— не менять —</option>
|
||
<option v-for="a in auditories" :key="a.id" :value="a.id">{{ a.audnazvanie }}</option>
|
||
</select>
|
||
<button class="btn btn-sm btn-outline-primary ms-2" :disabled="!it.selectedAudId" @click="assignToAuditory(it)">Перенести</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-2" v-if="homeSearchDone && homeSearchResults.length === 0">
|
||
<div class="col-12 text-center text-muted">Ничего не найдено по запросу «{{ homeSearch }}»</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="view==='byAud'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title no-print">Оборудование по аудитории</h3>
|
||
<h2 class="print-only print-title">{{ printTitle }}</h2>
|
||
<div class="mb-2 d-flex align-items-center justify-content-center gap-2 no-print">
|
||
<label for="aud-select" class="mb-0">Аудитория:</label>
|
||
<select id="aud-select" class="form-select w-auto" v-model="selectedAudId">
|
||
<option value="">— выберите аудиторию —</option>
|
||
<option v-for="a in auditories" :key="a.id" :value="a.id">{{ a.audnazvanie }}</option>
|
||
</select>
|
||
<button class="btn btn-primary" @click="loadOboruds">Показать</button>
|
||
<button class="btn btn-secondary" @click="printPage" :disabled="!oboruds.length">Печать</button>
|
||
</div>
|
||
<div class="status no-print" :class="{error: !!error}">{{ status }}</div>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">ID</th>
|
||
<th scope="col">Инв. номер</th>
|
||
<th scope="col">Название</th>
|
||
<th scope="col">Расположение</th>
|
||
<th scope="col" class="no-print">Кол-во</th>
|
||
<th scope="col" class="no-print">Тип</th>
|
||
<th scope="col" class="no-print">Владелец</th>
|
||
<th scope="col" class="print-only">Проверено</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="it in oboruds" :key="it.id">
|
||
<td>{{ it.id }}</td>
|
||
<td class="inv">{{ it.invNumber ?? '' }}</td>
|
||
<td>{{ it.nazvanie ?? '' }}</td>
|
||
<td class="rasp">{{ it.raspologenie ?? '' }}</td>
|
||
<td class="no-print">{{ it.kolichestvo ?? '' }}</td>
|
||
<td class="no-print">{{ it.type?.name ?? '' }}</td>
|
||
<td class="no-print">
|
||
<template v-if="canEdit">
|
||
<select class="form-select form-select-sm d-inline w-auto" v-model="it.selectedOwnerId">
|
||
<option value="">— нет —</option>
|
||
<option v-for="ow in owners" :key="ow.id" :value="ow.id">{{ ow.name }}</option>
|
||
</select>
|
||
<button class="btn btn-sm btn-outline-primary ms-2" @click="saveOwner(it)">Сохранить</button>
|
||
</template>
|
||
<template v-else>
|
||
{{ it.owner?.name ?? '' }}
|
||
</template>
|
||
</td>
|
||
<td class="print-only"></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="view==='allEquipment'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title no-print">Всё оборудование (по инв. номеру)</h3>
|
||
<h2 class="print-only print-title">Всё оборудование</h2>
|
||
<div class="mb-2 no-print">
|
||
<button class="btn btn-secondary" @click="printAllEquipment" :disabled="!allOboruds.length">Печать</button>
|
||
</div>
|
||
<div class="status no-print" :class="{error: !!error}">{{ status }}</div>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col" class="num-col">№</th>
|
||
<th scope="col" class="inv-col">Инв. номер</th>
|
||
<th scope="col">Название</th>
|
||
<th scope="col" class="aud-col">Аудитория</th>
|
||
<th scope="col">Расположение</th>
|
||
<th scope="col" class="no-print">Кол-во</th>
|
||
<th scope="col" class="no-print">Владелец</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="(it, index) in allOboruds" :key="it.id">
|
||
<td class="num-col">{{ index + 1 }}</td>
|
||
<td class="inv-col">{{ it.invNumber ?? '' }}</td>
|
||
<td>{{ it.nazvanie ?? '' }}</td>
|
||
<td class="aud-col">{{ getAuditoryName(it.aud_id) }}</td>
|
||
<td class="rasp">{{ it.raspologenie ?? '' }}</td>
|
||
<td class="no-print">{{ it.kolichestvo ?? '' }}</td>
|
||
<td class="no-print">{{ it.owner?.name ?? '' }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="view==='users'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Администрирование пользователей</h3>
|
||
<div v-if="role!=='admin'" class="alert alert-warning">Недостаточно прав. Войдите как администратор.</div>
|
||
|
||
<div v-else>
|
||
<h5 class="mt-2">Создать пользователя-админа</h5>
|
||
<div class="row g-2 align-items-end">
|
||
<div class="col-auto">
|
||
<label class="form-label">Логин</label>
|
||
<input class="form-control" v-model="newAdminUsername" placeholder="username" />
|
||
</div>
|
||
<div class="col-auto">
|
||
<label class="form-label">Пароль</label>
|
||
<input type="password" class="form-control" v-model="newAdminPassword" placeholder="password" />
|
||
</div>
|
||
<div class="col-auto">
|
||
<button class="btn btn-success" @click="createAdmin">Создать админа</button>
|
||
</div>
|
||
</div>
|
||
<div class="status mt-2" :class="{error: !!error}">{{ status }}</div>
|
||
|
||
<h5 class="mt-4">Пользователи</h5>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Логин</th>
|
||
<th>Роль</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="u in users" :key="u.id">
|
||
<td>{{ u.id }}</td>
|
||
<td>{{ u.username }}</td>
|
||
<td>{{ u.role }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="view==='audManage'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Управление аудиториями</h3>
|
||
<div v-if="role!=='admin'" class="alert alert-warning">Недостаточно прав. Войдите как администратор.</div>
|
||
|
||
<div v-else>
|
||
<h5 class="mt-2">Добавить аудиторию</h5>
|
||
<div class="row g-2 align-items-end">
|
||
<div class="col-auto">
|
||
<label class="form-label">Название аудитории</label>
|
||
<input class="form-control" v-model="newAudName" placeholder="например, 519" />
|
||
</div>
|
||
<div class="col-auto">
|
||
<button class="btn btn-success" @click="createAuditory">Добавить</button>
|
||
</div>
|
||
</div>
|
||
<div class="status mt-2" :class="{error: !!error}">{{ status }}</div>
|
||
|
||
<h5 class="mt-4">Список аудиторий</h5>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Название</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="a in auditories" :key="a.id">
|
||
<td>{{ a.id }}</td>
|
||
<td>{{ a.audnazvanie }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="view==='owners'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Управление владельцами</h3>
|
||
<div v-if="!canEdit" class="alert alert-warning">Недостаточно прав. Войдите как администратор или редактор.</div>
|
||
<div v-else>
|
||
<h5 class="mt-2">Добавить владельца</h5>
|
||
<div class="row g-2 align-items-end">
|
||
<div class="col-auto">
|
||
<label class="form-label">Имя владельца</label>
|
||
<input class="form-control" v-model="newOwnerName" placeholder="например, Иванов И.И." />
|
||
</div>
|
||
<div class="col-auto">
|
||
<button class="btn btn-success" @click="createOwner">Добавить</button>
|
||
</div>
|
||
</div>
|
||
<div class="status mt-2" :class="{error: !!error}">{{ status }}</div>
|
||
|
||
<h5 class="mt-4">Список владельцев</h5>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Имя</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="o in owners" :key="o.id">
|
||
<td>{{ o.id }}</td>
|
||
<td>{{ o.name }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="view==='unassigned'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Не распределено: {{ unassignedOboruds.length }} из {{ totalOboruds }}</h3>
|
||
<div class="status" :class="{error: !!error}">{{ status }}</div>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">№</th>
|
||
<th scope="col">Инв. номер</th>
|
||
<th scope="col">Название</th>
|
||
<th scope="col">Владелец</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="(it, index) in unassignedOboruds" :key="it.id">
|
||
<td>{{ index + 1 }}</td>
|
||
<td class="inv">{{ it.invNumber ?? '' }}</td>
|
||
<td>{{ it.nazvanie ?? '' }}</td>
|
||
<td>{{ it.owner?.name ?? '' }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="view==='zametki'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Заметки</h3>
|
||
|
||
<div v-if="canEdit" class="mb-4">
|
||
<h5>Добавить заметку</h5>
|
||
<div class="row g-2 align-items-end">
|
||
<div class="col-md-8">
|
||
<textarea class="form-control" v-model="newZametkaText" rows="3" placeholder="Текст заметки..."></textarea>
|
||
</div>
|
||
<div class="col-auto">
|
||
<button class="btn btn-success" @click="createZametka">Добавить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="status" :class="{error: !!error}">{{ status }}</div>
|
||
|
||
<h5>Активные заметки</h5>
|
||
<div v-if="zametki.length === 0" class="text-muted">Нет активных заметок</div>
|
||
<div v-for="z in zametki" :key="z.id" class="card mb-2">
|
||
<div class="card-body">
|
||
<p class="card-text" style="white-space: pre-wrap;">{{ z.txtzam }}</p>
|
||
<small class="text-muted">{{ formatDate(z.created_date) }}</small>
|
||
<button v-if="canEdit" class="btn btn-sm btn-outline-success float-end" @click="resolveZametka(z.id)">Решено</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Добавить оборудование -->
|
||
<div v-if="view==='addEquipment'" class="row">
|
||
<div class="card col-12 col-md-8">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Добавить оборудование</h3>
|
||
<div class="row g-3">
|
||
<div class="col-md-4">
|
||
<label class="form-label">Инв. номер</label>
|
||
<input type="number" class="form-control" v-model="newEquipment.invNumber" />
|
||
</div>
|
||
<div class="col-md-8">
|
||
<label class="form-label">Название</label>
|
||
<input type="text" class="form-control" v-model="newEquipment.nazvanie" required />
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label">Аудитория</label>
|
||
<select class="form-select" v-model="newEquipment.aud_id">
|
||
<option value="">— не выбрано —</option>
|
||
<option v-for="a in auditories" :key="a.id" :value="a.id">{{ a.audnazvanie }}</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label">Тип</label>
|
||
<select class="form-select" v-model="newEquipment.type_id">
|
||
<option value="">— не выбрано —</option>
|
||
<option v-for="t in equipmentTypes" :key="t.id" :value="t.id">{{ t.name }}</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label">Владелец</label>
|
||
<select class="form-select" v-model="newEquipment.owner_id">
|
||
<option value="">— не выбрано —</option>
|
||
<option v-for="o in owners" :key="o.id" :value="o.id">{{ o.name }}</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-8">
|
||
<label class="form-label">Расположение</label>
|
||
<input type="text" class="form-control" v-model="newEquipment.raspologenie" />
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label">Количество</label>
|
||
<input type="number" class="form-control" v-model="newEquipment.kolichestvo" />
|
||
</div>
|
||
</div>
|
||
<div class="status mt-3" :class="{error: !!error}">{{ status }}</div>
|
||
<button class="btn btn-success mt-3" @click="createEquipment">Добавить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Добавить аудиторию -->
|
||
<div v-if="view==='addAuditory'" class="row">
|
||
<div class="card col-12 col-md-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Добавить аудиторию</h3>
|
||
<div class="mb-3">
|
||
<label class="form-label">Название аудитории</label>
|
||
<input type="text" class="form-control" v-model="newAudName" placeholder="например, 519" />
|
||
</div>
|
||
<div class="status" :class="{error: !!error}">{{ status }}</div>
|
||
<button class="btn btn-success" @click="createAuditory">Добавить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Добавить тип оборудования -->
|
||
<div v-if="view==='addType'" class="row">
|
||
<div class="card col-12 col-md-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Добавить тип оборудования</h3>
|
||
<div class="mb-3">
|
||
<label class="form-label">Название типа</label>
|
||
<input type="text" class="form-control" v-model="newTypeName" placeholder="например, Компьютер" />
|
||
</div>
|
||
<div class="status" :class="{error: !!error}">{{ status }}</div>
|
||
<button class="btn btn-success" @click="createEquipmentType">Добавить</button>
|
||
|
||
<h5 class="mt-4">Существующие типы</h5>
|
||
<ul class="list-group">
|
||
<li class="list-group-item" v-for="t in equipmentTypes" :key="t.id">{{ t.name }}</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Добавить владельца -->
|
||
<div v-if="view==='addOwner'" class="row">
|
||
<div class="card col-12 col-md-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Добавить владельца</h3>
|
||
<div class="mb-3">
|
||
<label class="form-label">Имя владельца</label>
|
||
<input type="text" class="form-control" v-model="newOwnerName" placeholder="например, Иванов И.И." />
|
||
</div>
|
||
<div class="status" :class="{error: !!error}">{{ status }}</div>
|
||
<button class="btn btn-success" @click="createOwner">Добавить</button>
|
||
|
||
<h5 class="mt-4">Существующие владельцы</h5>
|
||
<ul class="list-group">
|
||
<li class="list-group-item" v-for="o in owners" :key="o.id">{{ o.name }}</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Добавить пользователя -->
|
||
<div v-if="view==='addUser'" class="row">
|
||
<div class="card col-12 col-md-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Добавить пользователя</h3>
|
||
<div class="mb-3">
|
||
<label class="form-label">Логин</label>
|
||
<input type="text" class="form-control" v-model="newAdminUsername" />
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Пароль</label>
|
||
<input type="password" class="form-control" v-model="newAdminPassword" />
|
||
</div>
|
||
<div class="status" :class="{error: !!error}">{{ status }}</div>
|
||
<button class="btn btn-success" @click="createAdmin">Создать администратора</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Проверка оборудования -->
|
||
<div v-if="view==='inspection'" class="row">
|
||
<div class="card col-12">
|
||
<div class="card-body">
|
||
<h3 class="card-title">Проверка оборудования</h3>
|
||
|
||
<!-- Блок 1: Начать проверку (если нет активной сессии) -->
|
||
<div v-if="!activeInspection">
|
||
<h5>Начать новую проверку</h5>
|
||
<div class="row g-2 align-items-end mb-3">
|
||
<div class="col-auto">
|
||
<label class="form-label">Аудитория (необязательно)</label>
|
||
<select class="form-select" v-model="inspectionAudId">
|
||
<option value="">— Всё оборудование —</option>
|
||
<option v-for="a in auditories" :key="a.id" :value="a.id">{{ a.audnazvanie }}</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-auto">
|
||
<button class="btn btn-success" @click="startInspection">Начать проверку</button>
|
||
</div>
|
||
</div>
|
||
|
||
<h5 class="mt-4">История проверок</h5>
|
||
<button class="btn btn-secondary mb-3" @click="loadInspectionHistory">Загрузить историю</button>
|
||
|
||
<div v-if="inspectionHistory.length > 0" class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Начата</th>
|
||
<th>Завершена</th>
|
||
<th>Аудитория</th>
|
||
<th>Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="sess in inspectionHistory" :key="sess.id">
|
||
<td>{{ sess.id }}</td>
|
||
<td>{{ formatDate(sess.started_at) }}</td>
|
||
<td>{{ sess.completed_at ? formatDate(sess.completed_at) : '—' }}</td>
|
||
<td>{{ sess.aud_id ? getAuditoryName(sess.aud_id) : 'Всё' }}</td>
|
||
<td>
|
||
<button class="btn btn-sm btn-primary" @click="viewHistoryDetails(sess.id)">Детали</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Блок 2: Активная проверка -->
|
||
<div v-else>
|
||
<div class="alert alert-info">
|
||
<strong>Активная проверка</strong><br>
|
||
<span v-if="activeInspection.aud_id">Аудитория: {{ getAuditoryName(activeInspection.aud_id) }}</span>
|
||
<span v-else>Всё оборудование</span><br>
|
||
Начата: {{ formatDate(activeInspection.started_at) }}
|
||
</div>
|
||
|
||
<!-- Статистика прогресса -->
|
||
<div class="row mb-3">
|
||
<div class="col-md-3">
|
||
<div class="card text-center">
|
||
<div class="card-body">
|
||
<h5 class="card-title">{{ inspectionStats.total_checked }}</h5>
|
||
<p class="card-text">Проверено</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="card text-center">
|
||
<div class="card-body">
|
||
<h5 class="card-title">{{ inspectionStats.total_expected }}</h5>
|
||
<p class="card-text">Всего</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="card text-center">
|
||
<div class="card-body">
|
||
<h5 class="card-title">{{ inspectionStats.total_unknown }}</h5>
|
||
<p class="card-text">Не найдено</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="card text-center bg-success text-white">
|
||
<div class="card-body">
|
||
<h5 class="card-title">{{ inspectionStats.progress_percent }}%</h5>
|
||
<p class="card-text">Прогресс</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Поле для сканирования -->
|
||
<div class="mb-3">
|
||
<label class="form-label">Сканируйте штрихкод или введите инв. номер</label>
|
||
<input
|
||
ref="barcodeInput"
|
||
type="text"
|
||
class="form-control form-control-lg"
|
||
v-model="scannedBarcode"
|
||
@keyup.enter="checkBarcode"
|
||
placeholder="Отсканируйте или введите номер..."
|
||
autofocus
|
||
/>
|
||
</div>
|
||
|
||
<!-- Последний результат сканирования -->
|
||
<div v-if="lastScanResult" class="alert" :class="lastScanResult.status === 'found' ? 'alert-success' : 'alert-danger'">
|
||
{{ lastScanResult.message }}
|
||
<div v-if="lastScanResult.equipment">
|
||
<strong>{{ lastScanResult.equipment.nazvanie }}</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Кнопки действий -->
|
||
<div class="mt-3 mb-4">
|
||
<button class="btn btn-primary me-2" @click="refreshInspectionData">Обновить данные</button>
|
||
<button class="btn btn-success me-2" @click="completeInspection">Завершить проверку</button>
|
||
<button class="btn btn-secondary" @click="cancelInspection">Отменить</button>
|
||
</div>
|
||
|
||
<!-- Таблица проверенного оборудования (в реальном времени) -->
|
||
<h5 class="mt-4">Проверенное оборудование</h5>
|
||
<div class="table-responsive">
|
||
<table class="table datatable">
|
||
<thead>
|
||
<tr>
|
||
<th>Инв. номер</th>
|
||
<th>Название</th>
|
||
<th>Аудитория</th>
|
||
<th>Проверено</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="rec in checkedEquipment" :key="rec.id">
|
||
<td>{{ rec.oborud?.invNumber }}</td>
|
||
<td>{{ rec.oborud?.nazvanie }}</td>
|
||
<td>{{ getAuditoryName(rec.oborud?.aud_id) }}</td>
|
||
<td>{{ formatDate(rec.checked_at) }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Неизвестные штрихкоды -->
|
||
<div v-if="unknownBarcodes.length > 0">
|
||
<h5 class="mt-4 text-danger">Неизвестные штрихкоды</h5>
|
||
<ul class="list-group">
|
||
<li class="list-group-item" v-for="ub in unknownBarcodes" :key="ub.id">
|
||
{{ ub.barcode }} — {{ formatDate(ub.scanned_at) }}
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/app/vue.global.prod.js"></script>
|
||
<script src="/app/bootstrap.bundle.min.js"></script>
|
||
<script src="/app/app.js" defer></script>
|
||
</body>
|
||
</html>
|