feat: 新增客户经理管理(查询/新增/修改/批量删除)

main
halo 2026-07-09 15:53:25 +08:00
parent e02c264381
commit 26792e73f0
3 changed files with 271 additions and 0 deletions

View File

@ -1,6 +1,7 @@
import express from 'express'
import { orgs } from '../data/orgs.js'
import { channels, nextChannelId } from '../data/channels.js'
import { managers, nextManagerId } from '../data/managers.js'
import { requireAuth } from '../middleware/requireAuth.js'
const router = express.Router()
@ -104,4 +105,84 @@ router.delete('/channel/batch', (req, res) => {
res.json({ code: 200, msg: 'ok', data: { successIds, failed } })
})
// ---------------- 客户经理管理 ----------------
router.get('/manager/list', (req, res) => {
const { orgCode, orgName: orgNameQuery, name, jobNumber, page = 1, pageSize = 10 } = req.query || {}
let list = managers
if (orgCode) list = list.filter((m) => m.orgCode.includes(orgCode))
if (orgNameQuery) list = list.filter((m) => m.orgName.includes(orgNameQuery))
if (name) list = list.filter((m) => m.name.includes(name))
if (jobNumber) list = list.filter((m) => m.jobNumber.includes(jobNumber))
const total = list.length
const start = (Number(page) - 1) * Number(pageSize)
const pageList = list.slice(start, start + Number(pageSize))
res.json({ code: 200, msg: 'ok', data: { list: pageList, total } })
})
router.post('/manager', (req, res) => {
const { orgCode, orgName: managerOrgName, jobNumber, name } = req.body || {}
if (!orgCode) return res.json({ code: 40030, msg: '请输入机构号', data: null })
if (!managerOrgName) return res.json({ code: 40031, msg: '请输入机构名称', data: null })
if (!jobNumber) return res.json({ code: 40032, msg: '请输入工号', data: null })
if (!name) return res.json({ code: 40033, msg: '请输入客户经理姓名', data: null })
if (managers.some((m) => m.jobNumber === jobNumber)) {
return res.json({ code: 40034, msg: '工号已存在', data: null })
}
const manager = {
id: nextManagerId(),
orgCode,
orgName: managerOrgName,
jobNumber,
name,
createdAt: nowStr()
}
managers.push(manager)
res.json({ code: 200, msg: 'ok', data: manager })
})
router.put('/manager/:id', (req, res) => {
const { id } = req.params
const { orgCode, orgName: managerOrgName, jobNumber, name } = req.body || {}
const manager = managers.find((m) => m.id === id)
if (!manager) return res.json({ code: 40404, msg: '客户经理不存在', data: null })
if (!orgCode) return res.json({ code: 40030, msg: '请输入机构号', data: null })
if (!managerOrgName) return res.json({ code: 40031, msg: '请输入机构名称', data: null })
if (!jobNumber) return res.json({ code: 40032, msg: '请输入工号', data: null })
if (!name) return res.json({ code: 40033, msg: '请输入客户经理姓名', data: null })
if (managers.some((m) => m.jobNumber === jobNumber && m.id !== id)) {
return res.json({ code: 40034, msg: '工号已存在', data: null })
}
manager.orgCode = orgCode
manager.orgName = managerOrgName
manager.jobNumber = jobNumber
manager.name = name
res.json({ code: 200, msg: 'ok', data: manager })
})
router.delete('/manager/batch', (req, res) => {
const { ids } = req.body || {}
if (!Array.isArray(ids) || ids.length === 0) {
return res.json({ code: 40024, msg: '请选择要删除的客户经理', data: null })
}
const successIds = []
const failed = []
ids.forEach((id) => {
const manager = managers.find((m) => m.id === id)
if (!manager) {
failed.push({ id, name: id, reason: '客户经理不存在' })
return
}
successIds.push(id)
})
successIds.forEach((id) => {
const index = managers.findIndex((m) => m.id === id)
if (index !== -1) managers.splice(index, 1)
})
res.json({ code: 200, msg: 'ok', data: { successIds, failed } })
})
export default router

View File

