Files
autoAiWorkSys/admin/src/views/account/pla_account.vue
张成 517a320627 1
2025-12-29 15:27:55 +08:00

891 lines
39 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="content-view">
<div class="table-head-tool">
<div>
<Button type="primary" @click="showAddWarp">新增账号</Button>
<Button type="success" @click="batchParseLocation" :loading="batchParseLoading"
class="ml10">批量解析位置</Button>
</div>
<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="boss">Boss直聘</Option>
<Option value="liepin">猎聘</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="addColumns" :rules="gridOption.rules" @on-save="handleSaveSuccess">
</editModal>
<!-- 编辑组件使用 FloatPanel包含所有字段 -->
<PlaAccountEdit ref="accountEdit" @on-save="handleSaveSuccess" />
<!-- 授权设置弹窗 -->
<Modal
v-model="authorizationModal.visible"
title="设置授权"
:mask-closable="false"
width="600"
@on-ok="handleSaveAuthorization"
@on-cancel="handleCancelAuthorization"
>
<Form ref="authorizationForm" :model="authorizationModal.formData" :label-width="120">
<FormItem label="账号名称">
<Input v-model="authorizationModal.formData.accountName" disabled />
</FormItem>
<FormItem label="设备SN码">
<Input v-model="authorizationModal.formData.sn_code" disabled />
</FormItem>
<FormItem label="授权日期">
<DatePicker
v-model="authorizationModal.formData.authorization_date"
type="date"
placeholder="请选择授权日期(不选择则使用当前时间)"
style="width: 100%;"
format="yyyy-MM-dd"
/>
</FormItem>
<FormItem label="授权天数">
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
<InputNumber
v-model="authorizationModal.formData.authorization_days"
:min="0"
placeholder="请输入授权天数"
style="width: 200px;"
/>
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
<Button size="small" @click="setQuickAuthorizationDays(7)">7天</Button>
<Button size="small" @click="setQuickAuthorizationDays(30)">30天</Button>
<Button size="small" @click="setQuickAuthorizationDays(90)">90天</Button>
<Button size="small" @click="setQuickAuthorizationDays(180)">180天</Button>
<Button size="small" @click="setQuickAuthorizationDays(365)">365天</Button>
</div>
</div>
</FormItem>
<FormItem label="过期时间" v-if="authorizationModal.formData.authorization_date && authorizationModal.formData.authorization_days">
<span :style="{ color: getExpireDateColor(authorizationModal.formData) }">
{{ getExpireDate(authorizationModal.formData) }}
</span>
</FormItem>
</Form>
</Modal>
<!-- 简历详情弹窗 - 使用 ResumeInfoDetail 组件 -->
<ResumeInfoDetail ref="resumeDetail" @on-close="handleResumeDetailClose" />
<!-- 原简历详情弹窗(备份暂时保留) -->
<Modal v-model="resumeModal.visible" :title="resumeModal.title" width="900" :footer-hide="true" v-if="false">
<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'
import PlaAccountEdit from './pla_account_edit.vue'
import ResumeInfoDetail from './resume_info_detail.vue'
export default {
components: {
ResumeInfoDetail,
PlaAccountEdit
},
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
},
authorizationModal: {
visible: false,
formData: {
id: null,
accountName: '',
sn_code: '',
authorization_date: null,
authorization_days: 0
}
},
gridOption: {
param: {
seachOption: {
key: 'name',
value: '',
platform_type: null,
is_online: null,
is_online: null
},
pageOption: {
page: 1,
pageSize: 20
}
},
data: [],
rules: rules
},
// 新增时只显示必填字段
addColumns: [
{ title: '账户名', key: 'name', type: 'text', required: true },
{ title: '设备SN码', key: 'sn_code', type: 'text', required: true },
{
title: '平台', key: 'platform_type', com: 'Select', required: true, source: [
{ key: 'boss', value: 'Boss直聘' },
{ key: 'liepin', value: '猎聘' }
]
},
{ title: '登录名', key: 'login_name', com: 'Input', required: true },
{ title: '密码', key: 'pwd', com: 'Password', required: true },
],
listColumns: [
{ title: '账户名', key: 'name', minWidth: 120 },
{ title: '设备SN码', key: 'sn_code', minWidth: 150 },
{
title: '平台',
key: 'platform_type',
minWidth: 100,
render: (h, params) => {
const platformMap = {
'boss': { text: 'Boss直聘', color: 'blue' },
'liepin': { 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: 'is_online',
minWidth: 90,
render: (h, params) => {
return h('Tag', {
props: { color: params.row.is_online ? 'success' : 'default' }
}, params.row.is_online ? '在线' : '离线')
}
},
{
title: '自动投递',
key: 'auto_deliver',
minWidth: 90,
render: (h, params) => {
return h('Tag', {
props: { color: params.row.auto_deliver ? 'success' : 'default' }
}, params.row.auto_deliver ? '开启' : '关闭')
}
},
{
title: '自动沟通',
key: 'auto_chat',
minWidth: 90,
render: (h, params) => {
return h('Tag', {
props: { color: params.row.auto_chat ? 'success' : 'default' }
}, params.row.auto_chat ? '开启' : '关闭')
}
},
{
title: '剩余天数',
key: 'remaining_days',
minWidth: 100,
render: (h, params) => {
const remainingDays = params.row.remaining_days || 0
let color = 'success'
if (remainingDays <= 0) {
color = 'error'
} else if (remainingDays <= 7) {
color = 'warning'
}
return h('Tag', {
props: { color: color }
}, remainingDays > 0 ? `${remainingDays}` : '已过期')
}
},
{
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: 'action',
width: 500,
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: 'warning',
click: () => {
this.showAuthorizationModal(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)
},
}
]
}
},
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() {
// 新增使用简单的 editModal只包含必填字段
this.$refs.editModal.showModal()
},
showEditWarp(row) {
this.$refs.accountEdit.showEdit(row)
},
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() {
// 职位类型选项已移到编辑组件中加载
},
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, isEdit } = {}) {
try {
// 如果是新增(来自 editModaldata 只包含必填字段,直接保存
if (data && !data.id && !isEdit) {
await plaAccountServer.add(data)
this.$Message.success('保存成功!')
}
// 编辑时由 FloatPanel 组件PlaAccountEdit处理保存这里只刷新列表
// 刷新列表,保持当前页码
this.query(this.gridOption.param.pageOption.page || 1)
} catch (error) {
console.error('保存失败:', error)
// 优先从 error.response.data.message 获取,然后是 error.message
const errorMsg = error.response?.data?.message || error.message || '请稍后重试'
this.$Message.error('保存失败:' + errorMsg)
}
},
// 显示账号详情
showAccountDetails(row) {
this.$router.push({
path: '/pla_account/pla_account_detail',
query: { id: row.id }
})
},
// 查看简历
async showResume(row) {
// 显示加载提示
const loadingMsg = this.$Message.loading({
content: '正在加载简历数据...',
duration: 0
})
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}`)
loadingMsg()
if (res.code === 0 && res.data && res.data.resumeId) {
// 使用 ResumeInfoDetail 组件显示简历
this.$refs.resumeDetail.show(res.data.resumeId)
} else {
this.$Message.warning(res.message || '未找到简历数据')
}
} catch (error) {
loadingMsg()
console.error('获取简历失败:', error)
this.$Message.error('获取简历失败:' + (error.message || '请稍后重试'))
}
},
// 关闭简历详情
handleResumeDetailClose() {
// 可以在这里添加关闭后的逻辑
},
// 解析技能标签
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 []
}
},
// 格式化日期
formatDate(date) {
if (!date) return '-'
const d = new Date(date)
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
// 显示授权设置弹窗
showAuthorizationModal(row) {
this.authorizationModal.formData = {
id: row.id,
accountName: row.name,
sn_code: row.sn_code,
// 如果有授权日期则使用,否则默认使用当天
authorization_date: row.authorization_date ? new Date(row.authorization_date) : new Date(),
authorization_days: row.authorization_days || 0
}
this.authorizationModal.visible = true
},
// 设置快捷授权天数
setQuickAuthorizationDays(days) {
this.authorizationModal.formData.authorization_days = days
// 如果没有设置授权日期,自动设置为当前日期
if (!this.authorizationModal.formData.authorization_date) {
this.authorizationModal.formData.authorization_date = new Date()
}
},
// 保存授权信息
async handleSaveAuthorization() {
if (!this.authorizationModal.formData.id) {
this.$Message.error('账号ID不能为空')
return
}
if (!this.authorizationModal.formData.authorization_days || this.authorizationModal.formData.authorization_days <= 0) {
this.$Message.error('请输入授权天数')
return
}
try {
const param = {
id: this.authorizationModal.formData.id,
authorization_days: this.authorizationModal.formData.authorization_days
}
// 如果设置了授权日期,添加到参数中
if (this.authorizationModal.formData.authorization_date) {
param.authorization_date = this.authorizationModal.formData.authorization_date
}
await plaAccountServer.updateAuthorization(param)
this.$Message.success('授权设置成功!')
this.authorizationModal.visible = false
// 刷新列表
this.query(this.gridOption.param.pageOption.page || 1)
} catch (error) {
console.error('设置授权失败:', error)
this.$Message.error('设置授权失败:' + (error.message || '请稍后重试'))
}
},
// 取消授权设置
handleCancelAuthorization() {
this.authorizationModal.visible = false
this.authorizationModal.formData = {
id: null,
accountName: '',
sn_code: '',
authorization_date: null,
authorization_days: 0
}
},
// 获取过期时间
getExpireDate(formData) {
if (!formData.authorization_date || !formData.authorization_days) {
return '未授权'
}
const authDate = new Date(formData.authorization_date)
const expireDate = new Date(authDate.getTime() + formData.authorization_days * 24 * 60 * 60 * 1000)
const year = expireDate.getFullYear()
const month = String(expireDate.getMonth() + 1).padStart(2, '0')
const day = String(expireDate.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
// 获取过期时间颜色
getExpireDateColor(formData) {
if (!formData.authorization_date || !formData.authorization_days) {
return '#999'
}
const authDate = new Date(formData.authorization_date)
const expireDate = new Date(authDate.getTime() + formData.authorization_days * 24 * 60 * 60 * 1000)
const now = new Date()
const remaining = Math.ceil((expireDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000))
if (remaining <= 0) {
return '#ed4014'
} else if (remaining <= 7) {
return '#ff9900'
} else {
return '#515a6e'
}
},
// 解析工作经历
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)
// 优先从 error.response.data.message 获取,然后是 error.message
const errorMsg = error.response?.data?.message || error.message || '请稍后重试'
this.$Message.error(errorMsg)
}
}
})
},
// 批量解析位置
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)
// 优先从 error.response.data.message 获取,然后是 error.message
const errorMsg = error.response?.data?.message || error.message || '请稍后重试'
this.$Message.error(errorMsg)
} 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>