😎1、增加单独更新租户种子处理 2、适配人大金仓列排序 3、页面调整
This commit is contained in:
parent
bb5b8caba2
commit
7525ea2fa0
@ -39,7 +39,7 @@
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.166" />
|
||||
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.4" />
|
||||
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1069" />
|
||||
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1070" />
|
||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -133,8 +133,10 @@ public static class RepositoryExtension
|
||||
/// <returns> </returns>
|
||||
public static ISugarQueryable<T> OrderBuilder<T>(this ISugarQueryable<T> queryable, BasePageInput pageInput, string prefix = "", string defaultSortField = "Id", bool descSort = true)
|
||||
{
|
||||
var iSqlBuilder = InstanceFactory.GetSqlBuilderWithContext(queryable.Context);
|
||||
|
||||
// 约定默认每张表都有Id排序
|
||||
var orderStr = string.IsNullOrWhiteSpace(defaultSortField) ? "" : $"{prefix}{defaultSortField}" + (descSort ? " Desc" : " Asc");
|
||||
var orderStr = string.IsNullOrWhiteSpace(defaultSortField) ? "" : $"{prefix}{iSqlBuilder.GetTranslationColumnName(defaultSortField)}" + (descSort ? " Desc" : " Asc");
|
||||
|
||||
TypeAdapterConfig typeAdapterConfig = new();
|
||||
typeAdapterConfig.ForType<T, BasePageInput>().IgnoreNullValues(true);
|
||||
@ -144,9 +146,8 @@ public static class RepositoryExtension
|
||||
if (!string.IsNullOrEmpty(nowPagerInput.Field) && !string.IsNullOrEmpty(nowPagerInput.Order))
|
||||
{
|
||||
var col = queryable.Context.EntityMaintenance.GetEntityInfo<T>().Columns.FirstOrDefault(u => u.PropertyName.Equals(nowPagerInput.Field, StringComparison.CurrentCultureIgnoreCase));
|
||||
orderStr = col != null
|
||||
? $"{prefix}{col.DbColumnName} {(nowPagerInput.Order == nowPagerInput.DescStr ? "Desc" : "Asc")}"
|
||||
: $"{prefix}{nowPagerInput.Field} {(nowPagerInput.Order == nowPagerInput.DescStr ? "Desc" : "Asc")}";
|
||||
var dbColumnName = col != null ? col.DbColumnName : nowPagerInput.Field;
|
||||
orderStr = $"{prefix}{iSqlBuilder.GetTranslationColumnName(dbColumnName)} {(nowPagerInput.Order == nowPagerInput.DescStr ? "Desc" : "Asc")}";
|
||||
}
|
||||
return queryable.OrderByIF(!string.IsNullOrWhiteSpace(orderStr), orderStr);
|
||||
}
|
||||
|
||||
@ -442,7 +442,6 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
|
||||
// 默认数据库配置
|
||||
var defaultConfig = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault();
|
||||
|
||||
var config = new DbConnectionConfig
|
||||
{
|
||||
ConfigId = tenant.Id.ToString(),
|
||||
@ -455,7 +454,24 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
EnableUnderLine = defaultConfig.DbSettings.EnableUnderLine,
|
||||
}
|
||||
};
|
||||
SqlSugarSetup.InitTenantDatabase(App.GetRequiredService<ISqlSugarClient>().AsTenant(), config);
|
||||
SqlSugarSetup.InitTenantDatabase(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建租户数据 🔖
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("创建租户数据")]
|
||||
public async Task InitTenantData(TenantInput input)
|
||||
{
|
||||
var tenant = await _sysTenantRep.GetSingleAsync(u => u.Id == input.Id);
|
||||
if (tenant == null) return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tenant.Connection) || tenant.Connection.Length < 10)
|
||||
throw Oops.Oh(ErrorCodeEnum.Z1004);
|
||||
|
||||
SqlSugarSetup.InitTenantData(_sysTenantRep.AsTenant(), SqlSugarConst.MainConfigId.ToLong());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -403,12 +403,12 @@ public static class SqlSugarSetup
|
||||
/// <summary>
|
||||
/// 初始化租户业务数据库
|
||||
/// </summary>
|
||||
/// <param name="iTenant"></param>
|
||||
/// <param name="config"></param>
|
||||
public static void InitTenantDatabase(ITenant iTenant, DbConnectionConfig config)
|
||||
public static void InitTenantDatabase(DbConnectionConfig config)
|
||||
{
|
||||
SetDbConfig(config);
|
||||
|
||||
var iTenant = App.GetRequiredService<ISqlSugarClient>().AsTenant();
|
||||
if (!iTenant.IsAnyConnection(config.ConfigId.ToString()))
|
||||
iTenant.AddConnection(config);
|
||||
var db = iTenant.GetConnectionScope(config.ConfigId.ToString());
|
||||
@ -430,11 +430,24 @@ public static class SqlSugarSetup
|
||||
db.CodeFirst.SplitTables().InitTables(entityType);
|
||||
}
|
||||
|
||||
// 初始化业务应用种子数据
|
||||
InitTenantData(iTenant, config.ConfigId.ToLong());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化租户业务数据
|
||||
/// </summary>
|
||||
/// <param name="iTenant"></param>
|
||||
/// <param name="dbConfigId">租户Id</param>
|
||||
public static void InitTenantData(ITenant iTenant, long dbConfigId)
|
||||
{
|
||||
// 初始化业务应用种子数据
|
||||
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))))
|
||||
.Where(u => u.IsDefined(typeof(AppSeedAttribute), false))
|
||||
.OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? ((SeedDataAttribute)u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0]).Order : 0).ToList();
|
||||
if (seedDataTypes == null || seedDataTypes.Count < 1) return;
|
||||
|
||||
var db = iTenant.GetConnectionScope(dbConfigId);
|
||||
foreach (var seedType in seedDataTypes)
|
||||
{
|
||||
var instance = Activator.CreateInstance(seedType);
|
||||
@ -444,7 +457,7 @@ public static class SqlSugarSetup
|
||||
|
||||
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
||||
var entityInfo = db.EntityMaintenance.GetEntityInfo(entityType);
|
||||
var dbConfigId = config.ConfigId.ToLong();
|
||||
// var dbConfigId = config.ConfigId.ToLong();
|
||||
// 若实体包含租户Id字段,则设置为当前租户Id
|
||||
if (entityInfo.Columns.Any(u => u.PropertyName == nameof(EntityTenantId.TenantId)))
|
||||
{
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"qs": "^6.13.0",
|
||||
"relation-graph": "^2.2.3",
|
||||
"screenfull": "^6.0.2",
|
||||
"sm-crypto-v2": "^1.9.1",
|
||||
"sm-crypto-v2": "^1.9.2",
|
||||
"sortablejs": "^1.15.2",
|
||||
"splitpanes": "^3.1.5",
|
||||
"vcrontab-3": "^3.3.22",
|
||||
|
||||
@ -180,6 +180,54 @@ export const SysTenantApiAxiosParamCreator = function (configuration?: Configura
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据 🔖
|
||||
* @param {TenantInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysTenantInitTenantDataPost: async (body?: TenantInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysTenant/initTenantData`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication Bearer required
|
||||
// http bearer authentication required
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
|
||||
|
||||
const query = new URLSearchParams(localVarUrlObj.search);
|
||||
for (const key in localVarQueryParameter) {
|
||||
query.set(key, localVarQueryParameter[key]);
|
||||
}
|
||||
for (const key in options.params) {
|
||||
query.set(key, options.params[key]);
|
||||
}
|
||||
localVarUrlObj.search = (new URLSearchParams(query)).toString();
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
|
||||
localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
|
||||
|
||||
return {
|
||||
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据库 🔖
|
||||
@ -610,6 +658,20 @@ export const SysTenantApiFp = function(configuration?: Configuration) {
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据 🔖
|
||||
* @param {TenantInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysTenantInitTenantDataPost(body?: TenantInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
|
||||
const localVarAxiosArgs = await SysTenantApiAxiosParamCreator(configuration).apiSysTenantInitTenantDataPost(body, options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据库 🔖
|
||||
@ -760,6 +822,16 @@ export const SysTenantApiFactory = function (configuration?: Configuration, base
|
||||
async apiSysTenantGrantMenuPost(body?: RoleMenuInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
return SysTenantApiFp(configuration).apiSysTenantGrantMenuPost(body, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据 🔖
|
||||
* @param {TenantInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysTenantInitTenantDataPost(body?: TenantInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
return SysTenantApiFp(configuration).apiSysTenantInitTenantDataPost(body, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据库 🔖
|
||||
@ -882,6 +954,17 @@ export class SysTenantApi extends BaseAPI {
|
||||
public async apiSysTenantGrantMenuPost(body?: RoleMenuInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
return SysTenantApiFp(this.configuration).apiSysTenantGrantMenuPost(body, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据 🔖
|
||||
* @param {TenantInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysTenantApi
|
||||
*/
|
||||
public async apiSysTenantInitTenantDataPost(body?: TenantInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
return SysTenantApiFp(this.configuration).apiSysTenantInitTenantDataPost(body, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 创建租户数据库 🔖
|
||||
|
||||
@ -74,7 +74,6 @@
|
||||
<script lang="ts" setup name="sysCodeGen">
|
||||
import { onMounted, reactive, ref, defineAsyncComponent } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -129,7 +129,6 @@
|
||||
<script lang="ts" setup name="sysDict">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -86,7 +86,7 @@
|
||||
<script lang="ts" setup name="sysLdap">
|
||||
import { defineAsyncComponent, ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { auth, auths } from '/@/utils/authFunction';
|
||||
import { auths } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -199,9 +199,8 @@ const options = useVxeTable<SysLogOp>(
|
||||
);
|
||||
|
||||
// 页面初始化
|
||||
onMounted(async () => {
|
||||
onMounted(() => {
|
||||
state.localPageParam = Local.get(localPageParamKey) || state.localPageParam;
|
||||
await handleQuery();
|
||||
});
|
||||
|
||||
// 查询api
|
||||
|
||||
@ -71,7 +71,6 @@
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import commonFunction from '/@/utils/commonFunction';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -71,7 +71,6 @@
|
||||
<script lang="ts" setup name="sysOAuthUser">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -59,7 +59,6 @@
|
||||
<script lang="ts" setup name="sysOpenAccess">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -57,7 +57,6 @@
|
||||
<script lang="ts" setup name="sysPlugin">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -57,7 +57,6 @@
|
||||
<script lang="ts" setup name="sysPrint">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
|
||||
<script lang="ts" setup name="sysRegion">
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { Local } from '/@/utils/storage';
|
||||
|
||||
@ -80,13 +80,15 @@
|
||||
<el-button icon="ele-Edit" size="small" text type="primary" @click="handleEdit(row)" v-auth="'sysTenant/update'" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button icon="ele-Delete" text type="danger" @click="handleDelete(row)" :v-auth="'sysTenant/delete'" :disabled="row.id == 1300000000001"> </el-button>
|
||||
<el-button icon="ele-Delete" text type="danger" @click="handleDelete(row)" v-auth="'sysTenant/delete'" :disabled="row.id == 1300000000001"> </el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="重置密码" placement="top">
|
||||
<el-button icon="ele-RefreshLeft" text type="danger" @click="resetTenantPwd(row)" :v-auth="'sysTenant/resetPwd'"> </el-button>
|
||||
<el-button icon="ele-RefreshLeft" text type="danger" @click="resetTenantPwd(row)" v-auth="'sysTenant/resetPwd'"> </el-button>
|
||||
</el-tooltip>
|
||||
<el-button icon="ele-Coin" size="small" text type="danger" @click="createTenant(row)" v-auth="'sysTenant/createDb'" :disabled="row.tenantType == 0"> 创建库 </el-button>
|
||||
<el-button icon="ele-Menu" size="small" text type="primary" @click="openGrantMenu(row)" :v-auth="'sysTenant/grantMenu'"> 授权菜单 </el-button>
|
||||
|
||||
<el-button icon="ele-Coin" size="small" text type="danger" @click="createTenantData(row)" :v-auth="'sysTenant/createDb'" v-if="row.tenantType === 0"> 创建租户数据 </el-button>
|
||||
<el-button icon="ele-Coin" size="small" text type="danger" @click="createTenantDb(row)" :v-auth="'sysTenant/createDb'" v-else> 创建租户库表 </el-button>
|
||||
<el-button icon="ele-Menu" size="small" text type="primary" @click="openGrantMenu(row)" v-auth="'sysTenant/grantMenu'"> 授权菜单 </el-button>
|
||||
<el-button icon="ele-Link" size="small" text type="primary" @click="openGrantApi(row)"> 接口黑名单 </el-button>
|
||||
</template>
|
||||
</vxe-grid>
|
||||
@ -153,7 +155,7 @@ const options = useVxeTable<TenantOutput>(
|
||||
{ field: 'slaveConnections', title: '从库连接', showOverflow: 'tooltip' },
|
||||
{ field: 'orderNo', title: '排序', width: 80, showOverflow: 'tooltip' },
|
||||
{ field: '', title: '修改记录', width: 100, showOverflow: 'tooltip', slots: { default: 'row_record' } },
|
||||
{ title: '操作', fixed: 'right', width: 300, showOverflow: true, slots: { default: 'row_buttons' } },
|
||||
{ title: '操作', fixed: 'right', width: 430, showOverflow: true, slots: { default: 'row_buttons' } },
|
||||
],
|
||||
},
|
||||
// vxeGrid配置参数(此处可覆写任何参数),参考vxe-table官方文档
|
||||
@ -246,7 +248,7 @@ const openGrantApi = async (row: any) => {
|
||||
};
|
||||
|
||||
// 创建租户库
|
||||
const createTenant = (row: any) => {
|
||||
const createTenantDb = (row: any) => {
|
||||
ElMessageBox.confirm(`确定创建/更新租户数据库:【${row.name}】?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
@ -259,6 +261,20 @@ const createTenant = (row: any) => {
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 创建租户数据
|
||||
const createTenantData = (row: any) => {
|
||||
ElMessageBox.confirm(`确定创建/更新租户数据:【${row.name}】?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
await getAPI(SysTenantApi).apiSysTenantInitTenantDataPost({ id: row.id });
|
||||
ElMessage.success('创建/更新租户数据库成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 修改状态
|
||||
const changeStatus = async (scope: any) => {
|
||||
getAPI(SysTenantApi)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user