1
This commit is contained in:
335
admin/src/views/work/apply_records.vue
Normal file
335
admin/src/views/work/apply_records.vue
Normal file
@@ -0,0 +1,335 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增投递记录</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="平台">
|
||||
<Select v-model="gridOption.param.seachOption.platform" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="boss">Boss直聘</Option>
|
||||
<Option value="liepin">猎聘</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="投递状态">
|
||||
<Select v-model="gridOption.param.seachOption.applyStatus" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="pending">待投递</Option>
|
||||
<Option value="applying">投递中</Option>
|
||||
<Option value="success">投递成功</Option>
|
||||
<Option value="failed">投递失败</Option>
|
||||
<Option value="duplicate">重复投递</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="反馈状态">
|
||||
<Select v-model="gridOption.param.seachOption.feedbackStatus" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="none">无反馈</Option>
|
||||
<Option value="viewed">已查看</Option>
|
||||
<Option value="interested">感兴趣</Option>
|
||||
<Option value="not_suitable">不合适</Option>
|
||||
<Option value="interview">面试邀约</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"></editModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import applyRecordsServer from '@/api/work/apply_records_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
rules["jobTitle"] = [{ required: true, message: '请填写岗位名称', trigger: 'blur' }]
|
||||
rules["companyName"] = [{ required: true, message: '请填写公司名称', trigger: 'blur' }]
|
||||
rules["platform"] = [{ required: true, message: '请选择平台', trigger: 'change' }]
|
||||
rules["applyStatus"] = [{ required: true, message: '请选择投递状态', trigger: 'change' }]
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'jobTitle', value: '岗位名称' },
|
||||
{ key: 'companyName', value: '公司名称' },
|
||||
{ key: 'sn_code', value: '设备SN码' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'jobTitle',
|
||||
value: '',
|
||||
platform: null,
|
||||
applyStatus: null,
|
||||
feedbackStatus: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '设备SN码', key: 'sn_code', minWidth: 120 },
|
||||
{
|
||||
title: '平台',
|
||||
key: 'platform',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const platformMap = {
|
||||
'boss': { text: 'Boss直聘', color: 'blue' },
|
||||
'liepin': { text: '猎聘', color: 'green' }
|
||||
}
|
||||
const platform = platformMap[params.row.platform] || { text: params.row.platform, color: 'default' }
|
||||
return h('Tag', { props: { color: platform.color } }, platform.text)
|
||||
}
|
||||
},
|
||||
{ title: '岗位名称', key: 'jobTitle', minWidth: 180 },
|
||||
{ title: '公司名称', key: 'companyName', minWidth: 180 },
|
||||
{ title: '薪资', key: 'salary', minWidth: 120 },
|
||||
{ title: '地点', key: 'location', minWidth: 120 },
|
||||
{
|
||||
title: '投递状态',
|
||||
key: 'applyStatus',
|
||||
minWidth: 120,
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'pending': { text: '待投递', color: 'default' },
|
||||
'applying': { text: '投递中', color: 'blue' },
|
||||
'success': { text: '投递成功', color: 'success' },
|
||||
'failed': { text: '投递失败', color: 'error' },
|
||||
'duplicate': { text: '重复投递', color: 'warning' }
|
||||
}
|
||||
const status = statusMap[params.row.applyStatus] || { text: params.row.applyStatus, color: 'default' }
|
||||
return h('Tag', { props: { color: status.color } }, status.text)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '反馈状态',
|
||||
key: 'feedbackStatus',
|
||||
minWidth: 120,
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'none': { text: '无反馈', color: 'default' },
|
||||
'viewed': { text: '已查看', color: 'blue' },
|
||||
'interested': { text: '感兴趣', color: 'success' },
|
||||
'not_suitable': { text: '不合适', color: 'warning' },
|
||||
'interview': { text: '面试邀约', color: 'success' }
|
||||
}
|
||||
const status = statusMap[params.row.feedbackStatus] || { text: params.row.feedbackStatus, color: 'default' }
|
||||
return h('Tag', { props: { color: status.color } }, status.text)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '匹配度',
|
||||
key: 'matchScore',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const score = params.row.matchScore || 0
|
||||
const color = score >= 80 ? 'success' : score >= 60 ? 'warning' : 'error'
|
||||
return h('Tag', { props: { color: color } }, `${score}%`)
|
||||
}
|
||||
},
|
||||
{ title: '投递时间', key: 'applyTime', minWidth: 250 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 280,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '查看详情',
|
||||
type: 'info',
|
||||
click: () => {
|
||||
this.showApplyDetail(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return window.framework.uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '设备SN码', key: 'sn_code', type: 'text', required: false },
|
||||
{ title: '平台', key: 'platform', type: 'select', required: true, options: [
|
||||
{ value: 'boss', label: 'Boss直聘' },
|
||||
{ value: 'liepin', label: '猎聘' }
|
||||
]},
|
||||
{ title: '岗位ID', key: 'jobId', type: 'text', required: false },
|
||||
{ title: '岗位名称', key: 'jobTitle', type: 'text', required: true },
|
||||
{ title: '公司名称', key: 'companyName', type: 'text', required: true },
|
||||
{ title: '薪资', key: 'salary', type: 'text' },
|
||||
{ title: '地点', key: 'location', type: 'text' },
|
||||
{ title: '岗位链接', key: 'jobUrl', type: 'text' },
|
||||
{ title: '投递状态', key: 'applyStatus', type: 'select', required: true, options: [
|
||||
{ value: 'pending', label: '待投递' },
|
||||
{ value: 'applying', label: '投递中' },
|
||||
{ value: 'success', label: '投递成功' },
|
||||
{ value: 'failed', label: '投递失败' },
|
||||
{ value: 'duplicate', label: '重复投递' }
|
||||
]},
|
||||
{ title: '反馈状态', key: 'feedbackStatus', type: 'select', options: [
|
||||
{ value: 'none', label: '无反馈' },
|
||||
{ value: 'viewed', label: '已查看' },
|
||||
{ value: 'interested', label: '感兴趣' },
|
||||
{ value: 'not_suitable', label: '不合适' },
|
||||
{ value: 'interview', label: '面试邀约' }
|
||||
]},
|
||||
{ title: 'HR姓名', key: 'hrName', type: 'text' },
|
||||
{ title: 'HR职位', key: 'hrPosition', type: 'text' },
|
||||
{ title: '匹配度', key: 'matchScore', type: 'number' },
|
||||
{ title: '是否外包', key: 'isOutsourcing', type: 'switch' },
|
||||
{ title: '备注', key: 'notes', type: 'textarea' }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
applyRecordsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows
|
||||
this.gridOption.param.pageOption.total = res.data.count
|
||||
})
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal()
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row)
|
||||
},
|
||||
delConfirm(row) {
|
||||
window.framework.uiTool.delConfirm(async () => {
|
||||
await applyRecordsServer.del(row)
|
||||
this.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
applyRecordsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
window.framework.funTool.downloadFile(res, '投递记录.csv')
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'jobTitle',
|
||||
value: '',
|
||||
platform: null,
|
||||
applyStatus: null,
|
||||
feedbackStatus: null
|
||||
}
|
||||
this.gridOption.param.pageOption.page = 1
|
||||
this.query(1)
|
||||
},
|
||||
showApplyDetail(row) {
|
||||
this.$Modal.info({
|
||||
title: '投递记录详情',
|
||||
width: 800,
|
||||
render: (h) => {
|
||||
return h('div', { style: { maxHeight: '500px', overflowY: 'auto' } }, [
|
||||
h('h3', { style: { marginBottom: '15px', color: '#2d8cf0' } }, '基本信息'),
|
||||
h('p', [h('strong', '设备SN码: '), row.sn_code || '未知']),
|
||||
h('p', [h('strong', '平台: '), row.platform === 'boss' ? 'Boss直聘' : row.platform === 'liepin' ? '猎聘' : row.platform]),
|
||||
h('p', [h('strong', '岗位名称: '), row.jobTitle]),
|
||||
h('p', [h('strong', '公司名称: '), row.companyName]),
|
||||
h('p', [h('strong', '薪资: '), row.salary || '未知']),
|
||||
h('p', [h('strong', '地点: '), row.location || '未知']),
|
||||
|
||||
h('h3', { style: { marginTop: '20px', marginBottom: '15px', color: '#2d8cf0' } }, '投递状态'),
|
||||
h('p', [h('strong', '投递状态: '), this.getApplyStatusText(row.applyStatus)]),
|
||||
h('p', [h('strong', '反馈状态: '), this.getFeedbackStatusText(row.feedbackStatus)]),
|
||||
h('p', [h('strong', '投递时间: '), row.applyTime || '未知']),
|
||||
h('p', [h('strong', '匹配度: '), `${row.matchScore || 0}%`]),
|
||||
|
||||
h('h3', { style: { marginTop: '20px', marginBottom: '15px', color: '#2d8cf0' } }, 'HR信息'),
|
||||
h('p', [h('strong', 'HR姓名: '), row.hrName || '未知']),
|
||||
h('p', [h('strong', 'HR职位: '), row.hrPosition || '未知']),
|
||||
|
||||
h('h3', { style: { marginTop: '20px', marginBottom: '15px', color: '#2d8cf0' } }, '其他信息'),
|
||||
h('p', [h('strong', '是否外包: '), row.isOutsourcing ? '是' : '否']),
|
||||
row.jobUrl ? h('p', [
|
||||
h('strong', '岗位链接: '),
|
||||
h('a', { attrs: { href: row.jobUrl, target: '_blank' } }, row.jobUrl)
|
||||
]) : null,
|
||||
row.notes ? h('div', { style: { marginTop: '15px' } }, [
|
||||
h('strong', '备注:'),
|
||||
h('p', { style: { whiteSpace: 'pre-wrap', marginTop: '10px', padding: '10px', backgroundColor: '#f5f5f5', borderRadius: '4px' } }, row.notes)
|
||||
]) : null
|
||||
])
|
||||
}
|
||||
})
|
||||
},
|
||||
getApplyStatusText(status) {
|
||||
const statusMap = {
|
||||
'pending': '待投递',
|
||||
'applying': '投递中',
|
||||
'success': '投递成功',
|
||||
'failed': '投递失败',
|
||||
'duplicate': '重复投递'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
},
|
||||
getFeedbackStatusText(status) {
|
||||
const statusMap = {
|
||||
'none': '无反馈',
|
||||
'viewed': '已查看',
|
||||
'interested': '感兴趣',
|
||||
'not_suitable': '不合适',
|
||||
'interview': '面试邀约'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key)
|
||||
return selected ? selected.value : '请选择搜索类型'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
317
admin/src/views/work/job_postings.vue
Normal file
317
admin/src/views/work/job_postings.vue
Normal file
@@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增岗位</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="平台">
|
||||
<Select v-model="gridOption.param.seachOption.platform" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="boss">Boss直聘</Option>
|
||||
<Option value="liepin">猎聘</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="是否外包">
|
||||
<Select v-model="gridOption.param.seachOption.isOutsourcing" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option :value="true">是</Option>
|
||||
<Option :value="false">否</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"></editModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import jobPostingsServer from '@/api/work/job_postings_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
rules["jobTitle"] = [{ required: true, message: '请填写岗位名称', trigger: 'blur' }]
|
||||
rules["companyName"] = [{ required: true, message: '请填写公司名称', trigger: 'blur' }]
|
||||
rules["platform"] = [{ required: true, message: '请选择平台', trigger: 'change' }]
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'jobTitle', value: '岗位名称' },
|
||||
{ key: 'companyName', value: '公司名称' },
|
||||
{ key: 'jobId', value: '岗位ID' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'jobTitle',
|
||||
value: '',
|
||||
platform: null,
|
||||
isOutsourcing: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: '岗位ID', key: 'jobId', minWidth: 180 },
|
||||
{
|
||||
title: '平台',
|
||||
key: 'platform',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const platformMap = {
|
||||
'boss': { text: 'Boss直聘', color: 'blue' },
|
||||
'liepin': { text: '猎聘', color: 'green' }
|
||||
}
|
||||
const platform = platformMap[params.row.platform] || { text: params.row.platform, color: 'default' }
|
||||
return h('Tag', { props: { color: platform.color } }, platform.text)
|
||||
}
|
||||
},
|
||||
{ title: '岗位名称', key: 'jobTitle', minWidth: 180 },
|
||||
{ title: '公司名称', key: 'companyName', minWidth: 180 },
|
||||
{ title: '薪资', key: 'salary', minWidth: 120 },
|
||||
{ title: '地点', key: 'location', minWidth: 120 },
|
||||
{ title: '经验要求', key: 'experience', minWidth: 100 },
|
||||
{ title: '学历要求', key: 'education', minWidth: 100 },
|
||||
{
|
||||
title: 'AI匹配度',
|
||||
key: 'aiMatchScore',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const score = params.row.aiMatchScore || 0
|
||||
const color = score >= 80 ? 'success' : score >= 60 ? 'warning' : 'error'
|
||||
return h('Tag', { props: { color: color } }, `${score}%`)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '是否外包',
|
||||
key: 'isOutsourcing',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: { color: params.row.isOutsourcing ? 'warning' : 'success' }
|
||||
}, params.row.isOutsourcing ? '是' : '否')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '投递状态',
|
||||
key: 'applyStatus',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'pending': { text: '待投递', color: 'default' },
|
||||
'applied': { text: '已投递', color: 'success' },
|
||||
'rejected': { text: '被拒绝', color: 'error' },
|
||||
'accepted': { text: '已接受', color: 'blue' }
|
||||
}
|
||||
const status = statusMap[params.row.applyStatus] || statusMap['pending']
|
||||
return h('Tag', { props: { color: status.color } }, status.text)
|
||||
}
|
||||
},
|
||||
{ title: '发布时间', key: 'publishTime', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 300,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
const isApplied = params.row.applyStatus === 'applied'
|
||||
let btns = [
|
||||
{
|
||||
title: '查看详情',
|
||||
type: 'info',
|
||||
click: () => {
|
||||
this.showJobDetail(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: isApplied ? '已投递' : '打招呼',
|
||||
type: isApplied ? 'default' : 'success',
|
||||
disabled: isApplied,
|
||||
click: () => {
|
||||
if (!isApplied) {
|
||||
this.jobGreet(params.row)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return window.framework.uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '岗位ID', key: 'jobId', type: 'text', required: false },
|
||||
{ title: '平台', key: 'platform', type: 'select', required: true, options: [
|
||||
{ value: 'boss', label: 'Boss直聘' },
|
||||
{ value: 'liepin', label: '猎聘' }
|
||||
]},
|
||||
{ title: '岗位名称', key: 'jobTitle', type: 'text', required: true },
|
||||
{ title: '公司名称', key: 'companyName', type: 'text', required: true },
|
||||
{ title: '薪资', key: 'salary', type: 'text' },
|
||||
{ title: '地点', key: 'location', type: 'text' },
|
||||
{ title: '经验要求', key: 'experience', type: 'text' },
|
||||
{ title: '学历要求', key: 'education', type: 'text' },
|
||||
{ title: '岗位描述', key: 'jobDescription', type: 'textarea' },
|
||||
{ title: '岗位链接', key: 'jobUrl', type: 'text' },
|
||||
{ title: 'AI匹配度', key: 'aiMatchScore', type: 'number' },
|
||||
{ title: '技能匹配度', key: 'aiSkillMatch', type: 'number' },
|
||||
{ title: '经验匹配度', key: 'aiExperienceMatch', type: 'number' },
|
||||
{ title: '薪资合理性', key: 'aiSalaryReasonable', type: 'number' },
|
||||
{ title: '公司质量评分', key: 'aiCompanyQuality', type: 'number' },
|
||||
{ title: 'AI分析结果', key: 'aiAnalysisResult', type: 'textarea' },
|
||||
{ title: '是否外包', key: 'isOutsourcing', type: 'switch' }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
jobPostingsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows
|
||||
this.gridOption.param.pageOption.total = res.data.count
|
||||
})
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal()
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row)
|
||||
},
|
||||
delConfirm(row) {
|
||||
window.framework.uiTool.delConfirm(async () => {
|
||||
await jobPostingsServer.del(row)
|
||||
this.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
jobPostingsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
window.framework.funTool.downloadFile(res, '岗位信息.csv')
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'jobTitle',
|
||||
value: '',
|
||||
platform: null,
|
||||
isOutsourcing: null
|
||||
}
|
||||
this.query(1)
|
||||
},
|
||||
showJobDetail(row) {
|
||||
this.$Modal.info({
|
||||
title: '岗位详情',
|
||||
width: 800,
|
||||
render: (h) => {
|
||||
return h('div', { style: { maxHeight: '500px', overflowY: 'auto' } }, [
|
||||
h('p', [h('strong', '岗位ID: '), row.jobId]),
|
||||
h('p', [h('strong', '平台: '), row.platform === 'boss' ? 'Boss直聘' : row.platform]),
|
||||
h('p', [h('strong', '岗位名称: '), row.jobTitle]),
|
||||
h('p', [h('strong', '公司名称: '), row.companyName]),
|
||||
h('p', [h('strong', '薪资: '), row.salary || '未知']),
|
||||
h('p', [h('strong', '地点: '), row.location || '未知']),
|
||||
h('p', [h('strong', '经验要求: '), row.experience || '未知']),
|
||||
h('p', [h('strong', '学历要求: '), row.education || '未知']),
|
||||
h('p', [h('strong', 'AI匹配度: '), `${row.aiMatchScore || 0}%`]),
|
||||
h('p', [h('strong', '是否外包: '), row.isOutsourcing ? '是' : '否']),
|
||||
h('p', [h('strong', '发布时间: '), row.publishTime || '未知']),
|
||||
row.jobDescription ? h('div', [
|
||||
h('strong', '岗位描述:'),
|
||||
h('p', { style: { whiteSpace: 'pre-wrap', marginTop: '10px' } }, row.jobDescription)
|
||||
]) : null,
|
||||
row.jobUrl ? h('p', [
|
||||
h('strong', '岗位链接: '),
|
||||
h('a', { attrs: { href: row.jobUrl, target: '_blank' } }, row.jobUrl)
|
||||
]) : null
|
||||
])
|
||||
}
|
||||
})
|
||||
},
|
||||
jobGreet(row) {
|
||||
this.$Modal.confirm({
|
||||
title: '确认打招呼',
|
||||
content: `确定要向 ${row.companyName} 的 ${row.jobTitle} 岗位打招呼吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const loading = this.$Message.loading({
|
||||
content: '正在发送打招呼...',
|
||||
duration: 0
|
||||
})
|
||||
|
||||
// 获取设备SN码(这里需要从配置或用户选择中获取)
|
||||
const sn_code = localStorage.getItem('current_sn_code') || 'GHJU'
|
||||
|
||||
await jobPostingsServer.jobGreet({
|
||||
sn_code: sn_code,
|
||||
encryptJobId: row.jobId,
|
||||
brandName: row.companyName,
|
||||
platform: row.platform || 'boss'
|
||||
})
|
||||
|
||||
loading()
|
||||
this.$Message.success('打招呼成功!')
|
||||
|
||||
// 刷新列表
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} catch (error) {
|
||||
this.$Message.error('打招呼失败:' + (error.message || '未知错误'))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key)
|
||||
return selected ? selected.value : '请选择搜索类型'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
331
admin/src/views/work/job_types.vue
Normal file
331
admin/src/views/work/job_types.vue
Normal file
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增职位类型</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="状态">
|
||||
<Select v-model="gridOption.param.seachOption.is_enabled" style="width: 120px" clearable
|
||||
@on-change="query(1)">
|
||||
<Option :value="1">启用</Option>
|
||||
<Option :value="0">禁用</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules" @on-save="handleSaveSuccess">
|
||||
</editModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import jobTypesServer from '@/api/work/job_types_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
rules["name"] = [{ required: true, message: '请填写职位类型名称', trigger: 'blur' }]
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'name', value: '职位类型名称' },
|
||||
{ key: 'description', value: '描述' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'name',
|
||||
value: '',
|
||||
is_enabled: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '职位类型名称', key: 'name', minWidth: 150 },
|
||||
{ title: '描述', key: 'description', minWidth: 200 },
|
||||
{
|
||||
title: '状态',
|
||||
key: 'is_enabled',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const status = params.row.is_enabled === 1
|
||||
return h('Tag', { props: { color: status ? 'success' : 'default' } }, status ? '启用' : '禁用')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '常见技能关键词', key: 'commonSkills', minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: '排除关键词', key: 'excludeKeywords', minWidth: 200
|
||||
},
|
||||
{ title: '排序', key: 'sort_order', minWidth: 80 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
render: (h, params) => {
|
||||
return h('div', [
|
||||
h('Button', {
|
||||
props: {
|
||||
type: 'primary',
|
||||
size: 'small'
|
||||
},
|
||||
style: {
|
||||
marginRight: '5px'
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
this.edit(params.row)
|
||||
}
|
||||
}
|
||||
}, '编辑'),
|
||||
h('Button', {
|
||||
props: {
|
||||
type: 'error',
|
||||
size: 'small'
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
this.del(params.row)
|
||||
}
|
||||
}
|
||||
}, '删除')
|
||||
])
|
||||
}
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{
|
||||
title: '职位类型名称',
|
||||
key: 'name',
|
||||
type: 'input',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
key: 'description',
|
||||
com: 'TextArea',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
title: '常见技能关键词',
|
||||
key: 'commonSkills',
|
||||
com: 'TextArea',
|
||||
|
||||
required: false,
|
||||
placeholder: '请输入JSON数组格式,例如:["Vue", "React", "Node.js"]',
|
||||
tooltip: '技能关键词列表,JSON数组格式'
|
||||
},
|
||||
{
|
||||
title: '排除关键词',
|
||||
key: 'excludeKeywords',
|
||||
com: 'TextArea',
|
||||
required: false,
|
||||
placeholder: '请输入JSON数组格式,例如:["外包", "销售", "客服"]',
|
||||
tooltip: '排除关键词列表,JSON数组格式'
|
||||
},
|
||||
{
|
||||
title: '是否启用',
|
||||
key: 'is_enabled',
|
||||
type: 'select',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 1, label: '启用' },
|
||||
{ value: 0, label: '禁用' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '排序顺序',
|
||||
key: 'sort_order',
|
||||
type: 'number',
|
||||
required: false,
|
||||
defaultValue: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const item = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key)
|
||||
return item ? `请输入${item.value}` : '请选择'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
if (page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
}
|
||||
const param = {
|
||||
pageOption: this.gridOption.param.pageOption,
|
||||
seachOption: {}
|
||||
}
|
||||
if (this.gridOption.param.seachOption.key && this.gridOption.param.seachOption.value) {
|
||||
param.seachOption[this.gridOption.param.seachOption.key] = this.gridOption.param.seachOption.value
|
||||
}
|
||||
if (this.gridOption.param.seachOption.is_enabled !== null) {
|
||||
param.seachOption.is_enabled = this.gridOption.param.seachOption.is_enabled
|
||||
}
|
||||
jobTypesServer.page(param).then(res => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data
|
||||
this.gridOption.data = data.rows
|
||||
this.gridOption.param.pageOption.total = data.count || data.total || 0
|
||||
} else {
|
||||
this.$Message.error(res.message || '查询失败')
|
||||
}
|
||||
}).catch(err => {
|
||||
this.$Message.error('查询失败:' + (err.message || '未知错误'))
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'name',
|
||||
value: '',
|
||||
is_enabled: null
|
||||
}
|
||||
this.query(1)
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.show({
|
||||
name: '',
|
||||
description: '',
|
||||
commonSkills: '[]',
|
||||
excludeKeywords: '[]',
|
||||
is_enabled: 1,
|
||||
sort_order: 0
|
||||
})
|
||||
},
|
||||
edit(row) {
|
||||
// 解析 JSON 字段
|
||||
let commonSkills = row.commonSkills || '[]'
|
||||
let excludeKeywords = row.excludeKeywords || '[]'
|
||||
|
||||
// 如果是字符串,尝试解析并格式化
|
||||
if (typeof commonSkills === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(commonSkills)
|
||||
commonSkills = JSON.stringify(parsed, null, 2)
|
||||
} catch (e) {
|
||||
// 保持原样
|
||||
}
|
||||
} else {
|
||||
commonSkills = JSON.stringify(commonSkills, null, 2)
|
||||
}
|
||||
|
||||
if (typeof excludeKeywords === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(excludeKeywords)
|
||||
excludeKeywords = JSON.stringify(parsed, null, 2)
|
||||
} catch (e) {
|
||||
// 保持原样
|
||||
}
|
||||
} else {
|
||||
excludeKeywords = JSON.stringify(excludeKeywords, null, 2)
|
||||
}
|
||||
|
||||
this.$refs.editModal.editShow({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
description: row.description || '',
|
||||
commonSkills: commonSkills,
|
||||
excludeKeywords: excludeKeywords,
|
||||
is_enabled: row.is_enabled,
|
||||
sort_order: row.sort_order || 0
|
||||
})
|
||||
},
|
||||
del(row) {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除职位类型"${row.name}"吗?`,
|
||||
onOk: () => {
|
||||
jobTypesServer.del(row).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('删除成功')
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} else {
|
||||
this.$Message.error(res.message || '删除失败')
|
||||
}
|
||||
}).catch(err => {
|
||||
this.$Message.error('删除失败:' + (err.message || '未知错误'))
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSaveSuccess(data) {
|
||||
// 处理 JSON 字段
|
||||
const formData = { ...data }
|
||||
|
||||
// 处理 commonSkills
|
||||
if (formData.commonSkills) {
|
||||
try {
|
||||
const parsed = typeof formData.commonSkills === 'string'
|
||||
? JSON.parse(formData.commonSkills)
|
||||
: formData.commonSkills
|
||||
formData.commonSkills = Array.isArray(parsed) ? parsed : []
|
||||
} catch (e) {
|
||||
this.$Message.warning('常见技能关键词格式错误,将使用空数组')
|
||||
formData.commonSkills = []
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 excludeKeywords
|
||||
if (formData.excludeKeywords) {
|
||||
try {
|
||||
const parsed = typeof formData.excludeKeywords === 'string'
|
||||
? JSON.parse(formData.excludeKeywords)
|
||||
: formData.excludeKeywords
|
||||
formData.excludeKeywords = Array.isArray(parsed) ? parsed : []
|
||||
} catch (e) {
|
||||
this.$Message.warning('排除关键词格式错误,将使用空数组')
|
||||
formData.excludeKeywords = []
|
||||
}
|
||||
}
|
||||
|
||||
const apiMethod = formData.id ? jobTypesServer.update : jobTypesServer.add
|
||||
apiMethod(formData).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success(formData.id ? '更新成功' : '添加成功')
|
||||
this.$refs.editModal.hide()
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} else {
|
||||
this.$Message.error(res.message || (formData.id ? '更新失败' : '添加失败'))
|
||||
}
|
||||
}).catch(err => {
|
||||
this.$Message.error((formData.id ? '更新失败' : '添加失败') + ':' + (err.message || '未知错误'))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content-view {
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user