This commit is contained in:
张成
2025-11-24 13:23:42 +08:00
commit 5d7444cd65
156 changed files with 50653 additions and 0 deletions

View 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>

View 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>

View 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>