家谱现有接口调试50%

This commit is contained in:
rain
2026-07-09 17:29:25 +08:00
commit 6050508144
262 changed files with 63354 additions and 0 deletions
+258
View File
@@ -0,0 +1,258 @@
/**
* 日期时间工具类。
*
* 说明:
* - 页面只需要展示日期时,统一使用这里的方法,避免每个页面重复处理格式。
* - 后端常见日期格式为 `YYYY-MM-DD` 或 `YYYY-MM-DD HH:mm:ss`。
* - 纯日期字符串用本地时区解析,避免浏览器按 UTC 解析导致日期偏移。
*/
class DateUtil {
/**
* 转为“5月9日”格式。
*
* @param {string|Date} dateStr 后端时间字符串或 Date 对象
* @returns {string}
*/
static formatDateToMonthDay(dateStr) {
try {
// 第一步:把传入的字符串或 Date 对象统一转成 Date。
const date = DateUtil._parseDate(dateStr);
// 第二步:无效日期直接返回空字符串,页面不显示错误内容。
if (!DateUtil._isValidDate(date)) {
return '';
}
// 第三步:取月份和日期,拼接成中文展示格式。
const month = date.getMonth() + 1;
const day = date.getDate();
return `${month}${day}`;
} catch (error) {
return '';
}
}
/**
* 转为“2026年5月9日”格式。
*
* @param {string|Date} dateStr 后端时间字符串或 Date 对象
* @returns {string}
*/
static formatDateToYearMonthDay(dateStr) {
try {
// 第一步:统一解析日期。
const date = DateUtil._parseDate(dateStr);
// 第二步:无效日期不继续格式化。
if (!DateUtil._isValidDate(date)) {
return '';
}
// 第三步:分别取年月日,拼成完整中文日期。
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return `${year}${month}${day}`;
} catch (error) {
return '';
}
}
/**
* 转为“YYYY-MM-DD”格式,适合列表右侧日期展示。
*
* @param {string|Date} dateStr 后端时间字符串或 Date 对象
* @returns {string}
*/
static formatDateToYMD(dateStr) {
try {
// 第一步:统一解析日期。
const date = DateUtil._parseDate(dateStr);
// 第二步:无效日期返回空字符串。
if (!DateUtil._isValidDate(date)) {
return '';
}
// 第三步:月份和日期补零,保证列表展示宽度稳定。
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
} catch (error) {
return '';
}
}
/**
* 转为“YYYY-MM-DD HH:mm:ss”格式,适合资金流水、订单时间等完整时间展示。
*
* @param {string|Date} dateStr 后端时间字符串或 Date 对象
* @returns {string}
*/
static formatDateTimeToYMDHMS(dateStr) {
try {
// 第一步:统一解析日期。
const date = DateUtil._parseDate(dateStr);
// 第二步:无效日期返回空字符串,避免页面显示 Invalid Date。
if (!DateUtil._isValidDate(date)) {
return '';
}
// 第三步:分别补齐年月日时分秒,保证表格宽度稳定。
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
const second = String(date.getSeconds()).padStart(2, '0');
// 第四步:拼接成后端和页面都常用的完整时间格式。
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
} catch (error) {
return '';
}
}
/**
* 转为“HH:mm:ss”格式,适合日志或轮询状态时间展示。
*
* @param {string|number|Date} dateStr 后端时间、时间戳或 Date 对象
* @returns {string}
*/
/**
* 转为“YYYY-MM-DD HH:mm”格式,适合评论发布时间等不需要精确到秒的场景。
*
* @param {string|Date} dateStr 后端时间字符串或 Date 对象
* @returns {string}
*/
static formatDateTimeToYMDHM(dateStr) {
try {
// 第一步:统一解析传入的时间。
const date = DateUtil._parseDate(dateStr);
// 第二步:无效时间返回空字符串,避免页面显示 Invalid Date。
if (!DateUtil._isValidDate(date)) {
return '';
}
// 第三步:只补齐到分钟,不展示秒。
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
} catch (error) {
return '';
}
}
static formatTimeToHMS(dateStr) {
try {
// 第一步:时间戳直接转 Date,字符串和 Date 继续走统一解析。
const date = typeof dateStr === 'number' ? new Date(dateStr) : DateUtil._parseDate(dateStr);
// 第二步:无效时间返回空字符串。
if (!DateUtil._isValidDate(date)) {
return '';
}
// 第三步:补齐时分秒,保持展示稳定。
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
const second = String(date.getSeconds()).padStart(2, '0');
return `${hour}:${minute}:${second}`;
} catch (error) {
return '';
}
}
/**
* 获取当前时间。
*
* @returns {Date}
*/
static getCurrentDate() {
return new Date();
}
/**
* 判断两个日期是否为同一天。
*
* @param {string|Date} date1 第一个日期
* @param {string|Date} date2 第二个日期
* @returns {boolean}
*/
static isSameDay(date1, date2) {
try {
// 第一步:两个入参都先转成 Date。
const d1 = DateUtil._parseDate(date1);
const d2 = DateUtil._parseDate(date2);
// 第二步:任意一个日期无效,就判定不是同一天。
if (!DateUtil._isValidDate(d1) || !DateUtil._isValidDate(d2)) {
return false;
}
// 第三步:同时比较年、月、日。
return d1.getFullYear() === d2.getFullYear()
&& d1.getMonth() === d2.getMonth()
&& d1.getDate() === d2.getDate();
} catch (error) {
return false;
}
}
/**
* 统一解析日期。
*
* @param {string|Date} dateStr 日期字符串或 Date 对象
* @returns {Date|null}
*/
static _parseDate(dateStr) {
// 第一步:如果已经是 Date 对象,直接返回。
if (dateStr instanceof Date) {
return dateStr;
}
// 第二步:非字符串不尝试解析。
if (typeof dateStr !== 'string') {
return null;
}
// 第三步:去掉首尾空格,兼容接口偶发空白。
const value = dateStr.trim();
if (!value) {
return null;
}
// 第四步:纯日期用斜杠格式解析,避免浏览器按 UTC 导致日期前后偏移。
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
return new Date(value.replace(/-/g, '/'));
}
// 第五步:带时间的字符串交给浏览器 Date 解析。
return new Date(value);
}
/**
* 判断 Date 对象是否有效。
*
* @param {Date|null} date 日期对象
* @returns {boolean}
*/
static _isValidDate(date) {
return date instanceof Date && !Number.isNaN(date.getTime());
}
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = DateUtil;
} else if (typeof window !== 'undefined') {
window.DateUtil = DateUtil;
}
+61
View File
@@ -0,0 +1,61 @@
(function (root, factory) {
// 表单工具只处理跨页面通用转换,具体业务字段留在页面脚本中组织。
if (typeof module === 'object' && module.exports) {
module.exports = factory();
return;
}
root.FormUtil = factory();
})(typeof globalThis !== 'undefined' ? globalThis : window, function () {
'use strict';
function trimOrUndefined(value) {
if (value === undefined || value === null) return undefined;
var text = String(value).trim();
return text ? text : undefined;
}
function toNumberOrUndefined(value) {
var text = trimOrUndefined(value);
if (text === undefined) return undefined;
var numberValue = Number(text);
return Number.isNaN(numberValue) ? undefined : numberValue;
}
function toBoolean(value) {
if (value === true || value === false) return value;
var text = trimOrUndefined(value);
if (text === undefined) return false;
return !['0', 'false', 'off', 'no'].includes(text.toLowerCase());
}
function getControlValue(control) {
if (!control || control.disabled || !control.name) return undefined;
if (control.type === 'checkbox') return control.checked ? (control.value || 'on') : undefined;
if (control.type === 'radio') return control.checked ? control.value : undefined;
return control.value;
}
function getFormValues(form) {
var values = {};
if (!form || !form.elements) return values;
Array.prototype.forEach.call(form.elements, function (control) {
var value = getControlValue(control);
if (value !== undefined) values[control.name] = value;
});
return values;
}
return {
trimOrUndefined: trimOrUndefined,
toNumberOrUndefined: toNumberOrUndefined,
toBoolean: toBoolean,
getFormValues: getFormValues
};
});
+34
View File
@@ -0,0 +1,34 @@
(function (root, factory) {
// 消息工具同时支持浏览器页面和 Node 测试,避免页面脚本直接使用原生 alert。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.MessageUtil = factory(root);
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var DEFAULT_OPTIONS = {
skin: 'auth-layer-message',
time: 2200
};
function show(message, options) {
var text = String(message || '');
var settings = Object.assign({}, DEFAULT_OPTIONS, options || {});
if (root.layui && root.layui.layer && root.layui.layer.msg) {
root.layui.layer.msg(text, settings);
return;
}
if (root.console && root.console.warn) {
root.console.warn(text);
}
}
return {
show: show
};
});
+120
View File
@@ -0,0 +1,120 @@
(function (root, factory) {
// 请求工具封装 URL、Header 和响应解包,业务路径由 api-client 统一提供。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.RequestUtil = factory(root);
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
function trimSlashes(value) {
return String(value || '').replace(/^\/+|\/+$/g, '');
}
function joinUrl(baseUrl, path) {
var base = String(baseUrl || '').replace(/\/+$/g, '');
var cleanPath = '/' + trimSlashes(path);
return base + cleanPath;
}
function appendQuery(url, query) {
if (!query) return url;
var params = new URLSearchParams();
Object.keys(query).forEach(function (key) {
var value = query[key];
if (value === undefined || value === null || value === '') return;
params.append(key, value);
});
var queryText = params.toString();
return queryText ? url + '?' + queryText : url;
}
function isFormData(body) {
return typeof FormData !== 'undefined' && body instanceof FormData;
}
function createHttpError(message, detail) {
var error = new Error(message);
Object.keys(detail || {}).forEach(function (key) {
error[key] = detail[key];
});
return error;
}
async function parseJson(response) {
try {
return await response.json();
} catch (error) {
return null;
}
}
function unwrapResponse(payload) {
if (!payload || typeof payload !== 'object') return payload;
if (payload.code !== undefined && payload.code !== 200) {
throw createHttpError(payload.msg || '接口请求失败', {
code: payload.code,
response: payload
});
}
if (Object.prototype.hasOwnProperty.call(payload, 'data')) return payload.data;
if (Object.prototype.hasOwnProperty.call(payload, 'rows')) {
return {
rows: payload.rows,
total: payload.total
};
}
return payload;
}
function createRequester(options) {
var settings = options || {};
var fetchImpl = settings.fetchImpl || root.fetch;
return async function request(method, path, requestOptions) {
var req = requestOptions || {};
var headers = Object.assign({}, req.headers || {});
var token = settings.getToken ? settings.getToken() : '';
var tokenHeaderName = settings.tokenHeaderName || 'Authorization';
var url = appendQuery(joinUrl(settings.baseUrl, path), req.query);
var body = req.body;
headers.clientid = settings.clientId;
if (token && req.auth !== false) headers[tokenHeaderName] = 'Bearer ' + token;
if (body !== undefined && body !== null && !isFormData(body)) {
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
body = JSON.stringify(body);
}
var response = await fetchImpl(url, {
method: method,
headers: headers,
body: body
});
var payload = await parseJson(response);
if (!response.ok) {
throw createHttpError((payload && payload.msg) || '接口请求失败', {
status: response.status,
response: payload
});
}
return unwrapResponse(payload);
};
}
return {
createRequester: createRequester,
joinUrl: joinUrl,
appendQuery: appendQuery
};
});
+41
View File
@@ -0,0 +1,41 @@
(function (root, factory) {
// 存储工具同时支持浏览器页面和 Node 测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory();
return;
}
root.StorageUtil = factory();
})(typeof globalThis !== 'undefined' ? globalThis : window, function () {
'use strict';
function read(storage, key) {
try {
return storage && storage.getItem(key);
} catch (error) {
return null;
}
}
function write(storage, key, value) {
try {
if (storage) storage.setItem(key, value);
} catch (error) {
// localStorage 可能因为隐私模式或配额限制失败,调用方无需因此中断页面。
}
}
function remove(storage, key) {
try {
if (storage) storage.removeItem(key);
} catch (error) {
// 删除失败时保持静默,避免影响退出登录等后续流程。
}
}
return {
read: read,
write: write,
remove: remove
};
});