mcwl-pc/app/pages/planetMember/index.vue

389 lines
11 KiB
Vue

<script setup lang="ts">
import { useMessage } from 'naive-ui'
import { onMounted } from 'vue'
import { useRoute } from 'vue-router'
interface MemberItem {
userId: number | string
id: number | string
tenantId: number | string
avatar: string
nickname: string
role: '星主' | '管理人' | '成员'
type: '付费' | '免费'
joinTime: string
expireTime: string
lastLoginTime: string
isBlack: '0' | '1'
userType: number
communityId: number | string
joinType: string
}
definePageMeta({
layout: 'planet',
})
const route = useRoute()
const message = useMessage()
const memberList = ref<MemberItem[]>([])
const loading = ref(false)
const isInitialized = ref(false)
const params = ref({
orderByColum: '',
pageNum: 1,
pageSize: 10,
communityId: route.query.communityId,
tenantId: route.query.tenantId,
searchContent: '',
})
// 添加分页数据
const pagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
showSizePicker: false,
})
// 拉黑表单
const showBlackModal = ref(false)
const blackForm = ref({
blackDay: undefined,
blackReason: '',
userId: '',
})
// 添加表单验证规则
const rules = {
blackDay: {
required: true,
message: '请输入拉黑时长',
trigger: ['blur', 'change'],
type: 'number',
validator: (rule: any, value: any) => {
if (value === undefined || value === null)
return new Error('请输入拉黑时长')
return true
},
},
blackReason: {
required: true,
message: '请输入拉黑原因',
trigger: ['blur', 'change'],
},
}
// 表单ref
const formRef = ref()
// 处理分页变化
function handlePageChange(page: number) {
pagination.value.page = page
params.value.pageNum = page
getMemberList()
}
// 处理搜索
function handleSearch(e: KeyboardEvent) {
if (e.key === 'Enter') {
params.value.pageNum = 1
pagination.value.page = 1
getMemberList()
}
}
// 获取成员列表
async function getMemberList() {
try {
loading.value = true
const res = await request.post('/communityUser/list', params.value)
if (res.code === 200) {
memberList.value = res.rows
pagination.value.itemCount = res.total
isInitialized.value = true
}
}
catch (error) {
console.error(error)
}
finally {
loading.value = false
}
}
onMounted(() => {
getMemberList()
})
// 拉黑/解除拉黑
async function handleRemove(item: MemberItem) {
if (item.userType === 2)
return
if (item.isBlack === '1') {
// 直接解除拉黑
try {
const res = await request.post('/communityUser/unBlack', {
communityId: route.query.communityId,
tenantId: route.query.tenantId, // 用户userId
userId: item.userId,
})
if (res.code === 200) {
message.success('解除拉黑成功')
params.value.pageNum = 1
pagination.value.page = 1
getMemberList()
}
}
catch (error) {
console.error(error)
}
}
else {
// 显示拉黑弹窗
blackForm.value = {
blackDay: undefined,
blackReason: '',
userId: item.userId,
}
showBlackModal.value = true
}
}
// 确认拉黑
async function handleBlack() {
try {
await formRef.value?.validate()
const res = await request.post('/communityUser/black', {
communityId: route.query.communityId,
tenantId: route.query.tenantId,
userId: blackForm.value.userId,
blackDay: blackForm.value.blackDay,
blackReason: blackForm.value.blackReason,
})
if (res.code === 200) {
message.success('拉黑成功')
showBlackModal.value = false
params.value.pageNum = 1
pagination.value.page = 1
getMemberList()
}
}
catch (error) {
console.error(error)
}
}
// 设置管理员
async function handleSetAdmin(item: MemberItem) {
if (item.userType === 2)
return
const { tenantId, communityId, userId } = item
try {
const res = await request.post('/communityUser/manage', {
communityId,
tenantId,
userId,
})
if (res.code === 200) {
message.success('设置成功')
params.value.pageNum = 1
pagination.value.page = 1
getMemberList()
}
}
catch (error) {
console.error(error)
}
}
// 复制文本到剪贴板
function copyToClipboard(text: string) {
navigator.clipboard.writeText(text).then(() => {
message.success('复制成功')
}).catch(() => {
message.error('复制失败')
})
}
</script>
<template>
<div class="min-h-screen bg-[#F7F8FA] px-10 py-6 flex gap-4">
<div class="bg-white rounded-lg flex-1">
<div class="flex justify-between items-center m-3 text-sm">
<div class="bg-[#328afe] text-white px-3 py-1 rounded-sm cursor-pointer">
分享星球
</div>
<div class="relative flex items-center w-[300px]">
<input
v-model="params.searchContent"
type="text"
placeholder="请输入用户名称"
class="w-full h-9 pl-4 pr-14 border border-[#eee] rounded-lg text-sm placeholder-[#878D95] outline-none focus:border-[#3f7ef7] focus:ring-1 focus:ring-[#3f7ef7] transition-colors"
@keyup="handleSearch"
>
<button class="absolute right-3 flex items-center space-x-1 text-[#4A5563]">
<svg class="w-4 h-4" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.333 12.667A5.333 5.333 0 1 0 7.333 2a5.333 5.333 0 0 0 0 10.667zM14 14l-2.9-2.9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<!-- <span class="text-sm font-bold">搜索</span> -->
</button>
</div>
</div>
<div class="grid grid-cols-[200px_120px_120px_200px_200px_200px] border-b border-[#eee]">
<div class="p-3 text-[#1f2329] font-medium">
全部成员
</div>
<div class="p-3 text-[#1f2329] font-medium">
权限
</div>
<div class="p-3 text-[#1f2329] font-medium">
加入类型
</div>
<div class="p-3 text-[#1f2329] font-medium">
首次加入时间
</div>
<div class="p-3 text-[#1f2329] font-medium">
到期时间
</div>
<div class="p-3 text-[#1f2329] font-medium">
操作
</div>
</div>
<div v-if="loading" class="p-10">
<n-spin />
</div>
<template v-else>
<div
v-for="item in memberList"
:key="item.id"
class="grid grid-cols-[200px_120px_120px_200px_200px_200px] border-b border-[#eee] hover:bg-gray-50"
>
<div class="p-3 flex items-center gap-2 w-[200px]">
<img :src="item.avatar" class="w-8 h-8 rounded-full" :alt="item.nickname">
<span
class="truncate flex-1 cursor-pointer"
title="双击复制"
@dblclick="copyToClipboard(item.nickName)"
>
{{ item.nickName }}
</span>
</div>
<div class="p-3 flex items-center">
{{ item.userType === 0 ? '成员' : item.userType === 1 ? '管理员' : '群主' }}
</div>
<div class="p-3 flex items-center">
{{ item.userType !== 2 ? item.joinType : '-' }}
</div>
<div class="p-3 flex items-center">
{{ item.startTime || '-' }}
</div>
<div class="p-3 flex items-center">
{{ item.endTime || '-' }}
</div>
<div class="p-3 w-[300px] flex items-center">
<div class="w-[100px]">
<button
class="text-[#1e80ff] hover:text-[#3b8fff]"
@click="handleSetAdmin(item)"
>
{{ item.userType === 2 ? '-' : item.userType === 0 ? '设为管理员' : '取消管理员' }}
</button>
</div>
<div class="w-[100px]">
<button
class="text-[#f85149] hover:text-[#ff6b64]"
@click="handleRemove(item)"
>
{{ item.userType === 2 ? '-' : item.isBlack === '0' ? '拉黑' : '解除拉黑' }}
</button>
</div>
</div>
</div>
</template>
<div v-if="isInitialized" class="flex justify-center mt-4">
<n-pagination
v-model:page="pagination.page"
v-model:page-size="pagination.pageSize"
:item-count="pagination.itemCount"
:show-size-picker="false"
@update:page="handlePageChange"
/>
</div>
<n-modal
v-model:show="showBlackModal"
:style="{ width: '480px' }"
:mask-closable="false"
class="rounded-lg"
>
<div class="p-6 bg-white rounded-lg">
<div class="text-center text-lg font-medium mb-6">
拉黑成员
</div>
<n-form
ref="formRef"
:model="blackForm"
:rules="rules"
label-placement="left"
label-width="auto"
require-mark-placement="right-hanging"
class="space-y-4"
>
<n-form-item label="拉黑时长" path="blackDay">
<div class="flex items-center gap-2">
<n-input-number
v-model:value="blackForm.blackDay"
placeholder="请输入"
:min="1"
@update:value="() => formRef.value?.validate(['blackDay'])"
/>
<span class="text-sm">天</span>
<div class="relative group">
<div class="i-carbon-information text-gray-400 cursor-help" />
<div class="absolute left-6 top-0 hidden group-hover:block w-64 p-3 bg-black bg-opacity-75 text-white text-xs rounded-lg">
拉黑后该成员将不能对星球进行内容发布、点赞、评论内容等操作!拉黑时长结束后将自动解除
</div>
</div>
</div>
</n-form-item>
<n-form-item label="拉黑原因" path="blackReason">
<n-input
v-model:value="blackForm.blackReason"
type="textarea"
placeholder="请输入内容"
:autosize="{ minRows: 3, maxRows: 5 }"
class="w-full"
/>
</n-form-item>
</n-form>
<div class="flex justify-center gap-4 mt-8">
<n-button
class="w-24 h-9 hover:opacity-90"
@click="showBlackModal = false"
>
取消
</n-button>
<n-button
type="primary"
class="w-24 h-9 hover:opacity-90"
:theme-overrides="{
common: {
primaryColor: '#3f7ef7',
primaryColorHover: '#3f7ef7',
},
}"
@click="handleBlack"
>
确定拉黑
</n-button>
</div>
</div>
</n-modal>
</div>
<div class="w-[300px]">
<PlanetBaseInfo :community-id="route.query.communityId" :tenant-id="route.query.tenantId" />
</div>
</div>
</template>