feat: 新增核心企业管理(查询/新增/修改/批量删除)

main
halo 2026-07-09 15:48:44 +08:00
parent b647a8abf1
commit e02c264381
4 changed files with 290 additions and 0 deletions

View File

@ -3,6 +3,7 @@ import cors from 'cors'
import cookieParser from 'cookie-parser' import cookieParser from 'cookie-parser'
import authRoutes from './routes/auth.js' import authRoutes from './routes/auth.js'
import systemRoutes from './routes/system.js' import systemRoutes from './routes/system.js'
import baseConfigRoutes from './routes/baseConfig.js'
const app = express() const app = express()
app.use(cors({ origin: 'http://localhost:5173', credentials: true })) app.use(cors({ origin: 'http://localhost:5173', credentials: true }))
@ -11,6 +12,7 @@ app.use(cookieParser())
app.use('/auth', authRoutes) app.use('/auth', authRoutes)
app.use('/system', systemRoutes) app.use('/system', systemRoutes)
app.use('/base-config', baseConfigRoutes)
const PORT = 8888 const PORT = 8888
app.listen(PORT, () => { app.listen(PORT, () => {

View File

@ -0,0 +1,107 @@
import express from 'express'
import { orgs } from '../data/orgs.js'
import { channels, nextChannelId } from '../data/channels.js'
import { requireAuth } from '../middleware/requireAuth.js'
const router = express.Router()
router.use(requireAuth)
function orgName(orgId) {
return orgs.find((o) => o.id === orgId)?.name || ''
}
function nowStr() {
return new Date().toISOString().slice(0, 19).replace('T', ' ')
}
// ---------------- 核心企业管理 ----------------
router.get('/channel/list', (req, res) => {
const { orgId, channelCode, channelName, page = 1, pageSize = 10 } = req.query || {}
let list = channels
if (orgId) list = list.filter((c) => c.orgId === orgId)
if (channelCode) list = list.filter((c) => c.channelCode.includes(channelCode))
if (channelName) list = list.filter((c) => c.name.includes(channelName))
const total = list.length
const start = (Number(page) - 1) * Number(pageSize)
const pageList = list.slice(start, start + Number(pageSize)).map((c) => ({
id: c.id,
name: c.name,
channelCode: c.channelCode,
appCode: c.appCode,
motherAccountBank: c.motherAccountBank,
orgId: c.orgId,
orgName: orgName(c.orgId),
createdAt: c.createdAt
}))
res.json({ code: 200, msg: 'ok', data: { list: pageList, total } })
})
router.post('/channel', (req, res) => {
const { name, channelCode, appCode, motherAccountBank, orgId } = req.body || {}
if (!name) return res.json({ code: 40025, msg: '请输入渠道名称', data: null })
if (!channelCode) return res.json({ code: 40026, msg: '请输入渠道编号', data: null })
if (!appCode) return res.json({ code: 40027, msg: '请输入应用编号', data: null })
if (!motherAccountBank) return res.json({ code: 40028, msg: '请输入母户开户行', data: null })
if (!orgId || !orgs.some((o) => o.id === orgId)) {
return res.json({ code: 40029, msg: '请选择有效的所属机构', data: null })
}
const channel = {
id: nextChannelId(),
name,
channelCode,
appCode,
motherAccountBank,
orgId,
createdAt: nowStr()
}
channels.push(channel)
res.json({ code: 200, msg: 'ok', data: channel })
})
router.put('/channel/:id', (req, res) => {
const { id } = req.params
const { name, channelCode, appCode, motherAccountBank, orgId } = req.body || {}
const channel = channels.find((c) => c.id === id)
if (!channel) return res.json({ code: 40404, msg: '核心企业不存在', data: null })
if (!name) return res.json({ code: 40025, msg: '请输入渠道名称', data: null })
if (!channelCode) return res.json({ code: 40026, msg: '请输入渠道编号', data: null })
if (!appCode) return res.json({ code: 40027, msg: '请输入应用编号', data: null })
if (!motherAccountBank) return res.json({ code: 40028, msg: '请输入母户开户行', data: null })
if (!orgId || !orgs.some((o) => o.id === orgId)) {
return res.json({ code: 40029, msg: '请选择有效的所属机构', data: null })
}
channel.name = name
channel.channelCode = channelCode
channel.appCode = appCode
channel.motherAccountBank = motherAccountBank
channel.orgId = orgId
res.json({ code: 200, msg: 'ok', data: channel })
})
router.delete('/channel/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 channel = channels.find((c) => c.id === id)
if (!channel) {
failed.push({ id, name: id, reason: '核心企业不存在' })
return
}
successIds.push(id)
})
successIds.forEach((id) => {
const index = channels.findIndex((c) => c.id === id)
if (index !== -1) channels.splice(index, 1)
})
res.json({ code: 200, msg: 'ok', data: { successIds, failed } })
})
export default router

7
src/api/baseConfig.js Normal file
View File

@ -0,0 +1,7 @@
import request from './request'
// ---------------- 核心企业管理 ----------------
export const fetchChannelListApi = (params) => request.get('/base-config/channel/list', { params })
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 } })

