This commit is contained in:
che
2026-07-23 09:31:01 +05:00
commit 4cfc5d6ae6
64 changed files with 9779 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { showToast } from "vant";
import { employeeApi, messageApi, taskApi } from "../generated/api";
import { useModelApi } from "../composables/useModelApi";
import EmployeeSelect from "../components/EmployeeSelect.vue";
const route = useRoute();
const router = useRouter();
const taskId = computed(() => Number(route.params.id));
const messageText = ref("");
const selectedEmployeeId = ref(0);
const {
item: task,
loadingItem,
error,
retrieve: loadTask,
} = useModelApi(taskApi, {
retrieveErrorMessage: "Не удалось загрузить задачу",
});
const {
items: messages,
loading: loadingMessages,
creating: sendingMessage,
error: messageError,
load: loadMessages,
create: createMessageApi,
} = useModelApi(messageApi, {
loadErrorMessage: "Не удалось загрузить сообщения",
createErrorMessage: "Не удалось отправить сообщение",
});
const { items: employees, load: loadEmployees } = useModelApi(employeeApi, {
defaultListParams: { ordering: "id" },
loadErrorMessage: "Не удалось загрузить сотрудников",
});
async function sendMessage() {
if (!Number.isFinite(taskId.value)) {
showToast("Некорректный ID задачи");
return;
}
const text = messageText.value.trim();
if (!text) {
showToast("Введите сообщение");
return;
}
if (!selectedEmployeeId.value) {
showToast("Выберите сотрудника");
return;
}
const message = await createMessageApi({
task_id: taskId.value,
employee_id: selectedEmployeeId.value,
text,
});
if (message) {
messageText.value = "";
await loadMessages({ task_id: taskId.value });
showToast("Сообщение отправлено");
}
}
function employeeName(id: number) {
return employees.value.find((employee) => employee.id === id)?.name ?? `#${id}`;
}
function employeeAvatar(id: number) {
return employees.value.find((employee) => employee.id === id)?.avatar ?? "";
}
onMounted(() => {
if (Number.isFinite(taskId.value)) {
loadTask(taskId.value);
loadMessages({ task_id: taskId.value });
}
loadEmployees();
});
</script>
<template>
<section class="hero detail-hero">
<p class="eyebrow">Task details</p>
<h1>Детали</h1>
<p class="subtitle">Просмотр задачи и диалог сотрудников.</p>
</section>
<van-notice-bar
v-if="error || messageError"
class="notice"
color="#991b1b"
background="#fee2e2"
left-icon="warning-o"
wrapable
:scrollable="false"
:text="error || messageError"
/>
<section class="card detail-card">
<van-loading v-if="loadingItem" class="state" type="spinner">Загрузка...</van-loading>
<van-empty v-else-if="!task" description="Задача не найдена" />
<template v-else>
<van-cell-group inset>
<van-cell title="ID" :value="task.id" />
<van-cell title="Название" :value="task.name" />
<van-cell title="Автор" :value="task.author_id ?? 'не указан'" />
<van-cell title="Ответственный" :value="task.responsible_id ?? 'не указан'" />
</van-cell-group>
</template>
</section>
<section class="card messages-card">
<div class="list-title">
<h2>Диалог</h2>
<van-button
size="small"
plain
type="primary"
:loading="loadingMessages"
@click="loadMessages({ task_id: taskId })"
>
Обновить
</van-button>
</div>
<van-loading v-if="loadingMessages && messages.length === 0" class="state" type="spinner">
Загрузка сообщений...
</van-loading>
<van-empty v-else-if="messages.length === 0" description="Сообщений пока нет" />
<div v-else class="message-list">
<article v-for="message in messages" :key="message.id" class="message-item">
<van-image
class="message-avatar"
round
width="40"
height="40"
:src="employeeAvatar(message.employee_id)"
/>
<div class="message-bubble">
<div class="message-author">{{ employeeName(message.employee_id) }}</div>
<div class="message-text">{{ message.text }}</div>
</div>
</article>
</div>
</section>
<van-form class="card" @submit="sendMessage">
<van-field
name="employee_id"
label="Отправитель"
placeholder="Выберите сотрудника"
>
<template #input>
<EmployeeSelect v-model="selectedEmployeeId" />
</template>
</van-field>
<van-field
v-model="messageText"
name="text"
label="Сообщение"
type="textarea"
rows="2"
autosize
clearable
placeholder="Напишите сообщение"
:disabled="sendingMessage"
/>
<div class="form-actions stacked-actions">
<van-button
block
round
type="primary"
native-type="submit"
:loading="sendingMessage"
:disabled="!Number.isFinite(taskId) || !messageText.trim() || !selectedEmployeeId"
>
Отправить сообщение
</van-button>
<van-button block round type="primary" plain @click="router.back()">Назад</van-button>
</div>
</van-form>
</template>