mirror of https://github.com/4xmen/xshop.git
xController list + template-list
parent
5a3deaa61b
commit
56110bb254
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
interface SafeController
|
||||
{
|
||||
//
|
||||
public function createOrUpdate();
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div id="currency">
|
||||
<input @keyup="keyup" :id="xid" :placeholder="xtitle" :class="getClass" type="text" v-model="val">
|
||||
<input type="hidden" :name="xname" :value="noComma">
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
function commafy(num) {
|
||||
if (typeof num !== 'string') {
|
||||
return '';
|
||||
}
|
||||
let str = uncommafy(num.toString()).split('.');
|
||||
if (str[0].length >= 5) {
|
||||
str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
|
||||
}
|
||||
if (str[1] && str[1].length >= 5) {
|
||||
str[1] = str[1].replace(/(\d{3})/g, '$1 ');
|
||||
}
|
||||
return str.join('.');
|
||||
}
|
||||
|
||||
function uncommafy(txt) {
|
||||
return txt.split(',').join('');
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "curency-input",
|
||||
components: {},
|
||||
data: () => {
|
||||
return {
|
||||
val: ''
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Number, String],
|
||||
default: 'nop',
|
||||
},
|
||||
xname: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
xtitle: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
xvalue: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
xid: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
customClass: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
err: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
updateKey: {
|
||||
default: function () {
|
||||
|
||||
},
|
||||
type: Function,
|
||||
},
|
||||
|
||||
},
|
||||
mounted() {
|
||||
|
||||
if (this.modelValue !== 'nop') {
|
||||
|
||||
if (typeof this.modelValue == 'number') {
|
||||
this.val = commafy(this.modelValue.toString());
|
||||
} else {
|
||||
this.val = commafy(this.modelValue);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (typeof this.xvalue == 'number') {
|
||||
this.val = commafy(this.xvalue.toString());
|
||||
} else {
|
||||
this.val = commafy(this.xvalue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
computed: {
|
||||
noComma: function () {
|
||||
const n = uncommafy(this.val);
|
||||
if (this.modelValue != null) {
|
||||
this.$emit('update:modelValue', n);
|
||||
}
|
||||
return n;
|
||||
},
|
||||
getClass: function () {
|
||||
if (this.err == true || (typeof this.err == 'String' && this.err.trim() == '1')) {
|
||||
return 'form-control is-invalid ' + this.customClass;
|
||||
}
|
||||
return 'form-control ' + this.customClass;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
keyup: function () {
|
||||
this.val = commafy(this.val);
|
||||
if (typeof this.update === 'function') {
|
||||
this.update(this.updateKey, parseInt(this.noComma));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,23 +1,100 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">Example Component</div>
|
||||
<div id="example">
|
||||
<div class="p-2">
|
||||
<currency-input v-model="p"></currency-input>
|
||||
<span>
|
||||
{{ p }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<font-awesome-icon-picker v-model="f"></font-awesome-icon-picker>
|
||||
<span>
|
||||
{{ f }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<remix-icon-picker v-model="r"></remix-icon-picker>
|
||||
<span>
|
||||
{{ r }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
I'm an example component.
|
||||
<div class="p-2">
|
||||
<searchable-multi-select :items="cats" v-model="mss"></searchable-multi-select>
|
||||
<span>
|
||||
{{ mss }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<vue-date-range-picker v-model="dpr"></vue-date-range-picker>
|
||||
<span>
|
||||
{{ dpr }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<vue-date-time-picker v-model="dp"></vue-date-time-picker>
|
||||
<span>
|
||||
{{ dp }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<vue-date-time-picker v-model="dpt" :timepicker="true"></vue-date-time-picker>
|
||||
<span>
|
||||
{{ dpt }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<increment v-model="i"></increment>
|
||||
<span>
|
||||
{{ i }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
console.log('Component mounted.')
|
||||
import CurrencyInput from "./CurrencyInput.vue";
|
||||
import fontAwesomeIconPicker from "./FontAwesomeIconPicker.vue";
|
||||
import remixIconPicker from "./RemixIconPicker.vue";
|
||||
import searchableSelect from "./SearchableSelect.vue";
|
||||
import searchableMultiSelect from "./searchableMultiSelect.vue";
|
||||
import vueDateTimePicker from "./vueDateTimePicker.vue";
|
||||
import vueDateRangePicker from "./vueDateRangePicker.vue";
|
||||
import Increment from "./Increment.vue";
|
||||
export default {
|
||||
name: "example",
|
||||
components: {
|
||||
CurrencyInput, fontAwesomeIconPicker, remixIconPicker,Increment,
|
||||
searchableSelect, searchableMultiSelect, vueDateRangePicker, vueDateTimePicker
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
p: 100000,
|
||||
f: 'fa fa-user',
|
||||
r: 'ri-user-line',
|
||||
ss: 3,
|
||||
mss: [3,11,7],
|
||||
dpr:[],
|
||||
dp: null,
|
||||
dpt: null,
|
||||
i:7,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
cats: {
|
||||
default: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
computed: {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#example {
|
||||
|
||||
}
|
||||
</style>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div id="inc">
|
||||
<div class="btn btn-outline-danger" @click="dec">
|
||||
<i class="ri-subtract-line"></i>
|
||||
</div>
|
||||
<input type="text" class="form-control"
|
||||
v-model="val"
|
||||
@keyup.up="inc"
|
||||
@keyup.down="dec">
|
||||
<div class="btn btn-outline-success" @click="inc">
|
||||
<i class="ri-add-line"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "inc",
|
||||
components: {},
|
||||
data: () => {
|
||||
return {
|
||||
val: 0,
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Number, NaN],
|
||||
default: NaN,
|
||||
},
|
||||
xvalue: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
xmin: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
xmax: {
|
||||
type: Number,
|
||||
default: 100
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!isNaN(this.modelValue)) {
|
||||
this.val = this.modelValue;
|
||||
}else{
|
||||
this.val = this.xvalue;
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
inc(e){
|
||||
e.preventDefault();
|
||||
if (this.val < this.xmax){
|
||||
this.val++;
|
||||
}
|
||||
},
|
||||
dec(e){
|
||||
e.preventDefault();
|
||||
if (this.val > this.xmin){
|
||||
this.val--;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
val(newValue) {
|
||||
if (parseInt(this.val) > this.xmax){
|
||||
this.val = this.xmax;
|
||||
}
|
||||
if (parseInt(this.val) < this.xmin){
|
||||
this.val = this.xmin;
|
||||
}
|
||||
if (!isNaN(this.modelValue)) {
|
||||
this.$emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#inc {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
#inc .form-control {
|
||||
border-radius: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#inc div:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#inc div:last-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<div id="searchable-select">
|
||||
<div id="ss-modal" @click.self="hideModal" v-if="modalShow">
|
||||
<div id="ss-selector">
|
||||
<div class="p-2">
|
||||
<input type="text" class="form-control" v-model="q" :placeholder="xtitle">
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<ul id="vue-search-list" class="list-group list-group-flush">
|
||||
<template v-for="item in items">
|
||||
<li
|
||||
v-if="(q != '' && item[titleField].indexOf(q) != -1) || (q == '')"
|
||||
@click="selecting(item[valueField])"
|
||||
:class="`list-group-item ${val == item[valueField]?'selected':''}`">
|
||||
{{item[titleField]}}
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend" id="vue-search-btn" @click="showModal" >
|
||||
<span class="input-group-text" id="basic-addon1">
|
||||
<i class="ri-search-2-line"></i>
|
||||
</span>
|
||||
</div>
|
||||
<select :id="xid" :class="getClass" v-model="val" @change="select">
|
||||
<option value=""> {{xtitle}} </option>
|
||||
<option v-for="item in items"
|
||||
:value="item[valueField]"
|
||||
:selected="item[valueField] == val">
|
||||
{{item[titleField]}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" :name="xname" :value="val">
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "searchable-select",
|
||||
components: {},
|
||||
data: () => {
|
||||
return {
|
||||
modalShow: false, // modal handle
|
||||
q: '', // search query
|
||||
val:'',
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: {
|
||||
default: NaN,
|
||||
},
|
||||
items:{
|
||||
required: true,
|
||||
default: [],
|
||||
type: Array,
|
||||
},
|
||||
valueField:{
|
||||
default: 'id',
|
||||
type: String,
|
||||
},
|
||||
titleField:{
|
||||
default: 'title',
|
||||
type: String,
|
||||
},
|
||||
xname:{
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
xtitle:{
|
||||
default: "Please select",
|
||||
type: String,
|
||||
},
|
||||
xvalue:{
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
xid:{
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
customClass:{
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
err:{
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
onSelect:{
|
||||
default: function () {
|
||||
|
||||
},
|
||||
type: Function,
|
||||
},
|
||||
closeOnSelect: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!isNaN(this.modelValue)) {
|
||||
this.val = this.modelValue;
|
||||
}else{
|
||||
this.val = this.xvalue;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getClass: function () {
|
||||
if (this.err == true || ( typeof this.err == 'String' && this.err.trim() == '1' )) {
|
||||
return 'form-control is-invalid '+this.customClass;
|
||||
}
|
||||
return 'form-control '+this.customClass;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
selecting(i){
|
||||
this.val = i;
|
||||
this.onSelect(this.val);
|
||||
if (this.closeOnSelect){
|
||||
this.hideModal();
|
||||
}
|
||||
},
|
||||
select(){
|
||||
this.onSelect(this.val);
|
||||
},
|
||||
hideModal:function () {
|
||||
this.modalShow = false;
|
||||
},
|
||||
showModal(){
|
||||
this.modalShow = true;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
val(newValue) {
|
||||
if (!isNaN(this.modelValue)) {
|
||||
this.$emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#searchable-select {
|
||||
|
||||
}
|
||||
|
||||
#vue-search-btn{
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#vue-search-btn:hover .input-group-text{
|
||||
background: darkred;
|
||||
}
|
||||
|
||||
|
||||
#ss-modal{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
background: #00000033;
|
||||
backdrop-filter: blur(4px);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#ss-selector{
|
||||
height: 60vh;
|
||||
border-radius: 7px;
|
||||
min-width: 350px;
|
||||
width: 400px;
|
||||
max-width: 90%;
|
||||
margin: 20vh auto;
|
||||
background: #ffffff99;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#vue-search-list{
|
||||
height: calc(60vh - 90px);
|
||||
overflow-x: auto ;
|
||||
}
|
||||
|
||||
#vue-search-list .list-group-item:hover{
|
||||
background: deepskyblue;
|
||||
}
|
||||
#vue-search-list .list-group-item.selected{
|
||||
background: dodgerblue;
|
||||
color: white;;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<div id="searchable-select">
|
||||
<div id="ss-modal" @click.self="hideModal" v-if="modalShow">
|
||||
<div id="ss-selector">
|
||||
<div class="p-2">
|
||||
<input type="text" class="form-control" v-model="q" :placeholder="xtitle">
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<ul id="vue-search-list" class="list-group list-group-flush">
|
||||
<template v-for="item in items">
|
||||
<li
|
||||
v-if="(q != '' && item[titleField].toLocaleLowerCase().indexOf(q.toLocaleLowerCase()) != -1) || (q == '')"
|
||||
@click="selecting(item[valueField])"
|
||||
:class="`list-group-item ${val.indexOf(item[valueField]) !== -1?'selected':''}`">
|
||||
{{ item[titleField] }}
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend" id="vue-search-btn" @click="showModal">
|
||||
<span class="input-group-text" id="basic-addon1">
|
||||
<i class="ri-check-line"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-control" id="vue-lst" @click.self="showModal">
|
||||
<template v-for="item in items">
|
||||
<span class="tag-select" v-if=" val.indexOf(item[valueField]) !== -1" >
|
||||
{{ item[titleField] }}
|
||||
<i class="ri-close-line" @click="rem(item[valueField])"></i>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" :name="xname" :value="JSON.stringify(val)">
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "searchable-select",
|
||||
components: {},
|
||||
data: () => {
|
||||
return {
|
||||
modalShow: false, // modal handle
|
||||
q: '', // search query
|
||||
val: [],
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: {
|
||||
default: 'nop',
|
||||
},
|
||||
items: {
|
||||
required: true,
|
||||
default: [],
|
||||
type: Array,
|
||||
},
|
||||
valueField: {
|
||||
default: 'id',
|
||||
type: String,
|
||||
},
|
||||
titleField: {
|
||||
default: 'title',
|
||||
type: String,
|
||||
},
|
||||
xname: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
xtitle: {
|
||||
default: "Please select",
|
||||
type: String,
|
||||
},
|
||||
xvalue: {
|
||||
default: [],
|
||||
type: Array,
|
||||
},
|
||||
xid: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
customClass: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
err: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
onSelect: {
|
||||
default: function () {
|
||||
|
||||
},
|
||||
type: Function,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.modelValue != 'nop') {
|
||||
this.val = this.modelValue;
|
||||
}else{
|
||||
this.val = this.xvalue;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getClass: function () {
|
||||
if (this.err == true || (typeof this.err == 'String' && this.err.trim() == '1')) {
|
||||
return 'form-control is-invalid ' + this.customClass;
|
||||
}
|
||||
return 'form-control ' + this.customClass;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rem(i){
|
||||
this.val.splice(this.val.indexOf(i),1);
|
||||
this.onSelect(this.val,i);
|
||||
},
|
||||
selecting(i) {
|
||||
if (this.val.indexOf(i) == -1){
|
||||
this.val.push(i);
|
||||
}else{
|
||||
this.val.splice(this.val.indexOf(i),1);
|
||||
}
|
||||
this.onSelect(this.val,i);
|
||||
},
|
||||
select() {
|
||||
this.onSelect(this.val);
|
||||
},
|
||||
hideModal: function () {
|
||||
this.modalShow = false;
|
||||
},
|
||||
showModal() {
|
||||
this.modalShow = true;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
val(newValue) {
|
||||
if (this.modelValue != 'nop') {
|
||||
this.$emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#searchable-select {
|
||||
|
||||
}
|
||||
|
||||
#vue-search-btn {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#vue-search-btn:hover .input-group-text {
|
||||
background: deepskyblue;
|
||||
}
|
||||
|
||||
|
||||
#ss-modal {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
background: #00000033;
|
||||
backdrop-filter: blur(4px);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#ss-selector {
|
||||
height: 60vh;
|
||||
border-radius: 7px;
|
||||
min-width: 350px;
|
||||
width: 400px;
|
||||
max-width: 90%;
|
||||
margin: 20vh auto;
|
||||
background: #282D47;
|
||||
box-shadow: 0 0 4px gray;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#vue-search-list {
|
||||
height: calc(60vh - 90px);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#vue-search-list .list-group-item:hover {
|
||||
background: #6610F2;
|
||||
}
|
||||
|
||||
#vue-search-list .list-group-item.selected {
|
||||
background: darkred;
|
||||
color: white;;
|
||||
}
|
||||
|
||||
#vue-lst {
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.tag-select{
|
||||
display: inline-block;
|
||||
padding: 0 4px 0 20px ;
|
||||
margin-right: 5px;
|
||||
background: #282c34dd;
|
||||
color: white;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tag-select i{
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
.tag-select i:hover{
|
||||
color: red;
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,374 @@
|
||||
<template>
|
||||
<div id="vue-a1-calendar">
|
||||
<div v-if="pDate !== null">
|
||||
|
||||
<div class="cal-grid">
|
||||
<div @click="prvMonth">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M10.0859 12.0001L5.29297 16.793L6.70718 18.2072L12.9143 12.0001L6.70718 5.79297L5.29297 7.20718L10.0859 12.0001ZM17.0001 6.00008L17.0001 18.0001H15.0001L15.0001 6.00008L17.0001 6.00008Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
{{ pDate.persianMonthNames[cMonth] }}
|
||||
</div>
|
||||
<div>
|
||||
<input type="number" min="0" v-model="cYear">
|
||||
</div>
|
||||
<div @click="nxtMonth">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M13.9142 12.0001L18.7071 7.20718L17.2929 5.79297L11.0858 12.0001L17.2929 18.2072L18.7071 16.793L13.9142 12.0001ZM7 18.0001V6.00008H9V18.0001H7Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<table id="vue-cal-table">
|
||||
<tr>
|
||||
<th @click="selectByWeekDay(0)">
|
||||
{{ __("Saturday") }}
|
||||
</th>
|
||||
<th @click="selectByWeekDay(1)">
|
||||
{{ __("Sunday") }}
|
||||
</th>
|
||||
<th @click="selectByWeekDay(2)">
|
||||
{{ __("Monday") }}
|
||||
</th>
|
||||
<th @click="selectByWeekDay(3)">
|
||||
{{ __("Tuesday") }}
|
||||
</th>
|
||||
<th @click="selectByWeekDay(4)">
|
||||
{{ __("Wednesday") }}
|
||||
</th>
|
||||
<th @click="selectByWeekDay(5)">
|
||||
{{ __("Thursday") }}
|
||||
</th>
|
||||
<th @click="selectByWeekDay(6)">
|
||||
{{ __("Friday") }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr v-for="(tr,i) in monthArray">
|
||||
<td v-for="(td,j) in tr" @mouseenter="hover" @click="selecting(td)" :class="getClass(td)" :title="td.date+' '+td.pDate"
|
||||
:data-timpstamp="td.unixTimeStamp">
|
||||
{{ td.text }}
|
||||
<template v-if="texts[fixDate(td.date)] !== undefined">
|
||||
<div class="a1-badge" v-html="texts[fixDate(td.date)]"></div>
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="hidden" :value="JSON.stringify(selected)" :name="inputName">
|
||||
</div>
|
||||
<!-- <div>-->
|
||||
<!-- {{JSON.stringify(selected)}}-->
|
||||
<!-- </div>-->
|
||||
<div class="text-center mb-2">
|
||||
{{ currentDate }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import persianDate from './libs/persian-date.js';
|
||||
|
||||
export default {
|
||||
name: "vue-a1-calendar",
|
||||
components: {},
|
||||
data: () => {
|
||||
return {
|
||||
currentDate: '',
|
||||
pDate: null,
|
||||
cMonth: 4, // current month
|
||||
cYear: 1402, // current year
|
||||
selected: [],
|
||||
selectedTimeStamp: [],
|
||||
// translate simple
|
||||
strings: {
|
||||
Monday: 'دوشنبه',
|
||||
Tuesday: 'سهشنبه',
|
||||
Wednesday: 'چهارشنبه',
|
||||
Thursday: 'پنجشنبه',
|
||||
Friday: 'آدینه',
|
||||
Saturday: 'شنبه',
|
||||
Sunday: 'یکشنبه'
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
month: {
|
||||
default: 4,
|
||||
type: Number,
|
||||
},
|
||||
year: {
|
||||
default: 1370,
|
||||
type: Number,
|
||||
},
|
||||
texts: {
|
||||
default: {},
|
||||
type: Object,
|
||||
},
|
||||
onSelect: {
|
||||
type: Function,
|
||||
default: function () {
|
||||
|
||||
},
|
||||
},
|
||||
inputName: {
|
||||
type: String,
|
||||
default: 'vueCalendarValue'
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// initial values
|
||||
|
||||
this.cYear = this.year;
|
||||
this.cMonth = this.month;
|
||||
|
||||
this.pDate = new persianDate();
|
||||
|
||||
}
|
||||
,
|
||||
computed: {
|
||||
monthStart() {
|
||||
return this.pDate.getPersianWeekDay(this.cYear + '/' + this.cMonth + '/1');
|
||||
},
|
||||
monthArray() {
|
||||
let r = [[]];
|
||||
let counter = this.monthStart;
|
||||
|
||||
|
||||
for (let i = 1; i <= this.monthStart; i++) {
|
||||
r[0].push({
|
||||
date: '',
|
||||
text: '',
|
||||
unixTimeStamp: '',
|
||||
pDate: '',
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 1; i <= this.pDate.pGetLastDayMonth(this.cMonth, this.cYear); i++) {
|
||||
counter++;
|
||||
r[r.length - 1].push({
|
||||
date: this.pDate.imploiter(this.pDate.persian2Gregorian([this.cYear, this.cMonth, i]), '-'),
|
||||
text: this.pDate.parseHindi(i),
|
||||
unixTimeStamp: this.pDate.gDate2Timestamp(this.pDate.persian2Gregorian([this.cYear, this.cMonth, i])),
|
||||
pDate: this.pDate.parseHindi(this.cYear + '/' + this.cMonth + '/' + this.pDate.make2number(i)),
|
||||
});
|
||||
if (counter % 7 === 0) {
|
||||
r.push([]);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
,
|
||||
}
|
||||
,
|
||||
methods: {
|
||||
/**
|
||||
* Maybe one day translator
|
||||
* @param txt
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
__(txt) {
|
||||
return this.strings[txt];
|
||||
}
|
||||
,
|
||||
indexSelected(td) {
|
||||
return this.selectedTimeStamp.indexOf(td.unixTimeStamp);
|
||||
}
|
||||
,
|
||||
/**
|
||||
* td object toggle select
|
||||
* @param td
|
||||
*/
|
||||
selecting(td) {
|
||||
if (td.date === '') {
|
||||
return false;
|
||||
}
|
||||
if (this.indexSelected(td) === -1) {
|
||||
this.selected.push(td);
|
||||
this.selectedTimeStamp.push(td.unixTimeStamp);
|
||||
this.onSelect(td);
|
||||
} else {
|
||||
this.selected.splice(this.indexSelected(td), 1);
|
||||
this.selectedTimeStamp.splice(this.indexSelected(td), 1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* calculate class by props of td
|
||||
* @param td
|
||||
* @returns {string}
|
||||
*/
|
||||
getClass(td) {
|
||||
let cls = '';
|
||||
if (td.text === '') {
|
||||
return '';
|
||||
} else {
|
||||
cls += ' selectable-td';
|
||||
}
|
||||
|
||||
if (this.indexSelected(td) !== -1) {
|
||||
cls += ' selected-td';
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
,
|
||||
/**
|
||||
* Selected dates by day week
|
||||
* @param i
|
||||
*/
|
||||
selectByWeekDay(i) {
|
||||
for (const tr of this.monthArray) {
|
||||
if (tr[i] !== undefined) {
|
||||
this.selecting(tr[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
hover: function (e) {
|
||||
this.currentDate = e.target.getAttribute('title');
|
||||
},
|
||||
nxtMonth() {
|
||||
if (this.cMonth === 12) {
|
||||
this.cYear++;
|
||||
this.cMonth = 1;
|
||||
} else {
|
||||
this.cMonth++;
|
||||
}
|
||||
},
|
||||
prvMonth() {
|
||||
if (this.cMonth === 1) {
|
||||
this.cYear--;
|
||||
this.cMonth = 12;
|
||||
} else {
|
||||
this.cMonth--;
|
||||
}
|
||||
},
|
||||
fixDate(d) {
|
||||
let t = d.split('-');
|
||||
return parseInt(t[0]) + '-' + (parseInt(t[1]) < 10 ? '0' + parseInt(t[1]) : parseInt(t[1])) + '-' + (parseInt(t[2]) < 10 ? '0' + parseInt(t[2]) : parseInt(t[2]));
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#vue-a1-calendar {
|
||||
border: 1px solid #44444444;
|
||||
background: #fff;
|
||||
direction: rtl;
|
||||
user-select: none;
|
||||
font-family: 'Vazir', 'Vazirmatn', sans-serif;
|
||||
}
|
||||
|
||||
|
||||
#vue-cal-table {
|
||||
width: calc(100% - 2rem);
|
||||
border-spacing: 0;
|
||||
text-align: center;
|
||||
margin: 0 auto 1rem;
|
||||
}
|
||||
|
||||
|
||||
#vue-cal-table th {
|
||||
cursor: pointer;
|
||||
background: dodgerblue;
|
||||
border: 1px solid navy;
|
||||
border-right: 0;
|
||||
color: white;
|
||||
width: calc(100% / 7);
|
||||
}
|
||||
|
||||
|
||||
#vue-cal-table td, #vue-cal-table th {
|
||||
font-size: 22px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#vue-cal-table th:first-child, #vue-cal-table td:first-child {
|
||||
border-right: 1px solid navy;
|
||||
}
|
||||
|
||||
#vue-cal-table td {
|
||||
border: 1px solid navy;
|
||||
border-right: 0;
|
||||
border-top: 0;
|
||||
position: relative;
|
||||
height: 4rem;
|
||||
transition: 200ms;
|
||||
}
|
||||
|
||||
|
||||
#vue-cal-table td.selected-td {
|
||||
background: rgba(144, 238, 144, 0.82);
|
||||
}
|
||||
|
||||
#vue-cal-table td.selectable-td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cal-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
margin: 1rem;
|
||||
background: #00a2a2;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.cal-grid div {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
font-size: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cal-grid svg {
|
||||
width: 40px;
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.cal-grid div:first-child, .cal-grid div:last-child {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cal-grid input {
|
||||
font-size: 25px;
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.a1-badge {
|
||||
background: dodgerblue;
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
font-size: 14px;
|
||||
padding: 7px;
|
||||
opacity: .85;
|
||||
transition: 400ms;
|
||||
}
|
||||
|
||||
td.selectable-td:hover {
|
||||
background: #11111111;
|
||||
}
|
||||
|
||||
td.selectable-td.selected-td:hover {
|
||||
background: rgba(25, 107, 73, .5) !important;
|
||||
}
|
||||
|
||||
td:hover .a1-badge {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,7 @@
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('delete-confirm')) {
|
||||
if (!confirm('Are you sure you want to delete this item?')) { // WIP Need to translate
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
@ -1,5 +1,61 @@
|
||||
$pnl-brc-height: 50px;
|
||||
|
||||
#panel-breadcrumb{
|
||||
height: 50px;
|
||||
background: #00000033;
|
||||
height: $pnl-brc-height;
|
||||
background: #282d46;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
ul{
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-auto-columns: minmax(0, 1fr);
|
||||
grid-auto-flow: column;
|
||||
list-style: none;
|
||||
//border: 1px solid black;
|
||||
border-radius: 4px ;
|
||||
transition: .5s;
|
||||
li{
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
a,span{
|
||||
height: $pnl-brc-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
i{
|
||||
font-size: 25px;
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
width: 37px;
|
||||
height: 37px;
|
||||
border: 1px solid black;
|
||||
transform: rotate(45deg);
|
||||
position: absolute;
|
||||
inset-inline-start: -1.05rem;
|
||||
background: #282d46;
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
&:first-child:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
&:hover{
|
||||
background: #444e7c;
|
||||
background-image: url("../images/pattern.png");
|
||||
|
||||
& + li:before{
|
||||
background: #444e7c;
|
||||
background-image: url("../images/pattern.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
@if(session()->has('message'))
|
||||
<div class="alert alert-info">
|
||||
{{ session()->get('message') }}
|
||||
</div>
|
||||
@endif
|
||||
@foreach($errors->all() as $err)
|
||||
<div class="alert alert-danger">
|
||||
{{$err}}
|
||||
</div>
|
||||
@endforeach
|
@ -1,3 +1,28 @@
|
||||
<nav id="panel-breadcrumb">
|
||||
1
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#1">
|
||||
<i class="ri-home-3-line"></i>
|
||||
{{config('app.name')}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#2">
|
||||
<i class="ri-dashboard-3-line"></i>
|
||||
{{__("Dashboard")}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#3">
|
||||
<i class="ri-user-3-line"></i>
|
||||
{{__("Users")}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
<i class="ri-add-line"></i>
|
||||
{{__("Add new user")}}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
Loading…
Reference in New Issue