- Updated `generateUUID` function for improved readability and performance. - Deleted `logger.ts`, `other.ts`, `request.ts`, `storage.ts`, `tansParams.ts`, and `validate.ts` as they were no longer needed. - Simplified TypeScript configuration by removing unnecessary paths and aliases. - Enhanced Vite configuration for better project structure and maintainability.
11 KiB
渠道硬编码移除与动态关联重构计划
上下文与目标
当前 zn-ai 的"一键打开各渠道"功能存在以下问题:
- 渠道数据硬编码在
src/constant/channel.ts中,仅包含 fliggy/meituan/douyin 三个固定渠道; AddChannelDialog.vue是纯 UI 空壳,收集表单后不做任何持久化或事件抛出;TaskCenter.vue存在 bug:将普通数组channels当作 ref 使用(channels.value),实际传递undefined给主进程;- 脚本管理功能中,每个
AutomationScript已有channel字段(存储渠道名称或 URL),但未被"一键打开"功能复用。
目标:
- 移除
channels.ts中的硬编码主数据源,改为从脚本管理中动态聚合可用渠道; - 重塑
AddChannelDialog的交互:快捷搜索选择渠道 → 展示已选列表 → 保存; - 修复
TaskCenter.vue的渠道传递逻辑,使用用户动态配置的列表; - 确保
open_all_channel.js接收的数据格式正确,按需同步调整。
关键文件清单
| 路径 | 角色 | 改动类型 |
|---|---|---|
zn-ai/src/constant/channel.ts |
硬编码渠道常量 | 移除数组,保留字典+解析工具 |
zn-ai/src/stores/channel.ts |
新建渠道状态 store | 新增 |
zn-ai/src/stores/script.ts |
脚本 Pinia store | 只读依赖(聚合 channel) |
zn-ai/src/pages/home/components/AddChannelDialog.vue |
渠道关联弹窗 | 重写模板与逻辑 |
zn-ai/src/pages/home/TaskCenter.vue |
任务中心卡片 | 修复+替换数据源 |
zn-ai/src/pages/home/index.vue |
首页容器 | 可能需补充 store 初始化 |
zn-ai/src/pages/scripts/components/ScriptEditorDialog.vue |
脚本编辑弹窗 | 替换 channels 引用为字典 |
zn-ai/electron/scripts/open_all_channel.js |
打开渠道的 Playwright 脚本 | 按需调整字段读取 |
zn-ai/electron/process/runTaskOperationService.ts |
IPC 处理(OPEN_CHANNEL) | 按需做数据格式适配 |
方案设计
1. 数据层:constant/channel.ts + 新建 stores/channel.ts
constant/channel.ts 改造
- 移除
export const channels: Item[]硬编码数组。 - 保留
Item接口({id, channelName, channelUrl})。 - 新增
channelDictionary: Record<string, string>,仅作为"已知渠道名称→URL"的参考字典(从原数组迁移)。 - 新增
resolveChannel(value: string): { name?: string; url: string }:- 若
value匹配channelDictionary的 key,返回{name: value, url: dictionary[value]}; - 若
value本身像 URL(以 http 开头),尝试从字典反向查找 name,找不到则 name 取 hostname 或原值; - 其他情况返回
{name: value, url: value}。
- 若
新建 stores/channel.ts(Pinia)
使用 Composition API 风格,与 script.ts/cron.ts 保持一致。
export interface ChannelItem {
id: string;
channelName: string;
channelUrl: string;
}
const STORAGE_KEY = 'zn-ai:selected-channels';
export const useChannelStore = defineStore('channel', () => {
const scriptStore = useScriptStore();
// 用户选中的"一键打开"渠道(持久化到 localStorage)
const selectedChannels = ref<ChannelItem[]>([]);
// 从脚本 store 动态聚合可用渠道(去重,按 URL 去重)
const availableChannels = computed<ChannelItem[]>(() => {
const map = new Map<string, ChannelItem>();
for (const script of scriptStore.safeScripts) {
if (!script.channel) continue;
const resolved = resolveChannel(script.channel);
const url = resolved.url;
if (!map.has(url)) {
map.set(url, {
id: `channel-${url}`,
channelName: resolved.name || url,
channelUrl: url,
});
}
}
return Array.from(map.values());
});
const loadSelectedChannels = () => { ... };
const saveSelectedChannels = () => { ... };
const addSelectedChannel = (item: ChannelItem) => { ... };
const removeSelectedChannel = (id: string) => { ... };
const setSelectedChannels = (items: ChannelItem[]) => { ... };
return {
selectedChannels,
availableChannels,
loadSelectedChannels,
saveSelectedChannels,
addSelectedChannel,
removeSelectedChannel,
setSelectedChannels,
};
});
注意:AddChannelDialog 打开时应基于 availableChannels 做搜索,确认保存时调用 setSelectedChannels + saveSelectedChannels。
2. UI 层:AddChannelDialog.vue 重塑
当前弹窗是两个空白输入框(名称+URL),需要改为搜索选择 + 已选列表。
新交互流程
- 顶部搜索区:
el-autocomplete(或el-input+ 自定义下拉列表)绑定availableChannels过滤结果; - 选择后:将渠道添加到下方的"已选渠道"列表(去重检查);
- 已选列表:使用卡片/表格形式展示,每行包含:
- 渠道名称
- 渠道 URL(截断显示,tooltip 展示完整)
- 删除按钮(
el-icon Delete)
- 底部操作:取消 / 确认。确认时持久化到 store。
状态隔离
弹窗打开时,先 loadSelectedChannels() 到本地临时数组;编辑过程中操作临时数组;确认时写入 store 并持久化;取消时直接关闭,不污染 store。
样式适配
复用项目已有的 custom-script-dialog 圆角/深色模式变量,保持视觉一致性。
3. 修复 TaskCenter.vue
- 移除
import { channels } from '@constant/channel'。 - 注入
useChannelStore。 - 点击"一键打开各渠道"时:
const channelStore = useChannelStore(); window.api.openChannel(channelStore.selectedChannels) - 如果
selectedChannels为空,可给出提示(ElMessage.warning)或仍然允许空数组由主进程处理。
4. ScriptEditorDialog.vue 适配
- 将
import { channels } from '@constant/channel'改为import { channelDictionary, resolveChannel } from '@constant/channel'。 getChannelUrl(channel)改为在channelDictionary中查找。- channel URL 替换 watcher 逻辑同步使用
channelDictionary的 values。
5. open_all_channel.js 与主进程适配
当前 open_all_channel.js 读取环境变量 CHANNELS,期望元素结构为 { channelUrl: string }(第 81 行 channels[i]?.channelUrl)。
channelStore.selectedChannels 的结构保持为 Item({ id, channelName, channelUrl }),因此主进程接收到的数组本身已包含 channelUrl 字段。
需要确认 runTaskOperationService.ts 中 IPC_EVENTS.OPEN_CHANNEL handler:
ipcMain.handle(IPC_EVENTS.OPEN_CHANNEL, async (_event, channels: any) => { ... })
它直接将 channels 传给 executeScriptServiceInstance.executeScript(scriptPath, { channels }),后者 JSON.stringify 后写入 CHANNELS 环境变量。
结论:只要 TaskCenter.vue 传递的数组元素包含 channelUrl,open_all_channel.js 无需修改即可正常工作。
但用户要求"同步更新 open_all_channel.js",为确保严谨,计划内包含兼容性检查:若发现字段名不一致,统一调整为 channelUrl;若需要增强(如按名称去重、空值过滤),则在 runTaskOperationService.ts 的 handler 中做适配,保持 open_all_channel.js 的输入契约稳定。
Sub-agent 分工(共 3 个)
Agent A — 数据层与字典迁移
负责范围:
- 改造
zn-ai/src/constant/channel.ts(移除数组,保留字典+resolveChannel)。 - 新建
zn-ai/src/stores/channel.ts(Pinia store,含availableChannels/selectedChannels/ localStorage 持久化)。 - 修改
ScriptEditorDialog.vue中的引用,从原channels数组切换到channelDictionary。
输入依赖:已确认的 script.ts 结构、channel.ts 原数组。
输出产物:可运行的数据层代码。
Agent B — AddChannelDialog UI 重构
负责范围:
- 重写
zn-ai/src/pages/home/components/AddChannelDialog.vue。 - 实现搜索选择(
el-autocomplete或自定义搜索+下拉)与已选列表展示。 - 对接
useChannelStore:读取availableChannels,保存时写入selectedChannels。 - 保持与项目一致的 dark/light 样式。
输入依赖:Agent A 完成的 stores/channel.ts。
工作方式:可以并行启动,但代码编辑需等待 Agent A 的 store 文件就位(或先在本地 mock 接口)。
Agent C — TaskCenter 修复与端到端验证
负责范围:
- 修改
zn-ai/src/pages/home/TaskCenter.vue:移除硬编码导入,注入useChannelStore,修复.valuebug。 - 检查
zn-ai/electron/process/runTaskOperationService.ts与open_all_channel.js的数据格式兼容性,必要时做适配。 - 在
zn-ai/src/pages/home/index.vue中添加channelStore.loadSelectedChannels()初始化(onMounted 或合适位置)。 - 运行项目(
pnpm run dev或等效命令),验证:- 脚本管理中有渠道数据时,
AddChannelDialog能搜索到; - 选择并保存后,
TaskCenter能正确传递数组; open_all_channel.js能正常解析并打开页面。
- 脚本管理中有渠道数据时,
输入依赖:Agent A 与 Agent B 的产物。 工作方式:必须在 A、B 完成后启动。
执行顺序
Agent A (数据层) ─┬─► Agent B (UI 弹窗)
│
└─► Agent C (流程修复与验证)
- A 与 B 可并行或准并行:B 可以在 A 提交 store 初版后立即开始对接。
- C 必须在 A、B 都完成后启动,负责联调与验证。
风险与回退策略
| 风险 | 缓解措施 |
|---|---|
availableChannels 为空(脚本管理无渠道数据) |
AddChannelDialog 搜索下拉显示"暂无可用渠道",已选列表允许空状态;TaskCenter 点击时给出提示 |
脚本 channel 字段格式混乱(既有 URL 又有名称) |
resolveChannel() 统一做归一化处理,以 URL 为唯一键 |
open_all_channel.js 字段契约被打破 |
在 runTaskOperationService.ts 的 handler 中做字段映射/过滤,保证脚本侧输入不变 |
| localStorage 中旧数据格式不兼容 | loadSelectedChannels 读取时做 schema 校验,不兼容则清空并回退到空数组 |
验证清单(Agent C 负责)
channels.ts中不再导出硬编码数组。AddChannelDialog打开后,能从脚本中搜索出渠道(如 fliggy / meituan / douyin)。- 选择多个渠道后列表正确显示,删除单个后状态正确。
- 点击"确认"关闭弹窗,再次打开能恢复上次保存的选项。
TaskCenter中点击"一键打开各渠道",openChannel参数为正确的ChannelItem[](非 undefined)。open_all_channel.js成功解析CHANNELS并依次打开对应页面。- 脚本编辑弹窗中
getChannelUrl仍能正确将fliggy解析为对应 URL。