Skip to content

Commit be2b00a

Browse files
添加Kling2.1金币消耗提示 (#150)
* 添加Kling2.1金币消耗提示 * 更改版本号
1 parent cdcf87d commit be2b00a

3 files changed

Lines changed: 330 additions & 0 deletions

File tree

bizyui/js/handle_load_nodes.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { app } from "../../scripts/app.js";
2+
import { addPriceBadgeToNode, hasModelInput } from "./model_price.js";
3+
4+
// 获取node 的模型配置input
5+
export async function applyBadgeToNode(_this) {
6+
// 为包含model输入的节点添加价格徽章
7+
const hiddenOutput = hasModelInput(_this);
8+
if (hiddenOutput) {
9+
if (!hiddenOutput.type) {
10+
console.error(`error finding model type`);
11+
return;
12+
}
13+
14+
if (typeof hiddenOutput.type !== "string") {
15+
return;
16+
}
17+
18+
// 将hiddenOutput临时存储在节点上,方便后续使用
19+
_this._bizyairHiddenOutput = hiddenOutput;
20+
21+
await addPriceBadgeToNode(_this, getModelTypeFromHiddenInput(hiddenOutput));
22+
23+
// 删除无用的outputs
24+
_this.outputs = _this.outputs.filter(
25+
(output) => output.name !== hiddenOutput.name
26+
);
27+
}
28+
}
29+
30+
// 从hiddenInput中获取模型类型
31+
function getModelTypeFromHiddenInput(hiddenOutput){
32+
const modelJson = JSON.parse(hiddenOutput.type);
33+
const modelsList = Object.keys(modelJson);
34+
return modelJson[modelsList[0]]
35+
}
36+
37+
// 自定义节点创建处理函数
38+
async function onNodeCreatedGettingPrice(originalOnNodeCreated) {
39+
// 调用原始的 onNodeCreated 方法
40+
if (originalOnNodeCreated) {
41+
originalOnNodeCreated.apply(this, arguments);
42+
}
43+
applyBadgeToNode(this);
44+
}
45+
46+
app.registerExtension({
47+
name: "bizyair.hook.load.nodes",
48+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
49+
console.log("[handleLoadNodes] beforeRegisterNodeDef ready", true);
50+
51+
// 在节点创建后添加价格徽章
52+
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
53+
nodeType.prototype.onNodeCreated = function () {
54+
return onNodeCreatedGettingPrice.call(this, originalOnNodeCreated);
55+
};
56+
},
57+
});

bizyui/js/handle_node_configure.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { app } from "../../scripts/app.js";
2+
import { applyBadgeToNode } from "./handle_load_nodes.js";
3+
app.registerExtension({
4+
name: "bizyair.handle.node.configure",
5+
nodeCreated(node, app) {
6+
// 简单的节点配置处理逻辑
7+
console.log("Node created:", node);
8+
// 在这里可以拿到变化之后的值,并且也可以拿到node,这时候给node切换badge即可
9+
if (node && node.widgets && Array.isArray(node.widgets)) {
10+
// 只有model选择的widget才注册callback函数
11+
node.widgets.forEach((widget) => {
12+
if (widget.name === "model_name") {
13+
widget.callback = async function () {
14+
await applyBadgeToNode(node);
15+
};
16+
}
17+
});
18+
}
19+
},
20+
});

