Uploaded by v.starckov2012

Курсовая работа бд

advertisement
МИНОБРНАУКИ РОССИИ
Федеральное государственное бюджетное образовательное
учреждение высшего образования
«Ижевский государственный технический университет имени М.Т. Калашникова»
Институт информатики и вычислительной техники
Кафедра «Программное обеспечение»
Работа защищена с оценкой
«_______»
Дата______________
Подпись ___________/_____________
КУРСОВАЯ РАБОТА
По дисциплине: «Базы данных»
Тема: «Лабораторная информационная система»
Выполнил:
студент гр. Б18-191-1з
В.В. Старков
Принял:
к.т.н кафедры ПО
А.В. Старыгин
Рецензия:
степень достижения поставленной цели работы_________________________________
полнота разработки темы____________________________________________________
уровень самостоятельности работы обучающегося_______________________________
недостатки работы__________________________________________________________
__________________________________________________________________________
ОГЛАВЛЕНИЕ
1 ОПИСАНИЕ ЗАДАНИЯ ......................... Ошибка! Закладка не определена.
2 АНАЛИЗ ПРЕДМЕТНОЙ ОБЛАСТИ ............................................................... 4
3 ДИАГРАММА КЛАССОВ.................................................................................. 6
5 КОНТРОЛЬНЫЙ ПРИМЕР .................. Ошибка! Закладка не определена.2
6 КОД ПРОГРАММЫ .............................. Ошибка! Закладка не определена.6
ЗАКЛЮЧЕНИЕ ..................................................................................................... 49
СПИСОК ЛИТЕРАТУРЫ .................................................................................... 50
2
1 ОПИСАНИЕ ЗАДАНИЯ
В
лаборатории
учитывается
достаточно
много
показателей,
характеризующих эффективность получения результат исследования.
Очень важно получать как можно более полную отчетность по запасам
химических
реактивов,
состоянию
оборудования
от
исполнителей,
метрологов, закупщиков. Запасы – самый крупный и, по сути, единственный
актив в бухгалтерском балансе предприятий, поэтому управление этим
активом в значительной степени определяет качество обслуживания
клиентов и доходность компании.
В данной работе необходимо спроектировать базу данных и
разработать клиентское приложение для работы с созданной базой данных.
3
2 АНАЛИЗ ПРЕДМЕТНОЙ ОБЛАСТИ
Современные информационные менеджмент-системы осуществляют
взаимодействие между подразделениями, регулируют каждый процесс и
управляют всеми сферами деятельности лаборатории, тем самым
обеспечивая целостность организации. Схожие функции выполняет нервная
система живых организмов.
История развития ЛИМС следует тем же путем. Хотелось бы особенно
подчеркнуть, что каждый шаг на пути совершенствования лабораторных
информационных технологий был обусловлен необходимостью обеспечить
наилучшую приспособленность организации к жестким требованиям
внешней среды. Каждое качественно новое решение по автоматизации
процесса складывалось из строго определенных, жизненно важных
предпосылок. Переход на более высокий уровень позволял лаборатории
получить преимущество в конкурентной борьбе, которая в условиях
рыночной экономики выполняет функции естественного отбора.
До 1982 года осуществлялись попытки автоматизировать некоторые
лабораторные процессы с целью повышения производительности и
улучшения качества проведения исследований. Первоначально усилия были
направлены на упрощение процедуры ввода данных и управление системой
отчетов. Каждая организация справлялась собственными силами, затрачивая
на решение этих задач значительное время и ресурсы. В дальнейшем уже
готовые информационные системы могли быть сконфигурированы под
нужды нескольких лабораторий. Постепенно стали появляться программные
продукты, изготовленные под заказ сторонними фирмами.
В настоящее время информационные менеджмент-системы призваны
обеспечить не только целостность и качество лабораторного производства,
но и административное управление всеми процессами в учреждении, а также
эффективное взаимодействие с заказчиками и контролирующими
организациями.
4
3 ДИАГРАММА КЛАССОВ
Для разработки использовалась трехслойная архитектура. Каждый слой
может взаимодействовать с предыдущим слоем и логически разделяет
компоненты. Ниже представлены диаграммы классов каждого слоя.
1.
Слой доступа к данным состоит из интерфейсов (Рис. 3.1) и
реализующих их классов (Рис. 3.2), сущностей (Рис. 3.3) и репозиториев.
Рис. 3.1 Интерфейс слоя доступа к данным.
Рис. 3.2 Класс реализующие интерфейс.
5
Рис. 3.3 Сущности слоя доступа к данным.
2.
Слой бизнес уровня, состоит из интерфейсов паттерна медиатор (Рис.
3.4) и реализующих их классов (Рис. 3.5), сущностей (Рис. 3.6).
Рис. 3.4 Интерфейс бизнес уровня.
6
Рис. 3.5 Классы реализующие интерфейс бизнес уровня.
Рис. 3.6 Сущности бизнес уровня.
7
3.
Слой представления, состоит из клиентского приложения, которое
взаимодействуют с сервером и отправляет json в виде DTO объектов
сервисного слоя. Вся работа состоит из создания объекта json в браузере и
его отправки на сервер.
8
4 ВЫБОР И ОБОСНОВАНИЯ ЯЗЫКА ПРОГРАММИРОВАНИЯ
Для разработки курсового проекта был выбран язык программирования
C#, по причине быстрого старта разработки и получения прототипа
приложения. Далее описаны критерии, по которым был выбран язык
программирования C#.
1. Скорость разработки
С# позволяет стартовать разработку быстрее, а это позволяет быстрее
получить прототип решения. Скорость разработки на С# на начальных этапах
проекта значительно выше по сравнению с С++.
В коротких малобюджетных проектах С# будет иметь преимущество
по скорости разработки, но в длинных и относительно сложных, дорогих
данное преимущество будет незначительным.
2. Библиотеки
В С# огромное количество библиотек с .NET идет в базе, плюс к ним
множество свободно доступных библиотек, это покрывает практически все
первостепенные задачи разработки. Наличие большого количества
стандартных типов почти избавляет от библиотек, где базовые типы
переопределены. В библиотеках С# интерфейсы, как правило, лучше
вписываются в те или иные шаблоны проектирования, что часто упрощает их
изучение.
Неприятная особенность С++ библиотек - это создание и
переопределение своих базовых типов. Многие С++ библиотеки заводят свои
типы строк, контейнеров, переопределяют некоторые базовые типы. Этому
есть логичные объяснения (лучшая производительность, поддержка
кросплатформенности, отсутствие подходящих типов на момент написание
библиотеки), однако все это не добавляет удобства использования и красоты
коду. Базовые же С++ библиотеки дают не так много, как дают стандартные
9
библиотеки С#, поэтому подбор правильных библиотек для проекта С++ —
это задача, необходимая даже в сравнительно простых проектах.
3. Синтаксис
Более сложный код, часто, легче пишется и анализируется, если написан
более простым языком. С этой позиции, используя С#, меньше шансов
допустить ошибку в принципиально сложном коде и больше шансов
написать чистый код.
10
5 КОНТРОЛЬНЫЙ ПРИМЕР
Рис. 5.1 Начальная страница клиентского веб приложения.
Рис. 5.2 Страница оборудования с рабочей постраничной навигацией.
11
Рис. 5.3 Страница журнала поверок с рабочей постраничной навигацией.
Рис. 5.4 Страница поверок с рабочей постраничной навигацией.
12
Рис. 5.5 Страница инструкций с рабочей постраничной навигацией.
Рис. 5.6 Страница подразделений с рабочей постраничной навигацией.
13
Рис. 5.7 Страница местоположений с рабочей постраничной навигацией.
Рис 5.8 Пример фильтрации и сортировки.
14
Отчет «Таблица поверок», отображает оборудование, которое нужно отправить на
поверку. Входные данные период, за который искать поверенное оборудование.
Рис. 5.9 Отчет «Таблица поверок» экспортируемый в PDF.
Этикетки формируются из 3 таблиц: оборудование, тип оборудования, журнал
поверок.
Рис. 5.10 Отчет «Этикетка» экспортируемый в PDF.
15
Рис 5.11 Отчет «ПТС» экспортируемый в PDF.
16
6 КОД ПРОГРАММЫ
Класс для подключения к базе данных
public class ApplicationDbContext : IdentityDbContext<Domain.Entities.User.ApplicationUser, Role, int, UserClaim,
UserRole, UserLogin, RoleClaim, UserToken>
{
private readonly IDateTimeService _dateTime;
private readonly IAuthenticatedUserService _authenticatedUser;
private readonly ILoggerFactory _loggerFactory;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IDateTimeService dateTime,
IAuthenticatedUserService authenticatedUser, ILoggerFactory loggerFactory) : base(options)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
_dateTime = dateTime;
_authenticatedUser = authenticatedUser;
_loggerFactory = loggerFactory;
}
// Базовые сущности.
public DbSet<BusinessUnit> BusinessUnits { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Location> Locations { get; set; }
// Оборудование.
public DbSet<Equipment> Equipment { get; set; }
public DbSet<EquipmentVO> EquipmentVOs { get; set; }
public DbSet<EquipmentIO> EquipmentIOs { get; set; }
public DbSet<EquipmentCI> EquipmentCIs { get; set; }
public DbSet<Type> Types { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }
public DbSet<Tags> Tags { get; set; }
public DbSet<Instruction> Instructions { get; set; }
public DbSet<Verification> Verifications { get; set; }
public DbSet<VerificationStatus> VerificationStatus { get; set; }
public DbSet<Check> Checks { get; set; }
public DbSet<DocumentKind> DocumentKinds { get; set; }
public DbSet<Moving> Movings { get; set; }
// Файловое хранилище.
public DbSet<File> Files { get; set; }
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
foreach (var entry in ChangeTracker.Entries<AuditableBaseEntity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.Created = _dateTime.NowUtc;
entry.Entity.CreatedBy = _authenticatedUser.UserId;
break;
case EntityState.Modified:
entry.Entity.LastModified = _dateTime.NowUtc;
entry.Entity.LastModifiedBy = _authenticatedUser.UserId;
break;
}
}
return base.SaveChangesAsync(cancellationToken);
}
protected override void OnModelCreating(ModelBuilder builder)
{
17
//All Decimals will have 18,6 Range
foreach (var property in builder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?)))
{
property.SetColumnType("decimal(18,6)");
}
base.OnModelCreating(builder);
builder.Entity<Domain.Entities.User.ApplicationUser>(entity =>
{
entity.ToTable("User");
});
builder.Entity<Role>(entity =>
{
entity.ToTable("Role");
});
builder.Entity<UserRole>(entity =>
{
entity.ToTable("UserRoles");
});
builder.Entity<UserClaim>(entity =>
{
entity.ToTable("UserClaims");
});
builder.Entity<UserLogin>(entity =>
{
entity.ToTable("UserLogins");
});
builder.Entity<RoleClaim>(entity =>
{
entity.ToTable("RoleClaims");
});
builder.Entity<UserToken>(entity =>
{
entity.ToTable("UserTokens");
});
builder.Entity<Check>(entity =>
{
entity.Property(p => p.CurrentCheck).HasDefaultValue(null);
entity.Property(p => p.NextCheck).HasDefaultValue(null);
});
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(_loggerFactory);
}
}
Интерфейсы слоя доступа к данным
Базовый интерфейс репозитория.
18
public interface IGenericRepositoryAsync<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task<IReadOnlyList<T>> GetAllAsync();
Task<IReadOnlyList<T>> GetPagedReponseAsync(RequestParameter filter);
Task<T> AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
Task<int> CountAsync(RequestParameter filter);
}
Сущности слоя доступа к данным
Оборудование
public class Equipment : AuditableBaseEntity
{
public string Name { get; set; }
public string Number { get; set; }
//public string id_function_of_use { get; set; }
//public string id_object_study { get; set; }
public string PurposeOfUse { get; set; }
public string Model { get; set; }
public string SerialNumber { get; set; }
[Column(TypeName="Date")]
public DateTime? DateCreate { get; set; }
[Column(TypeName="Date")]
public DateTime? DateCommissioning { get; set; }
public string InventoryNumber { get; set; }
public string Description { get; set; }
public int? ManufacturerId { get; set; }
public Manufacturer Manufacturer { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
public int? LocationId { get; set; }
public Location Location { get; set; }
public int TypeId { get; set; }
public Type Type { get; set; }
public int? TagId { get; set; }
public Tags Tag { get; set; }
public virtual IEnumerable<Check> Checks { get; set; }
public virtual IEnumerable<Moving> Movings { get; set; }
}
Инструкции
public class Instruction : AuditableBaseEntity
{
public string Name { get; set; }
public string Number { get; set; }
public int FileId { get; set; }
public File File { get; set; }
}
Тип оборудования
public class Type : BaseEntity
{
public string Name { get; set; }
}
Журнал поверок
public class Check : BaseEntity
{
19
public string NumberDocument { get; set; }
public DateTime? CurrentCheck { get; set; }
public DateTime? NextCheck { get; set; }
public int? FileId { get; set; }
public File File { get; set; }
public int? DocumentKindId { get; set; }
public DocumentKind DocumentKind { get; set; }
public int EquipmentId { get; set; }
public Equipment Equipment { get; set; }
}
Поверка
public class Verification : BaseEntity
{
public int EquipmentId { get; set; }
public Equipment Equipment { get; set; }
public int StatusId { get; set; }
public VerificationStatus Status { get; set; }
}
Статусы поверок
public class VerificationStatus : BaseEntity
{
public string Name { get; set; }
}
Производитель
public class Manufacturer : BaseEntity
{
public string Name { get; set; }
public string Country { get; set; }
public string City { get; set; }
}
Вид документа
public class DocumentKind : BaseEntity
{
public string Name { get; set; }
}
Перемещения оборудования
public class Moving : BaseEntity
{
public int EquipmentId { get; set; }
public int CurrentDepartmentId { get; set; }
[ForeignKey("CurrentDepartmentId")]
public Department CurrentDepartment { get; set; }
public int NextDepartmentId { get; set; }
[ForeignKey("NextDepartmentId")]
public Department NextDepartment { get; set; }
public int? CurrentLocationId { get; set; }
public Location CurrentLocation { get; set; }
public int? NextLocationId { get; set; }
public Location NextLocation { get; set; }
public DateTime? MovingDate { get; set; }
}
20
Подразделение
public class Department : BaseEntity
{
public string Name { get; set; }
public string Number { get; set; }
public BusinessUnit BusinessUnit { get; set; }
}
Классы реализующие интерфейсы
Базовый дженерик репозиторий
public class GenericRepositoryAsync<T> : IGenericRepositoryAsync<T> where T : class
{
protected readonly ApplicationDbContext _dbContext;
public GenericRepositoryAsync(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public virtual async Task<T> GetByIdAsync(int id)
{
return await _dbContext.Set<T>().FindAsync(id);
}
public virtual async Task<IReadOnlyList<T>> GetPagedReponseAsync(RequestParameter filter)
{
return await _dbContext
.Set<T>()
.Sort(filter.SortBy)
.Skip((filter.PageNumber - 1) * filter.PageSize)
.Take(filter.PageSize)
.AsNoTracking()
.ToListAsync();
}
public async Task<T> AddAsync(T entity)
{
await _dbContext.Set<T>().AddAsync(entity);
try
{
await _dbContext.SaveChangesAsync();
}
catch (Exception ex) { }
return entity;
}
public async Task UpdateAsync(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}
public async Task DeleteAsync(T entity)
{
_dbContext.Set<T>().Remove(entity);
await _dbContext.SaveChangesAsync();
}
public virtual async Task<IReadOnlyList<T>> GetAllAsync()
{
return await _dbContext
21
.Set<T>()
.ToListAsync();
}
public virtual async Task<int> CountAsync(RequestParameter filter)
{
return await _dbContext.Set<T>().CountAsync();
}
}
В качестве примера класса реализующего репозиторий сущности
public class ManufacturerRepository : GenericRepositoryAsync<Manufacturer>, IManufacturerRepository
{
private readonly DbSet<Manufacturer> _manufacturer;
public ManufacturerRepository(ApplicationDbContext dbContext) : base(dbContext)
{
_manufacturer = dbContext.Set<Manufacturer>();
}
public async Task<int> CountAsync(GetAllManufacturerParameter filter)
{
return await _manufacturer.Filter(filter).CountAsync();
}
public async Task<IReadOnlyList<Manufacturer>> GetPagedReponseAsync(GetAllManufacturerParameter filter)
{
var equipments = await _manufacturer
.Filter(filter)
.Sort(filter.SortBy)
.Skip((filter.PageNumber - 1) * filter.PageSize)
.Take(filter.PageSize)
.AsNoTracking()
.ToListAsync();
return equipments;
}
}
Сущности и классы реализующие интерфейсы слоя бизнес уровня
Пример класса получения списка поверок.
Класс модели DTO
public class GetAllVerificationQuery : IRequest<PagedResponse<IEnumerable<GetAllVerificationViewModel>>>
{
public int PageNumber { get; set; }
public int PageSize { get; set; }
public string SortBy { get; set; }
public string EquipmentName { get; set; }
public string EquipmentModel { get; set; }
public string EquipmentSerialNumber { get; set; }
public int StatusId { get; set; }
public int DepartmentId { get; set; }
}
Обработка DTO модели и обращение к репозиторию
22
public class GetAllEquipmentQueryHandler : IRequestHandler<GetAllVerificationQuery,
PagedResponse<IEnumerable<GetAllVerificationViewModel>>>
{
private readonly IVerificationRepositoryAsync _equipmentRepositoryAsync;
private readonly IMapper _mapper;
public GetAllEquipmentQueryHandler(IVerificationRepositoryAsync equipmentRepository, IMapper mapper)
{
_equipmentRepositoryAsync = equipmentRepository;
_mapper = mapper;
}
public async Task<PagedResponse<IEnumerable<GetAllVerificationViewModel>>>
Handle(GetAllVerificationQuery request, CancellationToken cancellationToken)
{
var validFilter = _mapper.Map<GetAllVerificationParameter>(request);
var equipment = await _equipmentRepositoryAsync.GetPagedReponseAsync(validFilter);
var totalRecords = await _equipmentRepositoryAsync.CountAsync(validFilter);
var totalPages = ((double)totalRecords / (double)validFilter.PageSize);
var equipmentViewModel = _mapper.Map<IEnumerable<GetAllVerificationViewModel>>(equipment);
PagedResponse<IEnumerable<GetAllVerificationViewModel>> response = new
PagedResponse<IEnumerable<GetAllVerificationViewModel>>(equipmentViewModel, validFilter.PageNumber,
validFilter.PageSize);
response.TotalPages = Convert.ToInt32(Math.Ceiling(totalPages));
response.TotalRecords = totalRecords;
return response;
}
}
Добавление поверки
public class CreateVerificationCommand : IRequest<Response<bool>>
{
public IList<EqVal> Equipments { get; set; }
}
public class CreateVerificationCommandHandler : IRequestHandler<CreateVerificationCommand, Response<bool>>
{
private readonly IVerificationRepositoryAsync _genericRepositoryAsync;
private readonly IMapper _mapper;
public CreateVerificationCommandHandler(IVerificationRepositoryAsync equipmentRepository, IMapper mapper)
{
_genericRepositoryAsync = equipmentRepository;
_mapper = mapper;
}
public async Task<Response<bool>> Handle(CreateVerificationCommand request, CancellationToken
cancellationToken)
{
foreach (var eq in request.Equipments)
{
var verificationDto = _mapper.Map<DTOs.Equipment.Verification.VerificationDto>(eq);
var verificationBase = _mapper.Map<Domain.Entities.Equipment.Verification.Verification>(verificationDto);
verificationBase.StatusId = 1;
await _genericRepositoryAsync.AddAsync(verificationBase);
}
return new Response<bool>(true);
23
}
}
Удаление поверки
public class DeleteVerificationCommand : IRequest<Response<bool>>
{
public IList<EqVal> Equipments { get; set; }
}
public class DeleteVerificationCommandHandler : IRequestHandler<DeleteVerificationCommand, Response<bool>>
{
private readonly IVerificationRepositoryAsync _equipmentRepositoryAsync;
public DeleteVerificationCommandHandler(IVerificationRepositoryAsync equipmentRepository)
{
_equipmentRepositoryAsync = equipmentRepository;
}
public async Task<Response<bool>> Handle(DeleteVerificationCommand command, CancellationToken
cancellationToken)
{
foreach (var eq in command.Equipments)
{
var verification = await _equipmentRepositoryAsync.GetByIdAsync(eq.EquipmentId);
if (verification == null)
throw new ApiException($"Поверка с ИД \"{eq.EquipmentId}\" не найдена.");
await _equipmentRepositoryAsync.DeleteAsync(verification);
}
return new Response<bool>(true);
}
}
Обновление поверки
public class ReturnVerificationCommand : IRequest<Response<bool>>
{
public IList<Ver> Verifications { get; set; }
}
public class ReturnEquipmentCommandHandler : IRequestHandler<ReturnVerificationCommand, Response<bool>>
{
private readonly IVerificationRepositoryAsync _equipmentRepositoryAsync;
private readonly IMapper _mapper;
public ReturnEquipmentCommandHandler(IVerificationRepositoryAsync equipmentRepository, IMapper mapper)
{
_equipmentRepositoryAsync = equipmentRepository;
_mapper = mapper;
}
public async Task<Response<bool>> Handle(ReturnVerificationCommand command, CancellationToken
cancellationToken)
{
foreach (var eq in command.Verifications)
{
var verification = await _equipmentRepositoryAsync.GetByIdAsync(eq.VerificationId);
if (verification == null)
throw new ApiException($"Поверка с ИД \"{eq.VerificationId}\" не найдена.");
verification.StatusId = 4;
await _equipmentRepositoryAsync.UpdateAsync(verification);
}
24
return new Response<bool>(true);
}
Классы компонентов веб приложения (формы)
Код компонентов веб приложения отображен на одном типе сущности,
остальные созданы по аналогии.
Форма списка оборудования
<template>
<v-row>
<v-col cols="12">
<v-data-table
calculate-widths
dense
v-model="selected"
:show-select="true"
:headers="tableColumn"
:items="gridData"
:items-per-page="50"
:loading="load"
:options.sync="options"
:server-items-length="totalRecord"
:footer-props="{
showFirstLastPage: true,
firstIcon: 'mdi-arrow-collapse-left',
lastIcon: 'mdi-arrow-collapse-right',
prevIcon: 'mdi-minus',
nextIcon: 'mdi-plus',
itemsPerPageOptions: [10, 50, 100],
itemsPerPageText: 'Количество записей',
}">
<template #top>
<v-toolbar color="white" flat>
<v-toolbar-title style="margin-right: 10px">Оборудование</v-toolbar-title>
<v-divider inset vertical></v-divider>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" icon @click="getData()"><v-icon>mdi-refresh</vicon></v-btn>
</template>
<span>Обновить</span>
</v-tooltip>
<v-divider inset vertical></v-divider>
<v-menu :offset-y=true>
<template v-slot:activator="{ on: menu, attrs }">
<v-tooltip bottom>
<template v-slot:activator="{ on: tooltip }">
25
<v-btn v-bind="attrs" v-on="{ ...tooltip, ...menu }" icon vcan:add="'equipment'"><v-icon>mdi-plus</v-icon></v-btn>
</template>
<span>Создать оборудование</span>
</v-tooltip>
</template>
<v-list>
<v-list-item @click="showCreateEquipmentVO = true">
<v-list-item-title>Вспомогательное</v-list-item-title>
</v-list-item>
<v-list-item @click="showCreateEquipmentIO = true">
<v-list-item-title>Испытательное</v-list-item-title>
</v-list-item>
<v-list-item @click="showCreateEquipmentCI = true">
<v-list-item-title>Средство измерения</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" icon @click="sentToCheck()" vcan:add="'verification'"><v-icon>mdi-alert-circle-check</v-icon></v-btn>
</template>
<span>Отправить на поверку</span>
</v-tooltip>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" icon @click="setMoving()" v-can:add="'moving'"><vicon>mdi-map-marker</v-icon></v-btn>
</template>
<span>Сменить местоположение</span>
</v-tooltip>
<v-divider inset vertical></v-divider>
<v-spacer></v-spacer>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" icon @click="draw()"><v-icon>mdi-filter</vicon></v-btn>
</template>
<span>Фильтрация</span>
</v-tooltip>
</v-toolbar>
</template>
<template v-slot:item.tag="{ item }">
{{ item.tag }}
</template>
<template v-slot:item.actions="{ item }">
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
26
<v-btn v-bind="attrs" v-on="on" icon @click="openDetail(item.id)"><v-icon>mdi-cardtext</v-icon></v-btn>
</template>
<span>Карточка оборудования</span>
</v-tooltip>
</template>
</v-data-table>
<v-navigation-drawer v-model="drawer" absolute right temporary width="512">
<v-card flat>
<v-card-title>Фильтрация</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-form>
<v-row>
<v-col cols="9">
<tags @select-id="getTagsId" :show-view="true"
:existedId="filterBy.tagId"></tags>
<type @select-id="getTypeId" :show-view="true"
:existedId="filterBy.typeId"></type>
<department @select-id="getDepartmentId" :show-view="true"
:existedId="filterBy.departmentId"></department>
<v-text-field dense label="Наименование" outlined vmodel="filterBy.name"></v-text-field>
<v-text-field dense label="Номер" outlined v-model="filterBy.number"></vtext-field>
<v-text-field dense label="Модель" outlined v-model="filterBy.model"></vtext-field>
<v-text-field dense label="Серийный номер" outlined vmodel="filterBy.serialNumber"></v-text-field>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="warning" @click="clearFilter()">Сброс</v-btn>
<v-btn color="success" @click="submitFilter()">Применить</v-btn>
</v-card-actions>
</v-card>
</v-navigation-drawer>
<create-equipment-vo :visible="showCreateEquipmentVO" @close="closeDialogVo"></create-equipment-vo>
<create-equipment-io :visible="showCreateEquipmentIO" @close="closeDialogIo"></create-equipment-io>
<create-equipment-ci :visible="showCreateEquipmentCI" @close="closeDialogCi"></create-equipment-ci>
<FormuiMovingCreate :visible="showCreateMoving" @close="closeDialogCreateMoving"
:equipment="FirstSelectedItem"></FormuiMovingCreate>
</v-col>
</v-row>
<script lang="ts">
import { Component, Vue, Watch } from 'nuxt-property-decorator';
import CreateEquipmentVo from '../../../components/modal/equipment.vue';
27
import CreateEquipmentIo from '../../../components/modal/equipmentio.vue';
import CreateEquipmentCi from '../../../components/modal/equipmentci.vue';
import Type from '../../../components/formui/type/view.vue'
import Tags from '../../../components/formui/tags/view.vue'
import Department from '../../../components/formui/department/view.vue'
@Component({ components: { CreateEquipmentVo, CreateEquipmentIo, CreateEquipmentCi, Type, Department, Tags
} })
export default class EquipmentView extends Vue {
tableColumn: Array<object> = [
{ text: 'Номер', align: 'start', sortable: true, value: 'number'},
{ text: 'Отдел', align: 'start', sortable: true, value: 'department.name'},
{ text: 'Тип', align: 'start', sortable: true, value: 'type.name'},
{ text: 'Наименование', align: 'start', sortable: true, value: 'name' },
{ text: 'Модель', align: 'start', sortable: true, value: 'model'},
{ text: 'С/Н', align: 'end', sortable: true, value: 'serialNumber'},
{ text: 'Статус', align: 'center', sortable: true, value: 'tag.name'},
{ text: '', align: 'center', sortable: false, value: 'actions'}
]
gridData: Array<object> = []
selected: Array<object> = []
options: Object = {}
filterBy: Object = {}
totalRecord: number = 0
load: boolean = false
drawer: boolean = false
showCreateEquipmentVO: boolean = false
showCreateEquipmentIO: boolean = false
showCreateEquipmentCI: boolean = false
showCreateMoving: boolean = false
getTagsId(value: number)
{
this.filterBy.tagId = value
}
getTypeId (value: number)
{
this.filterBy.typeId = value
}
getDepartmentId (value: number)
{
this.filterBy.departmentId = value
}
closeDialogCreateMoving(value: boolean) {
this.showCreateMoving = false;
}
28
closeDialogVo(value: boolean){
if (value == true)
this.getData();
this.showCreateEquipmentVO = false;
}
closeDialogIo(value: boolean){
if (value == true)
this.getData();
this.showCreateEquipmentIO = false;
}
closeDialogCi(value: boolean){
if (value == true)
this.getData();
this.showCreateEquipmentCI = false;
}
formatDate(date: any){
return date === null ? null : new Date(date).toLocaleString().split(',')[0];
}
draw() {
this.drawer = !this.drawer;
}
openDetail(value: number){
this.$router.push({name: 'equipment-view-id', params: { id: value }});
}
created() {
this.getData();
}
async getData()
{
this.load = true;
let data = await this.$equipment.view(this.options, this.filterBy);
this.gridData = data['data']
this.totalRecord = data['totalRecords']
}
submitFilter(){
this.getData()
}
clearFilter(){
29
this.filterBy = {}
this.getData()
}
@Watch("options", { deep: true })
watchToOptions(newVal: Object){
this.getData()
}
@Watch("gridData")
watchToGridData(newVal: Array<object>){
this.load = false
}
async sentToCheck () {
if (this.selected == null || this.selected.length <= 0)
{
this.$toast.info("Не выбрано оборудование для отправки на поверку.");
return;
}
let obj = {
equipments: []
}
this.selected.forEach(el => {
obj.equipments.push({ equipmentId: el.id })
})
let data = await this.$verifications.sentToCheck(obj);
if (data)
this.selected = []
}
setMoving () {
if (this.selected == null || this.selected.length <= 0)
{
this.$toast.info("Не выбрано оборудование для смены местоположения.");
return;
}
if (this.selected.length > 1)
{
this.$toast.info("Для смены местоположения необходимо выбрать одно оборудование.");
return;
}
this.showCreateMoving = true;
}
30
get FirstSelectedItem() {
if (this.selected != null || this.selected.length > 0)
return this.selected[0]
return null;
}
}
</script>
Добавление оборудования
<template>
<v-dialog dense v-model="getVisible" @input="closeDialog()">
<v-form>
<v-card>
<v-card-title>Вспомогательное оборудование</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-form>
<v-row>
<v-col cols="12">
<v-textarea :rows="2" :height="60" dense label="Наименование" outlined vmodel="equipment.name"></v-textarea>
</v-col>
<v-col cols="12">
<FormuiManufacturerView @select-id="getManufacturerId" :showview="true"></FormuiManufacturerView>
</v-col>
<v-col cols="3">
<v-text-field clearable dense label="Модель" outlined vmodel="equipment.model"></v-text-field>
</v-col>
<v-col cols="3">
<v-text-field clearable dense label="Серийный номер" outlined vmodel="equipment.serialNumber"></v-text-field>
</v-col>
<v-col cols="3">
<v-text-field type="date" clearable dense label="Дата изготовления"
outlined v-model="equipment.dateCreate"></v-text-field>
</v-col>
<v-col cols="3">
<v-text-field type="date" clearable dense label="Дата ввода в
эксплуатацию" outlined v-model="equipment.dateCommissioning"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="6">
<FormuiDepartmentView @select-id="getDepartmentId" :showview="true"></FormuiDepartmentView>
31
</v-col>
<v-col cols="6">
<FormuiLocationView @select-id="getlocationId" :showview="true"></FormuiLocationView>
</v-col>
</v-row>
<v-row>
<v-col cols="3">
<v-text-field clearable dense label="Инвентарный номер" outlined vmodel="equipment.inventoryNumber"></v-text-field>
</v-col>
<v-col cols="3">
<v-text-field clearable dense label="Регистрационный номер" outlined vmodel="equipment.number"></v-text-field>
</v-col>
<!-- <v-col cols="6">
<instruction @select="getInstructionId" :show-create="true" :showview="false"></instruction>
</v-col> -->
</v-row>
<v-row>
<v-col cols="12">
<v-textarea :rows="2" :height="60" dense label="Характеристики" outlined
v-model="equipment.characteristics"></v-textarea>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-chip>±</v-chip>
<v-chip>°</v-chip>
<v-spacer></v-spacer>
<v-btn color="success" @click="submit()" :loading="loading">ОК</v-btn>
<v-btn color="error" v-on:click="closeDialog()">Отмена</v-btn>
</v-card-actions>
</v-card>
</v-form>
</v-dialog>
</template>
<script lang="ts">
import { Component, Prop, Watch, Emit, Vue } from "nuxt-property-decorator"
@Component
export default class DialogCreateEquipmentVo extends Vue
{
loading: boolean = false
public equipment = {} as IEquipmentVO
32
@Prop({default: false}) visible!: boolean
closeDialog(value: any){
this.$emit('close', value);
this.loading = false;
this.equipment = {} as IEquipmentVO
};
getManufacturerId (value: number)
{
this.equipment.manufacturerId = value;
}
getDepartmentId (value: number)
{
this.equipment.departmentId = value;
}
getlocationId (value: number)
{
this.equipment.locationId = value;
}
getInstructionId (value: number)
{
this.equipment.instructionId = value;
}
submit(){
try
{
this.loading = true;
this.equipment.typeId = 1;
this.$axios.post('/api/v1/equipment/vo', this.equipment).then(response => {
this.loading = false;
this.$toast.success("Вспомогательное оборудование добавлено.");
this.closeDialog(true);
}).catch(error => {
this.closeDialog(false);
this.$toast.error("Ошибка во время добавление вспомогательного оборудования.");
this.loading = false;
})
}
catch (e)
{
this.closeDialog(false);
this.$toast.error("Ошибка во время добавление вспомогательного оборудования.");
this.loading = false
}
33
};
get getVisible() {
return this.visible;
};
set getVisible(value: any) {
this.closeDialog(value);
};
}
</script>
Редактирование оборудования
<template>
<v-row>
<v-col cols="12">
<v-card>
<v-card-text>
<v-tabs>
<v-tab>Карточка</v-tab>
<v-tab>Характеристики</v-tab>
<v-tab>Поверки</v-tab>
<v-tab>Перемещения</v-tab>
<v-tab>Техническое обслуживание</v-tab>
<v-tab-item>
<v-card-text>
<v-form v-if="Object.keys(gridData).length > 0">
<v-row>
<v-col cols="12" md="6">
<v-text-field clearable dense label="Наименование" outlined vmodel="gridData.name"></v-text-field>
</v-col>
<v-col cols="12" md="6">
<manufacturer @select-id="getManufacturerId" :showview="this.$permissions.can('edit', 'equipment')" :existed-id="gridData.manufacturerId"></manufacturer>
</v-col>
<v-col cols="12" md="3">
<v-text-field clearable dense label="Инвентарный номер"
outlined v-model="gridData.inventoryNumber"></v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field clearable dense label="Серийный номер" outlined
v-model="gridData.serialNumber"></v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field clearable dense label="Модель" outlined vmodel="gridData.model"></v-text-field>
</v-col>
34
<v-col cols="12" md="3">
<v-text-field clearable type="date" dense label="Дата
изготовления" outlined v-model="gridData.dateCreate"></v-text-field>
</v-col>
<v-col cols="12" md="4">
<v-text-field clearable dense label="Номер" outlined vmodel="gridData.number"></v-text-field>
</v-col>
<v-col cols="12" md="4">
<type @select-id="getTypeId" :show-view="false" :existedid="gridData.typeId"></type>
</v-col>
<v-col cols="12" md="4">
<v-text-field clearable type="date" dense label="Дата ввода в
эксплуатацию" outlined v-model="gridData.dateCommissioning"></v-text-field>
</v-col>
<v-col cols="12">
<department @select-id="getDepartmentId" :show-view="false"
:existed-id="gridData.departmentId"></department>
</v-col>
<v-col cols="6">
<tags @select-id="getTagsId" :showview="this.$permissions.can('edit', 'equipment')" :existed-id="gridData.tagId"></tags>
</v-col>
<v-col cols="6">
<FormuiLocationView @select-id="getLocationId" :showview="false" :existed-id="gridData.locationId"></FormuiLocationView>
</v-col>
<v-col cols="12" md="12">
<v-textarea :rows="2" :height="100" dense label="Примечание"
outlined v-model="gridData.description"></v-textarea>
</v-col>
</v-row>
</v-form>
</v-card-text>
</v-tab-item>
<v-tab-item>
<v-card-text>
<v-form>
<v-row v-if="Object.keys(gridData).length > 0">
<v-col cols="12" md="12" v-if="gridData.typeId == 3">
<v-text-field clearable dense label="ФИФ" outlined vmodel="gridData.fifNumber"></v-text-field>
</v-col>
<v-col cols="12" md="6" v-if="gridData.typeId == 2 ||
gridData.typeId == 3">
<v-text-field clearable dense label="Точность" outlined vmodel="gridData.accuracy"></v-text-field>
</v-col>
<v-col cols="12" md="6" v-if="gridData.typeId == 3">
35
<v-text-field clearable dense label="Класс точности" outlined
v-model="gridData.classAccuracy"></v-text-field>
</v-col>
<v-col cols="12" md="6" v-if="gridData.typeId == 3">
<v-text-field clearable dense label="Диапазон измерений"
outlined v-model="gridData.measuringRange"></v-text-field>
</v-col>
<v-col cols="12" md="6" v-if="gridData.typeId == 2">
<v-text-field clearable dense label="Диапазон работы" outlined
v-model="gridData.measuringWork"></v-text-field>
</v-col>
<v-col cols="12" md="12" v-if="gridData.typeId == 1">
<v-textarea :rows="2" :height="100" dense
label="Дополнительные характеристики" outlined v-model="gridData.characteristics"></v-textarea>
</v-col>
</v-row>
</v-form>
</v-card-text>
</v-tab-item>
<v-tab-item>
<v-card-text>
<FormuiCheckTableComp :showSelect="false" :showToolbar="false"
:tableColumn="checkTableHeaders" :equipmentId="$route.params.id"></FormuiCheckTableComp>
</v-card-text>
</v-tab-item>
<v-tab-item>
<v-card-text>
<FormuiMovingTableComp :singleSelect="false" :showSelect="false"
:showToolbar="false" :equipmentId="$route.params.id" :showFilterPanel="true"></FormuiMovingTableComp>
</v-card-text>
</v-tab-item>
<v-tab-item>
<v-card-text>
ТО
</v-card-text>
</v-tab-item>
</v-tabs>
</v-card-text>
<v-card-actions>
<v-chip>±</v-chip>
<v-chip>°</v-chip>
<v-spacer></v-spacer>
<v-btn color="success" :disabled="!changed" @click="update()" v-can:edit="equipment"
:loading="updateLoad">Сохранить</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</template>
36
<script lang="ts">
import { Component, Vue, Watch } from "nuxt-property-decorator"
import Department from '../../../components/formui/department/view.vue'
import Manufacturer from '../../../components/formui/manufacturer/view.vue'
import Type from '../../../components/formui/type/view.vue'
import Tags from '../../../components/formui/tags/view.vue'
@Component({ components: { Department, Manufacturer, Type, Tags } })
export default class EquipmentDetails extends Vue
{
checkTableHeaders: Array<object> = [
{ text: 'Пройденная', align: 'start', sortable: false, value: 'currentCheck', visible: true},
{ text: 'Предстоящая', align: 'start', sortable: false, value: 'nextCheck', visible: true},
{ text: 'Вид документа', align: 'start', sortable: false, value: 'documentKind.name', visible:
true},
{ text: '№ документа', align: 'start', sortable: false, value: 'numberDocument', visible: true},
{ text: 'Экспорт', align: 'start', sortable: false, value: 'fileId', visible: true}
]
gridData: Object = {}
changed: boolean = false
updateLoad: boolean = false
formatDate(date: any) {
return date === null ? null : date.split("T")[0];
}
async getData (){
try
{
await this.$axios.get(`api/v1/equipment/detail/${this.$route.params.id}`).then(response => {
this.gridData = response.data["data"]
}
);
this.changed = false
}
catch (e)
{
this.$toast.error("Ошибка во время загрузки оборудования.");
}
}
formatDateLocalDate(date: any){
return date === null ? null : new Date(date).toLocaleString().split(',')[0];
}
getDepartmentId (value: number)
{
this.gridData.departmentId = value;
}
37
getManufacturerId (value: number)
{
this.gridData.manufacturerId = value;
}
getTagsId(value: number) {
this.gridData.tagId = value;
}
getTypeId (value: number)
{
this.gridData.typeId = value;
}
getLocationId (value: number)
{
this.gridData.locationId = value;
}
@Watch("gridData", { deep: true })
equipment(newVal: object, oldVal: object) {
newVal.dateCreate = this.formatDate(newVal.dateCreate)
newVal.dateCommissioning = this.formatDate(newVal.dateCommissioning)
this.changed = true
}
created (){
this.getData()
}
activated () {
this.getData()
}
async update(){
if (!this.changed)
return
try
{
this.updateLoad = true
await this.$axios.post(`api/v1/equipment/update/${this.$route.params.id}`,
this.gridData).then(response => {
this.getData()
this.updateLoad = false
}
);
this.changed = false
this.$toast.success("Оборудование обновлено.");
38
}
catch (e)
{
this.updateLoad = false
this.$toast.error("Ошибка во время обновления оборудования.");
}
}
}
</script>
39
ЗАКЛЮЧЕНИЕ
В ходе выполнения курсовой работы удалось ближе познакомиться с
дисциплиной базы данных. Также удалось улучшить свои навыки в
программировании на языке высокого уровня С#, который предоставляет
широкие возможности по написанию различных программ и приложений.
Получен опыт в разработке веб приложения с ипользованием фреймворка
VueJS и NuxtJS, которые обладают простотой и доступностью. Изучена
сфера применения рассматриваемой дисциплины, выявление положительные
стороны, так и недостатки объектно-ориентированного подхода.
Работа выполнена полностью, для указанного варианта задания
написано веб приложения и серверная часть, реализованное через паттерны
проектирования.
40
СПИСОК ЛИТЕРАТУРЫ
1.
Полное руководство по языку программирования С# 9.0 и платформе
.NET 5 [Электронный ресурс]. Дата обновления 12.11.2020. URL:
https://metanit.com/sharp/tutorial/ (дата обращения: 02.05.2021).
2.
Высокопроизводительный код на платформе .NET (pdf+epub) – Бен
Уотсон.: ООО «Питер», 2019. – 414 стр.
3.
Паттерны проектирования для C# и платформы .NET Core - Джеффри
Чилберто.: ООО "Питер", 2021. – 343 стр.
4.
Каталог паттернов проектирования [Электронный ресурс]. URL:
https://refactoring.guru/ru/design-patterns/catalog
(дата
обращения:
01.05.2021).
5.
Документация по C#. Начало работы, руководства, справочные
материалы.
|
Microsoft
Docs
[Электронный
https://docs.microsoft.com/ru-ru/dotnet/csharp/
01.05.2021).
41
(дата
ресурс].
URL:
обращения:
Download