Files

1069 lines
27 KiB
Vue

<template>
<div class="app-container">
<el-form
v-show="showSearch"
ref="queryRef"
:inline="true"
:model="queryParams"
label-width="68px"
>
<el-form-item label="公司名称" prop="companyName">
<el-input
v-model="queryParams.companyName"
clearable
placeholder="请输入公司名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="联系人" prop="contactName">
<el-input
v-model="queryParams.contactName"
clearable
placeholder="请输入联系人名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button icon="Search" type="primary" @click="handleQuery"
>搜索
</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
v-hasPermi="['tenant:add']"
icon="Plus"
plain
type="primary"
@click="handleAdd"
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['tenant:edit']"
:disabled="single"
icon="Edit"
plain
type="success"
@click="handleUpdate"
>修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['tenant:remove']"
:disabled="multiple"
icon="Delete"
plain
type="danger"
@click="handleDelete"
>删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['tenant:export']"
icon="Download"
plain
type="warning"
@click="handleExport"
>导出
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['tenant:remove']"
icon="Refresh"
plain
type="danger"
@click="handleRefreshCache"
>刷新缓存
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
:disabled="single || isInit(ids[0])"
icon="Refresh"
plain
type="primary"
@click="handleInitialize"
>初始化
</el-button>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="tenantList"
@selection-change="handleSelectionChange"
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="公司名称" prop="companyName" />
<el-table-column align="center" label="联系人名称" prop="contactName" />
<el-table-column align="center" label="联系人电话" prop="contactPhone" />
<el-table-column
align="center"
label="过期时间"
prop="expireTime"
width="180"
>
<template #default="scope">
<span>{{ parseTime(scope.row.expireTime, "{y}-{m}-{d}") }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="用户数量" prop="accountCount" />
<el-table-column align="center" label="状态">
<template #default="{ row }">
<el-switch
active-value="1"
inactive-value="0"
:model-value="row.status"
:loading="isTenantStatusSwitching"
@change="handleChangeTenantStatus($event, row)"
/>
</template>
</el-table-column>
<el-table-column align="center" label="初始化状态">
<template #default="{ row }">
<dict-tag :options="init_status_dict" :value="row.initialized" />
</template>
</el-table-column>
<el-table-column align="center" label="模式">
<template #default="{ row }">
<dict-tag :options="tenant_mode_dict" :value="row.mode" />
</template>
</el-table-column>
<el-table-column
align="center"
class-name="small-padding fixed-width"
label="操作"
width="290"
>
<template #default="{ row }">
<el-tooltip
:disabled="row.initialized !== '1'"
class="box-item"
content="初始化之后不可再更改"
effect="light"
placement="top"
>
<el-button
:disabled="row.initialized === '1'"
icon="Setting"
link
type="primary"
@click="handleSetting(row)"
>模式
</el-button>
</el-tooltip>
<el-button
v-if="row.mode === '3'"
icon="plus"
link
type="primary"
@click="handleOpenDataSource(row.tenantId)"
>数据源
</el-button>
<el-button
v-hasPermi="['tenant:edit']"
icon="Edit"
link
type="primary"
@click="handleUpdate(row)"
>修改
</el-button>
<el-button
v-hasPermi="['tenant:remove']"
icon="Delete"
link
type="primary"
@click="handleDelete(row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNum"
:total="total"
@pagination="getList"
/>
<!-- 添加或修改租户对话框 -->
<el-dialog
v-model="open"
:title="title"
append-to-body
draggable
width="500px"
>
<el-form ref="tenantRef" :model="form" :rules="rules" label-width="130px">
<el-form-item label="公司名称" prop="companyName">
<el-input v-model="form.companyName" placeholder="请输入公司名称" />
</el-form-item>
<el-form-item label="联系人名称" prop="contactName">
<el-input v-model="form.contactName" placeholder="请输入联系人名称" />
</el-form-item>
<el-form-item label="联系人电话" prop="contactPhone">
<el-input
v-model="form.contactPhone"
placeholder="请输入联系人电话"
/>
</el-form-item>
<el-form-item label="公司地址" prop="address">
<el-input v-model="form.address" placeholder="请输入公司地址" />
</el-form-item>
<el-form-item label="公司简介" prop="profile">
<el-input
v-model="form.profile"
placeholder="请输入内容"
type="textarea"
/>
</el-form-item>
<el-form-item label="统一社会信用代码" prop="licenseNumber">
<el-input
v-model="form.licenseNumber"
placeholder="请输入统一社会信用代码"
/>
</el-form-item>
<el-form-item label="过期时间" prop="expireTime">
<el-date-picker
v-model="form.expireTime"
clearable
placeholder="请选择过期时间"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
>
</el-date-picker>
</el-form-item>
<el-form-item label="用户数量" prop="accountCount">
<el-input v-model="form.accountCount" placeholder="请输入用户数量" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="form.remark"
placeholder="请输入内容"
type="textarea"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 修改租户模式 -->
<el-dialog
v-model="showTenantSetting"
append-to-body
draggable
title="模式设置"
width="500px"
>
<el-form
ref="tenantSettingRef"
:model="tenantSettingForm"
:rules="tenantSettingRules"
label-width="0"
>
<el-form-item class="mode-setting-radio">
<el-radio-group v-model="tenantSettingForm.mode">
<el-radio
v-for="option in tenant_mode_dict"
:key="option.value"
:label="option.value"
>{{ option.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitTenantSetting"
>
</el-button>
<el-button @click="cancelSettingTenant"> </el-button>
</div>
</template>
</el-dialog>
<!--查看数据源列表-->
<el-dialog
v-model="showDatasourceDialog"
append-to-body
draggable
title="数据源设置"
width="800px"
>
<!--添加或修改数据源-->
<el-form
v-if="isEditDatasource"
ref="datasourceFormRef"
:model="dataSourceForm"
:rules="dataSourceRules"
label-position="left"
label-width="100px"
>
<el-form-item label="数据库类型" prop="type">
<el-select v-model="dataSourceForm.type">
<el-option
v-for="option in database_type_dict"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库IP" prop="ip">
<el-input v-model="dataSourceForm.ip" />
</el-form-item>
<el-form-item label="数据库端口" prop="port">
<el-input-number
v-model="dataSourceForm.port"
:max="65535"
:min="1"
/>
</el-form-item>
<el-form-item label="数据库名称" prop="dbName">
<el-input v-model="dataSourceForm.dbName" />
</el-form-item>
<!-- schemaName 模式名称 仅当数据库类型不是Mysql时显示 -->
<el-form-item
v-if="dataSourceForm.type !== 'MySQL'"
label="模式名称"
prop="schemaName"
>
<el-input v-model="dataSourceForm.schemaName" />
</el-form-item>
<el-form-item label="数据库账号" prop="username">
<el-input v-model="dataSourceForm.username" />
</el-form-item>
<el-form-item label="数据库密码" prop="password">
<el-input v-model="dataSourceForm.password" />
</el-form-item>
<el-form-item label="使用" prop="isPrimary">
<el-switch
v-model="dataSourceForm.isPrimary"
active-value="1"
inactive-value="0"
/>
</el-form-item>
<el-divider content-position="left">
<div
class="show-advanced"
@click="showDatasourceAdvanced = !showDatasourceAdvanced"
>
<el-icon
:style="`transform: rotate(${
showDatasourceAdvanced ? 90 : 0
}deg);transition: all 0.3s;`"
>
<ArrowRight />
</el-icon>
<span>
<!-- 显示或隐藏高级选项 -->
{{ showDatasourceAdvanced ? "隐藏高级选项" : "显示高级选项" }}
</span>
</div>
</el-divider>
<Transition name="slide-fade">
<div v-if="showDatasourceAdvanced">
<el-form-item label="初始化连接数" prop="initCount">
<el-input-number v-model="dataSourceForm.initCount" :min="0" />
</el-form-item>
<el-form-item label="最小连接数" prop="minCount">
<el-input-number v-model="dataSourceForm.minCount" :min="0" />
</el-form-item>
<el-form-item label="最大连接数" prop="maxCount">
<el-input-number v-model="dataSourceForm.maxCount" :min="0" />
</el-form-item>
</div>
</Transition>
</el-form>
<template v-else>
<el-row justify="end">
<el-col :span="1.5">
<el-button
icon="plus"
plain
type="primary"
@click="handleAddDataSource"
>新增
</el-button>
</el-col>
</el-row>
<el-table :data="datasource" style="margin-top: 12px">
<el-table-column
align="center"
label="类型"
prop="type"
></el-table-column>
<el-table-column
align="center"
label="IP"
prop="ip"
></el-table-column>
<el-table-column
align="center"
label="端口"
prop="port"
></el-table-column>
<el-table-column
align="center"
label="名称"
prop="dbName"
></el-table-column>
<el-table-column
align="center"
label="账号"
prop="username"
></el-table-column>
<el-table-column
align="center"
label="密码"
prop="password"
></el-table-column>
<el-table-column label="使用" align="center">
<template #default="{ row }">
<el-switch
:loading="isSwitchingIsPrimary"
active-value="1"
inactive-value="0"
:model-value="row.isPrimary"
@change="handleSwitchIsPrimary($event, row)"
/>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="140">
<template #default="{ row }">
<el-button
icon="edit"
link
size="small"
type="warning"
@click="handleUpdateDataSource(row)"
>修改
</el-button>
<el-button
icon="delete"
link
size="small"
type="danger"
@click="handleDeleteDataSource(row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
</template>
<template #footer>
<div class="dialog-footer">
<template v-if="isEditDatasource">
<el-button type="primary" @click="submitDatasource"
>
</el-button>
<el-button @click="isEditDatasource = false"> </el-button>
</template>
<el-button v-else type="primary" @click="handleCloseDatasource"
>关闭
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="jsx" name="Tenant" setup>
import {
addTenant,
datasourceList,
deleteDatasource,
delTenant,
getTenant,
insertDatasource,
listTenant,
refreshCache,
switchPrimary,
updateMode,
updateDatasource,
updateTenant,
updateStatus,
} from "@/api/tenant/tenant";
import { getCurrentInstance, reactive, ref, toRefs } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
database_type_dict,
init_status_dict,
tenant_mode_dict,
tenant_status_dict,
} from "@/constant/dict";
import DictTag from "@/components/DictTag/index.vue";
import { cloneDeep } from "lodash-es";
import { ArrowRight } from "@element-plus/icons-vue";
import { useRouter } from "vue-router";
const { proxy } = getCurrentInstance();
const router = useRouter();
const tenantList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");
const tenantSettingRef = ref();
const datasourceFormRef = ref();
const showTenantSetting = ref(false); /*是否显示租户设置对话框*/
const showDatasourceDialog = ref(false); /*显示数据源列表dialog*/
const tempTenantId = ref(null); /*临时的租户id状态, 用于在新添加的数据源中提交*/
const isEditDatasource = ref(false); /*是否处于添加或修改状态*/
const showDatasourceAdvanced = ref(false); /*是否显示高级选项*/
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
companyName: null,
contactName: null,
contactPhone: null,
address: null,
profile: null,
licenseNumber: null,
domain: null,
expireTime: null,
accountCount: null,
status: null,
createUser: null,
updateUser: null,
},
rules: {
companyName: [
{ required: true, message: "公司名称不能为空", trigger: "blur" },
],
},
tenantSettingForm: {},
dataSourceForm: {} /*数据源项目*/,
dataSourceRules: {
type: [
{
required: true,
message: "请选择数据库类型",
trigger: "blur",
},
],
ip: [
{
required: true,
message: "请输入ip",
trigger: "blur",
},
],
port: [
{
required: true,
message: "请输入端口",
trigger: "blur",
},
],
schemaName: [
{
required: true,
message: "请输入模式名称",
trigger: "blur",
},
],
dbName: [
{
required: true,
message: "请输入数据库名称",
trigger: "blur",
},
],
username: [
{
required: true,
message: "请输入账号",
trigger: "blur",
},
],
password: [
{
required: true,
message: "请输入密码",
trigger: "blur",
},
],
},
tenantSettingRules: {
/*TODO:*/
},
});
const {
queryParams,
form,
rules,
tenantSettingForm,
tenantSettingRules,
dataSourceRules,
dataSourceForm,
} = toRefs(data);
const datasource = ref([]);
const isInit = (tenantId) => {
console.log(tenantId);
console.log(
tenantList.value.find((el) => el.tenantId === tenantId)?.initialized === "1"
);
return (
tenantList.value.find((el) => el.tenantId === tenantId)?.initialized === "1"
);
};
/** 查询租户列表 */
function getList() {
loading.value = true;
listTenant(queryParams.value).then((response) => {
tenantList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
// 取消按钮
function cancel() {
open.value = false;
reset();
}
/**
* 取消租户设置
*/
const cancelSettingTenant = () => {
showTenantSetting.value = false;
resetSettingTenant();
};
// 表单重置
function reset() {
form.value = {
tenantId: null,
companyName: null,
contactName: null,
contactPhone: null,
address: null,
profile: null,
licenseNumber: null,
domain: null,
expireTime: null,
accountCount: null,
status: null,
createUser: null,
createTime: null,
updateUser: null,
updateTime: null,
remark: null,
};
proxy.resetForm("tenantRef");
}
/**
* 重置租户设置表单
*/
const resetSettingTenant = () => {
tenantSettingForm.value = {};
tenantSettingRef.value?.resetFields();
};
/**
* 查看数据源
*/
const handleOpenDataSource = async (tenantId) => {
isEditDatasource.value = false;
tempTenantId.value = tenantId;
const resp = await datasourceList(tenantId);
datasource.value = resp.data;
showDatasourceDialog.value = true;
};
const handleCloseDatasource = () => {
isEditDatasource.value = false;
tempTenantId.value = null;
datasource.value = [];
showDatasourceDialog.value = false;
};
const resetDataSource = () => {
dataSourceForm.value = {
initCount: 1,
minCount: 1,
maxCount: 1,
};
datasourceFormRef.value?.resetFields();
};
/**
* 添加数据源
*/
const handleAddDataSource = () => {
resetDataSource();
dataSourceForm.value.tenantId = tempTenantId.value;
isEditDatasource.value = true;
};
/**
* 删除数据源
* @param row
*/
const handleDeleteDataSource = (row) => {
ElMessageBox.confirm(`确认删除此数据源: ${row.ip}`, "删除数据源", {}).then(
async () => {
await deleteDatasource(row.datasourceId);
ElMessage.success("已删除数据源");
const resp = await datasourceList(tempTenantId.value);
datasource.value = resp.data;
}
);
};
/**
* 修噶数据源
* @param row
*/
const handleUpdateDataSource = (row) => {
resetDataSource();
dataSourceForm.value = cloneDeep(row);
try {
dataSourceForm.value.port = parseInt(dataSourceForm.value.port);
} catch (e) {
console.log(e);
}
isEditDatasource.value = true;
};
const submitDatasource = async () => {
await datasourceFormRef.value.validate();
if (dataSourceForm.value.datasourceId) {
/*TODO 修改数据源*/
await updateDatasource(dataSourceForm.value);
ElMessage.success("修改数据源成功");
} else {
await insertDatasource(dataSourceForm.value);
ElMessage.success("添加数据源成功");
}
isEditDatasource.value = false;
const resp = await datasourceList(tempTenantId.value);
datasource.value = resp.data;
};
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
// 多选框选中数据
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.tenantId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = "添加租户";
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
const _tenantId = row.tenantId || ids.value;
getTenant(_tenantId).then((response) => {
form.value = response.data;
open.value = true;
title.value = "修改租户";
});
}
/**
* 打开模式设置
* @param row
*/
const handleSetting = (row) => {
tenantSettingForm.value = {
tenantId: row.tenantId,
mode: row.mode,
oriMode: row.mode /*旧mode*/,
};
showTenantSetting.value = true;
};
const tenantStatusSwitchingIndex = ref(-1); // 租户状态切换中的索引
// 是否正在租户状态切换
const isTenantStatusSwitching = ref(false);
/**
* 修改租户状态
*/
const handleChangeTenantStatus = (val, row) => {
const messageVNode = () => (
<>
要修改
<el-tag type="success">{row.companyName}</el-tag>
的状态, 请在下方输入"<span style="color: tomato">确定</span>".
</>
);
ElMessageBox.prompt(messageVNode, "修改租户状态", {
inputPattern: /^确定$/,
inputErrorMessage: "输入不正确",
}).then(async () => {
// tenantStatusSwitchingIndex.value = index;
isTenantStatusSwitching.value = true;
updateStatus(row.tenantId, val)
.then(() => {
ElMessage.success("修改租户状态成功");
})
.finally(() => {
isTenantStatusSwitching.value = false;
})
.then(() => {
getList();
});
});
};
/** 提交按钮 */
function submitForm() {
proxy.$refs["tenantRef"].validate((valid) => {
if (valid) {
if (form.value.tenantId != null) {
updateTenant(form.value).then((response) => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addTenant(form.value).then((response) => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
}
/**
* 提交租户设置
*/
const submitTenantSetting = async () => {
const ori = tenantSettingForm.value.oriMode;
const cur = tenantSettingForm.value.mode;
const warningText = () => (
<span style="color:red">
切换到其他模式会清除已有的数据源配置, 是否继续?
</span>
);
if (ori === "3" && (cur === "1" || cur === "2")) {
await ElMessageBox.confirm(warningText, {
type: "warning",
});
}
await tenantSettingRef.value.validate();
await updateMode(tenantSettingForm.value);
getList();
ElMessage.success("模式设置成功");
showTenantSetting.value = false;
};
/** 删除按钮操作 */
function handleDelete(row) {
const _tenantIds = row.tenantId || ids.value;
proxy.$modal
.confirm('是否确认删除租户编号为"' + _tenantIds + '"的数据项?')
.then(function () {
return delTenant(_tenantIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
"tenant/tenant/export",
{
...queryParams.value,
},
`tenant_${new Date().getTime()}.xlsx`
);
}
/** 刷新缓存按钮操作 */
function handleRefreshCache() {
refreshCache().then(() => {
proxy.$modal.msgSuccess("刷新缓存成功");
});
}
// 正在切换isPrimary 的索引
const switchingIsPrimaryIndex = ref(-1);
const isSwitchingIsPrimary = ref(false);
/**
* 切换数据源使用状态
* @param val
* @param row
*/
function handleSwitchIsPrimary(val, row) {
const messageVNode = () => (
<>
要修改
<el-tag type="success">{row.dbName}</el-tag>
的状态, 请在下方输入"<span style="color: tomato">确定</span>".
</>
);
ElMessageBox.prompt(messageVNode, "修改数据源使用状态", {
inputPattern: /^确定$/,
inputErrorMessage: "输入不正确",
}).then(async () => {
isSwitchingIsPrimary.value = true;
switchPrimary(tempTenantId.value, row.datasourceId)
.then(() => {
ElMessage.success("修改租户状态成功");
})
.finally(() => {
isSwitchingIsPrimary.value = false;
})
.then(async () => {
const resp = await datasourceList(tempTenantId.value);
datasource.value = resp.data;
});
});
}
/**
* 前往初始化页面
*/
const handleInitialize = () => {
const tenantId = ids.value;
router.push({
path: "/tenant/initialize",
query: {
id: tenantId,
},
});
};
getList();
</script>
<style lang="scss" scoped>
.data-source-item {
height: 32px;
width: 100%;
border-radius: 6px;
border: 1px solid rgb(203 213 225);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px 0 20px;
&:not(:first-child) {
margin-top: 12px;
}
.left {
display: flex;
align-items: center;
.database-type {
font-size: 15px;
}
.database-host {
width: 100px;
margin-left: 12px;
font-size: 13px;
}
.database-port {
width: 30px;
margin-left: 12px;
font-size: 13px;
}
.database-name {
margin-left: 10px;
font-size: 13px;
}
}
.right {
display: flex;
align-items: center;
}
}
:deep(.mode-setting-radio .el-form-item__content) {
display: flex;
justify-content: center;
}
// 高级选项显示切换按钮
.show-advanced {
display: flex;
align-items: center;
cursor: pointer;
//禁止选中
user-select: none;
.el-icon {
font-size: 16px;
}
span {
margin-left: 6px;
}
}
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translatey(-20px);
opacity: 0;
}
</style>