Compare commits

..

10 Commits

Author SHA1 Message Date
Sebastian Frank
7da482fc97 no jquery 2017-09-25 13:22:42 +02:00
Sebastian Frank
8899a291fc Merge branch 'master' into matdev 2017-09-25 13:08:03 +02:00
b0a5376733 Overlay 2017-09-15 10:21:06 +02:00
f5ccb0403e Create Overlay (on click dont work) 2017-09-15 10:18:28 +02:00
837a9c734e Create Medialist (New Icons dont work) 2017-09-12 12:13:32 +02:00
fad181c65e Set Icon for Domainlist (dont work!!) 2017-09-12 10:13:04 +02:00
02d2fc1ad2 Try tranistion of trigger-button (not work) 2017-09-12 09:37:20 +02:00
c037fc6eac Change Topbar Height, Fixed Outline on a-click 2017-09-12 09:26:56 +02:00
Sebastian Frank
da7c230927 Merge branch 'master' into 'matdev'
Master

See merge request !2
2017-09-07 23:32:16 +02:00
Sebastian Frank
9a8e22230d Merge branch 'master' into 'matdev'
Master

See merge request !1
2017-09-06 11:03:34 +02:00
33 changed files with 4413 additions and 6143 deletions

View File

@ -1,6 +0,0 @@
pipeline:
build_ui:
image: node
commands:
- npm install
- npm run build

4
.gitignore vendored
View File

@ -1,6 +1,4 @@
*~
/tags
/node_modules
/build/*
!/build/lib.js
!/build/lib.js.map
/build

View File

@ -1,5 +1,4 @@
![build status](https://ci.drone1.basehosts.de/api/badges/panel/baseui/status.svg)
[![pipeline status](https://git.basehosts.de:20443/panel/basispanel-ui/badges/master/pipeline.svg)](https://git.basehosts.de:20443/panel/basispanel-ui/commits/master)
# basispanel UI
@ -17,8 +16,8 @@
```bash
# falls nicht bereits vorhanden
git clone ssh://git@gitbase.de:2222/panel/baseui.git
cd baseui
git clone ssh://git@git.basehosts.de:22234/panel/basispanel-ui.git
cd basispanel-ui
# die Branch auschecken, für die man verantwortlich ist
git checkout matdev # z.B. für Mathias
@ -38,7 +37,7 @@ npm run serve
### Windows
- z.B. SourceTree für GIT
- Repository `ssh://git@gitbase.de:2222/panel/baseui.git` clonen
- Repository `ssh://git@git.basehosts.de:22234/panel/basispanel-ui.git` clonen
- Branch wechseln (`matdev` für Mathias)
- Konsole (cmd oder PowerShell) aufrufen
- ins Verzeichnis ..../basispanel-ui wechseln
@ -81,7 +80,7 @@ git push
## Build-Prozess
- wird auf Drone-Server durch `.drone.yml` automatisch erledigt
- wird auf Gitlab-Server/Runner durch `.gitlab-ci.yml` automatisch erledigt
```bash
# Module installieren (erzeugt node_modules/)
@ -103,6 +102,8 @@ npm run build
- in `.gitlab.yml` ist der Deploy für alle Branches auf "http://ui.basispanel.de/" eingerichtet
- master: http://ui.basispanel.de/master/
- matdev: http://ui.basispanel.de/matdev/
- die jeweiligen URL's sind auch unter Gitlab Environments im Projekt zu finden
- sollte mal ein automatische Deploy (fltp mirror) nicht alle geänderten Dateien kopieren, gibt es einen `deploy_fullsync` in Gitlab Pipelines, den man manuell anstoßen kann
## bei Problemen

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 133 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,41 +2,46 @@
"ui": {
"title": "MyApp",
"api": {
"baseURL": "https://www.basispanel.de/api/v1/"
"baseURL": "https://cx20.basehosts.de/api/v1/"
},
"navigation": {
"main": [
{
"name": "Dashboard",
"to": "/",
"icon": "baseicon-compass"
"icon": "icon-compass"
},
{
"name": "Userlist",
"to": "/userlist",
"icon": "baseicon-contacts"
"icon": "icon-contacts"
},
{
"name": "Domainlist",
"to": "/domainlist",
"icon": "baseicon-contacts"
"icon": "icon-world"
},
{
"name": "Medien",
"to": "/medialist",
"icon": "icon-images"
},
{
"name": "Settings",
"to": "/settings",
"icon": "baseicon-cog"
"icon": "icon-cog"
}
],
"profile": [
{
"name": "Profile anpassen",
"to": "/profile",
"icon": "baseicon-color-wand"
"icon": "icon-color-wand"
},
{
"name": "Logout",
"to": "/logout",
"icon": "baseicon-log-out"
"icon": "icon-log-out"
}
]
}
@ -62,6 +67,11 @@
"to": "/domainlist",
"content": "<domainlist></domainlist>"
},
{
"name": "Medialist",
"to": "/medialist",
"content": "<medialist></medialist>"
},
{
"name": "Settings",
"to": "/settings",

View File

@ -10,7 +10,7 @@
<div id="vue-app">
<div is="app">Lade, bitte warten...</div>
</div>
<script src="build/main.js"></script>
<script src="build/baseui-main.bundle.js"></script>
</body>
</html>

9057
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,9 @@
{
"name": "baseui",
"name": "basispanel-ui",
"repository": "ssh://git@git.basehosts.de:22234/panel/basispanel-ui.git",
"version": "0.1.0",
"description": "JS UI for Basispanel Server Management",
"main": "build/lib.js",
"jsnext:main": "build/lib.js",
"main": "index.js",
"scripts": {
"serve": "WEBPACK_ENV=dev webpack-dev-server --inline --hot --host 0.0.0.0",
"dev": "WEBPACK_ENV=dev webpack --progress --colors --watch",
@ -17,35 +16,34 @@
"license": "UNLICENSED",
"private": true,
"devDependencies": {
"babel-core": "^6.26.3",
"babel-core": "^6.26.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.5",
"babel-loader": "^7.1.2",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-preset-es2015": "^6.24.1",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"css-loader": "^0.28.5",
"eslint": "^4.5.0",
"eslint-loader": "^1.9.0",
"eslint-plugin-vue": "^3.14.0",
"less": "^3.8.1",
"less-loader": "^4.1.0",
"url-loader": "^0.6.2",
"vue-hot-reload-api": "^2.3.1",
"eslint-plugin-vue": "^3.12.0",
"file-loader": "^0.11.2",
"less": "^2.7.2",
"less-loader": "^4.0.5",
"vue-hot-reload-api": "^2.1.0",
"vue-html-loader": "^1.2.4",
"vue-loader": "^13.7.3",
"vue-template-compiler": "^2.5.17",
"webpack": "^3.12.0",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
"vue-loader": "^13.0.4",
"vue-template-compiler": "^2.4.2",
"webpack": "^3.5.5",
"webpack-dev-server": "^2.7.1"
},
"dependencies": {
"axios": "^0.16.2",
"babel-polyfill": "^6.26.0",
"intersection-observer": "^0.4.3",
"intersection-observer": "^0.4.0",
"jwt-decode": "^2.2.0",
"vue": "^2.5.17",
"vue": "^2.4.2",
"vue-observe-visibility": "^0.1.3",
"vue-router": "^2.8.1",
"vuex": "^2.5.0"
"vue-router": "^2.7.0",
"vuex": "^2.3.1"
},
"babel": {
"presets": [

View File

@ -82,6 +82,9 @@ strong {font-weight:700;}
mark {color: @cms_brand_secondary;}
a {text-decoration:none; color: @cms_brand_secondary;}
a:hover {color:#901624;}
a:focus {
outline: none;
}
/*--------------------------------------------------------------
@ -105,7 +108,7 @@ input, textarea, select {
}
textarea {padding:10px; height:auto;}
input:focus, textarea:focus, select:focus {background:#FFF; border-color: @cms_brand_primary;}
label {font-weight:600; color: @text_color; overflow: hidden; text-overflow: ellipsis;}
label {font-weight:600; color: @text_color;}
.checkbox_holder {
position: relative;
@ -191,20 +194,20 @@ button{
/*--------------------------------------------------------------
# CMS CONTENT FORM
--------------------------------------------------------------*/
.cms_content_form {}
/*.cms_content_form {}
.cms_content_form_sidebar {background:#FFF; padding:25px; border:solid 1px #CCC;}
.cms_fieldgroup {background:#f0f0f0; border:solid 1px #CCC; margin-bottom:20px;}
.cms_fieldgroup_header {position:relative; color:#333; border-bottom:solid 1px #CCC; height:40px; line-height:40px; padding:0px 25px; font-weight:600;}
.cms_fieldgroup_content {padding:25px; display:none;}
.cms_fieldgroup_trigger {position:absolute; top:0px; width:100px; right:0px; padding-right:25px; cursor:pointer; height:40px; line-height:40px; text-align:right;}
.cms_fieldgroup_trigger i {color:#333; font-size:18px;}
.fieldgroup_open .cms_fieldgroup_content {display:block;}
.fieldgroup_open .cms_fieldgroup_content {display:block;}*/
/*--------------------------------------------------------------
# Content Area
--------------------------------------------------------------*/
.content_area {position:absolute; left:70px; top:40px; right:0px; height: 100%;}
.content_area {position:absolute; left:70px; top:50px; right:0px; height: 100%;}
.content_main {position:relative; top:0px; padding:25px;}
.cms_content_main_headline {margin-bottom:20px; position:relative;}

