1
This commit is contained in:
784
admin/src/views/account/pla_account.vue
Normal file
784
admin/src/views/account/pla_account.vue
Normal file
@@ -0,0 +1,784 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增账号</Button>
|
||||
<Button type="success" @click="batchParseLocation" :loading="batchParseLoading" class="ml10">批量解析位置</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_type" style="width: 120px" clearable
|
||||
@on-change="query(1)">
|
||||
<Option value="1">Boss直聘</Option>
|
||||
<Option value="2">猎聘</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="在线状态">
|
||||
<Select v-model="gridOption.param.seachOption.is_online" 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" @on-save="handleSaveSuccess">
|
||||
</editModal>
|
||||
|
||||
<!-- 简历详情弹窗 -->
|
||||
<Modal v-model="resumeModal.visible" :title="resumeModal.title" width="900" :footer-hide="true">
|
||||
<div v-if="resumeModal.loading" style="text-align: center; padding: 40px;">
|
||||
<Spin size="large"></Spin>
|
||||
<p style="margin-top: 20px;">加载简历数据中...</p>
|
||||
</div>
|
||||
<div v-else-if="resumeModal.data" class="resume-detail">
|
||||
<!-- 基本信息 -->
|
||||
<Card title="基本信息" style="margin-bottom: 16px;">
|
||||
<Row :gutter="16">
|
||||
<Col span="8">
|
||||
<p><strong>姓名:</strong>{{ resumeModal.data.fullName || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>性别:</strong>{{ resumeModal.data.gender || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>年龄:</strong>{{ resumeModal.data.age || '-' }}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16" style="margin-top: 12px;">
|
||||
<Col span="8">
|
||||
<p><strong>电话:</strong>{{ resumeModal.data.phone || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>邮箱:</strong>{{ resumeModal.data.email || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>所在地:</strong>{{ resumeModal.data.location || '-' }}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<!-- 教育背景 -->
|
||||
<Card title="教育背景" style="margin-bottom: 16px;">
|
||||
<Row :gutter="16">
|
||||
<Col span="8">
|
||||
<p><strong>学历:</strong>{{ resumeModal.data.education || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>专业:</strong>{{ resumeModal.data.major || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>毕业院校:</strong>{{ resumeModal.data.school || '-' }}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<!-- 工作信息 -->
|
||||
<Card title="工作信息" style="margin-bottom: 16px;">
|
||||
<Row :gutter="16">
|
||||
<Col span="8">
|
||||
<p><strong>工作年限:</strong>{{ resumeModal.data.workYears || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>当前职位:</strong>{{ resumeModal.data.currentPosition || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>当前公司:</strong>{{ resumeModal.data.currentCompany || '-' }}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<!-- 期望信息 -->
|
||||
<Card title="期望信息" style="margin-bottom: 16px;">
|
||||
<Row :gutter="16">
|
||||
<Col span="8">
|
||||
<p><strong>期望职位:</strong>{{ resumeModal.data.expectedPosition || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>期望薪资:</strong>{{ resumeModal.data.expectedSalary || '-' }}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p><strong>期望地点:</strong>{{ resumeModal.data.expectedLocation || '-' }}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<!-- 技能标签 -->
|
||||
<Card title="技能标签" style="margin-bottom: 16px;"
|
||||
v-if="resumeModal.data.skills || resumeModal.data.aiSkillTags">
|
||||
<div v-if="resumeModal.data.aiSkillTags && resumeModal.data.aiSkillTags.length > 0">
|
||||
<Tag v-for="(skill, index) in resumeModal.data.aiSkillTags" :key="index" color="blue"
|
||||
style="margin: 4px;">
|
||||
{{ skill }}
|
||||
</Tag>
|
||||
</div>
|
||||
<div v-else-if="resumeModal.data.skills">
|
||||
<Tag v-for="(skill, index) in parseSkills(resumeModal.data.skills)" :key="index" color="blue"
|
||||
style="margin: 4px;">
|
||||
{{ skill }}
|
||||
</Tag>
|
||||
</div>
|
||||
<p v-else style="color: #999;">暂无技能标签</p>
|
||||
</Card>
|
||||
|
||||
<!-- AI分析 -->
|
||||
<Card title="AI分析" style="margin-bottom: 16px;" v-if="resumeModal.data.aiCompetitiveness">
|
||||
<Row :gutter="16">
|
||||
<Col span="24">
|
||||
<p><strong>竞争力评分:</strong>
|
||||
<Tag :color="getScoreColor(resumeModal.data.aiCompetitiveness)" size="large">
|
||||
{{ resumeModal.data.aiCompetitiveness }} 分
|
||||
</Tag>
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16" style="margin-top: 12px;">
|
||||
<Col span="24">
|
||||
<p><strong>优势分析:</strong></p>
|
||||
<p style="color: #19be6b; padding: 8px; background: #f0f9ff; border-radius: 4px;">
|
||||
{{ resumeModal.data.aiStrengths || '-' }}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16" style="margin-top: 12px;">
|
||||
<Col span="24">
|
||||
<p><strong>劣势分析:</strong></p>
|
||||
<p style="color: #ed4014; padding: 8px; background: #fff1f0; border-radius: 4px;">
|
||||
{{ resumeModal.data.aiWeaknesses || '-' }}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16" style="margin-top: 12px;">
|
||||
<Col span="24">
|
||||
<p><strong>职业建议:</strong></p>
|
||||
<p style="color: #2d8cf0; padding: 8px; background: #f0faff; border-radius: 4px;">
|
||||
{{ resumeModal.data.aiCareerSuggestion || '-' }}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<!-- 项目经验 -->
|
||||
<Card title="项目经验" style="margin-bottom: 16px;" v-if="resumeModal.data.projectExperience">
|
||||
<Timeline v-if="parseProjectExp(resumeModal.data.projectExperience).length > 0">
|
||||
<TimelineItem v-for="(project, index) in parseProjectExp(resumeModal.data.projectExperience)"
|
||||
:key="index">
|
||||
<p><strong>{{ project.name || project.projectName }}</strong></p>
|
||||
<p style="color: #999; font-size: 12px;">{{ project.roleName || project.role }}</p>
|
||||
<p style="margin-top: 8px;">{{ project.projectDesc || project.description }}</p>
|
||||
<p v-if="project.performance" style="margin-top: 4px; color: #19be6b;">
|
||||
<Icon type="ios-trophy" /> {{ project.performance }}
|
||||
</p>
|
||||
</TimelineItem>
|
||||
</Timeline>
|
||||
<p v-else style="color: #999;">暂无项目经验</p>
|
||||
</Card>
|
||||
|
||||
<!-- 工作经历 -->
|
||||
<Card title="工作经历" v-if="resumeModal.data.workExperience">
|
||||
<Timeline v-if="parseWorkExp(resumeModal.data.workExperience).length > 0">
|
||||
<TimelineItem v-for="(work, index) in parseWorkExp(resumeModal.data.workExperience)"
|
||||
:key="index">
|
||||
<p><strong>{{ work.companyName }}</strong> - {{ work.positionName }}</p>
|
||||
<p style="color: #999; font-size: 12px;">
|
||||
{{ work.startDate }} ~ {{ work.endDate }}
|
||||
</p>
|
||||
<p style="margin-top: 8px;">{{ work.workContent || work.description }}</p>
|
||||
</TimelineItem>
|
||||
</Timeline>
|
||||
<p v-else style="color: #999;">暂无工作经历</p>
|
||||
</Card>
|
||||
</div>
|
||||
<div v-else style="text-align: center; padding: 40px; color: #999;">
|
||||
<Icon type="ios-document-outline" size="60" />
|
||||
<p style="margin-top: 20px;">暂无简历数据</p>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import plaAccountServer from '@/api/profile/pla_account_server.js'
|
||||
import jobTypesServer from '@/api/work/job_types_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
rules["name"] = [{ required: true, message: '请填写账户名', trigger: 'blur' }]
|
||||
rules["sn_code"] = [{ required: true, message: '请填写设备SN码', trigger: 'blur' }]
|
||||
rules["platform_type"] = [{ required: true, message: '请选择平台', trigger: 'change' }]
|
||||
rules["login_name"] = [{ required: true, message: '请填写登录名', trigger: 'blur' }]
|
||||
|
||||
return {
|
||||
serverInstance: plaAccountServer,
|
||||
jobTypeOptions: [],
|
||||
batchParseLoading: false,
|
||||
seachTypes: [
|
||||
{ key: 'name', value: '账户名' },
|
||||
{ key: 'login_name', value: '登录名' },
|
||||
{ key: 'sn_code', value: '设备SN码' }
|
||||
],
|
||||
resumeModal: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
title: '在线简历详情',
|
||||
data: null
|
||||
},
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'name',
|
||||
value: '',
|
||||
platform_type: null,
|
||||
is_online: null,
|
||||
is_online: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '账户名', key: 'name', minWidth: 150 },
|
||||
{ title: '设备SN码', key: 'sn_code', minWidth: 120 },
|
||||
{
|
||||
title: '平台',
|
||||
key: 'platform_type',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const platformMap = {
|
||||
'1': { text: 'Boss直聘', color: 'blue' },
|
||||
'2': { text: '猎聘', color: 'green' }
|
||||
}
|
||||
const platform = platformMap[params.row.platform_type] || { text: params.row.platform_type, color: 'default' }
|
||||
return h('Tag', { props: { color: platform.color } }, platform.text)
|
||||
}
|
||||
},
|
||||
{ title: '登录名', key: 'login_name', minWidth: 150 },
|
||||
{ title: '搜索关键词', key: 'keyword', minWidth: 150 },
|
||||
{ title: '用户地址', key: 'user_address', minWidth: 150 },
|
||||
{
|
||||
title: '经纬度',
|
||||
key: 'location',
|
||||
minWidth: 150,
|
||||
render: (h, params) => {
|
||||
const lon = params.row.user_longitude;
|
||||
const lat = params.row.user_latitude;
|
||||
if (lon && lat) {
|
||||
return h('span', `${lat}, ${lon}`)
|
||||
}
|
||||
return h('span', { style: { color: '#999' } }, '未设置')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '在线状态',
|
||||
key: 'is_online',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: { color: params.row.is_online ? 'success' : 'default' }
|
||||
}, params.row.is_online ? '在线' : '离线')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '自动投递',
|
||||
key: 'auto_deliver',
|
||||
com: "Radio",
|
||||
minWidth: 100,
|
||||
options: [
|
||||
{ value: 1, label: '开启' },
|
||||
{ value: 0, label: '关闭' }
|
||||
],
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: { color: params.row.auto_deliver ? 'success' : 'default' }
|
||||
}, params.row.auto_deliver ? '开启' : '关闭')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '自动沟通',
|
||||
key: 'auto_chat',
|
||||
"com": "Radio",
|
||||
options: [
|
||||
{ value: 1, label: '开启' },
|
||||
{ value: 0, label: '关闭' }
|
||||
],
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: { color: params.row.auto_chat ? 'success' : 'default' }
|
||||
}, params.row.auto_chat ? '开启' : '关闭')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '自动活跃',
|
||||
key: 'auto_active',
|
||||
"com": "Radio",
|
||||
options: [
|
||||
{ value: 1, label: '开启' },
|
||||
{ value: 0, label: '关闭' }
|
||||
],
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: { color: params.row.auto_active ? 'success' : 'default' }
|
||||
}, params.row.auto_active ? '开启' : '关闭')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '启用状态',
|
||||
key: 'is_enabled',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('i-switch', {
|
||||
props: {
|
||||
value: Boolean(params.row.is_enabled),
|
||||
size: 'large'
|
||||
},
|
||||
on: {
|
||||
'on-change': (value) => {
|
||||
this.toggleEnabled(params.row, value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 450,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '详情',
|
||||
type: 'info',
|
||||
click: () => {
|
||||
this.showAccountDetails(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '查看简历',
|
||||
type: 'success',
|
||||
click: () => {
|
||||
this.showResume(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '解析位置',
|
||||
type: 'success',
|
||||
click: () => {
|
||||
this.parseLocation(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '停止任务',
|
||||
type: 'warning',
|
||||
click: () => {
|
||||
this.stopTasks(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return window.framework.uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '账户名', key: 'name', type: 'text', required: true },
|
||||
{ title: '设备SN码', key: 'sn_code', type: 'text', required: true },
|
||||
{
|
||||
title: '平台', key: 'platform_type', type: 'select', required: true, options: [
|
||||
{ value: '1', label: 'Boss直聘' },
|
||||
{ value: '2', label: '猎聘' }
|
||||
]
|
||||
},
|
||||
{ title: '登录名', key: 'login_name', type: 'text', required: true },
|
||||
{ title: '密码', key: 'pwd', type: 'password' },
|
||||
{ title: '搜索关键词', key: 'keyword', type: 'text' },
|
||||
{ title: '启用状态', key: 'is_enabled', type: 'switch' },
|
||||
{ title: '在线状态', key: 'is_online', type: 'switch' },
|
||||
{
|
||||
title: '职位类型',
|
||||
key: 'job_type_id',
|
||||
type: 'select',
|
||||
required: false,
|
||||
options: this.jobTypeOptions || []
|
||||
},
|
||||
{ title: '用户地址', key: 'user_address', type: 'text', placeholder: '请输入用户地址,如:北京市朝阳区' },
|
||||
// 自动投递配置
|
||||
{
|
||||
title: '自动投递', key: 'auto_deliver', "com": "Radio", options: [
|
||||
{ value: 1, label: '开启' },
|
||||
{ value: 0, label: '关闭' }
|
||||
],
|
||||
},
|
||||
{ title: '最低薪资(元)', key: 'min_salary', type: 'number', placeholder: '最低薪资,0表示不限制' },
|
||||
{ title: '最高薪资(元)', key: 'max_salary', type: 'number', placeholder: '最高薪资,0表示不限制' },
|
||||
// 自动沟通配置
|
||||
{
|
||||
title: '自动沟通', key: 'auto_chat', "com": "Radio", options: [
|
||||
{ value: 1, label: '开启' },
|
||||
{ value: 0, label: '关闭' }
|
||||
],
|
||||
},
|
||||
{ title: '沟通间隔(分钟)', key: 'chat_interval', type: 'number', placeholder: '沟通间隔时间,默认30分钟' },
|
||||
{ title: '自动回复', key: 'auto_reply', type: 'switch' },
|
||||
// 自动活跃配置
|
||||
{
|
||||
title: '自动活跃', key: 'auto_active', "com": "Radio", options: [
|
||||
{ value: 1, label: '开启' },
|
||||
{ value: 0, label: '关闭' }
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
this.loadJobTypes()
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
plaAccountServer.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) {
|
||||
// 将布尔字段从 0/1 转换为 true/false,以便开关组件正确显示
|
||||
const editData = { ...row }
|
||||
const booleanFields = ['is_enabled', 'is_online', 'auto_deliver', 'auto_chat', 'auto_reply', 'auto_active']
|
||||
booleanFields.forEach(field => {
|
||||
if (editData[field] !== undefined && editData[field] !== null) {
|
||||
editData[field] = Boolean(editData[field])
|
||||
}
|
||||
})
|
||||
this.$refs.editModal.showModal(editData)
|
||||
},
|
||||
toggleEnabled(row, value) {
|
||||
const action = value ? '启用' : '禁用'
|
||||
this.$Modal.confirm({
|
||||
title: `确认${action}`,
|
||||
content: `确定要${action}账号 "${row.name}" 吗?${!value ? '禁用后该账号将不会执行自动任务。' : ''}`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await plaAccountServer.update({ ...row, is_enabled: value ? 1 : 0 })
|
||||
this.$Message.success(`${action}成功!`)
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} catch (error) {
|
||||
this.$Message.error(`${action}失败`)
|
||||
// 恢复开关状态
|
||||
row.is_enabled = value ? 0 : 1
|
||||
}
|
||||
},
|
||||
onCancel: () => {
|
||||
// 取消时恢复开关状态
|
||||
row.is_enabled = value ? 0 : 1
|
||||
}
|
||||
})
|
||||
},
|
||||
loadJobTypes() {
|
||||
jobTypesServer.getAll().then(res => {
|
||||
if (res.code === 0 && res.data) {
|
||||
this.jobTypeOptions = res.data.map(item => ({
|
||||
value: item.id,
|
||||
label: item.name
|
||||
}))
|
||||
// 更新 editColumns 中的选项
|
||||
const jobTypeColumn = this.gridOption.editColumns.find(col => col.key === 'job_type_id')
|
||||
if (jobTypeColumn) {
|
||||
jobTypeColumn.options = this.jobTypeOptions
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('加载职位类型失败:', err)
|
||||
})
|
||||
},
|
||||
stopTasks(row) {
|
||||
this.$Modal.confirm({
|
||||
title: '确认停止',
|
||||
content: `确定要停止账号 "${row.name}" 的所有任务吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await plaAccountServer.stopTasks(row)
|
||||
this.$Message.success('停止任务成功!')
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} catch (error) {
|
||||
this.$Message.error('停止任务失败:' + (error.message || '请稍后重试'))
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
delConfirm(row) {
|
||||
window.framework.uiTool.delConfirm(async () => {
|
||||
await plaAccountServer.del(row)
|
||||
this.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
plaAccountServer.exportCsv(this.gridOption.param).then(res => {
|
||||
window.framework.funTool.downloadFile(res, '平台账号.csv')
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'name',
|
||||
value: '',
|
||||
platform_type: null,
|
||||
is_online: null
|
||||
}
|
||||
this.query(1)
|
||||
},
|
||||
async handleSaveSuccess({ data }) {
|
||||
try {
|
||||
// 将布尔字段从 true/false 转换为 1/0
|
||||
const saveData = { ...data }
|
||||
const booleanFields = ['is_enabled', 'is_online', 'auto_deliver', 'auto_chat', 'auto_reply', 'auto_active']
|
||||
booleanFields.forEach(field => {
|
||||
if (saveData[field] !== undefined && saveData[field] !== null) {
|
||||
saveData[field] = saveData[field] ? 1 : 0
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
// 根据是否有 id 判断是新增还是更新
|
||||
if (saveData.id) {
|
||||
await plaAccountServer.update(saveData)
|
||||
} else {
|
||||
await plaAccountServer.add(saveData)
|
||||
}
|
||||
|
||||
this.$Message.success('保存成功!')
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
this.$Message.error('保存失败:' + (error.message || '请稍后重试'))
|
||||
}
|
||||
},
|
||||
// 显示账号详情
|
||||
showAccountDetails(row) {
|
||||
this.$router.push({
|
||||
path: '/pla_account/pla_account_detail',
|
||||
query: { id: row.id }
|
||||
})
|
||||
},
|
||||
// 查看简历
|
||||
async showResume(row) {
|
||||
this.resumeModal.visible = true
|
||||
this.resumeModal.loading = true
|
||||
this.resumeModal.data = null
|
||||
this.resumeModal.title = `${row.name} - 在线简历`
|
||||
|
||||
try {
|
||||
// 根据 sn_code 和 platform 获取简历
|
||||
const platformMap = {
|
||||
'1': 'boss',
|
||||
'2': 'liepin'
|
||||
}
|
||||
const platform = platformMap[row.platform_type] || 'boss'
|
||||
|
||||
// admin 会自动加 /admin_api 前缀
|
||||
const res = await window.framework.http.get(`/resume/get-by-device?sn_code=${row.sn_code}&platform=${platform}`)
|
||||
|
||||
if (res.code === 0) {
|
||||
this.resumeModal.data = res.data
|
||||
} else {
|
||||
this.$Message.warning(res.message || '未找到简历数据')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取简历失败:', error)
|
||||
this.$Message.error('获取简历失败:' + (error.message || '请稍后重试'))
|
||||
} finally {
|
||||
this.resumeModal.loading = false
|
||||
}
|
||||
},
|
||||
// 解析技能标签
|
||||
parseSkills(skills) {
|
||||
if (!skills) return []
|
||||
if (Array.isArray(skills)) return skills
|
||||
try {
|
||||
return JSON.parse(skills)
|
||||
} catch (e) {
|
||||
return skills.split(',').map(s => s.trim()).filter(s => s)
|
||||
}
|
||||
},
|
||||
// 解析项目经验
|
||||
parseProjectExp(projectExp) {
|
||||
if (!projectExp) return []
|
||||
if (Array.isArray(projectExp)) return projectExp
|
||||
try {
|
||||
return JSON.parse(projectExp)
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 解析工作经历
|
||||
parseWorkExp(workExp) {
|
||||
if (!workExp) return []
|
||||
if (Array.isArray(workExp)) return workExp
|
||||
try {
|
||||
return JSON.parse(workExp)
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 获取评分颜色
|
||||
getScoreColor(score) {
|
||||
if (score >= 80) return 'success'
|
||||
if (score >= 60) return 'warning'
|
||||
return 'error'
|
||||
},
|
||||
// 解析单个账号的位置
|
||||
async parseLocation(row) {
|
||||
if (!row.user_address || row.user_address.trim() === '') {
|
||||
this.$Message.warning('请先设置用户地址')
|
||||
return
|
||||
}
|
||||
|
||||
this.$Modal.confirm({
|
||||
title: '确认解析位置',
|
||||
content: `确定要解析账号 "${row.name}" 的地址 "${row.user_address}" 吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const res = await window.framework.http.post('/pla_account/parseLocation', {
|
||||
id: row.id,
|
||||
address: row.user_address
|
||||
})
|
||||
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('位置解析成功!')
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} else {
|
||||
this.$Message.error(res.message || '位置解析失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('位置解析失败:', error)
|
||||
this.$Message.error('位置解析失败:' + (error.message || '请稍后重试'))
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 批量解析位置
|
||||
async batchParseLocation() {
|
||||
const selectedRows = this.gridOption.data.filter(row => row.user_address && row.user_address.trim() !== '')
|
||||
|
||||
if (selectedRows.length === 0) {
|
||||
this.$Message.warning('当前页面没有已设置地址的账号')
|
||||
return
|
||||
}
|
||||
|
||||
this.$Modal.confirm({
|
||||
title: '确认批量解析位置',
|
||||
content: `确定要批量解析 ${selectedRows.length} 个账号的位置吗?`,
|
||||
onOk: async () => {
|
||||
this.batchParseLoading = true
|
||||
try {
|
||||
const ids = selectedRows.map(row => row.id)
|
||||
const res = await window.framework.http.post('/pla_account/batchParseLocation', { ids })
|
||||
|
||||
if (res.code === 0) {
|
||||
const result = res.data
|
||||
const successCount = result.success || 0
|
||||
const failedCount = result.failed || 0
|
||||
|
||||
let message = `批量解析完成:成功 ${successCount} 个`
|
||||
if (failedCount > 0) {
|
||||
message += `,失败 ${failedCount} 个`
|
||||
}
|
||||
|
||||
this.$Message.success(message)
|
||||
|
||||
// 如果有失败的,显示详细信息
|
||||
if (failedCount > 0 && result.details) {
|
||||
const failedDetails = result.details.filter(d => !d.success)
|
||||
console.log('解析失败的账号:', failedDetails)
|
||||
}
|
||||
|
||||
this.query(this.gridOption.param.pageOption.page)
|
||||
} else {
|
||||
this.$Message.error(res.message || '批量解析失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量位置解析失败:', error)
|
||||
this.$Message.error('批量位置解析失败:' + (error.message || '请稍后重试'))
|
||||
} finally {
|
||||
this.batchParseLoading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
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;
|
||||
}
|
||||
|
||||
.resume-detail {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.resume-detail p {
|
||||
margin: 8px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.resume-detail strong {
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user