321 lines
8.0 KiB
Vue

<script setup>
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
import { NButton, NForm, NFormItem, NInput, NDatePicker } from 'naive-ui'
import CommonPage from '@/components/page/CommonPage.vue'
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
import CrudModal from '@/components/table/CrudModal.vue'
import CrudTable from '@/components/table/CrudTable.vue'
import LimitSettingModal from './LimitSettingModal.vue'
import UserDetailModal from './UserDetailModal.vue'
import { formatDate, renderIcon } from '@/utils'
import { useCRUD } from '@/composables'
import api from '@/api'
import TheIcon from '@/components/icon/TheIcon.vue'
defineOptions({ name: '用户管理' })
const $table = ref(null)
const queryItems = ref({})
const vPermission = resolveDirective('permission')
// 次数设置弹窗相关状态
const limitModalVisible = ref(false)
const currentUser = ref(null)
const detailModalVisible = ref(false)
const detailLoading = ref(false)
const userDetail = ref({
baseInfo: {},
invoiceHeaders: [],
operationLogs: [],
})
const {
modalVisible,
modalTitle,
modalAction,
modalLoading,
handleSave,
modalForm,
modalFormRef,
handleEdit,
handleDelete,
handleAdd,
} = useCRUD({
name: '用户',
initForm: { is_active: true },
doCreate: (data) => api.createAppUser({ phone: data.phone }),
doUpdate: (data) => api.updateAppUser(data),
doDelete: (data) => api.deleteAppUser({ id: data.id }),
refresh: () => $table.value?.handleSearch(),
})
onMounted(() => {
$table.value?.handleSearch()
})
const columns = [
{
title: 'ID',
key: 'id',
width: 100,
align: 'center',
ellipsis: { tooltip: true },
},
{
title: '手机号',
key: 'phone',
width: 140,
align: 'center',
ellipsis: { tooltip: true },
},
// {
// title: '微信号',
// key: 'wechat',
// width: 140,
// align: 'center',
// ellipsis: { tooltip: true },
// render(row) {
// return row.wechat || '-'
// },
// },
{
title: '注册时间',
key: 'created_at',
align: 'center',
width: 180,
ellipsis: { tooltip: true },
render(row) {
return row.created_at ? formatDate(row.created_at) : '-'
},
},
{
title: '备注',
key: 'notes',
align: 'center',
width: 120,
ellipsis: { tooltip: true },
render(row) {
return row.notes || '-'
},
},
{
title: '剩余体验次数',
key: 'remaining_count',
width: 120,
align: 'center',
render(row) {
return row.remaining_count !== undefined ? row.remaining_count : 0
},
},
{
title: '操作',
key: 'actions',
width: 200,
align: 'center',
fixed: 'right',
render(row) {
return [
withDirectives(
h(
NButton,
{
size: 'small',
type: 'info',
style: 'margin-right: 8px;',
onClick: () => handleViewDetail(row),
},
{
default: () => '详情',
icon: renderIcon('material-symbols:info', { size: 16 }),
}
),
[[vPermission, 'get/api/v1/app-user-admin/list']]
),
withDirectives(
h(
NButton,
{
size: 'small',
type: 'primary',
style: 'margin-right: 8px;',
onClick: () => handleSetLimit(row),
},
{
default: () => '次数设置',
icon: renderIcon('material-symbols:settings', { size: 16 }),
}
),
[[vPermission, 'post/api/v1/app-user-admin/quota']]
),
]
},
},
]
// 查看用户详情
async function handleViewDetail(row) {
detailModalVisible.value = true
currentUser.value = row
userDetail.value = {
baseInfo: {
id: row.id,
phone: row.phone,
wechat: row.wechat,
register_time: row.created_at ? formatDate(row.created_at) : '-',
notes: row.notes,
remaining_count: row.remaining_count,
user_type: row.user_type || '-',
}
}
}
// 次数设置
function handleSetLimit(row) {
currentUser.value = row
limitModalVisible.value = true
}
// 保存次数设置
async function handleSaveLimitSetting(data) {
try {
await api.updateAppUserQuota({
user_id: data.user_id,
target_count: data.target_count,
op_type: data.op_type,
remark: data.remark,
})
$message.success('次数设置保存成功')
limitModalVisible.value = false
$table.value?.handleSearch()
} catch (error) {
$message.error(error?.message || '保存失败')
}
}
const validateForm = {
phone: [
{
required: true,
message: '请输入手机号',
trigger: ['input', 'blur'],
},
{
trigger: ['blur'],
validator: (rule, value, callback) => {
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(value)) {
callback('请输入正确的手机号格式')
return
}
callback()
},
},
],
}
</script>
<template>
<CommonPage show-footer title="用户列表">
<template #action>
<!-- <NButton v-permission="'post/api/v1/app_user/create'" type="primary" @click="handleAdd">
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建用户
</NButton> -->
</template>
<!-- 表格 -->
<CrudTable
ref="$table"
v-model:query-items="queryItems"
:columns="columns"
:get-data="api.getAppUserList"
>
<template #queryBar>
<QueryBarItem label="手机号" :label-width="60">
<NInput
v-model:value="queryItems.phone"
clearable
type="text"
placeholder="请输入手机号"
style="width: 200px"
@keypress.enter="$table?.handleSearch()"
/>
</QueryBarItem>
<!-- <QueryBarItem label="微信号" :label-width="60">
<NInput
v-model:value="queryItems.wechat"
clearable
type="text"
placeholder="请输入微信号"
style="width: 200px"
@keypress.enter="$table?.handleSearch()"
/>
</QueryBarItem> -->
<QueryBarItem label="ID" :label-width="60">
<NInput
v-model:value="queryItems.id"
clearable
type="text"
placeholder="请输入ID"
style="width: 200px"
@keypress.enter="$table?.handleSearch()"
/>
</QueryBarItem>
<QueryBarItem label="注册时间" :label-width="70">
<NDatePicker
v-model:value="queryItems.created_at"
type="daterange"
clearable
placeholder="请选择注册时间"
style="width: 280px"
@update:value="$table?.handleSearch()"
/>
</QueryBarItem>
</template>
</CrudTable>
<!-- 新增/编辑 弹窗 -->
<CrudModal
v-model:visible="modalVisible"
:title="modalTitle"
:loading="modalLoading"
@save="handleSave"
>
<NForm
ref="modalFormRef"
label-placement="left"
label-align="left"
:label-width="80"
:model="modalForm"
:rules="validateForm"
>
<NFormItem label="手机号" path="phone">
<NInput v-model:value="modalForm.phone" clearable placeholder="请输入手机号" />
</NFormItem>
<NFormItem v-if="modalAction === 'add'" label="说明">
<span style="color: #999; font-size: 12px;">
注册后默认密码为手机号后6位
</span>
</NFormItem>
</NForm>
</CrudModal>
<!-- 次数设置弹窗 -->
<LimitSettingModal
v-model:visible="limitModalVisible"
:user-data="currentUser"
@save="handleSaveLimitSetting"
/>
<!-- 用户详情弹窗 -->
<UserDetailModal
v-model:visible="detailModalVisible"
:user-id="currentUser?.id"
:base-info="userDetail.baseInfo"
/>
</CommonPage>
</template>