From 001ab0748537daef249d3f03775d1441d214661e Mon Sep 17 00:00:00 2001 From: Kavehhn174 Date: Wed, 19 Aug 2026 06:45:55 +0330 Subject: [PATCH] feat(property): add Iranian date fields with Date conversion and fix edit coordinates/map loading --- src/utils/jalali.js | 164 ++++++++++++++++ src/views/pages/Property/CreateProperty.vue | 204 ++++++++++++++++---- src/views/uikit/MapComponent.vue | 35 +++- 3 files changed, 364 insertions(+), 39 deletions(-) create mode 100644 src/utils/jalali.js diff --git a/src/utils/jalali.js b/src/utils/jalali.js new file mode 100644 index 0000000..68bcfeb --- /dev/null +++ b/src/utils/jalali.js @@ -0,0 +1,164 @@ +/** + * Jalali (Solar Hijri / Shamsi) calendar conversion utilities + */ + +export const persianMonths = [ + { value: 1, label: 'فروردین' }, + { value: 2, label: 'اردیبهشت' }, + { value: 3, label: 'خرداد' }, + { value: 4, label: 'تیر' }, + { value: 5, label: 'مرداد' }, + { value: 6, label: 'شهریور' }, + { value: 7, label: 'مهر' }, + { value: 8, label: 'آبان' }, + { value: 9, label: 'آذر' }, + { value: 10, label: 'دی' }, + { value: 11, label: 'بهمن' }, + { value: 12, label: 'اسفند' } +]; + +/** + * Converts Persian and Arabic digits in a string to English digits + */ +export function toEnglishDigits(str) { + if (str === null || str === undefined) return ''; + return String(str) + .replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d)) + .replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d)); +} + +/** + * Converts Jalali date (year, month, day) to Gregorian date (gy, gm, gd) + */ +export function jalaliToGregorian(jy, jm, jd) { + jy = Number(jy); + jm = Number(jm); + jd = Number(jd); + + let gy = jy > 979 ? 1600 : 621; + jy = jy > 979 ? jy - 979 : jy; + + let days = + 365 * jy + + Math.floor(jy / 33) * 8 + + Math.floor(((jy % 33) + 3) / 4) + + 78 + + jd + + (jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186); + + gy += 400 * Math.floor(days / 146097); + days %= 146097; + + if (days > 36524) { + gy += 100 * Math.floor(--days / 36524); + days %= 36524; + if (days >= 365) days++; + } + + gy += 4 * Math.floor(days / 1461); + days %= 1461; + + if (days > 365) { + gy += Math.floor((days - 1) / 365); + days = (days - 1) % 365; + } + + let gd = days + 1; + const isLeap = (gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0; + const sal_a = [0, 31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + let gm; + for (gm = 0; gm < 13; gm++) { + const v = sal_a[gm]; + if (gd <= v) break; + gd -= v; + } + + return { gy, gm, gd }; +} + +/** + * Converts Gregorian date (gy, gm, gd) to Jalali date (jy, jm, jd) + */ +export function gregorianToJalali(gy, gm, gd) { + gy = Number(gy); + gm = Number(gm); + gd = Number(gd); + + const g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + let jy = gy > 1600 ? 979 : 0; + gy = gy > 1600 ? gy - 1600 : gy - 621; + + const gy2 = gm > 2 ? gy + 1 : gy; + let days = + 365 * gy + + Math.floor((gy2 + 3) / 4) - + Math.floor((gy2 + 99) / 100) + + Math.floor((gy2 + 399) / 400) - + 80 + + gd + + g_d_m[gm - 1]; + + jy += 33 * Math.floor(days / 12053); + days %= 12053; + + jy += 4 * Math.floor(days / 1461); + days %= 1461; + + if (days > 365) { + jy += Math.floor((days - 1) / 365); + days = (days - 1) % 365; + } + + const jm = days < 186 ? 1 + Math.floor(days / 31) : 7 + Math.floor((days - 186) / 30); + const jd = 1 + (days < 186 ? days % 31 : (days - 186) % 30); + + return { jy, jm, jd }; +} + +/** + * Converts Jalali year, month, day to a standard JS Date object at 12:00 UTC + */ +export function jalaliToDate(jy, jm, jd) { + const y = parseInt(toEnglishDigits(jy), 10); + const m = parseInt(toEnglishDigits(jm), 10); + const d = parseInt(toEnglishDigits(jd), 10); + + if (isNaN(y) || isNaN(m) || isNaN(d) || y <= 0 || m < 1 || m > 12 || d < 1 || d > 31) { + return null; + } + + const { gy, gm, gd } = jalaliToGregorian(y, m, d); + return new Date(Date.UTC(gy, gm - 1, gd, 12, 0, 0)); +} + +/** + * Converts a JS Date object, ISO string, or legacy Jalali string (YYYY/MM/DD) to { jy, jm, jd } + */ +export function parseDateToJalali(dateVal) { + if (!dateVal) return null; + + // Check if it's already a JS Date or ISO string + if (dateVal instanceof Date || (typeof dateVal === 'string' && (dateVal.includes('T') || dateVal.includes('-')))) { + const d = new Date(dateVal); + if (!isNaN(d.getTime())) { + return gregorianToJalali(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate()); + } + } + + // Check if it's a legacy string format e.g. "1399/04/15" or "۱۳۹۹/۰۴/۱۵" + if (typeof dateVal === 'string' && dateVal.includes('/')) { + const clean = toEnglishDigits(dateVal.trim()); + const parts = clean.split('/'); + if (parts.length === 3) { + const jy = parseInt(parts[0], 10); + const jm = parseInt(parts[1], 10); + const jd = parseInt(parts[2], 10); + if (!isNaN(jy) && !isNaN(jm) && !isNaN(jd)) { + return { jy, jm, jd }; + } + } + } + + return null; +} diff --git a/src/views/pages/Property/CreateProperty.vue b/src/views/pages/Property/CreateProperty.vue index 7a255fc..389f31f 100644 --- a/src/views/pages/Property/CreateProperty.vue +++ b/src/views/pages/Property/CreateProperty.vue @@ -1,5 +1,5 @@