feat: add admin management views (jobs, applications, organizations, users)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ca629a403c
commit
ef4c9bf307
|
|
@ -1,3 +1,71 @@
|
||||||
<template>
|
<template>
|
||||||
<div>投递管理 - 开发中</div>
|
<div>
|
||||||
|
<h2>投递管理</h2>
|
||||||
|
<el-table :data="applications" v-loading="loading" border>
|
||||||
|
<el-table-column prop="job_title" label="职位" />
|
||||||
|
<el-table-column label="求职者">
|
||||||
|
<template #default="{ row }">{{ row.resume_snapshot?.name }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="applied_at" label="投递时间" :formatter="(r,c,v) => v?.slice(0,10)" />
|
||||||
|
<el-table-column label="状态" width="150">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-select v-model="row.status" size="small" @change="updateStatus(row)">
|
||||||
|
<el-option value="pending" label="待查看" />
|
||||||
|
<el-option value="viewed" label="已查看" />
|
||||||
|
<el-option value="interviewing" label="面试中" />
|
||||||
|
<el-option value="hired" label="已录用" />
|
||||||
|
<el-option value="rejected" label="已拒绝" />
|
||||||
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button size="small" @click="viewResume(row)">查看简历</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-dialog v-model="resumeVisible" title="简历详情" width="600px">
|
||||||
|
<div v-if="currentResume">
|
||||||
|
<p><strong>姓名:</strong>{{ currentResume.name }}</p>
|
||||||
|
<p><strong>性别:</strong>{{ currentResume.gender }}</p>
|
||||||
|
<el-divider>教育经历</el-divider>
|
||||||
|
<div v-for="(e, i) in currentResume.education" :key="i">{{ e.school }} · {{ e.degree }} · {{ e.major }}</div>
|
||||||
|
<el-divider>工作经历</el-divider>
|
||||||
|
<div v-for="(e, i) in currentResume.experience" :key="i">{{ e.company }} · {{ e.position }} · {{ e.duration }}</div>
|
||||||
|
<div v-if="currentResume.attachment_url" style="margin-top:16px">
|
||||||
|
<a :href="currentResume.attachment_url" target="_blank">下载简历附件</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { getManageApplications, updateApplicationStatus } from '@/api/applications'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const applications = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const resumeVisible = ref(false)
|
||||||
|
const currentResume = ref(null)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
loading.value = true
|
||||||
|
const { data } = await getManageApplications()
|
||||||
|
applications.value = data.results
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
async function updateStatus(row) {
|
||||||
|
try {
|
||||||
|
await updateApplicationStatus(row.id, { status: row.status })
|
||||||
|
ElMessage.success('状态已更新,求职者将收到邮件通知')
|
||||||
|
} catch { ElMessage.error('更新失败') }
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewResume(row) {
|
||||||
|
currentResume.value = row.resume_snapshot
|
||||||
|
resumeVisible.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,93 @@
|
||||||
<template>
|
<template>
|
||||||
<div>职位管理 - 开发中</div>
|
<div>
|
||||||
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
|
||||||
|
<h2>职位管理</h2>
|
||||||
|
<el-button type="primary" @click="openDialog()">发布职位</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="jobs" v-loading="loading" border>
|
||||||
|
<el-table-column prop="title" label="职位名称" />
|
||||||
|
<el-table-column prop="location" label="地点" />
|
||||||
|
<el-table-column prop="salary" label="薪资" />
|
||||||
|
<el-table-column prop="status" label="状态">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.status === 'published' ? 'success' : row.status === 'draft' ? 'info' : 'danger'">
|
||||||
|
{{ { draft:'草稿', published:'已发布', closed:'已关闭' }[row.status] }}
|
||||||
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button size="small" @click="openDialog(row)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDelete(row.id)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" :title="editingJob ? '编辑职位' : '发布职位'" width="600px">
|
||||||
|
<el-form :model="form" label-width="80px">
|
||||||
|
<el-form-item label="职位名称"><el-input v-model="form.title" /></el-form-item>
|
||||||
|
<el-form-item label="职位类别"><el-input v-model="form.category" /></el-form-item>
|
||||||
|
<el-form-item label="工作地点"><el-input v-model="form.location" /></el-form-item>
|
||||||
|
<el-form-item label="薪资范围"><el-input v-model="form.salary" /></el-form-item>
|
||||||
|
<el-form-item label="职位描述"><el-input v-model="form.description" type="textarea" :rows="5" /></el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="form.status">
|
||||||
|
<el-option value="draft" label="草稿" />
|
||||||
|
<el-option value="published" label="立即发布" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave" :loading="saving">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { manageJobs, createJob, updateJob, deleteJob } from '@/api/jobs'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
const jobs = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const saving = ref(false)
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const editingJob = ref(null)
|
||||||
|
const form = reactive({ title: '', category: '', location: '', salary: '', description: '', status: 'draft' })
|
||||||
|
|
||||||
|
const fetchJobs = async () => {
|
||||||
|
loading.value = true
|
||||||
|
const { data } = await manageJobs()
|
||||||
|
jobs.value = data.results
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(job = null) {
|
||||||
|
editingJob.value = job
|
||||||
|
if (job) Object.assign(form, job)
|
||||||
|
else Object.assign(form, { title: '', category: '', location: '', salary: '', description: '', status: 'draft' })
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave() {
|
||||||
|
saving.value = true
|
||||||
|
try {
|
||||||
|
if (editingJob.value) await updateJob(editingJob.value.id, form)
|
||||||
|
else await createJob(form)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
dialogVisible.value = false
|
||||||
|
fetchJobs()
|
||||||
|
} catch { ElMessage.error('保存失败') }
|
||||||
|
finally { saving.value = false }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete(id) {
|
||||||
|
await ElMessageBox.confirm('确认删除该职位?', '提示', { type: 'warning' })
|
||||||
|
await deleteJob(id)
|
||||||
|
ElMessage.success('已删除')
|
||||||
|
fetchJobs()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(fetchJobs)
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,74 @@
|
||||||
<template>
|
<template>
|
||||||
<div>组织架构管理 - 开发中</div>
|
<div>
|
||||||
|
<div style="display:flex;justify-content:space-between;margin-bottom:16px">
|
||||||
|
<h2>组织架构管理</h2>
|
||||||
|
<el-button type="primary" @click="openDialog()">新增公司</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="orgs" border>
|
||||||
|
<el-table-column prop="name" label="公司名称" />
|
||||||
|
<el-table-column label="上级公司">
|
||||||
|
<template #default="{ row }">{{ row.parent ? orgs.find(o=>o.id===row.parent)?.name : '(集团)' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="email" label="联系邮箱" />
|
||||||
|
<el-table-column label="状态">
|
||||||
|
<template #default="{ row }"><el-tag :type="row.is_active?'success':'danger'">{{ row.is_active?'启用':'停用' }}</el-tag></template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="120">
|
||||||
|
<template #default="{ row }"><el-button size="small" @click="openDialog(row)">编辑</el-button></template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" :title="editing ? '编辑公司' : '新增公司'" width="480px">
|
||||||
|
<el-form :model="form" label-width="90px">
|
||||||
|
<el-form-item label="公司名称"><el-input v-model="form.name" /></el-form-item>
|
||||||
|
<el-form-item label="上级公司">
|
||||||
|
<el-select v-model="form.parent" clearable placeholder="不选则为集团顶级">
|
||||||
|
<el-option v-for="o in orgs" :key="o.id" :value="o.id" :label="o.name" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系邮箱"><el-input v-model="form.email" /></el-form-item>
|
||||||
|
<el-form-item label="简介"><el-input v-model="form.description" type="textarea" /></el-form-item>
|
||||||
|
<el-form-item label="状态"><el-switch v-model="form.is_active" /></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible=false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="save" :loading="saving">保存</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { manageOrganizations, createOrganization, updateOrganization } from '@/api/organizations'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const orgs = ref([])
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const editing = ref(null)
|
||||||
|
const saving = ref(false)
|
||||||
|
const form = reactive({ name: '', parent: null, email: '', description: '', is_active: true })
|
||||||
|
|
||||||
|
const fetchOrgs = async () => {
|
||||||
|
const { data } = await manageOrganizations()
|
||||||
|
orgs.value = data.results
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(org = null) {
|
||||||
|
editing.value = org
|
||||||
|
if (org) Object.assign(form, org)
|
||||||
|
else Object.assign(form, { name: '', parent: null, email: '', description: '', is_active: true })
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
saving.value = true
|
||||||
|
try {
|
||||||
|
if (editing.value) await updateOrganization(editing.value.id, form)
|
||||||
|
else await createOrganization(form)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
dialogVisible.value = false
|
||||||
|
fetchOrgs()
|
||||||
|
} catch { ElMessage.error('保存失败') } finally { saving.value = false }
|
||||||
|
}
|
||||||
|
onMounted(fetchOrgs)
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,88 @@
|
||||||
<template>
|
<template>
|
||||||
<div>用户管理 - 开发中</div>
|
<div>
|
||||||
|
<div style="display:flex;justify-content:space-between;margin-bottom:16px">
|
||||||
|
<h2>用户管理</h2>
|
||||||
|
<el-button type="primary" @click="openDialog()">新增管理员</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="users" border>
|
||||||
|
<el-table-column prop="username" label="用户名" />
|
||||||
|
<el-table-column prop="email" label="邮箱" />
|
||||||
|
<el-table-column label="角色">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.role==='superadmin'?'danger':row.role==='admin'?'warning':'info'">
|
||||||
|
{{ { superadmin:'超管', admin:'公司管理员', seeker:'求职者' }[row.role] }}
|
||||||
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="所属公司">
|
||||||
|
<template #default="{ row }">{{ row.organization ? orgs.find(o=>o.id===row.organization)?.name : '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态">
|
||||||
|
<template #default="{ row }"><el-tag :type="row.is_active?'success':'danger'">{{ row.is_active?'正常':'停用' }}</el-tag></template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template #default="{ row }"><el-button size="small" @click="openDialog(row)">编辑</el-button></template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" :title="editing ? '编辑用户' : '新增管理员'" width="480px">
|
||||||
|
<el-form :model="form" label-width="90px">
|
||||||
|
<el-form-item label="用户名"><el-input v-model="form.username" /></el-form-item>
|
||||||
|
<el-form-item label="邮箱"><el-input v-model="form.email" /></el-form-item>
|
||||||
|
<el-form-item label="手机号"><el-input v-model="form.phone" /></el-form-item>
|
||||||
|
<el-form-item label="角色">
|
||||||
|
<el-select v-model="form.role">
|
||||||
|
<el-option value="admin" label="公司管理员" />
|
||||||
|
<el-option value="superadmin" label="超级管理员" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属公司" v-if="form.role==='admin'">
|
||||||
|
<el-select v-model="form.organization" clearable>
|
||||||
|
<el-option v-for="o in orgs" :key="o.id" :value="o.id" :label="o.name" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码" v-if="!editing"><el-input v-model="form.password" type="password" /></el-form-item>
|
||||||
|
<el-form-item label="状态"><el-switch v-model="form.is_active" /></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible=false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="save" :loading="saving">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { manageOrganizations } from '@/api/organizations'
|
||||||
|
import client from '@/api/client'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const users = ref([])
|
||||||
|
const orgs = ref([])
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const editing = ref(null)
|
||||||
|
const saving = ref(false)
|
||||||
|
const form = reactive({ username: '', email: '', phone: '', role: 'admin', organization: null, password: '', is_active: true })
|
||||||
|
|
||||||
|
const fetchUsers = async () => { const { data } = await client.get('/auth/users/'); users.value = data.results || data }
|
||||||
|
const fetchOrgs = async () => { const { data } = await manageOrganizations(); orgs.value = data.results }
|
||||||
|
|
||||||
|
function openDialog(user = null) {
|
||||||
|
editing.value = user
|
||||||
|
if (user) Object.assign(form, user)
|
||||||
|
else Object.assign(form, { username: '', email: '', phone: '', role: 'admin', organization: null, password: '', is_active: true })
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
saving.value = true
|
||||||
|
try {
|
||||||
|
if (editing.value) await client.patch(`/auth/users/${editing.value.id}/`, form)
|
||||||
|
else await client.post('/auth/users/', form)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
dialogVisible.value = false
|
||||||
|
fetchUsers()
|
||||||
|
} catch { ElMessage.error('保存失败') } finally { saving.value = false }
|
||||||
|
}
|
||||||
|
onMounted(() => { fetchUsers(); fetchOrgs() })
|
||||||
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue