diff --git a/packages/geek-auto-start-chat-with-boss/default-config-file/boss.json b/packages/geek-auto-start-chat-with-boss/default-config-file/boss.json index 7f8215a..86602cd 100644 --- a/packages/geek-auto-start-chat-with-boss/default-config-file/boss.json +++ b/packages/geek-auto-start-chat-with-boss/default-config-file/boss.json @@ -23,5 +23,11 @@ "recentMessageQuantityForLlm": 8, "rechatLlmFallback": 1, "onlyRemindBossWithExpectJobType": true - } + }, + "jobSourceList": [ + { + "type": "expect", + "enabled": true + } + ] } \ No newline at end of file diff --git a/packages/geek-auto-start-chat-with-boss/index.mjs b/packages/geek-auto-start-chat-with-boss/index.mjs index df51dfd..2f799d6 100644 --- a/packages/geek-auto-start-chat-with-boss/index.mjs +++ b/packages/geek-auto-start-chat-with-boss/index.mjs @@ -152,6 +152,54 @@ if ( jobDetailRegExpMatchLogic = JobDetailRegExpMatchLogic.EVERY } +let { + jobSourceList +} = readConfigFile('boss.json') + +if (!jobSourceList?.length) { + jobSourceList = [ + { + type: "expect", + enabled: true + } + ] +} +const normalizedJobSource = [] +const addedSourceSet = new Set() +for (const source of jobSourceList) { + if (addedSourceSet.has(source.type)) { + continue + } + if (!source?.enabled) { + continue + } + if (source.type === 'search') { + for (const searchOption of (source.children ?? [])) { + if (!searchOption.enabled || !searchOption.keyword?.trim()) { + continue + } + const key = [ + source.type, + searchOption.keyword.trim() + ].join('__') + if (addedSourceSet.has(key)) { + continue + } + normalizedJobSource.push({ + type: 'search', + keyword: searchOption.keyword.trim() + }) + addedSourceSet.add(key) + } + addedSourceSet.add(source.type) + } + else { + normalizedJobSource.push({ + type: source.type, + }) + addedSourceSet.add(source.type) + } +} const localStoragePageUrl = `https://www.zhipin.com/desktop/` const recommendJobPageUrl = `https://www.zhipin.com/web/geek/jobs` @@ -443,23 +491,6 @@ async function setFilterCondition (selectedFilters) { } } -const jobSource = [ - { - name: 'recommendJob' - }, - { - name: 'userSetExpectJob' - }, - { - name: 'searchJob', - keyword: 'HRBP' - }, - { - name: 'searchJob', - keyword: '招聘' - } -] - async function toRecommendPage (hooks) { let userInfoPromise = page.waitForResponse((response) => { if (response.url().startsWith('https://www.zhipin.com/wapi/zpuser/wap/getUserInfo.json')) { @@ -512,11 +543,11 @@ async function toRecommendPage (hooks) { const SEARCH_BOX_SELECTOR = `.c-search-input .search-input-box` const computedSourceList = [] - for (const source of jobSource) { - switch (source.name) { - case 'recommendJob': { + for (const source of normalizedJobSource) { + switch (source.type) { + case 'recommend': { computedSourceList.push({ - sourceName: source.name, + type: source.type, selector: RECOMMEND_JOB_ENTRY_SELECTOR, async getIsCurrentActiveSource () { return await page.evaluate( @@ -536,12 +567,12 @@ async function toRecommendPage (hooks) { }) continue } - case 'userSetExpectJob': { + case 'expect': { await page.waitForSelector(USER_SET_EXPECT_JOB_ENTRIES_SELECTOR) const allExpectJobEntryHandles = await page.$$(USER_SET_EXPECT_JOB_ENTRIES_SELECTOR) allExpectJobEntryHandles.forEach((it, index) => { computedSourceList.push({ - sourceName: source.name, + type: source.type, selector: `${USER_SET_EXPECT_JOB_ENTRIES_SELECTOR}:nth-child(${index + 1})`, async getIsCurrentActiveSource () { return await page.evaluate( @@ -566,9 +597,9 @@ async function toRecommendPage (hooks) { }) break } - case 'searchJob': { + case 'search': { computedSourceList.push({ - sourceName: source.name, + type: source.type, async getIsCurrentActiveSource () { const elHandle = await page.$(`.page-jobs-main`) const currentKeyWord = await elHandle?.evaluate((el) => { diff --git a/packages/ui/package.json b/packages/ui/package.json index b76d3f1..5d75824 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -44,7 +44,8 @@ "pinia": "^3.0.2", "puppeteer": "20.1.0", "puppeteer-extra-plugin-stealth": "2.11.2", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "vuedraggable": "^4.1.0" }, "devDependencies": { "@electron-toolkit/eslint-config": "^1.0.2", diff --git a/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts b/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts index 67600d2..c055eb1 100644 --- a/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts +++ b/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts @@ -156,6 +156,9 @@ export default function initIpc() { bossConfig.isSkipEmptyConditionForCombineRecommendJobFilter = payload.isSkipEmptyConditionForCombineRecommendJobFilter } + if (hasOwn(payload, 'jobSourceList')) { + bossConfig.jobSourceList = payload.jobSourceList + } promiseArr.push(writeConfigFile('boss.json', bossConfig)) diff --git a/packages/ui/src/renderer/src/features/JobSourceDragOrderer/index.vue b/packages/ui/src/renderer/src/features/JobSourceDragOrderer/index.vue new file mode 100644 index 0000000..f9c19e4 --- /dev/null +++ b/packages/ui/src/renderer/src/features/JobSourceDragOrderer/index.vue @@ -0,0 +1,245 @@ + + + + + diff --git a/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue b/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue index 7b702fe..8505df3 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue @@ -17,6 +17,14 @@ + + +
+
职位来源及查找顺序
+ +
+
+
@@ -885,6 +893,7 @@ import { debounce } from 'lodash-es' import mittBus from '../../../utils/mitt' import CityChooser from './components/CityChooser.vue' import conditions from '@geekgeekrun/geek-auto-start-chat-with-boss/internal-config/job-filter-conditions-20241002.json' +import JobSourceDragOrderer from '../../../features/JobSourceDragOrderer/index.vue' const router = useRouter() @@ -917,7 +926,13 @@ const formContent = ref({ StrategyScopeOptionWhenMarkJobNotMatch.ONLY_COMPANY_MATCHED_JOB, jobDetailRegExpMatchLogic: JobDetailRegExpMatchLogic.EVERY, - isSkipEmptyConditionForCombineRecommendJobFilter: false + isSkipEmptyConditionForCombineRecommendJobFilter: false, + __jobSourceList: formatJobSourceConfigToFormValue([ + { + type: 'expect', + enabled: true + } + ]) }) const anyCombineBossRecommendFilterHasCondition = computed(() => { @@ -1032,6 +1047,9 @@ electron.ipcRenderer.invoke('fetch-config-file-content').then((res) => { res.config['boss.json'].jobDetailRegExpMatchLogic ?? JobDetailRegExpMatchLogic.EVERY formContent.value.isSkipEmptyConditionForCombineRecommendJobFilter = res.config['boss.json'].isSkipEmptyConditionForCombineRecommendJobFilter ?? false + formContent.value.__jobSourceList = formatJobSourceConfigToFormValue( + res.config['boss.json'].jobSourceList || [] + ) }) const formRules = { @@ -1104,7 +1122,12 @@ const handleSubmit = async () => { console.log(err) return } - await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value)) + const clonedFormContent = JSON.parse(JSON.stringify(formContent.value)) + clonedFormContent.jobSourceList = formatJobSourceFormValueToConfig( + clonedFormContent.__jobSourceList + ) + delete clonedFormContent.__jobSourceList + await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(clonedFormContent)) mittBus.emit('auto-start-chat-with-boss-config-saved') router.replace({ path: '/geekAutoStartChatWithBoss/prepareRun', @@ -1129,7 +1152,12 @@ const handleSave = async () => { console.log(err) return } - await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value)) + const clonedFormContent = JSON.parse(JSON.stringify(formContent.value)) + clonedFormContent.jobSourceList = formatJobSourceFormValueToConfig( + clonedFormContent.__jobSourceList + ) + delete clonedFormContent.__jobSourceList + await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(clonedFormContent)) mittBus.emit('auto-start-chat-with-boss-config-saved') ElMessage.success('配置保存成功') gtagRenderer('config_saved') @@ -1438,6 +1466,55 @@ function getJobDetailRegExpMatchLogicConfig() { } return result } + +function formatJobSourceConfigToFormValue(config = []) { + const typeToNameKey = { + recommend: '推荐列表中的职位', + expect: '根据设置的求职期望推荐的职位', + search: '通过搜索找到的职位' + } + const isInitEmpty = !config?.length + config = config.filter((it) => Object.hasOwn(typeToNameKey, it.type)) + + const addedSet = new Set() + const tempArr = [] + config.forEach((it) => { + if (!Object.hasOwn(typeToNameKey, it.type)) { + return + } + tempArr.push(it) + addedSet.add(it.type) + }) + config = tempArr + Object.keys(typeToNameKey).forEach((k) => { + if (addedSet.has(k)) { + return + } + // handle init value + tempArr.push({ + type: k, + enabled: isInitEmpty && k === 'expect' + }) + addedSet.add(k) + }) + + return config.map((outerItem) => { + return { + item: outerItem, + label: typeToNameKey[outerItem.type], + children: + outerItem.children && outerItem.type === 'search' + ? (outerItem.children || []).map((innerItem) => ({ + item: innerItem + })) + : null + } + }) +} + +function formatJobSourceFormValueToConfig(formValue = []) { + return formValue.map((it) => it.item) +}