Vue3 重构:REVIEW 租户管理

This commit is contained in:
YunaiV 2023-03-23 09:42:53 +08:00
parent 3db7b26e68
commit 7c92735aff
4 changed files with 114 additions and 326 deletions

View File

@ -32,31 +32,31 @@ export interface TenantExportReqVO {
} }
// 查询租户列表 // 查询租户列表
export const getTenantPageApi = (params: TenantPageReqVO) => { export const getTenantPage = (params: TenantPageReqVO) => {
return request.get({ url: '/system/tenant/page', params }) return request.get({ url: '/system/tenant/page', params })
} }
// 查询租户详情 // 查询租户详情
export const getTenantApi = (id: number) => { export const getTenant = (id: number) => {
return request.get({ url: '/system/tenant/get?id=' + id }) return request.get({ url: '/system/tenant/get?id=' + id })
} }
// 新增租户 // 新增租户
export const createTenantApi = (data: TenantVO) => { export const createTenant = (data: TenantVO) => {
return request.post({ url: '/system/tenant/create', data }) return request.post({ url: '/system/tenant/create', data })
} }
// 修改租户 // 修改租户
export const updateTenantApi = (data: TenantVO) => { export const updateTenant = (data: TenantVO) => {
return request.put({ url: '/system/tenant/update', data }) return request.put({ url: '/system/tenant/update', data })
} }
// 删除租户 // 删除租户
export const deleteTenantApi = (id: number) => { export const deleteTenant = (id: number) => {
return request.delete({ url: '/system/tenant/delete?id=' + id }) return request.delete({ url: '/system/tenant/delete?id=' + id })
} }
// 导出租户 // 导出租户
export const exportTenantApi = (params: TenantExportReqVO) => { export const exportTenant = (params: TenantExportReqVO) => {
return request.download({ url: '/system/tenant/export-excel', params }) return request.download({ url: '/system/tenant/export-excel', params })
} }

View File

