Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions packages/amis-core/src/utils/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,18 @@ extendsFilters({
[modifier === 'add' ? 'add' : 'subtract'](parseInt(amount, 10) || 0, unit)
.toDate();
},
date: (input, format = 'LLL', inputFormat = 'X') =>
moment(input, inputFormat).format(format),
date: (input, format = 'LLL', inputFormat = 'X') => {
// 与 tpl-lodash.ts formatDate 一致的守卫:
// input 为空 / 解析失败时不要静默渲染 "Invalid date" 字符串。
if (input == null || input === '') {
return '';
}
const m = moment(input, inputFormat);
if (!m.isValid()) {
return String(input);
}
return m.format(format);
},
number: input => {
let parts = String(input).split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
Expand Down
22 changes: 20 additions & 2 deletions packages/amis-core/src/utils/tpl-lodash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,26 @@ const imports = {

return Math.ceil((date.getTime() - now) / (1000 * 60 * 60 * 24)) + '天';
},
formatDate: (value: any, format: string = 'LLL', inputFormat: string = '') =>
moment(value, inputFormat).format(format)
formatDate: (
value: any,
format: string = 'LLL',
inputFormat: string = ''
) => {
// 当模板里 value 是用户配置 / 接口返回的脏数据时:
// 1) 字符串解析失败时 moment(value, inputFormat) 返回 invalid moment,
// .format() 静默返回字符串 "Invalid date" 直接渲染到页面里。
// 2) value 为 null/undefined 时 moment 默认会回退到 "现在"(now),
// 会让 typo 字段(如 data.creatAt)静默渲染成今天的日期。
// 这里两种情况都做守卫:null/undefined 回显空串,无法解析回显原值。
if (value == null || value === '') {
return '';
}
const m = moment(value, inputFormat);
if (!m.isValid()) {
return String(value);
}
return m.format(format);
}
};

// 缓存一下提升性能
Expand Down
67 changes: 67 additions & 0 deletions packages/amis/__tests__/utils/tpl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,70 @@ test('filter', () => {
})
).toEqual('xxx_a=1&b=2');
});

test('filter:formatDate echoes unparseable input instead of rendering "Invalid date"', () => {
// Before the fix this rendered the literal string "Invalid date" into the page.
// After the fix the bad input is echoed back so the upstream caller can
// decide how to display it (or it's empty for null/undefined).
expect(
filter('<%= formatDate(data.d, "YYYY-MM-DD") %>', {d: 'not-a-real-date'})
).toEqual('not-a-real-date');

expect(filter('<%= formatDate(data.d, "YYYY-MM-DD") %>', {d: null})).toEqual(
''
);

expect(
filter('<%= formatDate(data.d, "YYYY-MM-DD") %>', {d: undefined})
).toEqual('');

// Critical: must NOT render the silent "Invalid date" string anywhere.
expect(
filter('<%= formatDate(data.d, "YYYY-MM-DD") %>', {d: 'not-a-real-date'})
).not.toMatch(/Invalid date/);
});

test('filter:formatDate still formats valid input correctly', () => {
// Round-trip a known ISO date through the inputFormat overload.
expect(
filter('<%= formatDate(data.d, "YYYY-MM-DD", "YYYY-MM-DD") %>', {
d: '2024-01-15'
})
).toEqual('2024-01-15');

// Default (no inputFormat) path still works on standard ISO timestamps.
expect(
filter('<%= formatDate(data.d, "YYYY-MM-DD") %>', {
d: '2024-01-15T00:00:00Z'
})
).toEqual('2024-01-15');
});

// `date` filter (from amis-core/src/utils/filter.ts) — also exposed in lodash
// templates as `formatTimeStamp: filters.date` (see tpl-lodash.ts:50). Same
// one-liner shape as `formatDate` before the guards landed; same failure mode.
test('filter:date guards against null/undefined/empty input', () => {
// formatTimeStamp is filters.date with inputFormat defaulting to "X" (Unix s).
expect(
filter('<%= formatTimeStamp(data.d, "YYYY-MM-DD") %>', {d: null})
).toEqual('');
expect(
filter('<%= formatTimeStamp(data.d, "YYYY-MM-DD") %>', {d: undefined})
).toEqual('');
expect(filter('<%= formatTimeStamp(data.d, "YYYY-MM-DD") %>', {d: ''})).toEqual(
''
);

// Unparseable input echoes back the original — must NOT surface "Invalid date".
expect(
filter('<%= formatTimeStamp(data.d, "YYYY-MM-DD") %>', {d: 'not-a-real-date'})
).toEqual('not-a-real-date');
expect(
filter('<%= formatTimeStamp(data.d, "YYYY-MM-DD") %>', {d: 'not-a-real-date'})
).not.toMatch(/Invalid date/);

// Valid Unix-timestamp-seconds input still formats correctly.
expect(
filter('<%= formatTimeStamp(data.d, "YYYY-MM-DD") %>', {d: '1705276800'})
).toEqual('2024-01-15');
});
Loading