View File

@ -0,0 +1,174 @@
<template>
<div class="channel-list-page">
<ProTable
ref="proTableRef"
v-model:selected-row-keys="selectedRowKeys"
:columns="columns"
:fetch-data="loadChannelList"
:initial-search="{ orgId: undefined, channelCode: '', channelName: '' }"
>
<template #search="{ form }">
<a-form-item label="所属机构">
<OrgTreeSelect v-model="form.orgId" placeholder="请选择所属机构" style="width: 200px" />
</a-form-item>
<a-form-item label="渠道编号">
<a-input v-model:value="form.channelCode" placeholder="请输入渠道编号" allow-clear style="width: 160px" />
</a-form-item>
<a-form-item label="渠道名称">
<a-input v-model:value="form.channelName" placeholder="请输入渠道名称" allow-clear style="width: 160px" />
</a-form-item>
</template>
<template #actions>
<a-space>
<a-button v-permission="'channel:add'" type="primary" @click="openCreateModal">
<PlusOutlined /> 新增
</a-button>
<a-popconfirm
title="确定删除选中的核心企业吗?"
:disabled="selectedRowKeys.length === 0"
@confirm="handleBatchDelete"
>
<a-button v-permission="'channel: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="'channel: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="name">
<a-input v-model:value="form.name" placeholder="请输入渠道名称" />
</a-form-item>
<a-form-item label="渠道编号" name="channelCode">
<a-input v-model:value="form.channelCode" placeholder="请输入渠道编号" />
</a-form-item>
<a-form-item label="应用编号" name="appCode">
<a-input v-model:value="form.appCode" placeholder="请输入应用编号" />
</a-form-item>
<a-form-item label="母户开户行" name="motherAccountBank">
<a-input v-model:value="form.motherAccountBank" placeholder="请输入母户开户行" />
</a-form-item>
<a-form-item label="所属机构" name="orgId">
<OrgTreeSelect v-model="form.orgId" 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 OrgTreeSelect from '@/components/OrgTreeSelect.vue'
import { fetchChannelListApi, createChannelApi, updateChannelApi, batchDeleteChannelApi } from '@/api/baseConfig'
import { showBatchDeleteResult } from '@/utils/batchDeleteResult'
const columns = [
{ title: '序号', dataIndex: 'index', width: 60 },
{ title: '渠道名称', dataIndex: 'name' },
{ title: '渠道编号', dataIndex: 'channelCode' },
{ title: '应用编号', dataIndex: 'appCode' },
{ title: '母户开户行', dataIndex: 'motherAccountBank' },
{ title: '所属机构', dataIndex: 'orgName' },
{ title: '创建时间', dataIndex: 'createdAt' },
{ title: '操作', dataIndex: 'action', width: 100 }
]
const proTableRef = ref(null)
const selectedRowKeys = ref([])
async function loadChannelList(params) {
const res = await fetchChannelListApi(params)
if (res.data.code === 200) return res.data.data
return { list: [], total: 0 }
}
async function handleBatchDelete() {
const res = await batchDeleteChannelApi(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: '', name: '', channelCode: '', appCode: '', motherAccountBank: '', orgId: undefined })
const rules = {
name: [{ required: true, message: '请输入渠道名称' }],
channelCode: [{ required: true, message: '请输入渠道编号' }],
appCode: [{ required: true, message: '请输入应用编号' }],
motherAccountBank: [{ required: true, message: '请输入母户开户行' }],
orgId: [{ required: true, message: '请选择所属机构' }]
}
function openCreateModal() {
modalMode.value = 'create'
form.id = ''
form.name = ''
form.channelCode = ''
form.appCode = ''
form.motherAccountBank = ''
form.orgId = undefined
modalOpen.value = true
}
function openEditModal(record) {
modalMode.value = 'edit'
form.id = record.id
form.name = record.name
form.channelCode = record.channelCode
form.appCode = record.appCode
form.motherAccountBank = record.motherAccountBank
form.orgId = record.orgId
modalOpen.value = true
}
async function handleSubmit() {
try {
await formRef.value.validate()
} catch {
return
}
submitting.value = true
try {
const payload = {
name: form.name,
channelCode: form.channelCode,
appCode: form.appCode,
motherAccountBank: form.motherAccountBank,
orgId: form.orgId
}
const res =
modalMode.value === 'create' ? await createChannelApi(payload) : await updateChannelApi(form.id, payload)
if (res.data.code === 200) {
message.success(modalMode.value === 'create' ? '新增成功' : '修改成功')
modalOpen.value = false
proTableRef.value.reload()
}
} finally {
submitting.value = false
}
}
</script>