Initial commit

This commit is contained in:
Developer
2025-04-21 16:03:20 +02:00
commit 2832896157
22874 changed files with 3092801 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
<template>
<button type="button" @click="show = true">
<slot />
<teleport to="body">
<div v-if="show">
<div style="position: fixed; top: 0; right: 0; left: 0; bottom: 0; z-index: 99998;" @click="show = false" />
<div ref="dropdown" style="position: absolute; z-index: 99999;" @click.stop="show = autoClose ? false : true">
<slot name="dropdown" />
</div>
</div>
</teleport>
</button>
</template>
<script>
import Popper from 'popper.js'
export default {
props: {
placement: {
type: String,
default: 'bottom-end',
},
offset: {
type: String,
default: '0, 10',
},
boundary: {
type: String,
default: 'scrollParent',
},
autoClose: {
type: Boolean,
default: true,
},
},
data() {
return {
show: false,
}
},
watch: {
show(show) {
if (show) {
this.$nextTick(() => {
this.popper = new Popper(this.$el, this.$refs.dropdown, {
placement: this.placement,
modifiers: {
preventOverflow: { boundariesElement: this.boundary, padding: 0},
offset: {
enabled: true,
offset: this.offset
}
},
})
})
} else if (this.popper) {
setTimeout(() => this.popper.destroy(), 100)
}
},
},
mounted() {
document.addEventListener('keydown', (e) => {
if (e.keyCode === 27) {
this.show = false
}
})
},
}
</script>

View File

@@ -0,0 +1,27 @@
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<Menu as="div" class="relative inline-block text-left z-50">
<div>
<MenuButton class="transition inline-flex items-center justify-center shadow-classic2 rounded-full bg-orange text-white focus:outline-none w-12 h-12">
<svg class="h-4 w-4 md:h-5 md:w-5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
</MenuButton>
</div>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<slot />
</transition>
</Menu>
</template>
<script>
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
export default {
components: {
Menu,
MenuButton,
MenuItem,
MenuItems,
},
}
</script>

View File

@@ -0,0 +1,27 @@
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<Menu as="div" v-slot="{ open }" class="relative inline-block text-left z-50">
<div>
<MenuButton class="transition inline-flex items-center justify-center focus:outline-none ">
<svg :class="[open ? 'rotate-90' : 'rotate-180' ,'transform-gpu transition-transform transform w-6 h-6']">
<use xlink:href="#more-vertical"></use>
</svg>
</MenuButton>
</div>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<slot />
</transition>
</Menu>
</template>
<script>
import { Menu, MenuButton } from '@headlessui/vue'
export default {
components: {
Menu,
MenuButton,
},
}
</script>

View File

@@ -0,0 +1,53 @@
<template>
<div>
<label v-if="label" class="text-gray-light text-lg mb-2">{{ label }}:</label>
<div :class="{ error: error }">
<input ref="file" type="file" :accept="accept" class="hidden" @change="change">
<div v-if="!modelValue" class="py-2">
<button type="button" class="px-6 py-2 bg-indigo-300 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:outline-none focus:border-transparent rounded-sm text-sm text-white" @click="browse">
Выбрать файл
</button>
</div>
<div v-else class="flex justify-center items-start max-w-lg mt-3 p-2 border rounded-md">
<div class="flex-1 pr-1 text-gray">{{ modelValue.name }} <span class="text-xs text-gray-light">({{ filesize(modelValue.size) }})</span></div>
<button type="button" class="px-4 py-1 bg-indigo-300 hover:bg-indigo-100 rounded-sm text-xs font-medium text-white" @click="remove">
Удалить
</button>
</div>
</div>
<div v-if="error" class="text-red text-sm">{{ error }}</div>
</div>
</template>
<script>
export default {
props: {
modelValue: File,
label: String,
accept: String,
error: String,
},
watch: {
modelValue(value) {
if (!value) {
this.$refs.file.value = ''
}
},
},
methods: {
filesize(size) {
var i = Math.floor(Math.log(size) / Math.log(1024))
return (size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]
},
browse() {
this.$refs.file.click()
},
change(e) {
this.$emit('update:modelValue', e.target.files[0])
},
remove() {
this.$emit('update:modelValue', null)
},
},
}
</script>

View File

