mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-05-12 02:19:55 +08:00
add ui of commonJobConditionConfig
This commit is contained in:
@@ -58,6 +58,7 @@ import {
|
||||
waitForUserApproveAgreement
|
||||
} from '../../../features/first-launch-notice-window'
|
||||
import { getLastUsedAndAvailableBrowser } from '../../DOWNLOAD_DEPENDENCIES/utils/browser-history'
|
||||
import { createCommonJobConditionConfigWindow } from '../../../window/commonJobConditionConfigWindow'
|
||||
|
||||
export default function initIpc() {
|
||||
ipcMain.handle('fetch-config-file-content', async () => {
|
||||
@@ -607,6 +608,13 @@ export default function initIpc() {
|
||||
}
|
||||
}
|
||||
})
|
||||
ipcMain.handle('common-job-condition-config', async () => {
|
||||
createCommonJobConditionConfigWindow({
|
||||
parent: mainWindow!,
|
||||
modal: true,
|
||||
show: true
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.handle('exit-app-immediately', () => {
|
||||
app.exit(0)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { BrowserWindow } from 'electron'
|
||||
import path from 'path'
|
||||
|
||||
export let commonJobConditionConfigWindow: BrowserWindow | null = null
|
||||
export function createCommonJobConditionConfigWindow(
|
||||
opt?: Electron.BrowserWindowConstructorOptions
|
||||
): BrowserWindow {
|
||||
// Create the browser window.
|
||||
if (commonJobConditionConfigWindow) {
|
||||
commonJobConditionConfigWindow!.show()
|
||||
}
|
||||
commonJobConditionConfigWindow = new BrowserWindow({
|
||||
width: 1024,
|
||||
height: 768,
|
||||
resizable: false,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.js'),
|
||||
sandbox: false
|
||||
},
|
||||
...opt
|
||||
})
|
||||
|
||||
commonJobConditionConfigWindow.on('ready-to-show', () => {
|
||||
commonJobConditionConfigWindow!.show()
|
||||
})
|
||||
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
if (process.env.NODE_ENV === 'development' && process.env['ELECTRON_RENDERER_URL']) {
|
||||
commonJobConditionConfigWindow.loadURL(
|
||||
process.env['ELECTRON_RENDERER_URL'] + '#/commonJobConditionConfig'
|
||||
)
|
||||
} else {
|
||||
commonJobConditionConfigWindow.loadURL(
|
||||
'file://' + path.join(__dirname, '../renderer/index.html') + '#/commonJobConditionConfig'
|
||||
)
|
||||
}
|
||||
|
||||
commonJobConditionConfigWindow!.once('closed', () => {
|
||||
commonJobConditionConfigWindow = null
|
||||
})
|
||||
|
||||
return commonJobConditionConfigWindow!
|
||||
}
|
||||
@@ -0,0 +1,708 @@
|
||||
<template>
|
||||
<div class="common-job-condition-config" flex flex-col h-full>
|
||||
<div flex-1 of-auto>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formContent"
|
||||
:rules="formRules"
|
||||
inline-message
|
||||
w-800px
|
||||
pt-30px
|
||||
pb-30px
|
||||
ml-auto
|
||||
mr-auto
|
||||
>
|
||||
<div mb20px>公共职位筛选条件</div>
|
||||
<el-form-item prop="expectCompanies" mb0>
|
||||
<div
|
||||
font-size-14px
|
||||
flex
|
||||
mb6px
|
||||
:style="{
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'baseline',
|
||||
width: '100%',
|
||||
lineHeight: '1.25em'
|
||||
}"
|
||||
>
|
||||
<div>
|
||||
期望投递公司
|
||||
<br /><span font-size-12px
|
||||
><b color-orange>逗号分隔</b>,不区分大小写;输入框留空表示不筛选</span
|
||||
>
|
||||
</div>
|
||||
<el-dropdown @command="handleExpectCompanyTemplateClicked">
|
||||
<el-button size="small"
|
||||
>公司列表模板 <el-icon class="el-icon--right"><arrow-down /></el-icon
|
||||
></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="item in expectCompanyTemplateList"
|
||||
:key="item.name"
|
||||
:command="item"
|
||||
>{{ item.name }}</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="formContent.expectCompanies"
|
||||
:autosize="{ minRows: 4 }"
|
||||
max-h-8lh
|
||||
type="textarea"
|
||||
placeholder="置空表示“不限公司,任意公司都可以投递”"
|
||||
@blur="
|
||||
formContent.expectCompanies = normalizeCommaSplittedStr(formContent.expectCompanies)
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="h-1px bg-#f0f0f0" mt16px mb8px />
|
||||
<div
|
||||
ref="blockCompanyNameRegExpSectionEl"
|
||||
font-size-14px
|
||||
flex
|
||||
:style="{
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'baseline',
|
||||
width: '100%',
|
||||
lineHeight: '1.25em'
|
||||
}"
|
||||
>
|
||||
<div mb6px>
|
||||
不期望投递公司<b color-orange>正则</b><br /><span font-size-12px
|
||||
><b color-orange>正则表达式</b>,不区分大小写;输入框留空表示不筛选;<span
|
||||
color-orange
|
||||
>优先级高于上方“期望投递公司”</span
|
||||
><br />请<b color-red>小心验证</b
|
||||
>你编写的正则,填写太过于宽泛的正则(例如`.*`)将导致任何职位都不会开聊</span
|
||||
>
|
||||
</div>
|
||||
<el-dropdown @command="handleBlockCompanyNameRegExpTemplateClicked">
|
||||
<el-button size="small"
|
||||
>公司列表模板 <el-icon class="el-icon--right"><arrow-down /></el-icon
|
||||
></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="item in blockCompanyNameRegExpTemplateList"
|
||||
:key="item.name"
|
||||
:command="item"
|
||||
>{{ item.name }}</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div
|
||||
class="block-company-filter-wrap"
|
||||
:style="{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: '10px'
|
||||
}"
|
||||
>
|
||||
<el-form-item prop="blockCompanyNameRegExpStr" mb0 w-full>
|
||||
<el-input
|
||||
v-model="formContent.blockCompanyNameRegExpStr"
|
||||
:autosize="{ minRows: 4 }"
|
||||
max-h-8lh
|
||||
type="textarea"
|
||||
placeholder="置空表示“不限公司,任意公司都不会被标记为不合适”"
|
||||
@blur="
|
||||
formContent.blockCompanyNameRegExpStr =
|
||||
formContent.blockCompanyNameRegExpStr?.trim() ?? ''
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="h-1px bg-#f0f0f0" mt16px mb16px />
|
||||
<div mt16px>
|
||||
<div font-size-14px mb8px>工作地</div>
|
||||
<div
|
||||
:style="{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: '10px'
|
||||
}"
|
||||
>
|
||||
<el-form-item prop="expectCityList" mb0>
|
||||
<div
|
||||
font-size-12px
|
||||
:style="{
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: '100%'
|
||||
}"
|
||||
>
|
||||
<city-chooser v-model="formContent.expectCityList">
|
||||
<template #default="{ modelValue, showDialog, clearValue }">
|
||||
<div v-if="modelValue?.length">
|
||||
<div>当前已选择城市:</div>
|
||||
<div flex flex-wrap gap-10px>
|
||||
<el-tag v-for="it in modelValue" :key="it">
|
||||
{{ it }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>当前未选择任何期望城市,将不会按照城市进行筛选</div>
|
||||
</div>
|
||||
<div
|
||||
line-height-1
|
||||
:style="{
|
||||
marginTop: modelValue?.length ? '10px' : ''
|
||||
}"
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
// isDialogVisible = true
|
||||
showDialog()
|
||||
gtagRenderer('choose_city_entry_button_clicked')
|
||||
}
|
||||
"
|
||||
>选择城市</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="modelValue?.length"
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="clearValue"
|
||||
>清空已选择的所有城市</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</city-chooser>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-1px bg-#f0f0f0" mt16px mb16px />
|
||||
<div mt16px>
|
||||
<div font-size-14px mb8px>
|
||||
薪资(仅支持按月计算薪资的职位;非按月计算薪资职位(例如兼职职位、实习职位)将直接跳过)
|
||||
</div>
|
||||
<div
|
||||
:style="{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: '10px'
|
||||
}"
|
||||
>
|
||||
<div>
|
||||
<el-form-item prop="expectSalaryLow" mb10px>
|
||||
<div w-full>
|
||||
<div font-size-12px>薪资筛选方式</div>
|
||||
<el-select
|
||||
v-model="formContent.expectSalaryCalculateWay"
|
||||
@change="handleExpectSalaryCalculateWayChanged"
|
||||
>
|
||||
<el-option
|
||||
v-for="op in expectSalaryCalculateWayOption"
|
||||
:key="op.value"
|
||||
:label="op.name"
|
||||
:value="op.value"
|
||||
>{{ op.name }}</el-option
|
||||
>
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item prop="expectSalaryLow" mb10px>
|
||||
<div>
|
||||
<div font-size-12px>期望薪资范围</div>
|
||||
<div>
|
||||
<el-input-number
|
||||
v-model="formContent.expectSalaryLow"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
:step="0.25"
|
||||
placeholder="不设置"
|
||||
@change="
|
||||
() => {
|
||||
gtagRenderer('expect_salary_low_changed')
|
||||
ensureSalaryRangeCorrect({ formContent })
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #prefix>下限</template>
|
||||
<template #suffix>
|
||||
<template v-if="formContent.expectSalaryLow">
|
||||
<template
|
||||
v-if="
|
||||
formContent.expectSalaryCalculateWay ===
|
||||
SalaryCalculateWay.MONTH_SALARY
|
||||
"
|
||||
>k</template
|
||||
>
|
||||
<template
|
||||
v-if="
|
||||
formContent.expectSalaryCalculateWay ===
|
||||
SalaryCalculateWay.ANNUAL_PACKAGE
|
||||
"
|
||||
>W</template
|
||||
>
|
||||
</template>
|
||||
</template>
|
||||
</el-input-number>
|
||||
-
|
||||
<el-input-number
|
||||
v-model="formContent.expectSalaryHigh"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
:step="0.25"
|
||||
placeholder="不设置"
|
||||
@change="
|
||||
() => {
|
||||
gtagRenderer('expect_salary_high_changed')
|
||||
ensureSalaryRangeCorrect({ formContent })
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #prefix>上限</template>
|
||||
<template #suffix>
|
||||
<template v-if="formContent.expectSalaryHigh">
|
||||
<template
|
||||
v-if="
|
||||
formContent.expectSalaryCalculateWay ===
|
||||
SalaryCalculateWay.MONTH_SALARY
|
||||
"
|
||||
>k</template
|
||||
>
|
||||
<template
|
||||
v-if="
|
||||
formContent.expectSalaryCalculateWay ===
|
||||
SalaryCalculateWay.ANNUAL_PACKAGE
|
||||
"
|
||||
>W</template
|
||||
>
|
||||
</template>
|
||||
</template>
|
||||
</el-input-number>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="
|
||||
formContent.expectSalaryCalculateWay === SalaryCalculateWay.ANNUAL_PACKAGE &&
|
||||
(formContent.expectSalaryLow || formContent.expectSalaryHigh)
|
||||
"
|
||||
mb10px
|
||||
>
|
||||
<div>
|
||||
<div font-size-12px>薪资范围满足以下条件的职位将会被匹配</div>
|
||||
<div>
|
||||
<div flex flex-nowrap flex-items-start>
|
||||
<template
|
||||
v-for="(mGroup, index) in [
|
||||
[12, 13, 14, 15, 16, 17, 18],
|
||||
[19, 20, 21, 22, 23, 24]
|
||||
]"
|
||||
:key="index"
|
||||
>
|
||||
<table
|
||||
:style="{
|
||||
lineHeight: '1.25em'
|
||||
}"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
v-for="(text, i) in ['月薪下限', '月薪上限', '']"
|
||||
:key="i"
|
||||
:style="{
|
||||
borderBottom: '2px solid #f0f0f0'
|
||||
}"
|
||||
>
|
||||
{{ text }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr v-for="m in mGroup" :key="m">
|
||||
<td>
|
||||
{{
|
||||
formContent.expectSalaryLow
|
||||
? ((formContent.expectSalaryLow / m) * 10).toFixed(2)
|
||||
: '无下限'
|
||||
}}<small v-if="formContent.expectSalaryLow" class="color-#999 ml-2px"
|
||||
>k</small
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
formContent.expectSalaryHigh
|
||||
? ((formContent.expectSalaryHigh / m) * 10).toFixed(2)
|
||||
: '无上限'
|
||||
}}<small v-if="formContent.expectSalaryHigh" class="color-#999 ml-2px"
|
||||
>k</small
|
||||
>
|
||||
</td>
|
||||
<td>{{ m }}薪</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div v-if="index !== 1" class="bg-#f0f0f0 w-2px flex-self-stretch"></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-1px bg-#f0f0f0" mt16px mb16px />
|
||||
<div>
|
||||
<div
|
||||
flex
|
||||
:style="{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}"
|
||||
>
|
||||
<div font-size-14px>期望职位信息</div>
|
||||
<div>
|
||||
<el-dropdown ml20px @command="handleExpectJobFilterTemplateClicked">
|
||||
<el-button size="small"
|
||||
>职位详情筛选模板(按职类区分)
|
||||
<el-icon class="el-icon--right"><arrow-down /></el-icon
|
||||
></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu
|
||||
:style="{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr 1fr'
|
||||
}"
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-for="item in expectJobFilterTemplateList"
|
||||
:key="item.name"
|
||||
:command="item"
|
||||
>{{ item.name }}</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:style="{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
gap: '10px'
|
||||
}"
|
||||
>
|
||||
<div flex-1>
|
||||
<el-form-item mb0 prop="expectJobNameRegExpStr">
|
||||
<div font-size-12px>职位名称/类型/描述 正则匹配筛选逻辑</div>
|
||||
<el-select
|
||||
v-model="formContent.jobDetailRegExpMatchLogic"
|
||||
@change="(value) => gtagRenderer('job_detail_re_ml_change', { value })"
|
||||
>
|
||||
<el-option
|
||||
v-for="op in jobDetailRegExpMatchLogicOptions"
|
||||
:key="op.value"
|
||||
:label="op.name"
|
||||
:value="op.value"
|
||||
>{{ op.name }}</el-option
|
||||
>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div
|
||||
:style="{
|
||||
width: '100%',
|
||||
height: '1px',
|
||||
backgroundColor: '#f0f0f0',
|
||||
marginTop: '0.5lh'
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
:style="{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '2em 1fr',
|
||||
gap: '5px',
|
||||
flex: 1,
|
||||
alignItems: 'end'
|
||||
}"
|
||||
class="job-detail-filter-wrap"
|
||||
>
|
||||
<div></div>
|
||||
<el-tooltip
|
||||
effect="light"
|
||||
placement="bottom"
|
||||
@show="gtagRenderer('tooltip_show_about_how_to_fill_df')"
|
||||
>
|
||||
<template #content>
|
||||
<div w-800px>
|
||||
<div style="margin-top: 4px; margin-bottom: 4px">
|
||||
上方“职位名称/类型/描述
|
||||
正则匹配筛选逻辑”配置,你可以自行决定如下三个正则“所有正则匹配时才认为职位匹配”还是“任一正则匹配时即认为职位匹配”。
|
||||
</div>
|
||||
<ul m0>
|
||||
<li>
|
||||
当选择“所有正则匹配时才认为职位匹配”规则时,如果你留空某个输入框,表示任何职位一定匹配这个条件。
|
||||
</li>
|
||||
<li>
|
||||
当选择“任一正则匹配时即认为职位匹配”规则时,如果你留空某个输入框,表示任何职位一定不匹配这个条件。
|
||||
</li>
|
||||
</ul>
|
||||
<b>请注意</b>,如果<span color-orange>三个输入框均留空</span
|
||||
>,无论上方“职位名称/类型/描述 正则匹配筛选逻辑”配置是什么,都表示<span
|
||||
color-orange
|
||||
>列表中出现的任意职位都将认为同时匹配这三个条件(即不根据职位名称/类型/描述进行筛选)</span
|
||||
>。<br />
|
||||
因此,可以按照如下场景填写你对于期望职位的筛选条件:
|
||||
<ul style="margin-top: 4px; margin-bottom: 4px">
|
||||
<li>
|
||||
如果你只考虑工作类型,请填写“职位类型正则”输入框,其余两个输入框清空。这可以确保求职方向基本正确。
|
||||
</li>
|
||||
<li>
|
||||
如果你着重关注职位描述,请填写“职位描述正则”,其余两个输入框酌情填写。
|
||||
</li>
|
||||
<li>
|
||||
如果你想开聊列表里的推荐的任意职位,不根据职位名称/类型/描述进行筛选,请清空这三个输入框。
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
你可以在右侧"职位详情筛选模板"选择一个模板,并在选中模板基础上尝试修改
|
||||
</div>
|
||||
<div>
|
||||
<b>“职位类型正则”填写过程中请注意</b
|
||||
>,“职位类型”是由BOSS直聘预定义好的一系列职位分类,因此<b>请按照这个分类来编写正则</b>。<br />
|
||||
<!-- 这个分类可以在此找到:<br />
|
||||
<img w-400px src="../resources/job-type-source-entry.png" /> -->
|
||||
</div>
|
||||
<div>
|
||||
关于误伤/误投的排查,<a
|
||||
href="javascript:;"
|
||||
style="color: var(--el-color-primary)"
|
||||
@click.prevent="handleHowToFillDetailFilterClick"
|
||||
>请参阅这个链接</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-button
|
||||
type="text"
|
||||
font-size-12px
|
||||
:style="{
|
||||
width: 'fit-content',
|
||||
padding: '0',
|
||||
height: 'auto',
|
||||
position: 'relative',
|
||||
top: '6px'
|
||||
}"
|
||||
><span><QuestionFilled w-1em h-1em mr2px /></span
|
||||
>如下三个输入框工作机制是怎样的?怎样填写?误伤/误投如何排查?</el-button
|
||||
>
|
||||
</el-tooltip>
|
||||
<div></div>
|
||||
<el-form-item mb0 prop="expectJobNameRegExpStr">
|
||||
<div font-size-12px>职位名称正则(不区分大小写)</div>
|
||||
<el-input
|
||||
v-model="formContent.expectJobNameRegExpStr"
|
||||
type="textarea"
|
||||
:placeholder="
|
||||
getJobDetailRegExpMatchLogicConfig({ formContent }).inputPlaceholderText
|
||||
"
|
||||
:autosize="{ minRows: 2 }"
|
||||
max-h-6lh
|
||||
@blur="
|
||||
formContent.expectJobNameRegExpStr =
|
||||
formContent.expectJobNameRegExpStr?.trim() ?? ''
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div
|
||||
mb0px
|
||||
font-size-12px
|
||||
flex
|
||||
flex-justify-center
|
||||
fw-800
|
||||
flex-self-start
|
||||
position-relative
|
||||
style="top: 42px"
|
||||
>
|
||||
{{ getJobDetailRegExpMatchLogicConfig({ formContent }).logicText }}
|
||||
</div>
|
||||
<el-form-item mb0 prop="expectJobTypeRegExpStr">
|
||||
<div ref="jobDetailRegExpSectionEl" font-size-12px>
|
||||
职位类型正则(推荐填写,不区分大小写)
|
||||
</div>
|
||||
<el-input
|
||||
v-model="formContent.expectJobTypeRegExpStr"
|
||||
type="textarea"
|
||||
:placeholder="
|
||||
getJobDetailRegExpMatchLogicConfig({ formContent }).inputPlaceholderText
|
||||
"
|
||||
:autosize="{ minRows: 2 }"
|
||||
max-h-6lh
|
||||
@blur="
|
||||
formContent.expectJobTypeRegExpStr =
|
||||
formContent.expectJobTypeRegExpStr?.trim() ?? ''
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div
|
||||
mb0px
|
||||
font-size-12px
|
||||
flex
|
||||
flex-justify-center
|
||||
fw-800
|
||||
flex-self-start
|
||||
position-relative
|
||||
style="top: 42px"
|
||||
>
|
||||
{{ getJobDetailRegExpMatchLogicConfig({ formContent }).logicText }}
|
||||
</div>
|
||||
<el-form-item mb0 prop="expectJobDescRegExpStr">
|
||||
<div font-size-12px>职位描述正则(不区分大小写)</div>
|
||||
<el-input
|
||||
v-model="formContent.expectJobDescRegExpStr"
|
||||
type="textarea"
|
||||
:placeholder="
|
||||
getJobDetailRegExpMatchLogicConfig({ formContent }).inputPlaceholderText
|
||||
"
|
||||
:autosize="{ minRows: 2 }"
|
||||
max-h-6lh
|
||||
@blur="
|
||||
formContent.expectJobDescRegExpStr =
|
||||
formContent.expectJobDescRegExpStr?.trim() ?? ''
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="bg-#f8f8f8 pb10px pt10px">
|
||||
<div
|
||||
:style="{
|
||||
display: 'flex',
|
||||
justifyContent: 'end',
|
||||
maxWidth: '800px',
|
||||
margin: '0 auto',
|
||||
paddingLeft: '20px',
|
||||
paddingRight: 'calc(20px + 16px)'
|
||||
}"
|
||||
>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { gtagRenderer as baseGtagRenderer } from '@renderer/utils/gtag'
|
||||
import { SalaryCalculateWay } from '@geekgeekrun/sqlite-plugin/src/enums'
|
||||
import CityChooser from '../MainLayout/GeekAutoStartChatWithBoss/components/CityChooser.vue'
|
||||
import { QuestionFilled, ArrowDown } from '@element-plus/icons-vue'
|
||||
|
||||
import {
|
||||
getJobDetailRegExpMatchLogicConfig,
|
||||
expectSalaryCalculateWayOption,
|
||||
ensureSalaryRangeCorrect,
|
||||
expectCompanyTemplateList,
|
||||
blockCompanyNameRegExpTemplateList,
|
||||
getHandlerForBlockCompanyNameRegExpTemplateClicked,
|
||||
getHandlerForExpectCompanyTemplateClicked,
|
||||
getHandlerForExpectJobFilterTemplateClicked,
|
||||
getRuleOfExpectJobNameRegExpStr,
|
||||
getRuleOfExpectJobDescRegExpStr,
|
||||
getRuleOfExpectJobTypeRegExpStr,
|
||||
getRuleOfBlockCompanyNameRegExpStr,
|
||||
jobDetailRegExpMatchLogicOptions,
|
||||
getHandlerForExpectSalaryCalculateWayChanged,
|
||||
normalizeCommaSplittedStr
|
||||
} from '../MainLayout/GeekAutoStartChatWithBoss/common'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import expectJobFilterTemplateList from '../MainLayout/GeekAutoStartChatWithBoss/expectJobFilterTemplateList'
|
||||
|
||||
const gtagRenderer = (name, params?: object) => {
|
||||
return baseGtagRenderer(name, {
|
||||
scene: 'cjc_config',
|
||||
...params
|
||||
})
|
||||
}
|
||||
const router = useRouter()
|
||||
|
||||
const formContent = ref({
|
||||
expectJobNameRegExpStr: '',
|
||||
expectJobTypeRegExpStr: '',
|
||||
expectJobDescRegExpStr: '',
|
||||
expectSalaryCalculateWay: SalaryCalculateWay.ANNUAL_PACKAGE,
|
||||
expectSalaryHigh: null,
|
||||
expectSalaryLow: null,
|
||||
blockCompanyNameRegExpStr: ''
|
||||
})
|
||||
|
||||
const jobDetailRegExpSectionEl = ref<HTMLDivElement>()
|
||||
const blockCompanyNameRegExpSectionEl = ref<HTMLDivElement>()
|
||||
const formRules = computed(() => ({
|
||||
expectJobNameRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator: getRuleOfExpectJobNameRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl })
|
||||
},
|
||||
expectJobTypeRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator: getRuleOfExpectJobTypeRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl })
|
||||
},
|
||||
expectJobDescRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator: getRuleOfExpectJobDescRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl })
|
||||
},
|
||||
blockCompanyNameRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator: getRuleOfBlockCompanyNameRegExpStr({ gtagRenderer, blockCompanyNameRegExpSectionEl })
|
||||
}
|
||||
}))
|
||||
|
||||
const handleBlockCompanyNameRegExpTemplateClicked =
|
||||
getHandlerForBlockCompanyNameRegExpTemplateClicked({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
|
||||
const handleExpectCompanyTemplateClicked = getHandlerForExpectCompanyTemplateClicked({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
|
||||
const handleExpectJobFilterTemplateClicked = getHandlerForExpectJobFilterTemplateClicked({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
|
||||
function handleHowToFillDetailFilterClick() {
|
||||
gtagRenderer('click_linux_do_how_to_fill_df')
|
||||
electron.ipcRenderer.send(
|
||||
'open-external-link',
|
||||
'https://linux.do/t/topic/640626/74?u=geekgeekrun'
|
||||
)
|
||||
}
|
||||
|
||||
const handleExpectSalaryCalculateWayChanged = getHandlerForExpectSalaryCalculateWayChanged({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
|
||||
function handleCancel() {
|
||||
//
|
||||
window.close()
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
async function handleSave() {
|
||||
await formRef.value?.validate()
|
||||
//
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.common-job-condition-config .el-form-item__error.el-form-item__error--inline {
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,282 @@
|
||||
import { SalaryCalculateWay, JobDetailRegExpMatchLogic } from '@geekgeekrun/sqlite-plugin/src/enums'
|
||||
import sampleCompanyList from '@geekgeekrun/geek-auto-start-chat-with-boss/default-config-file/sample-company-list.json'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
export function isJobDetailRegExpEmpty({ formContent }) {
|
||||
return [
|
||||
formContent.expectJobDescRegExpStr,
|
||||
formContent.expectJobNameRegExpStr,
|
||||
formContent.expectJobTypeRegExpStr
|
||||
]
|
||||
.map((it) => Boolean(it?.trim()))
|
||||
.every((it) => it === false)
|
||||
}
|
||||
|
||||
export function getJobDetailRegExpMatchLogicConfig({ formContent }) {
|
||||
const result = {
|
||||
logicText: '-',
|
||||
inputPlaceholderText: '-'
|
||||
}
|
||||
if (formContent.jobDetailRegExpMatchLogic === JobDetailRegExpMatchLogic.EVERY) {
|
||||
Object.assign(result, {
|
||||
logicText: '且',
|
||||
inputPlaceholderText: 'true'
|
||||
})
|
||||
}
|
||||
if (formContent.jobDetailRegExpMatchLogic === JobDetailRegExpMatchLogic.SOME) {
|
||||
Object.assign(result, {
|
||||
logicText: '或',
|
||||
inputPlaceholderText: 'false'
|
||||
})
|
||||
}
|
||||
|
||||
if (isJobDetailRegExpEmpty({ formContent })) {
|
||||
result.inputPlaceholderText = 'true'
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const expectSalaryCalculateWayOption = [
|
||||
{
|
||||
name: '月薪(单位为 千元 - 即“k”)',
|
||||
value: SalaryCalculateWay.MONTH_SALARY
|
||||
},
|
||||
{
|
||||
name: '“年包”(单位为 万元 - 即“W”)',
|
||||
value: SalaryCalculateWay.ANNUAL_PACKAGE
|
||||
}
|
||||
]
|
||||
|
||||
export function ensureSalaryRangeCorrect({ formContent }) {
|
||||
if (!formContent.expectSalaryHigh || isNaN(parseFloat(formContent.expectSalaryHigh))) {
|
||||
formContent.expectSalaryHigh = null
|
||||
} else {
|
||||
formContent.expectSalaryHigh = parseFloat(formContent.expectSalaryHigh.toFixed(2))
|
||||
}
|
||||
if (!formContent.expectSalaryLow || isNaN(parseFloat(formContent.expectSalaryLow))) {
|
||||
formContent.expectSalaryLow = null
|
||||
} else {
|
||||
formContent.expectSalaryLow = parseFloat(formContent.expectSalaryLow.toFixed(2))
|
||||
}
|
||||
|
||||
if (
|
||||
formContent.expectSalaryLow &&
|
||||
formContent.expectSalaryHigh &&
|
||||
formContent.expectSalaryLow > formContent.expectSalaryHigh
|
||||
) {
|
||||
formContent.expectSalaryHigh = formContent.expectSalaryLow
|
||||
}
|
||||
}
|
||||
|
||||
export function getRuleOfExpectJobNameRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl }) {
|
||||
return (_, value, cb) => {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_expect_job_name')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_expect_job_name', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
jobDetailRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_expect_job_name', { v: value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRuleOfExpectJobTypeRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl }) {
|
||||
return (_, value, cb) => {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_expect_job_type')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_expect_job_type', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
jobDetailRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_expect_job_type', { v: value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRuleOfExpectJobDescRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl }) {
|
||||
return (_, value, cb) => {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_expect_job_desc')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_expect_job_desc', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
jobDetailRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_expect_job_desc', { v: value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRuleOfBlockCompanyNameRegExpStr({
|
||||
gtagRenderer,
|
||||
blockCompanyNameRegExpSectionEl
|
||||
}) {
|
||||
return (_, value, cb) => {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_bcn')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_bcn', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
blockCompanyNameRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_bcn', { v: value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const expectCompanyTemplateList = [
|
||||
{
|
||||
name: '不限公司(随便投)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
name: '示例公司',
|
||||
value: sampleCompanyList.join(',')
|
||||
},
|
||||
{
|
||||
name: '大厂及关联企业',
|
||||
value: `抖音,字节,字跳,有竹居,脸萌,头条,懂车帝,滴滴,嘀嘀,巨量引擎,小桔,网易,有道,腾讯,酷狗,酷我,阅文,搜狗,小鹅通,富途,京东,沃东天骏,达达,达冠,京邦达,百度,昆仑芯,小度,度小满,爱奇艺,携程,趣拿,去哪儿,集度,智图,长地万方,瑞图万方,道道通,小熊博望,理想,蔚来,顺丰,丰巢,中通,圆通,申通,跨越,讯飞,同程,艺龙,马蜂窝,贝壳,自如,链家,我爱我家,相寓,多点,金山,小米,猎豹,新浪,微博,阿里,淘宝,淘麦郎,天猫,盒马,口碑,优视,夸克,UC,蚂蚁,高德,LAZADA,来赞达,飞猪,菜鸟,哈啰,钉钉,乌鸫,饿了么,美团,三快,猫眼,快手,映客,小红书,行吟,奇虎,360,三六零,鸿盈,奇富,奇元,亚信,启明星辰,奇安信,深信服,长亭,绿盟,天融信,商汤,SenseTime,大华,海康威视,hikvision,汽车之家,车好多,瓜子,易车,昆仑万维,昆仑天工,闲徕,趣加,FunPlus,完美,马上消费,轻松,水滴,白龙马,58,更赢,车欢欢,五八,红布林,致美,快狗,天鹅到家,转转,美餐,知乎,智者四海,易点云,搜狐,用友,畅捷通,猿辅导,小猿,猿力,好未来,学而思,希望学,新东方,东方甄选,东方优选,作业帮,高途,跟谁学,学科网,天学网,一起教育,一起作业,美术宝,火花思维,粉笔,51talk,爱学习,高思,老虎国际,一心向上,向上一意,联想,拉勾,乐视,欢聚,竞技世界,拼多多,寻梦,从鲸,TEMU,得物,有赞,Moka,希瑞亚斯,北森,OPPO,欧珀,vivo,维沃,小天才,步步高,读书郎,货拉拉,陌陌,探探,Shopee,虾皮,首汽租车,GoFun,神州租车,天眼查,旷视,小冰,美图,智谱华章,MiniMax,石头科技,迅雷,TP,锐捷,Tenda,腾达,斐讯,希音,SHEIN,稀宇,深言,百川智能,与爱为舞,牵手,Grab,爱回收,洋钱罐,瓴岳,得到,思维造物,地平线,咪咕,翼支付,电信,天翼,联通,蓝湖,墨刀,海尔,美的,米哈游,传音,同花顺,国美,TCL`
|
||||
},
|
||||
{
|
||||
name: '阿里系',
|
||||
value: `阿里,淘宝,淘麦郎,天猫,盒马,口碑,优视,夸克,UC,蚂蚁,飞猪,乌鸫,饿了么,LAZADA,来赞达,菜鸟,哈啰,钉钉,高德,白龙马,新浪,微博`
|
||||
},
|
||||
{
|
||||
name: '字节(头条/抖音)系',
|
||||
value: `抖音,字节,字跳,有竹居,脸萌,头条,懂车帝,巨量引擎`
|
||||
},
|
||||
{
|
||||
name: '百度系',
|
||||
value: `百度,昆仑芯,小度,度小满,爱奇艺,携程,趣拿,去哪儿,集度,作业帮,智图,长地万方,瑞图万方,道道通,小熊博望`
|
||||
},
|
||||
{
|
||||
name: '腾讯系',
|
||||
value: `腾讯,酷狗,酷我,阅文,搜狗,小鹅通,富途,京东,沃东天骏,达达,达冠,京邦达,美团,三快,猫眼,快手,拼多多,寻梦,从鲸,TEMU,Shopee,虾皮,滴滴,嘀嘀,小桔,转转`
|
||||
},
|
||||
{
|
||||
name: '外包、劳务派遣企业',
|
||||
value: `青钱,软通动力,南天,睿服,中电金信,佰钧成,云链,博彦,汉克时代,柯莱特,拓保,亿达信息,纬创,微创,微澜,诚迈科技,法本,兆尹,诚迈,联合永道,新致软件,宇信科技,华为,德科,FESCO,科锐,科之锐`
|
||||
}
|
||||
]
|
||||
|
||||
export const blockCompanyNameRegExpTemplateList = [
|
||||
{
|
||||
name: '不限公司(不按照公司名称来标注不合适)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
name: '外包、劳务派遣企业',
|
||||
value: `青钱|软通动力|南天|睿服|中电金信|佰钧成|云链|博彦|汉克时代|柯莱特|拓保|亿达信息|纬创|微创|微澜|诚迈科技|法本|兆尹|诚迈|联合永道|新致软件|宇信科技|华为|德科|FESCO|科锐|科之锐`
|
||||
},
|
||||
{
|
||||
name: '京东及相关公司',
|
||||
value: '京东|沃东天骏|达达|达冠|京邦达'
|
||||
}
|
||||
]
|
||||
|
||||
export function getHandlerForExpectCompanyTemplateClicked({ gtagRenderer, formContent }) {
|
||||
return function handleExpectCompanyTemplateClicked(item) {
|
||||
gtagRenderer('expect_company_tpl_clicked', {
|
||||
name: item.name
|
||||
})
|
||||
formContent.value.expectCompanies = item.value
|
||||
}
|
||||
}
|
||||
|
||||
export function getHandlerForExpectJobFilterTemplateClicked({ gtagRenderer, formContent }) {
|
||||
return function handleExpectJobFilterTemplateClicked(item) {
|
||||
gtagRenderer('expect_job_filter_tpl_clicked', {
|
||||
name: item.name
|
||||
})
|
||||
Object.assign(formContent.value, {
|
||||
...item.config
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getHandlerForBlockCompanyNameRegExpTemplateClicked({ gtagRenderer, formContent }) {
|
||||
return function handleBlockCompanyNameRegExpTemplateClicked(item) {
|
||||
gtagRenderer('bcn_reg_exp_tpl_clicked', {
|
||||
name: item.name
|
||||
})
|
||||
formContent.value.blockCompanyNameRegExpStr = item.value
|
||||
}
|
||||
}
|
||||
|
||||
export const jobDetailRegExpMatchLogicOptions = [
|
||||
{
|
||||
name: '“且”模式 - 所有正则匹配时才认为职位匹配',
|
||||
value: JobDetailRegExpMatchLogic.EVERY
|
||||
},
|
||||
{
|
||||
name: '“或”模式 - 任一正则匹配时即认为职位匹配',
|
||||
value: JobDetailRegExpMatchLogic.SOME
|
||||
}
|
||||
]
|
||||
|
||||
export function getHandlerForExpectSalaryCalculateWayChanged({ gtagRenderer, formContent }) {
|
||||
return async function handleExpectSalaryCalculateWayChanged(value) {
|
||||
gtagRenderer('expect_salary_calculate_way_changed', { value })
|
||||
|
||||
await nextTick()
|
||||
// convert annual package to month salary as 12-month
|
||||
if (value === SalaryCalculateWay.MONTH_SALARY) {
|
||||
if (formContent.value.expectSalaryHigh) {
|
||||
formContent.value.expectSalaryHigh = Number(
|
||||
((formContent.value.expectSalaryHigh * 10) / 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
if (formContent.value.expectSalaryLow) {
|
||||
formContent.value.expectSalaryLow = Number(
|
||||
((formContent.value.expectSalaryLow * 10) / 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
// convert month salary to annual package as 12-month
|
||||
else if (value === SalaryCalculateWay.ANNUAL_PACKAGE) {
|
||||
if (formContent.value.expectSalaryHigh) {
|
||||
formContent.value.expectSalaryHigh = Number(
|
||||
((formContent.value.expectSalaryHigh / 10) * 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
if (formContent.value.expectSalaryLow) {
|
||||
formContent.value.expectSalaryLow = Number(
|
||||
((formContent.value.expectSalaryLow / 10) * 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizeCommaSplittedStr = (str) => {
|
||||
return str
|
||||
.split(/,|,/)
|
||||
.map((it) => it.trim())
|
||||
.filter(Boolean)
|
||||
.join(',')
|
||||
}
|
||||
@@ -292,7 +292,11 @@
|
||||
max-h-8lh
|
||||
type="textarea"
|
||||
placeholder="置空表示“不限公司,任意公司都可以投递”"
|
||||
@blur="normalizeExpectCompanies"
|
||||
@blur="
|
||||
formContent.expectCompanies = normalizeCommaSplittedStr(
|
||||
formContent.expectCompanies
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="h-1px bg-#f0f0f0" mt16px mb8px />
|
||||
@@ -570,7 +574,7 @@
|
||||
@change="
|
||||
() => {
|
||||
gtagRenderer('expect_salary_low_changed')
|
||||
ensureSalaryRangeCorrect()
|
||||
ensureSalaryRangeCorrect({ formContent })
|
||||
}
|
||||
"
|
||||
>
|
||||
@@ -604,7 +608,7 @@
|
||||
@change="
|
||||
() => {
|
||||
gtagRenderer('expect_salary_high_changed')
|
||||
ensureSalaryRangeCorrect()
|
||||
ensureSalaryRangeCorrect({ formContent })
|
||||
}
|
||||
"
|
||||
>
|
||||
@@ -1033,7 +1037,9 @@
|
||||
<el-input
|
||||
v-model="formContent.expectJobNameRegExpStr"
|
||||
type="textarea"
|
||||
:placeholder="getJobDetailRegExpMatchLogicConfig().inputPlaceholderText"
|
||||
:placeholder="
|
||||
getJobDetailRegExpMatchLogicConfig({ formContent }).inputPlaceholderText
|
||||
"
|
||||
:autosize="{ minRows: 2 }"
|
||||
max-h-6lh
|
||||
@blur="
|
||||
@@ -1052,7 +1058,7 @@
|
||||
position-relative
|
||||
style="top: 42px"
|
||||
>
|
||||
{{ getJobDetailRegExpMatchLogicConfig().logicText }}
|
||||
{{ getJobDetailRegExpMatchLogicConfig({ formContent }).logicText }}
|
||||
</div>
|
||||
<el-form-item mb0 prop="expectJobTypeRegExpStr">
|
||||
<div ref="jobDetailRegExpSectionEl" font-size-12px>
|
||||
@@ -1061,7 +1067,9 @@
|
||||
<el-input
|
||||
v-model="formContent.expectJobTypeRegExpStr"
|
||||
type="textarea"
|
||||
:placeholder="getJobDetailRegExpMatchLogicConfig().inputPlaceholderText"
|
||||
:placeholder="
|
||||
getJobDetailRegExpMatchLogicConfig({ formContent }).inputPlaceholderText
|
||||
"
|
||||
:autosize="{ minRows: 2 }"
|
||||
max-h-6lh
|
||||
@blur="
|
||||
@@ -1080,14 +1088,16 @@
|
||||
position-relative
|
||||
style="top: 42px"
|
||||
>
|
||||
{{ getJobDetailRegExpMatchLogicConfig().logicText }}
|
||||
{{ getJobDetailRegExpMatchLogicConfig({ formContent }).logicText }}
|
||||
</div>
|
||||
<el-form-item mb0 prop="expectJobDescRegExpStr">
|
||||
<div font-size-12px>职位描述正则(不区分大小写)</div>
|
||||
<el-input
|
||||
v-model="formContent.expectJobDescRegExpStr"
|
||||
type="textarea"
|
||||
:placeholder="getJobDetailRegExpMatchLogicConfig().inputPlaceholderText"
|
||||
:placeholder="
|
||||
getJobDetailRegExpMatchLogicConfig({ formContent }).inputPlaceholderText
|
||||
"
|
||||
:autosize="{ minRows: 2 }"
|
||||
max-h-6lh
|
||||
@blur="
|
||||
@@ -1099,7 +1109,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isJobDetailRegExpEmpty()"
|
||||
v-if="!isJobDetailRegExpEmpty({ formContent })"
|
||||
:style="{
|
||||
width: '400px',
|
||||
borderLeft: '1px solid #f0f0f0',
|
||||
@@ -1247,7 +1257,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, ref, watch, nextTick, onUnmounted } from 'vue'
|
||||
import { ElForm, ElMessage } from 'element-plus'
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
import { QuestionFilled, ArrowDown } from '@element-plus/icons-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import AnyCombineBossRecommendFilter from '@renderer/features/AnyCombineBossRecommendFilter/index.vue'
|
||||
import StaticCombineBossRecommendFilter from '@renderer/features/StaticCombineBossRecommendFilter/index.vue'
|
||||
@@ -1258,8 +1268,6 @@ import {
|
||||
formatStaticCombineFilters
|
||||
} from '@geekgeekrun/geek-auto-start-chat-with-boss/combineCalculator.mjs'
|
||||
import { gtagRenderer as baseGtagRenderer } from '@renderer/utils/gtag'
|
||||
import sampleCompanyList from '@geekgeekrun/geek-auto-start-chat-with-boss/default-config-file/sample-company-list.json'
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import {
|
||||
CombineRecommendJobFilterType,
|
||||
MarkAsNotSuitOp,
|
||||
@@ -1275,6 +1283,24 @@ import JobSourceDragOrderer from '../../../features/JobSourceDragOrderer/index.v
|
||||
import expectJobFilterTemplateList from './expectJobFilterTemplateList'
|
||||
import RunningOverlay from '@renderer/features/RunningOverlay/index.vue'
|
||||
import { RUNNING_STATUS_ENUM } from '../../../../../common/enums/auto-start-chat'
|
||||
import {
|
||||
getJobDetailRegExpMatchLogicConfig,
|
||||
isJobDetailRegExpEmpty,
|
||||
expectSalaryCalculateWayOption,
|
||||
ensureSalaryRangeCorrect,
|
||||
getRuleOfExpectJobNameRegExpStr,
|
||||
getRuleOfExpectJobDescRegExpStr,
|
||||
getRuleOfBlockCompanyNameRegExpStr,
|
||||
expectCompanyTemplateList,
|
||||
blockCompanyNameRegExpTemplateList,
|
||||
getHandlerForExpectCompanyTemplateClicked,
|
||||
getHandlerForExpectJobFilterTemplateClicked,
|
||||
getHandlerForBlockCompanyNameRegExpTemplateClicked,
|
||||
getRuleOfExpectJobTypeRegExpStr,
|
||||
jobDetailRegExpMatchLogicOptions,
|
||||
getHandlerForExpectSalaryCalculateWayChanged,
|
||||
normalizeCommaSplittedStr
|
||||
} from './common'
|
||||
|
||||
const gtagRenderer = (name, params?: object) => {
|
||||
return baseGtagRenderer(name, {
|
||||
@@ -1451,7 +1477,7 @@ electron.ipcRenderer.invoke('fetch-config-file-content').then((res) => {
|
||||
StrategyScopeOptionWhenMarkJobNotMatch.ONLY_COMPANY_MATCHED_JOB
|
||||
formContent.value.expectSalaryLow = parseFloat(res.config['boss.json'].expectSalaryLow) || null
|
||||
formContent.value.expectSalaryHigh = parseFloat(res.config['boss.json'].expectSalaryHigh) || null
|
||||
ensureSalaryRangeCorrect()
|
||||
ensureSalaryRangeCorrect({ formContent })
|
||||
|
||||
// work exp
|
||||
formContent.value.expectWorkExpList =
|
||||
@@ -1497,60 +1523,15 @@ const blockCompanyNameRegExpSectionEl = ref()
|
||||
const formRules = {
|
||||
expectJobNameRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator(_, value, cb) {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_expect_job_name')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_expect_job_name', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
jobDetailRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_expect_job_name', { v: value })
|
||||
}
|
||||
}
|
||||
validator: getRuleOfExpectJobNameRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl })
|
||||
},
|
||||
expectJobTypeRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator(_, value, cb) {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_expect_job_type')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_expect_job_type', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
jobDetailRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_expect_job_type', { v: value })
|
||||
}
|
||||
}
|
||||
validator: getRuleOfExpectJobTypeRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl })
|
||||
},
|
||||
expectJobDescRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator(_, value, cb) {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_expect_job_desc')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_expect_job_desc', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
jobDetailRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_expect_job_desc', { v: value })
|
||||
}
|
||||
}
|
||||
validator: getRuleOfExpectJobDescRegExpStr({ gtagRenderer, jobDetailRegExpSectionEl })
|
||||
},
|
||||
__jobSourceList: {
|
||||
trigger: null,
|
||||
@@ -1611,22 +1592,7 @@ const formRules = {
|
||||
},
|
||||
blockCompanyNameRegExpStr: {
|
||||
trigger: 'blur',
|
||||
validator(_, value, cb) {
|
||||
if (!value) {
|
||||
cb()
|
||||
gtagRenderer('empty_reg_exp_for_bcn')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new RegExp(value, 'ig')
|
||||
gtagRenderer('valid_reg_exp_for_bcn', { v: value })
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(new Error(`正则无效:${err?.message}`))
|
||||
blockCompanyNameRegExpSectionEl.value?.scrollIntoViewIfNeeded()
|
||||
gtagRenderer('invalid_reg_exp_for_bcn', { v: value })
|
||||
}
|
||||
}
|
||||
validator: getRuleOfBlockCompanyNameRegExpStr({ gtagRenderer, blockCompanyNameRegExpSectionEl })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1707,7 +1673,7 @@ const handleSave = async () => {
|
||||
opTimes: formContent.value.sageTimeOpTimes
|
||||
})
|
||||
})
|
||||
normalizeExpectCompanies()
|
||||
formContent.value.expectCompanies = normalizeCommaSplittedStr(formContent.value.expectCompanies)
|
||||
try {
|
||||
await formRef.value!.validate()
|
||||
} catch (err) {
|
||||
@@ -1729,64 +1695,15 @@ const handleSave = async () => {
|
||||
gtagRenderer('config_saved')
|
||||
}
|
||||
|
||||
const normalizeExpectCompanies = () => {
|
||||
formContent.value.expectCompanies = formContent.value.expectCompanies
|
||||
.split(/,|,/)
|
||||
.map((it) => it.trim())
|
||||
.filter(Boolean)
|
||||
.join(',')
|
||||
}
|
||||
const handleExpectCompanyTemplateClicked = getHandlerForExpectCompanyTemplateClicked({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
|
||||
const expectCompanyTemplateList = [
|
||||
{
|
||||
name: '不限公司(随便投)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
name: '示例公司',
|
||||
value: sampleCompanyList.join(',')
|
||||
},
|
||||
{
|
||||
name: '大厂及关联企业',
|
||||
value: `抖音,字节,字跳,有竹居,脸萌,头条,懂车帝,滴滴,嘀嘀,巨量引擎,小桔,网易,有道,腾讯,酷狗,酷我,阅文,搜狗,小鹅通,富途,京东,沃东天骏,达达,达冠,京邦达,百度,昆仑芯,小度,度小满,爱奇艺,携程,趣拿,去哪儿,集度,智图,长地万方,瑞图万方,道道通,小熊博望,理想,蔚来,顺丰,丰巢,中通,圆通,申通,跨越,讯飞,同程,艺龙,马蜂窝,贝壳,自如,链家,我爱我家,相寓,多点,金山,小米,猎豹,新浪,微博,阿里,淘宝,淘麦郎,天猫,盒马,口碑,优视,夸克,UC,蚂蚁,高德,LAZADA,来赞达,飞猪,菜鸟,哈啰,钉钉,乌鸫,饿了么,美团,三快,猫眼,快手,映客,小红书,行吟,奇虎,360,三六零,鸿盈,奇富,奇元,亚信,启明星辰,奇安信,深信服,长亭,绿盟,天融信,商汤,SenseTime,大华,海康威视,hikvision,汽车之家,车好多,瓜子,易车,昆仑万维,昆仑天工,闲徕,趣加,FunPlus,完美,马上消费,轻松,水滴,白龙马,58,更赢,车欢欢,五八,红布林,致美,快狗,天鹅到家,转转,美餐,知乎,智者四海,易点云,搜狐,用友,畅捷通,猿辅导,小猿,猿力,好未来,学而思,希望学,新东方,东方甄选,东方优选,作业帮,高途,跟谁学,学科网,天学网,一起教育,一起作业,美术宝,火花思维,粉笔,51talk,爱学习,高思,老虎国际,一心向上,向上一意,联想,拉勾,乐视,欢聚,竞技世界,拼多多,寻梦,从鲸,TEMU,得物,有赞,Moka,希瑞亚斯,北森,OPPO,欧珀,vivo,维沃,小天才,步步高,读书郎,货拉拉,陌陌,探探,Shopee,虾皮,首汽租车,GoFun,神州租车,天眼查,旷视,小冰,美图,智谱华章,MiniMax,石头科技,迅雷,TP,锐捷,Tenda,腾达,斐讯,希音,SHEIN,稀宇,深言,百川智能,与爱为舞,牵手,Grab,爱回收,洋钱罐,瓴岳,得到,思维造物,地平线,咪咕,翼支付,电信,天翼,联通,蓝湖,墨刀,海尔,美的,米哈游,传音,同花顺,国美,TCL`
|
||||
},
|
||||
{
|
||||
name: '阿里系',
|
||||
value: `阿里,淘宝,淘麦郎,天猫,盒马,口碑,优视,夸克,UC,蚂蚁,飞猪,乌鸫,饿了么,LAZADA,来赞达,菜鸟,哈啰,钉钉,高德,白龙马,新浪,微博`
|
||||
},
|
||||
{
|
||||
name: '字节(头条/抖音)系',
|
||||
value: `抖音,字节,字跳,有竹居,脸萌,头条,懂车帝,巨量引擎`
|
||||
},
|
||||
{
|
||||
name: '百度系',
|
||||
value: `百度,昆仑芯,小度,度小满,爱奇艺,携程,趣拿,去哪儿,集度,作业帮,智图,长地万方,瑞图万方,道道通,小熊博望`
|
||||
},
|
||||
{
|
||||
name: '腾讯系',
|
||||
value: `腾讯,酷狗,酷我,阅文,搜狗,小鹅通,富途,京东,沃东天骏,达达,达冠,京邦达,美团,三快,猫眼,快手,拼多多,寻梦,从鲸,TEMU,Shopee,虾皮,滴滴,嘀嘀,小桔,转转`
|
||||
},
|
||||
{
|
||||
name: '外包、劳务派遣企业',
|
||||
value: `青钱,软通动力,南天,睿服,中电金信,佰钧成,云链,博彦,汉克时代,柯莱特,拓保,亿达信息,纬创,微创,微澜,诚迈科技,法本,兆尹,诚迈,联合永道,新致软件,宇信科技,华为,德科,FESCO,科锐,科之锐`
|
||||
}
|
||||
]
|
||||
function handleExpectCompanyTemplateClicked(item) {
|
||||
gtagRenderer('expect_company_tpl_clicked', {
|
||||
name: item.name
|
||||
})
|
||||
formContent.value.expectCompanies = item.value
|
||||
}
|
||||
|
||||
function handleExpectJobFilterTemplateClicked(item) {
|
||||
gtagRenderer('expect_job_filter_tpl_clicked', {
|
||||
name: item.name
|
||||
})
|
||||
|
||||
Object.assign(formContent.value, {
|
||||
...item.config
|
||||
})
|
||||
}
|
||||
const handleExpectJobFilterTemplateClicked = getHandlerForExpectJobFilterTemplateClicked({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
|
||||
const strategyOptionWhenCurrentJobNotMatch = [
|
||||
{
|
||||
@@ -1814,61 +1731,11 @@ const strategyScopeOptionWhenMarkJobNotMatch = [
|
||||
}
|
||||
]
|
||||
|
||||
const jobDetailRegExpMatchLogicOptions = [
|
||||
{
|
||||
name: '“且”模式 - 所有正则匹配时才认为职位匹配',
|
||||
value: JobDetailRegExpMatchLogic.EVERY
|
||||
},
|
||||
{
|
||||
name: '“或”模式 - 任一正则匹配时即认为职位匹配',
|
||||
value: JobDetailRegExpMatchLogic.SOME
|
||||
}
|
||||
]
|
||||
const handleExpectSalaryCalculateWayChanged = getHandlerForExpectSalaryCalculateWayChanged({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
|
||||
async function handleExpectSalaryCalculateWayChanged(value) {
|
||||
gtagRenderer('expect_salary_calculate_way_changed', { value })
|
||||
|
||||
await nextTick()
|
||||
// convert annual package to month salary as 12-month
|
||||
if (value === SalaryCalculateWay.MONTH_SALARY) {
|
||||
if (formContent.value.expectSalaryHigh) {
|
||||
formContent.value.expectSalaryHigh = Number(
|
||||
((formContent.value.expectSalaryHigh * 10) / 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
if (formContent.value.expectSalaryLow) {
|
||||
formContent.value.expectSalaryLow = Number(
|
||||
((formContent.value.expectSalaryLow * 10) / 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
// convert month salary to annual package as 12-month
|
||||
else if (value === SalaryCalculateWay.ANNUAL_PACKAGE) {
|
||||
if (formContent.value.expectSalaryHigh) {
|
||||
formContent.value.expectSalaryHigh = Number(
|
||||
((formContent.value.expectSalaryHigh / 10) * 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
if (formContent.value.expectSalaryLow) {
|
||||
formContent.value.expectSalaryLow = Number(
|
||||
((formContent.value.expectSalaryLow / 10) * 12).toFixed(2)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const expectSalaryCalculateWayOption = [
|
||||
{
|
||||
name: '月薪(单位为 千元 - 即“k”)',
|
||||
value: SalaryCalculateWay.MONTH_SALARY
|
||||
},
|
||||
{
|
||||
name: '“年包”(单位为 万元 - 即“W”)',
|
||||
value: SalaryCalculateWay.ANNUAL_PACKAGE
|
||||
}
|
||||
]
|
||||
const salaryMarkAsNotSuitLabelText = computed(() => {
|
||||
const textSeg = []
|
||||
if (formContent.value.expectSalaryLow) {
|
||||
@@ -1894,30 +1761,6 @@ const isShowSalaryMarkAsNotSuitStrategy = computed(() => {
|
||||
return flag
|
||||
})
|
||||
|
||||
function ensureSalaryRangeCorrect() {
|
||||
if (
|
||||
!formContent.value.expectSalaryHigh ||
|
||||
isNaN(parseFloat(formContent.value.expectSalaryHigh))
|
||||
) {
|
||||
formContent.value.expectSalaryHigh = null
|
||||
} else {
|
||||
formContent.value.expectSalaryHigh = parseFloat(formContent.value.expectSalaryHigh.toFixed(2))
|
||||
}
|
||||
if (!formContent.value.expectSalaryLow || isNaN(parseFloat(formContent.value.expectSalaryLow))) {
|
||||
formContent.value.expectSalaryLow = null
|
||||
} else {
|
||||
formContent.value.expectSalaryLow = parseFloat(formContent.value.expectSalaryLow.toFixed(2))
|
||||
}
|
||||
|
||||
if (
|
||||
formContent.value.expectSalaryLow &&
|
||||
formContent.value.expectSalaryHigh &&
|
||||
formContent.value.expectSalaryLow > formContent.value.expectSalaryHigh
|
||||
) {
|
||||
formContent.value.expectSalaryHigh = formContent.value.expectSalaryLow
|
||||
}
|
||||
}
|
||||
|
||||
const noActiveDefinitionMarks = computed(() => {
|
||||
let arr = [...activeDescList]
|
||||
arr.shift()
|
||||
@@ -1948,40 +1791,6 @@ function handleHowToFillDetailFilterClick() {
|
||||
)
|
||||
}
|
||||
|
||||
function isJobDetailRegExpEmpty() {
|
||||
return [
|
||||
formContent.value.expectJobDescRegExpStr,
|
||||
formContent.value.expectJobNameRegExpStr,
|
||||
formContent.value.expectJobTypeRegExpStr
|
||||
]
|
||||
.map((it) => Boolean(it?.trim()))
|
||||
.every((it) => it === false)
|
||||
}
|
||||
|
||||
function getJobDetailRegExpMatchLogicConfig() {
|
||||
const result = {
|
||||
logicText: '-',
|
||||
inputPlaceholderText: '-'
|
||||
}
|
||||
if (formContent.value.jobDetailRegExpMatchLogic === JobDetailRegExpMatchLogic.EVERY) {
|
||||
Object.assign(result, {
|
||||
logicText: '且',
|
||||
inputPlaceholderText: 'true'
|
||||
})
|
||||
}
|
||||
if (formContent.value.jobDetailRegExpMatchLogic === JobDetailRegExpMatchLogic.SOME) {
|
||||
Object.assign(result, {
|
||||
logicText: '或',
|
||||
inputPlaceholderText: 'false'
|
||||
})
|
||||
}
|
||||
|
||||
if (isJobDetailRegExpEmpty()) {
|
||||
result.inputPlaceholderText = 'true'
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function formatJobSourceConfigToFormValue(config = []) {
|
||||
const typeToNameKey = {
|
||||
recommend: '推荐列表中的职位',
|
||||
@@ -2063,26 +1872,11 @@ const handleStopButtonClick = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const blockCompanyNameRegExpTemplateList = [
|
||||
{
|
||||
name: '不限公司(不按照公司名称来标注不合适)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
name: '外包、劳务派遣企业',
|
||||
value: `青钱|软通动力|南天|睿服|中电金信|佰钧成|云链|博彦|汉克时代|柯莱特|拓保|亿达信息|纬创|微创|微澜|诚迈科技|法本|兆尹|诚迈|联合永道|新致软件|宇信科技|华为|德科|FESCO|科锐|科之锐`
|
||||
},
|
||||
{
|
||||
name: '京东及相关公司',
|
||||
value: '京东|沃东天骏|达达|达冠|京邦达'
|
||||
}
|
||||
]
|
||||
const handleBlockCompanyNameRegExpTemplateClicked = (item) => {
|
||||
gtagRenderer('bcn_reg_exp_tpl_clicked', {
|
||||
name: item.name
|
||||
const handleBlockCompanyNameRegExpTemplateClicked =
|
||||
getHandlerForBlockCompanyNameRegExpTemplateClicked({
|
||||
gtagRenderer,
|
||||
formContent
|
||||
})
|
||||
formContent.value.blockCompanyNameRegExpStr = item.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<div class="group-item">
|
||||
<div class="group-title">全局设置</div>
|
||||
<div flex flex-col class="link-list">
|
||||
<a href="javascript:void(0)" @click="handleClickConfigCommonJobCondition">
|
||||
公共职位筛选条件
|
||||
</a>
|
||||
<a href="javascript:void(0)" @click="handleClickBrowserSetting">
|
||||
编辑浏览器偏好<TopRight w-1em h-1em mr10px />
|
||||
</a>
|
||||
@@ -69,6 +72,15 @@ const handleClickConfigLlm = async () => {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClickConfigCommonJobCondition = async () => {
|
||||
gtagRenderer('config_cjc_clicked')
|
||||
try {
|
||||
await electron.ipcRenderer.invoke('common-job-condition-config')
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" src="./style.scss"></style>
|
||||
|
||||
@@ -59,6 +59,13 @@ const routes: Array<RouteRecordRaw> = [
|
||||
title: '已读不回自动复聊 大语言模型测试'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/commonJobConditionConfig',
|
||||
component: () => import('@renderer/page/CommonJobConditionConfig/index.vue'),
|
||||
meta: {
|
||||
title: '公共职位筛选条件'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/main-layout',
|
||||
component: () => import('@renderer/page/MainLayout/index.vue'),
|
||||
|
||||
Reference in New Issue
Block a user