@ -7,96 +7,67 @@
label-width="80px" label-width="80px"
v-loading="formLoading" v-loading="formLoading"
> >
<el-row> <el-form-item label="租户名" prop="name">
<el-col :span="10"> <el-input v-model="formData.name" placeholder="请输入租户名" />
<el-form-item label="租户名" prop="name"> </el-form-item>
<el-input v-model="formData.name" placeholder="请输入租户名" /> <el-form-item label="租户套餐" prop="packageId">
</el-form-item> <el-select v-model="formData.packageId" placeholder="请选择租户套餐" clearable>
</el-col> <el-option
<el-col :span="10" :offset="2"> v-for="item in packageList"
<el-form-item label="租户套餐" prop="packageId"> :key="item.id"
<el-select v-model="formData.packageId" placeholder="请选择租户套餐" clearable> :label="item.name"
<el-option :value="item.id"
v-for="item in packageList" />
:key="item.id" </el-select>
:label="item.name" </el-form-item>
:value="item.id" <el-form-item label="联系人" prop="contactName">
/> <el-input v-model="formData.contactName" placeholder="请输入联系人" />
</el-select> </el-form-item>
</el-form-item> <el-form-item label="联系手机" prop="contactMobile">
</el-col> <el-input v-model="formData.contactMobile" placeholder="请输入联系手机" />
</el-row> </el-form-item>
<el-row> <el-form-item v-if="formData.id === undefined" label="用户名称" prop="username">
<el-col :span="10"> <el-input v-model="formData.username" placeholder="请输入用户名称" />
<el-form-item label="联系人" prop="contactName"> </el-form-item>
<el-input v-model="formData.contactName" placeholder="请输入联系人" /> <el-form-item v-if="formData.id === undefined" label="用户密码" prop="password">
</el-form-item> <el-input
</el-col> v-model="formData.password"
<el-col :span="10" :offset="2"> placeholder="请输入用户密码"
<el-form-item label="联系手机" prop="contactMobile"> type="password"
<el-input v-model="formData.contactMobile" placeholder="请输入联系手机" /> show-password
</el-form-item> />
</el-col> </el-form-item>
</el-row> <el-form-item label="账号额度" prop="accountCount">
<el-row> <el-input-number
<el-col :span="10"> v-model="formData.accountCount"
<el-form-item v-if="formData.id === undefined" label="用户名称" prop="username"> placeholder="请输入账号额度"
<el-input v-model="formData.username" placeholder="请输入用户名称" /> controls-position="right"
</el-form-item> :min="0"
</el-col> />
<el-col :span="10" :offset="2"> </el-form-item>
<el-form-item v-if="formData.id === undefined" label="用户密码" prop="password"> <el-form-item label="过期时间" prop="expireTime">
<el-input <el-date-picker
v-model="formData.password" clearable
placeholder="请输入用户密码" v-model="formData.expireTime"
type="password" type="date"
show-password value-format="x"
/> placeholder="请选择过期时间"
</el-form-item> />
</el-col> </el-form-item>
</el-row> <el-form-item label="绑定域名" prop="domain">
<el-row> <el-input v-model="formData.domain" placeholder="请输入绑定域名" />
<el-col :span="10"> </el-form-item>
<el-form-item label="账号额度" prop="accountCount"> <el-form-item label="租户状态" prop="status">
<el-input-number <el-radio-group v-model="formData.status">
v-model="formData.accountCount" <el-radio
placeholder="请输入账号额度" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
controls-position="right" :key="dict.value"
:min="0" :label="dict.value"
/> >
</el-form-item> {{ dict.label }}
</el-col> </el-radio>
<el-col :span="10" :offset="2"> </el-radio-group>
<el-form-item label="过期时间" prop="expireTime"> </el-form-item>
<el-date-picker
clearable
v-model="formData.expireTime"
type="date"
value-format="x"
placeholder="请选择过期时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="10">
<el-form-item label="绑定域名" prop="domain">
<el-input v-model="formData.domain" placeholder="请输入绑定域名" />
</el-form-item>
</el-col>
<el-col :span="10" :offset="2">
<el-form-item label="租户状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -110,7 +81,7 @@
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as TenantApi from '@/api/system/tenant' import * as TenantApi from '@/api/system/tenant'
import { CommonStatusEnum } from '@/utils/constants' import { CommonStatusEnum } from '@/utils/constants'
import { getTenantPackageList as getTenantPackageListApi } from '@/api/system/tenantPackage' import * as TenantPackageApi from '@/api/system/tenantPackage'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -142,7 +113,7 @@ const formRef = ref() // 表单 Ref
const packageList = ref([]) // const packageList = ref([]) //
/** 打开弹窗 */ /** 打开弹窗 */
const openModal = async (type: string, id?: number) => { const open = async (type: string, id?: number) => {
modelVisible.value = true modelVisible.value = true
modelTitle.value = t('action.' + type) modelTitle.value = t('action.' + type)
formType.value = type formType.value = type
@ -151,14 +122,15 @@ const openModal = async (type: string, id?: number) => {
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
formData.value = await TenantApi.getTenantApi(id) formData.value = await TenantApi.getTenant(id)
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
} }
packageList.value = await getTenantPackageListApi() //
packageList.value = await TenantPackageApi.getTenantPackageList()
} }
defineExpose({ openModal }) // openModal defineExpose({ open }) // open
/** 提交表单 */ /** 提交表单 */
const emit = defineEmits(['success']) // success const emit = defineEmits(['success']) // success
@ -172,10 +144,10 @@ const submitForm = async () => {
try { try {
const data = formData.value as unknown as TenantApi.TenantVO const data = formData.value as unknown as TenantApi.TenantVO
if (formType.value === 'create') { if (formType.value === 'create') {
await TenantApi.createTenantApi(data) await TenantApi.createTenant(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
} else { } else {
await TenantApi.updateTenantApi(data) await TenantApi.updateTenant(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} }
modelVisible.value = false modelVisible.value = false

View File

@ -1,13 +1,20 @@
<template> <template>
<!-- 搜索 --> <!-- 搜索 -->
<content-wrap> <ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true"> <el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="租户名" prop="name"> <el-form-item label="租户名" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
placeholder="请输入租户名" placeholder="请输入租户名"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="联系人" prop="contactName"> <el-form-item label="联系人" prop="contactName">
@ -16,6 +23,7 @@
placeholder="请输入联系人" placeholder="请输入联系人"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="联系手机" prop="contactMobile"> <el-form-item label="联系手机" prop="contactMobile">
@ -24,12 +32,18 @@
placeholder="请输入联系手机" placeholder="请输入联系手机"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="租户状态" prop="status"> <el-form-item label="租户状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择租户状态" clearable> <el-select
v-model="queryParams.status"
placeholder="请选择租户状态"
clearable
class="!w-240px"
>
<el-option <el-option
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
@ -41,10 +55,10 @@
v-model="queryParams.createTime" v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
type="daterange" type="daterange"
range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/> />
</el-form-item> </el-form-item>
@ -57,11 +71,7 @@
<Icon icon="ep:refresh" class="mr-5px" /> <Icon icon="ep:refresh" class="mr-5px" />
重置 重置
</el-button> </el-button>
<el-button <el-button type="primary" @click="openForm('create')" v-hasPermi="['system:tenant:create']">
type="primary"
@click="openModal('create')"
v-hasPermi="['system:tenant:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> <Icon icon="ep:plus" class="mr-5px" />
新增 新增
</el-button> </el-button>
@ -77,10 +87,10 @@
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</content-wrap> </ContentWrap>
<!-- 列表 --> <!-- 列表 -->
<content-wrap> <ContentWrap>
<el-table v-loading="loading" :data="list" align="center"> <el-table v-loading="loading" :data="list" align="center">
<el-table-column label="租户编号" align="center" prop="id" /> <el-table-column label="租户编号" align="center" prop="id" />
<el-table-column label="租户名" align="center" prop="name" /> <el-table-column label="租户名" align="center" prop="name" />
@ -126,7 +136,7 @@
<el-button <el-button
link link
type="primary" type="primary"
@click="openModal('update', scope.row.id)" @click="openForm('update', scope.row.id)"
v-hasPermi="['system:tenant:update']" v-hasPermi="['system:tenant:update']"
> >
编辑 编辑
@ -149,20 +159,18 @@
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@pagination="getList" @pagination="getList"
/> />
</content-wrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<tenant-form ref="modalRef" @success="getList" /> <TenantForm ref="formRef" @success="getList" />
</template> </template>
<script setup lang="ts" name="Tenant"> <script setup lang="ts" name="Tenant">
import { DICT_TYPE, getDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import * as TenantApi from '@/api/system/tenant' import * as TenantApi from '@/api/system/tenant'
import { getTenantPackageList as getTenantPackageListApi } from '@/api/system/tenantPackage' import * as TenantPackageApi from '@/api/system/tenantPackage'
import TenantForm from './form.vue' import TenantForm from './form.vue'
import ContentWrap from '@/components/ContentWrap/src/ContentWrap.vue'
import DictTag from '@/components/DictTag/src/DictTag.vue'
const message = useMessage() // const message = useMessage() //
const { t } = useI18n() // const { t } = useI18n() //
@ -170,7 +178,6 @@ const { t } = useI18n() // 国际化
const loading = ref(true) // const loading = ref(true) //
const total = ref(0) // const total = ref(0) //
const list = ref([]) // const list = ref([]) //
const packageList = ref([]) //
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
@ -179,15 +186,16 @@ const queryParams = reactive({
contactMobile: undefined, contactMobile: undefined,
status: undefined, status: undefined,
createTime: [] createTime: []
}) // })
const queryFormRef = ref() // const queryFormRef = ref() //
const exportLoading = ref(false) // const exportLoading = ref(false) //
const packageList = ref([]) //
/** 查询参数列表 */ /** 查询参数列表 */
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
try { try {
const data = await TenantApi.getTenantPageApi(queryParams) const data = await TenantApi.getTenantPage(queryParams)
list.value = data.list list.value = data.list
total.value = data.total total.value = data.total
} finally { } finally {
@ -208,9 +216,9 @@ const resetQuery = () => {
} }
/** 添加/修改操作 */ /** 添加/修改操作 */
const modalRef = ref() const formRef = ref()
const openModal = (type: string, id?: number) => { const openForm = (type: string, id?: number) => {
modalRef.value.openModal(type, id) formRef.value.open(type, id)
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
@ -219,7 +227,7 @@ const handleDelete = async (id: number) => {
// //
await message.delConfirm() await message.delConfirm()
// //
await TenantApi.deleteTenantApi(id) await TenantApi.deleteTenant(id)
message.success(t('common.delSuccess')) message.success(t('common.delSuccess'))
// //
await getList() await getList()
@ -233,23 +241,17 @@ const handleExport = async () => {
await message.exportConfirm() await message.exportConfirm()
// //
exportLoading.value = true exportLoading.value = true
const data = await TenantApi.exportTenantApi(queryParams) const data = await TenantApi.exportTenant(queryParams)
download.excel(data, '参数配置.xls') download.excel(data, '租户列表.xls')
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false
} }
} }
/**获取租户套餐**/
const getTenantPackageList = async () => {
const data = await getTenantPackageListApi()
packageList.value = data
}
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
getList() await getList()
getTenantPackageList() packageList.value = await TenantPackageApi.getTenantPackageList()
}) })
</script> </script>

View File

@ -1,186 +0,0 @@
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
import { getTenantPackageList, TenantPackageVO } from '@/api/system/tenantPackage'
import { ComponentOptions } from '@/types/components'
const { t } = useI18n() // 国际化
export const tenantPackageOption: ComponentOptions[] = []
const getTenantPackageOptions = async () => {
const res = await getTenantPackageList()
res.forEach((tenantPackage: TenantPackageVO) => {
tenantPackageOption.push({
key: tenantPackage.id,
value: tenantPackage.id,
label: tenantPackage.name
})
})
return tenantPackageOption
}
getTenantPackageOptions()
const validateName = (rule: any, value: any, callback: any) => {
const reg = /^[a-zA-Z0-9]{4,30}$/
if (value === '') {
callback(new Error('请输入用户名称'))
} else {
console.log(reg.test(rule), 'reg.test(rule)')
if (!reg.test(value)) {
callback(new Error('用户名称由 数字、字母 组成'))
} else {
callback()
}
}
}
const validateMobile = (rule: any, value: any, callback: any) => {
const reg = /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/
if (value === '') {
callback(new Error('请输入联系手机'))
} else {
if (!reg.test(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
}
}
// 表单校验
export const rules = reactive({
name: [required],
packageId: [required],
contactName: [required],
contactMobile: [
required,
{
validator: validateMobile,
trigger: 'blur'
}
],
accountCount: [required],
expireTime: [required],
username: [
required,
{
min: 4,
max: 30,
trigger: 'blur',
message: '用户名称长度为 4-30 个字符'
},
{ validator: validateName, trigger: 'blur' }
],
password: [
required,
{
min: 4,
max: 16,
trigger: 'blur',
message: '密码长度为 4-16 位'
}
],
domain: [required],
status: [required]
})
// CrudSchema.
const crudSchemas = reactive<VxeCrudSchema>({
primaryKey: 'id',
primaryTitle: '租户编号',
primaryType: 'id',
action: true,
columns: [
{
title: '租户名称',
field: 'name',
isSearch: true
},
{
title: '租户套餐',
field: 'packageId',
table: {
slots: {
default: 'packageId_default'
}
},
form: {
component: 'Select',
componentProps: {
options: tenantPackageOption
}
}
},
{
title: '联系人',
field: 'contactName',
isSearch: true
},
{
title: '联系手机',
field: 'contactMobile',
isSearch: true
},
{
title: '用户名称',
field: 'username',
isTable: false,
isDetail: false
},
{
title: '用户密码',
field: 'password',
isTable: false,
isDetail: false,
form: {
component: 'InputPassword'
}
},
{
title: '账号额度',
field: 'accountCount',
table: {
slots: {
default: 'accountCount_default'
}
},
form: {
component: 'InputNumber'
}
},
{
title: '过期时间',
field: 'expireTime',
formatter: 'formatDate',
form: {
component: 'DatePicker',
componentProps: {
type: 'datetime',
valueFormat: 'x'
}
}
},
{
title: '绑定域名',
field: 'domain'
},
{
title: '租户状态',
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
dictClass: 'number',
isSearch: true
},
{
title: t('table.createTime'),
field: 'createTime',
formatter: 'formatDate',
isForm: false,
search: {
show: true,
itemRender: {
name: 'XDataTimePicker'
}
}
}
]
})
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)