View File

@ -1,13 +0,0 @@
import MyForm from 'components/my-form.vue';
import MyInput from 'components/my-input.vue';
import TextareaInput from 'components/textarea-input.vue';
import MyTable from 'components/my-table.vue';
import ScrollTable from 'components/scroll-table.vue';
export default {
MyForm,
MyInput,
TextareaInput,
MyTable,
ScrollTable
}

View File

@ -1,17 +1,16 @@
<!--
<template>
<form @submit.prevent="submit()">
<div v-for="(el, idx) in elements" :key="idx">
<div v-for="(item, key) in elements" :key="key">
<component
:is="el.element"
:name="el.key"
:label="el.label"
:description="el.description"
:props="el.props"
v-model="formData[el.key]"
@validate="validateData(el.key)"
:invalid="formErrors[el.key]"
:validatorMessage="formErrors[el.key] ? formErrors[el.key].message : ''"
:is="item.element"
:name="key"
:label="item.label"
:description="item.description"
:props="item.props"
v-model="formData[key]"
@validate="validateData(key)"
:invalid="formErrors[key]"
:validatorMessage="formErrors[key] ? formErrors[key].message : ''"
>
</component>
</div>
@ -20,131 +19,13 @@
</div>
</form>
</template>
-->
<script>
import MyInput from 'components/my-input.vue';
export default {
name: "MyForm",
render(createElement) {
let self = this;
let _cElCall;
_cElCall = function(elements, formData) {
let formEl = [ ];
elements.forEach(el => {
let n = el.name ? el.name : el.key;
switch (el.element) {
case "my-input":
var inputClass = { };
if (el.class) {
inputClass = el.class;
}
formEl.push(createElement(MyInput, {
props: {
name: n,
label: el.label,
description: el.description,
props: el.props,
value: formData[n],
validate: el.validate
},
class: inputClass,
on: {
input(val) {
formData[n] = val;
},
validate(valid) {
el.valid = valid;
}
}
}));
break;
case "section":
// formEl.push(createElement("h3", el.label));
if (!formData[n]) {
formData[n] = { };
}
formEl.push(
createElement("div", {
class: {
cms_fieldgroup: true,
fieldgroup_open: true,
clear: true
}
}, [
createElement("header", {
class: {
cms_fieldgroup_header: true
}
}, [
el.label,
createElement("div", {
class: {
cms_fieldgroup_trigger: true
}
} [
createElement("i", {
class: {
"ion-chevron-down": true
}
})
])
]),
createElement("div", {
class: {
cms_fieldgroup_content: true,
clear: true
}
},
_cElCall(el.subElements, formData[n])
)
])
); //));
break;
}
});
formEl.push(createElement("div", {
class: {
clear: true
}
})); // clearer as last element in group
return formEl;
}
let formElements = _cElCall(this.elements, self.formData);
this.buttons.forEach(b => {
formElements.push(createElement("button", {
class: {
button: true
},
on: {
click(event) {
event.preventDefault();
self.buttonClick(b.type);
}
}
}, b.label ? b.label : "OK"))
})
return createElement('form', {
on: {
submit(e) {
e.preventDefault();
}
}
}, formElements)
},
props: {
initData: {
type: Object,
default: () => {
return {};
}
},
elements: {
type: Array,
type: Object,
default: () => {
return [];
}
@ -164,34 +45,16 @@ export default {
},
data() {
return {
formData: this.initData,
// formErrors: {},
// elementsMap: {}
}
},
/*created() {
this.setOrder();
},*/
watch: {
/*elements() {
this.setOrder();
},*/
initData() {
this.formData = this.initData;
formData: {},
formErrors: {}
}
},
methods: {
/*setOrder() {
this.elementsMap = {};
for (let a in this.elements) {
this.elementsMap[this.elements[a].key] = this.elements[a];
}
},*/
/*validateData(name) {
if (this.elementsMap[name].required) {
validateData(name) {
if (this.elements[name].required) {
if (!this.formData[name]) {
this.$set(this.formErrors, name, {
message: this.elementsMap[name].requiredMessage
message: this.elements[name].requiredMessage
});
return false;
@ -201,17 +64,6 @@ export default {
}
return true;
},
validateElement(el, val) {
if (el.validate) {
let v = el.validate;
if (v.required && !val) {
el.validatorMessage = v.requiredMessage ? v.requiredMessage : "input is required";
return false;
}
}
return true;
},*/
buttonClick(type) {
switch(type) {
case 'submit':
@ -220,40 +72,13 @@ export default {
}
},
submit() {
// bad hacky solution, but works
this.$children.forEach(c => {
c.validateValue();
});
let _validateE;
_validateE = function(eArr) {
for (let i = 0; i< eArr.length; ++i) {
if (eArr[i].valid === false) {
return false;
} else if (eArr[i].subElements) {
// validate subElements
let subValid = _validateE(eArr[i].subElements);
if (!subValid) {
return false;
}
}
}
return true;
}
if (_validateE(this.elements)) {
this.submitHandler(this.formData);
}
/*
let valid = true;
Object.keys(this.elementsMap).forEach(key => {
Object.keys(this.elements).forEach(key => {
valid = (this.validateData(key) && valid);
});
if (valid) {
this.submitHandler(this.formData);
}
*/
}
}
}

View File

@ -1,27 +1,18 @@
<template>
<div class="input_holder">
<div class="input_header" v-if="label || description">
<label :for="name" v-if="label && props.type != 'checkbox'">{{ label }}<span v-if="validate && validate.required">*</span></label>
<label :for="name" v-if="label">{{ label }}</label>
<div class="input_description" v-if="description">{{ description }}</div>
</div>
<!-- -->
<input type="number" v-if="props && props.type == 'number'"
v-model="currentValue"
:class="{invalid}"
:id="name"
:name="name"
:placeholder="props.placeholder"
@blur="validateValue"
@change="handleChange"
>
<input type="text" v-if="props && props.type == 'text'"
v-model="currentValue"
:class="{invalid}"
:id="name"
:name="name"
:placeholder="props.placeholder"
@blur="validateValue"
@blur="validate"
@change="handleChange"
>
<input type="password" v-else-if="props && props.type == 'password'"
@ -30,7 +21,7 @@
:id="name"
:name="name"
:placeholder="props.placeholder"
@blur="validateValue"
@blur="validate"
@change="handleChange"
>
<div class="checkbox_holder" v-else-if="props && props.type == 'checkbox'">
@ -39,11 +30,9 @@
v-model="currentValue"
:id="name"
:name="name"
@blur="validateValue"
@change="handleChange"
>
<div class="check_checkbox"></div>
<label :for="name" style="margin-left: 25px; width: 99%;" :title="label">{{ label }}<span v-if="validate && validate.required">*</span></label>
</div>
<div v-if="invalid && validatorMessage">{{ validatorMessage }}</div>
@ -65,29 +54,17 @@ export default {
'name',
'props',
'value',
// 'invalid',
// 'validatorMessage',
'validate'
'invalid',
'validatorMessage'
],
data() {
return {
currentValue: this.value,
invalid: false,
validatorMessage: ""
currentValue: this.value
}
},
methods: {
validateValue() {
var valid = true;
let v = this.validate;
if (v) {
if (v.required && !this.currentValue) {
valid = false;
this.validatorMessage = (typeof v.requiredMessage == "string") ? v.requiredMessage : "input required";
}
}
this.invalid = !valid;
this.$emit('validate', valid);
validate() {
this.$emit('validate', this.currentValue);
},
handleChange() {
this.$emit('change', this.currentValue);
@ -95,30 +72,6 @@ export default {
},
watch: {
currentValue(val) {
if (this.props && this.props.datatype) {
// convert
let t = this.props.datatype;
let newVal;
switch (t) {
case "int":
newVal = parseInt(this.currentValue);
if (isNaN(newVal)) {
newVal = 0;
}
break;
case "float":
newVal = parseFloat(this.currentValue);
if (isNaN(newVal)) {
newVal = 0;
}
break;
}
if (newVal !== this.currentValue) {
this.currentValue = newVal;
return; // prevent double input events
}
}
this.$emit('input', val);
}
}

View File

@ -12,9 +12,9 @@
<div @click="orderBy(col.orderBy)" v-if="col.orderBy">
{{ col.heading }}
<i class="icon baseicon-angle-up" aria-hidden="true" v-if="currentOrderBy !== col.orderBy"></i>
<i class="icon baseicon-angle-circled-down" aria-hidden="true" v-else-if="currentOrderDesc"></i>
<i class="icon baseicon-angle-circled-up" aria-hidden="true" v-else></i>
<i class="icon icon-angle-up" aria-hidden="true" v-if="currentOrderBy !== col.orderBy"></i>
<i class="icon icon-angle-circled-down" aria-hidden="true" v-else-if="currentOrderDesc"></i>
<i class="icon icon-angle-circled-up" aria-hidden="true" v-else></i>
</div>
<!-- Column without sorting -->
@ -45,7 +45,7 @@
</li>
</ul>
<div title="Open actions" class="actions_btn" @click="toggleActions">
<i class="icon baseicon-cog" aria-hidden="true"></i>
<i class="icon icon-cog" aria-hidden="true"></i>
</div>
</div>
</div>

View File

@ -19,6 +19,7 @@
<style lang="less">
@import "../mixins";
@bar_height: 50px;
/*--------------------------------------------------------------
# Left Sidebar (Main Navigation)
@ -26,7 +27,7 @@
.sidebar {
position: fixed;
left: 0px;
top: 40px;
top: @bar_height;
bottom: 0px;
background: @ui_bg;
color: white;

View File

@ -6,9 +6,9 @@
<!-- User Profile -->
<div class="user_profile" ref="user_profile">
<a class="trigger" @click.prevent="toggleMenu">
<div class="image"><i class="icon baseicon-user" aria-hidden="true"></i></div>
<!-- <div class="text"> {{ loginDisplay }} <div class="role"> {{ loginType }}</div></div> -->
<div class="trigger_btn"><i class="icon baseicon-arrow-down" aria-hidden="true"></i></div>
<div class="image"><i class="icon icon-user" aria-hidden="true"></i></div>
<div class="text"> {{ loginDisplay }} <div class="role"> {{ loginType }}</div></div>
<div class="trigger_btn"><i class="icon icon-arrow-down" aria-hidden="true"></i></div>
</a>
<nav class="user_menu">
<ul>
@ -81,7 +81,7 @@ export default {
<style lang="less">
@import "../mixins.less";
@bar_height: 40px;
@bar_height: 50px;
/*--------------------------------------------------------------
# Topbar (Sitename)
@ -119,7 +119,7 @@ export default {
width: 30px;
height: auto;
margin: 0 auto;
padding-top: 4px;
padding-top: 8px;
}
}
@ -159,7 +159,7 @@ export default {
&>.trigger {
.clearfix();
display: block;
padding: 4px 10px;
padding: 9px 10px;
font-size: 0;
color: white;
user-select: none;
@ -196,6 +196,8 @@ export default {
font-size: 11px;
color: fade(white, 50%);
}
}
}
@ -215,6 +217,7 @@ export default {
text-align: center;
font-size: 20px;
}
/*.trigger_btn .icon:before {transition:all 0.3s;}*/
.title {
float: left;
font-size: 14px;
@ -239,4 +242,15 @@ export default {
.trigger_btn .icon:before{transform: rotate(180deg);}
}
}
/*@media(max-width: @screen-md-max) {
.user_profile {
&>.trigger {
.text {
display:none;
}
}
}
}*/
</style>

View File

@ -1,6 +1,6 @@
{
"name": "basefont",
"css_prefix_text": "baseicon-",
"name": "iconfont",
"css_prefix_text": "icon-",
"css_use_suffix": false,
"hinting": true,
"units_per_em": 1000,

BIN
src/fonts/iconfont.eot Normal file

Binary file not shown.

80
src/fonts/iconfont.svg Normal file
View File

@ -0,0 +1,80 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2017 by original authors @ fontello.com</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1000" >
<font-face font-family="iconfont" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="search" unicode="&#xe800;" d="M643 386q0 103-73 176t-177 74-177-74-73-176 73-177 177-73 177 73 73 177z m286-465q0-29-22-50t-50-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 153-31 125-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
<glyph glyph-name="user" unicode="&#xe801;" d="M714 69q0-60-35-104t-84-44h-476q-49 0-84 44t-35 104q0 48 5 90t17 85 33 73 52 50 76 19q73-72 174-72t175 72q42 0 75-19t52-50 33-73 18-85 4-90z m-143 495q0-88-62-151t-152-63-151 63-63 151 63 152 151 63 152-63 62-152z" horiz-adv-x="714.3" />
<glyph glyph-name="key" unicode="&#xe802;" d="M464 564q0 45-31 76t-76 31-76-31-31-76q0-23 11-46-23 11-47 11-44 0-76-32t-31-76 31-75 76-32 76 32 31 75q0 24-10 47 23-11 46-11 45 0 76 31t31 76z m475-393q0-9-27-36t-37-28q-5 0-16 9t-20 19-22 22-13 14l-54-53 123-123q15-16 15-38 0-23-21-45t-46-22q-22 0-37 16l-375 374q-98-73-204-73-91 0-148 57t-57 149q0 89 53 174t138 139 175 53q91 0 148-58t57-148q0-105-73-203l198-199 54 54q-2 2-14 14t-23 21-18 21-9 15q0 10 27 37t37 28q7 0 13-6 3-3 26-25t45-44 49-48 40-44 16-23z" horiz-adv-x="1000" />
<glyph glyph-name="cog" unicode="&#xe803;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
<glyph glyph-name="pencil" unicode="&#xe804;" d="M203-7l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
<glyph glyph-name="eye-off" unicode="&#xe805;" d="M310 105l43 79q-48 35-76 88t-27 114q0 67 34 125-128-65-213-197 94-144 239-209z m217 424q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m202 106q0-4 0-5-59-105-176-316t-176-316l-28-50q-5-9-15-9-7 0-75 39-9 6-9 16 0 7 25 49-80 36-147 96t-117 137q-11 17-11 38t11 39q86 131 212 207t277 76q50 0 100-10l31 54q5 9 15 9 3 0 10-3t18-9 18-10 18-10 10-7q9-5 9-15z m21-249q0-78-44-142t-117-91l157 280q4-25 4-47z m250-72q0-19-11-38-22-36-61-81-84-96-194-149t-234-53l41 74q119 10 219 76t169 171q-65 100-158 164l35 63q53-36 102-85t81-103q11-19 11-39z" horiz-adv-x="1000" />
<glyph glyph-name="cancel" unicode="&#xe806;" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
<glyph glyph-name="ok" unicode="&#xe807;" d="M933 534q0-22-16-38l-404-404-76-76q-16-15-38-15t-38 15l-76 76-202 202q-15 16-15 38t15 38l76 76q16 16 38 16t38-16l164-165 366 367q16 16 38 16t38-16l76-76q16-15 16-38z" horiz-adv-x="1000" />
<glyph glyph-name="trash-empty" unicode="&#xe808;" d="M286 439v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m143 0v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m142 0v-321q0-8-5-13t-12-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q7 0 12-5t5-13z m72-404v529h-500v-529q0-12 4-22t8-15 6-5h464q2 0 6 5t8 15 4 22z m-375 601h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q23 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
<glyph glyph-name="docs" unicode="&#xf0c5;" d="M946 636q23 0 38-16t16-38v-678q0-23-16-38t-38-16h-535q-23 0-38 16t-16 38v160h-303q-23 0-38 16t-16 38v375q0 22 11 49t27 42l228 228q15 16 42 27t49 11h232q23 0 38-16t16-38v-183q38 23 71 23h232z m-303-119l-167-167h167v167z m-357 214l-167-167h167v167z m109-361l176 176v233h-214v-233q0-22-15-37t-38-16h-233v-357h286v143q0 22 11 49t27 42z m534-449v643h-215v-232q0-22-15-38t-38-15h-232v-358h500z" horiz-adv-x="1000" />
<glyph glyph-name="menu" unicode="&#xf0c9;" d="M857 100v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-14-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
<glyph glyph-name="angle-left" unicode="&#xf104;" d="M350 546q0-7-6-12l-219-220 219-219q6-6 6-13t-6-13l-28-28q-5-5-12-5t-13 5l-260 261q-6 5-6 12t6 13l260 260q5 6 13 6t12-6l28-28q6-5 6-13z" horiz-adv-x="357.1" />
<glyph glyph-name="angle-right" unicode="&#xf105;" d="M332 314q0-7-5-12l-261-261q-5-5-12-5t-13 5l-28 28q-6 6-6 13t6 13l219 219-219 220q-6 5-6 12t6 13l28 28q5 6 13 6t12-6l261-260q5-5 5-13z" horiz-adv-x="357.1" />
<glyph glyph-name="angle-up" unicode="&#xf106;" d="M600 189q0-7-6-12l-28-28q-5-6-12-6t-13 6l-220 219-219-219q-5-6-13-6t-12 6l-28 28q-6 5-6 12t6 13l260 260q5 6 12 6t13-6l260-260q6-5 6-13z" horiz-adv-x="642.9" />
<glyph glyph-name="angle-down" unicode="&#xf107;" d="M600 439q0-7-6-12l-260-261q-5-5-13-5t-12 5l-260 261q-6 5-6 12t6 13l28 28q5 6 12 6t13-6l219-219 220 219q5 6 13 6t12-6l28-28q6-5 6-13z" horiz-adv-x="642.9" />
<glyph glyph-name="chatbox" unicode="&#xf11b;" d="M148 69c-82 0-148 60-148 138v409c0 78 66 140 148 140h516c82 0 149-62 149-140v-409c0-78-67-138-149-138h-8v-125s-154 105-168 115-13 10-41 10h-299z" horiz-adv-x="812.5" />
<glyph glyph-name="document-text" unicode="&#xf12e;" d="M559 520c2-6 4-12 4-20v-478c0-26-20-47-43-47h-475c-23 0-45 21-45 47v656c0 26 22 47 45 47h312c6 0 12 0 18-4 4-2 10-6 14-10l164-179c4-4 4-8 6-12z m-465-20v-19c0-2 2-6 6-6h138c4 0 6 4 6 6v19c0 4-2 6-6 6h-138c-4 0-6-2-6-6z m0-250v-19c0-2 2-6 6-6h240c4 0 4 4 4 6v19c0 4 0 6-4 6h-240c-4 0-6-2-6-6z m312-144v19c0 4 0 6-4 6h-302c-4 0-6-2-6-6v-19c0-2 2-6 6-6h302c4 0 4 4 4 6z m63 250v19c0 4 0 6-4 6h-365c-4 0-6-2-6-6v-19c0-2 2-6 6-6h365c4 0 4 4 4 6z m-92 146h117l-138 153v-129c0-12 9-24 21-24z" horiz-adv-x="562.5" />
<glyph glyph-name="angle-circled-left" unicode="&#xf137;" d="M507 72l57 57q11 10 11 25t-11 25l-171 171 171 171q11 11 11 25t-11 26l-57 57q-10 10-25 10t-25-10l-253-254q-11-10-11-25t11-25l253-253q11-11 25-11t25 11z m350 278q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
<glyph glyph-name="angle-circled-right" unicode="&#xf138;" d="M400 72l254 253q10 11 10 25t-10 25l-254 254q-10 10-25 10t-25-10l-57-57q-11-11-11-26t11-25l171-171-171-171q-11-11-11-25t11-25l57-57q11-11 25-11t25 11z m457 278q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
<glyph glyph-name="angle-circled-up" unicode="&#xf139;" d="M650 214l57 57q11 11 11 25t-11 26l-253 253q-11 11-25 11t-25-11l-254-253q-10-11-10-26t10-25l57-57q11-10 25-10t25 10l172 172 171-172q11-10 25-10t25 10z m207 136q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
<glyph glyph-name="angle-circled-down" unicode="&#xf13a;" d="M454 125l253 254q11 10 11 25t-11 25l-57 57q-10 10-25 10t-25-10l-171-172-172 172q-10 10-25 10t-25-10l-57-57q-10-11-10-25t10-25l254-254q10-10 25-10t25 10z m403 225q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
<glyph glyph-name="gear" unicode="&#xf13d;" d="M678 350c0-47 29-84 72-109-8-26-19-51-31-75-49 12-88-5-121-39s-43-72-32-121c-23-12-48-23-74-31-25 43-70 72-117 72s-92-29-117-72c-26 8-53 19-76 31 11 49 4 88-30 121s-72 41-121 30c-11 23-23 50-31 76 43 25 72 70 72 117s-29 84-72 109c8 26 18 51 31 75 49-12 88 6 121 39s41 72 30 121c23 12 50 23 76 31 25-43 70-72 117-72s92 29 117 72c26-8 53-19 76-31-11-49-3-88 30-121s72-51 121-39c12-24 23-49 31-75-43-25-72-62-72-109z m-303-191c106 0 191 86 191 191s-85 191-191 191-191-85-191-191 86-191 191-191z" horiz-adv-x="750" />
<glyph glyph-name="images" unicode="&#xf148;" d="M832 577c25-2 45-24 43-47l-27-514c-2-25-24-43-49-41l-645 31c-25 2-45 20-43 45l4 90-29-2c-23-2-43 14-45 35l-41 461c-2 22 14 41 37 43l578 47c24 2 43-14 45-35l10-106z m-693-12c2 23 21 43 47 43l427-22-4 61h-2v2c-1 10-9 16-19 16l-510-43c-10 0-19-8-19-18v0-2l31-348 35 49z m652-461l20 387v2 0c0 11-10 19-22 19l-113 6-57 4-398 18c-12 0-22-8-24-18v0-2l-6-123-13-250v-14l12 14 199 213c8 8 15 12 27 12s22-6 27-14l84-96 6-6c4-4 12-8 20-8s10 2 17 6l34 24c7 6 13 8 21 8s18-4 22-10l52-65z m-125 234c-39 0-68 30-68 69s31 68 68 68c39 0 68-31 68-68s-31-69-68-69z" horiz-adv-x="875" />
<glyph glyph-name="log-out" unicode="&#xf29f;" d="M654 217l102 102h-443v62h443l-102 102 43 45 178-178-178-178z m47-133c6 6 12 10 16 16h80c-78-113-211-188-359-188-243 0-438 196-438 438s195 438 438 438c148 0 281-75 359-188h-80c-4 6-10 10-16 16-70 70-164 109-263 109s-196-39-266-109-109-166-109-266 39-195 109-266 166-109 266-109 193 39 263 109z" horiz-adv-x="875" />
<glyph glyph-name="pie-graph" unicode="&#xf2a5;" d="M2 350c0 10-2 22-2 31 0 225 182 407 406 407 10 0 22-2 32-2v-63-373h-373-63z m150-285c-50 62-82 139-87 223h435v435c84-6 160-37 223-88 92-74 152-187 152-316 0-225-182-407-406-407-129 0-242 61-317 153z" horiz-adv-x="875" />
<glyph glyph-name="paper-airplane" unicode="&#xf2c3;" d="M0 319l875 469-219-876-218 219-157-219-31 313z m623-283l156 630-629-337 163-61 375 301-250-344z" horiz-adv-x="875" />
<glyph glyph-name="contacts" unicode="&#xf2d9;" d="M404 178c92-27 149-121 170-203h-574c22 82 78 176 170 203 35-19 74-31 117-31s82 12 117 31z m-117 438c113 0 207-94 207-207s-94-207-207-207-207 93-207 207 94 207 207 207z m0-366c67 0 121 45 145 100h-289c23-55 78-100 144-100z m281-150c-25 39-66 84-117 104 22 17 41 44 55 68 25-10 53-16 82-16 43 0 82 12 117 32 92-28 149-106 170-188h-307z m-31 359c-15 73-60 135-123 170 37 57 102 96 174 96 113 0 207-94 207-207s-94-207-207-207c-22 0-43 4-63 10 6 15 12 31 14 49 16-6 31-8 49-8 66 0 121 43 144 97h-195z" horiz-adv-x="875" />
<glyph glyph-name="cube" unicode="&#xf318;" d="M850 522c13 0 25-12 25-24v-357c0-18-12-33-27-43v-2l-342-180v-2c-4-2-8-2-12-2-13 0-25 12-25 24v361c0 18 10 34 25 43v0l6 4 336 174 4 2c4 2 6 2 10 2z m-26 96c0 0 16-6 16-18 0-14-16-21-16-21l-357-190-4-2c-8-4-16-6-25-6s-18 2-26 6l-4 2-357 188s-16 9-16 23c0 12 16 18 16 18l353 160s22 10 34 10 33-10 33-10z m-443-278c16-9 25-25 25-43v-361c0-12-11-24-25-24-4 0-8 2-12 4v0l-342 180v2c-15 10-27 25-27 43v357c0 12 12 24 25 24 4 0 8 0 10-2l4-2 334-174z" horiz-adv-x="875" />
<glyph glyph-name="compass" unicode="&#xf37c;" d="M438 399c25 0 48-24 48-49s-23-49-48-49-49 24-49 49 23 49 49 49z m0 389c240 0 437-198 437-438s-197-438-437-438-438 198-438 438 197 438 438 438z m95-534l166 358-357-166-166-358z" horiz-adv-x="875" />
<glyph glyph-name="arrow-down" unicode="&#xf3d0;" d="M588 538l37-41-312-334-313 334 37 41 276-293z" horiz-adv-x="625" />
<glyph glyph-name="arrow-left" unicode="&#xf3d2;" d="M375 625l-293-275 293-275-41-37-334 312 334 313z" horiz-adv-x="375" />
<glyph glyph-name="arrow-right" unicode="&#xf3d3;" d="M0 625l41 38 334-313-334-312-41 37 293 275z" horiz-adv-x="375" />
<glyph glyph-name="arrow-up" unicode="&#xf3d8;" d="M588 163l-275 293-276-293-37 41 313 334 312-334z" horiz-adv-x="625" />
<glyph glyph-name="color-wand" unicode="&#xf416;" d="M273 540l500-502-66-67-500 502z m-31 64v125h63v-125h-63z m0-398v125h63v-125h-63z m180 234v62h125v-62h-125z m59 191l-88-88-45 43 88 90z m-415 0l45 45 88-90-45-43z m0-326l88 88 45-43-88-90z m-66 135v62h125v-62h-125z" horiz-adv-x="773.4" />
<glyph glyph-name="navigate" unicode="&#xf46e;" d="M406 756c225 0 407-181 407-406s-182-406-407-406-406 181-406 406 182 406 406 406z m0-656l188 438-438-188h250v-250z" horiz-adv-x="812.5" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/fonts/iconfont.ttf Normal file

Binary file not shown.

BIN
src/fonts/iconfont.woff2 Normal file

Binary file not shown.

View File

@ -1,53 +1,56 @@
@import "mixins";
@iconfont_path: "fonts/";
@iconfont_version: 4;
@font-face {
font-family: "basefont";
src: url('@{iconfont_path}basefont.woff') format('woff');
font-family: "iconfont";
.fontPath(@iconfont_path, "iconfont", @iconfont_version);
font-weight: normal;
font-style: normal;
text-rendering: optimizeLegibility;
}
[class^="baseicon-"]::before,
[class*=" baseicon-"]::before {
.iconStyles("basefont");
.icon:before {
.iconStyles("iconfont");
}
.baseicon-search:before { content: '\e800'; } /* '' */
.baseicon-user:before { content: '\e801'; } /* '' */
.baseicon-key:before { content: '\e802'; } /* '' */
.baseicon-cog:before { content: '\e803'; } /* '' */
.baseicon-pencil:before { content: '\e804'; } /* '' */
.baseicon-eye-off:before { content: '\e805'; } /* '' */
.baseicon-cancel:before { content: '\e806'; } /* '' */
.baseicon-ok:before { content: '\e807'; } /* '' */
.baseicon-trash-empty:before { content: '\e808'; } /* '' */
.baseicon-docs:before { content: '\f0c5'; } /* '' */
.baseicon-menu:before { content: '\f0c9'; } /* '' */
.baseicon-angle-left:before { content: '\f104'; } /* '' */
.baseicon-angle-right:before { content: '\f105'; } /* '' */
.baseicon-angle-up:before { content: '\f106'; } /* '' */
.baseicon-angle-down:before { content: '\f107'; } /* '' */
.baseicon-chatbox:before { content: '\f11b'; } /* '' */
.baseicon-document-text:before { content: '\f12e'; } /* '' */
.baseicon-angle-circled-left:before { content: '\f137'; } /* '' */
.baseicon-angle-circled-right:before { content: '\f138'; } /* '' */
.baseicon-angle-circled-up:before { content: '\f139'; } /* '' */
.baseicon-angle-circled-down:before { content: '\f13a'; } /* '' */
.baseicon-gear:before { content: '\f13d'; } /* '' */
.baseicon-images:before { content: '\f148'; } /* '' */
.baseicon-log-out:before { content: '\f29f'; } /* '' */
.baseicon-pie-graph:before { content: '\f2a5'; } /* '' */
.baseicon-paper-airplane:before { content: '\f2c3'; } /* '' */
.baseicon-contacts:before { content: '\f2d9'; } /* '' */
.baseicon-cube:before { content: '\f318'; } /* '' */
.baseicon-compass:before { content: '\f37c'; } /* '' */
.baseicon-arrow-down:before { content: '\f3d0'; } /* '' */
.baseicon-arrow-left:before { content: '\f3d2'; } /* '' */
.baseicon-arrow-right:before { content: '\f3d3'; } /* '' */
.baseicon-arrow-up:before { content: '\f3d8'; } /* '' */
.baseicon-color-wand:before { content: '\f416'; } /* '' */
.baseicon-navigate:before { content: '\f46e'; } /* '' */
.icon-search:before { content: '\e800'; } /* '' */
.icon-user:before { content: '\e801'; } /* '' */
.icon-key:before { content: '\e802'; } /* '' */
.icon-cog:before { content: '\e803'; } /* '' */
.icon-pencil:before { content: '\e804'; } /* '' */
.icon-eye-off:before { content: '\e805'; } /* '' */
.icon-cancel:before { content: '\e806'; } /* '' */
.icon-ok:before { content: '\e807'; } /* '' */
.icon-trash-empty:before { content: '\e808'; } /* '' */
.icon-docs:before { content: '\f0c5'; } /* '' */
.icon-menu:before { content: '\f0c9'; } /* '' */
.icon-angle-left:before { content: '\f104'; } /* '' */
.icon-angle-right:before { content: '\f105'; } /* '' */
.icon-angle-up:before { content: '\f106'; } /* '' */
.icon-angle-down:before { content: '\f107'; } /* '' */
.icon-chatbox:before { content: '\f11b'; } /* '' */
.icon-document-text:before { content: '\f12e'; } /* '' */
.icon-angle-circled-left:before { content: '\f137'; } /* '' */
.icon-angle-circled-right:before { content: '\f138'; } /* '' */
.icon-angle-circled-up:before { content: '\f139'; } /* '' */
.icon-angle-circled-down:before { content: '\f13a'; } /* '' */
.icon-gear:before { content: '\f13d'; } /* '' */
.icon-images:before { content: '\f148'; } /* '' */
.icon-log-out:before { content: '\f29f'; } /* '' */
.icon-pie-graph:before { content: '\f2a5'; } /* '' */
.icon-paper-airplane:before { content: '\f2c3'; } /* '' */
.icon-contacts:before { content: '\f2d9'; } /* '' */
.icon-world:before { content: '\f4d3'; } /* '' */
.icon-cube:before { content: '\f318'; } /* '' */
.icon-compass:before { content: '\f37c'; } /* '' */
.icon-arrow-down:before { content: '\f3d0'; } /* '' */
.icon-arrow-left:before { content: '\f3d2'; } /* '' */
.icon-arrow-right:before { content: '\f3d3'; } /* '' */
.icon-arrow-up:before { content: '\f3d8'; } /* '' */
.icon-color-wand:before { content: '\f416'; } /* '' */
.icon-navigate:before { content: '\f46e'; } /* '' */
.icon-videocamera:before { content: '\f256'; } /* '' */
.icon-stats-bars:before { content: '\f2b5'; } /* '' */

View File

@ -1,13 +1,156 @@
import Vue from 'vue';
import Vuex from 'vuex';
import VueRouter from 'vue-router';
import Axios from 'axios';
import JwtDecode from 'jwt-decode';
import App from 'app.vue';
import GlobalComponents from 'components/components.js';
import MyForm from 'components/my-form.vue';
import MyInput from 'components/my-input.vue';
import TextareaInput from 'components/textarea-input.vue';
import MyTable from 'components/my-table.vue';
import ScrollTable from 'components/scroll-table.vue';
Vue.use(VueRouter);
Vue.use(Vuex);
Vue.component('my-form', MyForm);
Vue.component('my-input', MyInput);
Vue.component('textarea-input', TextareaInput);
Vue.component('my-table', MyTable);
Vue.component('scroll-table', ScrollTable);
const globalConf = {
loginEndpoint: 'login',
loginRoute: '/login',
authHeader: 'X-Auth-Token',
responseType: 'json',
initUrl: 'conf/init.json',
el: '#my-app'
}
const Router = new VueRouter();
const Store = new Vuex.Store({
state: {
ui: {},
persist: {
login: {},
authToken: '',
jwt: {},
credentials: {}
}
},
mutations: {
setUI(state, payload) {
state.ui = payload;
},
setCredentials(state, payload) {
state.persist.credentials = payload;
objectToPersist(state.persist, 'persistantStore');
},
setLogin(state, payload) {
state.persist.login = payload.User;
state.persist.authToken = payload.AuthToken;
state.persist.jwt = JwtDecode(payload.AuthToken);
objectToPersist(state.persist, 'persistantStore');
},
clearLogin(state) {
state.persist.login = {};
state.persist.authToken = '';
state.persist.jwt = {};
state.persist.credentials = {};
objectToPersist(state.persist, 'persistantStore');
}
},
actions: {
apiRequest(context, payload) {
let doRequest = () => {
return new Promise((resolve, reject) => {
const addHeader = {};
addHeader[globalConf.authHeader] = context.state.persist.authToken;
Axios({
method: payload.method ? payload.method : 'get',
baseURL: context.state.ui.api.baseURL,
url: payload.endpoint,
params: payload.params,
data: payload.data,
headers: addHeader,
responseType: globalConf.responseType
})
.then(response => {
console.log(response);
resolve(response.data);
})
.catch(error => {
console.dir(error);
reject(error);
})
});
};
if (payload.endpoint != globalConf.loginEndpoint) {
// no jwt check for login call
if (!context.state.persist.jwt.exp) {
return new Promise((resolve, reject) => {
// show login page
Router.push(globalConf.loginRoute);
reject(['not logged in']);
});
}
let now = Math.round(Date.now() / 1000);
if (context.state.persist.jwt.exp < (now + 300)) {
// too old jwt, logout
return new Promise((resolve, reject) => {
context.commit('clearLogin');
Router.push(globalConf.loginRoute);
reject(['jwt too old, logout']);
});
}
if ((context.state.persist.jwt.exp - 60) < now) {
// jwt near expire, get new one
console.log("getting new jwt");
return context.dispatch(globalConf.loginEndpoint, context.state.persist.credentials)
.then(() => {
console.log("LOOOGIIIINNN");
return doRequest();
})
.catch(error => {
throw error;
})
}
}
return doRequest();
},
login(context, payload) {
return new Promise((resolve, reject) => {
context.dispatch('apiRequest', {
method: 'post',
endpoint: globalConf.loginEndpoint,
data: payload
})
.then(data => {
context.commit('setCredentials', payload);
context.commit('setLogin', data);
resolve(data.User);
})
.catch(error => {
if (error.response && error.response.data && error.response.data.error) {
reject(error.response.data.error);
} else {
reject([]);
}
});
});
}
}
});
function objectToPersist(obj, key) {
try {
@ -31,22 +174,20 @@ function persistToObject(key) {
}
}
// get store persist part from localStorage
let persist = persistToObject('persistantStore');
if (persist) {
Store.state.persist = persist;
}
export default {
config: {
loginEndpoint: 'login',
loginRoute: '/login',
authHeader: 'X-Auth-Token',
tokenClaim: 'AuthToken',
responseType: 'json',
initUrl: 'conf/init.json',
el: '#vue-app'
},
InitApp(vue, config) { // config: {initUrl, views, el}
Object.assign(this.config, config);
var $this = this;
Axios.get($this.config.initUrl)
Vue,
Router,
Store,
Axios,
InitApp(config) { // config: {initUrl, views, el}
Object.assign(globalConf, config);
Axios.get(globalConf.initUrl)
.then(results => {
// set navigation
if (!Array.isArray(results.data.routes)) {
@ -54,145 +195,6 @@ export default {
return;
}
// register global components
for (var prop in GlobalComponents) {
vue.component(prop, GlobalComponents[prop]);
}
vue.use(VueRouter);
const Router = new VueRouter();
vue.use(Vuex);
const Store = new Vuex.Store({
state: {
ui: {},
persist: {
login: {},
authToken: '',
jwt: {},
credentials: {}
}
},
mutations: {
setUI(state, payload) {
state.ui = payload;
},
setCredentials(state, payload) {
state.persist.credentials = payload;
objectToPersist(state.persist, 'persistantStore');
},
setLogin(state, payload) {
state.persist.login = payload.User;
state.persist.authToken = payload[$this.config.tokenClaim];
state.persist.jwt = JwtDecode(payload[$this.config.tokenClaim]);
objectToPersist(state.persist, 'persistantStore');
},
clearLogin(state) {
state.persist.login = {};
state.persist.authToken = '';
state.persist.jwt = {};
state.persist.credentials = {};
objectToPersist(state.persist, 'persistantStore');
}
},
actions: {
apiRequest(context, payload) {
let doRequest = () => {
return new Promise((resolve, reject) => {
const addHeader = {};
addHeader[$this.config.authHeader] = context.state.persist.authToken;
Axios({
method: payload.method ? payload.method : 'get',
baseURL: context.state.ui.api.baseURL,
url: payload.endpoint,
params: payload.params,
data: payload.data,
headers: addHeader,
responseType: $this.config.responseType
})
.then(response => {
console.log(response);
resolve(response);
})
.catch(error => {
console.dir(error);
reject(error);
})
});
};
if (payload.endpoint != $this.config.loginEndpoint) {
// no jwt check for login call
if (!context.state.persist.jwt.exp) {
return new Promise((resolve, reject) => {
// show login page
Router.push($this.config.loginRoute);
reject(['not logged in']);
});
}
let now = Math.round(Date.now() / 1000);
if (context.state.persist.jwt.exp < (now + 300)) {
// too old jwt, logout
return new Promise((resolve, reject) => {
context.commit('clearLogin');
Router.push($this.config.loginRoute);
reject(['jwt too old, logout']);
});
}
if ((context.state.persist.jwt.exp - 60) < now) {
// jwt near expire, get new one
console.log("getting new jwt");
return context.dispatch($this.config.loginEndpoint, context.state.persist.credentials)
.then(() => {
console.log("LOOOGIIIINNN");
return doRequest();
})
.catch(error => {
throw error;
})
}
}
return doRequest();
},
login(context, payload) {
return new Promise((resolve, reject) => {
context.dispatch('apiRequest', {
method: 'post',
endpoint: $this.config.loginEndpoint,
data: payload
})
.then(response => {
context.commit('setCredentials', payload);
context.commit('setLogin', response.data);
resolve(response.data.User);
})
.catch(error => {
if (error.response && error.response.data && error.response.data.error) {
reject(error.response.data.error);
} else {
reject([]);
}
});
});
}
}
});
// get store persist part from localStorage
let persist = persistToObject('persistantStore');
if (persist) {
Store.state.persist = persist;
}
// set ui config in store
Store.commit("setUI", results.data.ui);
@ -207,7 +209,7 @@ export default {
},
component: {
template: '<div id="' + name + rIdx + '">' + content + '</div>',
components: this.config.views,
components: globalConf.views,
data: function (data) {
if (typeof data != 'object') {
return () => { return {}; };
@ -223,13 +225,12 @@ export default {
});
// load app, when init finishs
return new vue({
el: this.config.el,
new Vue({
el: globalConf.el,
render: h => h(App),
router: Router,
store: Store
});
})
.catch(error => {
alert('error loading: ' + error.message);

View File

@ -1,10 +1,9 @@
import Vue from 'vue';
import BaseUI from './lib/baseui.js';
import Views from './views/views.js';
// load init
BaseUI.InitApp(Vue, {
BaseUI.InitApp({
el: '#vue-app',
initUrl: 'conf/init.json',
views: Views

View File

@ -2,7 +2,12 @@
// font path mixin
.fontPath(@path, @filename, @version){
src: url('@{path}@{filename}.woff?@{version}') format('woff');
src: url('@{path}@{filename}.eot?@{version}');
src: url('@{path}@{filename}.eot?@{version}#iefix') format('embedded-opentype'),
url('@{path}@{filename}.woff2?@{version}') format('woff2'),
url('@{path}@{filename}.woff?@{version}') format('woff'),
url('@{path}@{filename}.ttf?@{version}') format('truetype'),
url('@{path}@{filename}.svg?@{version}#iconfont') format('svg');
}
.iconStyles(@font-face){

View File

@ -23,34 +23,28 @@ export default {
return {
username: "",
password: "",
elements: [
{
key: "username",
elements: {
username: {
element: "my-input",
icon: "icon-user",
validate: {
required: true,
requiredMessage: "Der Benutzername wird benötigt!"
},
required: true,
requiredMessage: "Der Benutzername wird benötigt!",
props: {
type: "text",
placeholder: "Benutzername"
}
},
{
key: "password",
password: {
element: "my-input",
icon: "icon-key",
validate: {
required: true,
requiredMessage: "Das Passwort wird benötigt!"
},
required: true,
requiredMessage: "Das Passwort wird benötigt!",
props: {
type: "password",
placeholder: "Passwort"
}
}
],
},
buttons: [{
label: "login",
type: "submit"

View File

@ -68,9 +68,9 @@ export default {
search
}
})
.then(response => {
this.hasMore = (response.data.length >= limit);
this.newRows = response.data;
.then(rows => {
this.hasMore = (rows.length >= limit);
this.newRows = rows;
this.loading = false;
})
.catch(error => {

View File

@ -0,0 +1,522 @@
<template>
<div class="Medialist">
<!-- Overlay -->
<div id="overlay">
<div class="overlay_inner">
<div class="overlay_container">
<div class="overlay_top">
<ul>
<li>< <i class="ion-chevron-left"></i></li>
<li> > <i class="ion-chevron-right"></i></li>
<li class="close"><i class="ion-android-close"></i>x</li>
</ul>
</div>
<div class="overlay_content">
<div class="overlay_left">
<div class="overlay_img"><img src="assets/images/placeholder_image.jpg" alt=""></div>
</div>
<div class="overlay_right">
<table>
<tr>
<td width="150"><strong>Dateiname:</strong></td>
<td>210_21.jpg</td>
</tr>
<tr>
<td><strong>Hochgeladen am:</strong></td>
<td>14. September 2017</td>
</tr>
<tr>
<td><strong>Dateigröße:</strong></td>
<td>64 KB</td>
</tr>
<tr>
<td><strong>Abmessungen:</strong></td>
<td>600 × 600</td>
</tr>
</table>
<section class="overlay_form">
<form action="">
<input type="text" placeholder="URL">
<input type="text" placeholder="Titel">
<input type="text" placeholder="Beschriftung">
<input type="text" placeholder="Alternativtext">
</form>
</section>
</div>
</div>
</div>
</div>
</div>
<h1>Medien</h1>
<div class="medialist_container">
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media file">
<div class="media_inner">
<div class="text">Das-Dokument.pdf</div>
<i class="icon icon-document-text"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media file">
<div class="media_inner">
<div class="text">Das-Dokument.pdf</div>
<i class="icon icon-document-text"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media file">
<div class="media_inner">
<div class="text">Das-Dokument.pdf</div>
<i class="icon icon-document-text"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media file">
<div class="media_inner">
<div class="text">Das-Dokument.pdf</div>
<i class="icon icon-document-text"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media audio">
<div class="media_inner">
<div class="text">Audiofile.mp3</div>
<i class="icon icon-stats-bars"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media video">
<div class="media_inner">
<div class="text">Videofile.avi</div>
<i class="icon icon-videocamera"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media video">
<div class="media_inner">
<div class="text">Videofile.avi</div>
<i class="icon icon-videocamera"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media audio">
<div class="media_inner">
<div class="text">Audiofile.mp3</div>
<i class="icon icon-stats-bars"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media file">
<div class="media_inner">
<div class="text">Dokument.pdf</div>
<i class="icon icon-videocamera"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media file">
<div class="media_inner">
<div class="text">Dokument.pdf</div>
<i class="icon icon-document-text"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media image">
<div class="media_inner">
<img src="assets/images/placeholder_image.jpg" alt="">
<i class="icon icon-images"></i>
<a href=""></a>
</div>
</div>
<!-- Media-File -->
<div class="media video">
<div class="media_inner">
<div class="text">Videofile.avi</div>
<i class="icon icon-videocamera"></i>
<a href=""></a>
</div>
</div>
<div style="clear:both;"></div>
</div>
</div>
</template>
<script>
/*
WIR HABEN KEIN JQUERY
$(document).ready(function() {
$('.media a').on('click', function(e) {
$('#overlay').show();
});
});
*/
</script>
<style lang="less">
@import "~mixins";
.medialist_container {margin:-6px;}
.media {display:block; position:relative; width:10%; height:auto; line-height:0; float:left; padding:3px;}
.media_inner {transition:all ease 0.2s; border:solid 5px transparent; width:100%; height:0; padding-bottom:100%; position:relative;}
.media_inner a {position:absolute; top:0px; bottom:0px; left:0px; right:0px;}
.media:hover .media_inner {border:solid 5px #28b78d;}
.media img {
position:absolute;
height:100%;
width:100%;
object-fit: cover;
}
.media i {
display:block;
position:absolute;
left:0px;
bottom:0px;
width:30px;
height:30px;
background:#28b78d;
color:#FFF;
text-align:center;
font-size:20px;
padding-top:5px;
}
.file i {background:#2868b7;}
.video i {background:#ac1a0b;}
.audio i {background:#c95c14;}
.media .text {
position:absolute;
top:0px; bottom:0px; left:0px; right:0px;
background:#CCC;
color:#FFF;
font-weight:600;
padding:20px;
display:flex;
align-items: center;
justify-content: center;
text-align:center;
line-height:1.2;
}
.media .text i {
line-height:1;
}
.file .text {background:#2868b7;}
.video .text {background:#ac1a0b;}
.audio .text {background:#c95c14;}
/*.media .text i {
position:relative;
top:inherit; bottom:inherit; left:inherit; right:inherit;
display:block;
text-align:center;
margin:0 auto 5px auto;
background:none;
font-size:30px;
height:35px;
line-height:1;
}
.media .text i:before {
margin-left:0px!important;
}*/
@media(max-width:1600px) {
.media {width:20%;}
}
@media(max-width:1000px) {
.media {width:33.33333%;}
}
@media(max-width:600px) {
.media {width:50%;}
}
/* Overlay */
#overlay {
position:fixed;
top:0;
bottom:0;
right:0;
left:0;
z-index:2000;
background:rgba(0,0,0,0.8);
/*display:none;*/
}
.overlay_inner {
position:absolute;
top:0px;
left:0px;
right:0px;
bottom:0px;
display:flex;
align-items: center;
justify-content: center;
text-align:center;
line-height:1.2;
}
.overlay_container {
background: #FFF;
height: auto;
width: 50%;
margin-top: -50px;
/*border: solid 5px #CCC;*/
/*align-items: center;
justify-content: center;
text-align:center;*/
font-weight: 600;
}
.overlay_top {
height:40px;
width:100%;
background:#FFF;
border-bottom:solid 1px #CCC;
}
.overlay_top ul {list-style-type: none; float:right;}
.overlay_top ul li {float:left; height:40px; line-height:40px; width:40px; text-align:center; color:#666; border-left:solid 1px #CCC;}
.overlay_top ul li i:before {margin:0px;}
.overlay_content {
text-align: center;
display:flex;
}
.overlay_left {
float:left;
width:60%;
padding: 25px;
display:flex;
align-items: center;
justify-content: center;
text-align:center;
flex: 1;
}
.overlay_right {
float:left;
width:40%;
padding: 25px;
border-left:solid 1px #CCC;
background:#eeeeee;
/*display:flex;
align-items: center;
justify-content: center;*/
text-align:left;
flex: 1;
font-weight:400;
}
.overlay_img {
width:80%;
}
.overlay_img img {width:100%;}
.overlay_form {
border-top:solid 1px #CCC;
padding-top:30px;
margin-top:30px;
}
.overlay_form input {
margin-bottom:10px;
}
</style>
<script>
export default {
name: "Medialist"
}
</script>

View File

@ -114,9 +114,9 @@ export default {
search
}
})
.then(response => {
this.hasMore = (response.data.length >= limit);
this.newRows = response.data;
.then(rows => {
this.hasMore = (rows.length >= limit);
this.newRows = rows;
this.loading = false;
})
.catch(error => {

View File

@ -3,6 +3,7 @@ import LoginForm from './forms/login.vue';
import Logout from './forms/logout.vue';
import Userlist from './lists/userlist.vue';
import Domainlist from './lists/domainlist.vue';
import Medialist from './lists/medialist.vue';
import Settings from 'views/forms/settings.vue';
export default {
@ -11,5 +12,6 @@ export default {
Logout,
Userlist,
Domainlist,
Medialist,
Settings
}

View File

@ -46,7 +46,7 @@ module.exports = {
output: {
path: exportPath,
publicPath: 'build/',
filename: '[name].js',
filename: 'baseui-[name].bundle.js',
library: 'baseUI',
libraryTarget: 'umd'
},
@ -57,8 +57,11 @@ module.exports = {
loader: 'eslint-loader',
exclude: /node_modules/
}, {
test: /\.woff(\?.*)?$/i,
loader: 'url-loader',
test: /\.(jpe?g|png|gif|woff|woff2|ttf|svg|eot)(\?.*)?$/i,
loader: 'file-loader',
options: {
name: '[name].[sha512:hash:base64:7].[ext]'
}
}, {
test: /\.vue$/,
loader: 'vue-loader'
@ -80,4 +83,4 @@ module.exports = {
extensions: [".vue", ".js", ".less"]
},
plugins
};
};