Поддержка тегов для форм и обработка close события
This commit is contained in:
@@ -8,7 +8,7 @@ import PageBody from "../Components/Page/PageBody.vue";
|
|||||||
import Badge from "../Components/Badge/Badge.vue";
|
import Badge from "../Components/Badge/Badge.vue";
|
||||||
import {Link, router} from "@inertiajs/vue3"
|
import {Link, router} from "@inertiajs/vue3"
|
||||||
import AnimateSearch from "../Components/Input/Search/AnimateSearch.vue";
|
import AnimateSearch from "../Components/Input/Search/AnimateSearch.vue";
|
||||||
import {ref, watch} from "vue";
|
import {nextTick, ref, watch} from "vue";
|
||||||
import Button from "../Components/Button/Button.vue";
|
import Button from "../Components/Button/Button.vue";
|
||||||
import ImportDocumentModal from "./Parts/ImportDocumentModal.vue";
|
import ImportDocumentModal from "./Parts/ImportDocumentModal.vue";
|
||||||
import EditDocumentModal from "./Parts/EditDocumentModal.vue";
|
import EditDocumentModal from "./Parts/EditDocumentModal.vue";
|
||||||
@@ -27,12 +27,16 @@ const showModalEdit = ref(false)
|
|||||||
const editTemplateId = ref(null)
|
const editTemplateId = ref(null)
|
||||||
const vertical = ref(true)
|
const vertical = ref(true)
|
||||||
|
|
||||||
watch(() => searchValue.value, (search) => {
|
const getTemplates = async (search) => {
|
||||||
axios.post('/api/templates/search', {
|
await axios.post('/api/templates/search', {
|
||||||
search
|
search
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
workTemplates.value = res.data.templates
|
workTemplates.value = res.data.templates
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => searchValue.value, async (search) => {
|
||||||
|
await getTemplates()
|
||||||
})
|
})
|
||||||
|
|
||||||
const onChangeLayoutList = () => {
|
const onChangeLayoutList = () => {
|
||||||
@@ -42,10 +46,27 @@ const onShowModalEdit = (template) => {
|
|||||||
showModalEdit.value = true
|
showModalEdit.value = true
|
||||||
editTemplateId.value = template.id
|
editTemplateId.value = template.id
|
||||||
}
|
}
|
||||||
const onCloseModalEdit = () => {
|
const onCloseModalEdit = async () => {
|
||||||
|
router.reload({
|
||||||
|
only: ['templates'],
|
||||||
|
onSuccess: () => {
|
||||||
|
console.log(props)
|
||||||
|
workTemplates.value = [...props.templates]
|
||||||
|
}
|
||||||
|
})
|
||||||
showModalEdit.value = false
|
showModalEdit.value = false
|
||||||
editTemplateId.value = null
|
editTemplateId.value = null
|
||||||
}
|
}
|
||||||
|
const onCloseModalImport = async () => {
|
||||||
|
showModalImport.value = false
|
||||||
|
router.reload({
|
||||||
|
only: ['templates'],
|
||||||
|
onSuccess: () => {
|
||||||
|
console.log(props)
|
||||||
|
workTemplates.value = [...props.templates]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -137,11 +158,14 @@ const onCloseModalEdit = () => {
|
|||||||
<!-- </Button>-->
|
<!-- </Button>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<template v-slot:footer>
|
<template v-slot:footer>
|
||||||
<div class="flex gap-x-1.5">
|
<div v-if="template.tags.length > 0" v-for="tag in template.tags" class="flex gap-x-1.5">
|
||||||
<Badge variant="primary">
|
<Badge variant="primary">
|
||||||
Экономический отдел
|
{{ tag }}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="h-[24px]">
|
||||||
|
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -159,7 +183,7 @@ const onCloseModalEdit = () => {
|
|||||||
</div>
|
</div>
|
||||||
</PageBody>
|
</PageBody>
|
||||||
</Page>
|
</Page>
|
||||||
<ImportDocumentModal v-model:open="showModalImport" @close="showModalImport = false" />
|
<ImportDocumentModal v-model:open="showModalImport" @close="onCloseModalImport" />
|
||||||
<EditDocumentModal v-model:open="showModalEdit" :templateId="editTemplateId" @close="onCloseModalEdit" />
|
<EditDocumentModal v-model:open="showModalEdit" :templateId="editTemplateId" @close="onCloseModalEdit" />
|
||||||
</Workspace>
|
</Workspace>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import Card from "../../Components/Card/Card.vue";
|
|||||||
import FormGroup from "../../Components/Form/FormGroup.vue";
|
import FormGroup from "../../Components/Form/FormGroup.vue";
|
||||||
import Calendar from "../../Components/Calendar/Calendar.vue";
|
import Calendar from "../../Components/Calendar/Calendar.vue";
|
||||||
import Collapsible from "../../Components/Collapsible/Collapsible.vue";
|
import Collapsible from "../../Components/Collapsible/Collapsible.vue";
|
||||||
|
import TagsInput from "../../Components/Input/TagsInput.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
templateId: {
|
templateId: {
|
||||||
@@ -51,6 +52,7 @@ const loadTemplateData = async (templateId) => {
|
|||||||
uploadForm.value.name = template.name
|
uploadForm.value.name = template.name
|
||||||
uploadForm.value.description = template.description || ''
|
uploadForm.value.description = template.description || ''
|
||||||
uploadForm.value.variables = template.variables || []
|
uploadForm.value.variables = template.variables || []
|
||||||
|
uploadForm.value.tags = template.tags || []
|
||||||
isTemplateLoaded.value = true
|
isTemplateLoaded.value = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -62,6 +64,8 @@ const { formData: uploadForm, errors, reset, loading, submit, setFile: setFileTo
|
|||||||
variables: []
|
variables: []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['close'])
|
||||||
|
|
||||||
watch(() => stage.value, (value) => {
|
watch(() => stage.value, (value) => {
|
||||||
if (value === 'variables') description.value = 'Опишите найденные в документе переменные'
|
if (value === 'variables') description.value = 'Опишите найденные в документе переменные'
|
||||||
else description.value = ''
|
else description.value = ''
|
||||||
@@ -128,7 +132,7 @@ const submitForm = () => {
|
|||||||
uploadForm.value.variables = [...templateVariables.value]
|
uploadForm.value.variables = [...templateVariables.value]
|
||||||
router.post(`/templates/update`, uploadForm.value, {
|
router.post(`/templates/update`, uploadForm.value, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
open.value = false
|
emits('close')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -137,6 +141,7 @@ const afterCloseModal = () => {
|
|||||||
stage.value = 'upload'
|
stage.value = 'upload'
|
||||||
uploadedFile.value = null
|
uploadedFile.value = null
|
||||||
reset()
|
reset()
|
||||||
|
emits('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
const widthOfStage = computed(() => {
|
const widthOfStage = computed(() => {
|
||||||
@@ -415,6 +420,7 @@ const getRootIndex = (variable) => {
|
|||||||
<div v-if="stage === 'upload'" class="flex flex-col gap-y-1">
|
<div v-if="stage === 'upload'" class="flex flex-col gap-y-1">
|
||||||
<Input v-model:value="uploadForm.name" label="Наименование" />
|
<Input v-model:value="uploadForm.name" label="Наименование" />
|
||||||
<Input v-model:value="uploadForm.description" label="Описание" />
|
<Input v-model:value="uploadForm.description" label="Описание" />
|
||||||
|
<TagsInput v-model="uploadForm.tags" label="Теги" />
|
||||||
<div class="mt-2.5">
|
<div class="mt-2.5">
|
||||||
<Button block text-align="center" v-if="!isUpdateFile" @click="isUpdateFile = true">
|
<Button block text-align="center" v-if="!isUpdateFile" @click="isUpdateFile = true">
|
||||||
Загрузить обновленный шаблон
|
Загрузить обновленный шаблон
|
||||||
|
|||||||
@@ -11,12 +11,24 @@ import {useApiForm} from "../../Composables/useApiForm.js";
|
|||||||
import Card from "../../Components/Card/Card.vue";
|
import Card from "../../Components/Card/Card.vue";
|
||||||
import FormGroup from "../../Components/Form/FormGroup.vue";
|
import FormGroup from "../../Components/Form/FormGroup.vue";
|
||||||
import Calendar from "../../Components/Calendar/Calendar.vue";
|
import Calendar from "../../Components/Calendar/Calendar.vue";
|
||||||
|
import Collapsible from "../../Components/Collapsible/Collapsible.vue";
|
||||||
|
import TagsInput from "../../Components/Input/TagsInput.vue";
|
||||||
|
|
||||||
const open = defineModel('open')
|
const open = defineModel('open')
|
||||||
const stage = ref('upload')
|
const stage = ref('upload')
|
||||||
const description = ref('')
|
const description = ref('')
|
||||||
const uploadedFile = ref(null)
|
const uploadedFile = ref(null)
|
||||||
const templateVariables = ref(null)
|
const templateVariables = ref(null)
|
||||||
|
const formTitle = ref(null)
|
||||||
|
const isTemplateLoaded = ref(false)
|
||||||
|
const isUpdateFile = ref(false)
|
||||||
|
|
||||||
|
// Drag and drop состояния
|
||||||
|
const dragItem = ref(null)
|
||||||
|
const dragOverItem = ref(null)
|
||||||
|
const dragOverGroup = ref(null)
|
||||||
|
const dragSource = ref(null)
|
||||||
|
const lastDropPosition = ref({ targetIndex: null, group: null }) // Добавляем
|
||||||
|
|
||||||
const { formData: uploadForm, errors, reset, loading, submit, setFile: setFileToForm } = useApiForm({
|
const { formData: uploadForm, errors, reset, loading, submit, setFile: setFileToForm } = useApiForm({
|
||||||
name: '',
|
name: '',
|
||||||
@@ -30,6 +42,8 @@ watch(() => stage.value, (value) => {
|
|||||||
else description.value = ''
|
else description.value = ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['close'])
|
||||||
|
|
||||||
const uploadFile = async () => {
|
const uploadFile = async () => {
|
||||||
try {
|
try {
|
||||||
setFileToForm('doc_file', uploadedFile.value)
|
setFileToForm('doc_file', uploadedFile.value)
|
||||||
@@ -80,9 +94,10 @@ const variableTypes = [
|
|||||||
|
|
||||||
const submitForm = () => {
|
const submitForm = () => {
|
||||||
uploadForm.value.file = uploadedFile.value
|
uploadForm.value.file = uploadedFile.value
|
||||||
router.post('/templates/import', uploadForm.value, {
|
uploadForm.value.variables = [...templateVariables.value]
|
||||||
|
router.post(`/templates/import`, uploadForm.value, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
open.value = false
|
emits('close')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -91,6 +106,7 @@ const afterCloseModal = () => {
|
|||||||
stage.value = 'upload'
|
stage.value = 'upload'
|
||||||
uploadedFile.value = null
|
uploadedFile.value = null
|
||||||
reset()
|
reset()
|
||||||
|
emits('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
const widthOfStage = computed(() => {
|
const widthOfStage = computed(() => {
|
||||||
@@ -103,7 +119,18 @@ const calendarNowDate = ref(new Date())
|
|||||||
|
|
||||||
const selectedVariable = ref()
|
const selectedVariable = ref()
|
||||||
const activeVariable = computed(() => {
|
const activeVariable = computed(() => {
|
||||||
return uploadForm.value.variables.find(itm => itm.name === selectedVariable.value?.name)
|
for (const item of uploadForm.value.variables) {
|
||||||
|
if (item.name === selectedVariable.value?.name) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
else if (Array.isArray(item.children)) {
|
||||||
|
const foundChild = item.children.find(child => child.name === selectedVariable.value?.name);
|
||||||
|
if (foundChild) {
|
||||||
|
return foundChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputVariableOptions = (value) => {
|
const inputVariableOptions = (value) => {
|
||||||
@@ -121,6 +148,218 @@ const clickToVariable = (variable) => {
|
|||||||
selectedVariable.value = variable
|
selectedVariable.value = variable
|
||||||
calendarNowDate.value = new Date()
|
calendarNowDate.value = new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createVariableGroup = () => {
|
||||||
|
const groupCount = templateVariables.value.filter(itm => itm.isGroup === true)
|
||||||
|
const group = {
|
||||||
|
label: `Группа ${groupCount.length + 1}`,
|
||||||
|
children: [],
|
||||||
|
isGroup: true,
|
||||||
|
type: 'group',
|
||||||
|
name: `group-${Date.now()}`
|
||||||
|
}
|
||||||
|
templateVariables.value.push(group)
|
||||||
|
uploadForm.value.variables = [...templateVariables.value]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drag and Drop функции
|
||||||
|
const dragStart = (event, variable, index, sourceGroup = null) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
dragSource.value = sourceGroup ? 'group' : 'root'
|
||||||
|
dragItem.value = { variable, index, sourceGroup }
|
||||||
|
|
||||||
|
// Делаем оригинальный элемент полупрозрачным
|
||||||
|
event.currentTarget.style.opacity = '0.4'
|
||||||
|
|
||||||
|
event.dataTransfer.effectAllowed = 'move'
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragEnd = () => {
|
||||||
|
// Восстанавливаем прозрачность всех элементов
|
||||||
|
document.querySelectorAll('.drag-handle').forEach(el => {
|
||||||
|
el.style.opacity = '1'
|
||||||
|
})
|
||||||
|
|
||||||
|
dragItem.value = null
|
||||||
|
dragOverItem.value = null
|
||||||
|
dragOverGroup.value = null
|
||||||
|
dragSource.value = null
|
||||||
|
lastDropPosition.value = { targetIndex: null, group: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragOver = (event, targetIndex, group = null) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
console.log('dragOver:', { targetIndex, group, dragItem: dragItem.value })
|
||||||
|
|
||||||
|
// Сохраняем последнюю позицию
|
||||||
|
lastDropPosition.value = { targetIndex, group }
|
||||||
|
|
||||||
|
// Не показывать индикатор если перетаскиваемый элемент тот же самый
|
||||||
|
if (dragItem.value) {
|
||||||
|
const isSameElement = group ?
|
||||||
|
(dragItem.value.sourceGroup === group && dragItem.value.index === targetIndex) :
|
||||||
|
(dragItem.value.index === targetIndex)
|
||||||
|
|
||||||
|
if (isSameElement) {
|
||||||
|
dragOverItem.value = null
|
||||||
|
dragOverGroup.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dragOverItem.value = targetIndex
|
||||||
|
dragOverGroup.value = group
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGlobalDrop = (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
if (!dragItem.value) return
|
||||||
|
|
||||||
|
// Используем последнюю сохраненную позицию для глобального дропа
|
||||||
|
const { targetIndex, group } = lastDropPosition.value
|
||||||
|
|
||||||
|
if (targetIndex !== null) {
|
||||||
|
drop(event, targetIndex, group)
|
||||||
|
} else {
|
||||||
|
// Если позиция не определена, сбрасываем
|
||||||
|
dragEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const drop = (event, targetIndex, group = null) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
if (!dragItem.value) return
|
||||||
|
|
||||||
|
// Используем последнюю сохраненную позицию, если текущие параметры null
|
||||||
|
const finalTargetIndex = targetIndex ?? lastDropPosition.value.targetIndex
|
||||||
|
const finalGroup = group ?? lastDropPosition.value.group
|
||||||
|
|
||||||
|
console.log('=== DROP DEBUG ===')
|
||||||
|
console.log('dragItem:', dragItem.value)
|
||||||
|
console.log('source variable:', dragItem.value.variable)
|
||||||
|
console.log('isGroup:', dragItem.value.variable.isGroup)
|
||||||
|
console.log('sourceGroup:', dragItem.value.sourceGroup)
|
||||||
|
console.log('finalTargetIndex:', finalTargetIndex)
|
||||||
|
console.log('finalGroup:', finalGroup)
|
||||||
|
console.log('==================')
|
||||||
|
|
||||||
|
console.log('drop:', {
|
||||||
|
targetIndex,
|
||||||
|
group,
|
||||||
|
finalTargetIndex,
|
||||||
|
finalGroup,
|
||||||
|
lastDropPosition: lastDropPosition.value,
|
||||||
|
dragItem: dragItem.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const sourceVariable = dragItem.value.variable
|
||||||
|
const sourceGroup = dragItem.value.sourceGroup
|
||||||
|
|
||||||
|
// Обработка для targetIndex = -2 (начало группы)
|
||||||
|
const actualTargetIndex = targetIndex === -2 ? 0 : targetIndex
|
||||||
|
|
||||||
|
// Если перетаскиваем ГРУППУ (изменение порядка групп)
|
||||||
|
if (sourceVariable.isGroup && !group) {
|
||||||
|
const sourceIndex = dragItem.value.index
|
||||||
|
if (sourceIndex !== actualTargetIndex) {
|
||||||
|
const [removed] = templateVariables.value.splice(sourceIndex, 1)
|
||||||
|
|
||||||
|
// Корректируем targetIndex если удалили элемент перед целевой позицией
|
||||||
|
const adjustedTargetIndex = sourceIndex < actualTargetIndex
|
||||||
|
? actualTargetIndex - 1
|
||||||
|
: actualTargetIndex
|
||||||
|
|
||||||
|
templateVariables.value.splice(adjustedTargetIndex, 0, removed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Если перетаскиваем из группы в корень
|
||||||
|
else if (sourceGroup && !group) {
|
||||||
|
// Удаляем из группы
|
||||||
|
const sourceIndex = sourceGroup.children.findIndex(v => v.name === sourceVariable.name)
|
||||||
|
if (sourceIndex > -1) {
|
||||||
|
sourceGroup.children.splice(sourceIndex, 1)
|
||||||
|
// Добавляем в корень
|
||||||
|
if (actualTargetIndex === -1) {
|
||||||
|
templateVariables.value.push(sourceVariable)
|
||||||
|
} else {
|
||||||
|
templateVariables.value.splice(actualTargetIndex, 0, sourceVariable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Если перетаскиваем из корня в группу
|
||||||
|
else if (!sourceGroup && group && !sourceVariable.isGroup) {
|
||||||
|
// Удаляем из корня
|
||||||
|
const sourceIndex = templateVariables.value.findIndex(v => v.name === sourceVariable.name)
|
||||||
|
if (sourceIndex > -1) {
|
||||||
|
templateVariables.value.splice(sourceIndex, 1)
|
||||||
|
// Добавляем в группу
|
||||||
|
if (!group.children) group.children = []
|
||||||
|
if (actualTargetIndex === -1) {
|
||||||
|
group.children.push(sourceVariable)
|
||||||
|
} else {
|
||||||
|
group.children.splice(actualTargetIndex, 0, sourceVariable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Если перетаскиваем из группы в другую группу
|
||||||
|
else if (sourceGroup && group && sourceGroup !== group && !sourceVariable.isGroup) {
|
||||||
|
// Удаляем из исходной группы
|
||||||
|
const sourceIndex = sourceGroup.children.findIndex(v => v.name === sourceVariable.name)
|
||||||
|
if (sourceIndex > -1) {
|
||||||
|
sourceGroup.children.splice(sourceIndex, 1)
|
||||||
|
// Добавляем в целевую группу
|
||||||
|
if (!group.children) group.children = []
|
||||||
|
if (actualTargetIndex === -1) {
|
||||||
|
group.children.push(sourceVariable)
|
||||||
|
} else {
|
||||||
|
group.children.splice(actualTargetIndex, 0, sourceVariable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Если перетаскиваем между элементами в корне
|
||||||
|
else if (!sourceGroup && !group && !sourceVariable.isGroup) {
|
||||||
|
const sourceIndex = dragItem.value.index
|
||||||
|
const [removed] = templateVariables.value.splice(sourceIndex, 1)
|
||||||
|
templateVariables.value.splice(actualTargetIndex, 0, removed)
|
||||||
|
}
|
||||||
|
// Если перетаскиваем внутри одной группы
|
||||||
|
else if (sourceGroup && group && sourceGroup === group && !sourceVariable.isGroup) {
|
||||||
|
const sourceIndex = dragItem.value.index
|
||||||
|
const [removed] = group.children.splice(sourceIndex, 1)
|
||||||
|
group.children.splice(actualTargetIndex, 0, removed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сбрасываем состояния
|
||||||
|
dragItem.value = null
|
||||||
|
dragOverItem.value = null
|
||||||
|
dragOverGroup.value = null
|
||||||
|
dragSource.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление группы
|
||||||
|
const removeGroup = (group) => {
|
||||||
|
const index = templateVariables.value.findIndex(v => v === group)
|
||||||
|
if (index > -1) {
|
||||||
|
// Перемещаем детей группы в корень
|
||||||
|
if (group.children && group.children.length) {
|
||||||
|
templateVariables.value.splice(index, 1, ...group.children)
|
||||||
|
} else {
|
||||||
|
templateVariables.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Редактирование группы
|
||||||
|
const editGroup = (group) => {
|
||||||
|
selectedVariable.value = group
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -128,21 +367,219 @@ const clickToVariable = (variable) => {
|
|||||||
<div v-if="stage === 'upload'" class="flex flex-col gap-y-1">
|
<div v-if="stage === 'upload'" class="flex flex-col gap-y-1">
|
||||||
<Input v-model:value="uploadForm.name" label="Наименование" />
|
<Input v-model:value="uploadForm.name" label="Наименование" />
|
||||||
<Input v-model:value="uploadForm.description" label="Описание" />
|
<Input v-model:value="uploadForm.description" label="Описание" />
|
||||||
<FileUpload class="mt-2.5" v-model:file="uploadedFile" accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
|
<TagsInput v-model="uploadForm.tags" label="Теги" />
|
||||||
|
<div class="mt-2.5">
|
||||||
|
<FileUpload v-model:file="uploadedFile" accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="grid grid-cols-[280px_1fr] gap-x-2 ">
|
<div v-else class="grid grid-cols-[280px_1fr] gap-x-2 ">
|
||||||
<Card header="Переменные документа">
|
<Card header="Переменные документа">
|
||||||
<div class="flex flex-col gap-y-0.5 max-h-[420px] pr-2 overflow-y-auto">
|
<Button class="mb-2 -mt-2" block @click="createVariableGroup" icon-left>
|
||||||
<Button v-for="variable in templateVariables" block @click="clickToVariable(variable)">
|
<template #icon>
|
||||||
{{ variable.label }}
|
<svg 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" class="h-4 w-4">
|
||||||
</Button>
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M4 4h6v6h-6zm10 0h6v6h-6zm-10 10h6v6h-6zm10 3h6m-3 -3v6" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
Добавить группу
|
||||||
|
</Button>
|
||||||
|
<div class="flex flex-col gap-y-0.5 max-h-[396px] overflow-y-auto">
|
||||||
|
<template v-for="(variable, index) in templateVariables" :key="variable.name || variable.label">
|
||||||
|
|
||||||
|
<!-- Визуальный дубликат перетаскиваемого элемента -->
|
||||||
|
<div v-if="dragItem && dragOverItem === index && !dragOverGroup && dragItem.variable !== variable"
|
||||||
|
@dragover="dragOver($event, index)"
|
||||||
|
@drop="drop($event, index)"
|
||||||
|
class="opacity-60 transform">
|
||||||
|
<Button
|
||||||
|
icon-left
|
||||||
|
block
|
||||||
|
class="cursor-grabbing bg-blue-50 border-blue-200"
|
||||||
|
disabled>
|
||||||
|
<template #icon>
|
||||||
|
<svg 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" class="h-4 w-4">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
{{ dragItem.variable.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="variable.isGroup"
|
||||||
|
class="pl-px pt-px pb-px pr-px"
|
||||||
|
:class="{ 'bg-blue-50/10 rounded': dragOverGroup === variable }"
|
||||||
|
@dragover="dragOver($event, index)"
|
||||||
|
@drop="drop($event, index)">
|
||||||
|
<Collapsible class="drag-handle"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="dragStart($event, variable, index)"
|
||||||
|
@dragend="dragEnd">
|
||||||
|
<template #icon>
|
||||||
|
<div class="cursor-grab active:cursor-grabbing">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between w-full">
|
||||||
|
<div class="flex items-center flex-1 drag-handle">
|
||||||
|
<span class="block text-sm font-medium truncate">{{variable.label}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #header-extra>
|
||||||
|
<button @click="editGroup(variable)" class="text-white hover:text-zinc-300 flex-shrink-0">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
|
||||||
|
<path d="M13.5 6.5l4 4" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button @click="removeGroup(variable)" class="text-red-500 hover:text-red-700 flex-shrink-0">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M3 6h18"></path>
|
||||||
|
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||||
|
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<div class="min-h-2 flex flex-col gap-y-0.5"
|
||||||
|
@dragover="dragOver($event, -2, variable)"
|
||||||
|
@drop="drop($event, -2, variable)">
|
||||||
|
|
||||||
|
<template v-if="variable.children && variable.children.length > 0">
|
||||||
|
<div v-for="(child, childIndex) in variable.children"
|
||||||
|
:key="child.name"
|
||||||
|
class="relative"
|
||||||
|
@dragover="dragOver($event, childIndex, variable)"
|
||||||
|
@drop="drop($event, childIndex, variable)"
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- Визуальный дубликат между элементами в группе -->
|
||||||
|
<div v-if="dragItem && dragOverItem === childIndex && dragOverGroup === variable"
|
||||||
|
class="opacity-60 transform">
|
||||||
|
<Button
|
||||||
|
icon-left
|
||||||
|
block
|
||||||
|
class="cursor-grabbing bg-blue-50 border-blue-200"
|
||||||
|
disabled>
|
||||||
|
<template #icon>
|
||||||
|
<svg 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" class="h-4 w-4">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
{{ dragItem.variable.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
icon-left
|
||||||
|
block
|
||||||
|
@click="clickToVariable(child)"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="dragStart($event, child, childIndex, variable)"
|
||||||
|
@dragend="dragEnd"
|
||||||
|
class="drag-handle cursor-grab active:cursor-grabbing">
|
||||||
|
<template #icon>
|
||||||
|
<svg 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" class="h-4 w-4">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
<span class="truncate">{{ child.label }}</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<div v-else
|
||||||
|
class="text-center text-gray-400 py-2 text-sm border-2 border-dashed border-gray-300 rounded mx-2"
|
||||||
|
@dragover="dragOver($event, -2, variable)"
|
||||||
|
@drop="drop($event, -2, variable)">
|
||||||
|
Перетащите переменные сюда
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
<div v-else
|
||||||
|
class="relative"
|
||||||
|
:class="{ 'bg-blue-50/10': dragOverItem === index && !dragOverGroup }"
|
||||||
|
@dragover="dragOver($event, index)"
|
||||||
|
@drop="drop($event, index)">
|
||||||
|
<Button
|
||||||
|
icon-left
|
||||||
|
block
|
||||||
|
@click="clickToVariable(variable)"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="dragStart($event, variable, index)"
|
||||||
|
@dragend="dragEnd"
|
||||||
|
class="drag-handle cursor-grab active:cursor-grabbing">
|
||||||
|
<template #icon>
|
||||||
|
<svg 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" class="h-4 w-4">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
{{ variable.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Визуальный дубликат в конце корневого списка -->
|
||||||
|
<div v-if="dragItem && dragOverItem === -1 && !dragOverGroup"
|
||||||
|
class="opacity-60 transform scale-105">
|
||||||
|
<Button
|
||||||
|
icon-left
|
||||||
|
block
|
||||||
|
class="cursor-grabbing bg-blue-50 border-blue-200"
|
||||||
|
disabled>
|
||||||
|
<template #icon>
|
||||||
|
<svg 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" class="h-4 w-4">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
<path d="M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
{{ dragItem.variable.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <ListStrate v-for="variable in uploadForm.variables" :header="variable.label">-->
|
|
||||||
<!-- <Select :options="variableTypes" v-model:value="variable.type" placeholder="Выберите тип" />-->
|
|
||||||
<!-- </ListStrate>-->
|
|
||||||
</Card>
|
</Card>
|
||||||
<Card :header="selectedVariable.label">
|
<Card :header="selectedVariable?.label || 'Выберите переменную'">
|
||||||
<div class="pr-2">
|
<div class="pr-2" v-if="selectedVariable">
|
||||||
<div class="flex flex-col gap-y-1">
|
<div class="flex flex-col gap-y-1">
|
||||||
<FormGroup label="Наименование переменной" position="top">
|
<FormGroup label="Наименование переменной" position="top">
|
||||||
<Input v-model:value="activeVariable.name" disabled />
|
<Input v-model:value="activeVariable.name" disabled />
|
||||||
@@ -152,7 +589,7 @@ const clickToVariable = (variable) => {
|
|||||||
<Input v-model:value="activeVariable.label" />
|
<Input v-model:value="activeVariable.label" />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup label="Тип ввода" position="top">
|
<FormGroup v-if="activeVariable.type !== 'group'" label="Тип ввода" position="top">
|
||||||
<Select :options="variableTypes" v-model:value="activeVariable.type" @update:value="changeTypeValue(value)" placeholder="Выберите тип" />
|
<Select :options="variableTypes" v-model:value="activeVariable.type" @update:value="changeTypeValue(value)" placeholder="Выберите тип" />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@@ -169,6 +606,9 @@ const clickToVariable = (variable) => {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="text-center text-gray-400 py-8">
|
||||||
|
Выберите переменную для редактирования
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user