@@ -0,0 +1,111 @@
<template>
<div>
<label v-if="label" class="text-gray-light text-lg mb-2">{{ label }}:</label>
<div :class="{ error: error }">
<input
ref="file"
type="file"
:accept="accept"
multiple
class="hidden"
@change="change"
/>
<div v-if="!modelValue" class="py-2">
<button
type="button"
class="px-6 py-2 bg-indigo-300 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:outline-none focus:border-transparent rounded-sm text-sm text-white"
@click="browse"
>
Выбрать файлы
</button>
</div>
<div v-else class="flex flex-col justify-center max-w-lg mt-3 p-2 border rounded-md">
<div
v-for="(file, index) in modelValue"
:key="index"
class="flex items-center justify-between p-1"
>
<div class="flex-1 pr-1 text-gray">
{{ file.name }}
<span class="text-xs text-gray-light"
>({{ filesize(file.size) }})</span
>
</div>
<button
type="button"
class="px-4 py-1 bg-indigo-300 hover:bg-indigo-100 rounded-sm text-xs font-medium text-white"
@click="remove(index)"
>
Удалить
</button>
</div>
</div>
</div>
<div v-if="error" class="text-red text-sm">{{ error }}</div>
</div>
</template>
<script>
import helper from "@/includes/helper";
export default {
props: {
modelValue: FileList,
label: String,
accept: String,
error: String,
},
emits:['fileTime', 'update:modelValue'],
methods: {
filesize(size) {
var i = Math.floor(Math.log(size) / Math.log(1024));
return (
(size / Math.pow(1024, i)).toFixed(2) * 1 +
" " +
["B", "kB", "MB", "GB", "TB"][i]
);
},
browse() {
this.$refs.file.click();
},
change(e) {
const files = Array.from(e.target.files);
const that = this;
files.map(function (file) {
if(file.type === "audio/mpeg"){
var audioCtx = new (AudioContext || webkitAudioContext)();
var readerAudio = new FileReader();
readerAudio.readAsArrayBuffer(file);
readerAudio.onload = function(ev) {
audioCtx.decodeAudioData(ev.target.result).then(function(buffer) {
that.$emit("fileTime", file.name + ',' + that.formatTimeSong(buffer.duration));
});
};
}
});
this.$emit("update:modelValue", e.target.files);
},
formatTimeSong(value) {
return helper.formatTime(value);
},
remove(file_index) {
const dt = new DataTransfer();
let files = Array.from(this.$refs.file.files);
files.map(function (file, index) {
if (index !== file_index) {
dt.items.add(file);
}
});
if (dt.files.length) {
this.$refs.file.files = dt.files;
this.$emit("update:modelValue", dt.files);
} else {
this.$refs.file.files = null;
this.$emit("update:modelValue", null);
}
},
},
};
</script>

View File

@@ -0,0 +1,14 @@
<template>
<button :disabled="loading">
<div v-if="loading" class="btn-spinner mr-2" />
<slot />
</button>
</template>
<script>
export default {
props: {
loading: Boolean,
},
}
</script>

View File

@@ -0,0 +1,37 @@
<template>
<label class="text-gray-light text-lg mb-2" v-if="label" :for="id">{{ label }}:</label>
<input :id="id" ref="input" v-bind="$attrs" :class="{ error: error }" :type="type" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
<div v-if="error">{{ error }}</div>
</template>
<script>
export default {
props: {
id: {
type: String,
default() {
return `select-input-${Math.random() * 1000}`;
},
},
type: {
type: String,
default: 'text',
},
modelValue: String,
label: String,
error: String,
},
emits: ['update:modelValue'],
methods: {
focus() {
this.$refs.input.focus()
},
select() {
this.$refs.input.select()
},
setSelectionRange(start, end) {
this.$refs.input.setSelectionRange(start, end)
},
},
}
</script>

View File

@@ -0,0 +1,30 @@
<template>
<label class="text-gray-light text-lg mb-2" v-if="label" :for="id">{{ label }}:</label>
<textarea :id="id" ref="input" v-bind="$attrs" :class="{ error: error }" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
<div v-if="error" class="text-red text-sm">{{ error }}</div>
</template>
<script>
export default {
inheritAttrs: false,
props: {
id: {
type: String,
default() {
return `select-input-${Math.random() * 1000}`;
},
},
modelValue: String,
label: String,
error: String,
},
methods: {
focus() {
this.$refs.input.focus()
},
select() {
this.$refs.input.select()
},
},
}
</script>

View File

@@ -0,0 +1,40 @@
<template>
<SwitchGroup @click="clicked" as="div" class="flex items-center">
<Switch v-model="enabled" :class="[enabled ? 'bg-orange' : 'bg-indigo-200', 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500']">
<!-- <span class="sr-only">Use setting</span> -->
<span aria-hidden="true" :class="[enabled ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200']" />
</Switch>
<SwitchLabel as="span" class="ml-3">
<span v-show="!enabled" class="text-sm text-white">{{textin}}</span>
<span v-show="enabled" class="text-sm text-white">{{textout}}</span>
</SwitchLabel>
</SwitchGroup>
</template>
<script>
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue'
export default {
components: {
Switch,
SwitchGroup,
SwitchLabel,
},
emits: ['clicked'],
props: {
textin: String,
textout: String,
user_id: Number,
enabled: {
type: Boolean,
default: false
},
},
methods: {
clicked(){
this.$emit('clicked', this.user_id);
}
}
}
</script>