增加租户套餐
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
<template>
|
||||
<div class="tenant-package-tab" v-loading="loading">
|
||||
<el-descriptions :column="2" border class="quota-desc">
|
||||
<el-descriptions-item label="当前套餐">
|
||||
<el-select
|
||||
v-model="selectedPackageId"
|
||||
placeholder="请选择套餐"
|
||||
size="small"
|
||||
style="width: 220px"
|
||||
@change="handleSetPackage"
|
||||
>
|
||||
<el-option
|
||||
v-for="p in packageOptions"
|
||||
:key="p.id"
|
||||
:label="p.name"
|
||||
:value="p.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="开通功能">
|
||||
<template v-if="info.module_names && info.module_names.length">
|
||||
<el-tag v-for="(m, i) in info.module_names" :key="i" size="small" class="module-tag">
|
||||
{{ m }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<span v-else class="muted">未配置功能</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="用户数使用">
|
||||
<span class="quota-text">
|
||||
<b :class="{ danger: info.remaining <= 0 }">{{ info.used }}</b> / {{ info.quota }} 人
|
||||
</span>
|
||||
<el-progress
|
||||
:percentage="usedPercent"
|
||||
:status="info.remaining <= 0 ? 'exception' : undefined"
|
||||
:stroke-width="8"
|
||||
class="quota-progress"
|
||||
/>
|
||||
<span class="muted">剩余 {{ info.remaining }} 个名额</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="增购单价">
|
||||
<span>{{ formatMoney(info.extra_user_price) }}/人</span>
|
||||
<el-button type="primary" link class="ml8" @click="openRecharge">增购用户数</el-button>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-alert
|
||||
v-if="info.remaining <= 0"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="quota-alert"
|
||||
title="该租户用户数已达上限,新增账号前请先增购用户数"
|
||||
/>
|
||||
|
||||
<div class="section-title">增购记录</div>
|
||||
<el-table :data="orders" style="width: 100%" size="small" v-loading="ordersLoading">
|
||||
<el-table-column prop="id" label="ID" width="70" align="center" />
|
||||
<el-table-column label="增购方式" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="Number(row.type) === 2 ? 'success' : 'info'" effect="plain">
|
||||
{{ Number(row.type) === 2 ? "套餐增购" : "单个增购" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="package_name" label="规格" min-width="120" align="center" />
|
||||
<el-table-column label="用户数" width="90" align="center">
|
||||
<template #default="{ row }">+{{ row.user_count }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" width="110" align="center">
|
||||
<template #default="{ row }">{{ formatMoney(row.unit_price) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="110" align="center">
|
||||
<template #default="{ row }">{{ formatMoney(row.amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上限变化" width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.quota_before }} → <b>{{ row.quota_after }}</b>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operator" label="操作人" width="120" align="center" />
|
||||
<el-table-column label="时间" min-width="160" align="center">
|
||||
<template #default="{ row }">{{ formatDateTime(row.create_time) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip />
|
||||
</el-table>
|
||||
|
||||
<!-- 增购用户数 -->
|
||||
<el-dialog v-model="rechargeVisible" title="增购用户数" width="520px" destroy-on-close>
|
||||
<el-form :model="rechargeForm" label-width="100px">
|
||||
<el-form-item label="增购方式">
|
||||
<el-radio-group v-model="rechargeForm.type">
|
||||
<el-radio :label="1">单个增购</el-radio>
|
||||
<el-radio :label="2">按套餐增购</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="rechargeForm.type === 1">
|
||||
<el-form-item label="增购数量">
|
||||
<el-input-number
|
||||
v-model="rechargeForm.user_count"
|
||||
:min="1"
|
||||
:max="9999"
|
||||
controls-position="right"
|
||||
/>
|
||||
<span class="field-tip">个用户</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="单价">
|
||||
<el-input-number
|
||||
v-model="rechargeForm.unit_price"
|
||||
:min="0"
|
||||
:max="999999"
|
||||
:precision="2"
|
||||
controls-position="right"
|
||||
/>
|
||||
<span class="field-tip">元/人(默认为套餐设置的单用户增购价)</span>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item label="加购套餐">
|
||||
<el-select v-model="rechargeForm.quota_package_id" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="q in info.quota_packages || []"
|
||||
:key="q.id"
|
||||
:label="`${q.name}(${q.user_count}人 / ${formatMoney(q.price)})`"
|
||||
:value="q.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="应付金额">
|
||||
<span class="amount-text">{{ formatMoney(rechargeAmount) }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="rechargeForm.remark" maxlength="200" placeholder="选填,如:客户已付款" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="rechargeVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="recharging" @click="submitRecharge">确认增购</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { formatDateTime } from "@/utils/datetime";
|
||||
import {
|
||||
getTenantQuotaInfo,
|
||||
getTenantQuotaOrders,
|
||||
getTenantPackageSelectList,
|
||||
setTenantPackage,
|
||||
rechargeTenantQuota,
|
||||
} from "@/api/tenantPackage";
|
||||
|
||||
const props = defineProps<{
|
||||
/** 当前租户 ID,为空时不请求 */
|
||||
tid: number | null;
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const ordersLoading = ref(false);
|
||||
const recharging = ref(false);
|
||||
const info = ref<any>({});
|
||||
const orders = ref<any[]>([]);
|
||||
const packageOptions = ref<any[]>([]);
|
||||
const selectedPackageId = ref<number | undefined>(undefined);
|
||||
|
||||
const rechargeVisible = ref(false);
|
||||
const rechargeForm = ref({
|
||||
type: 1,
|
||||
user_count: 1,
|
||||
unit_price: 0,
|
||||
quota_package_id: undefined as number | undefined,
|
||||
remark: "",
|
||||
});
|
||||
|
||||
const formatMoney = (v: any) => `¥${Number(v || 0).toFixed(2)}`;
|
||||
|
||||
const usedPercent = computed(() => {
|
||||
const quota = Number(info.value.quota || 0);
|
||||
const used = Number(info.value.used || 0);
|
||||
if (quota <= 0) return 0;
|
||||
return Math.min(100, Math.round((used / quota) * 100));
|
||||
});
|
||||
|
||||
const rechargeAmount = computed(() => {
|
||||
if (Number(rechargeForm.value.type) === 2) {
|
||||
const pkg = (info.value.quota_packages || []).find(
|
||||
(q: any) => Number(q.id) === Number(rechargeForm.value.quota_package_id)
|
||||
);
|
||||
return Number(pkg?.price || 0);
|
||||
}
|
||||
return Number(rechargeForm.value.user_count || 0) * Number(rechargeForm.value.unit_price || 0);
|
||||
});
|
||||
|
||||
const loadInfo = async () => {
|
||||
const tid = props.tid;
|
||||
if (tid == null) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getTenantQuotaInfo(tid);
|
||||
if (res.code === 200) {
|
||||
info.value = res.data || {};
|
||||
selectedPackageId.value = Number(info.value.package_id || 0) || undefined;
|
||||
} else {
|
||||
ElMessage.error(res.msg || "获取租户套餐信息失败");
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || "获取租户套餐信息失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadOrders = async () => {
|
||||
const tid = props.tid;
|
||||
if (tid == null) return;
|
||||
ordersLoading.value = true;
|
||||
try {
|
||||
const res = await getTenantQuotaOrders(tid);
|
||||
if (res.code === 200) {
|
||||
orders.value = res.data?.list || [];
|
||||
}
|
||||
} catch {
|
||||
orders.value = [];
|
||||
} finally {
|
||||
ordersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadPackageOptions = async () => {
|
||||
try {
|
||||
const res = await getTenantPackageSelectList();
|
||||
if (res.code === 200) {
|
||||
packageOptions.value = res.data || [];
|
||||
}
|
||||
} catch {
|
||||
packageOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
const refresh = () => {
|
||||
loadInfo();
|
||||
loadOrders();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.tid,
|
||||
(val) => {
|
||||
if (val != null) {
|
||||
refresh();
|
||||
if (packageOptions.value.length === 0) loadPackageOptions();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const handleSetPackage = async (id: number) => {
|
||||
const tid = props.tid;
|
||||
if (tid == null || !id) return;
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
"调整套餐会改变该租户可用的功能模块(用户数上限只增不减),确定继续吗?",
|
||||
"提示",
|
||||
{ type: "warning" }
|
||||
);
|
||||
} catch {
|
||||
// 取消时回滚选择
|
||||
selectedPackageId.value = Number(info.value.package_id || 0) || undefined;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await setTenantPackage({ tid, package_id: id, sync_quota: true });
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("套餐设置成功");
|
||||
refresh();
|
||||
} else {
|
||||
ElMessage.error(res.msg || "设置失败");
|
||||
selectedPackageId.value = Number(info.value.package_id || 0) || undefined;
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || "设置失败");
|
||||
}
|
||||
};
|
||||
|
||||
const openRecharge = () => {
|
||||
rechargeForm.value = {
|
||||
type: 1,
|
||||
user_count: 1,
|
||||
unit_price: Number(info.value.extra_user_price || 0),
|
||||
quota_package_id: (info.value.quota_packages || [])[0]?.id,
|
||||
remark: "",
|
||||
};
|
||||
rechargeVisible.value = true;
|
||||
};
|
||||
|
||||
const submitRecharge = async () => {
|
||||
const tid = props.tid;
|
||||
if (tid == null) return;
|
||||
const payload: any = {
|
||||
tid,
|
||||
type: rechargeForm.value.type,
|
||||
remark: rechargeForm.value.remark,
|
||||
};
|
||||
if (Number(rechargeForm.value.type) === 2) {
|
||||
if (!rechargeForm.value.quota_package_id) {
|
||||
ElMessage.warning("请选择加购套餐");
|
||||
return;
|
||||
}
|
||||
payload.quota_package_id = rechargeForm.value.quota_package_id;
|
||||
} else {
|
||||
if (Number(rechargeForm.value.user_count) <= 0) {
|
||||
ElMessage.warning("请输入增购数量");
|
||||
return;
|
||||
}
|
||||
payload.user_count = rechargeForm.value.user_count;
|
||||
payload.unit_price = rechargeForm.value.unit_price;
|
||||
}
|
||||
|
||||
recharging.value = true;
|
||||
try {
|
||||
const res = await rechargeTenantQuota(payload);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(`增购成功,本次增加 ${res.data?.add || 0} 个用户数`);
|
||||
rechargeVisible.value = false;
|
||||
refresh();
|
||||
} else {
|
||||
ElMessage.error(res.msg || "增购失败");
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || "增购失败");
|
||||
} finally {
|
||||
recharging.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ refresh });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.quota-desc {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.module-tag {
|
||||
margin: 0 6px 6px 0;
|
||||
}
|
||||
.quota-text b {
|
||||
font-size: 16px;
|
||||
}
|
||||
.quota-text b.danger {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
.quota-progress {
|
||||
margin: 6px 0 2px;
|
||||
max-width: 220px;
|
||||
}
|
||||
.muted {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.ml8 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.quota-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.section-title {
|
||||
margin: 16px 0 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
.amount-text {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
.field-tip {
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,14 @@
|
||||
<template>
|
||||
<div class="tenant-users-tab">
|
||||
<div class="section-header">
|
||||
<div class="section-title">用户列表</div>
|
||||
<div class="section-title">
|
||||
用户列表
|
||||
<span v-if="quotaInfo" class="quota-badge">
|
||||
用户数
|
||||
<b :class="{ danger: Number(quotaInfo.remaining) <= 0 }">{{ quotaInfo.used }}</b>
|
||||
/ {{ quotaInfo.quota }}
|
||||
</span>
|
||||
</div>
|
||||
<el-button type="primary" size="small" :disabled="!tid" @click="handleAddUser">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加用户
|
||||
@@ -121,6 +128,7 @@ import { ref, reactive, watch } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Plus } from "@element-plus/icons-vue";
|
||||
import { getTenantUserList, editTenantUser, deleteTenantUser } from "@/api/tenantUser";
|
||||
import { getTenantQuotaInfo } from "@/api/tenantPackage";
|
||||
import { getOrganizationList } from "@/api/erp";
|
||||
import AddUser from "./adduser.vue";
|
||||
|
||||
@@ -134,6 +142,25 @@ const usersLoading = ref(false);
|
||||
const userSearchKeyword = ref("");
|
||||
const addUserRef = ref<{ open: (tenantId: number) => void } | null>(null);
|
||||
|
||||
// 租户用户数配额(已用/上限):用于展示与新增前的拦截
|
||||
const quotaInfo = ref<any>(null);
|
||||
|
||||
const loadQuotaInfo = async () => {
|
||||
const id = props.tid;
|
||||
if (id == null) {
|
||||
quotaInfo.value = null;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await getTenantQuotaInfo(id);
|
||||
if (res.code === 200) {
|
||||
quotaInfo.value = res.data || null;
|
||||
}
|
||||
} catch {
|
||||
quotaInfo.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const orgTree = ref<any[]>([]);
|
||||
const orgNameMap = ref<Record<number, string>>({});
|
||||
|
||||
@@ -234,6 +261,8 @@ const refreshTenantUsers = async () => {
|
||||
} finally {
|
||||
usersLoading.value = false;
|
||||
}
|
||||
// 用户数统计随增删变化,同步刷新配额展示
|
||||
loadQuotaInfo();
|
||||
};
|
||||
|
||||
const loadUsersForTid = async (tid: number) => {
|
||||
@@ -259,9 +288,11 @@ watch(
|
||||
loadOrgs();
|
||||
if (id == null) {
|
||||
tenantUsers.value = [];
|
||||
quotaInfo.value = null;
|
||||
return;
|
||||
}
|
||||
loadUsersForTid(id);
|
||||
loadQuotaInfo();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
@@ -276,9 +307,17 @@ const resetUserSearch = () => {
|
||||
};
|
||||
|
||||
const handleAddUser = () => {
|
||||
if (props.tid != null) {
|
||||
addUserRef.value?.open(props.tid);
|
||||
if (props.tid == null) return;
|
||||
// 用户数上限校验:达到上限时提示到「套餐与用户数」增购
|
||||
if (quotaInfo.value && Number(quotaInfo.value.quota) > 0 && Number(quotaInfo.value.used) >= Number(quotaInfo.value.quota)) {
|
||||
ElMessageBox.alert(
|
||||
`该租户用户数已达上限(${quotaInfo.value.used}/${quotaInfo.value.quota}),请先在「套餐与用户数」中增购用户数。`,
|
||||
"用户数已达上限",
|
||||
{ type: "warning", confirmButtonText: "我知道了" }
|
||||
).catch(() => {});
|
||||
return;
|
||||
}
|
||||
addUserRef.value?.open(props.tid);
|
||||
};
|
||||
|
||||
const openPasswordDialog = (row: any) => {
|
||||
@@ -419,6 +458,22 @@ defineExpose({
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.quota-badge {
|
||||
margin-left: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
b {
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
b.danger {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
.user-search-form {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 12px;
|
||||
|
||||
@@ -38,6 +38,16 @@
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ detailData.create_time ? formatDateTime(detailData.create_time) : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="租户套餐">
|
||||
<el-tag size="small" type="warning" effect="plain">
|
||||
{{ detailData.package_name || '未绑定' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="用户数">
|
||||
<span>
|
||||
{{ detailData.user_used ?? 0 }} / {{ detailData.user_quota ?? 0 }} 人
|
||||
</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-divider />
|
||||
@@ -53,6 +63,11 @@
|
||||
<TenantUsersTab :tid="detailTenantId" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="套餐与用户数" name="package">
|
||||
<div class="tab-pane-inner">
|
||||
<TenantPackageTab :tid="detailTenantId" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- 后续功能:在此继续增加 <el-tab-pane label="..." name="...">...</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -68,6 +83,7 @@
|
||||
import { ref } from "vue";
|
||||
import { getTenantDetail } from "@/api/tenant";
|
||||
import TenantUsersTab from "./TenantUsersTab.vue";
|
||||
import TenantPackageTab from "./TenantPackageTab.vue";
|
||||
import { formatDateTime } from "@/utils/datetime";
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
@@ -24,6 +24,21 @@
|
||||
<el-form-item label="租户地址" prop="address">
|
||||
<el-input v-model="formData.address" type="textarea" placeholder="请输入地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户套餐" prop="package_id">
|
||||
<el-select v-model="formData.package_id" placeholder="请选择套餐" clearable
|
||||
style="width: 100%" @change="handlePackageChange">
|
||||
<el-option v-for="p in packageOptions" :key="p.id" :label="p.name" :value="p.id">
|
||||
<span>{{ p.name }}</span>
|
||||
<span class="option-tip">{{ packageSummary(p) }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<div class="form-tip">决定租户端可用的功能模块(套餐在「基础设置 → 租户套餐」维护)</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户数上限" prop="user_quota">
|
||||
<el-input-number v-model="formData.user_quota" :min="1" :max="99999"
|
||||
controls-position="right" />
|
||||
<div class="form-tip">默认 20 人;达到上限后需增购用户数才能继续开账号</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">启用</el-radio>
|
||||
@@ -40,9 +55,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { createTenant, editTenant, getTenantDetail, checkTenantCode } from '@/api/tenant';
|
||||
import { getTenantPackageSelectList } from '@/api/tenantPackage';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const visible = ref(false);
|
||||
@@ -50,6 +66,39 @@ const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const formRef = ref();
|
||||
|
||||
// 套餐下拉:套餐决定租户端开通的功能模块与初始用户数
|
||||
const packageOptions = ref<any[]>([]);
|
||||
|
||||
const loadPackages = async () => {
|
||||
try {
|
||||
const res = await getTenantPackageSelectList();
|
||||
if (res.code === 200) {
|
||||
packageOptions.value = res.data || [];
|
||||
}
|
||||
} catch {
|
||||
packageOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(loadPackages);
|
||||
|
||||
const packageSummary = (p: any) => {
|
||||
const names = (p.modules || []).map((m: any) => m.module_name || m.module_code).join('、');
|
||||
return `${names || '未配置功能'} · ${p.user_quota || 0}人`;
|
||||
};
|
||||
|
||||
// 选择套餐时联动用户数上限(取套餐包含人数与当前值的较大者,避免回收已购用户数)
|
||||
const handlePackageChange = (id: number) => {
|
||||
const pkg = packageOptions.value.find((p) => Number(p.id) === Number(id));
|
||||
if (!pkg) return;
|
||||
const quota = Number(pkg.user_quota || 20);
|
||||
if (!formData.id) {
|
||||
formData.user_quota = quota;
|
||||
} else if (quota > Number(formData.user_quota || 0)) {
|
||||
formData.user_quota = quota;
|
||||
}
|
||||
};
|
||||
|
||||
const initialData = {
|
||||
id: null,
|
||||
tenant_name: '',
|
||||
@@ -58,7 +107,9 @@ const initialData = {
|
||||
contact_phone: '',
|
||||
contact_email: '',
|
||||
address: '',
|
||||
status: 1
|
||||
status: 1,
|
||||
package_id: undefined as number | undefined,
|
||||
user_quota: 20
|
||||
};
|
||||
|
||||
const formData = reactive({ ...initialData });
|
||||
@@ -191,4 +242,20 @@ const handleClosed = () => {
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.option-tip {
|
||||
float: right;
|
||||
margin-left: 16px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 1.5;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user