<script setup lang="tsx">
|
import { computed, ref, watch } from 'vue'
|
import type { DataTableColumns } from 'naive-ui'
|
import { NTag, useMessage } from 'naive-ui'
|
import Pagination from '@/components/common/Pagination.vue'
|
import { fetchWhitelistPageList } from '@/service/api/whitelist'
|
import dayjs from 'dayjs'
|
|
interface Props {
|
show: boolean
|
}
|
|
interface Emits {
|
(e: 'update:show', value: boolean): void
|
(e: 'sendAll', payload: { userCount: number }): void
|
(e: 'sendSelected', rows: any[]): void
|
}
|
|
const props = defineProps<Props>()
|
const emits = defineEmits<Emits>()
|
const message = useMessage()
|
|
const showModal = computed({
|
get: () => props.show,
|
set: val => emits('update:show', val),
|
})
|
|
const loading = ref(false)
|
|
const searchModel = ref({
|
account: '',
|
whitelistSource: null,
|
status: null,
|
})
|
|
const sourceOptions = [
|
{ label: '白名单添加', value: 1 },
|
{ label: '邀请码绑定', value: 2 },
|
]
|
|
const statusOptions = [
|
{ label: '正常', value: 1 },
|
{ label: '冻结', value: 0 },
|
]
|
|
// 下拉框展开状态
|
const isSourceSelectOpen = ref(false)
|
const isStatusSelectOpen = ref(false)
|
|
const checkedRowKeys = ref<number[]>([])
|
const list = ref<any[]>([])
|
|
const currentPage = ref(1)
|
const pageSize = ref(10)
|
const total = ref(0)
|
|
function handleSearch() {
|
currentPage.value = 1
|
getList()
|
}
|
|
function handleReset() {
|
searchModel.value = {
|
account: '',
|
whitelistSource: null,
|
status: null,
|
}
|
currentPage.value = 1
|
getList()
|
}
|
|
const isNormalStatus = (status: unknown) => status === 1 || status === true
|
|
const columns: DataTableColumns<any> = [
|
{ type: 'selection' },
|
{ title: '序号', key: 'id', width: 80, align: 'center', render: (_, index) => (currentPage.value - 1) * pageSize.value + index + 1 },
|
{ title: '用户名称', key: 'nickName', width: 150 },
|
{ title: '白名单账号', key: 'account' },
|
{
|
title: '状态',
|
key: 'status',
|
width: 100,
|
render(row) {
|
const isNormal = isNormalStatus(row.status)
|
return (
|
<NTag type={isNormal ? 'info' : 'warning'} size="small" bordered={false} style={{ color: isNormal ? '#1890FF' : '#FAAD14', backgroundColor: isNormal ? '#E6F7FF' : '#FFFBE6' }}>
|
{isNormal ? '正常' : '冻结'}
|
</NTag>
|
)
|
},
|
},
|
{
|
title: '优先派单',
|
key: 'isPriorityDispatch',
|
width: 100,
|
render: (row) => {
|
const isPriority = row.isPriorityDispatch === 1 || row.isPriorityDispatch === true
|
return <span>{isPriority ? '是' : '否'}</span>
|
},
|
},
|
{ title: '创建人', key: 'createBy', width: 120 },
|
{ title: '创建时间', key: 'createTime', width: 180, render: (row) => row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '' },
|
]
|
|
async function getList() {
|
loading.value = true
|
try {
|
const { code, data } = await fetchWhitelistPageList({
|
...searchModel.value,
|
pageNum: currentPage.value,
|
pageSize: pageSize.value,
|
})
|
if (code === 200 && data) {
|
list.value = data.records || []
|
total.value = data.total || 0
|
}
|
}
|
finally {
|
loading.value = false
|
}
|
}
|
|
watch(() => props.show, (val) => {
|
if (val) {
|
checkedRowKeys.value = []
|
getList()
|
}
|
})
|
|
function handlePageChange(page: number, size: number) {
|
const isPageSizeChanged = size !== pageSize.value
|
pageSize.value = size
|
currentPage.value = isPageSizeChanged ? 1 : page
|
getList()
|
}
|
|
function handleSendAll() {
|
// if (!isNormalStatus(searchModel.value.status)) {
|
// message.warning('一键发放所有用户仅支持“冻结状态”筛选为“正常”')
|
// return
|
// }
|
emits('sendAll', { userCount: total.value })
|
// showModal.value = false
|
}
|
|
function handleSendSelected() {
|
const selectedRows = list.value.filter(item => checkedRowKeys.value.includes(item.id))
|
const hasFrozen = selectedRows.some(row => !isNormalStatus(row.status))
|
if (hasFrozen) {
|
message.warning('选中用户包含冻结状态,请仅选择“正常”用户')
|
return
|
}
|
emits('sendSelected', selectedRows)
|
// showModal.value = false
|
}
|
</script>
|
|
<template>
|
<n-modal v-model:show="showModal">
|
<n-card style="width: 1000px; height: 80vh; max-height: 800px; border-radius: 8px; display: flex; flex-direction: column;" :bordered="false"
|
:segmented="{ content: true }" content-style="padding: 16px 24px; flex: 1; display: flex; flex-direction: column; overflow: hidden;"
|
footer-style="padding: 16px 24px; border-top: 1px solid rgba(46, 61, 77, 0.1); flex-shrink: 0;">
|
<template #header>
|
<span style="font-size: 16px; font-weight: bold">选择用户</span>
|
</template>
|
<template #header-extra>
|
<div @click="showModal = false">
|
<span style="font-size: 18px; width: 10px; height: 10px;cursor: pointer;">×</span>
|
</div>
|
</template>
|
<div class="flex flex-col h-full overflow-hidden">
|
<!-- 搜索区域 -->
|
<div class="mb-4 flex items-end gap-4" style="flex-shrink: 0;">
|
<div class="flex-1">
|
<div class="mb-2 text-sm">
|
白名单账号
|
</div>
|
<n-input v-model:value="searchModel.account" placeholder="请输入" clearable />
|
</div>
|
<div class="flex-1">
|
<div class="mb-2 text-sm">
|
来源
|
</div>
|
<n-select v-model:value="searchModel.whitelistSource" :options="sourceOptions" placeholder="请搜索" clearable
|
style="width: 100%" @update:show="isSourceSelectOpen = $event">
|
<template #arrow>
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class="arrow-icon" :class="{ 'arrow-icon--open': isSourceSelectOpen }"
|
style="width: 16px;height: 16px;" alt="arrow">
|
</template>
|
</n-select>
|
</div>
|
<div class="flex-1">
|
<div class="mb-2 text-sm">
|
冻结状态
|
</div>
|
<n-select v-model:value="searchModel.status" :options="statusOptions" placeholder="请搜索" clearable
|
style="width: 100%" @update:show="isStatusSelectOpen = $event">
|
<template #arrow>
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class="arrow-icon" :class="{ 'arrow-icon--open': isStatusSelectOpen }"
|
style="width: 16px;height: 16px;" alt="arrow">
|
</template>
|
</n-select>
|
</div>
|
<div class="flex gap-2">
|
<n-button style="width: 84px; height: 32px" @click="handleReset">
|
重置
|
</n-button>
|
<n-button type="primary" style="width: 84px; height: 32px" @click="handleSearch">
|
查询
|
</n-button>
|
</div>
|
</div>
|
<div class="flex-1 overflow-hidden" style="min-height: 0;">
|
<!-- 表格区域 -->
|
<n-data-table style="height: 100%;" flex-height v-model:checked-row-keys="checkedRowKeys" :columns="columns" :data="list" :loading="loading"
|
:bordered="false" :row-key="(row) => row.id" :pagination="false" />
|
</div>
|
|
<Pagination :count="total" :page="currentPage" :page-size="pageSize" style="margin-top: 12px; flex-shrink: 0;"
|
@change="handlePageChange" />
|
</div>
|
|
|
<template #footer>
|
<div class="flex justify-end gap-3">
|
<n-button class="footer-btn" @click="showModal = false">
|
取消
|
</n-button>
|
<n-button class="footer-btn"
|
style="box-sizing: border-box;color: #0068FF;border: 1px solid #7ABDFF;background-color: #E6F4FF;" secondary
|
type="primary" @click="handleSendAll">
|
一键发放所有用户
|
</n-button>
|
<n-button class="footer-btn" type="primary" :disabled="checkedRowKeys.length === 0"
|
@click="handleSendSelected">
|
确认发送选中用户
|
</n-button>
|
</div>
|
</template>
|
</n-card>
|
</n-modal>
|
</template>
|
|
<style scoped lang="scss">
|
.mb-4 {
|
margin-bottom: 1rem;
|
}
|
|
.flex {
|
display: flex;
|
}
|
|
.items-end {
|
align-items: flex-end;
|
}
|
|
.gap-4 {
|
gap: 1rem;
|
}
|
|
.gap-2 {
|
gap: 0.5rem;
|
}
|
|
.gap-3 {
|
gap: 0.75rem;
|
}
|
|
.flex-1 {
|
flex: 1 1 0%;
|
}
|
|
.justify-end {
|
justify-content: flex-end;
|
}
|
|
.mb-2 {
|
margin-bottom: 0.5rem;
|
}
|
|
.text-sm {
|
font-size: 0.875rem;
|
line-height: 1.25rem;
|
}
|
|
.footer-btn {
|
padding: 7px 29px;
|
height: auto;
|
font-size: 13px;
|
line-height: 18px;
|
}
|
</style>
|