【招生用户端】 新增# 子账号管理:1、对接编辑、删除接口;2、组织状态设为停用时不可新增账号

main
kaeery 2025-02-27 00:27:52 +08:00
parent ebbe48d31c
commit 314c71a520
5 changed files with 108 additions and 30 deletions

View File

@ -16,3 +16,11 @@ export function subAccountList(params: Record<string, any>) {
export function subAccountNumber() { export function subAccountNumber() {
return request.get({ url: '/user/validUserCount' }) return request.get({ url: '/user/validUserCount' })
} }
// 子账号详情
export function subAccountDetail(params: Record<string, any>) {
return request.get({ url: '/user/detail', params })
}
// 删除子账号
export function subAccountDelete(params: Record<string, any>) {
return request.post({ url: '/user/sonDel', params })
}

View File

@ -66,9 +66,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { organzationLists } from '@/api/account_center/organization' import { organzationLists } from '@/api/account_center/organization'
import { postLists } from '@/api/account_center/postion' import { postLists } from '@/api/account_center/postion'
import { subAccountAdd } from '@/api/account_center/sub_account' import { subAccountAdd, subAccountDetail, subAccountEdit } from '@/api/account_center/sub_account'
import ProDialog, { type IParams } from '@/components/ProDialog/index.vue' import ProDialog, { type IParams } from '@/components/ProDialog/index.vue'
import { DataFlowEnum } from '@/enums' import { DataFlowEnum, StatusEnum } from '@/enums'
import { validateContact } from '@/utils/validate' import { validateContact } from '@/utils/validate'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
@ -83,7 +83,7 @@ export interface IAccount {
} }
const proDialogRef = ref<InstanceType<typeof ProDialog>>() const proDialogRef = ref<InstanceType<typeof ProDialog>>()
const emit = defineEmits(['fetchTableList']) const emit = defineEmits(['confirmAfter'])
const data = ref([]) const data = ref([])
const defaultProps = { const defaultProps = {
label: 'name', // label: 'name', //
@ -102,6 +102,7 @@ const accountStatusOptions = ref([
]) ])
const channelTooltips = ['选择默认组织则数据流向到所属组织下的所有招生老师;', '选择组织指定则可选择将数据流向到所属组织下的指定招生老师;'] const channelTooltips = ['选择默认组织则数据流向到所属组织下的所有招生老师;', '选择组织指定则可选择将数据流向到所属组织下的指定招生老师;']
const form = ref({ const form = ref({
id: '',
organizationId: '', organizationId: '',
username: '', username: '',
mobile: '', mobile: '',
@ -110,17 +111,23 @@ const form = ref({
status: 1, status: 1,
teacher: '' teacher: ''
}) })
const fetchPostionData = async () => { const fetchPostionData = async (callback?: (params: []) => void) => {
try { try {
const result = await postLists() const result = await postLists()
positionOptions.value = result.lists positionOptions.value = result.lists
callback && callback(result.lists)
} catch (error) {} } catch (error) {}
} }
const fetchOrganizationData = async () => { const fetchOrganizationData = async (organizationId: number) => {
try { try {
const result = await organzationLists() const result = await organzationLists()
setDsiabled(result) setDsiabled(result)
data.value = result data.value = result
const targetNode = findNodeById(data.value, organizationId)
if (targetNode) {
const { id, disabled } = targetNode
form.value.organizationId = !disabled ? id : ''
}
} catch (error) {} } catch (error) {}
} }
// //
@ -128,15 +135,48 @@ const setDsiabled = (nodes: any[]) => {
nodes.forEach(node => { nodes.forEach(node => {
const ancestorArray = node.ancestors ? node.ancestors.split(',') : [] const ancestorArray = node.ancestors ? node.ancestors.split(',') : []
const level = ancestorArray.length - 1 const level = ancestorArray.length - 1
node.disabled = level < 2 node.disabled = level < 2 || node.status == StatusEnum.Stop
if (node.children && node.children.length > 0) { if (node.children && node.children.length > 0) {
setDsiabled(node.children) setDsiabled(node.children)
} }
}) })
} }
const openDialog = (params: IParams) => { const fetchDetail = async (data: any) => {
const { id } = data
try {
const result = await subAccountDetail({ id })
setFormData(result)
} catch (error) {}
}
const setFormData = result => {
for (const key in result) {
if (Object.prototype.hasOwnProperty.call(form.value, key)) {
form.value[key] = result[key]
}
if (key == 'postIds') {
form.value.postIds = result[key].split(',').map((item: string) => Number(item))
}
}
}
// id
const findNodeById = (nodes: any[], id: number): any | undefined => {
for (const node of nodes) {
if (node.id === id) {
return node
}
if (node.children && node.children.length > 0) {
const foundNode = findNodeById(node.children, id)
if (foundNode) {
return foundNode
}
}
}
return undefined
}
const openDialog = async (params: IParams) => {
fetchPostionData() fetchPostionData()
fetchOrganizationData() fetchOrganizationData(params.data.organizationId)
if (params.data.id) fetchDetail(params.data)
proDialogRef.value?.openDialog(params) proDialogRef.value?.openDialog(params)
} }
const handleCancel = (callback: () => void) => { const handleCancel = (callback: () => void) => {
@ -161,9 +201,10 @@ const handleConfirm = (callback: () => void) => {
...form.value, ...form.value,
postIds: form.value.postIds.join(',') postIds: form.value.postIds.join(',')
} }
await subAccountAdd(data) const api = form.value.id ? subAccountEdit : subAccountAdd
await api(data)
callback() callback()
emit('fetchTableList') emit('confirmAfter')
resetForm() resetForm()
} catch (error) {} } catch (error) {}
}) })
@ -172,7 +213,8 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
defineExpose({ defineExpose({
openDialog openDialog,
fetchPostionData
}) })
</script> </script>

