This commit is contained in:
424
src/views/settings/wecom/members/index.vue
Normal file
424
src/views/settings/wecom/members/index.vue
Normal file
@@ -0,0 +1,424 @@
|
||||
<template>
|
||||
<div class="wecom-members-page">
|
||||
<ElCard shadow="never">
|
||||
<template #header>
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<div class="page-title">企微成员管理</div>
|
||||
<div class="page-description"
|
||||
>同步应用可见成员,设置默认审批发起人,并完成平台账号绑定。</div
|
||||
>
|
||||
</div>
|
||||
<ElButton @click="goToApplications">返回应用列表</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElForm inline class="search-form" @submit.prevent>
|
||||
<ElFormItem label="企微应用">
|
||||
<ElSelect
|
||||
v-model="selectedApplicationId"
|
||||
filterable
|
||||
class="application-select"
|
||||
placeholder="请选择企微应用"
|
||||
@change="handleApplicationChange"
|
||||
>
|
||||
<ElOption
|
||||
v-for="application in applications"
|
||||
:key="application.id"
|
||||
:label="application.name"
|
||||
:value="application.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="成员搜索">
|
||||
<ElInput
|
||||
v-model="keyword"
|
||||
clearable
|
||||
placeholder="姓名"
|
||||
class="keyword-input"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton type="primary" @click="handleSearch">查询</ElButton>
|
||||
<ElButton @click="handleReset">重置</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="selected-application">
|
||||
当前应用:<span>{{ selectedApplication?.name || '未选择' }}</span>
|
||||
<ElTag v-if="selectedApplication?.default_creator_name" type="success" size="small">
|
||||
默认发起人:{{ selectedApplication.default_creator_name }}
|
||||
</ElTag>
|
||||
</div>
|
||||
<ElButton
|
||||
v-permission="JULY_PERMISSIONS.wecom.member"
|
||||
:disabled="!selectedApplicationId"
|
||||
:loading="syncing"
|
||||
@click="syncMembers"
|
||||
>
|
||||
同步成员
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<ElTable
|
||||
v-loading="loading"
|
||||
:data="members"
|
||||
row-key="userid"
|
||||
highlight-current-row
|
||||
border
|
||||
@row-click="selectMember"
|
||||
>
|
||||
<ElTableColumn label="选择" width="70" align="center">
|
||||
<template #default="scope">
|
||||
<ElRadio v-model="selectedUserid" :label="scope.row.userid">
|
||||
<span class="sr-only">选择 {{ scope.row.name }}</span>
|
||||
</ElRadio>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="name" label="成员姓名" min-width="150" />
|
||||
<ElTableColumn prop="synced_at" label="同步时间" min-width="180" />
|
||||
<ElTableColumn label="操作" width="230" fixed="right">
|
||||
<template #default="scope">
|
||||
<ElButton
|
||||
v-permission="JULY_PERMISSIONS.wecom.member"
|
||||
link
|
||||
type="primary"
|
||||
@click.stop="setDefaultCreator(scope.row)"
|
||||
>
|
||||
设为默认发起人
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-permission="JULY_PERMISSIONS.wecom.binding"
|
||||
link
|
||||
@click.stop="selectMember(scope.row)"
|
||||
>
|
||||
绑定账号
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
|
||||
<div class="pagination-wrapper">
|
||||
<ElPagination
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.page_size"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="loadMembers"
|
||||
/>
|
||||
</div>
|
||||
</ElCard>
|
||||
|
||||
<ElCard shadow="never" class="operation-card">
|
||||
<template #header>当前成员操作</template>
|
||||
<ElAlert
|
||||
v-if="selectedMember"
|
||||
:title="`已选择:${selectedMember.name}`"
|
||||
type="info"
|
||||
:closable="false"
|
||||
/>
|
||||
<ElEmpty v-else description="请先在上方列表选择成员" :image-size="70" />
|
||||
|
||||
<div class="operation-row">
|
||||
<span class="operation-label">绑定平台账号</span>
|
||||
<ElInputNumber
|
||||
v-model="bindingAccountId"
|
||||
:min="1"
|
||||
:disabled="!selectedMember"
|
||||
controls-position="right"
|
||||
placeholder="账号 ID"
|
||||
/>
|
||||
<ElButton
|
||||
v-permission="JULY_PERMISSIONS.wecom.binding"
|
||||
type="primary"
|
||||
:disabled="!selectedMember || !bindingAccountId"
|
||||
:loading="binding"
|
||||
@click="bindAccount"
|
||||
>
|
||||
绑定账号
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-permission="JULY_PERMISSIONS.wecom.member"
|
||||
:disabled="!selectedMember"
|
||||
:loading="savingDefault"
|
||||
@click="saveSelectedDefaultCreator"
|
||||
>
|
||||
保存为默认发起人
|
||||
</ElButton>
|
||||
</div>
|
||||
</ElCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { AccountService, WecomService } from '@/api/modules'
|
||||
import { JULY_PERMISSIONS } from '@/config/constants'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
import type { WecomApplication, WecomMember } from '@/types/api'
|
||||
|
||||
defineOptions({ name: 'WecomMembers' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const syncing = ref(false)
|
||||
const savingDefault = ref(false)
|
||||
const binding = ref(false)
|
||||
const applications = ref<WecomApplication[]>([])
|
||||
const members = ref<WecomMember[]>([])
|
||||
const selectedApplicationId = ref<number>()
|
||||
const selectedUserid = ref('')
|
||||
const bindingAccountId = ref<number>()
|
||||
const keyword = ref('')
|
||||
const pagination = reactive({ page: 1, page_size: 20, total: 0 })
|
||||
|
||||
const selectedApplication = computed(() =>
|
||||
applications.value.find((application) => application.id === selectedApplicationId.value)
|
||||
)
|
||||
const selectedMember = computed(() =>
|
||||
members.value.find((member) => member.userid === selectedUserid.value)
|
||||
)
|
||||
|
||||
const loadApplications = async () => {
|
||||
const response = await WecomService.getApplications({ page: 1, page_size: 100 })
|
||||
if (response.code !== 0) return
|
||||
applications.value = response.data.items || []
|
||||
const queryApplicationId = Number(route.query.application_id)
|
||||
selectedApplicationId.value =
|
||||
applications.value.find((application) => application.id === queryApplicationId)?.id ||
|
||||
applications.value[0]?.id
|
||||
}
|
||||
|
||||
const loadMembers = async () => {
|
||||
if (!selectedApplicationId.value) {
|
||||
members.value = []
|
||||
pagination.total = 0
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await WecomService.getMembers(selectedApplicationId.value, {
|
||||
page: pagination.page,
|
||||
page_size: pagination.page_size,
|
||||
keyword: keyword.value.trim() || undefined
|
||||
})
|
||||
if (response.code === 0) {
|
||||
members.value = response.data.items || []
|
||||
pagination.total = response.data.total || 0
|
||||
if (!members.value.some((member) => member.userid === selectedUserid.value)) {
|
||||
selectedUserid.value = ''
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleApplicationChange = () => {
|
||||
pagination.page = 1
|
||||
selectedUserid.value = ''
|
||||
void loadMembers()
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1
|
||||
void loadMembers()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
keyword.value = ''
|
||||
pagination.page = 1
|
||||
void loadMembers()
|
||||
}
|
||||
|
||||
const handleSizeChange = (size: number) => {
|
||||
pagination.page_size = size
|
||||
pagination.page = 1
|
||||
void loadMembers()
|
||||
}
|
||||
|
||||
const selectMember = (member: WecomMember) => {
|
||||
selectedUserid.value = member.userid
|
||||
}
|
||||
|
||||
const syncMembers = async () => {
|
||||
if (!selectedApplicationId.value) return
|
||||
syncing.value = true
|
||||
try {
|
||||
const response = await WecomService.syncMembers(selectedApplicationId.value)
|
||||
if (response.code === 0) {
|
||||
ElMessage.success(`已同步 ${response.data.synced_count} 名成员`)
|
||||
await loadMembers()
|
||||
}
|
||||
} finally {
|
||||
syncing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const setDefaultCreator = async (member: WecomMember) => {
|
||||
if (!selectedApplicationId.value) return
|
||||
selectedUserid.value = member.userid
|
||||
await saveSelectedDefaultCreator()
|
||||
}
|
||||
|
||||
const saveSelectedDefaultCreator = async () => {
|
||||
if (!selectedApplicationId.value || !selectedMember.value) {
|
||||
ElMessage.warning('请选择要设置的成员')
|
||||
return
|
||||
}
|
||||
savingDefault.value = true
|
||||
try {
|
||||
const response = await WecomService.setDefaultCreator(
|
||||
selectedApplicationId.value,
|
||||
selectedMember.value.userid
|
||||
)
|
||||
if (response.code === 0) {
|
||||
ElMessage.success('默认发起人已保存')
|
||||
const application = selectedApplication.value
|
||||
if (application) {
|
||||
application.default_creator_userid = selectedMember.value.userid
|
||||
application.default_creator_name = selectedMember.value.name
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
savingDefault.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const bindAccount = async () => {
|
||||
if (!selectedApplicationId.value || !selectedMember.value || !bindingAccountId.value) {
|
||||
ElMessage.warning('请选择成员并输入账号 ID')
|
||||
return
|
||||
}
|
||||
binding.value = true
|
||||
try {
|
||||
const response = await AccountService.bindWecom(bindingAccountId.value, {
|
||||
application_id: selectedApplicationId.value,
|
||||
userid: selectedMember.value.userid
|
||||
})
|
||||
if (response.code === 0) ElMessage.success('账号企微绑定成功')
|
||||
} finally {
|
||||
binding.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const goToApplications = () => void router.push(RoutesAlias.WecomApplications)
|
||||
|
||||
onMounted(async () => {
|
||||
await loadApplications()
|
||||
await loadMembers()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wecom-members-page {
|
||||
.page-header,
|
||||
.toolbar,
|
||||
.operation-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-header,
|
||||
.toolbar {
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
margin-top: 6px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-top: 4px;
|
||||
padding: 14px 16px 0;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.application-select {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.keyword-input {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
margin: 20px 0 12px;
|
||||
|
||||
.selected-application {
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
span {
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.operation-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.operation-row {
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.operation-label {
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-header,
|
||||
.toolbar {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.application-select,
|
||||
.keyword-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user