Paginate Tahesab customer list by code windows past the 5000 cap.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
IsUrl,
|
IsUrl,
|
||||||
|
Max,
|
||||||
MaxLength,
|
MaxLength,
|
||||||
Min,
|
Min,
|
||||||
MinLength,
|
MinLength,
|
||||||
@@ -155,10 +156,24 @@ export class ImportBulkFromTahesabDto {
|
|||||||
@Min(1)
|
@Min(1)
|
||||||
toCode?: number;
|
toCode?: number;
|
||||||
|
|
||||||
/** اگر true باشد همهٔ مشتریان با اسکن بازهای خوانده میشوند */
|
/** اگر true باشد یک صفحه از مشتریان (بازهٔ کد) خوانده میشود */
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
listAll?: boolean;
|
listAll?: boolean;
|
||||||
|
|
||||||
|
/** صفحهٔ بازهٔ کد هنگام listAll (پیشفرض ۱) */
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(100)
|
||||||
|
@Max(2000)
|
||||||
|
pageSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SearchTahesabDto {
|
export class SearchTahesabDto {
|
||||||
@@ -191,10 +206,28 @@ export class SearchTahesabDto {
|
|||||||
@Min(1)
|
@Min(1)
|
||||||
toCode?: number;
|
toCode?: number;
|
||||||
|
|
||||||
/** بازه خالی در UI → همهٔ مشتریان */
|
/** بازه خالی در UI → مرور صفحهای همهٔ مشتریان */
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
listAll?: boolean;
|
listAll?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* صفحهٔ بازهٔ کد برای listAll (۱ = کد ۱…۲۰۰۰، ۲ = ۲۰۰۱…۴۰۰۰، …).
|
||||||
|
* سقف ۵۰۰۰ دیگر اعمال نمیشود.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
/** اندازهٔ پنجرهٔ کد در هر صفحه (پیشفرض ۲۰۰۰، حداکثر ۲۰۰۰) */
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(100)
|
||||||
|
@Max(2000)
|
||||||
|
pageSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SearchTahesabProductsDto {
|
export class SearchTahesabProductsDto {
|
||||||
|
|||||||
@@ -17,10 +17,9 @@ import {
|
|||||||
} from './dto';
|
} from './dto';
|
||||||
import { normalizeIranMobile, SanadBuilders, type TahesabApi } from './tahesab-methods';
|
import { normalizeIranMobile, SanadBuilders, type TahesabApi } from './tahesab-methods';
|
||||||
|
|
||||||
const LIST_ALL_CHUNK = 2000;
|
/** اندازهٔ هر صفحهٔ «همه» (بازهٔ کد) — امن برای پروکسی/nginx */
|
||||||
/** سقف «همه» در یک درخواست ByCode — تکهتکه باعث ۵۰۲ پروکسی میشود */
|
const LIST_PAGE_SIZE = 2000;
|
||||||
const LIST_ALL_MAX_CODE = 5_000;
|
const LIST_TIMEOUT_MS = 55_000;
|
||||||
const LIST_ALL_TIMEOUT_MS = 55_000;
|
|
||||||
|
|
||||||
function asMoshtariList(raw: unknown): Array<Record<string, unknown>> {
|
function asMoshtariList(raw: unknown): Array<Record<string, unknown>> {
|
||||||
if (!raw) return [];
|
if (!raw) return [];
|
||||||
@@ -318,25 +317,32 @@ export class TahesabPersonLinkService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* لیست همهٔ مشتریان در یک درخواست (بازه ۱…LIST_ALL_MAX_CODE).
|
* یک صفحه از مشتریان بر اساس بازهٔ کد:
|
||||||
* تکهتکه کردن پشتسرهم معمولاً از مهلت nginx بیشتر میشود و ۵۰۲ میدهد.
|
* صفحه ۱ → ۱…pageSize، صفحه ۲ → pageSize+1…۲×pageSize، …
|
||||||
|
* سقف ۵۰۰۰ حذف شده؛ با پیجینیت میتوان همه را مرور کرد.
|
||||||
*/
|
*/
|
||||||
private async listAllMoshtari(api: TahesabApi) {
|
private async listMoshtariPage(
|
||||||
try {
|
api: TahesabApi,
|
||||||
return asMoshtariList(
|
page: number,
|
||||||
await api.doListMoshtariByCode(1, LIST_ALL_MAX_CODE, {
|
pageSize?: number,
|
||||||
timeoutMs: LIST_ALL_TIMEOUT_MS,
|
) {
|
||||||
}),
|
const size = Math.min(
|
||||||
);
|
LIST_PAGE_SIZE,
|
||||||
} catch (err) {
|
Math.max(100, Math.floor(pageSize ?? LIST_PAGE_SIZE) || LIST_PAGE_SIZE),
|
||||||
// اگر بازهٔ بزرگ سمت تهحساب رد شد، همان سقف دستی ۲۰۰۰ را یکبار امتحان کن
|
);
|
||||||
if (LIST_ALL_MAX_CODE <= LIST_ALL_CHUNK) throw err;
|
const p = Math.max(1, Math.floor(page) || 1);
|
||||||
return asMoshtariList(
|
const fromCode = (p - 1) * size + 1;
|
||||||
await api.doListMoshtariByCode(1, LIST_ALL_CHUNK, {
|
const toCode = fromCode + size - 1;
|
||||||
timeoutMs: LIST_ALL_TIMEOUT_MS,
|
const raw = await api.doListMoshtariByCode(fromCode, toCode, {
|
||||||
}),
|
timeoutMs: LIST_TIMEOUT_MS,
|
||||||
);
|
});
|
||||||
}
|
return {
|
||||||
|
rows: asMoshtariList(raw),
|
||||||
|
fromCode,
|
||||||
|
toCode,
|
||||||
|
page: p,
|
||||||
|
pageSize: size,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async search(organizationId: string, dto: SearchTahesabDto) {
|
async search(organizationId: string, dto: SearchTahesabDto) {
|
||||||
@@ -345,8 +351,28 @@ export class TahesabPersonLinkService {
|
|||||||
dto.connectionId,
|
dto.connectionId,
|
||||||
);
|
);
|
||||||
let raw: unknown = [];
|
let raw: unknown = [];
|
||||||
|
let listMeta:
|
||||||
|
| {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
fromCode: number;
|
||||||
|
toCode: number;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
|
||||||
if (dto.listAll) {
|
if (dto.listAll) {
|
||||||
raw = await this.listAllMoshtari(api);
|
const listed = await this.listMoshtariPage(
|
||||||
|
api,
|
||||||
|
dto.page ?? 1,
|
||||||
|
dto.pageSize,
|
||||||
|
);
|
||||||
|
raw = listed.rows;
|
||||||
|
listMeta = {
|
||||||
|
page: listed.page,
|
||||||
|
pageSize: listed.pageSize,
|
||||||
|
fromCode: listed.fromCode,
|
||||||
|
toCode: listed.toCode,
|
||||||
|
};
|
||||||
} else if (dto.fromCode != null && dto.toCode != null) {
|
} else if (dto.fromCode != null && dto.toCode != null) {
|
||||||
if (dto.toCode < dto.fromCode) {
|
if (dto.toCode < dto.fromCode) {
|
||||||
throw new BadRequestException('کد پایان باید بزرگتر یا مساوی کد شروع باشد.');
|
throw new BadRequestException('کد پایان باید بزرگتر یا مساوی کد شروع باشد.');
|
||||||
@@ -354,7 +380,9 @@ export class TahesabPersonLinkService {
|
|||||||
if (dto.toCode - dto.fromCode > 2000) {
|
if (dto.toCode - dto.fromCode > 2000) {
|
||||||
throw new BadRequestException('بازهٔ کد حداکثر ۲۰۰۰ عدد است (مثلاً ۱ تا ۲۰۰۰). برای همه، بازه را خالی بگذارید.');
|
throw new BadRequestException('بازهٔ کد حداکثر ۲۰۰۰ عدد است (مثلاً ۱ تا ۲۰۰۰). برای همه، بازه را خالی بگذارید.');
|
||||||
}
|
}
|
||||||
raw = await api.doListMoshtariByCode(dto.fromCode, dto.toCode);
|
raw = await api.doListMoshtariByCode(dto.fromCode, dto.toCode, {
|
||||||
|
timeoutMs: LIST_TIMEOUT_MS,
|
||||||
|
});
|
||||||
} else if (dto.moshtariCode) {
|
} else if (dto.moshtariCode) {
|
||||||
raw = await api.doListMoshtariByCode(dto.moshtariCode, dto.moshtariCode);
|
raw = await api.doListMoshtariByCode(dto.moshtariCode, dto.moshtariCode);
|
||||||
} else if (dto.phone) {
|
} else if (dto.phone) {
|
||||||
@@ -414,11 +442,23 @@ export class TahesabPersonLinkService {
|
|||||||
kind: 'search',
|
kind: 'search',
|
||||||
status: 'success',
|
status: 'success',
|
||||||
request: dto as unknown as Prisma.InputJsonValue,
|
request: dto as unknown as Prisma.InputJsonValue,
|
||||||
result: { count: results.length } as Prisma.InputJsonValue,
|
result: {
|
||||||
|
count: results.length,
|
||||||
|
...(listMeta ?? {}),
|
||||||
|
} as Prisma.InputJsonValue,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { results };
|
return {
|
||||||
|
results,
|
||||||
|
...(listMeta
|
||||||
|
? {
|
||||||
|
...listMeta,
|
||||||
|
hasMore: results.length > 0,
|
||||||
|
note: `صفحه ${listMeta.page}: کد ${listMeta.fromCode} تا ${listMeta.toCode}. برای ادامه صفحه بعد را بزنید.`,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async importFromTahesab(
|
async importFromTahesab(
|
||||||
@@ -516,10 +556,15 @@ export class TahesabPersonLinkService {
|
|||||||
organizationId,
|
organizationId,
|
||||||
dto.connectionId,
|
dto.connectionId,
|
||||||
);
|
);
|
||||||
const listed = (await this.listAllMoshtari(api))
|
const listed = await this.listMoshtariPage(
|
||||||
|
api,
|
||||||
|
dto.page ?? 1,
|
||||||
|
dto.pageSize,
|
||||||
|
);
|
||||||
|
const listedCodes = listed.rows
|
||||||
.map((row) => pickCode(row))
|
.map((row) => pickCode(row))
|
||||||
.filter((c): c is number => c != null);
|
.filter((c): c is number => c != null);
|
||||||
codes = [...new Set([...codes, ...listed])];
|
codes = [...new Set([...codes, ...listedCodes])];
|
||||||
} else if (dto.fromCode != null && dto.toCode != null) {
|
} else if (dto.fromCode != null && dto.toCode != null) {
|
||||||
if (dto.toCode < dto.fromCode) {
|
if (dto.toCode < dto.fromCode) {
|
||||||
throw new BadRequestException('کد پایان باید بزرگتر یا مساوی کد شروع باشد.');
|
throw new BadRequestException('کد پایان باید بزرگتر یا مساوی کد شروع باشد.');
|
||||||
@@ -545,10 +590,10 @@ export class TahesabPersonLinkService {
|
|||||||
'کد مشتری یا بازهٔ کد برای وارد کردن مشخص نشده است.',
|
'کد مشتری یا بازهٔ کد برای وارد کردن مشخص نشده است.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const maxImport = dto.listAll ? 2000 : 500;
|
const maxImport = dto.listAll || (dto.fromCode != null && dto.toCode != null) ? 2000 : 500;
|
||||||
if (codes.length > maxImport) {
|
if (codes.length > maxImport) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`حداکثر ${maxImport} مشتری در هر بار وارد کنید (الان ${codes.length}). بازه را کوچکتر کنید.`,
|
`حداکثر ${maxImport} مشتری در هر بار وارد کنید (الان ${codes.length}). صفحه یا بازه را کوچکتر کنید.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { computed, onMounted, reactive, ref } from 'vue';
|
||||||
import { RouterLink } from 'vue-router';
|
import { RouterLink } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { api } from '@/lib/api';
|
import { api } from '@/lib/api';
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
UiField,
|
UiField,
|
||||||
UiInput,
|
UiInput,
|
||||||
UiPageHeader,
|
UiPageHeader,
|
||||||
|
UiPagination,
|
||||||
UiSelect,
|
UiSelect,
|
||||||
} from '@/components/ui';
|
} from '@/components/ui';
|
||||||
import UiIcon from '@/components/UiIcon.vue';
|
import UiIcon from '@/components/UiIcon.vue';
|
||||||
@@ -55,6 +56,32 @@ type SyncResult = {
|
|||||||
personName?: string | null;
|
personName?: string | null;
|
||||||
};
|
};
|
||||||
const syncResults = ref<SyncResult[]>([]);
|
const syncResults = ref<SyncResult[]>([]);
|
||||||
|
/** حالت آخرین جستجو — برای پیجینیت بازهٔ کد */
|
||||||
|
const syncListMode = ref<'none' | 'listAll' | 'range' | 'filter'>('none');
|
||||||
|
const syncListPage = ref(1);
|
||||||
|
const syncListMeta = reactive({
|
||||||
|
fromCode: 0,
|
||||||
|
toCode: 0,
|
||||||
|
hasMore: false,
|
||||||
|
pageSize: 2000,
|
||||||
|
});
|
||||||
|
const SYNC_TABLE_PAGE_SIZE = 50;
|
||||||
|
const syncTablePage = ref(1);
|
||||||
|
|
||||||
|
const syncCodeTotalPages = computed(() => {
|
||||||
|
if (syncListMode.value !== 'listAll') return 1;
|
||||||
|
// اگر صفحه فعلی نتیجه دارد، صفحهٔ بعد هم قابل امتحان است
|
||||||
|
return syncListMeta.hasMore
|
||||||
|
? syncListPage.value + 1
|
||||||
|
: Math.max(1, syncListPage.value);
|
||||||
|
});
|
||||||
|
const syncTableTotalPages = computed(() =>
|
||||||
|
Math.max(1, Math.ceil(syncResults.value.length / SYNC_TABLE_PAGE_SIZE)),
|
||||||
|
);
|
||||||
|
const syncPagedResults = computed(() => {
|
||||||
|
const start = (syncTablePage.value - 1) * SYNC_TABLE_PAGE_SIZE;
|
||||||
|
return syncResults.value.slice(start, start + SYNC_TABLE_PAGE_SIZE);
|
||||||
|
});
|
||||||
|
|
||||||
const productSync = reactive({
|
const productSync = reactive({
|
||||||
connectionId: '',
|
connectionId: '',
|
||||||
@@ -255,7 +282,7 @@ async function removeConn(id: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchTahesab(mode: 'range' | 'filter') {
|
async function searchTahesab(mode: 'range' | 'filter', codePage?: number) {
|
||||||
error.value = '';
|
error.value = '';
|
||||||
ok.value = '';
|
ok.value = '';
|
||||||
if (!sync.connectionId) {
|
if (!sync.connectionId) {
|
||||||
@@ -269,8 +296,13 @@ async function searchTahesab(mode: 'range' | 'filter') {
|
|||||||
const fromRaw = String(sync.fromCode ?? '').trim();
|
const fromRaw = String(sync.fromCode ?? '').trim();
|
||||||
const toRaw = String(sync.toCode ?? '').trim();
|
const toRaw = String(sync.toCode ?? '').trim();
|
||||||
if (!fromRaw && !toRaw) {
|
if (!fromRaw && !toRaw) {
|
||||||
// API برای «همه» فلگ listAll دارد؛ ارسال ۱…۵۰۰۰ با سقف ۲۰۰۰ کد رد میشود
|
// همهٔ مشتریان — صفحهبندی بازهٔ کد (هر صفحه ۲۰۰۰ کد؛ بدون سقف ۵۰۰۰)
|
||||||
|
const page = Math.max(1, codePage ?? syncListPage.value);
|
||||||
body.listAll = true;
|
body.listAll = true;
|
||||||
|
body.page = page;
|
||||||
|
body.pageSize = 2000;
|
||||||
|
syncListPage.value = page;
|
||||||
|
syncListMode.value = 'listAll';
|
||||||
} else {
|
} else {
|
||||||
const fromCode = fromRaw ? Number(fromRaw) : 1;
|
const fromCode = fromRaw ? Number(fromRaw) : 1;
|
||||||
const toCode = toRaw ? Number(toRaw) : fromCode + 1999;
|
const toCode = toRaw ? Number(toRaw) : fromCode + 1999;
|
||||||
@@ -284,28 +316,59 @@ async function searchTahesab(mode: 'range' | 'filter') {
|
|||||||
}
|
}
|
||||||
body.fromCode = fromCode;
|
body.fromCode = fromCode;
|
||||||
body.toCode = toCode;
|
body.toCode = toCode;
|
||||||
|
syncListMode.value = 'range';
|
||||||
|
syncListMeta.hasMore = false;
|
||||||
}
|
}
|
||||||
} else if (sync.moshtariCode !== '' && sync.moshtariCode != null) {
|
} else if (sync.moshtariCode !== '' && sync.moshtariCode != null) {
|
||||||
body.moshtariCode = Number(sync.moshtariCode);
|
body.moshtariCode = Number(sync.moshtariCode);
|
||||||
|
syncListMode.value = 'filter';
|
||||||
|
syncListMeta.hasMore = false;
|
||||||
} else if (sync.phone.trim()) {
|
} else if (sync.phone.trim()) {
|
||||||
body.phone = sync.phone.trim();
|
body.phone = sync.phone.trim();
|
||||||
|
syncListMode.value = 'filter';
|
||||||
|
syncListMeta.hasMore = false;
|
||||||
} else if (sync.name.trim()) {
|
} else if (sync.name.trim()) {
|
||||||
body.name = sync.name.trim();
|
body.name = sync.name.trim();
|
||||||
|
syncListMode.value = 'filter';
|
||||||
|
syncListMeta.hasMore = false;
|
||||||
} else {
|
} else {
|
||||||
error.value = 'موبایل، نام، کد مشتری یا بازهٔ کد را وارد کنید.';
|
error.value = 'موبایل، نام، کد مشتری یا بازهٔ کد را وارد کنید.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { data } = await api.post<{ results: SyncResult[] }>(
|
const { data } = await api.post<{
|
||||||
|
results: SyncResult[];
|
||||||
|
page?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
fromCode?: number;
|
||||||
|
toCode?: number;
|
||||||
|
hasMore?: boolean;
|
||||||
|
note?: string;
|
||||||
|
}>(
|
||||||
'/integrations/tahesab/sync/search',
|
'/integrations/tahesab/sync/search',
|
||||||
body,
|
body,
|
||||||
{ timeout: body.listAll ? 180_000 : 60_000 },
|
{ timeout: body.listAll ? 90_000 : 60_000 },
|
||||||
);
|
);
|
||||||
syncResults.value = data.results ?? [];
|
syncResults.value = data.results ?? [];
|
||||||
|
syncTablePage.value = 1;
|
||||||
|
if (data.page != null) {
|
||||||
|
syncListPage.value = data.page;
|
||||||
|
syncListMeta.fromCode = data.fromCode ?? 0;
|
||||||
|
syncListMeta.toCode = data.toCode ?? 0;
|
||||||
|
syncListMeta.hasMore = Boolean(data.hasMore);
|
||||||
|
syncListMeta.pageSize = data.pageSize ?? 2000;
|
||||||
|
}
|
||||||
if (!syncResults.value.length) {
|
if (!syncResults.value.length) {
|
||||||
ok.value = 'نتیجهای یافت نشد.';
|
ok.value =
|
||||||
|
data.note ??
|
||||||
|
(syncListMode.value === 'listAll' && syncListPage.value > 1
|
||||||
|
? 'در این صفحه مشتریای نیست. صفحه قبل را ببینید یا بازه دستی بزنید.'
|
||||||
|
: 'نتیجهای یافت نشد.');
|
||||||
} else {
|
} else {
|
||||||
const linked = syncResults.value.filter((r) => r.alreadyLinked).length;
|
const linked = syncResults.value.filter((r) => r.alreadyLinked).length;
|
||||||
ok.value = `${syncResults.value.length} مشتری پیدا شد` + (linked ? ` (${linked} قبلاً لینک شده)` : '');
|
const base =
|
||||||
|
`${syncResults.value.length} مشتری پیدا شد` +
|
||||||
|
(linked ? ` (${linked} قبلاً لینک شده)` : '');
|
||||||
|
ok.value = data.note ? `${base} — ${data.note}` : base;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = apiErr(e, 'جستجو ناموفق');
|
error.value = apiErr(e, 'جستجو ناموفق');
|
||||||
@@ -314,6 +377,14 @@ async function searchTahesab(mode: 'range' | 'filter') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSyncCodePageChange(page: number) {
|
||||||
|
void searchTahesab('range', page);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSyncTablePageChange(page: number) {
|
||||||
|
syncTablePage.value = page;
|
||||||
|
}
|
||||||
|
|
||||||
async function importOne(moshtariCode: number) {
|
async function importOne(moshtariCode: number) {
|
||||||
error.value = '';
|
error.value = '';
|
||||||
ok.value = '';
|
ok.value = '';
|
||||||
@@ -362,14 +433,25 @@ async function importBulk(mode: 'results' | 'range') {
|
|||||||
const fromRaw = String(sync.fromCode ?? '').trim();
|
const fromRaw = String(sync.fromCode ?? '').trim();
|
||||||
const toRaw = String(sync.toCode ?? '').trim();
|
const toRaw = String(sync.toCode ?? '').trim();
|
||||||
if (!fromRaw && !toRaw) {
|
if (!fromRaw && !toRaw) {
|
||||||
|
const page = syncListMode.value === 'listAll' ? syncListPage.value : 1;
|
||||||
|
const from =
|
||||||
|
syncListMode.value === 'listAll' && syncListMeta.fromCode
|
||||||
|
? syncListMeta.fromCode
|
||||||
|
: (page - 1) * 2000 + 1;
|
||||||
|
const to =
|
||||||
|
syncListMode.value === 'listAll' && syncListMeta.toCode
|
||||||
|
? syncListMeta.toCode
|
||||||
|
: from + 1999;
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm(
|
||||||
`کل مشتریان قابلخواندن از تهحساب بهعنوان شخص وارد ${brand.nameFa} میشوند. ادامه؟`,
|
`مشتریان صفحهٔ ${page} (کد ${from} تا ${to}) از تهحساب بهعنوان شخص وارد ${brand.nameFa} میشوند. برای صفحات بعدی، پیجینیت را بزنید و دوباره وارد کنید. ادامه؟`,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
body.listAll = true;
|
body.listAll = true;
|
||||||
|
body.page = page;
|
||||||
|
body.pageSize = 2000;
|
||||||
} else {
|
} else {
|
||||||
const fromCode = fromRaw ? Number(fromRaw) : 1;
|
const fromCode = fromRaw ? Number(fromRaw) : 1;
|
||||||
const toCode = toRaw ? Number(toRaw) : fromCode + 1999;
|
const toCode = toRaw ? Number(toRaw) : fromCode + 1999;
|
||||||
@@ -668,7 +750,7 @@ onMounted(load);
|
|||||||
<div class="sync-sub-section">
|
<div class="sync-sub-section">
|
||||||
<h4 class="sync-subhead">۱) وارد کردن دستهای با بازه کد مشتری</h4>
|
<h4 class="sync-subhead">۱) وارد کردن دستهای با بازه کد مشتری</h4>
|
||||||
<p class="sync-sub-desc">
|
<p class="sync-sub-desc">
|
||||||
بازهٔ دستی حداکثر ۲۰۰۰ کد است. اگر «از» و «تا» خالی باشد، همهٔ مشتریان خوانده میشوند.
|
بازهٔ دستی حداکثر ۲۰۰۰ کد است. اگر «از» و «تا» خالی باشد، مشتریان صفحهبهصفحه (هر صفحه ۲۰۰۰ کد) خوانده میشوند — بدون سقف ۵۰۰۰.
|
||||||
</p>
|
</p>
|
||||||
<div class="crm-form-grid-2">
|
<div class="crm-form-grid-2">
|
||||||
<UiInput v-model="sync.fromCode" type="number" min="1" dir="ltr" placeholder="از کد (مثلاً ۱)" />
|
<UiInput v-model="sync.fromCode" type="number" min="1" dir="ltr" placeholder="از کد (مثلاً ۱)" />
|
||||||
@@ -678,17 +760,31 @@ onMounted(load);
|
|||||||
<UiButton
|
<UiButton
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
:disabled="!sync.connectionId || syncBusy"
|
:disabled="!sync.connectionId || syncBusy"
|
||||||
@click="searchTahesab('range')"
|
@click="searchTahesab('range', 1)"
|
||||||
>
|
>
|
||||||
نمایش بازه
|
نمایش بازه / همه
|
||||||
</UiButton>
|
</UiButton>
|
||||||
<UiButton
|
<UiButton
|
||||||
:disabled="!sync.connectionId || syncBusy"
|
:disabled="!sync.connectionId || syncBusy"
|
||||||
@click="importBulk('range')"
|
@click="importBulk('range')"
|
||||||
>
|
>
|
||||||
وارد کردن کل بازه
|
وارد کردن این صفحه / بازه
|
||||||
</UiButton>
|
</UiButton>
|
||||||
</div>
|
</div>
|
||||||
|
<UiPagination
|
||||||
|
v-if="syncListMode === 'listAll' && (syncResults.length || syncListPage > 1)"
|
||||||
|
:page="syncListPage"
|
||||||
|
:total-pages="syncCodeTotalPages"
|
||||||
|
class="sync-code-pagination"
|
||||||
|
@update:page="onSyncCodePageChange"
|
||||||
|
/>
|
||||||
|
<p
|
||||||
|
v-if="syncListMode === 'listAll' && syncListMeta.fromCode"
|
||||||
|
class="sync-sub-desc"
|
||||||
|
>
|
||||||
|
صفحه {{ syncListPage }} — کد {{ syncListMeta.fromCode }} تا {{ syncListMeta.toCode }}
|
||||||
|
<span v-if="syncListMeta.hasMore"> · صفحه بعد برای کدهای بالاتر</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sync-sub-section">
|
<div class="sync-sub-section">
|
||||||
@@ -729,7 +825,7 @@ onMounted(load);
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="r in syncResults" :key="String(r.moshtariCode)">
|
<tr v-for="r in syncPagedResults" :key="String(r.moshtariCode)">
|
||||||
<td class="font-mono">{{ r.moshtariCode }}</td>
|
<td class="font-mono">{{ r.moshtariCode }}</td>
|
||||||
<td class="font-bold">{{ r.name || 'بدون نام' }}</td>
|
<td class="font-bold">{{ r.name || 'بدون نام' }}</td>
|
||||||
<td class="font-mono" dir="ltr">{{ r.tel }}</td>
|
<td class="font-mono" dir="ltr">{{ r.tel }}</td>
|
||||||
@@ -761,6 +857,15 @@ onMounted(load);
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<UiPagination
|
||||||
|
v-if="syncTableTotalPages > 1"
|
||||||
|
:page="syncTablePage"
|
||||||
|
:total-pages="syncTableTotalPages"
|
||||||
|
:total-items="syncResults.length"
|
||||||
|
:page-size="SYNC_TABLE_PAGE_SIZE"
|
||||||
|
class="sync-table-pagination"
|
||||||
|
@update:page="onSyncTablePageChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</UiCard>
|
</UiCard>
|
||||||
@@ -1046,6 +1151,11 @@ onMounted(load);
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sync-code-pagination,
|
||||||
|
.sync-table-pagination {
|
||||||
|
margin-top: var(--sp-3);
|
||||||
|
}
|
||||||
|
|
||||||
.sync-results-box {
|
.sync-results-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user