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,25 @@
/*
* 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.
*/
export {
MonetizeConfigPanel,
MonetizeConfigValue,
} from './monetize-config-panel';
export { MonetizeCreditRefreshCycle } from './monetize-credit-refresh-cycle';
export { MonetizeDescription } from './monetize-description';
export { MonetizeFreeChatCount } from './monetize-free-chat-count';
export { MonetizeSwitch } from './monetize-switch';
export { MonetizePublishInfo } from './monetize-publish-info';

View File

@@ -0,0 +1,101 @@
/*
* 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 { useDebounceFn } from 'ahooks';
import { type BotMonetizationRefreshPeriod } from '@coze-arch/bot-api/benefit';
import { MonetizeSwitch } from '../monetize-switch';
import { MonetizeFreeChatCount } from '../monetize-free-chat-count';
import { MonetizeDescription } from '../monetize-description';
import { MonetizeCreditRefreshCycle } from '../monetize-credit-refresh-cycle';
export interface MonetizeConfigValue {
/** 是否开启付费 */
isOn: boolean;
/** 开启付费后,用户免费体验的次数 */
freeCount: number;
/** 刷新周期 */
refreshCycle: BotMonetizationRefreshPeriod;
}
interface MonetizeConfigPanelProps {
disabled?: boolean;
value: MonetizeConfigValue;
onChange: (value: MonetizeConfigValue) => void;
/**
* 内置防抖后的 onChange 事件,业务侧可选择性使用,正常只传 onChange 即可
* (由于该组件是完全受控组件,因此不能只传 onDebouncedChange必须传 onChange 实时更新视图)
*/
onDebouncedChange?: (value: MonetizeConfigValue) => void;
}
export function MonetizeConfigPanel({
disabled = false,
value,
onChange,
onDebouncedChange,
}: MonetizeConfigPanelProps) {
const { run: debouncedChange } = useDebounceFn(
({ isOn, freeCount, refreshCycle }: MonetizeConfigValue) => {
onDebouncedChange?.({
isOn,
freeCount,
refreshCycle,
});
},
{ wait: 300 },
);
const refreshCycleDisabled = !value.isOn || disabled || value.freeCount <= 0;
return (
<div className="w-[480px] p-[24px] flex flex-col gap-[24px]">
<MonetizeSwitch
disabled={disabled}
isOn={value.isOn}
onChange={v => {
onChange({ ...value, isOn: v });
debouncedChange({
...value,
isOn: v,
});
}}
/>
<MonetizeDescription isOn={value.isOn} />
<MonetizeFreeChatCount
isOn={value.isOn}
disabled={disabled}
freeCount={value.freeCount}
onFreeCountChange={v => {
onChange({ ...value, freeCount: v });
debouncedChange({
...value,
freeCount: v,
});
}}
/>
<MonetizeCreditRefreshCycle
freeCount={value.freeCount}
disabled={refreshCycleDisabled}
refreshCycle={value.refreshCycle}
onRefreshCycleChange={v => {
onChange({ ...value, refreshCycle: v });
debouncedChange({ ...value, refreshCycle: v });
}}
/>
</div>
);
}

View File

