提交 65745ccc authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 274b4694
{ {
"recommendations": ["johnsoncodehk.volar", "johnsoncodehk.vscode-typescript-vue-plugin"] "recommendations": ["vue.volar", "vue.vscode-typescript-vue-plugin"]
} }
<script setup lang="ts">
import { useArea } from '@/composables/useArea'
const { treeList } = useArea()
const props = defineProps<{ modelValue: string | null }>()
const emit = defineEmits(['update:modelValue', 'change'])
const dataValue = ref<string[]>([])
watch(
() => props.modelValue,
value => {
dataValue.value = value?.split('-') || []
},
{ immediate: true }
)
function onChange(value: string[] | string) {
if (Array.isArray(value)) {
emit('update:modelValue', value.join('-'))
emit('change', value.join('-'))
} else {
emit('update:modelValue', value)
emit('change', value)
}
}
</script>
<template>
<el-cascader v-model="dataValue" :options="treeList" separator="-" clearable @change="onChange" v-bind="$attrs" />
</template>
<script setup lang="ts"> <script setup lang="ts">
import AppLayout from './layout/Index.vue' import AppLayout from './layout/Index.vue'
// const menus = [ const menus = [
// { title: '发布职位', path: '/hr/posts/job' }, { title: '发布职位', path: '/hr/posts/job' },
// { title: '校友人才', path: '/hr/alumni' } { title: '校友人才', path: '/hr/alumni' }
// ] ]
</script> </script>
<template> <template>
<AppLayout> <AppLayout>
<AppContainer background="white"></AppContainer> <AppContainer :menus="menus" background="white"></AppContainer>
</AppLayout> </AppLayout>
</template> </template>
...@@ -119,7 +119,7 @@ onMounted(() => { ...@@ -119,7 +119,7 @@ onMounted(() => {
fetchList() fetchList()
}) })
defineExpose({ refetch, tableRef }) defineExpose({ search, refetch, tableRef })
</script> </script>
<template> <template>
......
...@@ -165,6 +165,8 @@ const rows: Record<string, any>[] = [ ...@@ -165,6 +165,8 @@ const rows: Record<string, any>[] = [
} }
if (user.projects.length === 0) { if (user.projects.length === 0) {
ElMessage({ message: '请联系我们完成企业注册' }) ElMessage({ message: '请联系我们完成企业注册' })
} else if (user.hasCompany) {
window.open('/hr/company/view')
} else { } else {
window.open('/hr/company/create') window.open('/hr/company/create')
} }
......
...@@ -29,6 +29,11 @@ export interface AlumniType { ...@@ -29,6 +29,11 @@ export interface AlumniType {
workplace: string workplace: string
department: string department: string
position: string position: string
company?: {
id: string
name: string
desc: string
}
} }
export interface ClassType { export interface ClassType {
......
...@@ -87,9 +87,9 @@ const listOptions = computed(() => ({ ...@@ -87,9 +87,9 @@ const listOptions = computed(() => ({
] ]
})) }))
function handleUpdateList() { function handleSearch() {
nextTick(() => { nextTick(() => {
appList.value?.refetch() appList.value?.search()
}) })
} }
...@@ -106,7 +106,7 @@ onMounted(() => { ...@@ -106,7 +106,7 @@ onMounted(() => {
<router-link :to="`/hr/alumni/view/${row.id}`" target="_blank" style="color: #399ee8">查看</router-link> <router-link :to="`/hr/alumni/view/${row.id}`" target="_blank" style="color: #399ee8">查看</router-link>
</template> </template>
<template #filter-province> <template #filter-province>
<el-select filterable clearable v-model="provinceValue" @change="handleUpdateList" style="width: 250px"> <el-select filterable clearable v-model="provinceValue" @change="handleSearch" style="width: 250px">
<el-option v-for="item in provinceList" :value="item.label" :key="item.code"></el-option> <el-option v-for="item in provinceList" :value="item.label" :key="item.code"></el-option>
</el-select> </el-select>
</template> </template>
...@@ -115,7 +115,7 @@ onMounted(() => { ...@@ -115,7 +115,7 @@ onMounted(() => {
filterable filterable
clearable clearable
v-model="cityValue" v-model="cityValue"
@change="handleUpdateList" @change="handleSearch"
no-data-text="请先选择省份" no-data-text="请先选择省份"
style="width: 250px" style="width: 250px"
> >
...@@ -133,5 +133,10 @@ onMounted(() => { ...@@ -133,5 +133,10 @@ onMounted(() => {
.el-form-item__label { .el-form-item__label {
width: 80px; width: 80px;
} }
.el-input,
.el-select {
width: 210px !important;
}
} }
</style> </style>
...@@ -106,11 +106,14 @@ onMounted(() => { ...@@ -106,11 +106,14 @@ onMounted(() => {
</AppCard> </AppCard>
<AppCard class="company-info"> <AppCard class="company-info">
<h1>企业简介<span>Company Profile</span></h1> <h1>企业简介<span>Company Profile</span></h1>
<p class="tips">暂未注册企业</p> <template v-if="data.company && data.company.id">
<article class="article" v-html="data.company.desc"></article>
</template>
<p class="tips" v-else>暂未注册企业</p>
</AppCard> </AppCard>
</template> </template>
<style lang="scss"> <style lang="scss" scoped>
.alumni-info { .alumni-info {
padding: 24px; padding: 24px;
display: grid; display: grid;
...@@ -176,4 +179,14 @@ onMounted(() => { ...@@ -176,4 +179,14 @@ onMounted(() => {
text-align: center; text-align: center;
} }
} }
.article {
padding: 20px 0;
font-size: 15px;
line-height: 1.8;
color: #666;
::v-deep(img) {
margin: 20px 0;
max-width: 100%;
}
}
</style> </style>
...@@ -12,9 +12,8 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -12,9 +12,8 @@ export const routes: Array<RouteRecordRaw> = [
component: () => import('./views/Update.vue') component: () => import('./views/Update.vue')
}, },
{ {
path: 'view/:id', path: 'view',
component: () => import('./views/View.vue'), component: () => import('./views/View.vue')
props: true
} }
] ]
} }
......
<script setup lang="ts"></script> <script setup lang="ts">
import { getMyCompany, getCompany } from '../api'
import type { CompanyType } from '../types'
const route = useRoute()
const data = reactive<CompanyType>({
id: '',
logo: '',
name: '',
desc: '',
email: '',
nature: '',
code: '',
business_licence: '',
province: '',
city: '',
industry: ''
})
// 获取我的公司信息
function fetchMyCompany() {
getMyCompany().then(res => {
const { detail } = res.data
Object.assign(data, detail)
})
}
// 获取公司信息
function fetchCompany() {
getCompany({ id: route.query.id as string }).then(res => {
const { detail } = res.data
Object.assign(data, detail)
})
}
onMounted(() => {
route.query.id ? fetchCompany() : fetchMyCompany()
})
</script>
<template> <template>
<AppCard class="company-info"> <AppCard class="company-info">
<h1>企业简介<span>Company Profile</span></h1> <h1>企业简介<span>Company Profile</span></h1>
<p class="tips">暂未注册企业</p> <article class="article" v-html="data.desc"></article>
</AppCard> </AppCard>
</template> </template>
<style lang="scss"> <style lang="scss" scoped>
.company-info { .company-info {
padding: 44px; padding: 44px;
margin-top: 20px; margin-top: 20px;
...@@ -27,14 +63,15 @@ ...@@ -27,14 +63,15 @@
color: #666666; color: #666666;
} }
} }
.tips { }
padding: 100px 0; .article {
font-size: 26px; padding: 20px 0;
font-family: Lato; font-size: 15px;
font-weight: 400; line-height: 1.8;
line-height: 32px; color: #666;
color: #666666; ::v-deep(img) {
text-align: center; margin: 20px 0;
max-width: 100%;
} }
} }
</style> </style>
import httpRequest from '@/utils/axios' import httpRequest from '@/utils/axios'
// 获取校友列表 // 获取岗位列表
export function getAlumniList(params: { export function getJobList(params?: {
project_prefix_arr: [] page?: number
workplace: string limit?: number
industry: string name?: string
province: string type?: number
city: string education?: number
work_locations?: string
company_id?: string
}) { }) {
return httpRequest.get('/api/hr/api/v1/alumni-talents', { params }) return httpRequest.get('/api/hr/api/v1/positions', { params })
} }
// 获取校友详情 // 获取岗位详情
export function getAlumniDetail(params: { id: string }) { export function getJob(params: { id: string }) {
return httpRequest.get(`/api/hr/api/v1/alumni-talent/${params.id}`, { params }) return httpRequest.get(`/api/hr/api/v1/position/${params.id}/detail`, { params })
} }
// 获取当前登录的校友信息 // 投递简历
export function getAlumniUserInfo() { export function submitResume(data: { id: string; resume: string }) {
return httpRequest.get('/api/hr/api/v1/alumni-talent/login-user-info') return httpRequest.post(`/api/hr/api/v1/position/${data.id}/deliver`, data)
} }
export interface JobType {
id?: string
name: string
education?: 1 | 2 | 3 | 4 | 5
type?: 1 | 2 | 3 | 4 | 5
work_locations: string
salary_min?: number
salary_max?: number
desc: string
status?: 1 | 2
audit_status?: 1 | 2 | 3
company?: {
id: string
name: string
logo: string
status: 1 | 2
audit_status: 1 | 2 | 3
}
}
<script setup lang="ts"> <script setup lang="ts">
import AppList from '@/components/base/AppList.vue' import AppList from '@/components/base/AppList.vue'
import { jobTypeList, educationList } from '@/utils/dictionary' import AppArea from '@/components/AppArea.vue'
import { jobType, jobTypeList, education, educationList } from '@/utils/dictionary'
import { getJobList } from '../api'
import type { JobType } from '../types'
const appList = $ref<InstanceType<typeof AppList> | null>(null) const appList = $ref<InstanceType<typeof AppList> | null>(null)
const listOptions = { const listOptions = {
remote: {}, remote: {
httpRequest: getJobList,
params: {
name: '',
education: undefined,
type: undefined,
work_locations: ''
}
},
filters: [ filters: [
{ type: 'input', label: '企业名称', prop: 'name', placeholder: '请输入' }, { type: 'input', label: '企业名称', prop: 'name', placeholder: '请输入' },
{ type: 'select', label: '学历要求', prop: 'education', options: educationList }, { type: 'select', label: '学历要求', prop: 'education', options: educationList },
{ type: 'select', label: '工作地点', prop: 'work_locations' }, { type: 'select', label: '工作地点', prop: 'work_locations', slots: 'filter-area' },
{ type: 'select', label: '岗位类型', prop: 'type', options: jobTypeList }, { type: 'select', label: '岗位类型', prop: 'type', options: jobTypeList },
{ type: 'input', label: '岗位名称', prop: 'name', placeholder: '请输入' } { type: 'input', label: '岗位名称', prop: 'name', placeholder: '请输入' }
], ],
columns: [ columns: []
{ label: '编号', prop: 'id' }, }
{ label: '企业名称', prop: 'name' }, function handleSearch() {
{ label: '岗位类型', prop: 'type' }, appList?.search()
{ label: '地点', prop: 'work_locations' }, }
{ label: '学历要求', prop: 'education' }, function getEducationName(educationValue: JobType['education']) {
{ label: '薪酬范围', prop: 'education' }, return (educationValue && education[educationValue]) || educationValue
{ label: '投递人数', prop: 'education' }, }
{ label: '状态', prop: 'status' },
{ label: '操作', slots: 'table-actions' } function getTypeName(typeValue: JobType['type']) {
], return (typeValue && jobType[typeValue]) || typeValue
data: [
{ id: '1', name: '企业名称', education: 1, type: 1, salary_min: 10000, salary_max: 20000, desc: '描述' },
{ id: '2', name: '企业名称', education: 1, type: 1, salary_min: 10000, salary_max: 20000, desc: '描述' },
{ id: '3', name: '企业名称', education: 1, type: 1, salary_min: 10000, salary_max: 20000, desc: '描述' },
{ id: '4', name: '企业名称', education: 1, type: 1, salary_min: 10000, salary_max: 20000, desc: '描述' },
{ id: '5', name: '企业名称', education: 1, type: 1, salary_min: 10000, salary_max: 20000, desc: '描述' }
]
} }
</script> </script>
<template> <template>
<AppList v-bind="listOptions" ref="appList"> <AppList v-bind="listOptions" ref="appList">
<template #body="{ data }"> <template #filter-area="{ params }">
<div class="job-list"> <AppArea v-model="params.work_locations" @change="handleSearch"></AppArea>
</template>
<template #body="{ data }: { data: JobType[] }">
<div class="job-list" v-if="data.length">
<div class="job-item" v-for="item in data" :key="item.id"> <div class="job-item" v-for="item in data" :key="item.id">
<router-link :to="`/hr/job/view/${item.id}`" target="_blank" class="job-item__inner"> <router-link :to="`/hr/job/view/${item.id}`" target="_blank" class="job-item__inner">
<div class="job-item-hd"> <div class="job-item-hd">
...@@ -44,18 +52,21 @@ const listOptions = { ...@@ -44,18 +52,21 @@ const listOptions = {
<p>{{ item.salary_min }}-{{ item.salary_max }}</p> <p>{{ item.salary_min }}-{{ item.salary_max }}</p>
</div> </div>
<div class="job-item-bd"> <div class="job-item-bd">
<p>{{ item.province }}-{{ item.city }}</p> <p>{{ item.work_locations }}</p>
<p>{{ item.education }}</p> <p>{{ getEducationName(item.education) }}</p>
</div> </div>
<div class="job-item-ft"> <div class="job-item-ft">
<p> <p v-if="item.company">
<router-link :to="`/hr/company/view/${item.id}`" target="_blank">{{ item.name }}</router-link> <router-link :to="`/hr/company/view?id=${item.company.id}`" target="_blank">
{{ item.company.name }}
</router-link>
</p> </p>
<p>{{ item.type }}</p> <p>{{ getTypeName(item.type) }}</p>
</div> </div>
</router-link> </router-link>
</div> </div>
</div> </div>
<el-empty description="暂无岗位" v-else />
</template> </template>
<template #table-actions="{ row }"> <template #table-actions="{ row }">
<router-link :to="`/hr/posts/job/view/${row.id}`" target="_blank"> <router-link :to="`/hr/posts/job/view/${row.id}`" target="_blank">
......
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage } from 'element-plus'
import { getJob, submitResume } from '../api'
import { jobType, education } from '@/utils/dictionary'
import type { JobType } from '../types'
const props = defineProps<{ id: string }>()
const data = reactive<JobType>({
name: '',
education: undefined,
type: undefined,
work_locations: '',
salary_min: undefined,
salary_max: undefined,
desc: ''
})
const jobTypeName = computed(() => {
return data.type && jobType[data.type]
})
const educationName = computed(() => {
return data.education && education[data.education]
})
const descHtml = computed(() => {
return data.desc.replace(/\n/g, '<br />')
})
let isUploaded = $ref(false) let isUploaded = $ref(false)
const uploadButtonText = computed(() => (isUploaded ? '已投递' : '申请该职位')) const uploadButtonText = computed(() => (isUploaded ? '已投递' : '申请该职位'))
function fetchData() {
if (!props.id) return
getJob({ id: props.id }).then(res => {
const { detail } = res.data
Object.assign(data, detail)
isUploaded = detail.is_delivered
})
}
onMounted(() => {
fetchData()
})
const uploadFileUrl = ref('') const uploadFileUrl = ref('')
function handleUploadSuccess(e: any) { function handleUploadSuccess(file: any) {
submitResume({ id: props.id, resume: file.raw.url })
.then(() => {
isUploaded = true isUploaded = true
console.log(e) ElMessage.success('投递成功')
})
.catch(res => {
ElMessage.error(res.message)
})
} }
const jobContent = ref(
`岗位职责
1、建立申请风险评分、欺诈评分、催收评分等系列预测模型;
2、分析模型效果,使用统计方法设计模型使用方案;
3、对各类场景下的金融产品进行客户分群、多因素模型敏感性分析;
4、对于指标波动等情况进行分析,出具分析报告,提出有效建议。
任职资格:
1、本科及以上学历,数学、应用数学、金融数学、统计学、自动化、人工智能相关专业优先。
2、1年左右建模经验(专业相关的应届毕业生也可)
3、了解风险建模相关工作内容及流程。
4、熟练使用Python、SQL、R、SAS等工具,了解各类模型分类与回归算法,有数据发掘经验。
5、工作责任心强,抗压能力强,有较好的学习能力及创新精神。`
)
const jobContentHtml = computed(() => {
return jobContent.value.replace(/\n/g, '<br />')
})
</script> </script>
<template> <template>
<AppCard> <AppCard>
<div class="job-info"> <div class="job-info">
<div class="job-info-hd"> <div class="job-info-hd">
<h4>Java开发工程师</h4> <h4>{{ data.name }}</h4>
<p>15000-25000</p> <p>{{ data.salary_min }}-{{ data.salary_max }}</p>
</div> </div>
<div class="job-info-bd"> <div class="job-info-bd">
<ul> <ul>
<li>河北-唐山</li> <li>{{ data.work_locations }}</li>
<li>本科</li> <li>{{ educationName }}</li>
</ul> </ul>
</div> </div>
<div class="job-info-ft"> <div class="job-info-ft">
...@@ -45,18 +71,23 @@ const jobContentHtml = computed(() => { ...@@ -45,18 +71,23 @@ const jobContentHtml = computed(() => {
v-model="uploadFileUrl" v-model="uploadFileUrl"
accept="application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document" accept="application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
@success="handleUploadSuccess" @success="handleUploadSuccess"
><el-button type="primary" :disabled="isUploaded" style="border-radius: 10px">
{{ uploadButtonText }}
</el-button></AppUpload
> >
<el-button type="primary" :disabled="isUploaded" style="border-radius: 10px">
{{ uploadButtonText }}
</el-button>
</AppUpload>
<p class="job-info-company"> <p class="job-info-company">
<span>专业技术类</span> <span>{{ jobTypeName }}</span>
<span><router-link to="/hr/company/view/123" target="_blank">河北唐山技术开发有限公司</router-link></span> <span v-if="data.company">
<router-link :to="`/hr/company/view?id=${data.company.id}`" target="_blank">
{{ data.company.name }}
</router-link>
</span>
</p> </p>
</div> </div>
</div> </div>
<h2 class="job-desc-title">职位描述</h2> <h2 class="job-desc-title">职位描述</h2>
<div class="job-desc" v-html="jobContentHtml"></div> <div class="job-desc" v-html="descHtml"></div>
</AppCard> </AppCard>
</template> </template>
......
import httpRequest from '@/utils/axios' import httpRequest from '@/utils/axios'
import type { JobType } from './types' import type { JobType } from './types'
// 获取我的岗位列表
export function getMyJobList(params?: {
page?: number
limit?: number
name?: string
type?: number
education?: number
work_locations?: string
status?: 1 | 2
audit_status?: 1 | 2 | 3
}) {
return httpRequest.get('/api/hr/api/v1/position/my-list', { params })
}
// 创建岗位 // 创建岗位
export function createCompany(data: JobType) { export function createJob(data: JobType) {
return httpRequest.post('/api/psp/backend/banner/create', data) return httpRequest.post('/api/hr/api/v1/position/publish', data)
} }
// 更新岗位 // 更新岗位
export function updateCompany(data: JobType) { export function updateJob(data: JobType) {
return httpRequest.post('/api/psp/backend/banner/update', data) return httpRequest.put(`/api/hr/api/v1/position/${data.id}/update`, data)
} }
// 获取岗位详情 // 获取岗位详情
export function getCompany(params: { id: string }) { export function getJob(params: { id: string }) {
return httpRequest.get('/api/psp/backend/banner/view', { params }) return httpRequest.get(`/api/hr/api/v1/position/${params.id}/detail`, { params })
}
// 获取岗位简历列表
export function getJobResumeList(params: { id: string }) {
return httpRequest.get(`/api/hr/api/v1/position/${params.id}/delever-records`, { params })
}
// 启用/禁用岗位
export function enableJob(data: { id: string; status: 1 | 2 }) {
return httpRequest.post(`/api/hr/api/v1/position/${data.id}/enable`, data)
} }
...@@ -9,7 +9,7 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -9,7 +9,7 @@ export const routes: Array<RouteRecordRaw> = [
{ path: '', redirect: '/hr/posts/job' }, { path: '', redirect: '/hr/posts/job' },
{ path: 'job', component: () => import('./views/Job.vue') }, { path: 'job', component: () => import('./views/Job.vue') },
{ path: 'job/create', component: () => import('./views/JobUpdate.vue') }, { path: 'job/create', component: () => import('./views/JobUpdate.vue') },
{ path: 'job/update/:id', component: () => import('./views/JobUpdate.vue') }, { path: 'job/update/:id', component: () => import('./views/JobUpdate.vue'), props: true },
{ {
path: 'job/view/:id', path: 'job/view/:id',
component: () => import('./views/JobView.vue'), component: () => import('./views/JobView.vue'),
......
export interface JobType { export interface JobType {
id?: string id?: string
name: string name: string
education: number | undefined education?: 1 | 2 | 3 | 4 | 5
type: number | undefined type?: 1 | 2 | 3 | 4 | 5
work_locations: string work_locations: string
salary_min: number | undefined salary_min?: number
salary_max: number | undefined salary_max?: number
desc: string desc: string
status?: 1 | 2
audit_status?: 1 | 2 | 3
company?: {
id: string
name: string
logo: string
status: 1 | 2
audit_status: 1 | 2 | 3
}
project_prefix_group?: string
} }
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus'
import AppList from '@/components/base/AppList.vue' import AppList from '@/components/base/AppList.vue'
import { jobTypeList, educationList } from '@/utils/dictionary' import AppArea from '@/components/AppArea.vue'
import { jobType, jobTypeList, education, educationList, auditStatus } from '@/utils/dictionary'
import { getMyJobList, enableJob } from '../api'
import type { JobType } from '../types'
const appList = $ref<InstanceType<typeof AppList> | null>(null) const appList = $ref<InstanceType<typeof AppList> | null>(null)
const listOptions = { const listOptions = {
remote: {}, remote: {
httpRequest: getMyJobList,
params: {
name: '',
type: undefined,
education: undefined,
work_locations: '',
status: undefined,
audit_status: undefined
}
},
filters: [ filters: [
{ type: 'input', label: '岗位名称', prop: 'name', placeholder: '请输入' }, { type: 'input', label: '岗位名称', prop: 'name', placeholder: '请输入' },
{ type: 'select', label: '岗位类型', prop: 'type', options: jobTypeList }, { type: 'select', label: '岗位类型', prop: 'type', options: jobTypeList },
{ type: 'select', label: '学历要求', prop: 'education', options: educationList }, { type: 'select', label: '学历要求', prop: 'education', options: educationList },
{ type: 'select', label: '工作地点', prop: 'work_locations' } { type: 'select', label: '工作地点', prop: 'work_locations', slots: 'filter-area' }
], ],
columns: [ columns: [
{ label: '编号', prop: 'id' },
{ label: '岗位名称', prop: 'name' }, { label: '岗位名称', prop: 'name' },
{ label: '岗位类型', prop: 'type' }, {
{ label: '地点', prop: 'work_locations' }, label: '岗位类型',
{ label: '学历要求', prop: 'education' }, prop: 'type',
{ label: '薪酬范围', prop: 'education' }, align: 'center',
{ label: '投递人数', prop: 'education' }, computed({ row }: { row: JobType }) {
{ label: '状态', prop: 'status' }, return row.type && jobType[row.type]
{ label: '操作', slots: 'table-actions' } }
], },
data: [ { label: '地点', prop: 'work_locations', align: 'center' },
{ id: '1', name: '岗位名称', education: 1, type: 1, salary_min: 10000, salary_max: 20000, desc: '描述' }, {
{ id: '2', name: '岗位名称', education: 1, type: 1, salary_min: 10000, salary_max: 20000, desc: '描述' } label: '学历要求',
prop: 'education',
align: 'center',
computed({ row }: { row: JobType }) {
return row.education && education[row.education]
}
},
{
label: '薪酬范围',
prop: 'salary_max',
align: 'center',
computed({ row }: { row: JobType }) {
return `${row.salary_min}-${row.salary_max}`
}
},
{ label: '投递人数', prop: 'deliver_count', align: 'center' },
{ label: '启用状态', prop: 'status', align: 'center', slots: 'table-status' },
{
label: '审核状态',
prop: 'audit_status',
align: 'center',
computed({ row }: { row: JobType }) {
return row.audit_status && auditStatus[row.audit_status]
}
},
{ label: '操作', align: 'center', slots: 'table-actions' }
] ]
} }
function handleSearch() {
appList?.search()
}
// 启用/禁用
function onEnable(row: JobType) {
if (!row.id || !row.status) return
const status = row.status === 1 ? 2 : 1
const statusText = status === 1 ? '启用' : '禁用'
ElMessageBox.confirm(`确定要${statusText}该岗位吗?`).then(() => {
enableJob({ status, id: row.id as string })
.then(() => {
ElMessage.success(`${statusText}成功`)
appList?.refetch()
})
.catch(res => {
ElMessage.error(res.message)
})
})
}
</script> </script>
<template> <template>
...@@ -36,15 +94,21 @@ const listOptions = { ...@@ -36,15 +94,21 @@ const listOptions = {
<el-button type="primary">发布岗位</el-button> <el-button type="primary">发布岗位</el-button>
</router-link> </router-link>
</div> </div>
<template #filter-area="{ params }">
<AppArea v-model="params.work_locations" @change="handleSearch"></AppArea>
</template>
<template #table-status="{ row }">
<el-switch :value="row.status" :active-value="1" :inactive-value="2" @change="onEnable(row)" />
</template>
<template #table-actions="{ row }"> <template #table-actions="{ row }">
<router-link :to="`/hr/posts/job/view/${row.id}`" target="_blank"> <router-link :to="`/hr/posts/job/view/${row.id}`" target="_blank" style="color: #399ee8">查看</router-link>
<el-button link>查看</el-button> <router-link
</router-link> :to="`/hr/posts/job/update/${row.id}`"
<el-button link>启用</el-button> style="margin-left: 10px; color: #399ee8"
<el-button link>禁用</el-button> v-if="row.audit_status !== 3"
<router-link :to="`/hr/posts/job/update/${row.id}`"> >编辑</router-link
<el-button link>编辑</el-button> >
</router-link>
</template> </template>
</AppList> </AppList>
</template> </template>
...@@ -54,5 +118,13 @@ const listOptions = { ...@@ -54,5 +118,13 @@ const listOptions = {
padding: 30px 30px 10px; padding: 30px 30px 10px;
background: #f8f8f8; background: #f8f8f8;
border-radius: 20px; border-radius: 20px;
.el-form-item__label {
width: 80px;
}
.el-input,
.el-select {
width: 210px !important;
}
} }
</style> </style>
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { createCompany, updateCompany, getCompany } from '../api' import AppArea from '@/components/AppArea.vue'
import { createJob, updateJob, getJob } from '../api'
import type { JobType } from '../types' import type { JobType } from '../types'
import { jobTypeList, educationList } from '@/utils/dictionary' import { jobTypeList, educationList } from '@/utils/dictionary'
...@@ -41,7 +42,7 @@ const onCancel = () => { ...@@ -41,7 +42,7 @@ const onCancel = () => {
} }
// 创建 // 创建
const create = () => { const create = () => {
createCompany(form).then(() => { createJob(form).then(() => {
ElMessage({ message: '创建成功', type: 'success' }) ElMessage({ message: '创建成功', type: 'success' })
router.push('/hr/posts/job') router.push('/hr/posts/job')
}) })
...@@ -49,7 +50,7 @@ const create = () => { ...@@ -49,7 +50,7 @@ const create = () => {
// 修改 // 修改
const update = () => { const update = () => {
const params = { ...form, id: props.id as string } const params = { ...form, id: props.id as string }
updateCompany(params).then(() => { updateJob(params).then(() => {
ElMessage({ message: '修改成功', type: 'success' }) ElMessage({ message: '修改成功', type: 'success' })
router.push('/hr/posts/job') router.push('/hr/posts/job')
}) })
...@@ -57,8 +58,8 @@ const update = () => { ...@@ -57,8 +58,8 @@ const update = () => {
onMounted(() => { onMounted(() => {
props.id && props.id &&
getCompany({ id: props.id }).then(res => { getJob({ id: props.id }).then(res => {
Object.assign(form, res.data) Object.assign(form, res.data.detail)
}) })
}) })
</script> </script>
...@@ -87,7 +88,7 @@ onMounted(() => { ...@@ -87,7 +88,7 @@ onMounted(() => {
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="工作地点" prop="work_locations"> <el-form-item label="工作地点" prop="work_locations">
<el-input v-model="form.work_locations" placeholder="请输入" /> <AppArea v-model="form.work_locations" style="width: 100%"></AppArea>
</el-form-item> </el-form-item>
<el-form-item label="薪酬范围" prop="salary_min"> <el-form-item label="薪酬范围" prop="salary_min">
<el-row> <el-row>
...@@ -104,8 +105,8 @@ onMounted(() => { ...@@ -104,8 +105,8 @@ onMounted(() => {
<el-input type="textarea" v-model="form.desc" :autosize="{ minRows: 12, maxRows: 20 }" maxlength="200" /> <el-input type="textarea" v-model="form.desc" :autosize="{ minRows: 12, maxRows: 20 }" maxlength="200" />
</el-form-item> </el-form-item>
<el-row justify="center" style="margin: 40px 0 20px"> <el-row justify="center" style="margin: 40px 0 20px">
<el-button type="primary" auto-insert-space @click="onSubmit">发布</el-button> <el-button type="primary" auto-insert-space @click="onSubmit" style="border-radius: 10px">点击发布</el-button>
<el-button auto-insert-space @click="onCancel">取消</el-button> <el-button auto-insert-space @click="onCancel" v-if="false">取消</el-button>
</el-row> </el-row>
</el-form> </el-form>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getJob, getJobResumeList } from '../api'
import { jobType, education, projectPrefix } from '@/utils/dictionary'
import type { JobType } from '../types'
const props = defineProps<{ id?: string }>()
const data = reactive<JobType>({
name: '',
education: undefined,
type: undefined,
work_locations: '',
salary_min: undefined,
salary_max: undefined,
desc: ''
})
const jobTypeName = computed(() => {
return data.type && jobType[data.type]
})
const educationName = computed(() => {
return data.education && education[data.education]
})
const descHtml = computed(() => {
return data.desc.replace(/\n/g, '<br />')
})
function fetchData() {
if (!props.id) return
getJob({ id: props.id }).then(res => {
Object.assign(data, res.data.detail)
})
}
onMounted(() => {
fetchData()
})
const listOptions = { const listOptions = {
remote: {}, hasPagination: false,
remote: {
httpRequest: getJobResumeList,
params: { id: props.id },
callback(data: any) {
return { data: data.items }
}
},
columns: [ columns: [
{ label: '编号', prop: 'name' }, { label: '编号', align: 'center', slots: 'table-index' },
{ label: '姓名', prop: 'name' }, { label: '姓名', prop: 'username', align: 'center' },
{ label: '已投递简历数', prop: 'type' }, { label: '已投递简历数', prop: 'deliver_counts', align: 'center' },
{ label: '所在项目', prop: 'work_locations' }, {
{ label: '操作', slots: 'table-actions' } label: '所在项目',
prop: 'project_prefix_group',
align: 'center',
computed({ row }: { row: JobType }) {
return row.project_prefix_group
?.split(',')
.map(item => {
return projectPrefix[item] || item
})
.join(',')
}
},
{ label: '操作', slots: 'table-actions', align: 'center' }
] ]
} }
// 编号
function indexName(index: number): string {
return (index + 1).toString().padStart(3, '0')
}
</script> </script>
<template> <template>
...@@ -17,43 +75,39 @@ const listOptions = { ...@@ -17,43 +75,39 @@ const listOptions = {
<div class="job-info"> <div class="job-info">
<dl> <dl>
<dt>岗位名称</dt> <dt>岗位名称</dt>
<dd>北京奔驰技术工人</dd> <dd>{{ data.name }}</dd>
</dl> </dl>
<dl> <dl>
<dt>岗位类型</dt> <dt>岗位类型</dt>
<dd>技术工人</dd> <dd>{{ jobTypeName }}</dd>
</dl> </dl>
<dl> <dl>
<dt>岗位地点</dt> <dt>岗位地点</dt>
<dd>北京</dd> <dd>{{ data.work_locations }}</dd>
</dl> </dl>
<dl> <dl>
<dt>学历要求</dt> <dt>学历要求</dt>
<dd>大专</dd> <dd>{{ educationName }}</dd>
</dl> </dl>
<dl> <dl>
<dt>公司名称</dt> <dt>公司名称</dt>
<dd>北京奔驰技术工人某某公司</dd> <dd>{{ data.company?.name }}</dd>
</dl> </dl>
<dl> <dl>
<dt>薪资范围</dt> <dt>薪资范围</dt>
<dd>10000-13000</dd> <dd>{{ data.salary_min }}-{{ data.salary_max }}</dd>
</dl> </dl>
<dl style="grid-column: 3; grid-row: 1/5"> <dl style="grid-column: 3; grid-row: 1/5">
<dt>岗位介绍</dt> <dt>岗位介绍</dt>
<dd> <dd v-html="descHtml"></dd>
中加本硕直通车项目是针对大专及本科生提升学历、移居海外的定制通道。
采用国内2.5+1.5(专升本)/3.5+2(本硕连读)年的合作模式即可分别获得加拿大卡普顿大学的本科与硕士学位,符合条件的国内院校的大专及本科生分别在国内完成2.5年及3.5年的专科和本科的学习,可直接进入加拿大卡普顿大学进行本科和硕士阶段的学习。本项目对接经教育部认证的国外名校,旨在帮助学生缩短留学周期,让学生顺利完成国外规定的学业课程,获得国外学位及移民海外的需求。
中加本硕直通车项目是针对大专及本科生提升学历、移居海外的定制通道。
采用国内2.5+1.5(专升本)/3.5+2(本硕连读)年的合作模式即可分别获得加拿大卡普顿大学的本科与硕士学位,符合条件的国内院校的大专及本科生分别在国内完成2.5年及3.5年的专科和本科的学习,可直接进入加拿大卡普顿大学进行本科和硕士阶段的学习。本项目对接经教育部认证的国外名校,旨在帮助学生缩短留学周期,让学生顺利完成国外规定的学业课程,获得国外学位及移民海外的需求。
中加本硕直通车项目是针对大专及本科生提升学历、移居海外的定制通道。
采用国内2.5+1.5(专升本)/3.5+2(本硕连读)年的合作模式即可分别获得加拿大卡普顿大学的本科与硕士学位,符合条件的国内院校的大专及本科生分别在国内完成2.5年及3.5年的专科和本科的学习,可直接进入加拿大卡普顿大学进行本科和硕士阶段的学习。本项目对接经教育部认证的国外名校,旨在帮助学生缩短留学周期,让学生顺利完成国外规定的学业课程,获得国外学位及移民海外的需求。
</dd>
</dl> </dl>
</div> </div>
<h2 class="subtitle" style="padding-bottom: 0">简历</h2> <h2 class="subtitle" style="padding-bottom: 0">投递详情</h2>
<AppList v-bind="listOptions"> <AppList v-bind="listOptions">
<template #table-actions> 查看 </template> <template #table-index="{ $index }"> {{ indexName($index) }} </template>
<template #table-actions="{ row }">
<a :href="row.resume" target="_blank" style="color: #399ee8">查看</a>
</template>
</AppList> </AppList>
</div> </div>
</template> </template>
...@@ -74,6 +128,7 @@ const listOptions = { ...@@ -74,6 +128,7 @@ const listOptions = {
dd { dd {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
line-height: 34px;
color: #333333; color: #333333;
} }
} }
......
...@@ -26,7 +26,7 @@ export function getProject(params: { id: string }) { ...@@ -26,7 +26,7 @@ export function getProject(params: { id: string }) {
return httpRequest.get(`/api/hr/api/v1/project/${params.id}/detail`, { params }) return httpRequest.get(`/api/hr/api/v1/project/${params.id}/detail`, { params })
} }
// 项目审核 // 项目启用和禁用
export function auditProject(data: { id: string; status: 1 | 2 }) { export function enableProject(data: { id: string; status: 1 | 2 }) {
return httpRequest.post(`/api/hr/api/v1/project/${data.id}/audit`, data) return httpRequest.post(`/api/hr/api/v1/project/${data.id}/audit`, data)
} }
<script setup lang="ts"> <script setup lang="ts">
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import type { ProjectType } from '../types' import type { ProjectType } from '../types'
import ProjectListItem from './ProjectListItem.vue' import ProjectListItem from './ProjectListItem.vue'
import { getMyProjectList, auditProject } from '../api' import { getMyProjectList, enableProject } from '../api'
import { useUserStore } from '@/stores/user'
const user = useUserStore()
const router = useRouter()
const dataset = reactive<{ total: number; list: ProjectType[] }>({ total: 0, list: [] }) const dataset = reactive<{ total: number; list: ProjectType[] }>({ total: 0, list: [] })
const params = reactive({ page: 1, limit: 3 }) const params = reactive({ page: 1, limit: 3 })
...@@ -33,10 +38,10 @@ onMounted(() => { ...@@ -33,10 +38,10 @@ onMounted(() => {
}) })
// 启用/禁用 // 启用/禁用
function onAudit(status: 1 | 2, data: ProjectType) { function onEnable(status: 1 | 2, data: ProjectType) {
const statusText = status === 1 ? '启用' : '禁用' const statusText = status === 1 ? '启用' : '禁用'
ElMessageBox.confirm(`确定要${statusText}该项目吗?`).then(() => { ElMessageBox.confirm(`确定要${statusText}该项目吗?`).then(() => {
auditProject({ status, id: data.id as string }) enableProject({ status, id: data.id as string })
.then(() => { .then(() => {
ElMessage.success(`${statusText}成功`) ElMessage.success(`${statusText}成功`)
fetchList() fetchList()
...@@ -46,6 +51,14 @@ function onAudit(status: 1 | 2, data: ProjectType) { ...@@ -46,6 +51,14 @@ function onAudit(status: 1 | 2, data: ProjectType) {
}) })
}) })
} }
function onAdd() {
if (!user.hasCompany) {
ElMessage({ message: '请先注册您的企业' })
} else {
router.push('/project/create')
}
}
</script> </script>
<template> <template>
...@@ -61,10 +74,14 @@ function onAudit(status: 1 | 2, data: ProjectType) { ...@@ -61,10 +74,14 @@ function onAudit(status: 1 | 2, data: ProjectType) {
</div> </div>
<div class="project-list" v-loading="loading"> <div class="project-list" v-loading="loading">
<ProjectListItem v-for="item in dataset.list" :key="item.id" :data="item" isMine @audit="onAudit"></ProjectListItem> <ProjectListItem
<div class="project-item"> v-for="item in dataset.list"
<router-link to="/project/create" class="project-item__add"><Plus />发布新项目</router-link> :key="item.id"
</div> :data="item"
isMine
@enable="onEnable"
></ProjectListItem>
<div class="project-item project-item__add" @click="onAdd"><Plus />发布新项目</div>
</div> </div>
</template> </template>
...@@ -81,8 +98,6 @@ function onAudit(status: 1 | 2, data: ProjectType) { ...@@ -81,8 +98,6 @@ function onAudit(status: 1 | 2, data: ProjectType) {
row-gap: 20px; row-gap: 20px;
} }
.project-item__add { .project-item__add {
width: 100%;
height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
......
...@@ -3,7 +3,7 @@ import type { ProjectType } from '../types' ...@@ -3,7 +3,7 @@ import type { ProjectType } from '../types'
const props = defineProps<{ data: ProjectType; isMine?: boolean }>() const props = defineProps<{ data: ProjectType; isMine?: boolean }>()
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'audit', status: 1 | 2, data: ProjectType): void (e: 'enable', status: 1 | 2, data: ProjectType): void
}>() }>()
const descHtml = computed(() => { const descHtml = computed(() => {
...@@ -28,8 +28,8 @@ const descHtml = computed(() => { ...@@ -28,8 +28,8 @@ const descHtml = computed(() => {
</div> </div>
</router-link> </router-link>
<ul class="project-actions" v-if="isMine && data.audit_status === 1"> <ul class="project-actions" v-if="isMine && data.audit_status === 1">
<li style="color: #60b0ea" v-if="data.status === 2" @click="emit('audit', 1, data)">启用</li> <li style="color: #60b0ea" v-if="data.status === 2" @click="emit('enable', 1, data)">启用</li>
<li style="color: #f84e46" v-if="data.status === 1" @click="emit('audit', 2, data)">禁用</li> <li style="color: #f84e46" v-if="data.status === 1" @click="emit('enable', 2, data)">禁用</li>
</ul> </ul>
</div> </div>
</template> </template>
......
<script setup lang="ts"> <script setup lang="ts">
import MyProjectList from '../components/MyProjectList.vue' import MyProjectList from '../components/MyProjectList.vue'
import ProjectList from '../components/ProjectList.vue' import ProjectList from '../components/ProjectList.vue'
import { useUserStore } from '@/stores/user'
const user = useUserStore()
</script> </script>
<template> <template>
<AppContainer background="#fff"> <AppContainer background="#fff">
<MyProjectList></MyProjectList> <MyProjectList v-if="user.hasCompany"></MyProjectList>
<ProjectList></ProjectList> <ProjectList></ProjectList>
</AppContainer> </AppContainer>
</template> </template>
...@@ -126,7 +126,7 @@ export const industryCategory: Record<string, string> = { ...@@ -126,7 +126,7 @@ export const industryCategory: Record<string, string> = {
export const industryCategoryList = Object.values(industryCategory).map(item => ({ label: item, value: item })) export const industryCategoryList = Object.values(industryCategory).map(item => ({ label: item, value: item }))
// 项目 // 项目
export const projectPrefix = { export const projectPrefix: Record<string, string> = {
sofia: '索菲亚', sofia: '索菲亚',
kelley: '凯丽', kelley: '凯丽',
marywood: '玛丽伍德', marywood: '玛丽伍德',
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论