init
This commit is contained in:
+170
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<header>
|
||||
<div class="column">
|
||||
<span class="left-menu">
|
||||
</span>
|
||||
<span class="middle-menu">
|
||||
</span>
|
||||
<span class="right-menu">
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<router-view></router-view>
|
||||
<footer>
|
||||
<div class="column">
|
||||
<div class="footer-left">
|
||||
d.kollerov
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'app'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@font-face{
|
||||
font-family: "Buran";
|
||||
src: local("Buran USSR"), url('./assets/fonts/Buran USSR.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Mustica";
|
||||
src: local("MusticaPro-SemiBold"), url('./assets/fonts/MusticaPro-SemiBold.otf') format('opentype');
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
color: white;
|
||||
font-family: Mustica;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h5, h6 {
|
||||
font-family: Buran, Arial;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 20px
|
||||
}
|
||||
|
||||
hr {
|
||||
color: white;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
background-color: #826c55;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #5e4226;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #2f2014;
|
||||
}
|
||||
|
||||
div#app {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction:column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #5e4226;
|
||||
font-family: Buran;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
header .left-menu, header .right-menu {
|
||||
width: calc((100% - 40px) / 2);
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
header .right-menu {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
header .middle-menu {
|
||||
width: 40px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
header a, header a:visited {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
text-shadow: 0px 0px 6px #2f2014;
|
||||
}
|
||||
|
||||
header a:hover {
|
||||
|
||||
}
|
||||
|
||||
header a.router-link-active {
|
||||
text-shadow: 0px 0px 6px white;
|
||||
color: #2f2014;
|
||||
}
|
||||
|
||||
.column {
|
||||
margin-left: 150px;
|
||||
margin-right: 150px;
|
||||
}
|
||||
|
||||
.background {
|
||||
background-color: #baa48f;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 10px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
color: #2f2014;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
margin-top: auto;
|
||||
align-self: flex-end;
|
||||
width: 100%;
|
||||
height: min-content;
|
||||
background-color: #5e4226;
|
||||
}
|
||||
|
||||
footer .footer-left {
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
|
||||
::: Buy Premium Version & Full Package, please visit :::
|
||||
|
||||
--> https://www.myfonts.com/fonts/alifinart-studio/mustica-pro/
|
||||
|
||||
|
||||
::: My Portfolio :::
|
||||
|
||||
--> www.behance.net/alifinart/
|
||||
|
||||
|
||||
::: Join our circle or subscribe now to get an extra-special offers :::
|
||||
|
||||
--> https://gumroad.com/alifinart
|
||||
|
||||
--------------------
|
||||
|
||||
Any questions or request please feel free to email:
|
||||
|
||||
alifinart@gmail.com
|
||||
|
||||
Thank's.
|
||||
|
||||
Copyright © 2021 Alifinart
|
||||
|
||||
--------------------
|
||||
|
||||
If you distribute my work, MUST include the name of the foundry “Alifinart Studio”
|
||||
and include a link to my portfolio (behance.net/alifinart), unless you have received
|
||||
permission from us.
|
||||
|
||||
Thank you.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="background">
|
||||
<div class="list-container">
|
||||
<h2>Books</h2>
|
||||
<div class="column">
|
||||
<Dashboard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dashboard from "./admin/Dashboard.vue";
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
data() {
|
||||
},
|
||||
components: {
|
||||
Dashboard: Dashboard
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h2 {
|
||||
color: #2f2014;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
|
||||
.list-elem {
|
||||
width: 25%;
|
||||
padding: 2px 4px 2px 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="game">
|
||||
<div class="background">
|
||||
<form @submit.prevent="uploadBook">
|
||||
<BookEditor v-bind:entry="entry"
|
||||
v-bind:error="error"
|
||||
editorTitle="Add book"
|
||||
:is_edit="false"
|
||||
@modelEdit="data => onInput(data)"
|
||||
@error="this.error = $event.message"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
import BookEditor from './elements/BookEditor.vue'
|
||||
|
||||
export default {
|
||||
name: "BookAdd",
|
||||
data() {
|
||||
return {
|
||||
error: null,
|
||||
entry: {
|
||||
id: '',
|
||||
title: '',
|
||||
author: '',
|
||||
yearPublished: new Date,
|
||||
bookIndex: '<root>\n\t<chapter>\n\t\t<name></name><page></page>\n\t</chapter>\n</root>'
|
||||
},
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BookEditor
|
||||
},
|
||||
computed: mapState({
|
||||
//token: state => "Bearer " + state.token,
|
||||
}),
|
||||
methods: {
|
||||
uploadBook: function () {
|
||||
var self = this;
|
||||
var data = {
|
||||
"author": self.entry.author,
|
||||
"title": self.entry.title,
|
||||
"yearPublished": self.entry.yearPublished,
|
||||
"bookIndex": self.entry.bookIndex
|
||||
};
|
||||
fetch(self.$store.state.config.API_URL + '/Book/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
// "Authorization": self.token,
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}).then(function (response) {
|
||||
if (response.status == 401 || response.status == 403) {
|
||||
// this.$store.commit('purgeToken');
|
||||
// this.$router.push("/admin");
|
||||
}else{
|
||||
return response.json();
|
||||
}
|
||||
}).then((response) => {
|
||||
if(response.isSuccess == true)
|
||||
{
|
||||
self.$router.push("/");
|
||||
}else{
|
||||
self.error = JSON.stringify(response);
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.status == 401 || error.status == 403) {
|
||||
// self.$store.commit('purgeToken');
|
||||
// self.$router.push("/admin");
|
||||
}
|
||||
self.error = error;
|
||||
});
|
||||
},
|
||||
onInput(data) {
|
||||
this.entry = data;
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="game">
|
||||
<div class="background">
|
||||
<form @submit.prevent="saveBook">
|
||||
<BookEditor v-bind:entry="entry"
|
||||
:error="error"
|
||||
editorTitle="Edit game"
|
||||
:is_edit="true"
|
||||
@modelEdit="data => onInput(data)"
|
||||
@error="this.error = $event.message"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import BookEditor from './elements/BookEditor.vue'
|
||||
|
||||
export default {
|
||||
name: "BookEdit",
|
||||
data() {
|
||||
return {
|
||||
error: null,
|
||||
entry: {
|
||||
id: '',
|
||||
title: '',
|
||||
author: '',
|
||||
yearPublished: new Date,
|
||||
bookIndex: ''
|
||||
},
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BookEditor: BookEditor
|
||||
},
|
||||
computed: mapState({
|
||||
// token: state => "Bearer " + state.token,
|
||||
}),
|
||||
mounted() {
|
||||
this.getBook()
|
||||
},
|
||||
methods: {
|
||||
getBook() {
|
||||
var bookId = this.$route.params.id;
|
||||
fetch(this.$store.state.config.API_URL + '/Book/' + bookId, {
|
||||
headers: {
|
||||
// "Authorization": this.token,
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
}).then(function (response) {
|
||||
if (response.status == 401 || response.status == 403) {
|
||||
// this.$store.commit('purgeToken');
|
||||
// this.$router.push("/admin");
|
||||
}else{
|
||||
return response.json();
|
||||
}
|
||||
}).then(response => {
|
||||
this.entry.id = response.book.id;
|
||||
this.entry.title = response.book.title;
|
||||
this.entry.author = response.book.author;
|
||||
this.entry.yearPublished = new Date(response.book.yearPublished);
|
||||
this.entry.bookIndex = response.book.bookIndex;
|
||||
});
|
||||
},
|
||||
saveBook() {
|
||||
var self = this;
|
||||
var data = {
|
||||
"id": self.entry.id,
|
||||
"author": self.entry.author,
|
||||
"title": self.entry.title,
|
||||
"yearPublished": self.entry.yearPublished,
|
||||
"bookIndex": self.entry.bookIndex
|
||||
};
|
||||
fetch(self.$store.state.config.API_URL + '/Book/', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
// "Authorization": self.token,
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}).then(function (response) {
|
||||
if (response.status == 401 || response.status == 403) {
|
||||
// this.$store.commit('purgeToken');
|
||||
// this.$router.push("/admin");
|
||||
}else{
|
||||
return response.json();
|
||||
}
|
||||
}).then((response) => {
|
||||
if(response.isSuccess == true)
|
||||
{
|
||||
self.$router.push("/");
|
||||
}else{
|
||||
self.error = JSON.stringify(response);
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.status == 401 || error.status == 403) {
|
||||
// self.$store.commit('purgeToken');
|
||||
// self.$router.push("/admin");
|
||||
}
|
||||
self.error = error;
|
||||
});
|
||||
},
|
||||
onInput(data) {
|
||||
this.entry = data;
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="table-wrapper">
|
||||
<router-link class="addbtn" :to="{ path: '/add-book' }">Add</router-link>
|
||||
<table>
|
||||
<thead> <tr> <td>Id</td> <td>Title</td> <td>Author</td> <td>Year published</td> <td>Index</td> <td>Actions</td> </tr> </thead>
|
||||
<tbody>
|
||||
<tr v-for="item of bookEntries" v-bind:key="item.id">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.title}}</td>
|
||||
<td>{{item.author}}</td>
|
||||
<td>{{item.yearPublished}}</td>
|
||||
<td>{{item.bookIndex}}</td>
|
||||
<td><router-link :to="{ path: '/edit-book/' + item.id }">Edit</router-link> <button v-on:click="deleteBook(item.id)">Delete</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "BookTable",
|
||||
data() {
|
||||
return {
|
||||
bookEntries: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getBookList();
|
||||
},
|
||||
computed: mapState({
|
||||
}),
|
||||
methods: {
|
||||
getBookList: function () {
|
||||
var self = this;
|
||||
fetch(this.$store.state.config.API_URL + '/Book')
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
}).then(response => {
|
||||
self.bookEntries = response.books;
|
||||
});
|
||||
},
|
||||
deleteBook(id) {
|
||||
fetch(this.$store.state.config.API_URL + '/Book/' + id, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
}
|
||||
}).then(function (response) {
|
||||
if (response.status == 401 || response.status == 403) {
|
||||
// this.$store.commit('purgeToken');
|
||||
// this.$router.push("/admin");
|
||||
}else{
|
||||
return response.json()
|
||||
}
|
||||
}).then(() => {
|
||||
this.getBookList();
|
||||
}).error((error) => {
|
||||
if (error.status == 401 || error.status == 403) {
|
||||
// this.$store.commit('purgeToken');
|
||||
// this.$router.push("/admin");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.addbtn {
|
||||
font-size: 24px;
|
||||
}
|
||||
thead {
|
||||
background-color: #2f2014;
|
||||
color: white;
|
||||
}
|
||||
tr {
|
||||
border: dotted 1px #2f2014;
|
||||
}
|
||||
td {
|
||||
border: dotted 1px #2f2014;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="admin-dashboard">
|
||||
<div class="admin-dashboard">
|
||||
<button :class="{tab: true, active: tabs.indexOf(tab) == selectedTab}" v-bind:key="tab.name" v-for="tab of tabs" v-html="tab.name" v-on:click="selectTab(tabs.indexOf(tab))"></button>
|
||||
<div class="shown-container">
|
||||
<keep-alive>
|
||||
<component v-bind:is="this.tabs[this.selectedTab].component"></component>
|
||||
</keep-alive>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BookTable from './BookTable.vue';
|
||||
import { shallowRef } from 'vue'
|
||||
|
||||
export default {
|
||||
name: "Dashboard",
|
||||
data() {
|
||||
return {
|
||||
selectedTab: 0,
|
||||
tabs: [
|
||||
{
|
||||
name: "Books",
|
||||
component: shallowRef(BookTable)
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BookTable: BookTable
|
||||
},
|
||||
mounted() {
|
||||
// if (this.$store.state.token === undefined || this.$store.state.token === null || this.$store.state.token === '') {
|
||||
// this.$router.push("/admin")
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
selectTab(index) {
|
||||
this.selectedTab = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-dashboard{
|
||||
margin:0 auto;
|
||||
padding: 15px;
|
||||
}
|
||||
button.tab {
|
||||
background-color: #baa48f;
|
||||
border:none;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button.tab.active {
|
||||
border-top: solid 2px #2f2014;
|
||||
font-weight: bold;
|
||||
}
|
||||
.shown-container {
|
||||
background-color: #baa48f;
|
||||
color: #2f2014;
|
||||
padding:5px
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="editor">
|
||||
<div class="column">
|
||||
<h2>{{editorTitle}}</h2>
|
||||
<div class="error" v-if="this.error != null"><p v-html="this.error"></p></div>
|
||||
|
||||
<input class="savebtn" type="submit" value="Save" />
|
||||
<router-link class="cancelbtn" :to="{ path: '/' }">Cancel</router-link>
|
||||
|
||||
<SingleLineEdit name="title"
|
||||
label="Title"
|
||||
v-bind:value="entry.title"
|
||||
@valueChanged="v => onTitle(v)" />
|
||||
|
||||
<SingleLineEdit name="author"
|
||||
label="Author"
|
||||
v-bind:value="entry.author"
|
||||
@valueChanged="v => onAuthor(v)" />
|
||||
|
||||
<DateEdit name="yearPublished"
|
||||
label="Year published"
|
||||
v-bind:value="entry.yearPublished"
|
||||
@valueChanged="v => onYear(v)" />
|
||||
|
||||
<MultiLineEdit name="bookIndex"
|
||||
label="Book index"
|
||||
v-bind:value="entry.bookIndex"
|
||||
@valueChanged="v => onIndex(v)" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SingleLineEdit from './SingleLineEdit.vue';
|
||||
import MultiLineEdit from './MultiLineEdit.vue';
|
||||
import DateEdit from './DateEdit.vue';
|
||||
|
||||
export default {
|
||||
name: "BookEditor",
|
||||
model: {
|
||||
prop: 'entry',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
error: "AAAAAA",
|
||||
is_edit: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
editorTitle: {
|
||||
type: String,
|
||||
default() {
|
||||
return 'Title';
|
||||
}
|
||||
},
|
||||
entry: {
|
||||
id: {
|
||||
type: String,
|
||||
default() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
author: {
|
||||
type: String,
|
||||
default() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
yearPublished: {
|
||||
type: Date,
|
||||
default() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
bookIndex: {
|
||||
type: String,
|
||||
default() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
components: {
|
||||
SingleLineEdit,
|
||||
MultiLineEdit,
|
||||
DateEdit
|
||||
},
|
||||
methods: {
|
||||
onIndex(x) {
|
||||
this.$emit('modelEdit', {
|
||||
id: this.entry.id,
|
||||
yearPublished: this.entry.yearPublished,
|
||||
title: this.entry.title,
|
||||
author: this.entry.author,
|
||||
bookIndex: x
|
||||
});
|
||||
},
|
||||
onAuthor(x) {
|
||||
this.$emit('modelEdit', {
|
||||
id: this.entry.id,
|
||||
yearPublished: this.entry.yearPublished,
|
||||
title: this.entry.title,
|
||||
author: x,
|
||||
bookIndex: this.entry.bookIndex
|
||||
});
|
||||
},
|
||||
onTitle(x) {
|
||||
this.$emit('modelEdit', {
|
||||
id: this.entry.id,
|
||||
yearPublished: this.entry.yearPublished,
|
||||
title: x,
|
||||
author: this.entry.author,
|
||||
bookIndex: this.entry.bookIndex
|
||||
});
|
||||
},
|
||||
onYear(x) {
|
||||
this.$emit('modelEdit', {
|
||||
id: this.entry.id,
|
||||
yearPublished: x,
|
||||
title: this.entry.title,
|
||||
author: this.entry.author,
|
||||
bookIndex: this.entry.bookIndex
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.savebtn {
|
||||
margin: 15px;
|
||||
font-size: 24px;
|
||||
color: #2f2014;
|
||||
background-color: #c3ffa8;
|
||||
border: none;
|
||||
}
|
||||
.cancelbtn {
|
||||
margin: 6px;
|
||||
font-size: 15px;
|
||||
color: #2f2014;
|
||||
background-color: #ffa8a9;
|
||||
border: none;
|
||||
}
|
||||
.error {
|
||||
overflow: auto;
|
||||
max-width: 50%;
|
||||
max-height: 150px;
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="editor-line">
|
||||
<div class="label">
|
||||
<label v-if="this.label != null">{{label}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input
|
||||
:name="name"
|
||||
type="date"
|
||||
v-model="innerValue"
|
||||
:disabled="is_disabled" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: "DateEdit",
|
||||
props: {
|
||||
label: null,
|
||||
name: null,
|
||||
value: {
|
||||
type: Date,
|
||||
default() {
|
||||
return new Date();
|
||||
}
|
||||
},
|
||||
is_disabled: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
innerValue: new Date()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
handler: function (x) {
|
||||
this.$emit('valueChanged', x);
|
||||
},
|
||||
immediate: false
|
||||
},
|
||||
value: {
|
||||
handler: function(x) {
|
||||
this.innerValue = x.toISOString().slice(0, 10);
|
||||
},
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.label, .input {
|
||||
display: inline-block;
|
||||
}
|
||||
.label {
|
||||
margin-right: 5px;
|
||||
min-width: 39%;
|
||||
}
|
||||
.input {
|
||||
width: 60%;
|
||||
}
|
||||
.input > input {
|
||||
width: 100%;
|
||||
}
|
||||
.editor-line {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
border-top: dashed 1px #2f2014;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="editor-line">
|
||||
<div class="label">
|
||||
<label v-if="this.label != null">{{label}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<!-- <textarea :name="name" v-model="message_slug" :disabled="is_disabled" /> -->
|
||||
<VAceEditor
|
||||
ref="aceRef"
|
||||
v-model:value="message_slug"
|
||||
style="height: 300px"
|
||||
:options="{ useWorker: false }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from 'vue';
|
||||
import { VAceEditor } from 'vue3-ace-editor';
|
||||
|
||||
export default {
|
||||
name: "MultiLineEdit",
|
||||
data() {
|
||||
return {
|
||||
message_slug: ''
|
||||
}
|
||||
},
|
||||
components: {
|
||||
VAceEditor: VAceEditor
|
||||
},
|
||||
props: {
|
||||
label: null,
|
||||
name: null,
|
||||
value: '',
|
||||
is_disabled: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
message_slug: {
|
||||
handler: function (x) {
|
||||
this.$emit('valueChanged', x);
|
||||
},
|
||||
immediate: false
|
||||
},
|
||||
value: {
|
||||
handler: function (x) {
|
||||
this.message_slug = x;
|
||||
},
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.message_slug = this.value;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.label, .input {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-right: 5px;
|
||||
min-width: 39%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.input > textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.editor-line {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
border-top: dashed 1px #2f2014;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="editor-line">
|
||||
<div class="label">
|
||||
<label v-if="this.label != null">{{label}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input
|
||||
:name="name"
|
||||
type="text"
|
||||
v-model="innerValue"
|
||||
:disabled="is_disabled" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: "SingleLineEdit",
|
||||
props: {
|
||||
label: null,
|
||||
name: null,
|
||||
value: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
is_disabled: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
innerValue: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
handler: function (x) {
|
||||
this.$emit('valueChanged', x);
|
||||
},
|
||||
immediate: false
|
||||
},
|
||||
value: {
|
||||
handler: function(x) {
|
||||
this.innerValue = x;
|
||||
},
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.innerValue = this.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.label, .input {
|
||||
display: inline-block;
|
||||
}
|
||||
.label {
|
||||
margin-right: 5px;
|
||||
min-width: 39%;
|
||||
}
|
||||
.input {
|
||||
width: 60%;
|
||||
}
|
||||
.input > input {
|
||||
width: 100%;
|
||||
}
|
||||
.editor-line {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
border-top: dashed 1px #2f2014;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,2 @@
|
||||
import config from './infrastructure/config.js';
|
||||
export default config;
|
||||
@@ -0,0 +1,4 @@
|
||||
export default {
|
||||
API_URL: 'http://localhost:5222/api',
|
||||
DEFAULT_TITLE: 'Books'
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import { createWebHistory, createRouter } from 'vue-router';
|
||||
import { createStore } from 'vuex'
|
||||
|
||||
import Home from './components/Home.vue';
|
||||
import BookAdd from './components/admin/BookAdd.vue';
|
||||
import BookEdit from './components/admin/BookEdit.vue';
|
||||
|
||||
import config from './config.js'
|
||||
|
||||
import ace from 'ace-builds';
|
||||
|
||||
import modeJsonUrl from 'ace-builds/src-noconflict/mode-json?url';
|
||||
ace.config.setModuleUrl('ace/mode/json', modeJsonUrl);
|
||||
|
||||
import themeChromeUrl from 'ace-builds/src-noconflict/theme-chrome?url';
|
||||
ace.config.setModuleUrl('ace/theme/chrome', themeChromeUrl);
|
||||
|
||||
import workerJsonUrl from 'ace-builds/src-noconflict/worker-json?url'; // For vite
|
||||
ace.config.setModuleUrl('ace/mode/json_worker', workerJsonUrl);
|
||||
|
||||
let siteName = 'Not rocket science';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
component: Home,
|
||||
meta: { title: siteName }
|
||||
},
|
||||
{
|
||||
path: '/add-book',
|
||||
component: BookAdd,
|
||||
meta: { title: siteName + " EDIT" }
|
||||
},
|
||||
{
|
||||
path: '/edit-book/:id',
|
||||
component: BookEdit,
|
||||
meta: { title: siteName + " EDIT" }
|
||||
},
|
||||
];
|
||||
|
||||
const store = new createStore({
|
||||
state: {
|
||||
token: null,
|
||||
config: {
|
||||
API_URL: config.API_URL
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
setToken(state, token) {
|
||||
state.token = token
|
||||
},
|
||||
purgeToken(state) {
|
||||
state.token = null
|
||||
},
|
||||
initialiseStore(state) {
|
||||
var tokenInStorage = localStorage.getItem('token');
|
||||
if (tokenInStorage) {
|
||||
state.token = JSON.parse(tokenInStorage);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
store.subscribe((mutation, state) => {
|
||||
localStorage.setItem('token', JSON.stringify(state.token));
|
||||
});
|
||||
|
||||
const router = new createRouter({
|
||||
routes: routes,
|
||||
history: createWebHistory()
|
||||
});
|
||||
|
||||
router.afterEach((to) => {
|
||||
document.title = to.meta.title || config.DEFAULT_TITLE;
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
extends: App,
|
||||
beforeCreate() {
|
||||
this.$store.commit('initialiseStore')
|
||||
}
|
||||
})
|
||||
.use(router)
|
||||
.use(store)
|
||||
.mount('#app')
|
||||
Reference in New Issue
Block a user