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