批量优化

This commit is contained in:
hero920103 committed 2026-10-08 15:30:37 +08:00
1 parent 438145cae7
commit c925b198d1
50 files changed
+5716 -291

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
import request from "@/utils/request";
/**
* 小程序多应用管理
*/
/** 获取小程序应用列表(AppSecret 不回显,返回 configured 标记) */
export function listMiniApps() {
return request({
url: "/platform/wechatMini/apps",
method: "get",
});
}
/**
* 新增/更新小程序应用
* @param {Object} data
* @param {number} [data.id] 更新时携带;新增不传
* @param {string} data.name 应用名称
* @param {string} data.app_id 小程序 AppID
* @param {string} [data.app_secret] 掩码/空串 = 不修改原值
* @param {number} data.enabled 1=启用 0=停用
* @param {number} data.auto_bind 1=手机号命中身份后自动绑定
* @param {string} [data.remark] 备注
*/
export function saveMiniApp(data) {
return request({
url: "/platform/wechatMini/app/save",
method: "post",
data,
});
}
/** 删除应用(软删并停用) */
export function deleteMiniApp(id) {
return request({
url: `/platform/wechatMini/app/delete/${id}`,
method: "post",
});
}
+52
View File
@@ -0,0 +1,52 @@
import request from "@/utils/request";
/**
* 获取企业申请列表(platform 审核用)
* @param {Object} params
* @param {number} params.page 页码
* @param {number} params.pageSize 每页数量
* @param {number} params.type 类型筛选:1=加入企业 2=创建企业
* @param {number} params.status 状态筛选:0=待审核 1=已通过 2=已驳回
*/
export function getTenantApplies(params) {
return request({
url: "/platform/tenantApply/list",
method: "get",
params,
});
}
/**
* 获取申请详情
* @param {number|string} id 申请ID
*/
export function getTenantApplyDetail(id) {
return request({
url: `/platform/tenantApply/detail/${id}`,
method: "get",
});
}
/**
* 通过创建企业申请:自动创建租户并把申请人绑定为企业管理员
* @param {number|string} id 申请ID
*/
export function approveTenantApply(id) {
return request({
url: `/platform/tenantApply/approve/${id}`,
method: "post",
});
}
/**
* 驳回申请
* @param {number|string} id 申请ID
* @param {{reason: string}} data 驳回原因
*/
export function rejectTenantApply(id, data) {
return request({
url: `/platform/tenantApply/reject/${id}`,
method: "post",
data,
});
}
@@ -39,6 +39,10 @@
controls-position="right" />
<div class="form-tip">默认 20 人;达到上限后需增购用户数才能继续开账号</div>
</el-form-item>
<el-form-item v-if="!formData.id" label="指定管理员" prop="admin_identifier">
<el-input v-model="formData.admin_identifier" placeholder="选填:已有用户的账号或手机号" clearable />
<div class="form-tip">填已有用户,创建后自动绑定为该租户的管理员(不新建账号、不修改其原密码);留空则租户暂无管理员</div>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">启用</el-radio>
@@ -109,7 +113,8 @@ const initialData = {
address: '',
status: 1,
package_id: undefined as number | undefined,
user_quota: 20
user_quota: 20,
admin_identifier: '' as string
};
const formData = reactive({ ...initialData });
@@ -184,12 +189,30 @@ const submitForm = async () => {
}
}
// 3. 发起真正的保存请求
const saveApi = formData.id ? editTenant(formData.id, formData) : createTenant(formData);
// 3. 发起真正的保存请求(创建时把「指定管理员」拆成后端归并键字段)
let payload: any = { ...formData };
if (!formData.id) {
const identifier = String(formData.admin_identifier || '').trim();
delete payload.admin_identifier;
if (identifier) {
if (/^1[3-9]\d{9}$/.test(identifier)) {
payload.admin_phone = identifier;
} else {
payload.admin_account = identifier;
}
}
}
const saveApi = formData.id ? editTenant(formData.id, formData) : createTenant(payload);
const saveRes = await saveApi;
if (saveRes.code === 200) {
ElMessage.success('保存成功');
if (saveRes.data && saveRes.data.admin_bound) {
ElMessage.success('保存成功,已绑定指定用户为租户管理员');
} else if (saveRes.msg && saveRes.msg !== 'success') {
ElMessage.warning(saveRes.msg);
} else {
ElMessage.success('保存成功');
}
visible.value = false;
emit('success');
}
@@ -0,0 +1,332 @@
<template>
<div class="miniapp-settings">
<el-card shadow="never" class="settings-card">
<template #header>
<div class="card-header">
<span>小程序应用</span>
<el-button type="primary" size="small" @click="openAdd">
<el-icon><Plus /></el-icon>
新增小程序
</el-button>
</div>
</template>
<el-alert
type="info"
:closable="false"
show-icon
class="tip-alert"
title="支持配置多个小程序。小程序端登录时按其 AppID 匹配对应配置;未传 AppID 时使用列表中第一条启用记录。"
/>
<el-table :data="apps" style="width: 100%" v-loading="loading">
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column
prop="name"
label="应用名称"
min-width="140"
align="center"
>
<template #default="scope">
{{ scope.row.name || "-" }}
</template>
</el-table-column>
<el-table-column
prop="app_id"
label="AppID"
min-width="180"
align="center"
show-overflow-tooltip
/>
<el-table-column label="AppSecret" width="110" align="center">
<template #default="scope">
<el-tag
:type="scope.row.configured ? 'success' : 'info'"
size="small"
>
{{ scope.row.configured ? "已配置" : "未配置" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="启用" width="80" align="center">
<template #default="scope">
<el-switch
:model-value="scope.row.enabled === 1"
@change="(val) => toggleEnabled(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="手机号自动绑定" width="130" align="center">
<template #default="scope">
<el-switch
:model-value="scope.row.auto_bind === 1"
@change="(val) => toggleAutoBind(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
min-width="140"
align="center"
show-overflow-tooltip
>
<template #default="scope">
{{ scope.row.remark || "-" }}
</template>
</el-table-column>
<el-table-column
prop="create_time"
label="创建时间"
width="170"
align="center"
>
<template #default="{ row }">
{{ formatDateTime(row.create_time) }}
</template>
</el-table-column>
<el-table-column label="操作" width="130" align="center" fixed="right">
<template #default="scope">
<el-button size="small" type="primary" link @click="openEdit(scope.row)">
编辑
</el-button>
<el-button size="small" type="danger" link @click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 新增/编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="form.id ? '编辑小程序' : '新增小程序'"
width="520px"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<el-form-item label="应用名称" prop="name">
<el-input
v-model="form.name"
placeholder="如:商城小程序"
maxlength="64"
/>
</el-form-item>
<el-form-item label="AppID" prop="app_id">
<el-input v-model="form.app_id" placeholder="wx 开头的小程序 AppID" />
</el-form-item>
<el-form-item label="AppSecret" prop="app_secret">
<el-input
v-model="form.app_secret"
type="password"
show-password
:placeholder="
form.id && secretConfigured
? '已配置,留空 / 掩码表示不修改'
: '小程序 AppSecret'
"
/>
</el-form-item>
<el-form-item label="启用">
<el-switch v-model="form.enabled" :active-value="1" :inactive-value="0" />
<span class="form-tip">启用后小程序端才可使用微信一键登录</span>
</el-form-item>
<el-form-item label="手机号自动绑定">
<el-switch v-model="form.auto_bind" :active-value="1" :inactive-value="0" />
<span class="form-tip">手机号命中已有身份后自动完成绑定</span>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="form.remark"
type="textarea"
:rows="2"
maxlength="200"
placeholder="选填"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="handleSave">
保存
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import { listMiniApps, saveMiniApp, deleteMiniApp } from "@/api/miniApp";
import { formatDateTime } from "@/utils/datetime";
const loading = ref(false);
const saving = ref(false);
const apps = ref([]);
const dialogVisible = ref(false);
const formRef = ref(null);
const secretConfigured = ref(false);
const emptyForm = () => ({
id: 0,
name: "",
app_id: "",
app_secret: "",
enabled: 0,
auto_bind: 1,
remark: "",
});
const form = ref(emptyForm());
const rules = {
name: [{ required: true, message: "请输入应用名称", trigger: "blur" }],
app_id: [
{ required: true, message: "请输入小程序 AppID", trigger: "blur" },
{ pattern: /^wx[0-9a-zA-Z]{10,}$/, message: "AppID 一般以 wx 开头", trigger: "blur" },
],
};
const fetchApps = async () => {
loading.value = true;
try {
const res = await listMiniApps();
if (res.code === 200) {
apps.value = res.data.list || [];
} else {
ElMessage.error(res.msg || "获取应用列表失败");
}
} catch (error) {
ElMessage.error(error?.message || "获取应用列表失败");
} finally {
loading.value = false;
}
};
const openAdd = () => {
form.value = emptyForm();
secretConfigured.value = false;
dialogVisible.value = true;
};
const openEdit = (row) => {
form.value = {
id: row.id,
name: row.name || "",
app_id: row.app_id || "",
app_secret: "",
enabled: row.enabled,
auto_bind: row.auto_bind,
remark: row.remark || "",
};
secretConfigured.value = !!row.configured;
dialogVisible.value = true;
};
const handleSave = () => {
formRef.value?.validate(async (valid) => {
if (!valid) return;
saving.value = true;
try {
const res = await saveMiniApp({ ...form.value });
if (res.code === 200) {
ElMessage.success(res.msg || "保存成功");
dialogVisible.value = false;
fetchApps();
} else {
ElMessage.error(res.msg || "保存失败");
}
} catch (error) {
ElMessage.error(error?.message || "保存失败");
} finally {
saving.value = false;
}
});
};
// 快捷开关:直接以当前值保存
const toggleEnabled = async (row, val) => {
await quickToggle(row, "enabled", val);
};
const toggleAutoBind = async (row, val) => {
await quickToggle(row, "auto_bind", val);
};
const quickToggle = async (row, field, val) => {
try {
const res = await saveMiniApp({
id: row.id,
name: row.name,
app_id: row.app_id,
app_secret: "",
enabled: field === "enabled" ? (val ? 1 : 0) : row.enabled,
auto_bind: field === "auto_bind" ? (val ? 1 : 0) : row.auto_bind,
remark: row.remark || "",
});
if (res.code === 200) {
ElMessage.success("已更新");
fetchApps();
} else {
ElMessage.error(res.msg || "更新失败");
}
} catch (error) {
ElMessage.error(error?.message || "更新失败");
}
};
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm(
`确认删除小程序「${row.name || row.app_id}」?删除后该小程序端将无法使用微信一键登录。`,
"删除应用",
{ confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" },
);
} catch {
return;
}
try {
const res = await deleteMiniApp(row.id);
if (res.code === 200) {
ElMessage.success(res.msg || "已删除");
fetchApps();
} else {
ElMessage.error(res.msg || "删除失败");
}
} catch (error) {
ElMessage.error(error?.message || "删除失败");
}
};
onMounted(() => {
fetchApps();
});
</script>
<style lang="less" scoped>
.miniapp-settings {
.settings-card {
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
span {
font-weight: 600;
}
}
}
.tip-alert {
margin-bottom: 16px;
}
.form-tip {
margin-left: 10px;
color: #8a94ad;
font-size: 12px;
}
}
</style>
@@ -410,44 +410,6 @@
</el-form>
</el-card>
<!-- 小程序一键登录配置 -->
<el-card shadow="never" class="settings-card">
<template #header>
<div class="card-header">
<span>微信小程序(一键登录)配置</span>
<el-tag v-if="Number(miniCfg.enabled) === 1 && miniCfg.configured" type="success" size="small">已启用</el-tag>
<el-tag v-else type="info" size="small" class="ml8">未启用:小程序端仅账号密码登录</el-tag>
</div>
</template>
<el-form label-width="140px" label-position="right">
<el-form-item label="启用状态">
<el-switch v-model="miniCfg.enabled" :active-value="1" :inactive-value="0" />
<span class="form-tip-inline">开启后 uniapp 小程序端登录页展示「微信手机号一键登录」主入口</span>
</el-form-item>
<el-form-item label="AppID" required>
<el-input v-model="miniCfg.app_id" placeholder="小程序后台「开发管理 → 开发设置」中的 AppID" clearable />
</el-form-item>
<el-form-item label="AppSecret">
<el-input v-model="miniCfg.app_secret" type="password" show-password placeholder="已保存时显示掩码,保持不变则无需修改" clearable />
</el-form-item>
<el-form-item label="自动绑定">
<el-switch v-model="miniCfg.auto_bind" :active-value="1" :inactive-value="0" />
<span class="form-tip-inline">开启后未通过手机号验证的 openid 也会自动绑定账号;关闭时仅在手机号验证通过后建立绑定</span>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="miniCfg.remark" placeholder="仅后台可见" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="miniSaving" @click="saveMiniConfig">保存配置</el-button>
<div class="form-tip">
① 此处 AppID 必须与 uniapp manifest.json「mp-weixin.appid」一致,否则 code 换 openid 会报 40029;<br />
② 「手机号一键登录」需小程序为已认证企业主体,并在小程序后台开通「手机号快速验证」能力;<br />
③ 保存并启用后,小程序端登录页自动切换为微信认证登录优先、账号密码其次。
</div>
</el-form-item>
</el-form>
</el-card>
<!-- 绑定我的微信 -->
<!-- <el-card shadow="never" class="settings-card bind-card">
<template #header>
@@ -626,6 +588,19 @@
</div>
</el-tab-pane>
<!-- 小程序配置(支持多个小程序应用) -->
<el-tab-pane name="miniapp">
<template #label>
<span class="tab-label-item">
<el-icon><Cellphone /></el-icon>
<span>小程序配置</span>
</span>
</template>
<div class="tab-pane-content">
<miniAppSettings />
</div>
</el-tab-pane>
</el-tabs>
</div>
@@ -661,8 +636,6 @@ import { formatTime } from "@/utils/datetime";
import {
getWechatMpConfig,
saveWechatMpConfig,
getWechatMiniConfig,
saveWechatMiniConfig,
testWechatMpConnection,
publishWechatMpMenu,
uploadWechatFollowQrcode,
@@ -678,6 +651,7 @@ import {
pushWechatMpMessage,
pushWechatMpTest
} from "@/api/wechatMp";
import miniAppSettings from "./miniAppSettings.vue";
const STORAGE_KEY = "notification_settings_draft";
const activeSubTab = ref("email");
@@ -1089,56 +1063,6 @@ const saveWechatConfig = async () => {
}
};
/* ==================== 微信小程序(一键登录) ==================== */
const miniCfg = reactive({
app_id: "",
app_secret: "",
enabled: 0,
auto_bind: 1,
remark: "",
configured: false,
});
const miniSaving = ref(false);
const loadMiniConfig = async () => {
try {
const res = await getWechatMiniConfig();
if (res.code === 200 && res.data) {
Object.assign(miniCfg, res.data);
}
} catch (e) {
console.error("加载小程序配置失败:", e);
}
};
const saveMiniConfig = async () => {
if (!miniCfg.app_id) {
ElMessage.warning("请先填写 AppID");
return;
}
miniSaving.value = true;
try {
const res = await saveWechatMiniConfig({
app_id: miniCfg.app_id,
app_secret: miniCfg.app_secret,
enabled: Number(miniCfg.enabled) === 1 ? 1 : 0,
auto_bind: Number(miniCfg.auto_bind) === 1 ? 1 : 0,
remark: miniCfg.remark,
});
if (res.code === 200) {
ElMessage.success("小程序配置保存成功");
await loadMiniConfig();
} else {
ElMessage.error(res.msg || "保存失败");
}
} catch (e) {
ElMessage.error(e?.message || "保存失败");
} finally {
miniSaving.value = false;
}
};
// 关注二维码上传/删除(存 data URL,供租户端「绑定微信」页展示扫码)
const followQrcodeUploading = ref(false);
const handleFollowQrcodeUpload = async ({ file }) => {
@@ -1434,7 +1358,6 @@ onMounted(() => {
loadSiteReminderConfig();
loadBarkConfig();
loadWechatConfig();
loadMiniConfig();
loadWechatBinding();
});
@@ -1442,7 +1365,6 @@ onMounted(() => {
watch(activeSubTab, (tab) => {
if (tab === "wechat") {
loadWechatConfig();
loadMiniConfig();
loadWechatBinding();
loadFollowers();
}
+424
View File
@@ -0,0 +1,424 @@
<template>
<div class="tenant-apply">
<div class="header-bar">
<h2>企业创建审核</h2>
<div>
<el-button @click="refresh">
<el-icon><Refresh /></el-icon>
刷新
</el-button>
</div>
</div>
<el-divider />
<!-- 搜索筛选 -->
<div class="filter-bar">
<el-form :inline="true" :model="filterForm" class="filter-form">
<el-form-item label="申请类型">
<el-select
v-model="filterForm.type"
placeholder="全部"
clearable
style="width: 140px"
@change="handleSearch"
>
<el-option label="创建企业" :value="2" />
<el-option label="加入企业" :value="1" />
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select
v-model="filterForm.status"
placeholder="请选择状态"
clearable
style="width: 140px"
@change="handleSearch"
>
<el-option label="待审核" :value="0" />
<el-option label="已通过" :value="1" />
<el-option label="已驳回" :value="2" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">
<el-icon><Search /></el-icon>
搜索
</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 申请列表 -->
<el-table :data="applies" style="width: 100%" v-loading="loading">
<el-table-column prop="id" label="ID" width="70" align="center" />
<el-table-column label="申请类型" width="110" align="center">
<template #default="scope">
<el-tag
:type="scope.row.type === 2 ? 'primary' : 'info'"
size="small"
>
{{ scope.row.type === 2 ? "创建企业" : "加入企业" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="申请人" min-width="140" align="center">
<template #default="scope">
<div>{{ scope.row.identity_nickname || "-" }}</div>
<div class="sub-text">{{ scope.row.identity_mobile || "" }}</div>
</template>
</el-table-column>
<el-table-column
prop="tenant_name"
label="企业名称"
min-width="170"
align="center"
show-overflow-tooltip
/>
<el-table-column
prop="credit_code"
label="统一社会信用代码"
width="190"
align="center"
show-overflow-tooltip
/>
<el-table-column
prop="legal_person"
label="法定代表人"
width="110"
align="center"
/>
<el-table-column label="联系人" min-width="140" align="center">
<template #default="scope">
<div>{{ scope.row.contact_person || "-" }}</div>
<div class="sub-text">{{ scope.row.contact_phone || "" }}</div>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="statusTagType(scope.row.status)" size="small">
{{ statusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="申请时间" width="170" align="center">
<template #default="{ row }">
{{ formatDateTime(row.create_time) }}
</template>
</el-table-column>
<el-table-column label="操作" width="190" align="center" fixed="right">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleDetail(scope.row)">
详情
</el-button>
<template v-if="scope.row.type === 2 && scope.row.status === 0">
<el-button size="small" type="success" link @click="handleApprove(scope.row)">
通过
</el-button>
<el-button size="small" type="danger" link @click="handleReject(scope.row)">
驳回
</el-button>
</template>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-bar">
<el-pagination
v-model:current-page="page"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div>
<!-- 详情对话框 -->
<el-dialog v-model="detailVisible" title="申请详情" width="640px">
<el-descriptions v-if="current" :column="2" border>
<el-descriptions-item label="申请类型" :span="2">
{{ current.type === 2 ? "创建企业" : "加入企业" }}
</el-descriptions-item>
<el-descriptions-item label="申请人">
{{ current.identity_nickname || "-" }}
</el-descriptions-item>
<el-descriptions-item label="申请人手机号">
{{ current.identity_mobile || "-" }}
</el-descriptions-item>
<el-descriptions-item label="企业名称" :span="2">
{{ current.tenant_name || "-" }}
</el-descriptions-item>
<template v-if="current.type === 2">
<el-descriptions-item label="统一社会信用代码">
{{ current.credit_code || "-" }}
</el-descriptions-item>
<el-descriptions-item label="法定代表人">
{{ current.legal_person || "-" }}
</el-descriptions-item>
<el-descriptions-item label="营业执照" :span="2">
<template v-if="current.license_file">
<el-image
v-if="isImage(current.license_file)"
:src="current.license_file"
:preview-src-list="[current.license_file]"
fit="cover"
style="width: 120px; height: 90px; border-radius: 4px"
/>
<el-link
v-else
type="primary"
:href="current.license_file"
target="_blank"
>
查看(PDF)
</el-link>
</template>
<span v-else>-</span>
</el-descriptions-item>
</template>
<el-descriptions-item label="联系人">
{{ current.contact_person || "-" }}
</el-descriptions-item>
<el-descriptions-item label="联系电话">
{{ current.contact_phone || "-" }}
</el-descriptions-item>
<el-descriptions-item label="电子邮箱">
{{ current.contact_email || "-" }}
</el-descriptions-item>
<el-descriptions-item label="公司地址">
{{ current.address || "-" }}
</el-descriptions-item>
<el-descriptions-item label="状态">
{{ statusText(current.status) }}
</el-descriptions-item>
<el-descriptions-item label="申请时间">
{{ formatDateTime(current.create_time) }}
</el-descriptions-item>
<el-descriptions-item label="审核人">
{{ current.reviewer_name || "-" }}
</el-descriptions-item>
<el-descriptions-item label="审核时间">
{{ current.review_time ? formatDateTime(current.review_time) : "-" }}
</el-descriptions-item>
<el-descriptions-item
v-if="current.status === 2"
label="驳回原因"
:span="2"
>
{{ current.reject_reason || "-" }}
</el-descriptions-item>
</el-descriptions>
<template #footer>
<el-button @click="detailVisible = false">关闭</el-button>
<template v-if="current && current.type === 2 && current.status === 0">
<el-button type="success" @click="handleApprove(current); detailVisible = false">
通过
</el-button>
<el-button type="danger" @click="handleReject(current); detailVisible = false">
驳回
</el-button>
</template>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { Refresh, Search } from "@element-plus/icons-vue";
import {
getTenantApplies,
getTenantApplyDetail,
approveTenantApply,
rejectTenantApply,
} from "@/api/tenantApply";
import { formatDateTime } from "@/utils/datetime";
const page = ref(1);
const pageSize = ref(20);
const total = ref(0);
const loading = ref(false);
const applies = ref([]);
const detailVisible = ref(false);
const current = ref(null);
const filterForm = ref({
type: "",
status: "",
});
const statusText = (status) => {
const map = { 0: "待审核", 1: "已通过", 2: "已驳回" };
return map[status] || "未知";
};
const statusTagType = (status) => {
const map = { 0: "warning", 1: "success", 2: "danger" };
return map[status] || "info";
};
const isImage = (url) => /\.(png|jpe?g|gif|webp|bmp)(\?.*)?$/i.test(url || "");
// 获取申请列表
const fetchApplies = async () => {
loading.value = true;
try {
const params = {
page: page.value,
pageSize: pageSize.value,
};
if (filterForm.value.type !== "") params.type = filterForm.value.type;
if (filterForm.value.status !== "") params.status = filterForm.value.status;
const res = await getTenantApplies(params);
if (res.code === 200) {
applies.value = res.data.list || [];
total.value = res.data.total || 0;
} else {
ElMessage.error(res.msg || "获取申请列表失败");
}
} catch (error) {
ElMessage.error(error?.message || "获取申请列表失败");
} finally {
loading.value = false;
}
};
const refresh = () => {
fetchApplies();
};
const handleSearch = () => {
page.value = 1;
fetchApplies();
};
const handleReset = () => {
filterForm.value = { type: "", status: "" };
page.value = 1;
fetchApplies();
};
const handleSizeChange = () => {
page.value = 1;
fetchApplies();
};
const handlePageChange = () => {
fetchApplies();
};
// 查看详情
const handleDetail = async (row) => {
current.value = { ...row };
detailVisible.value = true;
try {
const res = await getTenantApplyDetail(row.id);
if (res.code === 200 && res.data) {
current.value = { ...current.value, ...res.data };
}
} catch (error) {
console.error("获取申请详情失败", error);
}
};
// 通过申请:创建租户并绑定申请人为企业管理员
const handleApprove = async (row) => {
try {
await ElMessageBox.confirm(
`确认通过「${row.tenant_name}」的创建企业申请?通过后将自动创建租户,申请人(${row.identity_nickname || row.identity_mobile})会成为该企业的管理员账号,不可更改。`,
"通过申请",
{ confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" },
);
} catch {
return;
}
try {
const res = await approveTenantApply(row.id);
if (res.code === 200) {
ElMessage.success(res.msg || "已通过");
fetchApplies();
} else {
ElMessage.error(res.msg || "操作失败");
}
} catch (error) {
ElMessage.error(error?.message || "操作失败");
}
};
// 驳回申请(需填写原因)
const handleReject = async (row) => {
let reason = "";
try {
const { value } = await ElMessageBox.prompt("请输入驳回原因", `驳回「${row.tenant_name}」的申请`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
inputPlaceholder: "驳回原因(可选)",
inputValidator: (v) => {
if (v && v.length > 200) return "原因不能超过200字";
return true;
},
});
reason = value || "";
} catch {
return;
}
try {
const res = await rejectTenantApply(row.id, { reason });
if (res.code === 200) {
ElMessage.success(res.msg || "已驳回");
fetchApplies();
} else {
ElMessage.error(res.msg || "操作失败");
}
} catch (error) {
ElMessage.error(error?.message || "操作失败");
}
};
onMounted(() => {
fetchApplies();
});
</script>
<style lang="less" scoped>
.tenant-apply {
padding: 20px;
}
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
h2 {
margin: 0;
font-size: 20px;
font-weight: 600;
}
}
.filter-bar {
margin-bottom: 20px;
.filter-form {
margin: 0;
}
}
.pagination-bar {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.sub-text {
color: #8a94ad;
font-size: 12px;
line-height: 1.4;
}
</style>