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
15 changes: 15 additions & 0 deletions packages/amis-core/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1553,9 +1553,24 @@ export interface AMISTrackConfig {
* AMIS 加载效果配置
*/
export interface AMISSpinnerConfig {
/**
* 是否显示加载状态
*/
loading?: boolean | AMISSchema;

/**
* 加载效果配置
*/
loadingConfig?: {
root?: string;
show?: boolean;
icon?: string;
tip?: string;
tipPlacement?: 'top' | 'right' | 'bottom' | 'left';
delay?: number;
size?: 'sm' | 'lg' | '';
className?: string;
spinnerClassName?: string;
};
}

Expand Down
7 changes: 4 additions & 3 deletions packages/amis-ui/scss/components/_spinner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--Spinner-color);
opacity: 0;
transition: ease-out opacity var(--animation-duration);

Expand Down Expand Up @@ -53,14 +54,14 @@
.icon {
width: 100%;
height: 100%;
color: var(--Spinner-color);
color: inherit;
}

svg.icon {
top: 0;

path {
fill: var(--Spinner-color);
fill: currentColor;
}
}

Expand Down Expand Up @@ -116,7 +117,7 @@
word-break: keep-all;
white-space: nowrap;
font-size: var(--spinner-base-fontSize);
color: var(--Spinner-color);
color: inherit;
font-weight: var(--spinner-base-fontWeight);
}

Expand Down
37 changes: 27 additions & 10 deletions packages/amis-ui/src/components/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ export interface SpinnerExtraProps {
loadingConfig?: {
root?: string;
show?: boolean;
icon?: string;
tip?: string;
tipPlacement?: 'top' | 'right' | 'bottom' | 'left';
delay?: number;
size?: 'sm' | 'lg' | '';
className?: string;
spinnerClassName?: string;
};
}

