Преглед изворни кода

Merge branch 'i18n' into v2

夜鹰 пре 7 месеци
родитељ
комит
15593c39aa

+ 1 - 1
Web/src/layout/navBars/topBar/user.vue

@@ -35,7 +35,7 @@
 			<i class="icon-skin iconfont" title="布局配置"></i>
 		</div>
 		<div class="layout-navbars-breadcrumb-user-icon">
-			<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
+			<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="400" :persistent="false">
 				<template #reference>
 					<el-badge :is-dot="hasUnreadNotice">
 						<el-icon title="消息">

+ 49 - 21
Web/src/layout/navBars/topBar/userNews.vue

@@ -9,13 +9,21 @@
 				<div class="notice-box">
 					<template v-if="noticeList.length > 0">
 						<div class="notice-item" v-for="(v, k) in noticeList" :key="k" @click="viewNoticeDetail(v)" v-show="v.readStatus == 1 ? false : true">
-							<div class="notice-title">{{ v.type == 1 ? '【通知】' : '【公告】' }}{{ v.title }}</div>
-							<div class="notice-content">{{ removeHtmlSub(v.content) }}</div>
-							<div class="notice-time">{{ v.publicTime }}</div>
-							<el-divider border-style="dashed" style="margin: 10px 0" />
+							<div class="notice-item-icon">
+                                <el-icon size="24" color="var(--el-color-primary)">
+                                    <ele-Notification v-if="v.type == 1" />
+                                    <ele-Message v-else />
+                                </el-icon>
+                            </div>
+                            <div>
+                                <div class="notice-title">{{ v.title }}</div>
+                                <div class="notice-content">{{ removeHtmlSub(v.content) }}</div>
+                                <div class="notice-time">{{ v.publicTime }}</div>
+                            </div>
+                            
 						</div>
 					</template>
-					<el-empty description="空" v-else></el-empty>
+					<el-empty description="没有新消息" v-else style="height: 85%;"></el-empty>
 				</div>
 				<div class="notice-foot" @click="goToNotice" v-if="noticeList.length > 0">前往通知中心</div>
 			</el-tab-pane>
@@ -24,8 +32,8 @@
 					<el-icon><ele-Position /></el-icon>
 					<span style="margin-left: 5px">我的</span>
 				</template>
-				<div style="height: 400px; overflow-y: auto; padding-right: 10px">
-					<el-empty description=""></el-empty>
+				<div class="notice-box" style="height: 435px;">
+					<el-empty description="没有新消息" style="height: 85%;"></el-empty>
 				</div>
 			</el-tab-pane>
 		</el-tabs>
