From b58f53769f004fb0ba6b5aedf55ef602850c7c97 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期日, 20 九月 2026 15:46:05 +0800
Subject: [PATCH] 天津豹鸣app 1.部署修改

---
 src/utils/warning.js |  137 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 137 insertions(+), 0 deletions(-)

diff --git a/src/utils/warning.js b/src/utils/warning.js
new file mode 100644
index 0000000..b0976c4
--- /dev/null
+++ b/src/utils/warning.js
@@ -0,0 +1,137 @@
+/**
+ * 鍒版湡棰勮璁$畻宸ュ叿
+ *
+ * 鍚勪笟鍔℃ā鍧楃殑棰勮闃堝�间笉鍚岋細
+ *   杞﹁締骞存/淇濋櫓銆佹埧灞嬬璧佸悎鍚屻�佽澶囨牎鍑嗐�佹姤琛ㄦ湁鏁堟湡 鈫� 30 澶�
+ *   瀹藉甫涓庨�氫俊涓嬫缂磋垂 鈫� 15 澶�
+ *   閿�鍞彴璐﹀悎鍚屽師浠剁害瀹氬洖浼犳棩鏈� 鈫� 鎻愬墠 N 澶╋紙鐢遍」鐩鐞嗛厤缃喅瀹氾級
+ * 缁熶竴鐢� warnDays 浼犲叆锛岄伩鍏嶅悇椤甸潰鍚勫啓涓�濂楁棩鏈熷樊閫昏緫銆�
+ */
+
+const MS_PER_DAY = 24 * 60 * 60 * 1000;
+
+/**
+ * 瑙f瀽鍚庣杩斿洖鐨勬棩鏈燂紝鍏煎鏃堕棿鎴� / Date / 'YYYY-MM-DD' / 'YYYY/MM/DD' / 'YYYY-MM-DD HH:mm:ss'
+ */
+export function parseDate(value) {
+  if (value === undefined || value === null || value === "") return null;
+  if (value instanceof Date) {
+    return Number.isNaN(value.getTime()) ? null : new Date(value.getTime());
+  }
+  if (typeof value === "number") {
+    const d = new Date(value);
+    return Number.isNaN(d.getTime()) ? null : d;
+  }
+  const matched = String(value)
+    .trim()
+    .replace(/\//g, "-")
+    .replace("T", " ")
+    .match(/^(\d{4})-(\d{1,2})-(\d{1,2})/);
+  if (!matched) return null;
+  const date = new Date(
+    Number(matched[1]),
+    Number(matched[2]) - 1,
+    Number(matched[3])
+  );
+  return Number.isNaN(date.getTime()) ? null : date;
+}
+
+/**
+ * 璺濈鍒版湡鏃ョ殑鍓╀綑澶╂暟
+ * 姝f暟=杩樻湁 N 澶╋紝0=浠婂ぉ鍒版湡锛岃礋鏁�=宸查�炬湡 N 澶╋紝鏃犳硶瑙f瀽杩斿洖 null
+ */
+export function remainDays(dateStr) {
+  const target = parseDate(dateStr);
+  if (!target) return null;
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  target.setHours(0, 0, 0, 0);
+  return Math.round((target.getTime() - today.getTime()) / MS_PER_DAY);
+}
+
+/**
+ * 棰勮绾у埆锛歰verdue 宸查�炬湡 / warning 鍗冲皢鍒版湡 / normal 姝e父 / none 鏃犳棩鏈�
+ */
+export function warnLevel(dateStr, warnDays = 30) {
+  const days = remainDays(dateStr);
+  if (days === null) return "none";
+  if (days < 0) return "overdue";
+  if (days <= warnDays) return "warning";
+  return "normal";
+}
+
+export function warnColor(level) {
+  const colors = {
+    overdue: "#f56c6c",
+    warning: "#ff9900",
+    normal: "#19be6b",
+    none: "#909399",
+  };
+  return colors[level] || colors.none;
+}
+
+export function warnText(level, days) {
+  if (level === "overdue") return `宸查�炬湡${Math.abs(days)}澶ー;
+  if (level === "warning") return days === 0 ? "浠婂ぉ鍒版湡" : `鍗冲皢鍒版湡(${days}澶�)`;
+  if (level === "normal") return "姝e父";
+  return "";
+}
+
+/**
+ * 涓�娆℃嬁鍒版覆鏌撻璀︽爣璁版墍闇�鐨勫叏閮ㄤ俊鎭�
+ * @returns {{days: number|null, level: string, color: string, text: string}}
+ */
+export function getWarnInfo(dateStr, warnDays = 30) {
+  const days = remainDays(dateStr);
+  const level = warnLevel(dateStr, warnDays);
+  return {
+    days,
+    level,
+    color: warnColor(level),
+    text: warnText(level, days),
+  };
+}
+
+/**
+ * 鏄惁宸查�炬湡鎴栧嵆灏嗗埌鏈燂紙鐢ㄤ簬鍒楄〃绛涢�夈�侀珮浜級
+ */
+export function isExpiringSoon(dateStr, warnDays = 30) {
+  const level = warnLevel(dateStr, warnDays);
+  return level === "overdue" || level === "warning";
+}
+
+/**
+ * 鎸夊ぉ/鏈堟帹绠楁棩鏈燂紝鐢ㄤ簬"涓嬫缂磋垂鏃ユ湡 = 瀹夎鏃ユ湡 + 缂磋垂鍛ㄦ湡鏈�"杩欑被鑷姩璁$畻
+ * @returns {string} 'YYYY-MM-DD'锛屾棤娉曡绠楄繑鍥� ''
+ */
+export function addMonths(dateStr, months) {
+  const base = parseDate(dateStr);
+  if (!base || !Number.isFinite(Number(months))) return "";
+  const day = base.getDate();
+  base.setDate(1);
+  base.setMonth(base.getMonth() + Number(months));
+  // 澶勭悊 1/31 + 1 鏈堣繖绫绘孩鍑猴紝鍥為��鍒板綋鏈堟渶鍚庝竴澶�
+  const lastDay = new Date(base.getFullYear(), base.getMonth() + 1, 0).getDate();
+  base.setDate(Math.min(day, lastDay));
+  const pad = n => String(n).padStart(2, "0");
+  return `${base.getFullYear()}-${pad(base.getMonth() + 1)}-${pad(base.getDate())}`;
+}
+
+/**
+ * 鐢熸垚 [start, end] 涔嬮棿鐨勬湀浠藉垪琛紙鍚灏撅級锛岀敤浜庢埧灞嬬璧佺殑鏈堝害璐圭敤瀛愯〃
+ * @returns {string[]} ['2026-01', '2026-02', ...]
+ */
+export function monthRange(startStr, endStr) {
+  const start = parseDate(startStr);
+  const end = parseDate(endStr);
+  if (!start || !end || end < start) return [];
+  const months = [];
+  const cursor = new Date(start.getFullYear(), start.getMonth(), 1);
+  const last = new Date(end.getFullYear(), end.getMonth(), 1);
+  while (cursor <= last && months.length < 600) {
+    const pad = n => String(n).padStart(2, "0");
+    months.push(`${cursor.getFullYear()}-${pad(cursor.getMonth() + 1)}`);
+    cursor.setMonth(cursor.getMonth() + 1);
+  }
+  return months;
+}

--
Gitblit v1.9.3