Initial commit
This commit is contained in:
12
users-manage/src/components/1_atoms/AppPageHeader.vue
Normal file
12
users-manage/src/components/1_atoms/AppPageHeader.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppPageHeader',
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a
|
||||
href="/#"
|
||||
class="text-white block w-fit bg-slate-700 hover:bg-blue-600 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm text-center px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
><i class="ri-reply-line" /> На главную</a>
|
||||
</template>
|
||||
33
users-manage/src/components/1_atoms/NewSiteButton.vue
Normal file
33
users-manage/src/components/1_atoms/NewSiteButton.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<script>
|
||||
import {mapActions} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'NewSiteButton',
|
||||
props: {
|
||||
name: {
|
||||
default: "",
|
||||
type: String
|
||||
},
|
||||
port: {
|
||||
default: "",
|
||||
type: String
|
||||
},
|
||||
site: {
|
||||
default: () => ({}),
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('services', ['addNewSiteLayout'])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="cursor-pointer flex items-center justify-center w-full p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 text-2xl font-w-700 text-slate-700"
|
||||
@click="addNewSiteLayout"
|
||||
>
|
||||
Добавить сайт
|
||||
</div>
|
||||
</template>
|
||||
20
users-manage/src/components/1_atoms/VButton.vue
Normal file
20
users-manage/src/components/1_atoms/VButton.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'VButton',
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: 'purple'
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
:class="`rounded-md bg-${color}-700 hover:bg-fuchsia-700 transition-all cursor-pointer text-white p-2`"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
102
users-manage/src/components/1_atoms/VDoubleSwitch.vue
Normal file
102
users-manage/src/components/1_atoms/VDoubleSwitch.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'VDoubleSwitch',
|
||||
components: {
|
||||
// Button,
|
||||
},
|
||||
props: {
|
||||
machine: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
firstTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
secondTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
firstColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
secondColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isCheck: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
position: {
|
||||
type: String,
|
||||
default: 'row'
|
||||
},
|
||||
labelClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
switchClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
emits: ['switched'],
|
||||
data() {
|
||||
return {
|
||||
isChecked: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
watch: {
|
||||
isCheck: function () {
|
||||
this.isChecked = !this.isChecked
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
|
||||
},
|
||||
methods: {
|
||||
buttonClass: function(value) {
|
||||
return `border border-slate-400 flex items-center justify-center cursor-pointer rounded transition-all text-xs text-center py-[5px] px-2 ${value}`
|
||||
},
|
||||
setChecked(e) {
|
||||
this.$emit('switched', e.target.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<label :class="`${position === 'row' ? 'flex-row' : 'flex-col'} ${labelClass} relative flex items-center cursor-pointer`">
|
||||
<input
|
||||
v-bind="$attrs"
|
||||
class="input sr-only peer/checkbox"
|
||||
type="checkbox"
|
||||
:checked="isCheck"
|
||||
@change="setChecked"
|
||||
>
|
||||
<span
|
||||
v-if="position === 'col'"
|
||||
class="block peer-checked/checkbox:hidden ml-0 text-xs font-medium text-gray-900 dark:text-gray-300"
|
||||
> {{ firstTitle }}</span>
|
||||
<span
|
||||
v-if="position === 'col'"
|
||||
class="hidden peer-checked/checkbox:block ml-0 text-xs font-medium text-gray-900 dark:text-gray-300"
|
||||
> {{ secondTitle }} </span>
|
||||
<div
|
||||
:class="`${switchClass} relative w-11 h-6 rounded-full peer peer-focus:ring-4 peer-focus:ring-${secondColor}-300 dark:peer-focus:ring-${secondColor}-800 dark:bg-${firstColor}-700 peer-checked/checkbox:after:translate-x-full peer-checked/checkbox:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-${firstColor}-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-${firstColor}-600`"
|
||||
:style="{background: !isCheck ? firstColor : secondColor}"
|
||||
/>
|
||||
<span
|
||||
v-if="position === 'row'"
|
||||
class="block peer-checked/checkbox:hidden ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
|
||||
> {{ firstTitle }}</span>
|
||||
<span
|
||||
v-if="position === 'row'"
|
||||
class="hidden peer-checked/checkbox:block ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
|
||||
> {{ secondTitle }} </span>
|
||||
</label>
|
||||
</template>
|
||||
59
users-manage/src/components/1_atoms/VInput.vue
Normal file
59
users-manage/src/components/1_atoms/VInput.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'VInput',
|
||||
props: {
|
||||
id: {
|
||||
default: 'input',
|
||||
type: String
|
||||
},
|
||||
type: {
|
||||
default: 'text',
|
||||
type: String
|
||||
},
|
||||
name: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
inputClass: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
placeholder: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
required: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
value: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
onChange: {
|
||||
default: () => {},
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
setValue: function (e) {
|
||||
this.onChange({key: this.name, value: e.target.value})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input
|
||||
:id="id"
|
||||
:type="type"
|
||||
:name="name"
|
||||
:class="`flex w-full rounded-lg border-zinc-300 py-1 px-2 text-zinc-900 focus:outline-none focus:ring-4 text-sm sm:leading-6 focus:border-zinc-400 focus:ring-zinc-800/5 ${inputClass}`"
|
||||
:placeholder="placeholder"
|
||||
:required="required"
|
||||
:value="value"
|
||||
:on:change="setValue"
|
||||
>
|
||||
</template>
|
||||
59
users-manage/src/components/1_atoms/VTextarea.vue
Normal file
59
users-manage/src/components/1_atoms/VTextarea.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'VTextarea',
|
||||
props: {
|
||||
id: {
|
||||
default: 'input',
|
||||
type: String
|
||||
},
|
||||
type: {
|
||||
default: 'text',
|
||||
type: String
|
||||
},
|
||||
name: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
textareaClass: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
placeholder: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
required: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
value: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
onChange: {
|
||||
default: () => {},
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
setValue: function (e) {
|
||||
this.onChange({key: this.name, value: e.target.value})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<textarea
|
||||
:id="id"
|
||||
:type="type"
|
||||
:name="name"
|
||||
:class="`border p-2.5 w-full text-sm bg-gray-50 rounded-lg border-gray-200 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 ${textareaClass}`"
|
||||
:placeholder="placeholder"
|
||||
:required="required"
|
||||
:value="value"
|
||||
:on:change="setValue"
|
||||
/>
|
||||
</template>
|
||||
198
users-manage/src/components/2_molecules/Tabulator/VTabulator.vue
Normal file
198
users-manage/src/components/2_molecules/Tabulator/VTabulator.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<script>
|
||||
|
||||
import {
|
||||
prepColumns,
|
||||
sorter,
|
||||
renderGroupHeader,
|
||||
groupByFunction,
|
||||
tablefy,
|
||||
pipe
|
||||
} from "./helper";
|
||||
|
||||
import {TabulatorFull as Tabulator} from 'tabulator-tables';
|
||||
|
||||
|
||||
import localization from "./localization";
|
||||
import { is, sort, mergeDeepRight } from "ramda";
|
||||
import Pagination from "./VTabulatorPagination.vue";
|
||||
|
||||
export default {
|
||||
name: 'TabulatorFull',
|
||||
components: {
|
||||
Pagination
|
||||
},
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
initialSort: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
groupBy: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
groupByTemplate: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
groupValues: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: "550px"
|
||||
},
|
||||
groupExtraValues: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
groupHeader: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
selectOption: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
filterData: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
withTablefy: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
refId: Math.random().toString(36).slice(4),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
data () {
|
||||
if (!is(Array, this.dataSource)) return []
|
||||
if (this.withTablefy) return tablefy(this.dataSource)
|
||||
return this.dataSource
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.makeTable()
|
||||
},
|
||||
updated() {
|
||||
this.makeTable()
|
||||
},
|
||||
methods: {
|
||||
makeTable () {
|
||||
console.log(Tabulator)
|
||||
this.table = new Tabulator(this.$refs[this.refId], this.getConfig())
|
||||
},
|
||||
getConfig () {
|
||||
let configurator = pipe(this.__defaultCfg());
|
||||
return configurator(
|
||||
(x) => mergeDeepRight(x, this.__getColsCfg()),
|
||||
(x) => mergeDeepRight(x, this.__getSortCfg()),
|
||||
(x) => mergeDeepRight(x, this.__getGroupCfg()),
|
||||
(x) => mergeDeepRight(x, this.__getGroupHeaderCfg()),
|
||||
// (x) => mergeAll(x, this.__getPaginationCgs())
|
||||
);
|
||||
},
|
||||
__defaultCfg () {
|
||||
return {
|
||||
locale: "ru",
|
||||
langs: localization(),
|
||||
height: this.height || "250px",
|
||||
responsiveLayout: "collapse",
|
||||
placeholder: "Нет записей",
|
||||
movableColumns: true,
|
||||
autoResize: false,
|
||||
data: this.data,
|
||||
groupStartOpen: false,
|
||||
|
||||
groupHeader: function (value, count) {
|
||||
return value + "<span>(" + count + " записей)</span>";
|
||||
},
|
||||
};
|
||||
},
|
||||
__getColsCfg() {
|
||||
let layout, columns = []
|
||||
if (this.autoColumns && is(Object, this.data[0])) {
|
||||
let first = this.data[0];
|
||||
let keys = Object.keys(first).map((key) => ({
|
||||
headerFilter: "input",
|
||||
headerFilterPlaceholder: key,
|
||||
title: key,
|
||||
field: key,
|
||||
download: false,
|
||||
print: false,
|
||||
vertAlign: "middle",
|
||||
sorter: "string",
|
||||
responsive: 0,
|
||||
minWidth: 80,
|
||||
maxWidth: 300,
|
||||
}));
|
||||
layout = "fitDataFill"
|
||||
columns = sort(sorter, keys)
|
||||
} else {
|
||||
layout = "fitDataStretch"
|
||||
columns = prepColumns(this.columns)
|
||||
}
|
||||
if (this.withTablefy) {
|
||||
columns = tablefy(columns)
|
||||
}
|
||||
return {columns, layout}
|
||||
},
|
||||
__getSortCfg() {
|
||||
if (this.initialSort) return { initialSort: this.initialSort };
|
||||
return {};
|
||||
},
|
||||
__getGroupCfg() {
|
||||
if (this.groupByTemplate)
|
||||
return { groupBy: groupByFunction(this.groupByTemplate) };
|
||||
if (this.groupBy) return { groupBy: this.groupBy };
|
||||
return {};
|
||||
},
|
||||
__getGroupHeaderCfg() {
|
||||
if (this.groupHeader)
|
||||
return {
|
||||
groupHeader: (value, count, data, group) =>
|
||||
renderGroupHeader({
|
||||
extra: this.groupExtraValues,
|
||||
groupHeader: this.groupHeader,
|
||||
value,
|
||||
count,
|
||||
data,
|
||||
group,
|
||||
}),
|
||||
};
|
||||
return {};
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mb-4">
|
||||
<div class="overflow-x-auto w-full">
|
||||
<div
|
||||
:ref="refId"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pagination">
|
||||
<Pagination :params="pagination" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,183 @@
|
||||
<!-- eslint-disable -->
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
|
||||
import { validation } from './helper'
|
||||
|
||||
export default {
|
||||
name: 'Pagination',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
params: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: null,
|
||||
initLocalStorage: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('main', ['machinesData', 'historyData', 'historyMachines']),
|
||||
},
|
||||
watch: {
|
||||
params: {
|
||||
handler(value, oldValue) {
|
||||
if(value) {
|
||||
const loader = document.querySelector(`#table_loder_${this.params.id}`)
|
||||
loader.style.display = 'none'
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
|
||||
},
|
||||
methods: {
|
||||
...mapMutations('main', ['setInitHistory', 'setHistoryData', 'setHistoryMachines']),
|
||||
selectOptions: (selectOption, paginationOptions) => {
|
||||
const options = selectOption ? selectOption : optionsDefault
|
||||
const currentOptions = options.filter((el) => {
|
||||
if(el <= Math.ceil(paginationOptions.total / 10) * 10) {
|
||||
return el
|
||||
}
|
||||
})
|
||||
if(paginationOptions.show_all) {
|
||||
currentOptions.push('Все')
|
||||
}
|
||||
return currentOptions
|
||||
},
|
||||
currentEntries: (pagination) => {
|
||||
const endPageDataIdx = pagination.page_size * pagination.page_number
|
||||
const isEndPageDataIdxMore = endPageDataIdx > pagination.total
|
||||
if(isEndPageDataIdxMore || pagination.page_size > pagination.total) {
|
||||
return (`${pagination.page_size * (pagination.page_number - 1) + 1} ... ${pagination.total}`)
|
||||
} else {
|
||||
return (`${pagination.page_size * (pagination.page_number - 1) + 1} ... ${pagination.page_size * pagination.page_number}`)
|
||||
}
|
||||
},
|
||||
setPagination: function ({pageNumber, pageSize}) {
|
||||
const loader = document.querySelector(`#table_loder_${this.params.id}`)
|
||||
loader.style.display = 'block'
|
||||
this.params.updatePagination({pageNumber: pageNumber, pageSize: pageSize})
|
||||
},
|
||||
updateSelectedValue: function (e) {
|
||||
const loader = document.querySelector(`#table_loder_${this.params.id}`)
|
||||
loader.style.display = 'block'
|
||||
this.setPagination({
|
||||
pageNumber: 1, // this.params.paginationOptions.page_number,
|
||||
pageSize: e.target.value
|
||||
})
|
||||
},
|
||||
updateInputValue: function (e) {
|
||||
const pageNumber = e.target.value.trim()
|
||||
const buttonPageInput = document.querySelector(`.page-button-${this.params.id}`)
|
||||
if(pageNumber) {
|
||||
this.inputValue = pageNumber
|
||||
buttonPageInput.removeAttribute('disabled')
|
||||
buttonPageInput.classList.remove('opacity-50')
|
||||
} else {
|
||||
buttonPageInput.setAttribute('disabled', 'disabled')
|
||||
buttonPageInput.classList.add('opacity-50')
|
||||
}
|
||||
},
|
||||
setPaginationButton: function (e) {
|
||||
if(this.inputValue) {
|
||||
const error = validation(this.inputValue, this.params.paginationOptions)
|
||||
if(Number(this.inputValue) === this.params.paginationOptions.page_number) {
|
||||
return false
|
||||
}
|
||||
if(!error) {
|
||||
e.target.removeAttribute('disabled')
|
||||
e.target.classList.remove('opacity-50')
|
||||
const loader = document.querySelector(`#table_loder_${this.params.id}`)
|
||||
loader.style.display = 'block'
|
||||
e.target.focus()
|
||||
const params = {pageNumber: this.inputValue, pageSize: this.params.paginationOptions.page_size}
|
||||
return this.params.updatePagination(params)
|
||||
} else {
|
||||
e.target.setAttribute('disabled', 'disabled')
|
||||
e.target.classList.add('opacity-50')
|
||||
const errorObj = {
|
||||
id: this.params.id,
|
||||
color: "red",
|
||||
icon: "<i class='ri-alert-line'></i>",
|
||||
message: error,
|
||||
delay: 5000,
|
||||
}
|
||||
console.error(errorObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- eslint-disable -->
|
||||
|
||||
<template>
|
||||
<div class="pagination w-full flex flex-row items-center justify-between gap-3 mt-2 mb-8 xl:mb-0 pb-8 xl:p-2.5 bg-slate-100 border-2 border-solid border-slate-300 rounded-lg flex-col 2xl:flex-row">
|
||||
<div class="pagination-left flex flex-row gap-1">
|
||||
<div class="current-entries flex text-sm font-bold">Показаны {{currentEntries(params.paginationOptions)}}</div>
|
||||
<div class="count-entries flex text-sm font-bold">(Всего записей - {{params.paginationOptions.total}})</div>
|
||||
</div>
|
||||
<div class="pagination-right flex flex-col xl:flex-row items-center gap-3">
|
||||
<div :id="`table_loder_${params.id}`" class="hidden w-full h-auto bg-light flex items-center justify-center px-2"><div type-node="loader" class="flex my_margin-3 items-center justify-center">
|
||||
<div role="status">
|
||||
<svg aria-hidden="true" class="mr-2 w-6 h-6 text-gray-400 animate-spin dark:text-gray-400 fill-white" viewBox="0 0 100 101" fill="none" >
|
||||
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
|
||||
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="buttons flex h-fit gap-1 sm:gap-3 text-sm">
|
||||
<div class="flex gap-1 sm:gap-3" v-if="params.paginationOptions.page_number > 1">
|
||||
<div v-if="params.paginationOptions.page_number - 1 !== 1">
|
||||
<button :table_btn="`${params.id}`" class="previous flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: 1, pageSize: params.paginationOptions.page_size})">
|
||||
<span>1</span>
|
||||
</button>
|
||||
</div>
|
||||
<button :table_btn="`${params.id}`" class="previous flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number - 1, pageSize: params.paginationOptions.page_size})">
|
||||
<span class="hidden sm:flex">Назад</span><i class="ri-arrow-left-line sm:hidden"></i>
|
||||
</button>
|
||||
<button :table_btn="`${params.id}`" class="previous flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number - 1, pageSize: params.paginationOptions.page_size})">
|
||||
<span>{{params.paginationOptions.page_number - 1}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<button :table_btn="`${params.id}`" class="active flex items-center text-blue-700 font-bold bg-white py-1 px-3 gap-2 rounded cursor-default">
|
||||
<span>{{params.paginationOptions.page_number}}</span>
|
||||
</button>
|
||||
<div class="flex gap-1 sm:gap-3" v-if="params.paginationOptions.page_number < params.paginationOptions.total_pages">
|
||||
<button :table_btn="`${params.id}`" class="next flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number + 1, pageSize: params.paginationOptions.page_size})">
|
||||
<span>{{params.paginationOptions.page_number + 1}}</span>
|
||||
</button>
|
||||
<button :table_btn="`${params.id}`" class="next flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number + 1, pageSize: params.paginationOptions.page_size})">
|
||||
<span class="hidden sm:flex">Вперёд</span><i class="ri-arrow-right-line sm:hidden"></i>
|
||||
</button>
|
||||
<div v-if="params.paginationOptions.page_number + 1 !== params.paginationOptions.total_pages">
|
||||
<button :table_btn="`${params.id}`" class="next flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.total_pages, pageSize: params.paginationOptions.page_size})">
|
||||
<span>{{params.paginationOptions.total_pages}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row items-center justify-center gap-2"><input v-on:input="updateInputValue" :class="`page-input-${params.id}`" class="flex max-w-[80px] items-center text-slate-700 text-sm font-bold focus-visible:outline-blue-300 bg-white py-1 px-2 gap-2 rounded" value="" placeholder="Страница"/><button @click="setPaginationButton" disabled :class="`page-button-${params.id}`" class="opacity-50 flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-1 rounded">Перейти</button></div>
|
||||
<div id="select-on-page" class="select-on-page flex items-center justify-center gap-3 p-1 text-sm text-slate-700 font-bold">
|
||||
Показать
|
||||
<select name="on_page" v-bind:value="params.paginationOptions.page_size" v-on:change="updateSelectedValue" :table_option="`${params.id}`" class="flex text-gray-700 pl-3 pr-9 py-0.5 rounded cursor-pointer border-none" style="--tw-ring-color:focus: none" >
|
||||
<option :key={idx} v-for="(el, idx) in selectOptions(params.selectOption, params.paginationOptions)" :value="`${el === 'Все' ? params.paginationOptions.total : el}`">
|
||||
{{ el }}
|
||||
</option>`
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
332
users-manage/src/components/2_molecules/Tabulator/helper.js
Normal file
332
users-manage/src/components/2_molecules/Tabulator/helper.js
Normal file
@@ -0,0 +1,332 @@
|
||||
import { always, cond, equals, is, T, mergeLeft } from "ramda";
|
||||
import Handlebars from "handlebars/dist/cjs/handlebars";
|
||||
import moment from "moment";
|
||||
import presetConfig from './presetConfig'
|
||||
|
||||
const tablefy = (list = []) => {
|
||||
const defaultData = (title) => ({
|
||||
headerFilter: "input",
|
||||
minWidth: 80,
|
||||
headerFilterPlaceholder: title,
|
||||
download: false,
|
||||
print: false,
|
||||
vertAlign: "middle",
|
||||
sorter: "string",
|
||||
responsive: 0
|
||||
})
|
||||
|
||||
if (!is(Array, list)) return []
|
||||
return list.map((el) => mergeLeft(el, defaultData(el?.title || "")))
|
||||
}
|
||||
|
||||
const math = (value, operator, operand) => {
|
||||
var left = parseFloat(value);
|
||||
var right = parseFloat(operand);
|
||||
|
||||
switch(operator) {
|
||||
case "+":
|
||||
return left + right;
|
||||
case "-":
|
||||
return left - right;
|
||||
case "*":
|
||||
return left * right;
|
||||
case "/":
|
||||
return left / right;
|
||||
case "%":
|
||||
return left % right;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const link = (col) => ({
|
||||
...col,
|
||||
formatter: (cell, { prefix = "", text, key = "id" }, onRendered) => {
|
||||
onRendered(function () {
|
||||
let row = cell.getRow().getData();
|
||||
if (!text && !cell.getValue()) {
|
||||
return null;
|
||||
}
|
||||
if (!row[key]) return null;
|
||||
|
||||
cell.getElement().innerHTML = `<a target="_blank" href="${prefix}/${
|
||||
row[key]
|
||||
}">${text || cell.getValue()}</a>`;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
[
|
||||
['math', math],
|
||||
['presetConfig', (record, pack) => presetConfig(pack, record)],
|
||||
[
|
||||
"dt_shift",
|
||||
(date, shift, param) => {
|
||||
return date ? moment(date).add(shift, param) : "нд";
|
||||
},
|
||||
],
|
||||
[
|
||||
"dt_format",
|
||||
(date) => {
|
||||
return date ? moment(date).format("DD.MM.YYYY HH:mm") : "нд";
|
||||
},
|
||||
],
|
||||
[
|
||||
"dt_db_format",
|
||||
(date) => {
|
||||
return date ? moment(date).format("YYYY-MM-DDTHH:mm:ss") : "нд";
|
||||
},
|
||||
],
|
||||
[
|
||||
"from_now",
|
||||
(date) => {
|
||||
return moment(date).fromNow()
|
||||
},
|
||||
],
|
||||
[
|
||||
"json_parse",
|
||||
(item) => {
|
||||
return `
|
||||
<div>
|
||||
${item}
|
||||
</div>
|
||||
`
|
||||
},
|
||||
],
|
||||
[
|
||||
"time_from_sec",
|
||||
(ms) => {
|
||||
if (!ms) return "н/д"
|
||||
return `${moment(moment().subtract(Math.round(ms), 'milliseconds')).fromNow()}`.replace("назад", '')
|
||||
},
|
||||
],
|
||||
[
|
||||
"lenght",
|
||||
(list) => {
|
||||
return is(Array, list) ? list.length : 0;
|
||||
},
|
||||
],
|
||||
[
|
||||
"more",
|
||||
(one, two) => {
|
||||
return one > two;
|
||||
},
|
||||
],
|
||||
[
|
||||
"less",
|
||||
(one, two) => {
|
||||
return one < two;
|
||||
},
|
||||
],
|
||||
[
|
||||
"or",
|
||||
(one, two) => {
|
||||
return one || two;
|
||||
},
|
||||
],
|
||||
[
|
||||
"eq",
|
||||
(one, two) => {
|
||||
return one === two;
|
||||
},
|
||||
],
|
||||
[
|
||||
"endOf",
|
||||
(date, param) => moment(date).endOf(param)
|
||||
],
|
||||
[
|
||||
"startOf",
|
||||
(date, param) => moment(date).startOf(param)
|
||||
],
|
||||
[
|
||||
"render",
|
||||
(list, key) => {
|
||||
return list.map(el => el[key]).join(", ")
|
||||
}
|
||||
],
|
||||
[
|
||||
"get",
|
||||
(map, key) => {
|
||||
return map[key] ? `${map[key]}` : ""
|
||||
}
|
||||
],
|
||||
[
|
||||
"get_from",
|
||||
(map, key) => {
|
||||
return map[key] ? `${map[key]}` : ""
|
||||
}
|
||||
],
|
||||
[
|
||||
"uint8parse",
|
||||
(list) => pipe(
|
||||
(x) => Uint8Array.from(x, e => e.charCodeAt(0)),
|
||||
Array.from
|
||||
)(list)
|
||||
],
|
||||
[
|
||||
"filter_aoo",
|
||||
(list = [], key, _availible_list) => {
|
||||
const availible_list = pipe(
|
||||
(x) => Uint8Array.from(x, e => e.charCodeAt(0)),
|
||||
Array.from
|
||||
)(_availible_list)
|
||||
return list.filter((el) =>
|
||||
availible_list.includes(el[key]))
|
||||
}
|
||||
]
|
||||
].forEach(([name, func]) => {
|
||||
Handlebars.registerHelper(name, func);
|
||||
});
|
||||
|
||||
const render = (col) => ({
|
||||
...col,
|
||||
formatter: (cell, _, onRendered) => {
|
||||
|
||||
onRendered(function () {
|
||||
if (`${col.render}`.includes("{{") && `${col.render}`.includes("}}")) {
|
||||
let record = cell.getRow().getData();
|
||||
cell.getElement().innerHTML = Handlebars.compile(col.render)(
|
||||
{...record, __record__: record}
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let sorter = (a, b) => {
|
||||
let [a_field, b_field] = [a["field"], b["field"]];
|
||||
if (a_field.includes("id") && b_field.includes("id"))
|
||||
return a_field.localeCompare(b_field);
|
||||
if (a_field.includes("id")) return -1;
|
||||
if (b_field.includes("id")) return 1;
|
||||
if (a_field.includes("dt") && b_field.includes("dt"))
|
||||
return b_field.localeCompare(a_field);
|
||||
if (a_field.includes("dt")) return -1;
|
||||
if (b_field.includes("dt")) return 1;
|
||||
if (a_field.includes("imei") && b_field.includes("imei"))
|
||||
return b_field.localeCompare(a_field);
|
||||
if (a_field.includes("imei")) return -1;
|
||||
if (b_field.includes("imei")) return 1;
|
||||
return a_field.localeCompare(b_field);
|
||||
};
|
||||
|
||||
const prepCol = (col) =>
|
||||
cond([
|
||||
[() => is(String, col.render), () => render(col)],
|
||||
[equals("link"), () => link(col)],
|
||||
[T, always(col)],
|
||||
])(col.mode);
|
||||
|
||||
const prepColumns = (cols) =>
|
||||
is(Array, cols) ? cols.map((col) => prepCol(col)) : [];
|
||||
|
||||
const renderGroupHeader = ({ groupHeader, value, count, group, extra }) => {
|
||||
const groupData = group._group;
|
||||
|
||||
if (groupData.level == 0) {
|
||||
const {company_id, param, train_info: train, date: _date} = groupData.groupList[0].rows[0].data
|
||||
|
||||
const date = `${_date}`.replace(/\s(.*)/gm, "")
|
||||
|
||||
const icident_group = extra.find((el) =>
|
||||
el.date === date &&
|
||||
el.company_id == company_id &&
|
||||
el.train == train &&
|
||||
el.param == param
|
||||
) || {}
|
||||
|
||||
return Handlebars.compile(groupHeader)({
|
||||
_value: value,
|
||||
_count: count,
|
||||
extra: extra,
|
||||
icident_group: icident_group
|
||||
});
|
||||
}
|
||||
|
||||
return `${value} (${count} записей)`;
|
||||
};
|
||||
|
||||
const groupByFunction = (groupBy) => (data) =>
|
||||
Handlebars.compile(groupBy)(data);
|
||||
|
||||
const makedFiltering = (id, filterParams, selectedFilter, clearFilters) => {
|
||||
|
||||
const table = document.querySelector(`#${id}`)
|
||||
const isFiltering = document.querySelector(`${id}-filters`)
|
||||
|
||||
if(id && filterParams) {
|
||||
filterParams.unshift([])
|
||||
const filtersContainer = document.createElement('div')
|
||||
filtersContainer.setAttribute("id", `${id}-filters`)
|
||||
filtersContainer.classList.add("col-span-12")
|
||||
filtersContainer.innerHTML = `
|
||||
<div id="${id}-filtersContainer" class="filters flex flex-row items-center justify-start gap-3 mb-0 pt-2.5 bg-slate-100 rounded-t-md flex-row 2xl:flex-row">
|
||||
<div id="${id}-selectFilters" class="flex items-center justify-start gap-3 p-1 ml-2 text-base text-slate-700 font-bold">
|
||||
Фильтр по статусу
|
||||
<select name="selectFilter" id="${id}-selectFilter" class="flex text-gray-700 pl-3 pr-9 py-0.5 rounded cursor-pointer border-none" style="--tw-ring-color:focus: none" >
|
||||
${filterParams.map((el) => {
|
||||
return (`<option value='${el}'>
|
||||
${el}
|
||||
</option>`)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<button id="${id}-filtersClear"class="w-fit h-fit flex items-center text-white text-sm font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-1 rounded">Очистить фильтр</button>
|
||||
</div>`
|
||||
if(!isFiltering) {
|
||||
table.before(filtersContainer)
|
||||
}
|
||||
else {
|
||||
isFiltering.remove()
|
||||
table.before(filtersContainer)
|
||||
}
|
||||
|
||||
const selectFilters = document.querySelectorAll(`#${id}-selectFilter`)
|
||||
const selectOptions = document.querySelectorAll(`#${id}-selectFilter option`)
|
||||
const buttonClearFilters = document.querySelector(`#${id}-filtersClear`)
|
||||
|
||||
selectFilters.forEach((el) => {
|
||||
el.onchange = (e) => {
|
||||
if(e.target.value) {
|
||||
selectOptions[0].removeAttribute('selected')
|
||||
console.log('e', e)
|
||||
console.log('selectFilters[0]', selectFilters[0])
|
||||
console.log('selectOptions[0][0]', selectOptions[0])
|
||||
selectedFilter(e.target.value)
|
||||
} else {
|
||||
clearFilters()
|
||||
e.target.setAttribute('selected', 'selected')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
buttonClearFilters.onclick = () => {
|
||||
clearFilters()
|
||||
selectOptions[0].setAttribute('selected', 'selected')
|
||||
console.log('selectFilters[0]', selectFilters[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const validation = (value, pagination) => {
|
||||
const pipe = (...fns) => (...x) => fns.reduce((error, validator) => error || validator(...x), undefined)
|
||||
|
||||
const minPage = length => value => {
|
||||
value = value.replace(/\s+/g, '')
|
||||
return value >= length ? undefined : `Номер страницы должен быть больше или равен ${length}`
|
||||
}
|
||||
const maxPage = length => value => {
|
||||
value = value.replace(/\s+/g, '')
|
||||
return length >= value ? undefined : `Номер страницы должен быть меньше или равен ${length}`
|
||||
}
|
||||
const onlyDigits = value => {
|
||||
const pattern = /^([\d])*$/g
|
||||
return pattern.test(value) ? undefined : `Номер страницы должен содержать только цифры`
|
||||
}
|
||||
const composedValidators = pipe(onlyDigits, minPage(1), maxPage(pagination.total_pages))
|
||||
return composedValidators(value)
|
||||
}
|
||||
|
||||
const pipe = (x) => (...fns) => fns.reduce((r, fn) => fn(r), x)
|
||||
|
||||
export {prepColumns, sorter, renderGroupHeader, groupByFunction, makedFiltering, tablefy, validation, pipe};
|
||||
@@ -0,0 +1,29 @@
|
||||
const localization = () => ({
|
||||
ru: {
|
||||
groups: {
|
||||
item: "запись",
|
||||
items: "записей",
|
||||
},
|
||||
"pagination":{
|
||||
"page_size":"Размер таблицы", //label for the page size select element
|
||||
"page_title":"Показать страницу",//tooltip text for the numeric page button, appears in front of the page number (eg. "Show Page" will result in a tool tip of "Show Page 1" on the page 1 button)
|
||||
"first":"К первой", //text for the first page button
|
||||
"first_title":"Показать страницу", //tooltip text for the first page button
|
||||
"last":"К последней",
|
||||
"last_title":"Последняя страница",
|
||||
"prev":"<<",
|
||||
"prev_title":"<<",
|
||||
"next":">>",
|
||||
"next_title":">>",
|
||||
"all":"Все",
|
||||
"counter":{
|
||||
"showing": "Показать",
|
||||
"of": "из",
|
||||
"rows": "строки",
|
||||
"pages": "страницы",
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default localization;
|
||||
@@ -0,0 +1,197 @@
|
||||
import { cond, T } from "ramda";
|
||||
|
||||
const get_f1_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_2_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация, не установлена (по умолчанию)",
|
||||
1: "Стандартная, конфигурация МРТ (МПТ/АВФ)",
|
||||
2: "Стандартная конфигурация ЖДСМ",
|
||||
3: "Стандартная конфигурация УК/ВПО",
|
||||
4: "ЩОМ/РМ (Акселерометр)",
|
||||
5: "СМ (Сибирский ПГУПС)",
|
||||
6: "СМ (зарезервировано)",
|
||||
7: "Эмуляция платы БКП",
|
||||
8: "Конфигурация Блок-М",
|
||||
40: "Эмуляторы",
|
||||
41: "Эмуляторы (CAN1 + CAN2 (Блок-М) + ModBus",
|
||||
50: "Стандартная конфигурация ModBus Master (опрос внешних устройств)",
|
||||
51: "Стандартная конфигурация ModBus Master (опрос плат АС КРСПС)",
|
||||
52: "Конфигурация трекера (электрички и т.п.)",
|
||||
53: "Конфигурация трекер + счетчик импульсов + акселерометр",
|
||||
54: "РадарП",
|
||||
55: "СДРГО",
|
||||
100 : "ПМА-1М РПМ",
|
||||
101 : "ЩОМ-1400 РПМ",
|
||||
102 : "АМ-140 СКРТ РПМ",
|
||||
103 : "АС-01 РПМ",
|
||||
104 : "ТЭС ПСС-1К РПМ",
|
||||
105 : "ПСС-2П РПМ",
|
||||
200 : "РПБ-01 ВНИКТИ",
|
||||
201 : "МПК Спецлоко",
|
||||
202 : "УК 25/25 ВНИКТИ",
|
||||
203 : "СЗ-800 ВНИКТИ",
|
||||
300 : "ЩОМ-1200С ТЖДМ",
|
||||
301 : "ЩОМ-2000 ТЖДМ",
|
||||
302 : "ССГС-1 ТЖДМ",
|
||||
303 : "ВПО-С ТЖДМ",
|
||||
304 : "МПВ ТЖДМ",
|
||||
305 : "УПК ТЖДМ",
|
||||
306 : "ПРЛ-М ТЖДМ",
|
||||
307 : "РУ-700 ТЖДМ",
|
||||
308 : "МПВ Секция 2 ТЖДМ",
|
||||
1911: "Конфигурация для отладки",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_4_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
1: "Стандартная конфигурация МРТ (МПТ/АВФ)",
|
||||
2: "Стандартная конфигурация ЖДСМ",
|
||||
3: "Стандартная конфигурация УК/ВПО",
|
||||
4: "ЩОМ/РМ (Акселерометр)",
|
||||
5: "СМ (Сибирский ПГУПС)",
|
||||
6: "СМ (зарезервировано)",
|
||||
7: "Эмуляция платы БКП",
|
||||
8: "Конфигурация Блок-М",
|
||||
40: "Эмуляторы (CAN1 + ОНК-160 + ModBus)",
|
||||
41: "Эмуляторы (CAN1 + CAN2 (Блок-М) + ModBus)",
|
||||
50: "Стандартная конфигурация ModBus Master (опрос внешних устройств)",
|
||||
51: "Стандартная конфигурация ModBus Master (опрос плат АС КРСПС)",
|
||||
52: "Конфигурация трекера (электрички и т.п.)",
|
||||
53: "Конфигурация трекер + счетчик импульсов + акселерометр",
|
||||
54: "РадарП",
|
||||
55: "СДРГО",
|
||||
100: "ПМА-1М РПМ",
|
||||
101: "ЩОМ-1400 РПМ",
|
||||
102: "АМ-140 СКРТ РПМ",
|
||||
103: "АС-01 РПМ",
|
||||
104: "ТЭС ПСС-1К РПМ",
|
||||
105: "ПСС-2П РПМ",
|
||||
200: "РПБ-01 ВНИКТИ",
|
||||
201: "МПК Спецлоко",
|
||||
202: "УК 25/25 ВНИКТИ",
|
||||
203: "СЗ-800 ВНИКТИ",
|
||||
300: "ЩОМ-1200С ТЖДМ",
|
||||
301: "ЩОМ-2000 ТЖДМ",
|
||||
302: "ССГС-1 ТЖДМ",
|
||||
303: "ВПО-С ТЖДМ",
|
||||
304: "МПВ ТЖДМ",
|
||||
305: "УПК ТЖДМ",
|
||||
306: "ПРЛ-М ТЖДМ",
|
||||
307: "РУ-700 ТЖДМ",
|
||||
308: "МПВ Секция 2 ТЖДМ",
|
||||
1911: "Конфигурация для отладки",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_skrt_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
1: "Обработка импульсной СКРТ",
|
||||
2: "Конфигурация ModBus Slave Base",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const uk_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
1: "УК AC с подключением по SPI",
|
||||
2: "УК AC с подключением по LORA",
|
||||
3: "УК DC с подключением по SPI",
|
||||
4: "УК DC с подключением по LORA",
|
||||
5: "ВПО AC/DC с подключением по SPI",
|
||||
6: "ВПО AC/DC с подключением по LORA",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_64_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_dig_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
1: "Конфигурация с эмуляторами",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_69_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_80_preset = (el) => {
|
||||
const r = {
|
||||
0: "Плата с экраном для СМ",
|
||||
1: "Плата с экраном для ПСС",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_81_preset = (el) => {
|
||||
const r = {
|
||||
0: "Плата с ультразвуковыми датчиками (СМ и т.п.)",
|
||||
4096: "Плата с ультразвуковыми датчиками (СМ и т.п.) настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
const sadko_82_preset = (el) => {
|
||||
const r = {
|
||||
0: "Плата с дискретными входами + LORA (СМ Tail)",
|
||||
4096: "Плата с дискретными входами + LORA (СМ Tail) настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
|
||||
const sadko_83_preset = (el) => {
|
||||
const r = {
|
||||
0: "Конфигурация не установлена (по умолчанию)",
|
||||
1: "Конфигурация для МПТ",
|
||||
2: "Конфигурация для ТЭС ПСС-1К",
|
||||
3: "Конфигурация для СМ (СМ-2Б)",
|
||||
4: "Конфигурация для Динамик",
|
||||
5: "Конфигурация для УТМ-5",
|
||||
4096: "Настраиваемая конфигурация",
|
||||
};
|
||||
return r[el] || el;
|
||||
};
|
||||
|
||||
const presetConfig = (el, record) => {
|
||||
const { board_id: board_id } = record;
|
||||
board_id == 64 && console.log(record)
|
||||
|
||||
|
||||
return cond([
|
||||
[(board) => board == 1, () => get_f1_preset(el)],
|
||||
[(board) => board == 2, () => sadko_2_preset(el)],
|
||||
[(board) => board == 4, () => sadko_4_preset(el)],
|
||||
[(board) => board == 16, () => sadko_skrt_preset(el)],
|
||||
[(board) => board == 19, () => uk_preset(el)],
|
||||
[(board) => board == 64, () => sadko_64_preset(el)],
|
||||
[(board) => board == 68, () => sadko_dig_preset(el)],
|
||||
[(board) => board == 69, () => sadko_69_preset(el)],
|
||||
[(board) => board == 80, () => sadko_80_preset(el)],
|
||||
[(board) => board == 81, () => sadko_81_preset(el)],
|
||||
[(board) => board == 82, () => sadko_82_preset(el)],
|
||||
[(board) => board == 83, () => sadko_83_preset(el)],
|
||||
[T, () => el],
|
||||
])(board_id);
|
||||
};
|
||||
|
||||
export default presetConfig
|
||||
168
users-manage/src/components/2_molecules/Tabulator/time_funcs.js
Normal file
168
users-manage/src/components/2_molecules/Tabulator/time_funcs.js
Normal file
@@ -0,0 +1,168 @@
|
||||
import { inc, type, cond, equals, T, pipe } from "ramda";
|
||||
|
||||
export const isDate = (date) =>
|
||||
type(date) === "Date" || new Date(date).getTime() > 0;
|
||||
export const formateDateToString = (date) =>
|
||||
isDate(date) && date.toLocaleString();
|
||||
|
||||
const monthNames = [
|
||||
"Январь",
|
||||
"Февраль",
|
||||
"Март",
|
||||
"Апрель",
|
||||
"Май",
|
||||
"Июнь",
|
||||
"Июль",
|
||||
"Август",
|
||||
"Сентябрь",
|
||||
"Октябрь",
|
||||
"Ноябрь",
|
||||
"Декабрь",
|
||||
];
|
||||
|
||||
export const reformateDateString = (date, format = "full") =>
|
||||
date ? reformate(date, format) : null;
|
||||
|
||||
const reformate = (date, format) =>
|
||||
cond([
|
||||
[
|
||||
equals("time-only"),
|
||||
() => {
|
||||
const { hr, min, sec } = getTime(date);
|
||||
return `${hr}:${min}:${sec}`;
|
||||
},
|
||||
],
|
||||
[
|
||||
equals("date-only"),
|
||||
() => {
|
||||
const { day, mth, year } = getDate(date);
|
||||
return `${day}/${mth}/${year}`;
|
||||
},
|
||||
],
|
||||
[
|
||||
equals("date-report"),
|
||||
() => {
|
||||
const { day, mth, year } = getDate(date);
|
||||
return `${year}-${mth}-${day}`;
|
||||
},
|
||||
],
|
||||
[
|
||||
equals("mth-date-report"),
|
||||
() => {
|
||||
date = new Date(date);
|
||||
return `${getMthName(date)} ${date.getDate()}`;
|
||||
},
|
||||
],
|
||||
[
|
||||
T,
|
||||
() => {
|
||||
const { hr, min, sec } = getTime(date);
|
||||
const { day, mth, year } = getDate(date);
|
||||
return `${day}/${mth}/${year} ${hr}:${min}:${sec}`;
|
||||
},
|
||||
],
|
||||
])(format);
|
||||
|
||||
export const addHours = (oldDate, h) => {
|
||||
const date = new Date(oldDate);
|
||||
date.setHours(date.getHours() + h);
|
||||
return date;
|
||||
};
|
||||
|
||||
export const lastDay = addHours(new Date(), -24);
|
||||
|
||||
const getTime = (date) => {
|
||||
date = new Date(date);
|
||||
return {
|
||||
hr: formatTime(date.getHours()),
|
||||
minutes: formatTime(date.getMinutes()),
|
||||
sec: formatTime(date.getSeconds()),
|
||||
};
|
||||
};
|
||||
|
||||
const getDate = (date) => {
|
||||
date = new Date(date);
|
||||
return {
|
||||
mth: formatDate(date.getMonth()),
|
||||
day: date.getDate(),
|
||||
year: date.getFullYear(),
|
||||
};
|
||||
};
|
||||
|
||||
export const getMthName = (date) => monthNames[date.getMonth()];
|
||||
export const formatDate = (t) => (inc(t) >= 10 ? inc(t) : `0${inc(t)}`);
|
||||
export const formatTime = (t) => `0${t}`.slice(-2);
|
||||
|
||||
export const getDateByType = (date) => {
|
||||
if (type(date) === "String") return new Date(date.replace(/\s/, "T"));
|
||||
return new Date(date);
|
||||
};
|
||||
|
||||
export const parseSepareteDate = (
|
||||
date = new Date(),
|
||||
symbol = ".",
|
||||
mode = "DateDMY"
|
||||
) => {
|
||||
const selfDate = getDateByType(date);
|
||||
const dateBits = {
|
||||
year: selfDate.getFullYear(),
|
||||
month: selfDate.getMonth() + 1,
|
||||
day: selfDate.getDate(),
|
||||
hours: selfDate.getHours(),
|
||||
minutes: selfDate.getMinutes(),
|
||||
seconds: selfDate.getSeconds(),
|
||||
};
|
||||
Object.keys(dateBits).forEach((key) => {
|
||||
if (dateBits[key] < 10) dateBits[key] = `0${dateBits[key]}`;
|
||||
});
|
||||
switch (mode) {
|
||||
case "DateDMY":
|
||||
return dateBits.day + symbol + dateBits.month + symbol + dateBits.year;
|
||||
case "DateDM":
|
||||
return dateBits.day + symbol + dateBits.month;
|
||||
case "DateYMD":
|
||||
return dateBits.year + symbol + dateBits.month + symbol + dateBits.day;
|
||||
case "DateDMYHM":
|
||||
return `${
|
||||
dateBits.day + symbol + dateBits.month + symbol + dateBits.year
|
||||
} ${dateBits.hours}:${dateBits.minutes}`;
|
||||
case "DateDMYHMS":
|
||||
return `${
|
||||
dateBits.day + symbol + dateBits.month + symbol + dateBits.year
|
||||
} ${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
|
||||
case "DateYMDHM":
|
||||
return `${
|
||||
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
|
||||
} ${dateBits.hours}:${dateBits.minutes}`;
|
||||
case "DateYMDHMS":
|
||||
return `${
|
||||
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
|
||||
} ${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
|
||||
case "DateHM":
|
||||
return `${dateBits.hours}:${dateBits.minutes}`;
|
||||
case "DateHMDM":
|
||||
return `${dateBits.hours}:${dateBits.minutes} ${dateBits.day}${symbol}${dateBits.month}`;
|
||||
case "DateDataBase":
|
||||
return `${
|
||||
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
|
||||
}T${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
|
||||
case "DateDataBaseSpace":
|
||||
return `${
|
||||
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
|
||||
} ${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
|
||||
default:
|
||||
return dateBits.day + symbol + dateBits.month + symbol + dateBits.year;
|
||||
}
|
||||
};
|
||||
|
||||
export const toStartDay = (date = new Date()) => {
|
||||
const selfDate = getDateByType(date);
|
||||
|
||||
return selfDate.setHours(0, 0, 0);
|
||||
};
|
||||
|
||||
export const withTime = (date, time) =>
|
||||
pipe(
|
||||
(x) => parseSepareteDate(x, "-", "DateYMD"),
|
||||
(x) => `${x}T${time}`
|
||||
)(date);
|
||||
@@ -0,0 +1,68 @@
|
||||
<script>
|
||||
import {mapActions, mapGetters, useStore} from 'vuex'
|
||||
import { FwbSpinner } from 'flowbite-vue'
|
||||
import SiteCard from "./SiteListSiteCard.vue"
|
||||
import NewSiteButton from "@atoms/NewSiteButton.vue"
|
||||
|
||||
export default {
|
||||
name: 'SiteList',
|
||||
components: {FwbSpinner, SiteCard, NewSiteButton},
|
||||
props: {
|
||||
selectSite: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
const store = useStore()
|
||||
store.dispatch('services/uploadSites')
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('services', ["sites", "routes", "sitesState", "newSite", 'isDeleteData']),
|
||||
maxHeight () {
|
||||
return document.documentElement.clientHeight - 400
|
||||
},
|
||||
|
||||
},
|
||||
mounted () {
|
||||
},
|
||||
methods: {
|
||||
...mapActions('services', ["uploadSites", "uploadSiteRoutes"])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="sitesState === 'active'"
|
||||
:class="`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-6 gap-5 overflow-y-auto mb-14 pr-1`"
|
||||
:style="{maxHeight: `${maxHeight}px`}"
|
||||
>
|
||||
<NewSiteButton />
|
||||
<SiteCard
|
||||
v-for="site in sites"
|
||||
:id="site.id"
|
||||
:key="site.name"
|
||||
:site="site"
|
||||
:name="site.name"
|
||||
:port="`${site.port}`"
|
||||
:device_ip="site.device_ip"
|
||||
:proxy_ip="site.proxy_ip"
|
||||
:status="site.status"
|
||||
:description="site.description"
|
||||
:selectSite="selectSite"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="sitesState === 'loading'"
|
||||
class="flex w-full justify-center"
|
||||
>
|
||||
<fwb-spinner
|
||||
size="8"
|
||||
class="mr-2"
|
||||
/>
|
||||
<span class="text-2xl text-slate-700">
|
||||
Загрузка сайтов...
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,108 @@
|
||||
<!-- eslint-disable vue/prop-name-casing -->
|
||||
<script>
|
||||
import {mapActions, mapGetters} from 'vuex'
|
||||
import Input from '@atoms/VInput.vue'
|
||||
import Textarea from '@atoms/VTextarea.vue'
|
||||
import VDoubleSwitch from '@atoms/VDoubleSwitch.vue'
|
||||
|
||||
export default {
|
||||
name: 'EditCard',
|
||||
components: {Input, Textarea, VDoubleSwitch},
|
||||
props: {
|
||||
id: {
|
||||
default: -1,
|
||||
type: Number
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('services', ["selectedSite"]),
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
...mapActions('services', ["editSelectedSite", "breakSavingSite", "breakeAddingSite"]),
|
||||
editData (params) {
|
||||
this.editSelectedSite(params)
|
||||
},
|
||||
breakSavingSiteDate() {
|
||||
if (this.id == -1) {
|
||||
this.breakeAddingSite()
|
||||
} else {
|
||||
this.editable_name = this.name
|
||||
this.editable_port = this.port
|
||||
this.breakSavingSite()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<div class="w-fit mr-2">
|
||||
<VDoubleSwitch
|
||||
name="isCbOn"
|
||||
firstColor="#8b5cf6"
|
||||
secondColor="#2563eb"
|
||||
firstTitle="Офлайн"
|
||||
secondTitle="Онлайн"
|
||||
:isCheck="selectedSite.is_online"
|
||||
position="col"
|
||||
labelClass="items-start pb-2"
|
||||
switchClass="mt-1"
|
||||
@switched="(e) => editData({key: 'is_online', value: e})"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
name="name"
|
||||
:value="selectedSite.name"
|
||||
inputClass="!w-[90%] py-2"
|
||||
placeholder="Указать путь"
|
||||
:onChange="editData"
|
||||
/>
|
||||
<div>
|
||||
<i
|
||||
v-tippy="{ content: 'закрыть сервис' }"
|
||||
class="ri-close-line cursor-pointer"
|
||||
@click="breakSavingSiteDate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center mb-2 w-full">
|
||||
<span class="mr-2 min-w-[80px]">Порт:</span>
|
||||
<Input
|
||||
name="port"
|
||||
:value="`${selectedSite.port}`"
|
||||
inputClass="py-2"
|
||||
placeholder="Порт"
|
||||
:onChange="editData"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center mb-2 w-full">
|
||||
<span class="mr-2 min-w-[80px]">IP proxy:</span>
|
||||
<Input
|
||||
name="proxy_ip"
|
||||
:value="selectedSite.proxy_ip"
|
||||
inputClass="py-2"
|
||||
placeholder="IP proxy"
|
||||
:onChange="editData"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center w-full mb-2">
|
||||
<span class="mr-2 min-w-[80px]">IP устр-ва:</span>
|
||||
<Input
|
||||
name="device_ip"
|
||||
:value="selectedSite.device_ip"
|
||||
inputClass="py-2"
|
||||
placeholder="IP устройства"
|
||||
:onChange="editData"
|
||||
/>
|
||||
</div>
|
||||
<Textarea
|
||||
name="description"
|
||||
:value="selectedSite.description"
|
||||
textareaClass="py-2"
|
||||
placeholder="Описание..."
|
||||
:onChange="editData"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,169 @@
|
||||
<!-- eslint-disable vue/prop-name-casing -->
|
||||
<script>
|
||||
import {mapActions, mapGetters} from 'vuex'
|
||||
import EditCard from './SiteListEditCard.vue'
|
||||
|
||||
export default {
|
||||
name: 'SiteCard',
|
||||
components: {EditCard},
|
||||
props: {
|
||||
id: {
|
||||
default: -1,
|
||||
type: Number
|
||||
},
|
||||
name: {
|
||||
default: "",
|
||||
type: String
|
||||
},
|
||||
port: {
|
||||
default: "",
|
||||
type: String
|
||||
},
|
||||
device_ip: {
|
||||
default: "",
|
||||
type: String
|
||||
},
|
||||
proxy_ip: {
|
||||
default: "",
|
||||
type: String
|
||||
},
|
||||
status: {
|
||||
default: "disable",
|
||||
type: String
|
||||
},
|
||||
description: {
|
||||
default: "disable",
|
||||
type: String
|
||||
},
|
||||
site: {
|
||||
default: () => ({}),
|
||||
type: Object
|
||||
},
|
||||
selectSite: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isDelete: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('services', ["selectedSite", "routes", 'isSaveData']),
|
||||
__btnClass__() {
|
||||
if (this.forcedBtnClass) return this.forcedBtnClass
|
||||
return `${this.btnClass} cursor-pointer w-full bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up`
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isSaveData: function (newVal) {
|
||||
console.log('newVal', newVal)
|
||||
if (newVal) {
|
||||
this.saveSiteData()
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
...mapActions('services', ["saveSite", "createNewSite", 'removeSite', 'updateRoutesWithApi', 'closeRoutesList']),
|
||||
...mapActions('users', ["fetchUsersList"]),
|
||||
toggle () {
|
||||
this.open = !this.open
|
||||
this.onToggle({isOpen: this.open})
|
||||
},
|
||||
saveSiteData () {
|
||||
if (this.selectedSite.id == -1) {
|
||||
const data = {
|
||||
name: this.selectedSite.name,
|
||||
port: this.selectedSite.port,
|
||||
device_ip: this.selectedSite.device_ip,
|
||||
proxy_ip: this.selectedSite.proxy_ip,
|
||||
description: this.selectedSite.description
|
||||
}
|
||||
this.createNewSite(data)
|
||||
} else {
|
||||
this.updateRoutesWithApi(this.selectedSite)
|
||||
this.saveSite(this.selectedSite)
|
||||
}
|
||||
},
|
||||
deleteSite (v) {
|
||||
this.isDelete = v
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="!selectedSite || selectedSite.id !== id"
|
||||
class="block w-full p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
<h5
|
||||
v-tippy="{ content: name }"
|
||||
class="max-w-[200px] text-xl font-bold tracking-tight text-slate-600 dark:text-white truncate"
|
||||
>
|
||||
{{ name }}
|
||||
</h5>
|
||||
<div class="relative">
|
||||
<i
|
||||
v-tippy="{ content: 'редактировать сервис' }"
|
||||
class="ri-pencil-line cursor-pointer"
|
||||
@click="selectSite(site.id, 'dev')"
|
||||
/>
|
||||
<i
|
||||
v-tippy="{ content: 'удалить сервис' }"
|
||||
class="ri-close-line cursor-pointer ml-2"
|
||||
@click="deleteSite(true)"
|
||||
/>
|
||||
<div
|
||||
v-if="isDelete"
|
||||
class="absolute flex flex-row top-0 right-0 p-3 bg-white rounded-lg shadow-lg z-10"
|
||||
>
|
||||
<button
|
||||
class="flex items-center text-xs text-white bg-blue-700 p-1 mr-2 rounded-lg shadow"
|
||||
@click="deleteSite(false)"
|
||||
>
|
||||
Отменить
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center p-1 text-xs text-white bg-slate-700 rounded-lg shadow"
|
||||
@click="removeSite(id)"
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full h-[1px] bg-slate-200 mb-4" />
|
||||
<div class="font-normal text-sm text-gray-700 dark:text-gray-400 mb-4 flex -translate-x-2">
|
||||
<span class="flex border-slate-300 mr-4 bg-slate-100 rounded-full py-1 px-2 items-center w-full max-w-[50%]">
|
||||
<span class="inline-block flex mr-2 font-w-700"> статус:</span> <span class="mr-2">{{ status }}</span> <div class="min-h-2 min-w-2 bg-green-700 rounded-full" />
|
||||
</span>
|
||||
<span class="flex border-slate-300 bg-slate-100 rounded-full py-1 px-2 grow">
|
||||
<span class="inline-block flex mr-2 font-w-700"> Порт:</span> <span>{{ port }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<p class=" font-normal text-sm text-gray-700 dark:text-gray-400 mb-2 flex">
|
||||
<span class="min-w-[80px] font-w-700 inline-block flex"> IP proxy:</span> {{ proxy_ip }}
|
||||
</p>
|
||||
<p class=" font-normal text-sm text-gray-700 dark:text-gray-400 mb-2 flex">
|
||||
<span class="min-w-[80px] font-w-700 inline-block flex"> IP устр-ва:</span> {{ device_ip }}
|
||||
</p>
|
||||
<p class=" font-normal text-sm text-gray-700 dark:text-gray-400 mb-2 flex ">
|
||||
<span class="min-w-[80px] font-w-700 inline-block flex"> Описание:</span> <span
|
||||
v-tippy="{ content: description }"
|
||||
class="truncate"
|
||||
>{{ description }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedSite && selectedSite.id === id"
|
||||
href="#"
|
||||
class="block w-full p-6 bg-white border-4 border-blue-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
|
||||
>
|
||||
<EditCard :id="id" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,101 @@
|
||||
<script>
|
||||
import ModuleContainer from "@organisms/UsersManager/UsersManagerContainer.vue";
|
||||
import UsersTable from "@organisms/UsersManager/UsersManagerUsersTable.vue";
|
||||
import UsersEditor from "@organisms/UsersManager/UsersManagerUserEditor.vue";
|
||||
import ManagerTitle from "@organisms/UsersManager/UsersManagerTitle.vue";
|
||||
import VButton from "@atoms/VButton.vue";
|
||||
import {mapGetters} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'UsersManager',
|
||||
components: {ModuleContainer, UsersTable, UsersEditor, ManagerTitle, VButton},
|
||||
data() {
|
||||
return {
|
||||
componentState: 'view' // view | create | select
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('users', ["usersList"]),
|
||||
gridCols() {
|
||||
return this.componentState === 'view' ? 'grid-cols-1' : 'grid-cols-2'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openUserPanelOfCreate() {
|
||||
this.componentState = 'create'
|
||||
},
|
||||
openUserPanelOfSelect() {
|
||||
this.componentState = 'select'
|
||||
},
|
||||
closeUserPanel() {
|
||||
this.componentState = 'view'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="grid gap-4"
|
||||
:class="`${gridCols}`"
|
||||
>
|
||||
<ModuleContainer>
|
||||
<ManagerTitle>
|
||||
<template #title>
|
||||
<span>Список пользователей</span>
|
||||
</template>
|
||||
<template #control>
|
||||
<div class="flex items-center gap-2">
|
||||
<VButton
|
||||
color="blue"
|
||||
@click="openUserPanelOfCreate"
|
||||
>
|
||||
Добавить пользователя
|
||||
</VButton>
|
||||
<VButton
|
||||
color="purple"
|
||||
@click="openUserPanelOfSelect"
|
||||
>
|
||||
Добавить существующего
|
||||
</VButton>
|
||||
</div>
|
||||
</template>
|
||||
</ManagerTitle>
|
||||
<UsersTable :usersList="usersList" />
|
||||
</ModuleContainer>
|
||||
<ModuleContainer v-if="componentState === 'create'">
|
||||
<ManagerTitle>
|
||||
<template #title>
|
||||
<span>Создать пользователя</span>
|
||||
</template>
|
||||
<template #control>
|
||||
<VButton
|
||||
class="close-button"
|
||||
color="gray"
|
||||
@click="closeUserPanel"
|
||||
>
|
||||
Закрыть
|
||||
</VButton>
|
||||
</template>
|
||||
</ManagerTitle>
|
||||
<UsersEditor />
|
||||
</ModuleContainer>
|
||||
<ModuleContainer v-if="componentState === 'select'">
|
||||
<ManagerTitle>
|
||||
<template #title>
|
||||
<span>Выбрать пользователя</span>
|
||||
</template>
|
||||
<template #control>
|
||||
<VButton
|
||||
class="close-button"
|
||||
color="gray"
|
||||
@click="closeUserPanel"
|
||||
>
|
||||
Закрыть
|
||||
</VButton>
|
||||
</template>
|
||||
</ManagerTitle>
|
||||
<UsersEditor />
|
||||
</ModuleContainer>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'UsersManagerSiteInfo',
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="p-4 shadow-md rounded-md border border-slate-200"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'UsersManagerTitle',
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<h2 class="text-xl font-w-500 mr-6">
|
||||
<slot name="title" />
|
||||
</h2>
|
||||
<slot name="control" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'UsersManagerUserEditor',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
UsersManagerUserEditor
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,55 @@
|
||||
<script>
|
||||
// import VTabulator from '@molecules/Tabulator/VTabulator.vue';
|
||||
|
||||
export default {
|
||||
name: 'UsersManagerUsersTable',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
field: "id",
|
||||
title: "ID",
|
||||
width: 100,
|
||||
sorter: "number",
|
||||
},
|
||||
{
|
||||
field: "name",
|
||||
title: "Name",
|
||||
width: 200,
|
||||
sorter: "string",
|
||||
},
|
||||
{
|
||||
field: "age",
|
||||
title: "Age",
|
||||
width: 100,
|
||||
sorter: "number",
|
||||
},
|
||||
],
|
||||
dataSource: [{
|
||||
id: 1,
|
||||
name: "Adnrew",
|
||||
age: 15
|
||||
}, {
|
||||
id: 2,
|
||||
name: "Micheal",
|
||||
age: 22
|
||||
}, {
|
||||
id: 3,
|
||||
name: "Sara",
|
||||
age: 32
|
||||
}]
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col max-h-[70vh] mb-14"
|
||||
>
|
||||
<div>
|
||||
users table
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script>
|
||||
import PageHeader from "@atoms/AppPageHeader.vue"
|
||||
import SiteList from "@organisms/SiteList/SiteList.vue"
|
||||
import SiteManager from "@organisms/UsersManager/UsersManager.vue"
|
||||
import {mapActions, mapGetters} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'SitesManagerPage',
|
||||
components: {SiteManager, SiteList, PageHeader},
|
||||
computed: {
|
||||
...mapGetters('services', ["sites", "routes", "newRoute", "selectedSiteState"]),
|
||||
},
|
||||
mounted () {
|
||||
this.uploadSites()
|
||||
},
|
||||
methods: {
|
||||
...mapActions('services', ["uploadSites", "uploadAndSelectService"]),
|
||||
...mapActions('users', ["fetchUsersList"]),
|
||||
async selectSite(siteId, mode = 'prod') {
|
||||
await this.fetchUsersList({siteId, mode})
|
||||
this.uploadAndSelectService(siteId)
|
||||
return "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<PageHeader class="me-2 mb-6" />
|
||||
<SiteList :selectSite="selectSite" />
|
||||
<SiteManager v-if="selectedSiteState === 'active'" />
|
||||
</div>
|
||||
</template>
|
||||
23
users-manage/src/components/5_pages/UserManage/index.vue
Normal file
23
users-manage/src/components/5_pages/UserManage/index.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script>
|
||||
import {mapActions, mapGetters} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'UserPage',
|
||||
components: {},
|
||||
computed: {
|
||||
...mapGetters('services', ["sites", "routes", "newRoute"]),
|
||||
},
|
||||
mounted () {
|
||||
this.uploadSites()
|
||||
},
|
||||
methods: {
|
||||
...mapActions('services', ["uploadSites", "uploadSiteData"])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6">
|
||||
users-list
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user