189 lines
5.3 KiB
Vue
189 lines
5.3 KiB
Vue
<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: "Не удалось загрузить задачу",
|
|
autoLoad: false,
|
|
autoLoadOnFilterChange: false,
|
|
});
|
|
const {
|
|
items: messages,
|
|
loading: loadingMessages,
|
|
creating: sendingMessage,
|
|
error: messageError,
|
|
load: loadMessages,
|
|
create: createMessageApi,
|
|
} = useModelApi(messageApi, {
|
|
loadErrorMessage: "Не удалось загрузить сообщения",
|
|
createErrorMessage: "Не удалось отправить сообщение",
|
|
autoLoad: false,
|
|
autoLoadOnFilterChange: false,
|
|
});
|
|
const { items: employees } = 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_small ?? "";
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (Number.isFinite(taskId.value)) {
|
|
loadTask(taskId.value);
|
|
loadMessages({ task_id: taskId.value });
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<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>
|