diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/.npmrc b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/.npmrc
new file mode 100644
index 0000000..fdb6938
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/.npmrc
@@ -0,0 +1,4 @@
+# pnpm 配置
+shamefully-hoist=true
+strict-peer-dependencies=false
+auto-install-peers=true
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/index.html b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/index.html
new file mode 100644
index 0000000..dfe92ff
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ 简历智能体 - HR管理系统
+
+
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/package.json b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/package.json
new file mode 100644
index 0000000..8b1d171
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "ylhp-hr-frontend",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "packageManager": "pnpm@9.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "vue": "^3.4.0",
+ "vue-router": "^4.2.0",
+ "pinia": "^2.1.0",
+ "axios": "^1.6.0",
+ "element-plus": "^2.5.0",
+ "@element-plus/icons-vue": "^2.3.0",
+ "dayjs": "^1.11.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^5.0.0",
+ "vite": "^5.0.0",
+ "sass": "^1.70.0"
+ }
+}
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/App.vue b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/App.vue
new file mode 100644
index 0000000..02e6d3e
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/App.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/api/api.js b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/api/api.js
new file mode 100644
index 0000000..1eae067
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/api/api.js
@@ -0,0 +1,154 @@
+import axios from 'axios'
+
+const api = axios.create({
+ baseURL: '',
+ timeout: 30000,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+})
+
+// 响应拦截器 - 统一处理
+api.interceptors.response.use(
+ (response) => {
+ const data = response.data
+ if (data.code !== 200) {
+ return Promise.reject(new Error(data.msg || '请求失败'))
+ }
+ return data
+ },
+ (error) => {
+ return Promise.reject(error)
+ }
+)
+
+// 招聘者管理 API
+export const recruiterApi = {
+ // 获取平台来源列表
+ getSources: () => api.get('/api/recruiters/sources'),
+
+ // 获取招聘者列表
+ getList: (params = {}) => api.get('/api/recruiters', { params }),
+
+ // 获取招聘者详情
+ getDetail: (id) => api.get(`/api/recruiters/${id}`),
+
+ // 创建招聘者
+ create: (data) => api.post('/api/recruiters', data),
+
+ // 自动注册
+ register: (data) => api.post('/api/recruiters/register', data),
+
+ // 更新招聘者
+ update: (id, data) => api.put(`/api/recruiters/${id}`, data),
+
+ // 删除招聘者
+ delete: (id) => api.delete(`/api/recruiters/${id}`),
+
+ // 启用招聘者
+ activate: (id) => api.post(`/api/recruiters/${id}/activate`),
+
+ // 停用招聘者
+ deactivate: (id) => api.post(`/api/recruiters/${id}/deactivate`),
+
+ // 同步招聘者
+ sync: (id) => api.post(`/api/recruiters/${id}/sync`)
+}
+
+// 职位管理 API
+export const jobApi = {
+ // 获取职位列表
+ getList: (params = {}) => api.get('/api/jobs', { params }),
+
+ // 筛选职位
+ filter: (data) => api.post('/api/jobs/filter', data),
+
+ // 获取职位详情
+ getDetail: (id) => api.get(`/api/jobs/${id}`),
+
+ // 创建职位
+ create: (data) => api.post('/api/jobs', data),
+
+ // 更新职位
+ update: (id, data) => api.put(`/api/jobs/${id}`, data),
+
+ // 删除职位
+ delete: (id) => api.delete(`/api/jobs/${id}`),
+
+ // 关联评价方案
+ bindSchema: (id, schemaId) => api.post(`/api/jobs/${id}/bind-schema`, { evaluation_schema_id: schemaId }),
+
+ // 获取职位关联的评价方案
+ getSchema: (id) => api.get(`/api/jobs/${id}/schema`),
+
+ // 获取评价方案列表
+ getSchemaList: (params = {}) => api.get('/api/jobs/schemas/list', { params })
+}
+
+// 候选人管理 API
+export const candidateApi = {
+ // 获取筛选通过的候选人
+ getFiltered: (params = {}) => api.get('/candidates/filtered', { params }),
+
+ // 筛选候选人
+ filter: (data) => api.post('/candidates/filter', data),
+
+ // 获取候选人详情
+ getDetail: (id) => api.get(`/candidates/${id}`),
+
+ // 标记候选人筛选状态
+ markFiltered: (data) => api.post('/candidates/mark-filtered', data),
+
+ // 更新候选人评分
+ updateScore: (data) => api.post('/candidates/update-score', data),
+
+ // 根据评分范围查询
+ getByScoreRange: (params) => api.get('/candidates/by-score-range', { params })
+}
+
+// 定时任务管理 API
+export const schedulerApi = {
+ // 获取任务列表
+ getJobs: () => api.get('/api/scheduler/jobs'),
+
+ // 获取任务状态列表
+ getJobsStatus: () => api.get('/api/scheduler/jobs/status'),
+
+ // 获取单个任务状态
+ getJobStatus: (id) => api.get(`/api/scheduler/jobs/${id}/status`),
+
+ // 立即执行任务
+ runJob: (id) => api.post(`/api/scheduler/jobs/${id}/run`),
+
+ // 暂停任务
+ pauseJob: (id) => api.post(`/api/scheduler/jobs/${id}/pause`),
+
+ // 恢复任务
+ resumeJob: (id) => api.post(`/api/scheduler/jobs/${id}/resume`),
+
+ // 更新任务配置
+ updateConfig: (id, data) => api.put(`/api/scheduler/jobs/${id}/config`, data),
+
+ // 获取调度器状态
+ getStatus: () => api.get('/api/scheduler/status'),
+
+ // 启动调度器
+ start: () => api.post('/api/scheduler/start'),
+
+ // 停止调度器
+ stop: () => api.post('/api/scheduler/stop')
+}
+
+// 系统 API
+export const systemApi = {
+ // 获取首页信息
+ getHome: () => api.get('/'),
+
+ // 健康检查
+ health: () => api.get('/health'),
+
+ // 获取API状态
+ getStatus: () => api.get('/api/status')
+}
+
+export default api
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/components/Layout.vue b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/components/Layout.vue
new file mode 100644
index 0000000..2f8cff7
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/components/Layout.vue
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/main.js b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/main.js
new file mode 100644
index 0000000..9e3a977
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/main.js
@@ -0,0 +1,22 @@
+import { createApp } from 'vue'
+import { createPinia } from 'pinia'
+import ElementPlus from 'element-plus'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import 'element-plus/dist/index.css'
+import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
+
+import App from './App.vue'
+import router from './router'
+
+const app = createApp(App)
+
+// 注册所有图标
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+ app.component(key, component)
+}
+
+app.use(createPinia())
+app.use(router)
+app.use(ElementPlus, { locale: zhCn })
+
+app.mount('#app')
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/router/index.js b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/router/index.js
new file mode 100644
index 0000000..6323a7f
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/router/index.js
@@ -0,0 +1,49 @@
+import { createRouter, createWebHistory } from 'vue-router'
+import Layout from '@/components/Layout.vue'
+
+const routes = [
+ {
+ path: '/',
+ component: Layout,
+ redirect: '/dashboard',
+ children: [
+ {
+ path: 'dashboard',
+ name: 'Dashboard',
+ component: () => import('@/views/Dashboard.vue'),
+ meta: { title: '首页', icon: 'HomeFilled' }
+ },
+ {
+ path: 'recruiters',
+ name: 'Recruiters',
+ component: () => import('@/views/Recruiters.vue'),
+ meta: { title: '招聘者管理', icon: 'UserFilled' }
+ },
+ {
+ path: 'jobs',
+ name: 'Jobs',
+ component: () => import('@/views/Jobs.vue'),
+ meta: { title: '职位管理', icon: 'Briefcase' }
+ },
+ {
+ path: 'candidates',
+ name: 'Candidates',
+ component: () => import('@/views/Candidates.vue'),
+ meta: { title: '候选人管理', icon: 'Avatar' }
+ },
+ {
+ path: 'scheduler',
+ name: 'Scheduler',
+ component: () => import('@/views/Scheduler.vue'),
+ meta: { title: '定时任务', icon: 'Clock' }
+ }
+ ]
+ }
+]
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes
+})
+
+export default router
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Candidates.vue b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Candidates.vue
new file mode 100644
index 0000000..b817bfb
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Candidates.vue
@@ -0,0 +1,364 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+ 搜索
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.salary_min }}K-{{ row.salary_max }}K
+
+ -
+
+
+
+
+
+
+
+ 未评分
+
+
+
+
+
+ {{ row.llm_filtered ? '已通过' : '未筛选' }}
+
+
+
+
+
+
+ 详情
+ 评分
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ currentCandidate.name }}
+ {{ currentCandidate.gender }}
+ {{ currentCandidate.age }}
+ {{ currentCandidate.education }}
+ {{ currentCandidate.phone || '-' }}
+ {{ currentCandidate.email || '-' }}
+ {{ currentCandidate.current_company }}
+ {{ currentCandidate.current_position }}
+ {{ currentCandidate.location }}
+ {{ currentCandidate.source }}
+
+
+ {{ currentCandidate.llm_score }}
+
+ {{ currentCandidate.llm_filtered ? '已通过筛选' : '未通过筛选' }}
+
+
+ 未评分
+
+
+ {{ JSON.stringify(currentCandidate.llm_score_details, null, 2) }}
+
+
+
+
+
+
+
+
+
+ {{ scoreForm.llm_score }} 分
+
+
+
+
+
+
+ 取消
+
+ 确定
+
+
+
+
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Dashboard.vue b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Dashboard.vue
new file mode 100644
index 0000000..3b500be
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Dashboard.vue
@@ -0,0 +1,244 @@
+
+
+
系统概览
+
+
+
+
+
+
+
+
+
+
{{ stats.recruiters }}
+
招聘者账号
+
+
+
+
+
+
+
+
+
+
+
{{ stats.jobs }}
+
职位数量
+
+
+
+
+
+
+
+
+
{{ stats.candidates }}
+
候选人
+
+
+
+
+
+
+
+
+
+
+
{{ stats.evaluations }}
+
评价记录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加招聘者
+
+
+ 创建职位
+
+
+ 启动任务
+
+
+
+
+
+
+
+
+
+
+
+
+ API服务
+
+ {{ apiStatus === 'running' ? '运行中' : '异常' }}
+
+
+
+ 调度器
+
+ {{ schedulerStatus.running ? '运行中' : '已停止' }}
+
+
+
+ 任务数量
+ {{ schedulerStatus.total_jobs || 0 }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Jobs.vue b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Jobs.vue
new file mode 100644
index 0000000..23cbfff
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Jobs.vue
@@ -0,0 +1,478 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.salary_min }}K - {{ row.salary_max }}K
+
+ 面议
+
+
+
+
+
+ {{ getStatusLabel(row.status) }}
+
+
+
+
+
+
+ {{ row.candidate_count || 0 }}人
+
+ {{ row.candidate_count || 0 }}人
+
+
+
+
+
+
+ {{ getSchemaName(row.evaluation_schema_id) }}
+
+
+ 解除
+
+
+
+ 关联方案
+
+
+
+
+
+ {{ formatTime(row.last_sync_at) }}
+
+
+
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+ 确定
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+ 确定
+
+
+
+
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Recruiters.vue b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Recruiters.vue
new file mode 100644
index 0000000..0bb225e
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Recruiters.vue
@@ -0,0 +1,415 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+ {{ getSourceLabel(row.source) }}
+
+
+
+
+
+
+ {{ row.status === 'active' ? '启用' : '停用' }}
+
+
+
+
+
+
+
VIP: {{ row.privilege.vip_level || '无' }}
+
剩余简历: {{ row.privilege.resume_view_count || 0 }}
+
+ 暂无权益信息
+
+
+
+
+ {{ formatTime(row.last_sync_at) }}
+
+
+
+
+
+ 编辑
+ 同步
+
+ {{ row.status === 'active' ? '停用' : '启用' }}
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+ 确定
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+ 自动注册
+
+
+
+
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Scheduler.vue b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Scheduler.vue
new file mode 100644
index 0000000..a88d5a5
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/src/views/Scheduler.vue
@@ -0,0 +1,357 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ schedulerStatus.running ? '运行中' : '已停止' }}
+
调度器状态
+
+
+
+
+
+
+
+
+
+
{{ schedulerStatus.total_jobs || 0 }}
+
任务总数
+
+
+
+
+
+
+
+
+
+
{{ schedulerStatus.job_status_summary?.enabled || 0 }}
+
已启用任务
+
+
+
+
+
+
+
+
+
+
{{ schedulerStatus.job_status_summary?.running || 0 }}
+
正在运行
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.enabled ? '启用' : '禁用' }}
+
+
+
+
+
+
+ {{ row.is_running ? '运行中' : '空闲' }}
+
+
+
+
+
+
+ 成功: {{ row.success_count || 0 }}
+ 失败: {{ row.fail_count || 0 }}
+ 总计: {{ row.run_count || 0 }}
+
+
+
+
+
+ {{ formatTime(row.last_run_time) }}
+
+
+
+
+ {{ formatTime(row.next_run_time) }}
+
+
+
+
+
+
+ 执行
+
+
+ {{ row.enabled ? '暂停' : '恢复' }}
+
+ 配置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+ 保存
+
+
+
+
+
+
+
+
+
diff --git a/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/vite.config.js b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/vite.config.js
new file mode 100644
index 0000000..165f3f3
--- /dev/null
+++ b/src/main/web/cn.yinlihupo/ylhp_hr_2_0_fronted/vite.config.js
@@ -0,0 +1,25 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import { resolve } from 'path'
+
+export default defineConfig({
+ plugins: [vue()],
+ resolve: {
+ alias: {
+ '@': resolve(__dirname, 'src')
+ }
+ },
+ server: {
+ port: 3000,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:8000',
+ changeOrigin: true
+ },
+ '/candidates': {
+ target: 'http://localhost:8000',
+ changeOrigin: true
+ }
+ }
+ }
+})