View File

@ -3,7 +3,7 @@
<account-number :accoutnInfo="accoutnInfo" /> <account-number :accoutnInfo="accoutnInfo" />
<div class="flex flex-1 bg-white overflow-x-auto"> <div class="flex flex-1 bg-white overflow-x-auto">
<organization @set-selected-node="setSelectedNode" :curSelectedNode="selectedNode" @fetch-table-list="fetchTableList" /> <organization @set-selected-node="setSelectedNode" :curSelectedNode="selectedNode" @fetch-table-list="fetchTableList" />
<account-list ref="accountListRef" :curOrganization="selectedNode" /> <account-list ref="accountListRef" :curOrganization="selectedNode" @refresh-sub-account-number="fetchSubAccountNumber" />
</div> </div>
</div> </div>
</template> </template>
@ -14,6 +14,8 @@ import organization from './modules/organization.vue'
import accountList from './modules/account-list.vue' import accountList from './modules/account-list.vue'
import type { Tree } from './components/organization/organization-tree.vue' import type { Tree } from './components/organization/organization-tree.vue'
import { subAccountNumber } from '@/api/account_center/sub_account' import { subAccountNumber } from '@/api/account_center/sub_account'
import { StatusEnum } from '@/enums'
import feedback from '@/utils/feedback'
const selectedNode = ref() const selectedNode = ref()
const setSelectedNode = (data: Tree) => { const setSelectedNode = (data: Tree) => {
@ -23,6 +25,7 @@ watch(
() => selectedNode.value, () => selectedNode.value,
val => { val => {
if (val) { if (val) {
if (val.status == StatusEnum.Stop) feedback.msgError('当前组织状态为停用,请先启用')
fetchTableList() fetchTableList()
} }
} }

View File

@ -5,7 +5,7 @@
</div> </div>
<div class="px-[20px] py-[16px] flex justify-between"> <div class="px-[20px] py-[16px] flex justify-between">
<el-space> <el-space>
<el-button type="primary" :icon="Plus" @click="showAccountDialog"></el-button> <el-button type="primary" :icon="Plus" :disabled="isDisabledAdd" @click="showAccountDialog"></el-button>
<el-button type="danger" plain :icon="Delete" :disabled="isDisabled" @click="handleBatchDelete"></el-button> <el-button type="danger" plain :icon="Delete" :disabled="isDisabled" @click="handleBatchDelete"></el-button>
</el-space> </el-space>
<el-space> <el-space>
@ -16,7 +16,10 @@
</el-select> </el-select>
</template> </template>
</el-input> </el-input>
<el-select placeholder="请选择账号状态" v-model="searchForm.accountStatus" clearable @change="handleSelectChange"> <el-select placeholder="请选择岗位名称" v-model="searchForm.postId" clearable @change="handleSelectChange">
<el-option v-for="option in positionOptions" :key="option.id" :label="option.name" :value="option.id" />
</el-select>
<el-select placeholder="请选择账号状态" v-model="searchForm.status" clearable @change="handleSelectChange">
<el-option v-for="option in accountStatusOptions" :key="option.value" :label="option.label" :value="option.value" /> <el-option v-for="option in accountStatusOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-space> </el-space>
@ -44,7 +47,7 @@
</template> </template>
</ProTable> </ProTable>
</div> </div>
<account-dialog ref="accountDialogRef" @fetch-table-list="fetchTableList" /> <account-dialog ref="accountDialogRef" @confirm-after="confirmAfter" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -53,7 +56,8 @@ import feedback from '@/utils/feedback'
import { Plus, Delete } from '@element-plus/icons-vue' import { Plus, Delete } from '@element-plus/icons-vue'
import accountDialog from '../components/account-list/account-dialog.vue' import accountDialog from '../components/account-list/account-dialog.vue'
import { useDebounceFn } from '@vueuse/core' import { useDebounceFn } from '@vueuse/core'
import { subAccountList } from '@/api/account_center/sub_account' import { subAccountDelete, subAccountList } from '@/api/account_center/sub_account'
import { StatusEnum } from '@/enums'
const props = defineProps({ const props = defineProps({
curOrganization: { curOrganization: {
@ -61,16 +65,17 @@ const props = defineProps({
default: () => ({}) default: () => ({})
} }
}) })
const selectKey = ref('accountName') const emit = defineEmits(['refreshSubAccountNumber'])
const selectKey = ref('username')
const searchOptions = shallowRef([ const searchOptions = shallowRef([
{ field: 'accountName', label: '账号名称' }, { field: 'username', label: '账号名称' },
{ field: 'mobile', label: '联系电话' }, { field: 'mobile', label: '联系电话' }
{ field: 'positionName', label: '岗位名称' }
]) ])
const placeholder = computed(() => searchOptions.value.find(item => item.field == selectKey.value)?.label) const placeholder = computed(() => searchOptions.value.find(item => item.field == selectKey.value)?.label)
const accountStatusOptions = ref([ const accountStatusOptions = ref([
{ label: '启用', value: 1 }, { label: '启用', value: StatusEnum.Normal },
{ label: '停用', value: 2 } { label: '停用', value: StatusEnum.Stop }
]) ])
const loading = ref(false) const loading = ref(false)
const proTableRef = ref() const proTableRef = ref()
@ -87,6 +92,7 @@ const columns = reactive([
const isGroupLeader = computed(() => (groupLeader: number) => groupLeader == 1) const isGroupLeader = computed(() => (groupLeader: number) => groupLeader == 1)
const btnText = computed(() => (groupLeader: number) => isGroupLeader.value(groupLeader) ? '取消设为组长' : '设置组长') const btnText = computed(() => (groupLeader: number) => isGroupLeader.value(groupLeader) ? '取消设为组长' : '设置组长')
const isDisabledAdd = computed(() => props.curOrganization.status == StatusEnum.Stop)
const fetchTableList = async (nodeId?: number) => { const fetchTableList = async (nodeId?: number) => {
loading.value = true loading.value = true
try { try {
@ -121,13 +127,22 @@ const handleDelete = async row => {
const deleteCommon = async (row?: any) => { const deleteCommon = async (row?: any) => {
if (row) proTableRef.value.tableRef.toggleRowSelection(row, true) if (row) proTableRef.value.tableRef.toggleRowSelection(row, true)
const ids = proTableRef.value.selectedListIds const ids = proTableRef.value.selectedListIds
const message = '是否要删除选中的员工?' const message = '是否要删除选中的账号?'
const flag = await useHandleData(message) const flag = await useHandleData(message)
if (flag) { if (flag) {
console.log(ids) try {
await subAccountDelete({ ids })
feedback.msgSuccess('删除成功')
fetchTableList()
emit('refreshSubAccountNumber')
} catch (error) {}
} }
proTableRef.value.tableRef.clearSelection() proTableRef.value.tableRef.clearSelection()
} }
const confirmAfter = () => {
fetchTableList()
emit('refreshSubAccountNumber')
}
const handleEdit = row => { const handleEdit = row => {
showAccountDialog(row) showAccountDialog(row)
} }
@ -139,24 +154,34 @@ const showAccountDialog = (row?: any) => {
accountDialogRef.value?.openDialog({ accountDialogRef.value?.openDialog({
title: row?.id ? '编辑账号' : '新建账号', title: row?.id ? '编辑账号' : '新建账号',
width: 400, width: 400,
data: {} data: {
id: row?.id ?? '',
organizationId: props.curOrganization.id
}
}) })
} }
const searchForm = ref({ const searchForm = ref({
keyword: '', keyword: '',
accountStatus: '' postId: '',
status: ''
}) })
const handleInputChange = useDebounceFn(() => { const handleInputChange = useDebounceFn(() => {
const formMap: Record<string, string> = { const formMap: Record<string, string> = {
accountName: '', username: '',
mobile: '', mobile: ''
position: ''
} }
formMap[selectKey.value] = searchForm.value.keyword formMap[selectKey.value] = searchForm.value.keyword
}, 500) }, 500)
const handleSelectChange = () => { const handleSelectChange = () => {
console.log('handleSelectChange') console.log('handleSelectChange')
} }
const positionOptions = ref<any[]>([])
onMounted(() => {
accountDialogRef.value?.fetchPostionData(lists => {
positionOptions.value = lists ?? []
})
})
watch( watch(
() => selectKey.value, () => selectKey.value,
(newVal, oldVal) => { (newVal, oldVal) => {

View File

@ -23,6 +23,6 @@ const props = defineProps({
default: () => ({}) default: () => ({})
} }
}) })
const parseUnuseNumber = computed(() => props.accoutnInfo.quota - props.accoutnInfo.quantity) const parseUnuseNumber = computed(() => props.accoutnInfo?.quota - props.accoutnInfo?.quantity)
</script> </script>
<style scoped></style> <style scoped></style>