@@ -86,24 +94,34 @@ const viewNoticeDetail = async (notice: any) => {
 		font-size: 12px;
 		.notice-box {
 			height: 400px;
-			padding-right: 10px;
-
-			margin-bottom: 35px;
-			&:hover {
-				overflow-y: scroll;
-			}
+			padding: 0 5px;
+            overflow-y: auto;
 		}
 		.notice-item {
+            display: flex;
+            
+            padding: 10px 0;
+            border-bottom: 1px dashed var(--el-border-color);
+
+            &-icon {
+                margin: 0 15px;
+                display: inline-flex;
+                justify-content: center;
+                align-items: center;
+            }
+
 			&:hover {
-				background-color: rgba(#b8b8b8, 0.1);
+				background-color: var(--el-color-primary-light-9);
+                cursor: pointer;
+			}
+			.notice-title {
+                font-size: 14px;
+                font-weight: 600;
+				//color: var(--el-color-primary);
 			}
-			// .notice-title {
-			// 	color: var(--el-color-primary);
-			// }
 			.notice-content {
 				color: var(--el-text-color-secondary);
-				margin-top: 3px;
-				margin-bottom: 3px;
+				margin: 10px 0;
 			}
 			.notice-time {
 				color: var(--el-text-color-secondary);
@@ -117,12 +135,22 @@ const viewNoticeDetail = async (notice: any) => {
 		color: var(--el-color-primary);
 		font-size: 14px;
 		cursor: pointer;
-		position: absolute;
-		bottom: 0px;
 		background-color: #fff;
 		display: flex;
 		align-items: center;
 		justify-content: center;
+
+        border-top: 1px solid #eee;
+        padding-top: 9px;
 	}
 }
+.el-tabs {
+    :deep(.el-tabs__header) {
+        margin: 0;
+    }
+    .el-tab-pane {
+        display: flex;
+        flex-direction: column;
+    }
+}
 </style>

+ 11 - 0
Web/src/stores/themeConfig.ts

@@ -152,6 +152,17 @@ export const useThemeConfig = defineStore('themeConfig', {
 			icp: '',
 			// Icp地址
 			icpUrl: '',
+
+			// 是否开启二级验证
+			secondVer: false,
+			// 是否开启注册功能
+			registration: false,
+			// 登录时隐藏租户
+			hideTenantForLogin: false,
+			// 是否开启验证码
+			captcha: false,
+			// 是否加载完成
+			isLoaded: false,
 		},
 	}),
 	actions: {

+ 35 - 0
Web/src/theme/element.scss

@@ -2,6 +2,9 @@
 
 /* Button 按钮
 ------------------------------- */
+.el-button {
+    font-family: var(--el-font-family);
+}
 // 第三方字体图标大小
 .el-button:not(.is-circle) i.el-icon,
 .el-button i.iconfont,
@@ -509,6 +512,38 @@
     --el-tree-node-content-height: 30px;
 }
 
+.el-table .el-table__cell {
+    &:has(.cell .el-button, .el-tag, .el-switch, .el-avatar) {
+        padding: 0;
+        .cell { text-overflow: clip; }
+    }
+
+    .el-button.is-text {
+        height: 20px;
+    }
+
+    .el-text--large {
+        font-size: var(--el-font-size-base);;
+    }
+    .el-button--large {
+        --el-button-size: 32px;
+    }
+    .el-tag--large {
+        height: 28px;
+    }
+    .el-button--default {
+        height: 28px;
+    }
+    .el-button [class*=el-icon]+span{
+        margin-left: 0;
+    }
+}
+.el-table [class*=el-table__row--level] .el-table__expand-icon {
+    height: 14px;
+    line-height: 14px;
+    width: 14px;
+}
+
 $--el-table-text-color: #fb6d49;
 
 // hack列表页

+ 3 - 0
Web/src/types/axios.d.ts

@@ -10,4 +10,7 @@ declare module 'axios' {
 		type?: string;
 		[key: string]: T;
 	}
+	export interface AxiosRequestConfig {
+		customCatch?: boolean;
+	}
 }

+ 7 - 5
Web/src/types/global.d.ts

@@ -1,12 +1,14 @@
 // 申明外部 npm 插件模块
 declare module 'vue-grid-layout';
+declare module 'vue-signature-pad';
+declare module 'vform3-builds';
 declare module 'qrcodejs2-fixes';
 declare module 'splitpanes';
 declare module 'js-cookie';
-declare module '@wangeditor/editor-for-vue';
-declare module 'js-table2excel';
-declare module 'qs';
-declare module 'sortablejs';
+//declare module '@wangeditor/editor-for-vue';
+//declare module 'js-table2excel';
+//declare module 'qs';
+//declare module 'sortablejs';
 declare module 'vue-plugin-hiprint';
 declare module 'vcrontab-3';
 
@@ -117,7 +119,7 @@ declare interface TableType<T = any> {
 }
 
 // 字典数据结构
-export interface DictItem {
+declare interface DictItem {
 	typeCode: string;
 	label: string;
 	value: string;

+ 30 - 2
Web/src/utils/arrayOperation.ts

@@ -23,7 +23,7 @@ export function judgementSameArr(newArr: unknown[] | string[], oldArr: string[])
  * @param b 要比较的对象二
  * @returns 相同返回 true,反之则反
  */
-export function isObjectValueEqual<T>(a: T, b: T): boolean {
+export function isObjectValueEqual<T extends Record<string, any>>(a: T, b: T): boolean {
 	if (!a || !b) return false;
 	let aProps = Object.getOwnPropertyNames(a);
 	let bProps = Object.getOwnPropertyNames(b);
@@ -43,11 +43,12 @@ export function isObjectValueEqual<T>(a: T, b: T): boolean {
 }
 
 /**
- * 数组、数组对象去重
+ * 原始实现:数组、数组对象去重
  * @param arr 数组内容
  * @param attr 需要去重的键值(数组对象)
  * @returns
  */
+/*
 export function removeDuplicate(arr: EmptyArrayType, attr?: string) {
 	if (!Object.keys(arr).length) {
 		return arr;
@@ -63,6 +64,33 @@ export function removeDuplicate(arr: EmptyArrayType, attr?: string) {
 		}
 	}
 }
+*/
+/**
+ * 优化后实现:数组、数组对象去重
+ * 支持普通数组和对象数组去重,类型安全,且兼容原有所有调用方式
+ * @param arr 数组内容
+ * @param attr 需要去重的键值(数组对象)
+ * @returns
+ */
+export function removeDuplicate<T>(arr: T[], attr?: string): T[] {
+	if (!arr.length) {
+		return arr;
+	} else {
+		if (attr) {
+			const obj: Record<string, boolean> = {};
+			return arr.reduce((cur: T[], item: T) => {
+				const key = (item as any)[attr];
+				if (key && !obj[key]) {
+					obj[key] = true;
+					cur.push(item);
+				}
+				return cur;
+			}, []);
+		} else {
+			return [...new Set(arr)];
+		}
+	}
+}
 
 /* 数组、对象深拷贝
  * @param value 需要拷贝内容

+ 1 - 1
Web/src/utils/authFunction.ts

@@ -1,6 +1,6 @@
 import { useUserInfo } from '/@/stores/userInfo';
 import { judgementSameArr } from '/@/utils/arrayOperation';
-import { resolveDirective, withDirectives } from 'vue';
+import { resolveDirective, withDirectives, VNode } from 'vue';
 
 /**
  * 单个权限验证

+ 2 - 2
Web/src/utils/getStyleSheets.ts

@@ -1,8 +1,8 @@
 import { nextTick } from 'vue';
 import * as svg from '@element-plus/icons-vue';
 // import 本地样式类名数组
-import { iconfonntClassList } from '/@/theme/iconfont/font_2298093_rnp72ifj3ba.ts';
-import { fontAwesomeClassList } from '/@/theme/font-awesome/font-awesome.ts';
+import { iconfonntClassList } from '/@/theme/iconfont/font_2298093_rnp72ifj3ba';
+import { fontAwesomeClassList } from '/@/theme/font-awesome/font-awesome';
 
 // 获取阿里字体图标
 const getAlicdnIconfont = () => {

+ 12 - 4
Web/src/views/system/job/index.vue

@@ -49,7 +49,7 @@
 			<el-table :data="state.jobData" style="width: 100%" v-loading="state.loading" border>
 				<el-table-column type="expand" fixed>
 					<template #default="scope">
-						<el-table style="margin-left: 48px; width: calc(100% - 48px)" :data="(scope.row as JobDetailOutput).jobTriggers" border size="small">
+						<el-table :data="(scope.row as JobDetailOutput).jobTriggers" border size="small">
 							<el-table-column type="index" label="序号" width="55" align="center" fixed />
 							<el-table-column prop="triggerId" label="触发器编号" width="180" header-align="center" fixed show-overflow-tooltip />
 							<el-table-column prop="triggerType" label="类型" width="200" header-align="center" show-overflow-tooltip />
@@ -536,9 +536,17 @@ const handleCurrentChange2 = async (val: number) => {
 };
 </script>
 
-<style>
+<style lang="scss" scoped>
 /* 此样式不能为 scoped */
-.job-index-descriptions-label-style {
-	width: 80px;
+// .job-index-descriptions-label-style {
+// 	width: 80px;
+// }
+
+:deep(.el-table__expanded-cell) {
+    padding: 10px 45px !important;
+    .el-descriptions__body {
+        .el-descriptions__table { table-layout: fixed; }
+        .el-descriptions__label { width: 150px; }
+    }
 }
 </style>

+ 2 - 2
Web/src/views/system/org/index.vue

@@ -42,7 +42,7 @@
                             </template>
                         </el-table-column>
                         <el-table-column prop="orderNo" label="排序" width="70" align="center" show-overflow-tooltip />
-                        <el-table-column label="状态" width="70" align="center" show-overflow-tooltip>
+                        <el-table-column label="状态" width="70" align="center">
                             <template #default="scope">
                                 <g-sys-dict v-model="scope.row.status" code="StatusEnum" />
                             </template>
@@ -52,7 +52,7 @@
                                 <ModifyRecord :data="scope.row" />
                             </template>
                         </el-table-column>
-                        <el-table-column label="操作" width="210" fixed="right" align="center" show-overflow-tooltip>
+                        <el-table-column label="操作" width="210" fixed="right" align="center">
                             <template #default="scope">
                                 <el-button icon="ele-Edit" text type="primary" @click="openEditOrg(scope.row)"
                                     v-auth="'sysOrg:update'"> 编辑 </el-button>

+ 1 - 1
Web/src/views/system/pos/index.vue

@@ -30,7 +30,7 @@
 				<el-table-column type="index" label="序号" width="55" align="center" />
 				<el-table-column prop="name" label="职位名称" align="center" show-overflow-tooltip />
 				<el-table-column prop="code" label="职位编码" align="center" show-overflow-tooltip />
-				<el-table-column prop="userList" label="在职人数" width="70" align="center" show-overflow-tooltip >
+				<el-table-column prop="userList" label="在职人数" width="100" align="center" show-overflow-tooltip >
 					<template #default="scope">{{ scope.row.userList?.length}}</template>
 				</el-table-column>
 				<el-table-column prop="userList" label="人员明细" width="120" align="center" show-overflow-tooltip >

+ 299 - 234
Web/src/views/system/tenant/index.vue

@@ -1,141 +1,172 @@
 <template>
-	<div class="sys-tenant-container">
-		<el-card shadow="hover" :body-style="{ padding: 5 }">
-			<el-form :model="state.queryParams" ref="queryForm" :inline="true">
-				<el-form-item label="租户名称">
-					<el-input v-model="state.queryParams.name" placeholder="租户名称" clearable />
-				</el-form-item>
-				<el-form-item label="联系电话">
-					<el-input v-model="state.queryParams.phone" placeholder="联系电话" clearable />
-				</el-form-item>
-				<el-form-item>
-					<el-button-group>
-						<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysTenant:page'"> 查询
-						</el-button>
-						<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
-					</el-button-group>
-				</el-form-item>
-				<el-form-item>
-					<el-button type="primary" icon="ele-Plus" @click="openAddTenant" v-auth="'sysTenant:add'"> 新增
-					</el-button>
-				</el-form-item>
-			</el-form>
-		</el-card>
+    <div class="sys-tenant-container">
+        <el-card shadow="hover" :body-style="{ padding: 5 }">
+            <el-form :model="state.queryParams" ref="queryForm" :inline="true">
+                <el-form-item label="租户名称">
+                    <el-input v-model="state.queryParams.name" placeholder="租户名称" clearable />
+                </el-form-item>
+                <el-form-item label="联系电话">
+                    <el-input v-model="state.queryParams.phone" placeholder="联系电话" clearable />
+                </el-form-item>
+                <el-form-item>
+                    <el-button-group>
+                        <el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysTenant:page'"> 查询
+                        </el-button>
+                        <el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
+                    </el-button-group>
+                </el-form-item>
+                <el-form-item>
+                    <el-button type="primary" icon="ele-Plus" @click="openAddTenant" v-auth="'sysTenant:add'"> 新增
+                    </el-button>
+                </el-form-item>
+            </el-form>
+        </el-card>
 
-		<el-card class="full-table" shadow="hover" style="margin-top: 5px">
-			<el-table :data="state.tenantData" style="width: 100%" v-loading="state.loading" border>
-				<el-table-column type="index" label="序号" width="55" align="center" fixed />
-				<el-table-column prop="logo" label="图标" width="55" align="center" show-overflow-tooltip>
-					<template #default="scope">
-						<el-avatar shape="square" :src="scope.row.logo" size="small" />
-					</template>
-				</el-table-column>
-				<el-table-column prop="name" label="名称" width="180" align="center" show-overflow-tooltip />
-				<el-table-column prop="title" label="标题" width="180" show-overflow-tooltip />
-				<el-table-column prop="viceTitle" label="副标题" width="180" show-overflow-tooltip />
-				<el-table-column prop="viceDesc" label="描述" width="300" show-overflow-tooltip />
-				<el-table-column prop="watermark" label="水印" width="130" show-overflow-tooltip />
-				<el-table-column prop="copyright" label="版权信息" width="350" show-overflow-tooltip />
-				<el-table-column prop="icp" label="备案号" width="130" show-overflow-tooltip />
-				<el-table-column prop="icpUrl" label="icp地址" width="280" show-overflow-tooltip />
-				<el-table-column prop="enableReg" label="启用注册" width="280" show-overflow-tooltip>
-					<template #default="scope">
-						<g-sys-dict v-model="scope.row.enableReg" code="YesNoEnum" />
-					</template>
-				</el-table-column>
-				<el-table-column prop="adminAccount" label="租管账号" align="center" width="120" show-overflow-tooltip />
-				<el-table-column prop="phone" label="电话" width="120" align="center" show-overflow-tooltip />
-				<el-table-column prop="host" label="域名" width="150" show-overflow-tooltip />
-				<!-- <el-table-column prop="email" label="邮箱" show-overflow-tooltip /> -->
-				<el-table-column prop="tenantType" label="租户类型" width="100" align="center" show-overflow-tooltip>
-					<template #default="scope">
-						<g-sys-dict v-model="scope.row.tenantType" code="TenantTypeEnum" />
-					</template>
-				</el-table-column>
-				<el-table-column label="状态" width="70" align="center" show-overflow-tooltip>
-					<template #default="scope">
-						<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="2" size="small"
-							@change="changeStatus(scope.row)" :disabled="scope.row.id == 123456780000000" />
-					</template>
-				</el-table-column>
-				<el-table-column prop="dbType" label="数据库类型" width="120" align="center" show-overflow-tooltip>
-					<template #default="scope">
-						<el-tag v-if="scope.row.dbType === 0"> MySql </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 1"> SqlServer </el-tag>
-						<el-tag v-if="scope.row.dbType === 2"> Sqlite </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 3"> Oracle </el-tag>
-						<el-tag v-if="scope.row.dbType === 4"> PostgreSQL </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 5"> Dm </el-tag>
-						<el-tag v-if="scope.row.dbType === 6"> Kdbndp </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 7"> Oscar </el-tag>
-						<el-tag v-if="scope.row.dbType === 8"> MySqlConnector </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 9"> Access </el-tag>
-						<el-tag v-if="scope.row.dbType === 10"> OpenGauss </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 11"> QuestDB </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 12"> HG </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 13"> ClickHouse </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 14"> GBase </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 15"> Odbc </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 16"> OceanBaseForOracle </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 17"> TDengine </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 18"> GaussDB </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 19"> OceanBase </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 20"> Tidb </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 21"> Vastbase </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 22"> PolarDB </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 23"> Doris </el-tag>
-						<el-tag v-else-if="scope.row.dbType === 900"> Custom </el-tag>
-					</template>
-				</el-table-column>
-				<!-- <el-table-column prop="configId" label="数据库标识" show-overflow-tooltip /> -->
-				<el-table-column prop="connection" label="数据库连接" min-width="300" header-align="center"
-					show-overflow-tooltip />
-				<el-table-column prop="slaveConnections" label="从库连接" min-width="300" header-align="center"
-					show-overflow-tooltip />
-				<el-table-column prop="orderNo" label="排序" width="70" show-overflow-tooltip />
-				<el-table-column label="修改记录" width="100" align="center" show-overflow-tooltip>
-					<template #default="scope">
-						<ModifyRecord :data="scope.row" />
-					</template>
-				</el-table-column>
-				<el-table-column label="操作" width="200" fixed="right" align="center" show-overflow-tooltip>
-					<template #default="scope">
-						<el-button icon="ele-Coin" size="small" text type="danger" @click="createTenant(scope.row)"
-							v-auth="'sysTenant:createDb'" :disabled="scope.row.tenantType == 0"> 创建库 </el-button>
-						<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditTenant(scope.row)"
-							v-auth="'sysTenant:update'"> 编辑 </el-button>
-						<el-dropdown>
-							<el-button icon="ele-MoreFilled" size="small" text type="primary"
-								style="padding-left: 12px" />
-							<template #dropdown>
-								<el-dropdown-menu>
-									<el-dropdown-item icon="ele-OfficeBuilding" @click="goTenant(scope.row)"
-									                  :v-auth="'sysTenant:goTenant'"> 进入租管端 </el-dropdown-item>
-									<el-dropdown-item icon="ele-OfficeBuilding" @click="changeTenant(scope.row)"
-									                  :v-auth="'sysTenant:changeTenant'"> 切换租户 </el-dropdown-item>
-									<el-dropdown-item icon="ele-OfficeBuilding" @click="openGrantMenu(scope.row)"
-										:v-auth="'sysTenant:grantMenu'"> 授权菜单 </el-dropdown-item>
-									<el-dropdown-item icon="ele-OfficeBuilding" @click="syncGrantMenu(scope.row)"
-									                  :v-auth="'sysTenant:syncGrantMenu'" title="用于版本更新后,同步授权数据"> 同步授权 </el-dropdown-item>
-									<el-dropdown-item icon="ele-RefreshLeft" @click="resetTenantPwd(scope.row)"
-										:v-auth="'sysTenant:resetPwd'"> 重置密码 </el-dropdown-item>
-									<el-dropdown-item icon="ele-Delete" @click="delTenant(scope.row)"
-										:v-auth="'sysTenant:delete'"> 删除租户 </el-dropdown-item>
-								</el-dropdown-menu>
-							</template>
-						</el-dropdown>
-					</template>
-				</el-table-column>
-			</el-table>
-			<el-pagination v-model:currentPage="state.tableParams.page" v-model:page-size="state.tableParams.pageSize"
-				:total="state.tableParams.total" :page-sizes="[10, 20, 50, 100]" size="small" background
-				@size-change="handleSizeChange" @current-change="handleCurrentChange"
-				layout="total, sizes, prev, pager, next, jumper" />
-		</el-card>
+        <el-card class="full-table" shadow="hover" style="margin-top: 5px">
+            <el-table :data="state.tenantData" style="width: 100%" :preserve-expanded-content="preserveExpanded" v-loading="state.loading" border>
+                <el-table-column type="expand">
+                    <template #default="props">
+                        <el-descriptions :column="3" border>
+                            <el-descriptions-item label="LOGO">
+                                <div style="display: flex; align-items: center;"><el-avatar shape="square" :src="props.row.logo" size="small" /></div>
+                            </el-descriptions-item>
+                            <el-descriptions-item label="标题">{{ props.row.title }}</el-descriptions-item>
+                            <el-descriptions-item label="副标题">{{ props.row.viceTitle }}</el-descriptions-item>
+                            
+                            <el-descriptions-item label="域名">{{ props.row.host }}</el-descriptions-item>
+                            <el-descriptions-item label="备案号">{{ props.row.icp }}</el-descriptions-item>
+                            <el-descriptions-item label="水印">{{ props.row.watermark }}</el-descriptions-item>
 
-		<EditTenant ref="editTenantRef" :title="state.editTenantTitle" @handleQuery="handleQuery" />
-		<GrantMenu ref="grantMenuRef" />
-	</div>
+                            <el-descriptions-item label="数据库标识">{{ props.row.configId }}</el-descriptions-item>
+                            <el-descriptions-item label="版权信息" :span="2">{{ props.row.copyright }}</el-descriptions-item>
+                            
+                            <el-descriptions-item label="数据库连接" :span="3">{{ props.row.connection }}</el-descriptions-item>
+                            <el-descriptions-item label="从库连接" :span="3">{{ props.row.slaveConnections }}</el-descriptions-item>
+                        </el-descriptions>
+                    </template>
+                </el-table-column>
+                <!-- <el-table-column type="index" label="序号" width="55" align="center" fixed /> -->
+                <!-- <el-table-column prop="logo" label="图标" width="55" align="center" show-overflow-tooltip>
+                    <template #default="scope">
+                        <el-avatar shape="square" :src="scope.row.logo" size="small" />
+                    </template>
+                </el-table-column> -->
+                <el-table-column prop="name" label="名称" width="180" align="center" show-overflow-tooltip />
+                <!-- <el-table-column prop="title" label="标题" width="180" show-overflow-tooltip /> -->
+                <!-- <el-table-column prop="viceTitle" label="副标题" width="180" show-overflow-tooltip />
+                <el-table-column prop="viceDesc" label="描述" show-overflow-tooltip />
+                <el-table-column prop="watermark" label="水印" width="130" show-overflow-tooltip />
+                <el-table-column prop="copyright" label="版权信息" width="350" show-overflow-tooltip />
+                <el-table-column prop="icp" label="备案号" width="130" show-overflow-tooltip />
+                <el-table-column prop="icpUrl" label="icp地址" width="280" show-overflow-tooltip />
+                <el-table-column prop="enableReg" label="启用注册" width="100" show-overflow-tooltip>
+                    <template #default="scope">
+                        <g-sys-dict v-model="scope.row.enableReg" code="YesNoEnum" />
+                    </template>
+                </el-table-column> -->
+                <el-table-column prop="adminAccount" label="租管账号" align="center" width="120" show-overflow-tooltip />
+                <el-table-column prop="phone" label="电话" width="150" align="center" show-overflow-tooltip />
+                <el-table-column prop="host" label="域名" width="200" show-overflow-tooltip />
+                <!-- <el-table-column prop="email" label="邮箱" show-overflow-tooltip /> -->
+                <el-table-column prop="tenantType" label="租户类型" width="100" align="center">
+                    <template #default="scope">
+                        <g-sys-dict v-model="scope.row.tenantType" code="TenantTypeEnum" />
+                    </template>
+                </el-table-column>
+                <el-table-column label="状态" width="70" align="center" class-name="status">
+                    <template #default="scope">
+                        <div>
+                            <el-switch size="small" class="status-switch" 
+                                v-model="scope.row.status"
+                                :active-value="1"
+                                :inactive-value="2" @change="changeStatus(scope.row)"
+                                :disabled="scope.row.id == 123456780000000" 
+                            />
+                            <span class="status-tage">
+                                <g-sys-dict v-model="scope.row.status" code="StatusEnum" />
+                            </span>
+                        </div>
+
+                    </template>
+                </el-table-column>
+                <el-table-column prop="dbType" label="数据库类型" width="120" align="center">
+                    <template #default="scope">
+                        <el-tag v-if="scope.row.dbType === 0"> MySql </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 1"> SqlServer </el-tag>
+                        <el-tag v-if="scope.row.dbType === 2"> Sqlite </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 3"> Oracle </el-tag>
+                        <el-tag v-if="scope.row.dbType === 4"> PostgreSQL </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 5"> Dm </el-tag>
+                        <el-tag v-if="scope.row.dbType === 6"> Kdbndp </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 7"> Oscar </el-tag>
+                        <el-tag v-if="scope.row.dbType === 8"> MySqlConnector </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 9"> Access </el-tag>
+                        <el-tag v-if="scope.row.dbType === 10"> OpenGauss </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 11"> QuestDB </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 12"> HG </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 13"> ClickHouse </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 14"> GBase </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 15"> Odbc </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 16"> OceanBaseForOracle </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 17"> TDengine </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 18"> GaussDB </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 19"> OceanBase </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 20"> Tidb </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 21"> Vastbase </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 22"> PolarDB </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 23"> Doris </el-tag>
+                        <el-tag v-else-if="scope.row.dbType === 900"> Custom </el-tag>
+                    </template>
+                </el-table-column>
+                <!-- <el-table-column prop="configId" label="数据库标识" show-overflow-tooltip /> -->
+                <!-- <el-table-column prop="connection" label="数据库连接" min-width="300" header-align="center" show-overflow-tooltip />
+                <el-table-column prop="slaveConnections" label="从库连接" min-width="300" header-align="center" show-overflow-tooltip /> -->
+                <el-table-column prop="viceDesc" label="描述" show-overflow-tooltip />
+                <el-table-column prop="orderNo" label="排序" width="70" align="center" />
+                <el-table-column label="修改记录" width="100" align="center">
+                    <template #default="scope">
+                        <ModifyRecord :data="scope.row" />
+                    </template>
+                </el-table-column>
+                <el-table-column label="操作" width="200" fixed="right" align="center">
+                    <template #default="scope">
+                        <el-button icon="ele-Coin" size="small" text type="danger" @click="createTenant(scope.row)"
+                            v-auth="'sysTenant:createDb'" :disabled="scope.row.tenantType == 0"> 创建库 </el-button>
+                        <el-button icon="ele-Edit" size="small" text type="primary" @click="openEditTenant(scope.row)"
+                            v-auth="'sysTenant:update'"> 编辑 </el-button>
+                        <el-dropdown>
+                            <el-button icon="ele-MoreFilled" size="small" text type="primary"
+                                style="padding-left: 12px" />
+                            <template #dropdown>
+                                <el-dropdown-menu>
+                                    <el-dropdown-item icon="ele-OfficeBuilding" @click="goTenant(scope.row)"
+                                        :v-auth="'sysTenant:goTenant'"> 进入租管端 </el-dropdown-item>
+                                    <el-dropdown-item icon="ele-OfficeBuilding" @click="changeTenant(scope.row)"
+                                        :v-auth="'sysTenant:changeTenant'"> 切换租户 </el-dropdown-item>
+                                    <el-dropdown-item icon="ele-OfficeBuilding" @click="openGrantMenu(scope.row)"
+                                        :v-auth="'sysTenant:grantMenu'"> 授权菜单 </el-dropdown-item>
+                                    <el-dropdown-item icon="ele-OfficeBuilding" @click="syncGrantMenu(scope.row)"
+                                        :v-auth="'sysTenant:syncGrantMenu'" title="用于版本更新后,同步授权数据"> 同步授权
+                                    </el-dropdown-item>
+                                    <el-dropdown-item icon="ele-RefreshLeft" @click="resetTenantPwd(scope.row)"
+                                        :v-auth="'sysTenant:resetPwd'"> 重置密码 </el-dropdown-item>
+                                    <el-dropdown-item icon="ele-Delete" @click="delTenant(scope.row)"
+                                        :v-auth="'sysTenant:delete'"> 删除租户 </el-dropdown-item>
+                                </el-dropdown-menu>
+                            </template>
+                        </el-dropdown>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <el-pagination v-model:currentPage="state.tableParams.page" v-model:page-size="state.tableParams.pageSize"
+                :total="state.tableParams.total" :page-sizes="[10, 20, 50, 100]" size="small" background
+                @size-change="handleSizeChange" @current-change="handleCurrentChange"
+                layout="total, sizes, prev, pager, next, jumper" />
+        </el-card>
+
+        <EditTenant ref="editTenantRef" :title="state.editTenantTitle" @handleQuery="handleQuery" />
+        <GrantMenu ref="grantMenuRef" />
+    </div>
 </template>
 
 <script lang="ts" setup name="sysTenant">
@@ -150,165 +181,199 @@ import { TenantOutput } from '/@/api-services/models';
 import { reLoadLoginAccessToken } from "/@/utils/request";
 import GSysDict from "/@/components/sysDict/sysDict.vue";
 
+const preserveExpanded = ref(false)
 const editTenantRef = ref<InstanceType<typeof EditTenant>>();
 const grantMenuRef = ref<InstanceType<typeof GrantMenu>>();
 const state = reactive({
-	loading: false,
-	tenantData: [] as Array<TenantOutput>,
-	queryParams: {
-		name: undefined,
-		phone: undefined,
-	},
-	tableParams: {
-		page: 1,
-		pageSize: 50,
-		total: 0 as any,
-	},
-	editTenantTitle: '',
+    loading: false,
+    tenantData: [] as Array<TenantOutput>,
+    queryParams: {
+        name: undefined,
+        phone: undefined,
+    },
+    tableParams: {
+        page: 1,
+        pageSize: 50,
+        total: 0 as any,
+    },
+    editTenantTitle: '',
 });
 
 onMounted(async () => {
-	handleQuery();
+    handleQuery();
 });
 
 // 查询操作
 const handleQuery = async () => {
-	state.loading = true;
-	let params = Object.assign(state.queryParams, state.tableParams);
-	var res = await getAPI(SysTenantApi).apiSysTenantPagePost(params);
-	state.tenantData = res.data.result?.items ?? [];
-	state.tableParams.total = res.data.result?.total;
-	state.loading = false;
+    state.loading = true;
+    let params = Object.assign(state.queryParams, state.tableParams);
+    var res = await getAPI(SysTenantApi).apiSysTenantPagePost(params);
+    state.tenantData = res.data.result?.items ?? [];
+    state.tableParams.total = res.data.result?.total;
+    state.loading = false;
 };
 
 // 进入租管端
 const goTenant = (row: any) => {
-	ElMessageBox.confirm(`确定要进入【${row.name}】租管端?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning',
-	}).then(() =>
-			getAPI(SysTenantApi)
-			.apiSysTenantGoTenantPost({ id: row.id })
-			.then(res => reLoadLoginAccessToken(res.data.result))
-	);
+    ElMessageBox.confirm(`确定要进入【${row.name}】租管端?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    }).then(() =>
+        getAPI(SysTenantApi)
+            .apiSysTenantGoTenantPost({ id: row.id })
+            .then(res => reLoadLoginAccessToken(res.data.result))
+    );
 }
 
 // 切换租户
 const changeTenant = (row: any) => {
-	ElMessageBox.confirm(`确定要将当前用户切换到【${row.name}】?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning',
-	}).then(() =>
-			getAPI(SysTenantApi)
-			.apiSysTenantChangeTenantPost({ id: row.id })
-			.then(res => reLoadLoginAccessToken(res.data.result))
-	);
+    ElMessageBox.confirm(`确定要将当前用户切换到【${row.name}】?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    }).then(() =>
+        getAPI(SysTenantApi)
+            .apiSysTenantChangeTenantPost({ id: row.id })
+            .then(res => reLoadLoginAccessToken(res.data.result))
+    );
 }
 
 const syncGrantMenu = (row: any) => {
-	ElMessageBox.confirm(`确定要将同步【${row.name}】的授权数据?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning',
-	}).then(async () => {
-		await getAPI(SysTenantApi).apiSysTenantSyncGrantMenuPost({ id: row.id });
-		ElMessage.success('同步授权成功');
-	});
+    ElMessageBox.confirm(`确定要将同步【${row.name}】的授权数据?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    }).then(async () => {
+        await getAPI(SysTenantApi).apiSysTenantSyncGrantMenuPost({ id: row.id });
+        ElMessage.success('同步授权成功');
+    });
 }
 
 // 重置操作
 const resetQuery = () => {
-	state.queryParams.name = undefined;
-	state.queryParams.phone = undefined;
-	handleQuery();
+    state.queryParams.name = undefined;
+    state.queryParams.phone = undefined;
+    handleQuery();
 };
 
 // 打开新增页面
 const openAddTenant = () => {
-	state.editTenantTitle = '添加租户';
-	editTenantRef.value?.openDialog({ tenantType: 0, orderNo: 100, host: '' });
+    state.editTenantTitle = '添加租户';
+    editTenantRef.value?.openDialog({ tenantType: 0, orderNo: 100, host: '' });
 };
 
 // 打开编辑页面
 const openEditTenant = (row: any) => {
-	state.editTenantTitle = '编辑租户';
-	editTenantRef.value?.openDialog(row);
+    state.editTenantTitle = '编辑租户';
+    editTenantRef.value?.openDialog(row);
 };
 
 // 打开授权菜单页面
 const openGrantMenu = async (row: any) => {
-	grantMenuRef.value?.openDialog(row);
+    grantMenuRef.value?.openDialog(row);
 };
 
 // 重置密码
 const resetTenantPwd = async (row: any) => {
-	ElMessageBox.confirm(`确定重置密码:【${row.name}】?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning',
-	})
-		.then(async () => {
-			await getAPI(SysTenantApi)
-				.apiSysTenantResetPwdPost({ userId: row.userId })
-				.then((res) => {
-					ElMessage.success(`密码重置成功为:${res.data.result}`);
-				});
-		})
-		.catch(() => { });
+    ElMessageBox.confirm(`确定重置密码:【${row.name}】?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    })
+        .then(async () => {
+            await getAPI(SysTenantApi)
+                .apiSysTenantResetPwdPost({ userId: row.userId })
+                .then((res) => {
+                    ElMessage.success(`密码重置成功为:${res.data.result}`);
+                });
+        })
+        .catch(() => { });
 };
 
 // 删除
 const delTenant = (row: any) => {
-	ElMessageBox.confirm(`确定删除租户:【${row.name}】?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning',
-	})
-		.then(async () => {
-			await getAPI(SysTenantApi).apiSysTenantDeletePost({ id: row.id });
-			handleQuery();
-			ElMessage.success('删除成功');
-		})
-		.catch(() => { });
+    ElMessageBox.confirm(`确定删除租户:【${row.name}】?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    })
+        .then(async () => {
+            await getAPI(SysTenantApi).apiSysTenantDeletePost({ id: row.id });
+            handleQuery();
+            ElMessage.success('删除成功');
+        })
+        .catch(() => { });
 };
 
 // 改变页面容量
 const handleSizeChange = (val: number) => {
-	state.tableParams.pageSize = val;
-	handleQuery();
+    state.tableParams.pageSize = val;
+    handleQuery();
 };
 
 // 改变页码序号
 const handleCurrentChange = (val: number) => {
-	state.tableParams.page = val;
-	handleQuery();
+    state.tableParams.page = val;
+    handleQuery();
 };
 
 // 创建租户库
 const createTenant = (row: any) => {
-	ElMessageBox.confirm(`确定创建/更新租户数据库:【${row.name}】?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning',
-	})
-		.then(async () => {
-			await getAPI(SysTenantApi).apiSysTenantCreateDbPost({ id: row.id });
-			ElMessage.success('创建/更新租户数据库成功');
-		})
-		.catch(() => { });
+    ElMessageBox.confirm(`确定创建/更新租户数据库:【${row.name}】?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    })
+        .then(async () => {
+            await getAPI(SysTenantApi).apiSysTenantCreateDbPost({ id: row.id });
+            ElMessage.success('创建/更新租户数据库成功');
+        })
+        .catch(() => { });
 };
 
 // 修改状态
 const changeStatus = (row: any) => {
-	getAPI(SysTenantApi)
-		.apiSysTenantSetStatusPost({ id: row.id, status: row.status })
-		.then(() => {
-			ElMessage.success('租户状态设置成功');
-		})
-		.catch(() => {
-			row.status = row.status == 1 ? 2 : 1;
-		});
+    getAPI(SysTenantApi)
+        .apiSysTenantSetStatusPost({ id: row.id, status: row.status })
+        .then(() => {
+            ElMessage.success('租户状态设置成功');
+        })
+        .catch(() => {
+            row.status = row.status == 1 ? 2 : 1;
+        });
 };
 </script>
+
+<style lang="scss" scoped>
+.status {
+    width: 100%;
+
+    .status-switch {
+        display: none;
+        height: 100%;
+        line-height: 100%;
+    }
+
+    .status-tage {
+        display: block;
+    }
+}
+.status:hover {
+    .status-switch {
+        display: block;
+    }
+
+    .status-tage {
+        display: none;
+    }
+}
+
+:deep(.el-table__expanded-cell) {
+    padding: 10px 45px !important;
+    .el-descriptions__body {
+        .el-descriptions__table { table-layout: fixed; }
+        .el-descriptions__label { width: 150px; }
+    }
+}
+</style>