@@ -0,0 +1,110 @@
/*
* 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 { I18n } from '@coze-arch/i18n';
import { IconCozInfoCircle } from '@coze-arch/coze-design/icons';
import { Tooltip, Select } from '@coze-arch/coze-design';
import { BotMonetizationRefreshPeriod } from '@coze-arch/bot-api/benefit';
const refreshCycleTextMap: Record<BotMonetizationRefreshPeriod, string> = {
[BotMonetizationRefreshPeriod.Unknown]: I18n.t(
'coze_premium_credits_cycle_4',
),
[BotMonetizationRefreshPeriod.Never]: I18n.t('coze_premium_credits_cycle_4'),
[BotMonetizationRefreshPeriod.Day]: I18n.t('coze_premium_credits_cycle_1'),
[BotMonetizationRefreshPeriod.Week]: I18n.t('coze_premium_credits_cycle_2'),
[BotMonetizationRefreshPeriod.Month]: I18n.t('coze_premium_credits_cycle_3'),
};
const getOptionList = () => [
{
value: BotMonetizationRefreshPeriod.Never,
text: refreshCycleTextMap[BotMonetizationRefreshPeriod.Never],
},
{
value: BotMonetizationRefreshPeriod.Day,
text: refreshCycleTextMap[BotMonetizationRefreshPeriod.Day],
tooltip: I18n.t('coze_premium_credits_cycle_tip6'),
},
{
value: BotMonetizationRefreshPeriod.Week,
text: refreshCycleTextMap[BotMonetizationRefreshPeriod.Week],
tooltip: I18n.t('coze_premium_credits_cycle_tip7'),
},
{
value: BotMonetizationRefreshPeriod.Month,
text: refreshCycleTextMap[BotMonetizationRefreshPeriod.Month],
tooltip: I18n.t('coze_premium_credits_cycle_tip8'),
},
];
export function MonetizeCreditRefreshCycle({
refreshCycle,
onRefreshCycleChange,
disabled,
freeCount,
}: {
freeCount: number;
disabled: boolean;
refreshCycle: number;
onRefreshCycleChange: (value: number) => void;
}) {
return (
<div className="flex items-center justify-between">
<div className="flex items-center gap-4px">
<div className="coz-fg-primary text-lg font-medium">
{I18n.t('coze_premium_credits_cycle_5')}
</div>
<Tooltip
theme="dark"
content={I18n.t('coze_premium_credits_cycle_tip1')}
>
<IconCozInfoCircle className="text-base coz-fg-secondary" />
</Tooltip>
</div>
<Tooltip
key={freeCount}
trigger={freeCount <= 0 ? 'hover' : 'custom'}
content={I18n.t('coze_premium_credits_cycle_tip4')}
>
<Select
disabled={disabled}
onChange={value => {
onRefreshCycleChange(Number(value));
}}
value={refreshCycle}
position="bottomRight"
className="w-[140px]"
renderSelectedItem={(item: Record<string, unknown>) =>
refreshCycleTextMap[item.value as BotMonetizationRefreshPeriod]
}
>
{getOptionList().map(item => (
<Select.Option key={item.value} value={item.value}>
<div className="mx-8px w-[100px]">{item.text}</div>
{item.tooltip ? (
<Tooltip theme="dark" position="right" content={item.tooltip}>
<IconCozInfoCircle className="mr-8px coz-fg-secondary text-base" />
</Tooltip>
) : null}
</Select.Option>
))}
</Select>
</Tooltip>
</div>
);
}

View File

@@ -0,0 +1,43 @@
/*
* 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 { I18n } from '@coze-arch/i18n';
import { Popover } from '@coze-arch/coze-design';
import previewCard from './preview-card.png';
export function MonetizeDescription({ isOn }: { isOn: boolean }) {
return (
<div className="coz-fg-primary">
<span>
{isOn ? I18n.t('monetization_on_des') : I18n.t('monetization_off_des')}
</span>
{isOn ? (
<Popover
content={
<div className="p-[12px] coz-bg-max rounded-[10px]">
<img width={320} src={previewCard} />
</div>
}
>
<span className="coz-fg-hglt cursor-pointer">
{I18n.t('monetization_on_viewbill')}
</span>
</Popover>
) : null}
</div>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@@ -0,0 +1,64 @@
/*
* 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 { I18n } from '@coze-arch/i18n';
import { IconCozInfoCircle } from '@coze-arch/coze-design/icons';
import { InputNumber, Tooltip } from '@coze-arch/coze-design';
export function MonetizeFreeChatCount({
isOn,
disabled,
freeCount,
onFreeCountChange,
}: {
isOn: boolean;
disabled: boolean;
freeCount: number;
onFreeCountChange: (value: number) => void;
}) {
return (
<div className="flex items-center justify-between">
<div>
<div className="flex items-center font-semibold leading-[20px]">
<span className="coz-fg-primary">
{I18n.t('free_chat_allowance')}
</span>
<Tooltip theme="dark" content={I18n.t('free_chat_allowance_tips')}>
<span className="ml-[4px] h-[12px] w-[12px] text-[12px] leading-[12px] coz-fg-secondary">
<IconCozInfoCircle />
</span>
</Tooltip>
</div>
<div className="coz-fg-secondary text-base leading-[16px]">
{freeCount > 5
? I18n.t('coze_premium_credits_cycle_tip2')
: I18n.t('coze_premium_credits_cycle_tip3')}
</div>
</div>
<InputNumber
keepFocus
className="w-[140px]"
disabled={!isOn || disabled}
precision={0}
min={0}
max={100}
value={freeCount}
onNumberChange={onFreeCountChange}
/>
</div>
);
}

View File

@@ -0,0 +1,92 @@
/*
* 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 cls from 'classnames';
import { I18n } from '@coze-arch/i18n';
import { IconCozInfoCircle } from '@coze-arch/coze-design/icons';
import { Avatar, Tooltip } from '@coze-arch/coze-design';
import { type PublishConnectorInfo } from '@coze-arch/bot-api/developer_api';
import { type BotMonetizationConfigData } from '@coze-arch/bot-api/benefit';
export function MonetizePublishInfo({
className,
monetizeConfig,
supportPlatforms,
}: {
className?: string;
monetizeConfig: BotMonetizationConfigData;
supportPlatforms: Array<Pick<PublishConnectorInfo, 'id' | 'name' | 'icon'>>;
}) {
const supportPlatformsText = supportPlatforms.map(p => p.name).join(', ');
return (
<div className={cls('flex justify-end items-center gap-[12px]', className)}>
<div className="flex items-center gap-[4px]">
<span className="font-medium coz-fg-plus">
{`${I18n.t('monetization')}: ${
monetizeConfig.is_enable
? I18n.t('monetization_publish_on')
: I18n.t('monetization_publish_off')
}`}
</span>
<Tooltip
content={
<div className="flex flex-col">
<div>
{monetizeConfig.is_enable
? I18n.t('monetization_on_des')
: I18n.t('monetization_off_des')}
</div>
{monetizeConfig.is_enable ? (
<div className="mt-[8px] pt-[8px] border-0 border-t border-solid coz-stroke-primary">
{`${I18n.t('free_chat_allowance')} : ${
monetizeConfig.free_chat_allowance_count
}`}
</div>
) : null}
</div>
}
>
<IconCozInfoCircle className="w-[16px] h-[16px] coz-fg-secondary" />
</Tooltip>
</div>
<div className="flex items-center gap-[4px]">
<span className="font-normal coz-fg-tertiary">
{I18n.t('monetization_support')}:
</span>
<span className="flex items-center gap-[4px]">
{supportPlatforms.map(p => (
<Avatar
key={p.id}
className="h-[16px] w-[16px] rounded-[4px]"
size="extra-extra-small"
shape="square"
src={p.icon}
/>
))}
</span>
<Tooltip
content={`${I18n.t(
'monetization_support_tips',
)}: ${supportPlatformsText}`}
>
<IconCozInfoCircle className="w-[16px] h-[16px] coz-fg-secondary" />
</Tooltip>
</div>
</div>
);
}

View File

@@ -0,0 +1,43 @@
/*
* 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 { I18n } from '@coze-arch/i18n';
import { Switch } from '@coze-arch/coze-design';
export function MonetizeSwitch({
disabled,
isOn,
onChange,
}: {
disabled: boolean;
isOn: boolean;
onChange: (isOn: boolean) => void;
}) {
return (
<div className="flex justify-between">
<h3 className="m-0 text-[20px] font-medium coz-fg-plus">
{I18n.t('premium_monetization_config')}
</h3>
<Switch
disabled={disabled}
className="ml-[5px]"
size="small"
checked={isOn}
onChange={onChange}
/>
</div>
);
}