This commit is contained in:
che
2026-07-25 13:48:34 +05:00
parent 249a4b455e
commit 6e64b97150
+147 -23
View File
@@ -20,7 +20,7 @@ type CurrentEmployee = {
avatar?: string | null;
};
type MainTabKey = "incoming" | "transfer" | "review" | "outgoing" | "archive";
type MainTabKey = "incoming" | "transfer" | "review" | "outgoing";
type DocTabKey =
| "all"
| "simple"
@@ -40,6 +40,22 @@ const employeeLoading = ref(false);
const refreshing = ref(false);
const switchingList = ref(false);
let suppressDocTabReload = false;
const mainTabCounts = ref<Record<MainTabKey, number>>({
incoming: 0,
transfer: 0,
review: 0,
outgoing: 0,
});
const docTabCounts = ref<Record<DocTabKey, number>>({
all: 0,
simple: 0,
memo: 0,
bill: 0,
contract: 0,
contract_application: 0,
entry_letter: 0,
outgoing_letter: 0,
});
const {
items: tasks,
@@ -48,6 +64,7 @@ const {
load: loadTasks,
} = useModelApi<Task, TaskCreate, TaskUpdate, TaskListParams>(taskApi, {
defaultListParams: { ordering: "-id" },
autoLoad: false,
loadErrorMessage: "Не удалось загрузить задачи",
});
@@ -63,6 +80,7 @@ const {
TaskTransferListParams
>(taskTransferApi, {
defaultListParams: { ordering: "-id" },
autoLoad: false,
loadErrorMessage: "Не удалось загрузить переносы срока",
});
@@ -71,7 +89,6 @@ const mainTabs = [
{ key: "transfer", label: "Перенос срока", icon: "underway-o" },
{ key: "review", label: "Для проверки", icon: "eye-o" },
{ key: "outgoing", label: "Исходящие", icon: "send-gift-o" },
{ key: "archive", label: "Архив", icon: "folder-o" },
] as const;
const docTabs = [
@@ -91,6 +108,7 @@ const visibleItems = computed(() =>
const activeLoading = computed(() =>
mainTab.value === "transfer" ? taskTransferLoading.value : taskLoading.value,
);
const showLoading = computed(() => activeLoading.value || refreshing.value);
const showEmployeeLoading = computed(
() =>
employeeLoading.value &&
@@ -101,6 +119,11 @@ const activeError = computed(() =>
mainTab.value === "transfer" ? taskTransferError.value : taskError.value,
);
const showDocTabs = computed(() => mainTab.value !== "transfer");
const visibleDocTabs = computed(() =>
showDocTabs.value
? docTabs.filter((tab) => (docTabCounts.value[tab.key] ?? 0) > 0)
: [],
);
function personName(
person: { short_name?: string | null; name?: string | null } | null,
@@ -120,6 +143,10 @@ function taskTitle(task: {
return task.text?.trim() || task.result?.trim() || `Задача #${task.id}`;
}
function badgeValue(count: number) {
return count > 0 ? count : undefined;
}
function isTaskTransfer(item: Task | TaskTransfer): item is TaskTransfer {
return "employee_from" in item;
}
@@ -144,19 +171,17 @@ function itemPath(item: Task | TaskTransfer) {
return isTaskTransfer(item) ? `/tasks/${item.task.id}` : `/tasks/${item.id}`;
}
function taskParams(): TaskListParams | TaskTransferListParams | null {
if (!employee.value) {
return null;
}
const employeeId = employee.value.id;
switch (mainTab.value) {
function buildTaskParams(
tab: MainTabKey,
doc: DocTabKey,
employeeId: number,
): TaskListParams | TaskTransferListParams | null {
switch (tab) {
case "incoming":
return {
doer: employeeId,
archive: false,
typ: docTab.value === "all" ? undefined : docTab.value,
typ: doc === "all" ? undefined : doc,
q: "entry",
incomplete: true,
};
@@ -164,24 +189,50 @@ function taskParams(): TaskListParams | TaskTransferListParams | null {
return {
q: "to_approve",
incomplete: true,
typ: docTab.value === "all" ? undefined : docTab.value,
archive: false,
typ: doc === "all" ? undefined : doc,
};
case "outgoing":
return {
q: "outgoing",
archive: false,
incomplete: true,
typ: docTab.value === "all" ? undefined : docTab.value,
typ: doc === "all" ? undefined : doc,
};
case "archive":
case "transfer":
return {
typ: docTab.value === "all" ? undefined : docTab.value,
employee_to: employeeId,
status: "A",
};
default:
return null;
}
}
function taskParams(): TaskListParams | TaskTransferListParams | null {
if (!employee.value) {
return null;
}
return buildTaskParams(mainTab.value, docTab.value, employee.value.id);
}
function taskCountParams(tab: MainTabKey): TaskListParams | TaskTransferListParams | null {
if (!employee.value) {
return null;
}
return buildTaskParams(tab, "all", employee.value.id);
}
function docCountParams(doc: DocTabKey): TaskListParams | null {
if (!employee.value || mainTab.value === "transfer") {
return null;
}
return buildTaskParams(mainTab.value, doc, employee.value.id) as TaskListParams;
}
async function loadCurrentEmployee() {
employeeLoading.value = true;
employeeError.value = "";
@@ -217,10 +268,80 @@ async function reloadList() {
await loadTasks(params as TaskListParams);
}
async function reloadForTabChange() {
async function countTaskItems(params: TaskListParams | TaskTransferListParams | null) {
if (!params) {
return 0;
}
if ("employee_to" in params) {
const response = await taskTransferApi.list({ ...params, limit: 1 });
return response.count;
}
const response = await taskApi.list({ ...params, limit: 1 });
return response.count;
}
async function reloadCounts() {
if (!employee.value) {
return;
}
const mainCounts = await Promise.allSettled(
mainTabs.map(async (tab) => [tab.key, await countTaskItems(taskCountParams(tab.key))] as const),
);
mainTabCounts.value = {
...mainTabCounts.value,
...Object.fromEntries(
mainCounts.flatMap((result) =>
result.status === "fulfilled" ? [result.value] : [],
),
),
} as Record<MainTabKey, number>;
if (mainTab.value === "transfer") {
docTabCounts.value = {
all: 0,
simple: 0,
memo: 0,
bill: 0,
contract: 0,
contract_application: 0,
entry_letter: 0,
outgoing_letter: 0,
};
return;
}
const docCounts = await Promise.allSettled(
docTabs.map(async (tab) => [tab.key, await countTaskItems(docCountParams(tab.key))] as const),
);
docTabCounts.value = {
...docTabCounts.value,
...Object.fromEntries(
docCounts.flatMap((result) =>
result.status === "fulfilled" ? [result.value] : [],
),
),
} as Record<DocTabKey, number>;
const nextDocTab = visibleDocTabs.value[0]?.key ?? "all";
if (docTab.value !== nextDocTab) {
suppressDocTabReload = true;
docTab.value = nextDocTab;
await nextTick();
suppressDocTabReload = false;
}
}
async function reloadForTabChange(options: { reloadCounts: boolean }) {
switchingList.value = true;
try {
if (options.reloadCounts) {
await reloadCounts();
}
await reloadList();
} finally {
switchingList.value = false;
@@ -231,6 +352,7 @@ async function onRefresh() {
refreshing.value = true;
try {
await reloadCounts();
await reloadList();
} finally {
refreshing.value = false;
@@ -246,18 +368,18 @@ watch(mainTab, () => {
});
}
void reloadForTabChange();
void reloadForTabChange({ reloadCounts: true });
});
watch(docTab, () => {
if (showDocTabs.value && !suppressDocTabReload) {
void reloadForTabChange();
void reloadForTabChange({ reloadCounts: false });
}
});
onMounted(async () => {
await loadCurrentEmployee();
await reloadList();
await reloadForTabChange({ reloadCounts: true });
});
</script>
@@ -285,22 +407,24 @@ onMounted(async () => {
:key="tab.key"
:name="tab.key"
:icon="tab.icon"
:badge="badgeValue(mainTabCounts[tab.key])"
>
{{ tab.label }}
</van-tabbar-item>
</van-tabbar>
<van-tabs
v-if="showDocTabs"
v-if="showDocTabs && visibleDocTabs.length > 0"
v-model:active="docTab"
shrink
class="tabs tabs-secondary"
>
<van-tab
v-for="tab in docTabs"
v-for="tab in visibleDocTabs"
:key="tab.key"
:name="tab.key"
:title="tab.label"
:badge="badgeValue(docTabCounts[tab.key])"
/>
</van-tabs>
@@ -311,8 +435,8 @@ onMounted(async () => {
@refresh="onRefresh"
>
<van-list
:loading="activeLoading || refreshing"
:finished="true"
:loading="showLoading"
:finished="false"
loading-text="Загрузка..."
:immediate-check="false"
>