bizyui/js/model_price.js

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import { getCookie } from "./subassembly/tools.js";
2+
3+
// API基础地址
4+
const API_BASE_URL = "https://uat-api.bizyair.cn/y/v1";
5+
6+
/**
7+
* 货币类型枚举,包含徽章配置信息
8+
*/
9+
export const CoinType = Object.freeze({
10+
Free: {
11+
value: 0,
12+
icon: "FREE",
13+
badge: {
14+
text: "免费",
15+
bgColor: "#4CAF50", // 绿色
16+
},
17+
},
18+
Gift: {
19+
value: 1,
20+
icon: "BZ",
21+
badge: {
22+
textPrefix: "赠送银币 ",
23+
bgColor: "#FF9800", // 橙色
24+
},
25+
},
26+
Charge: {
27+
value: 2,
28+
icon: "BZ",
29+
badge: {
30+
textPrefix: "付费金币 ",
31+
bgColor: "#2196F3", // 蓝色
32+
},
33+
},
34+
Mixed: {
35+
value: 3,
36+
icon: "BZ",
37+
badge: {
38+
textPrefix: "混合币 ",
39+
bgColor: "#9C27B0", // 紫色
40+
},
41+
},
42+
});
43+
44+
/**
45+
* 检测节点是否包含model输入
46+
* @param {Object} node - 节点对象
47+
* @returns {boolean} 如果节点包含model输入则返回true,否则返回false
48+
*/
49+
export function hasModelInput(node) {
50+
// 优先从节点临时存储中获取(适用于outputs已被删除的情况)
51+
if (node._bizyairHiddenOutput) {
52+
return node._bizyairHiddenOutput;
53+
}
54+
55+
// 如果临时存储中没有,则从outputs中查找
56+
const localized_name = "bizyair_model_name";
57+
if (!node.outputs) {
58+
console.error(`[modelPrice-hasModelInput] error finding node outputs`);
59+
return null;
60+
}
61+
return node.outputs.find((item) => item.name === localized_name);
62+
}
63+
64+
/**
65+
* 为节点添加自定义徽章
66+
* @param {Object} node - 要添加徽章的节点对象
67+
* @param {Object} badgeOptions - 徽章配置选项
68+
* @param {string} badgeOptions.text - 徽章显示的文本
69+
* @param {string} badgeOptions.fgColor - 前景色
70+
* @param {string} badgeOptions.bgColor - 背景色
71+
* @param {number} badgeOptions.fontSize - 字体大小
72+
* @param {number} badgeOptions.padding - 内边距
73+
* @param {number} badgeOptions.height - 高度
74+
* @param {number} badgeOptions.cornerRadius - 圆角半径
75+
* @param {Object} [badgeOptions.iconOptions] - 图标配置选项
76+
* @param {string} badgeOptions.iconOptions.unicode - 图标的unicode值
77+
* @param {string} badgeOptions.iconOptions.fontFamily - 图标字体族
78+
* @param {string} badgeOptions.iconOptions.color - 图标颜色
79+
* @param {string} badgeOptions.iconOptions.bgColor - 图标背景色
80+
* @param {number} badgeOptions.iconOptions.fontSize - 图标字体大小
81+
*
82+
* @param {number} badgeOptions.iconOptions.uuid - 图标uuid 用于删除 自定义属性
83+
*/
84+
export function addCustomBadge(node, badgeOptions) {
85+
const customBadge = new LGraphBadge(badgeOptions);
86+
// 每次添加badge 清空所有badge 因为badge没有一个唯一标识符,并且价格展示也就一个badge 所以直接全部清除
87+
node.badges = [];
88+
node.badges.push(() => customBadge);
89+
console.log(`[addCustomBadge] 添加badge成功 徽章信息`, node.badges);
90+
}
91+
92+
/**
93+
* 根据货币类型获取徽章配置
94+
* @param {number} coinType - 货币类型值
95+
* @param {string} priceText - 价格文本
96+
* @returns {Object} 徽章配置对象
97+
*/
98+
export function getBadgeConfigByCoinType(coinType, priceText) {
99+
const baseConfig = {
100+
fgColor: "white",
101+
fontSize: 10,
102+
padding: 6,
103+
height: 18,
104+
cornerRadius: 8,
105+
};
106+
107+
// 查找对应的货币类型配置
108+
for (const [key, config] of Object.entries(CoinType)) {
109+
if (config.value === coinType) {
110+
const badgeConfig = config.badge;
111+
112+
// 构建基础徽章配置
113+
const resultConfig = {
114+
...baseConfig,
115+
bgColor: badgeConfig.bgColor,
116+
iconOptions: {
117+
unicode: config.icon,
118+
fontFamily: "Arial, sans-serif",
119+
color: "white",
120+
bgColor: badgeConfig.bgColor,
121+
fontSize: 12,
122+
},
123+
};
124+
125+
if (key === "Free") {
126+
return {
127+
...resultConfig,
128+
text: badgeConfig.text,
129+
};
130+
} else {
131+
return {
132+
...resultConfig,
133+
text: `${badgeConfig.textPrefix}${priceText}`,
134+
};
135+
}
136+
}
137+
}
138+
139+
// 默认配置(未知类型)
140+
return {
141+
...baseConfig,
142+
text: priceText,
143+
bgColor: "#757575", // 灰色
144+
iconOptions: {
145+
unicode: "❓",
146+
fontFamily: "Arial, sans-serif",
147+
color: "white",
148+
bgColor: "#757575",
149+
fontSize: 12,
150+
},
151+
};
152+
}
153+
154+
/**
155+
* 为节点添加价格徽章
156+
* TODO 重新添加价格徽章的时候 节点元素不能重新render
157+
* @param {Object} node - 节点对象,包含 widgets 和其他节点信息
158+
*/
159+
export async function addPriceBadgeToNode(node, modelName = "") {
160+
try {
161+
console.log(`[modelPrice] addPriceBadgeToNode`, node);
162+
// 从节点 widgets 中提取价格计算所需的数据 这个也可以封装一个函数
163+
// NODE 上的type 考虑是否也封装一个函数
164+
const nodeInputs = {};
165+
if (node.widgets && Array.isArray(node.widgets)) {
166+
node.widgets.forEach((widget) => {
167+
if (widget.name && widget.value !== undefined) {
168+
nodeInputs[widget.name] = widget.value;
169+
}
170+
});
171+
}
172+
173+
// 获取价格信息
174+
const priceResult = await fetchNodePrice(modelName, nodeInputs);
175+
176+
if (priceResult && priceResult.data && priceResult.data.result) {
177+
// 根据货币类型获取徽章配置
178+
const badgeConfig = getBadgeConfigByCoinType(
179+
priceResult.data.coin_type,
180+
priceResult.data.result
181+
);
182+
// 添加价格徽章
183+
addCustomBadge(node, badgeConfig);
184+
}
185+
} catch (error) {
186+
console.error("添加价格徽章失败:", error);
187+
// 添加错误徽章
188+
addCustomBadge(node, {
189+
text: "价格获取失败",
190+
fgColor: "white",
191+
bgColor: "#F44336",
192+
fontSize: 10,
193+
padding: 6,
194+
height: 18,
195+
cornerRadius: 8,
196+
iconOptions: {
197+
unicode: "❌",
198+
fontFamily: "Arial, sans-serif",
199+
color: "white",
200+
bgColor: "#F44336",
201+
fontSize: 12,
202+
},
203+
});
204+
}
205+
}
206+
207+
/**
208+
* 获取节点价格信息
209+
* @param {string} model - 模型名称,用于构建请求路径
210+
* @param {Object} nodeInputs - 节点的所有输入信息,包含价格计算所需的数据
211+
* @returns {Promise<Object>} 返回价格信息
212+
* @property {number} code - 响应状态码,20000表示成功
213+
* @property {string} message - 响应消息
214+
* @property {boolean} status - 请求状态
215+
* @property {Object} data - 响应数据
216+
* @property {string} data.result - 价格描述,如"120/次"
217+
* @property {number} data.coin_type - 货币类型,0=免费,1=赠与币,2=付费币,3=混合使用
218+
*/
219+
export async function fetchNodePrice(model, nodeInputs) {
220+
try {
221+
// 从cookie中获取认证token
222+
const authToken = getCookie("bizy_token");
223+
if (!authToken) {
224+
throw new Error("未找到认证Token,请先登录");
225+
}
226+
if (!nodeInputs) {
227+
throw new Error("节点输入信息为空,无法获取价格");
228+
}
229+
230+
const response = await fetch(
231+
`${API_BASE_URL}/trd_api/node/${model}/price`,
232+
{
233+
method: "POST",
234+
headers: {
235+
"Content-Type": "application/json",
236+
Authorization: `Bearer ${authToken}`,
237+
},
238+
body: JSON.stringify(nodeInputs),
239+
}
240+
);
241+
242+
if (!response.ok) {
243+
throw new Error(`HTTP error! status: ${response.status}`);
244+
}
245+
246+
const result = await response.json();
247+
console.log("节点价格查询结果:", result);
248+
return result;
249+
} catch (error) {
250+
console.error("获取节点价格失败:", error);
251+
throw error;
252+
}
253+
}

0 commit comments

Comments
 (0)