field-formatter.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /**
  2. * H5版本的字段格式化工具函数
  3. * 基于小程序版本改造,适配H5环境
  4. */
  5. /**
  6. * 获取字典翻译 - H5版本
  7. * @param {string} cbName - 字典名称
  8. * @param {string|number} value - 值
  9. * @param {string} cacheKey - 缓存键
  10. * @param {Map} dictCache - 字典缓存
  11. * @param {Function} updateCallback - 更新回调函数
  12. */
  13. const getDictTranslation = async (cbName, value, cacheKey, dictCache, updateCallback) => {
  14. try {
  15. console.log(`🔍 获取字典翻译 [${cbName}:${value}]`);
  16. // 使用 loadObjpOpt 接口调用字典
  17. const result = await request.post(
  18. '/service?ssServ=loadObjpOpt&objectpickerdropdown1=1',
  19. {
  20. objectpickerparam: JSON.stringify({
  21. input: "false",
  22. codebook: cbName
  23. }),
  24. objectpickertype: 2, // 2表示获取要回显的一项
  25. objectpickervalue: value // 需回显的值
  26. },
  27. { loading: false, formData: true }
  28. );
  29. console.log(`字典翻译 [${cbName}:${value}] API返回:`, result);
  30. // 处理返回数据格式:{"result":{"622182":"会议室A"}}
  31. let translatedValue = value; // 默认返回原值
  32. if (result && result.data && result.data.result) {
  33. // 从result对象中根据value查找对应的翻译
  34. translatedValue = result.data.result[value] || value;
  35. console.log(`字典翻译结果: ${value} -> ${translatedValue}`);
  36. }
  37. dictCache.set(cacheKey, translatedValue);
  38. // 触发页面更新
  39. if (updateCallback) {
  40. updateCallback();
  41. }
  42. } catch (error) {
  43. console.error(`字典翻译失败 [${cbName}:${value}]:`, error);
  44. dictCache.set(cacheKey, value);
  45. }
  46. };
  47. /**
  48. * 格式化字段值(处理字典翻译、日期格式等)- H5版本
  49. * @param {Object} fieldObj - 字段对象 {field, value}
  50. * @param {Map} dictCache - 字典缓存
  51. * @param {Function} getDictTranslationFn - 字典翻译函数
  52. * @returns {string} 格式化后的值
  53. */
  54. const formatFieldValue = (fieldObj, dictCache, getDictTranslationFn) => {
  55. if (!fieldObj || !fieldObj.field || fieldObj.value === undefined) {
  56. return '';
  57. }
  58. const { field, value } = fieldObj;
  59. // 空值处理
  60. if (value === null || value === undefined || value === '') {
  61. return '';
  62. }
  63. // 如果字段有字典配置,进行字典翻译
  64. if (field.cbName) {
  65. const cacheKey = `${field.cbName}_${value}`;
  66. // 先从缓存中查找
  67. if (dictCache.has(cacheKey)) {
  68. return dictCache.get(cacheKey);
  69. }
  70. // 缓存中没有,异步获取翻译
  71. if (getDictTranslationFn) {
  72. getDictTranslationFn(field.cbName, value, cacheKey, dictCache);
  73. }
  74. // 返回原值作为临时显示
  75. return value;
  76. }
  77. // 时间格式化(优先检查fmt字段)
  78. if (field.fmt) {
  79. return h5FormatDate(value, field.fmt);
  80. }
  81. // 日期格式化(兼容旧逻辑)
  82. if (field.type === 'date' || field.name.toLowerCase().includes('date') ||
  83. field.name.toLowerCase().includes('time')) {
  84. return h5FormatDate(value);
  85. }
  86. // 数字格式化
  87. if (field.type === 'number' && typeof value === 'number') {
  88. return value.toLocaleString();
  89. }
  90. // 布尔值格式化
  91. if (field.type === 'boolean' || typeof value === 'boolean') {
  92. return value ? '是' : '否';
  93. }
  94. // 默认返回字符串
  95. return String(value);
  96. };
  97. /**
  98. * 解析特殊日期格式:Jul 4, 2025, 6:00:00 AM
  99. * @param {string} dateStr - 日期字符串
  100. * @returns {object|null} 解析后的日期对象或null
  101. */
  102. const parseSpecialDateFormat = (dateStr) => {
  103. try {
  104. // 匹配格式:Jul 4, 2025, 6:00:00 AM
  105. const regex = /^(\w{3})\s+(\d{1,2}),\s+(\d{4}),\s+(\d{1,2}):(\d{2}):(\d{2})\s+(AM|PM)$/;
  106. const match = dateStr.match(regex);
  107. if (!match) return null;
  108. const [, monthStr, day, year, hour, minute, second, ampm] = match;
  109. // 月份映射
  110. const months = {
  111. 'Jan': 0, 'Feb': 1, 'Mar': 2, 'Apr': 3, 'May': 4, 'Jun': 5,
  112. 'Jul': 6, 'Aug': 7, 'Sep': 8, 'Oct': 9, 'Nov': 10, 'Dec': 11
  113. };
  114. const month = months[monthStr];
  115. if (month === undefined) return null;
  116. // 处理12小时制
  117. let hour24 = parseInt(hour);
  118. if (ampm === 'PM' && hour24 !== 12) {
  119. hour24 += 12;
  120. } else if (ampm === 'AM' && hour24 === 12) {
  121. hour24 = 0;
  122. }
  123. return {
  124. year: parseInt(year),
  125. month: month,
  126. day: parseInt(day),
  127. hour: hour24,
  128. minute: parseInt(minute),
  129. second: parseInt(second)
  130. };
  131. } catch (error) {
  132. console.warn('解析特殊日期格式失败:', dateStr, error);
  133. return null;
  134. }
  135. };
  136. /**
  137. * 格式化日期对象
  138. * @param {object} dateObj - 日期对象 {year, month, day, hour, minute, second}
  139. * @param {string} format - 格式字符串
  140. * @returns {string} 格式化后的字符串
  141. */
  142. const formatDateObject = (dateObj, format) => {
  143. const { year, month, day, hour, minute, second } = dateObj;
  144. return format
  145. .replace(/YYYY/g, year)
  146. .replace(/yyyy/g, year)
  147. .replace(/MM/g, String(month + 1).padStart(2, '0'))
  148. .replace(/DD/g, String(day).padStart(2, '0'))
  149. .replace(/dd/g, String(day).padStart(2, '0'))
  150. .replace(/HH/g, String(hour).padStart(2, '0'))
  151. .replace(/hh/g, String(hour).padStart(2, '0'))
  152. .replace(/mm/g, String(minute).padStart(2, '0'))
  153. .replace(/ss/g, String(second).padStart(2, '0'));
  154. };
  155. /**
  156. * 增强的日期格式化函数 - H5版本
  157. * @param {string|Date} date - 日期,支持多种格式如 "Jul 4, 2025, 6:00:00 AM"
  158. * @param {string} format - 格式,如 'YYYY-MM-DD HH:mm:ss'
  159. * @returns {string} 格式化后的日期
  160. */
  161. const h5FormatDate = (date, format = 'YYYY-MM-DD HH:mm:ss') => {
  162. if (!date) return '';
  163. try {
  164. console.log(`🔧 h5FormatDate 开始处理:`, date, '目标格式:', format);
  165. // 手动解析特殊格式:Jul 4, 2025, 6:00:00 AM
  166. const parseResult = parseSpecialDateFormat(date);
  167. if (parseResult) {
  168. console.log(`🔧 手动解析成功:`, parseResult);
  169. const result = formatDateObject(parseResult, format);
  170. console.log(`✅ 手动格式化结果:`, result);
  171. return result;
  172. }
  173. // 使用 Day.js 解析日期(更强大的日期解析能力)
  174. if (typeof window !== 'undefined' && window.dayjs) {
  175. const dayObj = window.dayjs(date);
  176. console.log(`🔧 使用 Day.js 解析:`, dayObj.isValid(), dayObj.toString());
  177. if (dayObj.isValid()) {
  178. const result = dayObj.format(format);
  179. console.log(`✅ Day.js 格式化结果:`, result);
  180. return result;
  181. }
  182. }
  183. // 降级到原生 Date(如果 Day.js 未加载)
  184. console.log(`🔧 Day.js 未加载,使用原生 Date`);
  185. const d = new Date(date);
  186. console.log(`🔧 解析后的日期对象:`, d, '是否有效:', !isNaN(d.getTime()));
  187. // 检查日期是否有效
  188. if (isNaN(d.getTime())) {
  189. console.warn('❌ 无效日期格式:', date);
  190. return date; // 无效日期返回原值
  191. }
  192. const year = d.getFullYear();
  193. const month = String(d.getMonth() + 1).padStart(2, '0');
  194. const day = String(d.getDate()).padStart(2, '0');
  195. const hours = String(d.getHours()).padStart(2, '0');
  196. const minutes = String(d.getMinutes()).padStart(2, '0');
  197. const seconds = String(d.getSeconds()).padStart(2, '0');
  198. // 支持多种格式模式
  199. let result = format
  200. .replace(/YYYY/g, year)
  201. .replace(/yyyy/g, year)
  202. .replace(/MM/g, month)
  203. .replace(/DD/g, day)
  204. .replace(/dd/g, day)
  205. .replace(/HH/g, hours)
  206. .replace(/hh/g, hours)
  207. .replace(/mm/g, minutes)
  208. .replace(/ss/g, seconds);
  209. console.log(`日期格式化: ${date} -> ${result} (格式: ${format})`);
  210. return result;
  211. } catch (error) {
  212. console.error('日期格式化异常:', date, format, error);
  213. return date; // 异常时返回原值
  214. }
  215. };
  216. /**
  217. * 异步格式化字段值 - 支持字典转换
  218. * @param {object} fieldData - 字段数据 {field, value}
  219. * @param {Map} dictCache - 字典缓存
  220. * @returns {Promise<string>} 格式化后的值
  221. */
  222. const formatFieldValueAsync = async (fieldData, dictCache) => {
  223. if (!fieldData || !fieldData.field) {
  224. return fieldData?.value || '';
  225. }
  226. const { field, value } = fieldData;
  227. // 空值处理
  228. if (value === null || value === undefined || value === '') {
  229. return '';
  230. }
  231. // 优先处理时间格式化(如果字段有fmt属性)
  232. if (field.fmt) {
  233. try {
  234. console.log(`🕐 格式化时间字段 [${field.name}]:`, value, '格式:', field.fmt);
  235. console.log(`🔧 调用 h5FormatDate 前...`);
  236. const formattedTime = h5FormatDate(value, field.fmt);
  237. console.log(`🔧 调用 h5FormatDate 后,结果:`, formattedTime);
  238. console.log(`✅ 时间格式化结果:`, formattedTime);
  239. return formattedTime;
  240. } catch (error) {
  241. console.error('❌ 时间格式化失败:', field.name, value, error);
  242. // 格式化失败,返回原值
  243. return value;
  244. }
  245. }
  246. // 如果字段有字典配置,进行字典转换
  247. if (field.cbName) {
  248. const cacheKey = `${field.cbName}_${value}`;
  249. // 先从缓存中查找
  250. if (dictCache && dictCache.has(cacheKey)) {
  251. return dictCache.get(cacheKey);
  252. }
  253. // 缓存中没有,异步获取翻译
  254. try {
  255. const result = await request.post(
  256. `/service?ssServ=loadObjpOpt&objectpickerdropdown1=1`,
  257. {
  258. objectpickerparam: JSON.stringify({
  259. input: "false",
  260. codebook: field.cbName
  261. }),
  262. objectpickertype: 2,
  263. objectpickervalue: value
  264. },
  265. {
  266. loading: false,
  267. formData: true
  268. }
  269. );
  270. if (result && result.data && result.data.result && result.data.result[value]) {
  271. const translatedValue = result.data.result[value];
  272. // 缓存结果
  273. if (dictCache) {
  274. dictCache.set(cacheKey, translatedValue);
  275. }
  276. return translatedValue;
  277. }
  278. } catch (error) {
  279. console.warn('字典转换失败:', field.cbName, value, error);
  280. }
  281. // 转换失败,返回原值
  282. return value;
  283. }
  284. // 日期格式化
  285. if (field.type === 3 && field.fmt) {
  286. return h5FormatDate(value, field.fmt);
  287. }
  288. // 数字格式化
  289. if (field.type === 2 && typeof value === 'number') {
  290. return value.toLocaleString();
  291. }
  292. // 默认返回字符串
  293. return value.toString();
  294. };
  295. /**
  296. * 获取字典所有选项 - 用于下拉菜单
  297. * @param {string} cbName - 字典名称
  298. * @param {Map} dictCache - 字典缓存
  299. * @returns {Promise<Array>} 选项列表 [{n: '显示名', v: '值'}]
  300. */
  301. const getDictOptions = async (cbName, dictCache) => {
  302. const cacheKey = `options_${cbName}`;
  303. // 检查缓存
  304. if (dictCache && dictCache.has(cacheKey)) {
  305. return dictCache.get(cacheKey);
  306. }
  307. try {
  308. console.log(`🔍 获取字典选项 [${cbName}]`);
  309. const result = await request.post(
  310. `/service?ssServ=loadObjpOpt&objectpickerdropdown1=1`,
  311. {
  312. objectpickerparam: JSON.stringify({
  313. input: "false",
  314. codebook: cbName
  315. }),
  316. objectpickertype: 1,
  317. objectpickersearchAll: 1
  318. },
  319. {
  320. loading: false,
  321. formData: true
  322. }
  323. );
  324. console.log(`字典选项 [${cbName}] API返回:`, result);
  325. if (result && result.data && result.data.result) {
  326. const options = Object.entries(result.data.result).map(([value, label]) => ({
  327. n: label,
  328. v: value
  329. }));
  330. // 缓存结果
  331. if (dictCache) {
  332. dictCache.set(cacheKey, options);
  333. }
  334. return options;
  335. }
  336. return [];
  337. } catch (error) {
  338. console.error(`❌ 获取字典选项失败 [${cbName}]:`, error);
  339. return [];
  340. }
  341. };
  342. /**
  343. * 格式化对象列表数据 - 处理API返回的objectList
  344. * @param {Array} objectList - 原始对象列表
  345. * @param {Map} dictCache - 字典缓存
  346. * @returns {Promise<Array>} 格式化后的列表
  347. */
  348. const formatObjectList = async (objectList, dictCache) => {
  349. if (!Array.isArray(objectList)) return [];
  350. const formattedList = [];
  351. for (const item of objectList) {
  352. const formattedItem = { ...item };
  353. // 格式化 first 字段
  354. if (item.first) {
  355. formattedItem.firstDisplay = await formatFieldValueAsync(item.first, dictCache);
  356. }
  357. // 格式化 second 字段
  358. if (item.second) {
  359. formattedItem.secondDisplay = await formatFieldValueAsync(item.second, dictCache);
  360. }
  361. // 格式化 third 字段组
  362. if (item.third && Array.isArray(item.third)) {
  363. formattedItem.thirdDisplay = [];
  364. for (const group of item.third) {
  365. const formattedGroup = [];
  366. for (const fieldData of group) {
  367. const displayValue = await formatFieldValueAsync(fieldData, dictCache);
  368. formattedGroup.push({
  369. ...fieldData,
  370. displayValue
  371. });
  372. }
  373. formattedItem.thirdDisplay.push(formattedGroup);
  374. }
  375. }
  376. formattedList.push(formattedItem);
  377. }
  378. return formattedList;
  379. };
  380. // 导出到全局
  381. window.H5FieldFormatter = {
  382. getDictTranslation,
  383. formatFieldValue,
  384. formatFieldValueAsync,
  385. formatDate: h5FormatDate,
  386. getDictOptions,
  387. formatObjectList
  388. };
  389. // 兼容性:也导出为全局函数
  390. window.getDictTranslation = getDictTranslation;
  391. window.formatFieldValue = formatFieldValue;
  392. window.formatFieldValueAsync = formatFieldValueAsync;
  393. window.getDictOptions = getDictOptions;
  394. window.formatObjectList = formatObjectList;
  395. console.log('✅ H5字段格式化工具加载完成');