mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 12:19:28 +00:00
virtualsplit and searchbar vue component, TestSearch Controller and View for Demonstration, minor change to SearchBar Controller to work with posted json
This commit is contained in:
@@ -34,8 +34,12 @@ class SearchBar extends FHC_Controller
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$searchstr = $this->input->post(self::SEARCHSTR_PARAM);
|
||||
$types = $this->input->post(self::TYPES_PARAM);
|
||||
//$searchstr = $this->input->post(self::SEARCHSTR_PARAM);
|
||||
//$types = $this->input->post(self::TYPES_PARAM);
|
||||
|
||||
$json = json_decode($this->input->raw_input_stream, true);
|
||||
$searchstr = $json[self::SEARCHSTR_PARAM];
|
||||
$types = $json[self::TYPES_PARAM];
|
||||
|
||||
$this->outputJson($this->searchbarlib->search($searchstr, $types));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
* Test Search Vue Component
|
||||
*/
|
||||
class TestSearch extends Auth_Controller
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
array(
|
||||
'index' => 'system/developer:r'
|
||||
)
|
||||
);
|
||||
|
||||
// Loads WidgetLib
|
||||
$this->load->library('WidgetLib');
|
||||
|
||||
// Loads phrases system
|
||||
$this->loadPhrases(
|
||||
array(
|
||||
'global',
|
||||
'ui',
|
||||
'filter'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* Everything has a beginning
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->view('system/logs/testSearch.php');
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ class SearchBarLib
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function raum($searchstr, $type)
|
||||
private function _raum($searchstr, $type)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
$includesArray = array(
|
||||
'title' => 'Test Search',
|
||||
'jquery3' => true,
|
||||
'bootstrap5' => true,
|
||||
'fontawesome6' => true,
|
||||
'tablesorter2' => true,
|
||||
'vue3' => true,
|
||||
'ajaxlib' => true,
|
||||
'jqueryui1' => true,
|
||||
'filtercomponent' => true,
|
||||
'navigationcomponent' => true,
|
||||
'phrases' => array(
|
||||
'global' => array('mailAnXversandt'),
|
||||
'ui' => array('bitteEintragWaehlen')
|
||||
),
|
||||
'customCSSs' => array(
|
||||
'public/css/components/verticalsplit.css',
|
||||
'public/css/components/searchbar.css',
|
||||
),
|
||||
'customJSs' => array('vendor/axios/axios/axios.min.js'),
|
||||
'customJSModules' => array('public/js/apps/TestSearch.js')
|
||||
);
|
||||
|
||||
$this->load->view('templates/FHC-Header', $includesArray);
|
||||
?>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<!-- Navigation component -->
|
||||
<core-navigation-cmpt :add-side-menu-entries="appSideMenuEntries"></core-navigation-cmpt>
|
||||
|
||||
<div id="content">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h3 class="page-header">
|
||||
Test Search
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<searchbar :searchoptions="searchbaroptions" :searchfunction="searchfunction"></searchbar>
|
||||
|
||||
<verticalsplit>
|
||||
<template #top>
|
||||
<searchbar :searchoptions="searchbaroptions" :searchfunction="searchfunctiondummy"></searchbar>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<!-- Filter component -->
|
||||
<core-filter-cmpt filter-type="LogsViewer" @nw-new-entry="newSideMenuEntryHandler"></core-filter-cmpt>
|
||||
</template>
|
||||
</verticalsplit>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
|
||||
|
||||
+12
-1
@@ -325,6 +325,17 @@
|
||||
"type": "file"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "axios/axios",
|
||||
"version": "0.27.2",
|
||||
"dist": {
|
||||
"url": "https://unpkg.com/axios@0.27.2/dist/axios.min.js",
|
||||
"type": "file"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
@@ -367,7 +378,7 @@
|
||||
"twbs/bootstrap5": "5.1.*",
|
||||
|
||||
"vuejs/vuejs3": "3.2.33",
|
||||
|
||||
"axios/axios": "0.27.2",
|
||||
|
||||
"alvaro-prieto/colresizable": "1.6",
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Created on : Jun 15, 2022, 3:21:25 PM
|
||||
Author : bambi
|
||||
*/
|
||||
|
||||
.searchbar_settings {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
background-color: #fff;
|
||||
border: 2px solid #666;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.searchbar_results {
|
||||
position: absolute;
|
||||
z-index: 9998;
|
||||
background-color: #fff;
|
||||
border: 2px solid #666;
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.searchbar_result {
|
||||
border-bottom: 1px solid #666;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.searchbar_grid {
|
||||
display: grid;
|
||||
grid-template-columns: [icon] 100px [data] auto;
|
||||
}
|
||||
|
||||
.searchbar_icon {
|
||||
grid-column-start: icon;
|
||||
grid-column-end: span 1;
|
||||
}
|
||||
|
||||
.searchbar_data {
|
||||
grid-column-start: data;
|
||||
grid-column-end: span 1;
|
||||
}
|
||||
|
||||
.searchbar_actions {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.searchbar_actions li {
|
||||
list-style: none;
|
||||
text-align: center;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.searchbar_table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.searchbar_tablerow {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.searchbar_tablecell {
|
||||
display: table-cell;
|
||||
min-width: 175px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.searchbar_settings_types {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.searchbar_settings_types li {
|
||||
list-style: none;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.searchbar_settings_types li label {
|
||||
padding: 3px;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Created on : May 19, 2022, 10:16:21 AM
|
||||
Author : bambi
|
||||
*/
|
||||
:root {
|
||||
--fhc-verticalsplit-vsplitter-bg-color: #eee;
|
||||
--fhc-verticalsplit-vsplitter-border-color: #eee;
|
||||
--fhc-verticalsplit-vsplitter-splitactions-color: #000;
|
||||
}
|
||||
|
||||
.verticalsplitted {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.verticalsplitter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.verticalsplitter.top {
|
||||
border-top: solid 3px var(--fhc-verticalsplit-vsplitter-border-color);
|
||||
}
|
||||
|
||||
.verticalsplitter.bottom {
|
||||
border-bottom: solid 3px var(--fhc-verticalsplit-vsplitter-border-color);
|
||||
}
|
||||
|
||||
.splitactions {
|
||||
background-color: var(--fhc-verticalsplit-vsplitter-bg-color);
|
||||
color: var(--fhc-verticalsplit-vsplitter-splitactions-color);
|
||||
display: inline-block;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
.splitactions.top {
|
||||
border-radius: 0 0 40% 40%;
|
||||
}
|
||||
|
||||
.splitactions.bottom {
|
||||
border-radius: 40% 40% 0 0;
|
||||
}
|
||||
|
||||
.splitaction {
|
||||
display: inline-block;
|
||||
width: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.splitaction.resize {
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
#content > div:first-child {
|
||||
margin-top: 30px;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
import {CoreFilterCmpt} from '../components/Filter.js';
|
||||
import {CoreNavigationCmpt} from '../components/Navigation.js';
|
||||
import verticalsplit from "../components/verticalsplit/verticalsplit.js";
|
||||
import searchbar from "../components/searchbar/searchbar.js";
|
||||
import fhcapifactory from "./api/fhcapifactory.js";
|
||||
|
||||
Vue.$fhcapi = fhcapifactory;
|
||||
|
||||
Vue.createApp({
|
||||
"data": function() {
|
||||
return {
|
||||
"title": "Test Search",
|
||||
"appSideMenuEntries": {},
|
||||
"searchbaroptions": {
|
||||
"types": [
|
||||
"person",
|
||||
"raum",
|
||||
"mitarbeiter",
|
||||
"student",
|
||||
"prestudent",
|
||||
"document",
|
||||
"cms",
|
||||
"organisationunit"
|
||||
],
|
||||
"actions": {
|
||||
"person": {
|
||||
"defaultaction": {
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
//alert('person defaultaction ' + JSON.stringify(data));
|
||||
//window.location.href = data.profil;
|
||||
return data.profil;
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "testchildaction1",
|
||||
"icon": "fas fa-check-circle",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('person testchildaction 01 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction2",
|
||||
"icon": "fas fa-file-csv",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('person testchildaction 02 ' + JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"raum": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('raum defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "Rauminformation",
|
||||
"icon": "fas fa-info-circle",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.infolink;
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Raumreservierung",
|
||||
"icon": "fas fa-bookmark",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.booklink;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"employee": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "testchildaction1",
|
||||
"icon": "fas fa-address-book",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 01 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction2",
|
||||
"icon": "fas fa-user-slash",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 02 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction3",
|
||||
"icon": "fas fa-bell",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 03 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction4",
|
||||
"icon": "fas fa-calculator",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 04 ' + JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"organisationunit": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('organisationunit defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"searchbaroptions2": {
|
||||
"types": [
|
||||
"raum",
|
||||
"organisationunit"
|
||||
],
|
||||
"actions": {
|
||||
"raum": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('raum defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "Rauminformation",
|
||||
"icon": "fas fa-info-circle",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.infolink;
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Raumreservierung",
|
||||
"icon": "fas fa-bookmark",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.booklink;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"organisationunit": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('organisationunit defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
"components": {
|
||||
"CoreNavigationCmpt": CoreNavigationCmpt,
|
||||
"CoreFilterCmpt": CoreFilterCmpt,
|
||||
"verticalsplit": verticalsplit,
|
||||
"searchbar": searchbar
|
||||
},
|
||||
"methods": {
|
||||
"newSideMenuEntryHandler": function(payload) {
|
||||
this.appSideMenuEntries = payload;
|
||||
},
|
||||
"searchfunction": function(searchsettings) {
|
||||
return Vue.$fhcapi.Search.search(searchsettings);
|
||||
},
|
||||
"searchfunctiondummy": function(searchsettings) {
|
||||
return Vue.$fhcapi.Search.searchdummy(searchsettings);
|
||||
}
|
||||
}
|
||||
}).mount('#main');
|
||||
@@ -0,0 +1,5 @@
|
||||
import Search from "./search.js";
|
||||
|
||||
export default {
|
||||
"Search": Search
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
export default {
|
||||
search: function(searchsettings) {
|
||||
const url = FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
+ 'index.ci.php/components/SearchBar/search';
|
||||
return axios.post(url, searchsettings);
|
||||
},
|
||||
searchdummy: function(searchsettings) {
|
||||
const url = FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
+ 'public/js/apps/api/dummyapi.php/Search';
|
||||
return axios.post(url, searchsettings);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
export default {
|
||||
props: {
|
||||
res: {
|
||||
type: Object
|
||||
},
|
||||
action: {
|
||||
type: Object
|
||||
},
|
||||
cssclass: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<a :class="this.cssclass" :href="this.getactionhref()"
|
||||
@click="(this.action.type === 'function') ? this.execaction() : null">
|
||||
<slot>Action</slot>
|
||||
</a>
|
||||
`,
|
||||
methods: {
|
||||
getactionhref: function() {
|
||||
return (this.action.type === 'link') ? this.action.action(this.res)
|
||||
: 'javascript:void(0);';
|
||||
},
|
||||
execaction: function() {
|
||||
this.action.action(this.res);
|
||||
this.$emit('actionexecuted');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import action from "./action.js";
|
||||
|
||||
export default {
|
||||
props: [ "res", "actions" ],
|
||||
components: {
|
||||
action: action
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<ul class="searchbar_actions" v-if="this.actions.length > 0">
|
||||
<li v-for="(action, index) in this.actions" :key="action.label">
|
||||
<action :res="this.res" :action="action" :cssclass="'btn btn-primary btn-sm'" @actionexecuted="$emit('actionexecuted')">
|
||||
<i v-if="this.hasicon(index)" :class="this.geticonclass(index)"></i>
|
||||
<span class="p-2">{{ action.label }}</span>
|
||||
</action>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb-3" v-else=""></div>
|
||||
`,
|
||||
methods: {
|
||||
hasicon: function(index) {
|
||||
return (typeof this.actions[index].icon !== "undefined");
|
||||
},
|
||||
geticonclass: function(index) {
|
||||
return this.actions[index].icon;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
import action from "./action.js";
|
||||
import actions from "./actions.js";
|
||||
|
||||
export default {
|
||||
props: [ "res", "actions" ],
|
||||
components: {
|
||||
action: action,
|
||||
actions: actions
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<div class="searchbar_result searchbar_employee">
|
||||
|
||||
<div class="searchbar_grid">
|
||||
<div class="searchbar_icon">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<img v-if="res.foto !== null" :src="res.foto"
|
||||
class="rounded-circle" height="100" />
|
||||
<i v-else class="fas fa-user-circle fa-5x"></i>
|
||||
</action>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_data">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<span class="fw-bold">{{ res.name }}</span>
|
||||
</action>
|
||||
|
||||
<div class="mb-3"></div>
|
||||
|
||||
<div class="searchbar_table">
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">OrgEinheit</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.organisationunit_name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMail</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="this.mailtourl">
|
||||
{{ res.email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Phone</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="this.telurl">
|
||||
{{ res.phone }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<actions :res="this.res" :actions="this.actions.childactions" @actionexecuted="$emit('actionexecuted')"></actions>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`,
|
||||
methods: {
|
||||
},
|
||||
computed: {
|
||||
mailtourl: function() {
|
||||
return 'mailto:' + this.res.email;
|
||||
},
|
||||
telurl: function() {
|
||||
return 'tel:' + this.res.phone;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import action from "./action.js";
|
||||
import actions from "./actions.js";
|
||||
|
||||
export default {
|
||||
props: [ "res", "actions" ],
|
||||
components: {
|
||||
action: action,
|
||||
actions: actions
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<div class="searchbar_result searchbar_organisationunit">
|
||||
|
||||
<div class="searchbar_grid">
|
||||
<div class="searchbar_icon">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<i class="fas fa-sitemap fa-4x"></i>
|
||||
</action>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_data">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<span class="fw-bold">{{ res.name }}</span>
|
||||
</action>
|
||||
|
||||
<div class="mb-3"></div>
|
||||
|
||||
<div class="searchbar_table">
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">übergeordnete OrgEinheit</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.parentoe_name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Gruppen-EMail</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="this.mailtourl">
|
||||
{{ res.mailgroup }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Leiter</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.leader_name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Mitarbeiter-Anzahl</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.number_of_people }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<actions :res="this.res" :actions="this.actions.childactions" @actionexecuted="$emit('actionexecuted')"></actions>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`,
|
||||
methods: {
|
||||
},
|
||||
computed: {
|
||||
mailtourl: function() {
|
||||
return 'mailto:' + this.res.mailgroup;
|
||||
},
|
||||
telurl: function() {
|
||||
return 'tel:' + this.res.phone;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
import action from "./action.js";
|
||||
import actions from "./actions.js";
|
||||
|
||||
export default {
|
||||
props: [ "res", "actions" ],
|
||||
components: {
|
||||
action: action,
|
||||
actions: actions
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<div class="searchbar_result searchbar_person">
|
||||
|
||||
<div class="searchbar_grid">
|
||||
<div class="searchbar_icon">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<img v-if="res.foto !== null" :src="res.foto"
|
||||
class="rounded-circle" height="100" />
|
||||
<i v-else class="fas fa-user-circle fa-5x"></i>
|
||||
</action>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_data">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<span class="fw-bold">{{ res.gn }} {{ res.sn }}</span>
|
||||
</action>
|
||||
|
||||
<div class="mb-3"></div>
|
||||
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMail</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="this.mailtourl">
|
||||
{{ res.mail }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<actions :res="this.res" :actions="this.actions.childactions" @actionexecuted="$emit('actionexecuted')"></actions>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`,
|
||||
methods: {
|
||||
},
|
||||
computed: {
|
||||
mailtourl: function() {
|
||||
return 'mailto:' + this.res.mail;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
import action from "./action.js";
|
||||
import actions from "./actions.js";
|
||||
|
||||
export default {
|
||||
props: [ "res", "actions" ],
|
||||
components: {
|
||||
action: action,
|
||||
actions: actions
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<div class="searchbar_result searchbar_raum">
|
||||
|
||||
<div class="searchbar_grid">
|
||||
<div class="searchbar_icon">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<i class="fas fa-door-open fa-4x"></i>
|
||||
</action>
|
||||
</div>
|
||||
<div class="searchbar_data">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<span class="fw-bold">{{ res.r }}</span>
|
||||
</action>
|
||||
|
||||
<div class="mb-3"></div>
|
||||
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Gebäude</div>
|
||||
<div class="searchbar_tablecell">{{ res.g }}</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Stockwerk</div>
|
||||
<div class="searchbar_tablecell">{{ res.s }}</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Raumnummer</div>
|
||||
<div class="searchbar_tablecell">{{ res.rn }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<actions :res="this.res" :actions="this.actions.childactions" @actionexecuted="$emit('actionexecuted')"></actions>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`,
|
||||
methods: {
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
import person from "./person.js";
|
||||
import raum from "./raum.js";
|
||||
import employee from "./employee.js";
|
||||
import organisationunit from "./organisationunit.js";
|
||||
|
||||
export default {
|
||||
props: [ "searchoptions", "searchfunction" ],
|
||||
data: function() {
|
||||
return {
|
||||
searchtimer: null,
|
||||
showsettings: false,
|
||||
searchsettings: {
|
||||
searchstr: '',
|
||||
types: []
|
||||
},
|
||||
showresult: false,
|
||||
searchresult: [],
|
||||
searching: false,
|
||||
error: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
person: person,
|
||||
raum: raum,
|
||||
employee: employee,
|
||||
organisationunit: organisationunit
|
||||
},
|
||||
template: `
|
||||
<form class="d-flex me-3">
|
||||
<div class="input-group me-2 bg-white">
|
||||
<input ref="searchbox" @keyup="this.search" @focus="this.showsearchresult" v-model="this.searchsettings.searchstr" class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
<button ref="settingsbutton" @click="this.togglesettings" class="btn btn-outline-secondary" type="button" id="search-filter"><i class="fas fa-cog"></i></button>
|
||||
</div>
|
||||
<button @click="this.search" class="btn btn-outline-success" type="button">Search</button>
|
||||
</form>
|
||||
<div v-show="this.showresult" ref="result" class="searchbar_results">
|
||||
<div v-if="this.searching">
|
||||
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
||||
</div>
|
||||
<div v-else-if="this.error !== null">{{ this.error }}</div>
|
||||
<div v-else-if="this.searchresult.length < 1">Es wurden keine Ergebnisse gefunden.</div>
|
||||
<template v-else="" v-for="res in this.searchresult">
|
||||
<person v-if="res.type === 'person'" :res="res" :actions="this.searchoptions.actions.person" @actionexecuted="this.hideresult"></person>
|
||||
<employee v-else-if="res.type === 'mitarbeiter'" :res="res" :actions="this.searchoptions.actions.employee" @actionexecuted="this.hideresult"></employee>
|
||||
<organisationunit v-else-if="res.type === 'organisationunit'" :res="res" :actions="this.searchoptions.actions.organisationunit" @actionexecuted="this.hideresult"></organisationunit>
|
||||
<raum v-else-if="res.type === 'raum'" :res="res" :actions="this.searchoptions.actions.raum" @actionexecuted="this.hideresult"></raum>
|
||||
<div v-else="">Unbekannter Ergebnistyp: '{{ res.type }}'.</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-show="this.showsettings" ref="settings" class="searchbar_settings">
|
||||
<div class="btn-group" v-if="this.searchoptions.types.length > 0">
|
||||
<template v-for="(type, index) in this.searchoptions.types" :key="type">
|
||||
<input type="checkbox" class="btn-check" :id="this.$.uid + 'search_type_' + index" :value="type" v-model="this.searchsettings.types"/>
|
||||
<label class="btn btn-outline-secondary" :for="this.$.uid + 'search_type_' + index">{{ type }}</label>
|
||||
</template>
|
||||
</div>
|
||||
<div class="mb-2"></div>
|
||||
<button ref="settingsrefreshsearch" @click="this.refreshsearch" class="btn btn-primary">Übernehmen</button>
|
||||
</div>
|
||||
`,
|
||||
beforeMount: function() {
|
||||
this.updateSearchOptions();
|
||||
},
|
||||
methods: {
|
||||
updateSearchOptions: function() {
|
||||
this.searchsettings.types = [];
|
||||
for( const idx in this.searchoptions.types ) {
|
||||
this.searchsettings.types.push(this.searchoptions.types[idx]);
|
||||
}
|
||||
},
|
||||
search: function() {
|
||||
if( this.searchtimer !== null ) {
|
||||
clearTimeout(this.searchtimer);
|
||||
}
|
||||
if( this.searchsettings.searchstr.length >= 3 ) {
|
||||
var rect = this.$refs.searchbox.getBoundingClientRect();
|
||||
//console.log(window.innerWidth + ' ' + window.innerHeight + ' ' + JSON.stringify(rect));
|
||||
this.$refs.result.style.top = Math.floor(rect.bottom + 3) + 'px';
|
||||
this.$refs.result.style.right = Math.floor(window.innerWidth - rect.right) + 'px';
|
||||
this.$refs.result.style.width = Math.floor(window.innerWidth * 0.75) + 'px';
|
||||
this.$refs.result.style.height = Math.floor(window.innerHeight * 0.75) + 'px';
|
||||
this.searchtimer = setTimeout(
|
||||
this.callsearchapi,
|
||||
500
|
||||
);
|
||||
} else {
|
||||
this.showresult = false;
|
||||
}
|
||||
},
|
||||
callsearchapi: function() {
|
||||
var that = this;
|
||||
this.error = null;
|
||||
this.searchresult = [];
|
||||
this.searching = true;
|
||||
this.showsearchresult();
|
||||
this.searchfunction(this.searchsettings)
|
||||
.then(function(response) {
|
||||
that.searchresult = response.data.data;
|
||||
})
|
||||
.catch(function(error) {
|
||||
that.error = 'Bei der Suche ist ein Fehler aufgetreten.'
|
||||
+ ' ' + error.message;
|
||||
})
|
||||
.finally(function() {
|
||||
that.searching = false;
|
||||
});
|
||||
},
|
||||
refreshsearch: function() {
|
||||
this.search();
|
||||
this.togglesettings();
|
||||
},
|
||||
togglesettings: function() {
|
||||
this.showsettings = !this.showsettings;
|
||||
var rect = this.$refs.settingsbutton.getBoundingClientRect();
|
||||
//console.log(window.innerWidth + ' ' + window.innerHeight + ' ' + JSON.stringify(rect));
|
||||
this.$refs.settings.style.top = Math.floor(rect.bottom + 3) + 'px';
|
||||
this.$refs.settings.style.right = Math.floor(window.innerWidth - rect.right) + 'px';
|
||||
this.$refs.settings.style.width = Math.floor(window.innerWidth * 0.5) + 'px';
|
||||
//this.$refs.settings.style.height = Math.floor(window.innerHeight * 0.5) + 'px';
|
||||
},
|
||||
hideresult: function(e) {
|
||||
//window.removeEventListener('click', this.hideresult);
|
||||
this.showresult = false;
|
||||
},
|
||||
showsearchresult: function() {
|
||||
if( this.searchsettings.searchstr.length >= 3 ) {
|
||||
this.showresult = true;
|
||||
//window.addEventListener('click', this.hideresult);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
export default {
|
||||
data: function() {
|
||||
return {
|
||||
availHeight: 0,
|
||||
topheight: 0,
|
||||
bottomheight: 0,
|
||||
mousePosY: 0,
|
||||
resize: false,
|
||||
vsplitter: null,
|
||||
vsplitterOffset: 0,
|
||||
selfOffsetTop: 0
|
||||
};
|
||||
},
|
||||
template: `
|
||||
<div ref="verticalsplit">
|
||||
<div ref="toppanel" class="verticalsplitted"
|
||||
:style="{height: this.topheightcss}">
|
||||
<slot name="top">
|
||||
<p>Top Panel</p>
|
||||
</slot>
|
||||
</div>
|
||||
<div ref="vsplitter" class="verticalsplitter"
|
||||
:class="this.topOrBottomClass" @mousedown="this.dragStart">
|
||||
<div class="splitactions" :class="this.topOrBottomClass">
|
||||
<span @click="this.collapseTop" class="splitaction">
|
||||
<i class="fas fa-angle-up"></i>
|
||||
</span>
|
||||
<span @dblclick="this.showBoth" class="splitaction resize">
|
||||
<i class="fas fa-grip-horizontal"></i>
|
||||
</span>
|
||||
<span @click="this.collapseBottom" class="splitaction">
|
||||
<i class="fas fa-angle-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="bottompanel" class="verticalsplitted"
|
||||
:style="{height: this.bottomheightcss}">
|
||||
<slot name="bottom">
|
||||
<p>Bottom Panel</p>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
mounted: function() {
|
||||
this.calcHeights();
|
||||
this.trackVerticalSplitterOffsetTop();
|
||||
window.addEventListener('resize', this.calcHeights);
|
||||
},
|
||||
updated: function() {
|
||||
this.trackVerticalSplitterOffsetTop();
|
||||
},
|
||||
methods: {
|
||||
calcHeights: function() {
|
||||
var windowheight = window.innerHeight;
|
||||
var oldavailHeight = this.availHeight;
|
||||
this.selfOffsetTop = this.$refs.verticalsplit.offsetTop;
|
||||
this.availHeight = windowheight - this.selfOffsetTop - this.$refs.vsplitter.offsetHeight;
|
||||
if( (this.topheight === 0 && this.bottomheight === 0) || oldavailHeight === 0 ) {
|
||||
this.topheight = Math.floor(this.availHeight/2);
|
||||
} else {
|
||||
this.topheight = Math.floor( ((((this.topheight * 100) / oldavailHeight) / 100) * this.availHeight) );
|
||||
}
|
||||
this.bottomheight = this.availHeight - this.topheight;
|
||||
},
|
||||
collapseTop: function() {
|
||||
this.calcHeights();
|
||||
this.topheight = 0;
|
||||
this.bottomheight = this.availHeight;
|
||||
},
|
||||
collapseBottom: function() {
|
||||
this.calcHeights();
|
||||
this.topheight = this.availHeight;
|
||||
this.bottomheight = 0;
|
||||
},
|
||||
showBoth: function() {
|
||||
this.topheight = Math.floor(this.availHeight/2);
|
||||
this.bottomheight = Math.floor(this.availHeight/2);
|
||||
},
|
||||
dragStart: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.addEventListener('mouseup', this.dragEnd);
|
||||
window.addEventListener('mousemove', this.drag);
|
||||
this.resize = true;
|
||||
this.mousePosY = e.clientY;
|
||||
},
|
||||
drag: function(e) {
|
||||
if( !this.resize ) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var offsetY = e.clientY - this.mousePosY;
|
||||
this.topheight = this.topheight + offsetY;
|
||||
if( this.topheight < 0 ) {
|
||||
this.topheight = 0;
|
||||
}
|
||||
if( this.topheight > this.availHeight ) {
|
||||
this.topheight = this.availHeight;
|
||||
}
|
||||
this.bottomheight = this.availHeight - this.topheight;
|
||||
this.mousePosY = e.clientY;
|
||||
},
|
||||
dragEnd: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.removeEventListener('mousemove', this.drag);
|
||||
window.removeEventListener('mouseup', this.dragEnd);
|
||||
this.resize = false;
|
||||
this.mousePosY = e.clientY;
|
||||
},
|
||||
trackVerticalSplitterOffsetTop: function() {
|
||||
this.vsplitterOffset = this.$refs.vsplitter.offsetTop;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
topOrBottomClass: function() {
|
||||
return ((this.vsplitterOffset - this.selfOffsetTop) <= Math.floor(this.availHeight/2))
|
||||
? 'top'
|
||||
: 'bottom';
|
||||
},
|
||||
topheightcss: function() {
|
||||
return this.topheight + 'px';
|
||||
},
|
||||
bottomheightcss: function() {
|
||||
return this.bottomheight + 'px';
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user