Expand Down Expand Up @@ -110,10 +117,10 @@ export class Spinner extends React.Component<
show: true,
className: '',
spinnerClassName: '',
size: '' as '',
size: '' as const,
icon: '',
tip: '',
tipPlacement: 'bottom' as 'bottom',
tipPlacement: 'bottom' as const,
delay: 0,
overlay: false,
loadingConfig: {},
Expand Down Expand Up @@ -199,17 +206,27 @@ export class Spinner extends React.Component<
renderBody() {
const {
classnames: cx,
className,
spinnerClassName,
size = '',
className: propClassName,
spinnerClassName: propSpinnerClassName,
overlay,
delay,
icon: iconConfig,
tip,
tipPlacement = '',
loadingConfig,
disabled
disabled,
size: propSize,
tip: propTip,
tipPlacement: propTipPlacement,
delay: propDelay,
icon: propIcon
} = this.props;

const size = propSize || loadingConfig?.size || '';
const tip = propTip || loadingConfig?.tip || '';
const tipPlacement = propTipPlacement || loadingConfig?.tipPlacement || '';
const delay = propDelay ?? loadingConfig?.delay ?? 0;
const iconConfig = propIcon || loadingConfig?.icon || '';
const className = propClassName || loadingConfig?.className || '';
const spinnerClassName =
propSpinnerClassName || loadingConfig?.spinnerClassName || '';

// 定义了挂载位置时只能使用默认icon
const icon = loadingConfig?.root ? undefined : iconConfig;
const isCustomIcon = icon && React.isValidElement(icon);
Expand Down
8 changes: 6 additions & 2 deletions packages/amis/src/renderers/CRUD.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3194,7 +3194,11 @@ export default class CRUD<T extends CRUDProps> extends React.Component<T, any> {
headerToolbarRender: this.renderHeaderToolbar,
footerToolbarRender: this.renderFooterToolbar,
data: store.mergedData,
loading: store.loading,
loading: this.props.loading === false ? false : store.loading,
loadingConfig: {
...this.props.loadingConfig,
...(typeof this.props.loading === 'object' ? this.props.loading : {})
},
offset: store.offset,
host: this,
filterItemIndex: this.filterItemIndex,
Expand All @@ -3220,7 +3224,7 @@ export default class CRUD<T extends CRUDProps> extends React.Component<T, any> {
return (
<div
className={cx('Crud', className, {
'is-loading': store.loading,
'is-loading': this.props.loading === false ? false : store.loading,
'is-mobile': mobileUI
})}
style={style}
Expand Down
10 changes: 7 additions & 3 deletions packages/amis/src/renderers/CRUD2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1741,15 +1741,19 @@ export default class CRUD2<T extends CRUD2Props> extends React.Component<
onAction: this.handleAction,
dispatchEvent: this.dispatchEvent,
data: store.mergedData,
loading: store.loading,
loading: this.props.loading === false ? false : store.loading,
loadingConfig: {
...this.props.loadingConfig,
...(typeof this.props.loading === 'object' ? this.props.loading : {})
},
host: this
}
);

return (
<div
className={cx('Crud2', className, {
'is-loading': store.loading,
'is-loading': this.props.loading === false ? false : store.loading,
'is-mobile': mobileUI,
'is-mobile-cards':
mobileMode === 'cards' || mobileModeProps?.type === 'cards'
Expand Down Expand Up @@ -1789,7 +1793,7 @@ export default class CRUD2<T extends CRUD2Props> extends React.Component<
translate={__}
onRefresh={this.handlePullRefresh}
direction={pullRefresh.gestureDirection ?? 'up'}
loading={store.loading}
loading={this.props.loading === false ? false : store.loading}
completed={
!store.loading &&
store.lastPage > 0 &&
Expand Down
169 changes: 126 additions & 43 deletions packages/amis/src/renderers/Table/exportExcel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ export async function exportExcel(
}
// 用于 mapping source 的情况
const remoteMappingCache: any = {};
// 用于记录列宽(用于展宽图片列)
const maxColumnWidths: Record<number, number> = {};
// 数据从第二行开始
let rowIndex = 1;
if (toolbar.rowSlice) {
Expand All @@ -415,6 +417,7 @@ export async function exportExcel(
const rowData = createObject(data, row.data);
rowIndex += 1;
const sheetRow = worksheet.getRow(rowIndex);
let maxRowHeight = 0;
let columIndex = 0;
for (const column of filteredColumns) {
columIndex += 1;
Expand Down Expand Up @@ -442,53 +445,121 @@ export async function exportExcel(

const type = (column as AMISSchema).type || 'plain';
// TODO: 这里很多组件都是拷贝对应渲染的逻辑实现的,导致每种都得实现一遍
if ((type === 'image' || (type as any) === 'static-image') && value) {
try {
const imageData = await toDataURL(value);
const imageDimensions = await getImageDimensions(imageData);
let imageWidth = imageDimensions.width;
let imageHeight = imageDimensions.height;
// 限制一下图片高宽
const imageMaxSize = 100;
if (imageWidth > imageHeight) {
if (imageWidth > imageMaxSize) {
imageHeight = (imageMaxSize * imageHeight) / imageWidth;
imageWidth = imageMaxSize;
}
if (
(type === 'image' ||
(type as any) === 'static-image' ||
type === 'images' ||
(type as any) === 'static-images') &&
value
) {
let list: Array<any> = [];
if (type === 'images' || (type as any) === 'static-images') {
const delimiter = column.pristine.delimiter || ',';
if (typeof value === 'string') {
list = value.split(delimiter);
} else if (Array.isArray(value)) {
list = value;
} else {
if (imageHeight > imageMaxSize) {
imageWidth = (imageMaxSize * imageWidth) / imageHeight;
imageHeight = imageMaxSize;
}
}
const imageMatch = imageData.match(/data:image\/(.*);/);
let imageExt = 'png';
if (imageMatch) {
imageExt = imageMatch[1];
list = [value];
}
// 目前 excel 只支持这些格式,所以其它格式直接输出 url
if (imageExt != 'png' && imageExt != 'jpeg' && imageExt != 'gif') {
sheetRow.getCell(columIndex).value = value;
continue;
} else {
list = [value];
}

const srcKey = column.pristine.src || 'image';
const gap = 5; // 图片间距(像素)

// 第一遍:收集所有图片数据和尺寸
const imageItems: Array<{
imageId: number;
imageWidth: number;
imageHeight: number;
linkURL: string;
}> = [];
for (const item of list) {
const src =
typeof item === 'string'
? item
: (item && (item[srcKey] || item.src)) || item;
if (!src) continue;

try {
const imageData = await toDataURL(src);
const imageDimensions = await getImageDimensions(imageData);
let imageWidth = imageDimensions.width;
let imageHeight = imageDimensions.height;
// 限制一下图片高宽
const imageMaxSize = 100;
if (imageWidth > imageHeight) {
if (imageWidth > imageMaxSize) {
imageHeight = (imageMaxSize * imageHeight) / imageWidth;
imageWidth = imageMaxSize;
}
} else {
if (imageHeight > imageMaxSize) {
imageWidth = (imageMaxSize * imageWidth) / imageHeight;
imageHeight = imageMaxSize;
}
}
const imageMatch = imageData.match(/data:image\/(.*);/);
let imageExt = 'png';
if (imageMatch) {
imageExt = imageMatch[1];
if (imageExt === 'jpg') {
imageExt = 'jpeg';
}
}
// 目前 excel 只支持这些格式,所以其它格式直接输出 url
if (imageExt != 'png' && imageExt != 'jpeg' && imageExt != 'gif') {
sheetRow.getCell(columIndex).value = src;
continue;
}
const imageId = workbook.addImage({
base64: imageData,
extension: imageExt as any
});
imageItems.push({
imageId,
imageWidth,
imageHeight,
linkURL: getAbsoluteUrl(src)
});
maxRowHeight = Math.max(maxRowHeight, imageHeight * 0.75);
} catch (e) {
console.warn(e);
sheetRow.getCell(columIndex).value = src;
}
const imageId = workbook.addImage({
base64: imageData,
extension: imageExt
});
const linkURL = getAbsoluteUrl(value);
worksheet.addImage(imageId, {
// 这里坐标位置是从 0 开始的,所以要减一
tl: {col: columIndex - 1, row: rowIndex - 1},
ext: {
width: imageWidth,
height: imageHeight
}

if (imageItems.length === 0) continue;

// 计算总像素宽度(用于列宽自适应)
const totalPixelWidth =
imageItems.reduce((sum, img) => sum + img.imageWidth, 0) +
Math.max(0, imageItems.length - 1) * gap;

// Excel 列宽单位约等于 7 像素,更新最大列宽记录
maxColumnWidths[columIndex] = Math.max(
maxColumnWidths[columIndex] || 0,
totalPixelWidth / 7
);

// 使用 nativeColOff(EMU 单位,1 像素 = 9525 EMU)直接写入 XML
// 这种方式完全绕过 exceljs 内部的 col 宽度依赖计算,定位最为准确
let xOffset = 0;
for (const img of imageItems) {
worksheet.addImage(img.imageId, {
tl: {
nativeCol: columIndex - 1,
nativeColOff: Math.round(xOffset * 9525),
nativeRow: rowIndex - 1,
nativeRowOff: 0
},
hyperlinks: {
tooltip: linkURL
}
});
} catch (e) {
console.warn(e);
ext: {width: img.imageWidth, height: img.imageHeight},
editAs: 'oneCell',
hyperlinks: {tooltip: img.linkURL}
} as any);
xOffset += img.imageWidth + gap;
}
} else if (type == 'link' || (type as any) === 'static-link') {
const href = column.pristine.href;
Expand Down Expand Up @@ -620,8 +691,20 @@ export async function exportExcel(
sheetRow.getCell(columIndex).numFmt = '0';
}
}

if (maxRowHeight) {
sheetRow.height = maxRowHeight;
}
}

// 应用列宽
Object.keys(maxColumnWidths).forEach(colIndex => {
const width = maxColumnWidths[parseInt(colIndex, 10)];
if (width > 0) {
worksheet.getColumn(parseInt(colIndex, 10)).width = width;
}
});

// 后置总结行
renderSummary(worksheet, data, affixRow, rowIndex);

Expand Down
Loading
Loading