@ -5,3 +5,9 @@ export const fetchChannelListApi = (params) => request.get('/base-config/channel
export const createChannelApi = (data) => request.post('/base-config/channel', data)
export const updateChannelApi = (id, data) => request.put(`/base-config/channel/${id}`, data)
export const batchDeleteChannelApi = (ids) => request.delete('/base-config/channel/batch', { data: { ids } })
// ---------------- 客户经理管理 ----------------
export const fetchManagerListApi = (params) => request.get('/base-config/manager/list', { params })
export const createManagerApi = (data) => request.post('/base-config/manager', data)
export const updateManagerApi = (id, data) => request.put(`/base-config/manager/${id}`, data)
export const batchDeleteManagerApi = (ids) => request.delete('/base-config/manager/batch', { data: { ids } })

View File

@ -0,0 +1,184 @@
<template>
<div class="manager-list-page">
<ProTable
ref="proTableRef"
v-model:selected-row-keys="selectedRowKeys"
:columns="columns"
:fetch-data="loadManagerList"
:initial-search="{ orgCode: '', orgName: '', name: '', jobNumber: '' }"
@search="handleSearchResult"
>
<template #search="{ form }">
<a-form-item label="机构号">
<a-input v-model:value="form.orgCode" placeholder="请输入机构号" allow-clear style="width: 140px" />
</a-form-item>
<a-form-item label="机构名称">
<a-input v-model:value="form.orgName" placeholder="请输入机构名称" allow-clear style="width: 160px" />
</a-form-item>
<a-form-item label="客户经理姓名">
<a-input v-model:value="form.name" placeholder="请输入客户经理姓名" allow-clear style="width: 160px" />
</a-form-item>
<a-form-item label="工号">
<a-input v-model:value="form.jobNumber" placeholder="请输入工号" allow-clear style="width: 140px" />
</a-form-item>
</template>
<template #actions>
<a-space>
<a-button v-permission="'manager:add'" type="primary" @click="openCreateModal">
<PlusOutlined /> 新增
</a-button>
<a-popconfirm
title="确定删除选中的客户经理吗?"
:disabled="selectedRowKeys.length === 0"
@confirm="handleBatchDelete"
>
<a-button v-permission="'manager:delete'" danger :disabled="selectedRowKeys.length === 0">
删除
</a-button>
</a-popconfirm>
</a-space>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'index'">{{ index + 1 }}</template>
<template v-else-if="column.dataIndex === 'action'">
<a v-permission="'manager:edit'" @click="openEditModal(record)"></a>
</template>
</template>
</ProTable>
<a-modal
v-model:open="modalOpen"
:title="modalMode === 'create' ? '新增客户经理' : '修改客户经理'"
:confirm-loading="submitting"
@ok="handleSubmit"
>
<a-form ref="formRef" layout="vertical" :model="form" :rules="rules">
<a-form-item label="机构号" name="orgCode">
<a-input v-model:value="form.orgCode" placeholder="请输入机构号" />
</a-form-item>
<a-form-item label="机构名称" name="orgName">
<a-input v-model:value="form.orgName" placeholder="请输入机构名称" />
</a-form-item>
<a-form-item label="工号" name="jobNumber">
<a-input v-model:value="form.jobNumber" placeholder="请输入工号" />
</a-form-item>
<a-form-item label="客户经理姓名" name="name">
<a-input v-model:value="form.name" placeholder="请输入客户经理姓名" />
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { message } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import ProTable from '@/components/ProTable.vue'
import {
fetchManagerListApi,
createManagerApi,
updateManagerApi,
batchDeleteManagerApi
} from '@/api/baseConfig'
import { showBatchDeleteResult } from '@/utils/batchDeleteResult'
const columns = [
{ title: '序号', dataIndex: 'index', width: 60 },
{ title: '机构号', dataIndex: 'orgCode' },
{ title: '机构名称', dataIndex: 'orgName' },
{ title: '工号', dataIndex: 'jobNumber' },
{ title: '客户经理姓名', dataIndex: 'name' },
{ title: '操作', dataIndex: 'action', width: 100 }
]
const proTableRef = ref(null)
const selectedRowKeys = ref([])
const lastTotal = ref(0)
async function loadManagerList(params) {
const res = await fetchManagerListApi(params)
if (res.data.code === 200) {
lastTotal.value = res.data.data.total
return res.data.data
}
lastTotal.value = 0
return { list: [], total: 0 }
}
function handleSearchResult() {
if (lastTotal.value === 0) {
message.warning('客户经理不存在')
}
}
async function handleBatchDelete() {
const res = await batchDeleteManagerApi(selectedRowKeys.value)
if (res.data.code === 200) {
showBatchDeleteResult(res.data.data)
proTableRef.value.reload()
}
}
// ---------------- / ----------------
const modalOpen = ref(false)
const modalMode = ref('create')
const submitting = ref(false)
const formRef = ref(null)
const form = reactive({ id: '', orgCode: '', orgName: '', jobNumber: '', name: '' })
const rules = {
orgCode: [{ required: true, message: '请输入机构号' }],
orgName: [{ required: true, message: '请输入机构名称' }],
jobNumber: [{ required: true, message: '请输入工号' }],
name: [{ required: true, message: '请输入客户经理姓名' }]
}
function openCreateModal() {
modalMode.value = 'create'
form.id = ''
form.orgCode = ''
form.orgName = ''
form.jobNumber = ''
form.name = ''
modalOpen.value = true
}
function openEditModal(record) {
modalMode.value = 'edit'
form.id = record.id
form.orgCode = record.orgCode
form.orgName = record.orgName
form.jobNumber = record.jobNumber
form.name = record.name
modalOpen.value = true
}
async function handleSubmit() {
try {
await formRef.value.validate()
} catch {
return
}
submitting.value = true
try {
const payload = {
orgCode: form.orgCode,
orgName: form.orgName,
jobNumber: form.jobNumber,
name: form.name
}
const res =
modalMode.value === 'create' ? await createManagerApi(payload) : await updateManagerApi(form.id, payload)
if (res.data.code === 200) {
message.success(modalMode.value === 'create' ? '新增成功' : '修改成功')
modalOpen.value = false
proTableRef.value.reload()
}
} finally {
submitting.value = false
}
}
</script>