feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
.ctn {
display: flex;
flex-wrap: nowrap;
gap: 7px;
justify-content: center;
.image-item {
cursor: pointer;
position: relative;
width: 60px;
height: 60px;
border-radius: 8px;
img {
width: 100%;
height: 100%;
object-fit: cover;
border: 1.5px solid transparent;
border-radius: 8px;
&.selected {
border: 1.5px solid var(--coz-stroke-hglt, #4E40E5);
}
}
.delete-icon {
position: absolute;
top: -4px;
right: -4px;
display: none;
font-size: 16px;
color: var(--coz-fg-hglt-red, #F54A45);
}
&:hover .delete-icon {
display: block;
}
.check-icon {
position: absolute;
right: 4px;
bottom: 4px;
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
background-color: var(--coz-mg-hglt-plus, #4E40E5);
border-radius: 4px;
svg {
font-size: 12px;
color: white;
}
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type CSSProperties } from 'react';
import classNames from 'classnames';
import {
IconCozCheckMarkFill,
IconCozMinusCircleFillPalette,
} from '@coze-arch/coze-design/icons';
import { type PicTask } from '@coze-arch/bot-api/playground_api';
import s from './index.module.less';
export type ImageItem = PicTask;
export interface ImageListProps {
selectedKey?: string; // 选中的key
data: ImageItem[]; // 列表数据
className?: string;
imageItemClassName?: string;
showDeleteIcon?: boolean;
showSelectedIcon?: boolean;
style?: CSSProperties;
onRemove?: (params: {
index?: number;
item?: ImageItem;
data: ImageItem[];
}) => void; // 删除图片data是此次删除之后的数据
onSelect?: (params: {
index?: number;
item: ImageItem;
data: ImageItem[];
selected: boolean;
}) => void; // 选中图片其中item和data都是此次选中之前的数据selected表示在本次选中之前此图片是否已是选中状态
onClick?: (params: {
index: number;
item: ImageItem;
data: ImageItem[];
}) => void; // 点击图片
}
export const ImageList: React.FC<ImageListProps> = ({
data,
showDeleteIcon = true,
showSelectedIcon = true,
className,
imageItemClassName,
style,
onSelect,
onRemove,
onClick,
selectedKey,
}) => {
if (!data || data.length === 0) {
return null;
}
return (
<div className={classNames(className, s.ctn)} style={style}>
{data.map((item, index) => {
const { img_info } = item;
const { tar_uri, tar_url } = img_info ?? {};
return (
<div
key={tar_uri}
className={classNames(s['image-item'], imageItemClassName)}
>
<img
src={tar_url}
alt="图片"
className={classNames({
[s.selected]: showSelectedIcon && selectedKey === tar_uri,
})}
onClick={() => {
onClick?.({ index, item, data });
onSelect?.({
index,
item,
data,
selected: selectedKey === tar_uri,
});
}}
/>
{showDeleteIcon ? (
<IconCozMinusCircleFillPalette
className={s['delete-icon']}
onClick={() => {
onRemove?.({
index,
item,
data: data.filter(i => i !== item),
});
}}
/>
) : null}
{showSelectedIcon && selectedKey === tar_uri ? (
<div className={s['check-icon']}>
<IconCozCheckMarkFill />
</div>
) : null}
</div>
);
})}
</div>
);
};