代理功能+web客户端权限
This commit is contained in:
@@ -1,46 +1,64 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" :rules="formRules" label-width="120px">
|
||||
<!-- ========== 1. 基础信息 ========== -->
|
||||
<el-divider>基础信息</el-divider>
|
||||
<el-form-item label="租户名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入租户名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户套餐" prop="packageId">
|
||||
<el-select v-model="formData.packageId" clearable placeholder="请选择租户套餐">
|
||||
<el-option v-for="item in packageList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<el-form-item v-if="tenantLevel <= 1" label="租户类型" prop="tenantType">
|
||||
<el-radio-group v-model="formData.tenantType">
|
||||
<el-radio value="代理">代理</el-radio>
|
||||
<el-radio value="用户">用户</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="联系人" prop="contactName">
|
||||
<el-input v-model="formData.contactName" placeholder="请输入联系人" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="联系手机" prop="contactMobile">
|
||||
<el-input v-model="formData.contactMobile" placeholder="请输入联系手机" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.id === undefined" label="用户名称" prop="username">
|
||||
<el-input v-model="formData.username" placeholder="请输入用户名称" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.id === undefined" label="用户密码" prop="password">
|
||||
<el-input v-model="formData.password" placeholder="请输入用户密码" show-password type="password" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="账号额度" prop="accountCount">
|
||||
<el-input-number v-model="formData.accountCount" :min="0" controls-position="right" placeholder="请输入账号额度" />
|
||||
</el-form-item>
|
||||
<el-form-item label="AI过期时间" prop="aiExpireTime">
|
||||
|
||||
<el-form-item v-if="tenantLevel == 0" label="AI过期时间" prop="aiExpireTime">
|
||||
<el-date-picker v-model="formData.aiExpireTime" clearable placeholder="请选择AI过期时间" type="date"
|
||||
value-format="x" />
|
||||
</el-form-item>
|
||||
<el-form-item label="爬大哥过期时间" prop="brotherExpireTime">
|
||||
|
||||
<el-form-item v-if="tenantLevel == 0" label="爬大哥过期时间" prop="brotherExpireTime">
|
||||
<el-date-picker v-model="formData.brotherExpireTime" clearable placeholder="请选择爬大哥过期时间" type="date"
|
||||
value-format="x" />
|
||||
</el-form-item>
|
||||
<el-form-item label="爬虫后台过期时间" prop="expireTime">
|
||||
|
||||
<el-form-item v-if="tenantLevel == 0" label="爬虫后台过期时间" prop="expireTime">
|
||||
<el-date-picker v-model="formData.expireTime" clearable placeholder="请选择过期时间" type="date" value-format="x" />
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="爬虫后台过期时间" prop="expireTime">
|
||||
<el-date-picker v-model="formData.expireTime" clearable placeholder="请选择过期时间" disabled type="date"
|
||||
value-format="x" />
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定域名" prop="website">
|
||||
<el-input v-model="formData.website" placeholder="请输入绑定域名" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
|
||||
<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" :value="dict.value">
|
||||
@@ -48,14 +66,14 @@
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 选择用户 -->
|
||||
|
||||
<el-form-item v-if="formType !== 'create'" label="选择用户" prop="userId">
|
||||
<el-select v-model="userData.id" @change="changeUser" clearable placeholder="请选择租户">
|
||||
<el-select v-model="userData.id" @change="changeUser" clearable placeholder="请选择租户用户">
|
||||
<el-option v-for="item in userList" :key="item.id" :label="item.username" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 爬虫:不受限制,直接选 -->
|
||||
<!-- 爬虫:不受限制 -->
|
||||
<el-form-item v-if="userData.id" label="爬虫" prop="status">
|
||||
<el-radio-group v-model="userData.crawl">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
@@ -63,7 +81,7 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 爬大哥:必须先选“爬大哥过期时间” -->
|
||||
<!-- 爬大哥:依赖爬大哥过期时间 -->
|
||||
<el-form-item v-if="userData.id" label="爬大哥" prop="status">
|
||||
<el-radio-group v-model="userData.bigBrother" :disabled="!formData.brotherExpireTime">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
@@ -71,7 +89,7 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- ai自动化:必须先选“AI过期时间” -->
|
||||
<!-- ai自动化:依赖 AI 过期时间 -->
|
||||
<el-form-item v-if="userData.id" label="ai自动化" prop="status">
|
||||
<el-radio-group v-model="userData.aiChat" :disabled="!formData.aiExpireTime">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
@@ -79,70 +97,137 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- ai回复:必须先选“AI过期时间” -->
|
||||
<!-- ai回复:依赖 AI 过期时间 -->
|
||||
<el-form-item v-if="userData.id" label="ai回复" prop="status">
|
||||
<el-radio-group v-model="userData.aiReplay" :disabled="!formData.aiExpireTime">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
<el-radio :value="0">关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!--WEB回复:依赖 AI 过期时间 -->
|
||||
<el-form-item v-if="userData.id" label="WEB客户端" prop="status">
|
||||
<el-radio-group v-model="userData.webAi" :disabled="!formData.aiExpireTime">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
<el-radio :value="0">关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- ========== 2. 套餐 & 权限 ========== -->
|
||||
<el-divider>套餐 & 权限</el-divider>
|
||||
<el-form-item label="租户套餐" prop="packageId">
|
||||
<!-- 顶层租户或无 parentId,走原来的下拉选择 -->
|
||||
<el-select v-if="(formData.parentId == 1 || formData.parentId == null) && tenantLevel == 0"
|
||||
v-model="formData.packageId" clearable placeholder="请选择租户套餐">
|
||||
<el-option v-for="item in packageList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
|
||||
<!-- 代理租户:用功能勾选 + 套餐单选 -->
|
||||
<div v-else style="display: block;">
|
||||
<!-- 功能勾选:1=爬大哥,2=爬主播,3=AI套餐 -->
|
||||
<el-checkbox-group v-model="selectedFeatures">
|
||||
<el-checkbox :label="1">爬主播</el-checkbox>
|
||||
<el-checkbox :label="2">爬大哥</el-checkbox>
|
||||
<el-checkbox :label="3">AI套餐</el-checkbox>
|
||||
<el-checkbox :label="9">代理</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
|
||||
<!-- 按天数过滤:30 / 180 / 360 天 -->
|
||||
<div style="margin-top: 8px;">
|
||||
<el-radio-group v-model="selectedDays">
|
||||
<el-radio-button :label="30">30 天</el-radio-button>
|
||||
<el-radio-button :label="180">180 天</el-radio-button>
|
||||
<el-radio-button :label="365">365 天</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<!-- 根据勾选结果筛选套餐,用单选框选择一个套餐 -->
|
||||
<div class="package-radio-wrapper">
|
||||
<el-radio-group v-model="formData.packageId">
|
||||
<el-radio-button v-for="item in filteredAgencyPackages" :key="item.id" :label="item.id"
|
||||
class="package-radio">
|
||||
|
||||
{{ item.name }}
|
||||
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<div v-if="filteredAgencyPackages.length === 0" class="package-empty-tip">
|
||||
暂无匹配套餐,请调整上方功能勾选
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, computed } from 'vue'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as TenantApi from '@/api/system/tenant'
|
||||
import { getUserByTenant, updateRoleUser } from '@/api/system/user'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import * as TenantPackageApi from '@/api/system/tenantPackage'
|
||||
import * as userApi from '@/api/system/user'
|
||||
import { watch } from 'vue' // ⭐ 确保引入 watch
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
defineOptions({ name: 'SystemTenantForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const tenantLevel = ref(3)
|
||||
TenantApi.getSelfTenantLevel().then(res => {
|
||||
tenantLevel.value = res.tenantLevel
|
||||
console.log(tenantLevel.value)
|
||||
})
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const formLoading = ref(false)
|
||||
const formType = ref<'create' | 'update'>('create')
|
||||
const userStore = useUserStore()
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
name: undefined,
|
||||
packageId: undefined,
|
||||
contactName: undefined,
|
||||
contactMobile: undefined,
|
||||
accountCount: undefined,
|
||||
expireTime: undefined,
|
||||
website: undefined,
|
||||
name: undefined as string | undefined,
|
||||
packageId: undefined as number | undefined,
|
||||
contactName: undefined as string | undefined,
|
||||
contactMobile: undefined as string | undefined,
|
||||
accountCount: undefined as number | undefined,
|
||||
expireTime: Date.now() as number | undefined,
|
||||
website: undefined as string | undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
// 新增专属
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
remark: undefined,
|
||||
aiExpireTime: undefined,
|
||||
brotherExpireTime: undefined,
|
||||
username: undefined as string | undefined,
|
||||
password: undefined as string | undefined,
|
||||
remark: undefined as string | undefined,
|
||||
aiExpireTime: undefined as number | undefined,
|
||||
brotherExpireTime: undefined as number | undefined,
|
||||
tenantType: undefined as number | undefined,
|
||||
parentId: undefined as number | undefined,
|
||||
|
||||
})
|
||||
|
||||
const userData = ref({} as userApi.UserVO)
|
||||
|
||||
let userList = ref([{
|
||||
id: 0,
|
||||
aiChat: 0,
|
||||
bigBrother: 0,
|
||||
crawl: 0
|
||||
}])
|
||||
const userList = ref<userApi.UserVO[]>([
|
||||
{
|
||||
id: 0,
|
||||
aiChat: 0,
|
||||
bigBrother: 0,
|
||||
crawl: 0
|
||||
} as any
|
||||
])
|
||||
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '租户名不能为空', trigger: 'blur' }],
|
||||
packageId: [{ required: true, message: '租户套餐不能为空', trigger: 'blur' }],
|
||||
contactName: [{ required: true, message: '联系人不能为空', trigger: 'blur' }],
|
||||
// contactMobile: [
|
||||
// { required: true, message: '手机号不能为空', trigger: 'blur' },
|
||||
// { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的 11 位手机号', trigger: ['blur', 'change'] }
|
||||
// ],
|
||||
status: [{ required: true, message: '租户状态不能为空', trigger: 'blur' }],
|
||||
accountCount: [{ required: true, message: '账号额度不能为空', trigger: 'blur' }],
|
||||
expireTime: [{ required: true, message: '过期时间不能为空', trigger: 'blur' }],
|
||||
@@ -153,46 +238,110 @@ const formRules = reactive({
|
||||
],
|
||||
password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const packageList = ref([] as TenantPackageApi.TenantPackageVO[]) // 租户套餐
|
||||
|
||||
const formRef = ref()
|
||||
const packageList = ref([] as TenantPackageApi.TenantPackageVO[])
|
||||
const packageAgencyList = ref([] as TenantPackageApi.TenantPackageVO[])
|
||||
// 代理套餐功能勾选:1=爬大哥,2=爬主播,3=AI套餐
|
||||
const selectedFeatures = ref<number[]>([])
|
||||
// 按套餐天数筛选:30 / 180 / 360(不选则不过滤)
|
||||
const selectedDays = ref<number | null>(null)
|
||||
// 二级代理套餐的 ID
|
||||
const SECOND_AGENT_PACKAGE_ID = 10001
|
||||
// 根据 selectedFeatures + packageAgencyList 计算可选套餐
|
||||
const filteredAgencyPackages = computed(() => {
|
||||
// 1. 根据创建/编辑确定基础套餐列表
|
||||
let list: any[] = []
|
||||
if (formType.value === 'create') {
|
||||
list = packageList.value || []
|
||||
} else {
|
||||
list = packageAgencyList.value || []
|
||||
}
|
||||
if (!list.length) return []
|
||||
|
||||
const selected = selectedFeatures.value
|
||||
|
||||
// 2. 先按功能勾选过滤(保持你原来的逻辑)
|
||||
let result: any[] = []
|
||||
if (!selected.length) {
|
||||
// 一个功能都没勾选时,直接返回全部套餐
|
||||
result = list
|
||||
} else {
|
||||
// 有勾选时才进行过滤
|
||||
result = list.filter((item: any) => {
|
||||
const typeStr = String(item.packageType ?? '')
|
||||
// 要求选中的功能全部包含在 packageType 里
|
||||
// 例如勾选 [1,2] -> 匹配 '12'、'123' 等
|
||||
return selected.every(flag => typeStr.includes(String(flag)))
|
||||
})
|
||||
}
|
||||
|
||||
// 3. 按天数过滤:item.days === 30 / 180 / 360
|
||||
if (selectedDays.value != null) {
|
||||
result = result.filter((item: any) => item.days === selectedDays.value)
|
||||
}
|
||||
|
||||
// 4. 再根据租户类型进行限制
|
||||
const tenantType = formData.value.tenantType
|
||||
if (tenantType === '代理') {
|
||||
// 只能选二级代理套餐(10001)
|
||||
result = result.filter(item => item.id === SECOND_AGENT_PACKAGE_ID)
|
||||
} else if (tenantType === '租户') {
|
||||
// 租户不能选二级代理套餐
|
||||
result = result.filter(item => item.id !== SECOND_AGENT_PACKAGE_ID)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
const open = async (type: 'create' | 'update', id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 1. 租户基础信息
|
||||
formData.value = await TenantApi.getTenant(id)
|
||||
getUserByTenant(id).then(res => {
|
||||
console.log(res)
|
||||
userList.value = res
|
||||
})
|
||||
|
||||
// 2. 拉取租户用户列表
|
||||
const res = await getUserByTenant(id)
|
||||
userList.value = res || []
|
||||
|
||||
// 3. 默认选中第一个用户(有的话)
|
||||
if (userList.value.length > 0) {
|
||||
userData.value = userList.value[0]
|
||||
; (userData.value as any).tenantId = formData.value.id
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
// 加载套餐列表
|
||||
|
||||
// 套餐列表
|
||||
packageList.value = await TenantPackageApi.getTenantPackageList()
|
||||
if (userStore.getUser.id === 1) {
|
||||
packageAgencyList.value = await TenantPackageApi.getTenantPackageListagency()
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
defineExpose({ open })
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const emit = defineEmits(['success'])
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as TenantApi.TenantVO
|
||||
if (formType.value === 'create') {
|
||||
//下次更改内容,创建账号 给一个权限 爬大哥和AI
|
||||
await TenantApi.createTenant(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
@@ -201,7 +350,6 @@ const submitForm = async () => {
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
@@ -211,36 +359,48 @@ const submitForm = async () => {
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
id: undefined as any,
|
||||
name: undefined,
|
||||
packageId: undefined,
|
||||
contactName: undefined,
|
||||
contactMobile: undefined,
|
||||
accountCount: undefined,
|
||||
expireTime: undefined,
|
||||
expireTime: Date.now(),
|
||||
website: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
remark: undefined,
|
||||
aiExpireTime: undefined, // ⭐ 加上
|
||||
brotherExpireTime: undefined, // ⭐ 加上
|
||||
aiExpireTime: undefined,
|
||||
brotherExpireTime: undefined
|
||||
}
|
||||
userData.value = {} as userApi.UserVO
|
||||
formRef.value?.resetFields()
|
||||
userData.value = {} as any
|
||||
userList.value = [
|
||||
{
|
||||
id: 0,
|
||||
aiChat: 0,
|
||||
bigBrother: 0,
|
||||
crawl: 0
|
||||
} as any
|
||||
]
|
||||
// ⭐ 每次打开弹窗时都清空功能选择
|
||||
selectedFeatures.value = []
|
||||
|
||||
formRef.value?.resetFields?.()
|
||||
}
|
||||
function changeUser(val) {
|
||||
userList.value.forEach(item => {
|
||||
|
||||
|
||||
/** 切换用户 */
|
||||
function changeUser(val: number) {
|
||||
userList.value.forEach((item) => {
|
||||
if (item.id === val) {
|
||||
userData.value = item
|
||||
userData.value.tenantId = formData.value.id
|
||||
|
||||
; (userData.value as any).tenantId = formData.value.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// AI 过期时间变化:没有时间时,禁止并关闭 ai 功能
|
||||
/** 联动:AI 过期时间 -> AI 权限 */
|
||||
watch(
|
||||
() => formData.value.aiExpireTime,
|
||||
(val) => {
|
||||
@@ -251,7 +411,7 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// 爬大哥 过期时间变化:没有时间时,禁止并关闭 bigBrother
|
||||
/** 联动:爬大哥过期时间 -> bigBrother 权限 */
|
||||
watch(
|
||||
() => formData.value.brotherExpireTime,
|
||||
(val) => {
|
||||
@@ -261,3 +421,35 @@ watch(
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style scoped>
|
||||
.package-radio-wrapper {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 让按钮自动换行,并留点间隔 */
|
||||
.package-radio-wrapper :deep(.el-radio-group) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 美化每个 radio-button 的外观(胶囊 + 内边距) */
|
||||
.package-radio-wrapper :deep(.el-radio-button__inner) {
|
||||
border-radius: 999px;
|
||||
padding: 4px 10px;
|
||||
border: 1px solid #dcdfe6;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
/* 选中状态可以稍微加粗/加边框,Element Plus 自带选中样式,这里可选 */
|
||||
.package-radio-wrapper :deep(.is-active .el-radio-button__inner) {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
/* 空数据提示 */
|
||||
.package-empty-tip {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
104
src/views/system/tenant/TenantRenewForm.vue
Normal file
104
src/views/system/tenant/TenantRenewForm.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<Dialog :title="'租户续费'" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<!-- id:选中租户的 id,只读展示 -->
|
||||
<el-form-item label="租户 ID" prop="id">
|
||||
<el-input v-model="formData.id" disabled />
|
||||
</el-form-item>
|
||||
|
||||
<!-- packageId:套餐选择框 -->
|
||||
<el-form-item label="续费套餐" prop="packageId">
|
||||
<el-select v-model="formData.packageId" placeholder="请选择套餐" class="w-240px">
|
||||
<el-option v-for="pkg in packageList" :key="pkg.id" :label="pkg.name" :value="pkg.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- remark:备注 -->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" :rows="3" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="formLoading" @click="submitForm">
|
||||
确 定
|
||||
</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
import * as TenantApi from '@/api/system/tenant'
|
||||
import * as TenantPackageApi from '@/api/system/tenantPackage'
|
||||
|
||||
const message = useMessage()
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref()
|
||||
const formLoading = ref(false)
|
||||
|
||||
// 表单数据:id / packageId / remark
|
||||
const formData = reactive<{
|
||||
id: number | null
|
||||
packageId: number | null
|
||||
remark: string
|
||||
}>({
|
||||
id: null,
|
||||
packageId: null,
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
packageId: [{ required: true, message: '请选择续费套餐', trigger: 'change' }],
|
||||
// 如果备注必填就放开下面这一条
|
||||
// remark: [{ required: true, message: '请输入备注', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 套餐列表
|
||||
const packageList = ref<TenantPackageApi.TenantPackageVO[]>([])
|
||||
|
||||
// 对外暴露的 open 方法:传入租户 id
|
||||
const open = async (id: number) => {
|
||||
// 重置表单
|
||||
formData.id = id
|
||||
formData.packageId = null
|
||||
formData.remark = ''
|
||||
dialogVisible.value = true
|
||||
|
||||
// 需要时再加载套餐列表
|
||||
if (!packageList.value.length) {
|
||||
packageList.value = await TenantPackageApi.getTenantPackageList()
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
|
||||
// 提交
|
||||
const submitForm = () => {
|
||||
formRef.value?.validate(async (valid: boolean) => {
|
||||
if (!valid) return
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 🔴 根据你原来续费的接口改这里的调用
|
||||
// 假设后端有一个 TenantApi.renewTenant
|
||||
await TenantApi.renewal({
|
||||
id: formData.id,
|
||||
packageId: formData.packageId,
|
||||
remark: formData.remark
|
||||
})
|
||||
|
||||
message.success('续费成功')
|
||||
dialogVisible.value = false
|
||||
emit('success') // 通知父组件刷新列表
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -73,24 +73,25 @@
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
|
||||
<el-table v-loading="loading" :data="list" row-key="id" lazy :load="loadChildren"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren', checkStrictly: false }"
|
||||
@selection-change="handleRowCheckboxChange" :row-class-name="rowClassName">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="租户编号" align="center" prop="id" />
|
||||
<el-table-column label="租户编号" prop="id" width="100" />
|
||||
<el-table-column label="租户名" align="center" prop="name" />
|
||||
<el-table-column label="租户类型" align="center" prop="tenantType" />
|
||||
<el-table-column label="租户套餐" align="center" prop="packageId">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.packageId === 0" type="danger">系统租户</el-tag>
|
||||
<template v-else v-for="item in packageList">
|
||||
<el-tag type="success" :key="item.id" v-if="item.id === scope.row.packageId">
|
||||
{{ item.name }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<el-tag :type="scope.row.packageId === 0 ? 'danger' : 'success'">
|
||||
{{ getPackageName(scope.row.packageId) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系人" align="center" prop="contactName" />
|
||||
<el-table-column label="联系手机" align="center" prop="contactMobile" />
|
||||
<el-table-column label="联系手机" align="center" prop="contactMobile" />1
|
||||
<el-table-column label="账号额度" align="center" prop="accountCount">
|
||||
<template #default="scope">
|
||||
<el-tag>{{ scope.row.accountCount }}</el-tag>
|
||||
@@ -98,7 +99,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="AI时间" align="center" prop="aiExpireTime" width="180" :formatter="dateFormatter" />
|
||||
<el-table-column label="爬大哥时间" align="center" prop="brotherExpireTime" width="180" :formatter="dateFormatter" />
|
||||
<el-table-column label="爬虫后台过期时间" align="center" prop="expireTime" width="180" :formatter="dateFormatter" />
|
||||
<el-table-column label="后台管理过期时间" align="center" prop="expireTime" width="180" :formatter="dateFormatter" />
|
||||
<el-table-column label="绑定域名" align="center" prop="website" width="180" />
|
||||
<el-table-column label="租户状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
@@ -107,12 +108,16 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180" :formatter="dateFormatter" />
|
||||
<el-table-column label="操作" align="center" min-width="110" fixed="right">
|
||||
<el-table-column label="操作" align="center" min-width="160" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['system:tenant:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button v-if="userStore.getUser.id != 1" link type="warning" @click="openRenewForm(scope.row.id)"
|
||||
v-hasPermi="['system:tenant:renewal']">
|
||||
续费
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['system:tenant:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
@@ -124,8 +129,10 @@
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<TenantForm ref="formRef" @success="getList" />
|
||||
<TenantRenewForm ref="renewFormRef" @success="getList" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
@@ -134,9 +141,12 @@ import download from '@/utils/download'
|
||||
import * as TenantApi from '@/api/system/tenant'
|
||||
import * as TenantPackageApi from '@/api/system/tenantPackage'
|
||||
import TenantForm from './TenantForm.vue'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { toRaw } from 'vue' // 🔴 新增
|
||||
import TenantRenewForm from './TenantRenewForm.vue'
|
||||
|
||||
defineOptions({ name: 'SystemTenant' })
|
||||
|
||||
const userStore = useUserStore()
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
@@ -150,6 +160,7 @@ const queryParams = reactive({
|
||||
contactName: undefined,
|
||||
contactMobile: undefined,
|
||||
status: undefined,
|
||||
tenantType: undefined,
|
||||
remark: undefined,
|
||||
createTime: [],
|
||||
aiExpireTime: [],
|
||||
@@ -157,21 +168,39 @@ const queryParams = reactive({
|
||||
expireTime: [],
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const renewFormRef = ref() // 续费弹窗
|
||||
|
||||
const openRenewForm = (id: number) => {
|
||||
renewFormRef.value.open(id)
|
||||
}
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const packageList = ref([] as TenantPackageApi.TenantPackageVO[]) //租户套餐列表
|
||||
const packageAgencyList = ref([] as TenantPackageApi.TenantPackageVO[]) //租户套餐列表
|
||||
|
||||
/** 查询列表 */
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TenantApi.getTenantPage(queryParams)
|
||||
list.value = data.list
|
||||
let data: any = ''
|
||||
if (userStore.getUser.id === 1) {
|
||||
data = await TenantApi.getTenantPage(queryParams)
|
||||
} else {
|
||||
data = await TenantApi.getTenantPageSelf(queryParams)
|
||||
}
|
||||
|
||||
// 给顶级节点加 level = 1
|
||||
list.value = (data.list || []).map((item: any) => ({
|
||||
...item,
|
||||
level: 1
|
||||
}))
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
@@ -186,7 +215,7 @@ const resetQuery = () => {
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
const openForm = (type: 'create' | 'update' | 'renew', id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
@@ -235,11 +264,96 @@ const handleExport = async () => {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
const getPackageName = (packageId: number) => {
|
||||
if (userStore.getUser.id === 0) return '系统租户'
|
||||
|
||||
// 先从 packageList 找
|
||||
const pkg = packageList.value.find((p) => p.id === packageId)
|
||||
if (pkg) return pkg.name
|
||||
|
||||
// packageList 未找到,并且 packageId !== 1 时,再从 packageAgencyList 找
|
||||
if (packageId !== 1) {
|
||||
const agency = packageAgencyList.value.find((a) => a.id === packageId)
|
||||
if (agency) return agency.name
|
||||
}
|
||||
|
||||
return '未知套餐'
|
||||
}
|
||||
|
||||
// 懒加载子租户(树形表格用)
|
||||
const loadChildren = async (
|
||||
row: TenantApi.TenantVO & { level?: number },
|
||||
treeNode: unknown,
|
||||
resolve: (data: TenantApi.TenantVO[]) => void
|
||||
) => {
|
||||
try {
|
||||
const baseParams = toRaw(queryParams) as any
|
||||
const params = {
|
||||
...baseParams,
|
||||
pageNo: 1,
|
||||
pageSize: 999,
|
||||
id: row.id
|
||||
}
|
||||
|
||||
let data: any
|
||||
data = await TenantApi.getTenantChildren(params)
|
||||
|
||||
const parentLevel = row.level ?? 1
|
||||
|
||||
// 子节点 level = 父节点 level + 1
|
||||
const children = (data || []).map((item: any) => ({
|
||||
...item,
|
||||
level: parentLevel + 1
|
||||
}))
|
||||
|
||||
resolve(children)
|
||||
} catch (e) {
|
||||
console.error('loadChildren error', e)
|
||||
resolve([])
|
||||
}
|
||||
}
|
||||
|
||||
// 行样式 class
|
||||
const rowClassName = ({ row }: { row: any }) => {
|
||||
if (row.level === 2) return 'row-level-2'
|
||||
if (row.level >= 3) return 'row-level-3'
|
||||
return ''
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 获取租户套餐列表
|
||||
packageList.value = await TenantPackageApi.getTenantPackageList()
|
||||
if (userStore.getUser.id === 1) {
|
||||
packageAgencyList.value = await TenantPackageApi.getTenantPackageListagency()
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
/* 二级行背景色 */
|
||||
:deep(.row-level-2) {
|
||||
background-color: #fdf6ec;
|
||||
/* 浅橙色,你可以自己换 */
|
||||
}
|
||||
|
||||
/* 三级及以下行背景色 */
|
||||
:deep(.row-level-3) {
|
||||
background-color: #dedeff;
|
||||
/* 浅灰色 */
|
||||
}
|
||||
|
||||
/* 可选:加个左边彩色竖条,更醒目 */
|
||||
:deep(.row-level-2 td:first-child),
|
||||
:deep(.row-level-3 td:first-child) {
|
||||
border-left: 4px solid #e6a23c;
|
||||
/* 二级竖条 */
|
||||
}
|
||||
|
||||
:deep(.row-level-3 td:first-child) {
|
||||
border-left-color: #3636e0;
|
||||
/* 三级竖条 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" :rules="formRules" label-width="80px">
|
||||
<el-form-item label="套餐名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入套餐名" />
|
||||
</el-form-item>
|
||||
@@ -14,39 +8,19 @@
|
||||
<el-card class="w-full h-400px !overflow-y-scroll" shadow="never">
|
||||
<template #header>
|
||||
全选/全不选:
|
||||
<el-switch
|
||||
v-model="treeNodeAll"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
inline-prompt
|
||||
@change="handleCheckedTreeNodeAll"
|
||||
/>
|
||||
<el-switch v-model="treeNodeAll" active-text="是" inactive-text="否" inline-prompt
|
||||
@change="handleCheckedTreeNodeAll" />
|
||||
全部展开/折叠:
|
||||
<el-switch
|
||||
v-model="menuExpand"
|
||||
active-text="展开"
|
||||
inactive-text="折叠"
|
||||
inline-prompt
|
||||
@change="handleCheckedTreeExpand"
|
||||
/>
|
||||
<el-switch v-model="menuExpand" active-text="展开" inactive-text="折叠" inline-prompt
|
||||
@change="handleCheckedTreeExpand" />
|
||||
</template>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="menuOptions"
|
||||
:props="defaultProps"
|
||||
empty-text="加载中,请稍候"
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
/>
|
||||
<el-tree ref="treeRef" :data="menuOptions" :props="defaultProps" empty-text="加载中,请稍候" node-key="id"
|
||||
show-checkbox />
|
||||
</el-card>
|
||||
</el-form-item>
|
||||
<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"
|
||||
:value="dict.value"
|
||||
>
|
||||
<el-radio v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" :key="dict.value" :value="dict.value">
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
|
||||
249
src/views/system/tenantagencypackage/TenantAgencyPackageForm.vue
Normal file
249
src/views/system/tenantagencypackage/TenantAgencyPackageForm.vue
Normal file
@@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<el-form-item label="套餐名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入套餐名" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="租户状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="0">正常</el-radio>
|
||||
<el-radio :value="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- ✅ 改成树形菜单选择 -->
|
||||
<el-form-item label="菜单权限" prop="menuIds">
|
||||
<el-card class="w-full h-400px !overflow-y-scroll" shadow="never">
|
||||
<template #header>
|
||||
全选/全不选:
|
||||
<el-switch v-model="treeNodeAll" active-text="是" inactive-text="否" inline-prompt
|
||||
@change="handleCheckedTreeNodeAll" />
|
||||
全部展开/折叠:
|
||||
<el-switch v-model="menuExpand" active-text="展开" inactive-text="折叠" inline-prompt
|
||||
@change="handleCheckedTreeExpand" />
|
||||
</template>
|
||||
<el-tree ref="treeRef" :data="menuOptions" :props="defaultProps" empty-text="加载中,请稍候" node-key="id"
|
||||
show-checkbox />
|
||||
</el-card>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="套餐天数" prop="days">
|
||||
<el-input v-model="formData.days" placeholder="请输入套餐天数" />
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐价格" prop="price">
|
||||
<el-input v-model="formData.price" placeholder="请输入套餐价格" />
|
||||
</el-form-item>
|
||||
<!-- 爬大哥:必须先选“爬大哥过期时间” -->
|
||||
<el-form-item label="爬大哥">
|
||||
<el-radio-group v-model="formData.brotherClient">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
<el-radio :value="0">关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- ai自动化:必须先选“AI过期时间” -->
|
||||
<el-form-item label="爬主播">
|
||||
<el-radio-group v-model="formData.hostslClient">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
<el-radio :value="0">关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- ai回复:必须先选“AI过期时间” -->
|
||||
<el-form-item label="AI客户端">
|
||||
<el-radio-group v-model="formData.aiClient">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
<el-radio :value="0">关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- ai回复:必须先选“AI过期时间” -->
|
||||
<el-form-item label="AI回复">
|
||||
<el-radio-group v-model="formData.aiReplay">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
<el-radio :value="0">关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- WEB回复:必须先选“AI过期时间” -->
|
||||
<el-form-item label="WEB客户端">
|
||||
<el-radio-group v-model="formData.webAi">
|
||||
<el-radio :value="1">开启</el-radio>
|
||||
<el-radio :value="0">关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐类型" prop="packageType">
|
||||
<el-input v-model="formData.packageType" placeholder="请输入套餐类型" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { TenantAgencyPackageApi, TenantAgencyPackageVO } from '@/api/system/tenantagencypackage'
|
||||
import * as MenuApi from '@/api/system/menu'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import { ElTree } from 'element-plus'
|
||||
|
||||
/** 代理租户套餐 表单 */
|
||||
defineOptions({ name: 'TenantAgencyPackageForm' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const formLoading = ref(false)
|
||||
const formType = ref<'create' | 'update' | string>('')
|
||||
|
||||
const formData = ref({
|
||||
id: undefined as number | undefined,
|
||||
name: undefined as string | undefined,
|
||||
status: 0 as 0 | 1 | undefined, // 默认正常
|
||||
remark: undefined as string | undefined,
|
||||
menuIds: [] as number[], // ✅ 改成数组
|
||||
days: undefined as number | undefined,
|
||||
price: undefined as number | undefined,
|
||||
hostslClient: undefined as number | undefined,
|
||||
brotherClient: undefined as number | undefined,
|
||||
aiClient: undefined as number | undefined,
|
||||
aiReplay: undefined as number | undefined,
|
||||
webAi: undefined as number | undefined,
|
||||
packageType: undefined as string | undefined
|
||||
|
||||
})
|
||||
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '套餐名不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '租户状态不能为空', trigger: 'change' }],
|
||||
// menuIds: [{ required: true, message: '关联的菜单编号不能为空', trigger: 'change' }], // ✅ 校验菜单
|
||||
days: [{ required: true, message: '套餐天数不能为空', trigger: 'blur' }],
|
||||
price: [{ required: true, message: '套餐价格不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref()
|
||||
|
||||
// ✅ 菜单树相关
|
||||
const menuOptions = ref<any[]>([])
|
||||
const menuExpand = ref(false)
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const treeNodeAll = ref(false)
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
await resetForm()
|
||||
|
||||
// 先加载菜单树数据(一定要先有数据,后 setChecked)
|
||||
formLoading.value = true
|
||||
try {
|
||||
const menus = await MenuApi.getSimpleMenusList()
|
||||
menuOptions.value = handleTree(menus)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
||||
// 编辑:回显数据并勾选
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = await TenantAgencyPackageApi.getTenantAgencyPackage(id)
|
||||
formData.value = {
|
||||
...formData.value,
|
||||
...data,
|
||||
menuIds: Array.isArray(data.menuIds) ? data.menuIds : []
|
||||
}
|
||||
// 勾选已有关联
|
||||
formData.value.menuIds.forEach((mid: number) => {
|
||||
treeRef.value?.setChecked(mid, true, false)
|
||||
})
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success'])
|
||||
const submitForm = async () => {
|
||||
// 1. 先做表单校验
|
||||
await formRef.value.validate()
|
||||
|
||||
// 2. 从树组件拿选中的菜单 id(包含半选)
|
||||
const checked = (treeRef.value?.getCheckedKeys(false) || []) as number[]
|
||||
const half = (treeRef.value?.getHalfCheckedKeys() || []) as number[]
|
||||
const menuIds = Array.from(new Set([...checked, ...half])) // 去重一下
|
||||
|
||||
// 3. 把 menuIds 塞回 formData(或者直接合并到 data 里)
|
||||
formData.value.menuIds = menuIds
|
||||
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 4. 组装真正发给后端的参数
|
||||
const data = {
|
||||
...(formData.value as any),
|
||||
menuIds // ✅ 确保有这个字段
|
||||
} as TenantAgencyPackageVO
|
||||
if (formType.value === 'create') {
|
||||
await TenantAgencyPackageApi.createTenantAgencyPackage(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await TenantAgencyPackageApi.updateTenantAgencyPackage(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = async () => {
|
||||
treeNodeAll.value = false
|
||||
menuExpand.value = false
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: 0,
|
||||
remark: undefined,
|
||||
menuIds: [], // ✅ 清空
|
||||
days: undefined,
|
||||
price: undefined,
|
||||
hostslClient: undefined,
|
||||
brotherClient: undefined,
|
||||
aiClient: undefined,
|
||||
aiReplay: undefined,
|
||||
packageType: undefined
|
||||
}
|
||||
treeRef.value?.setCheckedNodes([])
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 全选/全不选 */
|
||||
const handleCheckedTreeNodeAll = () => {
|
||||
treeRef.value?.setCheckedNodes(treeNodeAll.value ? menuOptions.value : [])
|
||||
}
|
||||
|
||||
/** 展开/折叠全部 */
|
||||
const handleCheckedTreeExpand = () => {
|
||||
const nodes = (treeRef.value as any)?.store?.nodesMap
|
||||
if (!nodes) return
|
||||
for (const key in nodes) {
|
||||
if (Object.prototype.hasOwnProperty.call(nodes, key)) {
|
||||
nodes[key].expanded = menuExpand.value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
201
src/views/system/tenantagencypackage/index.vue
Normal file
201
src/views/system/tenantagencypackage/index.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
|
||||
<el-form-item label="套餐名" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入套餐名" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户状态 " prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择租户状态 " clearable class="!w-240px">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="queryParams.remark" placeholder="请输入备注" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-220px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐天数" prop="days">
|
||||
<el-input v-model="queryParams.days" placeholder="请输入套餐天数" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐价格" prop="price">
|
||||
<el-input v-model="queryParams.price" placeholder="请输入套餐价格" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="爬主播客户端 " prop="hostslClient">
|
||||
<el-input v-model="queryParams.hostslClient" placeholder="请输入爬主播客户端 " clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="爬大哥客户端 " prop="brotherClient">
|
||||
<el-input v-model="queryParams.brotherClient" placeholder="请输入爬大哥客户端 " clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="AI 客户端 " prop="aiClient">
|
||||
<el-input v-model="queryParams.aiClient" placeholder="请输入AI 客户端 " clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐类型" prop="packageType">
|
||||
<el-select v-model="queryParams.packageType" placeholder="请选择套餐类型" clearable class="!w-240px">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm('create')"
|
||||
v-hasPermi="['system:tenant-agency-package:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['system:tenant-agency-package:export']">
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="套餐编号" align="center" prop="id" />
|
||||
<el-table-column label="套餐名" align="center" prop="name" width="200px" />
|
||||
<!-- <el-table-column label="租户状态 " align="center" prop="status" /> -->
|
||||
<!-- <el-table-column label="关联的菜单编号" align="center" prop="menuIds" /> -->
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="套餐天数" align="center" prop="days" />
|
||||
<el-table-column label="套餐价格" align="center" prop="price" />
|
||||
<!-- <el-table-column label="爬主播客户端 " align="center" prop="hostslClient" />
|
||||
<el-table-column label="爬大哥客户端 " align="center" prop="brotherClient" />
|
||||
<el-table-column label="AI 客户端 " align="center" prop="aiClient" /> -->
|
||||
<el-table-column label="套餐类型" align="center" prop="packageType" />
|
||||
<el-table-column label="备注" align="center" prop="remark" min-width="50px" />
|
||||
|
||||
<el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['system:tenant-agency-package:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['system:tenant-agency-package:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<TenantAgencyPackageForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { TenantAgencyPackageApi, TenantAgencyPackageVO } from '@/api/system/tenantagencypackage'
|
||||
import TenantAgencyPackageForm from './TenantAgencyPackageForm.vue'
|
||||
|
||||
/** 代理租户套餐 列表 */
|
||||
defineOptions({ name: 'TenantAgencyPackage' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<TenantAgencyPackageVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
remark: undefined,
|
||||
menuIds: undefined,
|
||||
createTime: [],
|
||||
days: undefined,
|
||||
price: undefined,
|
||||
hostslClient: undefined,
|
||||
brotherClient: undefined,
|
||||
aiClient: undefined,
|
||||
packageType: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TenantAgencyPackageApi.getTenantAgencyPackagePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await TenantAgencyPackageApi.deleteTenantAgencyPackage(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await TenantAgencyPackageApi.exportTenantAgencyPackage(queryParams)
|
||||
download.excel(data, '代理租户套餐.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
169
src/views/system/tenantbalance/TenantBalanceForm.vue
Normal file
169
src/views/system/tenantbalance/TenantBalanceForm.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form v-if="formType === 'update'" ref="formRef" :model="formData" :rules="formRules" label-width="100px"
|
||||
v-loading="formLoading">
|
||||
<el-form-item label="充值用户" prop="id">
|
||||
<el-input v-model="formData.id" placeholder="请输入充值用户" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formType === 'update'" label="充值金额" prop="amount">
|
||||
<!-- 改为 el-input-number,仅允许数值输入,且最小 0 -->
|
||||
<el-input-number v-model.number="formData.amount" :min="0" controls-position="right" placeholder="请输入充值金额" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 只有 transfer 类型时显示 -->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
|
||||
<el-form v-if="formType === 'transfer'" ref="formRef" :model="formData" :rules="formRules" label-width="100px"
|
||||
v-loading="formLoading">
|
||||
<el-form-item label="转账用户" prop="targetTenantId">
|
||||
<el-input v-model="formData.targetTenantId" placeholder="请输入转账用户" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formType === 'transfer'" label="转账金额" prop="transferAmount">
|
||||
<!-- 改为 el-input-number,仅允许数值输入,且最小 0 -->
|
||||
<el-input-number v-model.number="formData.transferAmount" :min="0" controls-position="right"
|
||||
placeholder="请输入转账金额" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formType === 'transfer'" label="登录密码" prop="password">
|
||||
<el-input v-model="formData.password" type="password" show-password placeholder="请输入登录密码" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 只有 transfer 类型时显示 -->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, reactive } from 'vue'
|
||||
import { TenantBalanceApi, TenantBalanceVO } from '@/api/system/tenantbalance'
|
||||
|
||||
/** 租户余额 表单 */
|
||||
defineOptions({ name: 'TenantBalanceForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改;transfer - 转账等
|
||||
|
||||
const formData = ref({
|
||||
tenantId: undefined as number | undefined,
|
||||
id: undefined as number | undefined,
|
||||
amount: undefined as number | undefined,
|
||||
transferAmount: undefined as number | undefined,
|
||||
targetTenantId: undefined as number | undefined,
|
||||
remark: undefined as string | undefined, // 变动表述
|
||||
password: undefined as string | undefined // 登录密码
|
||||
})
|
||||
|
||||
/** 校验规则:transfer 时才要求 remark 必填 */
|
||||
const formRules = computed(() => {
|
||||
const baseRules: any = {
|
||||
id: [{ required: true, message: '用户不能为空', trigger: 'blur' }],
|
||||
amount: [{ required: true, message: '金额不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
if (formType.value === 'transfer') {
|
||||
|
||||
baseRules.password = [
|
||||
{ required: true, message: '登录密码不能为空', trigger: 'blur' }
|
||||
]
|
||||
baseRules.transferAmount = [
|
||||
{ required: true, message: '转账金额不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
return baseRules
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = await TenantBalanceApi.getTenantBalance(id)
|
||||
// 保证有 remark 和 password 字段(接口没有的话就保持 undefined)
|
||||
formData.value = {
|
||||
tenantId: data.tenantId,
|
||||
id: data.id,
|
||||
amount: data.amount,
|
||||
targetTenantId: data.id,
|
||||
remark: undefined,
|
||||
password: undefined,
|
||||
transferAmount: undefined
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
console.log(formData.value)
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as TenantBalanceVO
|
||||
if (formType.value === 'create') {
|
||||
await TenantBalanceApi.createTenantBalance(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else if (formType.value === 'transfer') {
|
||||
await TenantBalanceApi.tenantTransfer(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
} else {
|
||||
await TenantBalanceApi.addAmount(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
tenantId: undefined,
|
||||
id: undefined,
|
||||
amount: undefined,
|
||||
transferAmount: undefined,
|
||||
remark: undefined,
|
||||
password: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
132
src/views/system/tenantbalance/components/TenantPointsForm.vue
Normal file
132
src/views/system/tenantbalance/components/TenantPointsForm.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<el-form-item label="本次变动点数,正加负减" prop="points">
|
||||
<el-input v-model="formData.points" placeholder="请输入本次变动点数,正加负减" />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动后余额快照(冗余)" prop="balance">
|
||||
<el-input v-model="formData.balance" placeholder="请输入变动后余额快照(冗余)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动类型,如 RECHARGE, CONSUME, TRANSFER_OUT, TRANSFER_IN" prop="type">
|
||||
<el-select v-model="formData.type" placeholder="请选择变动类型,如 RECHARGE, CONSUME, TRANSFER_OUT, TRANSFER_IN">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<Editor v-model="formData.remark" height="150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="订单 Id/业务单号" prop="orderId">
|
||||
<el-input v-model="formData.orderId" placeholder="请输入订单 Id/业务单号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务流水号(转账、订单等唯一标识)" prop="bizNo">
|
||||
<el-input v-model="formData.bizNo" placeholder="请输入业务流水号(转账、订单等唯一标识)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="操作人 Id" prop="operatorId">
|
||||
<el-input v-model="formData.operatorId" placeholder="请输入操作人 Id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标租户 Id(转账使用)" prop="targetTenantId">
|
||||
<el-input v-model="formData.targetTenantId" placeholder="请输入目标租户 Id(转账使用)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="formData.createdAt" type="date" value-format="x" placeholder="选择创建时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { TenantBalanceApi } from '@/api/system/tenantbalance'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
points: undefined,
|
||||
balance: undefined,
|
||||
type: undefined,
|
||||
remark: undefined,
|
||||
orderId: undefined,
|
||||
bizNo: undefined,
|
||||
operatorId: undefined,
|
||||
targetTenantId: undefined,
|
||||
createdAt: undefined,
|
||||
remark: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
points: [{ required: true, message: '本次变动点数,正加负减不能为空', trigger: 'blur' }],
|
||||
createdAt: [{ required: true, message: '创建时间不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number, tenantId: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.tenantId = tenantId
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await TenantBalanceApi.getTenantPoints(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value
|
||||
if (formType.value === 'create') {
|
||||
await TenantBalanceApi.createTenantPoints(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await TenantBalanceApi.updateTenantPoints(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
points: undefined,
|
||||
balance: undefined,
|
||||
type: undefined,
|
||||
remark: undefined,
|
||||
orderId: undefined,
|
||||
bizNo: undefined,
|
||||
operatorId: undefined,
|
||||
targetTenantId: undefined,
|
||||
createdAt: undefined,
|
||||
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
112
src/views/system/tenantbalance/components/TenantPointsList.vue
Normal file
112
src/views/system/tenantbalance/components/TenantPointsList.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<!-- <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['system:tenant-balance:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button> -->
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<!-- <el-table-column label="主键" align="center" prop="id" /> -->
|
||||
<el-table-column label="金额" align="center" prop="points" />
|
||||
<el-table-column label="变动后余额" align="center" prop="balance" />
|
||||
<el-table-column label="类型" align="center" prop="type" />
|
||||
<el-table-column label="描述" align="center" prop="description" />
|
||||
<el-table-column label="订单 Id/业务单号" align="center" prop="orderId" />
|
||||
<el-table-column label="业务流水号" align="center" prop="bizNo" />
|
||||
<el-table-column label="操作人 Id" align="center" prop="operatorId" />
|
||||
<el-table-column label="目标租户Id" align="center" prop="targetTenantId" />
|
||||
<el-table-column label="创建时间" align="center" prop="createdAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['system:tenant-balance:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['system:tenant-balance:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<TenantPointsForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { TenantBalanceApi } from '@/api/system/tenantbalance'
|
||||
import TenantPointsForm from './TenantPointsForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps<{
|
||||
tenantId?: number // 租户 Id(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
tenantId: undefined as unknown
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.tenantId,
|
||||
(val: number) => {
|
||||
if (!val) {
|
||||
return
|
||||
}
|
||||
queryParams.tenantId = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TenantBalanceApi.getTenantPointsPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
if (!props.tenantId) {
|
||||
message.error('请选择一个租户余额')
|
||||
return
|
||||
}
|
||||
formRef.value.open(type, id, props.tenantId)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await TenantBalanceApi.deleteTenantPoints(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
</script>
|
||||
220
src/views/system/tenantbalance/index.vue
Normal file
220
src/views/system/tenantbalance/index.vue
Normal file
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
|
||||
<el-form-item label="积分余额" prop="balance">
|
||||
<el-input v-model="queryParams.balance" placeholder="请输入当前积分余额" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户id" prop="id">
|
||||
<el-input v-model="queryParams.id" placeholder="请输入租户id" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户名" prop="tenantName">
|
||||
<el-input v-model="queryParams.tenantName" placeholder="请输入租户id" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker v-model="queryParams.updatedAt" value-format="YYYY-MM-DD" type="date" placeholder="选择更新时间"
|
||||
clearable class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%;">
|
||||
<div class="center-align">
|
||||
<div>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['system:tenant-balance:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['system:tenant-balance:export']">
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="userStore.getUser.id !== 1" class="balance-text">余额:{{ mount.balance }} 测试号:{{
|
||||
mount.testAccountNum }}</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" highlight-current-row
|
||||
@current-change="handleCurrentChange">
|
||||
<el-table-column label="当前积分余额" align="center" prop="balance" />
|
||||
<el-table-column label="租户名" align="center" prop="tenantName" />
|
||||
<el-table-column label="租户id" align="center" prop="id" />
|
||||
<el-table-column label="更新时间" align="center" prop="updatedAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="描述" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['system:tenant-balance:update']">
|
||||
充值
|
||||
</el-button>
|
||||
<el-button v-if="userStore.getUser.id !== 1" link type="primary" @click="openForm('transfer', scope.row.id)"
|
||||
v-hasPermi="['system:tenant-balance:transfer']">
|
||||
转账
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['system:tenant-balance:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<TenantBalanceForm ref="formRef" @success="getList" />
|
||||
<!-- 子表的列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs model-value="tenantPoints">
|
||||
<el-tab-pane label="变动记录" name="tenantPoints">
|
||||
<TenantPointsList :tenant-id="currentRow.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { TenantBalanceApi, TenantBalanceVO } from '@/api/system/tenantbalance'
|
||||
import TenantBalanceForm from './TenantBalanceForm.vue'
|
||||
import TenantPointsList from './components/TenantPointsList.vue'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
const userStore = ref(useUserStore())
|
||||
/** 租户余额 列表 */
|
||||
defineOptions({ name: 'TenantBalance' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<TenantBalanceVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
balance: undefined,
|
||||
version: undefined,
|
||||
updatedAt: undefined,
|
||||
updatedAt: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const mount = ref({})
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
let data = {}
|
||||
console.log(checkPermi(['system:tenant-balance:self-subordinate']))
|
||||
if (userStore.value.getUser.id !== 1) {
|
||||
getMount()
|
||||
data = await TenantBalanceApi.getSubordinateaMountPage(queryParams)
|
||||
} else {
|
||||
data = await TenantBalanceApi.getTenantBalancePage(queryParams)
|
||||
}
|
||||
// if (checkPermi(['system:tenant-balance:self-subordinate'])) {
|
||||
|
||||
// } else {
|
||||
|
||||
// }
|
||||
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
/** 查询列表 */
|
||||
const getMount = async () => {
|
||||
const data = await TenantBalanceApi.getselfamount()
|
||||
mount.value = data
|
||||
|
||||
}
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await TenantBalanceApi.deleteTenantBalance(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await TenantBalanceApi.exportTenantBalance(queryParams)
|
||||
download.excel(data, '租户余额.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 选中行操作 */
|
||||
const currentRow = ref({}) // 选中行
|
||||
const handleCurrentChange = (row) => {
|
||||
currentRow.value = row
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.center-align {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.balance-text {
|
||||
margin-left: auto;
|
||||
/* 把余额顶到最右边 */
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
129
src/views/system/tenantpoints/TenantPointsForm.vue
Normal file
129
src/views/system/tenantpoints/TenantPointsForm.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<el-form-item label="本次变动点数,正加负减" prop="points">
|
||||
<el-input v-model="formData.points" placeholder="请输入本次变动点数,正加负减" />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动后余额快照(冗余)" prop="balance">
|
||||
<el-input v-model="formData.balance" placeholder="请输入变动后余额快照(冗余)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动类型,如 RECHARGE, CONSUME, TRANSFER_OUT, TRANSFER_IN" prop="type">
|
||||
<el-select v-model="formData.type" placeholder="请选择变动类型,如 RECHARGE, CONSUME, TRANSFER_OUT, TRANSFER_IN">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="description">
|
||||
<Editor v-model="formData.remark" height="150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="订单 Id/业务单号" prop="orderId">
|
||||
<el-input v-model="formData.orderId" placeholder="请输入订单 Id/业务单号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务流水号(转账、订单等唯一标识)" prop="bizNo">
|
||||
<el-input v-model="formData.bizNo" placeholder="请输入业务流水号(转账、订单等唯一标识)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="操作人 Id" prop="operatorId">
|
||||
<el-input v-model="formData.operatorId" placeholder="请输入操作人 Id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标租户 Id(转账使用)" prop="targetTenantId">
|
||||
<el-input v-model="formData.targetTenantId" placeholder="请输入目标租户 Id(转账使用)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="formData.createdAt" type="date" value-format="x" placeholder="选择创建时间" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { TenantPointsApi, TenantPointsVO } from '@/api/system/tenantpoints'
|
||||
|
||||
/** 租户积分记录 表单 */
|
||||
defineOptions({ name: 'TenantPointsForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
points: undefined,
|
||||
balance: undefined,
|
||||
type: undefined,
|
||||
remark: undefined,
|
||||
orderId: undefined,
|
||||
bizNo: undefined,
|
||||
operatorId: undefined,
|
||||
targetTenantId: undefined,
|
||||
createdAt: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
points: [{ required: true, message: '本次变动点数,正加负减不能为空', trigger: 'blur' }],
|
||||
createdAt: [{ required: true, message: '创建时间不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await TenantPointsApi.getTenantPoints(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as TenantPointsVO
|
||||
if (formType.value === 'create') {
|
||||
await TenantPointsApi.createTenantPoints(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await TenantPointsApi.updateTenantPoints(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
points: undefined,
|
||||
balance: undefined,
|
||||
type: undefined,
|
||||
description: undefined,
|
||||
orderId: undefined,
|
||||
bizNo: undefined,
|
||||
operatorId: undefined,
|
||||
targetTenantId: undefined,
|
||||
createdAt: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
198
src/views/system/tenantpoints/index.vue
Normal file
198
src/views/system/tenantpoints/index.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="120px">
|
||||
<!-- <el-form-item label="金额" prop="points">
|
||||
<el-input v-model="queryParams.points" placeholder="请输入金额(正加负减)" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动后余额" prop="balance">
|
||||
<el-input v-model="queryParams.balance" placeholder="请输入变动后余额(冗余)" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="queryParams.type" placeholder="请选择类型" clearable class="!w-240px">
|
||||
<el-option v-for="(dict, index) in getIntDictOptions(DICT_TYPE.PAY_TYPE)" :key="index" :label="dict.label"
|
||||
:value="dict.label" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单 Id" prop="orderId">
|
||||
<el-input v-model="queryParams.orderId" placeholder="请输入订单 Id / 业务单号" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务流水号" prop="bizNo">
|
||||
<el-input v-model="queryParams.bizNo" placeholder="请输入业务流水号(转账、订单等唯一标识)" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="操作人 Id" prop="operatorId">
|
||||
<el-input v-model="queryParams.operatorId" placeholder="请输入操作人 Id" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标租户 Id" prop="targetTenantId">
|
||||
<el-input v-model="queryParams.targetTenantId" placeholder="请输入目标租户 Id(转账使用)" clearable
|
||||
@keyup.enter="handleQuery" class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="queryParams.createdAt" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-240px" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="描述/备注" prop="remark">
|
||||
<el-input v-model="queryParams.remark" placeholder="请输入描述或备注" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item> -->
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<!-- <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['system:tenant-points:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button> -->
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['system:tenant-points:export']">
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<!-- 列头按你给的短名称展示,prop 对应后端字段 -->
|
||||
<el-table-column label="变动" align="center" prop="points" />
|
||||
<el-table-column label="变动后余额" align="center" prop="balance" />
|
||||
<el-table-column label="测试号余额" align="center" prop="testAccountNum" />
|
||||
<el-table-column label="类型" align="center" prop="type" />
|
||||
<el-table-column label="描述" align="center" prop="description" />
|
||||
<el-table-column label="订单 Id/业务单号" align="center" prop="orderId" />
|
||||
<el-table-column label="业务流水号" align="center" prop="bizNo" />
|
||||
<el-table-column label="操作人 Id" align="center" prop="operatorId" />
|
||||
<el-table-column label="目标租户Id" align="center" prop="targetTenantId" />
|
||||
<el-table-column label="创建时间" align="center" prop="createdAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<!-- <el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['system:tenant-points:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['system:tenant-points:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<TenantPointsForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { TenantPointsApi, TenantPointsVO } from '@/api/system/tenantpoints'
|
||||
import TenantPointsForm from './TenantPointsForm.vue'
|
||||
|
||||
/** 租户积分记录 列表 */
|
||||
defineOptions({ name: 'TenantPoints' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<TenantPointsVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
points: undefined,
|
||||
balance: undefined,
|
||||
type: undefined,
|
||||
remark: undefined, // 兼容后端的 remark 字段
|
||||
description: undefined, // 如果后端有 description,可用于搜索过滤
|
||||
orderId: undefined,
|
||||
bizNo: undefined,
|
||||
operatorId: undefined,
|
||||
targetTenantId: undefined,
|
||||
createdAt: undefined,
|
||||
createdAtArr: [] // 保留原来的数组字段名兼容用(如果你用范围)
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
|
||||
const data = await TenantPointsApi.getTenantPointsPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await TenantPointsApi.deleteTenantPoints(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await TenantPointsApi.exportTenantPoints(queryParams)
|
||||
download.excel(data, '租户积分记录.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user