mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 00:10:25 +08:00 
			
		
		
		
	feat: 机器新增命令过滤配置、首页功能完善(操作记录与快捷操作)
This commit is contained in:
		@@ -11,18 +11,17 @@
 | 
				
			|||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@element-plus/icons-vue": "^2.3.1",
 | 
					    "@element-plus/icons-vue": "^2.3.1",
 | 
				
			||||||
    "@vueuse/core": "^10.9.0",
 | 
					    "@vueuse/core": "^10.9.0",
 | 
				
			||||||
    "asciinema-player": "^3.7.0",
 | 
					    "asciinema-player": "^3.7.1",
 | 
				
			||||||
    "axios": "^1.6.2",
 | 
					    "axios": "^1.6.2",
 | 
				
			||||||
    "clipboard": "^2.0.11",
 | 
					    "clipboard": "^2.0.11",
 | 
				
			||||||
    "countup.js": "^2.8.0", 
 | 
					 | 
				
			||||||
    "cropperjs": "^1.6.1",
 | 
					    "cropperjs": "^1.6.1",
 | 
				
			||||||
    "echarts": "^5.5.0",
 | 
					    "echarts": "^5.5.0",
 | 
				
			||||||
    "element-plus": "^2.7.1",
 | 
					    "element-plus": "^2.7.2",
 | 
				
			||||||
    "js-base64": "^3.7.7",
 | 
					    "js-base64": "^3.7.7",
 | 
				
			||||||
    "jsencrypt": "^3.3.2",
 | 
					    "jsencrypt": "^3.3.2",
 | 
				
			||||||
    "lodash": "^4.17.21",
 | 
					    "lodash": "^4.17.21",
 | 
				
			||||||
    "mitt": "^3.0.1",
 | 
					    "mitt": "^3.0.1",
 | 
				
			||||||
    "monaco-editor": "^0.47.0",
 | 
					    "monaco-editor": "^0.48.0",
 | 
				
			||||||
    "monaco-sql-languages": "^0.11.0",
 | 
					    "monaco-sql-languages": "^0.11.0",
 | 
				
			||||||
    "monaco-themes": "^0.4.4",
 | 
					    "monaco-themes": "^0.4.4",
 | 
				
			||||||
    "nprogress": "^0.2.0",
 | 
					    "nprogress": "^0.2.0",
 | 
				
			||||||
@@ -34,7 +33,7 @@
 | 
				
			|||||||
    "sql-formatter": "^15.0.2",
 | 
					    "sql-formatter": "^15.0.2",
 | 
				
			||||||
    "trzsz": "^1.1.5",
 | 
					    "trzsz": "^1.1.5",
 | 
				
			||||||
    "uuid": "^9.0.1",
 | 
					    "uuid": "^9.0.1",
 | 
				
			||||||
    "vue": "^3.4.23",
 | 
					    "vue": "^3.4.25",
 | 
				
			||||||
    "vue-router": "^4.3.2",
 | 
					    "vue-router": "^4.3.2",
 | 
				
			||||||
    "xterm": "^5.3.0",
 | 
					    "xterm": "^5.3.0",
 | 
				
			||||||
    "xterm-addon-fit": "^0.8.0",
 | 
					    "xterm-addon-fit": "^0.8.0",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								mayfly_go_web/src/store/autoOpenResource.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								mayfly_go_web/src/store/autoOpenResource.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import { defineStore } from 'pinia';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 自动打开资源
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const useAutoOpenResource = defineStore('autoOpenResource', {
 | 
				
			||||||
 | 
					    state: () => ({
 | 
				
			||||||
 | 
					        autoOpenResource: {
 | 
				
			||||||
 | 
					            machineCodePath: '',
 | 
				
			||||||
 | 
					            dbCodePath: '',
 | 
				
			||||||
 | 
					            redisCodePath: '',
 | 
				
			||||||
 | 
					            mongoCodePath: '',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    actions: {
 | 
				
			||||||
 | 
					        setMachineCodePath(codePath: string) {
 | 
				
			||||||
 | 
					            this.autoOpenResource.machineCodePath = codePath;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        setDbCodePath(codePath: string) {
 | 
				
			||||||
 | 
					            this.autoOpenResource.dbCodePath = codePath;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        setRedisCodePath(codePath: string) {
 | 
				
			||||||
 | 
					            this.autoOpenResource.redisCodePath = codePath;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        setMongoCodePath(codePath: string) {
 | 
				
			||||||
 | 
					            this.autoOpenResource.mongoCodePath = codePath;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -1,137 +1,541 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="home-container">
 | 
					    <div class="home-container personal">
 | 
				
			||||||
        <el-row :gutter="15">
 | 
					        <el-row :gutter="15">
 | 
				
			||||||
            <el-col :sm="6" class="mb15">
 | 
					            <!-- 个人信息 -->
 | 
				
			||||||
                <div @click="toPage({ id: 'personal' })" class="home-card-item home-card-first">
 | 
					            <el-col :xs="24" :sm="16">
 | 
				
			||||||
                    <div class="flex-margin flex">
 | 
					                <el-card shadow="hover" header="个人信息">
 | 
				
			||||||
                        <img :src="userInfo.photo" />
 | 
					                    <div class="personal-user">
 | 
				
			||||||
                        <div class="home-card-first-right ml15">
 | 
					                        <div class="personal-user-left">
 | 
				
			||||||
                            <div class="flex-margin">
 | 
					                            <el-upload class="h100 personal-user-left-upload" action="" multiple :limit="1">
 | 
				
			||||||
                                <div class="home-card-first-right-title">{{ `${currentTime}, ${userInfo.username}` }}</div>
 | 
					                                <img :src="userInfo.photo" />
 | 
				
			||||||
                            </div>
 | 
					                            </el-upload>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div class="personal-user-right">
 | 
				
			||||||
 | 
					                            <el-row>
 | 
				
			||||||
 | 
					                                <el-col :span="24" class="personal-title mb18"
 | 
				
			||||||
 | 
					                                    >{{ currentTime }},{{ userInfo.name }},生活变的再糟糕,也不妨碍我变得更好!
 | 
				
			||||||
 | 
					                                </el-col>
 | 
				
			||||||
 | 
					                                <el-col :span="24">
 | 
				
			||||||
 | 
					                                    <el-row>
 | 
				
			||||||
 | 
					                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
				
			||||||
 | 
					                                            <div class="personal-item-label">用户名:</div>
 | 
				
			||||||
 | 
					                                            <div class="personal-item-value">{{ userInfo.username }}</div>
 | 
				
			||||||
 | 
					                                        </el-col>
 | 
				
			||||||
 | 
					                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
				
			||||||
 | 
					                                            <div class="personal-item-label">角色:</div>
 | 
				
			||||||
 | 
					                                            <div class="personal-item-value">{{ roleInfo }}</div>
 | 
				
			||||||
 | 
					                                        </el-col>
 | 
				
			||||||
 | 
					                                    </el-row>
 | 
				
			||||||
 | 
					                                </el-col>
 | 
				
			||||||
 | 
					                                <el-col :span="24">
 | 
				
			||||||
 | 
					                                    <el-row>
 | 
				
			||||||
 | 
					                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
				
			||||||
 | 
					                                            <div class="personal-item-label">上次登录IP:</div>
 | 
				
			||||||
 | 
					                                            <div class="personal-item-value">{{ userInfo.lastLoginIp }}</div>
 | 
				
			||||||
 | 
					                                        </el-col>
 | 
				
			||||||
 | 
					                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
				
			||||||
 | 
					                                            <div class="personal-item-label">上次登录时间:</div>
 | 
				
			||||||
 | 
					                                            <div class="personal-item-value">{{ dateFormat(userInfo.lastLoginTime) }}</div>
 | 
				
			||||||
 | 
					                                        </el-col>
 | 
				
			||||||
 | 
					                                    </el-row>
 | 
				
			||||||
 | 
					                                </el-col>
 | 
				
			||||||
 | 
					                            </el-row>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </el-card>
 | 
				
			||||||
            </el-col>
 | 
					            </el-col>
 | 
				
			||||||
            <el-col :sm="3" class="mb15" v-for="(v, k) in topCardItemList as any" :key="k">
 | 
					
 | 
				
			||||||
                <div @click="toPage(v)" class="home-card-item home-card-item-box" :style="{ background: v.color }">
 | 
					            <!-- 消息通知 -->
 | 
				
			||||||
                    <div class="home-card-item-flex">
 | 
					            <el-col :xs="24" :sm="8" class="pl15 personal-info">
 | 
				
			||||||
                        <div class="home-card-item-title pb3">{{ v.title }}</div>
 | 
					                <el-card shadow="hover">
 | 
				
			||||||
                        <div class="home-card-item-title-num pb6" :id="v.id"></div>
 | 
					                    <template #header>
 | 
				
			||||||
 | 
					                        <span>消息通知</span>
 | 
				
			||||||
 | 
					                        <span @click="showMsgs" class="personal-info-more">更多</span>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                    <div class="personal-info-box">
 | 
				
			||||||
 | 
					                        <ul class="personal-info-ul">
 | 
				
			||||||
 | 
					                            <li v-for="(v, k) in state.msgs as any" :key="k" class="personal-info-li">
 | 
				
			||||||
 | 
					                                <a class="personal-info-li-title">{{ `[${getMsgTypeDesc(v.type)}] ${v.msg}` }}</a>
 | 
				
			||||||
 | 
					                            </li>
 | 
				
			||||||
 | 
					                        </ul>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <i :class="v.icon" :style="{ color: v.iconColor }"></i>
 | 
					                </el-card>
 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </el-col>
 | 
					            </el-col>
 | 
				
			||||||
        </el-row>
 | 
					        </el-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-row :gutter="20" class="mt20 resource-info">
 | 
				
			||||||
 | 
					            <el-col :sm="12">
 | 
				
			||||||
 | 
					                <el-card shadow="hover">
 | 
				
			||||||
 | 
					                    <template #header>
 | 
				
			||||||
 | 
					                        <div class="pointer-icon" @click="toPage('machine')">
 | 
				
			||||||
 | 
					                            <div class="resource-num">
 | 
				
			||||||
 | 
					                                <SvgIcon
 | 
				
			||||||
 | 
					                                    class="mb5 mr5"
 | 
				
			||||||
 | 
					                                    :size="28"
 | 
				
			||||||
 | 
					                                    :name="TagResourceTypeEnum.Machine.extra.icon"
 | 
				
			||||||
 | 
					                                    :color="TagResourceTypeEnum.Machine.extra.iconColor"
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                                <span>{{ state.machine.num }}</span>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                    <el-row>
 | 
				
			||||||
 | 
					                        <el-col :sm="24">
 | 
				
			||||||
 | 
					                            <el-table :data="state.machine.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
 | 
				
			||||||
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip width="135">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        {{ dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column prop="codePath" min-width="400" show-overflow-tooltip>
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <TagCodePath :path="scope.row.codePath" />
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column width="30">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <el-link @click="toPage('machine', scope.row.codePath)" type="primary" icon="Position"></el-link>
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                            </el-table>
 | 
				
			||||||
 | 
					                        </el-col>
 | 
				
			||||||
 | 
					                    </el-row>
 | 
				
			||||||
 | 
					                </el-card>
 | 
				
			||||||
 | 
					            </el-col>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-col :sm="12">
 | 
				
			||||||
 | 
					                <el-card shadow="hover">
 | 
				
			||||||
 | 
					                    <template #header>
 | 
				
			||||||
 | 
					                        <div class="pointer-icon" @click="toPage('db')">
 | 
				
			||||||
 | 
					                            <div class="resource-num">
 | 
				
			||||||
 | 
					                                <SvgIcon class="mb5 mr5" :size="28" :name="TagResourceTypeEnum.Db.extra.icon" :color="TagResourceTypeEnum.Db.extra.iconColor" />
 | 
				
			||||||
 | 
					                                <span>{{ state.db.num }}</span>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                    <el-row>
 | 
				
			||||||
 | 
					                        <el-col :sm="24">
 | 
				
			||||||
 | 
					                            <el-table :data="state.db.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
 | 
				
			||||||
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        {{ dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <TagCodePath :path="scope.row.codePath" />
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column width="30">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <el-link @click="toPage('db', scope.row.codePath)" type="primary" icon="Position"></el-link>
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                            </el-table>
 | 
				
			||||||
 | 
					                        </el-col>
 | 
				
			||||||
 | 
					                    </el-row>
 | 
				
			||||||
 | 
					                </el-card>
 | 
				
			||||||
 | 
					            </el-col>
 | 
				
			||||||
 | 
					        </el-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-row :gutter="20" class="mt20 resource-info">
 | 
				
			||||||
 | 
					            <el-col :sm="12">
 | 
				
			||||||
 | 
					                <el-card shadow="hover">
 | 
				
			||||||
 | 
					                    <template #header>
 | 
				
			||||||
 | 
					                        <div class="pointer-icon" @click="toPage('redis')">
 | 
				
			||||||
 | 
					                            <div class="resource-num">
 | 
				
			||||||
 | 
					                                <SvgIcon
 | 
				
			||||||
 | 
					                                    class="mb5 mr5"
 | 
				
			||||||
 | 
					                                    :size="28"
 | 
				
			||||||
 | 
					                                    :name="TagResourceTypeEnum.Redis.extra.icon"
 | 
				
			||||||
 | 
					                                    :color="TagResourceTypeEnum.Redis.extra.iconColor"
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                                <span>{{ state.redis.num }}</span>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                    <el-row>
 | 
				
			||||||
 | 
					                        <el-col :sm="24">
 | 
				
			||||||
 | 
					                            <el-table :data="state.redis.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
 | 
				
			||||||
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        {{ dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <TagCodePath :path="scope.row.codePath" />
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column width="30">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <el-link @click="toPage('redis', scope.row.codePath)" type="primary" icon="Position"></el-link>
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                            </el-table>
 | 
				
			||||||
 | 
					                        </el-col>
 | 
				
			||||||
 | 
					                    </el-row>
 | 
				
			||||||
 | 
					                </el-card>
 | 
				
			||||||
 | 
					            </el-col>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-col :sm="12">
 | 
				
			||||||
 | 
					                <el-card shadow="hover">
 | 
				
			||||||
 | 
					                    <template #header>
 | 
				
			||||||
 | 
					                        <div class="pointer-icon" @click="toPage('mongo')">
 | 
				
			||||||
 | 
					                            <div class="resource-num">
 | 
				
			||||||
 | 
					                                <SvgIcon
 | 
				
			||||||
 | 
					                                    class="mb5 mr5"
 | 
				
			||||||
 | 
					                                    :size="28"
 | 
				
			||||||
 | 
					                                    :name="TagResourceTypeEnum.Mongo.extra.icon"
 | 
				
			||||||
 | 
					                                    :color="TagResourceTypeEnum.Mongo.extra.iconColor"
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                                <span>{{ state.mongo.num }}</span>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                    <el-row>
 | 
				
			||||||
 | 
					                        <el-col :sm="24">
 | 
				
			||||||
 | 
					                            <el-table :data="state.mongo.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
 | 
				
			||||||
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        {{ dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <TagCodePath :path="scope.row.codePath" />
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                                <el-table-column width="30">
 | 
				
			||||||
 | 
					                                    <template #default="scope">
 | 
				
			||||||
 | 
					                                        <el-link @click="toPage('mongo', scope.row.codePath)" type="primary" icon="Position"></el-link>
 | 
				
			||||||
 | 
					                                    </template>
 | 
				
			||||||
 | 
					                                </el-table-column>
 | 
				
			||||||
 | 
					                            </el-table>
 | 
				
			||||||
 | 
					                        </el-col>
 | 
				
			||||||
 | 
					                    </el-row>
 | 
				
			||||||
 | 
					                </el-card>
 | 
				
			||||||
 | 
					            </el-col>
 | 
				
			||||||
 | 
					        </el-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog width="900px" title="消息" v-model="msgDialog.visible">
 | 
				
			||||||
 | 
					            <el-table border :data="msgDialog.msgs.list" size="small">
 | 
				
			||||||
 | 
					                <el-table-column property="type" label="类型" width="60">
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        {{ getMsgTypeDesc(scope.row.type) }}
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="msg" label="消息"></el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="createTime" label="时间" width="150">
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        {{ dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					            </el-table>
 | 
				
			||||||
 | 
					            <el-row type="flex" class="mt5" justify="center">
 | 
				
			||||||
 | 
					                <el-pagination
 | 
				
			||||||
 | 
					                    small
 | 
				
			||||||
 | 
					                    @current-change="searchMsg"
 | 
				
			||||||
 | 
					                    style="text-align: center"
 | 
				
			||||||
 | 
					                    background
 | 
				
			||||||
 | 
					                    layout="prev, pager, next, total, jumper"
 | 
				
			||||||
 | 
					                    :total="msgDialog.msgs.total"
 | 
				
			||||||
 | 
					                    v-model:current-page="msgDialog.query.pageNum"
 | 
				
			||||||
 | 
					                    :page-size="msgDialog.query.pageSize"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </el-row>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { toRefs, reactive, onMounted, nextTick, computed } from 'vue';
 | 
					import { toRefs, reactive, onMounted, computed } from 'vue';
 | 
				
			||||||
// import * as echarts from 'echarts';
 | 
					// import * as echarts from 'echarts';
 | 
				
			||||||
import { CountUp } from 'countup.js';
 | 
					 | 
				
			||||||
import { formatAxis } from '@/common/utils/format';
 | 
					import { formatAxis } from '@/common/utils/format';
 | 
				
			||||||
import { indexApi } from './api';
 | 
					import { indexApi } from './api';
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from 'vue-router';
 | 
				
			||||||
import { storeToRefs } from 'pinia';
 | 
					import { storeToRefs } from 'pinia';
 | 
				
			||||||
import { useUserInfo } from '@/store/userInfo';
 | 
					import { useUserInfo } from '@/store/userInfo';
 | 
				
			||||||
 | 
					import { personApi } from '../personal/api';
 | 
				
			||||||
 | 
					import { dateFormat } from '@/common/utils/date';
 | 
				
			||||||
 | 
					import SvgIcon from '@/components/svgIcon/index.vue';
 | 
				
			||||||
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
 | 
					import { resourceOpLogApi } from '../ops/tag/api';
 | 
				
			||||||
 | 
					import TagCodePath from '../ops/component/TagCodePath.vue';
 | 
				
			||||||
 | 
					import { useAutoOpenResource } from '@/store/autoOpenResource';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = useRouter();
 | 
					const router = useRouter();
 | 
				
			||||||
const { userInfo } = storeToRefs(useUserInfo());
 | 
					const { userInfo } = storeToRefs(useUserInfo());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
    topCardItemList: [
 | 
					    accountInfo: {
 | 
				
			||||||
        {
 | 
					        roles: [],
 | 
				
			||||||
            title: 'Linux机器',
 | 
					    },
 | 
				
			||||||
            id: 'machineNum',
 | 
					    msgs: [],
 | 
				
			||||||
            color: '#F95959',
 | 
					    msgDialog: {
 | 
				
			||||||
 | 
					        visible: false,
 | 
				
			||||||
 | 
					        query: {
 | 
				
			||||||
 | 
					            pageSize: 10,
 | 
				
			||||||
 | 
					            pageNum: 1,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        msgs: {
 | 
				
			||||||
            title: '数据库',
 | 
					            list: [],
 | 
				
			||||||
            id: 'dbNum',
 | 
					            total: null,
 | 
				
			||||||
            color: '#8595F4',
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					    },
 | 
				
			||||||
            title: 'redis',
 | 
					    resourceOpTableHeight: 180,
 | 
				
			||||||
            id: 'redisNum',
 | 
					    defaultLogSize: 5,
 | 
				
			||||||
            color: '#1abc9c',
 | 
					    machine: {
 | 
				
			||||||
        },
 | 
					        num: 0,
 | 
				
			||||||
        {
 | 
					        opLogs: [],
 | 
				
			||||||
            title: 'Mongo',
 | 
					    },
 | 
				
			||||||
            id: 'mongoNum',
 | 
					    db: {
 | 
				
			||||||
            color: '#FEBB50',
 | 
					        num: 0,
 | 
				
			||||||
        },
 | 
					        opLogs: [],
 | 
				
			||||||
    ],
 | 
					    },
 | 
				
			||||||
 | 
					    redis: {
 | 
				
			||||||
 | 
					        num: 0,
 | 
				
			||||||
 | 
					        opLogs: [],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mongo: {
 | 
				
			||||||
 | 
					        num: 0,
 | 
				
			||||||
 | 
					        opLogs: [],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { topCardItemList } = toRefs(state);
 | 
					const { msgDialog } = toRefs(state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const roleInfo = computed(() => {
 | 
				
			||||||
 | 
					    if (state.accountInfo.roles.length == 0) {
 | 
				
			||||||
 | 
					        return '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return state.accountInfo.roles.map((val: any) => val.roleName).join('、');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 当前时间提示语
 | 
					// 当前时间提示语
 | 
				
			||||||
const currentTime = computed(() => {
 | 
					const currentTime = computed(() => {
 | 
				
			||||||
    return formatAxis(new Date());
 | 
					    return formatAxis(new Date());
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 页面加载时
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					    initData();
 | 
				
			||||||
 | 
					    getAccountInfo();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getMsgs().then((res) => {
 | 
				
			||||||
 | 
					        state.msgs = res.list;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const showMsgs = async () => {
 | 
				
			||||||
 | 
					    state.msgDialog.query.pageNum = 1;
 | 
				
			||||||
 | 
					    searchMsg();
 | 
				
			||||||
 | 
					    state.msgDialog.visible = true;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const searchMsg = async () => {
 | 
				
			||||||
 | 
					    state.msgDialog.msgs = await getMsgs();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getMsgTypeDesc = (type: number) => {
 | 
				
			||||||
 | 
					    if (type == 1) {
 | 
				
			||||||
 | 
					        return '登录';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (type == 2) {
 | 
				
			||||||
 | 
					        return '通知';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getAccountInfo = async () => {
 | 
				
			||||||
 | 
					    state.accountInfo = await personApi.accountInfo.request();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getMsgs = async () => {
 | 
				
			||||||
 | 
					    return await personApi.getMsgs.request(state.msgDialog.query);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 初始化数字滚动
 | 
					// 初始化数字滚动
 | 
				
			||||||
const initNumCountUp = async () => {
 | 
					const initData = async () => {
 | 
				
			||||||
    indexApi.machineDashbord.request().then((res: any) => {
 | 
					    resourceOpLogApi.getAccountResourceOpLogs
 | 
				
			||||||
        nextTick(() => {
 | 
					        .request({ resourceType: TagResourceTypeEnum.MachineAuthCert.value, pageSize: state.defaultLogSize })
 | 
				
			||||||
            new CountUp('machineNum', res.machineNum).start();
 | 
					        .then((res: any) => {
 | 
				
			||||||
 | 
					            state.machine.opLogs = res.list;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    resourceOpLogApi.getAccountResourceOpLogs.request({ resourceType: TagResourceTypeEnum.DbName.value, pageSize: state.defaultLogSize }).then((res: any) => {
 | 
				
			||||||
 | 
					        state.db.opLogs = res.list;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    resourceOpLogApi.getAccountResourceOpLogs.request({ resourceType: TagResourceTypeEnum.Redis.value, pageSize: state.defaultLogSize }).then((res: any) => {
 | 
				
			||||||
 | 
					        state.redis.opLogs = res.list;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    resourceOpLogApi.getAccountResourceOpLogs.request({ resourceType: TagResourceTypeEnum.Mongo.value, pageSize: state.defaultLogSize }).then((res: any) => {
 | 
				
			||||||
 | 
					        state.mongo.opLogs = res.list;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    indexApi.machineDashbord.request().then((res: any) => {
 | 
				
			||||||
 | 
					        state.machine.num = res.machineNum;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    indexApi.dbDashbord.request().then((res: any) => {
 | 
					    indexApi.dbDashbord.request().then((res: any) => {
 | 
				
			||||||
        nextTick(() => {
 | 
					        state.db.num = res.dbNum;
 | 
				
			||||||
            new CountUp('dbNum', res.dbNum).start();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    indexApi.redisDashbord.request().then((res: any) => {
 | 
					    indexApi.redisDashbord.request().then((res: any) => {
 | 
				
			||||||
        nextTick(() => {
 | 
					        state.redis.num = res.redisNum;
 | 
				
			||||||
            new CountUp('redisNum', res.redisNum).start();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    indexApi.mongoDashbord.request().then((res: any) => {
 | 
					    indexApi.mongoDashbord.request().then((res: any) => {
 | 
				
			||||||
        nextTick(() => {
 | 
					        state.mongo.num = res.mongoNum;
 | 
				
			||||||
            new CountUp('mongoNum', res.mongoNum).start();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const toPage = (item: any) => {
 | 
					const toPage = (item: any, codePath = '') => {
 | 
				
			||||||
    switch (item.id) {
 | 
					    let path;
 | 
				
			||||||
 | 
					    switch (item) {
 | 
				
			||||||
        case 'personal': {
 | 
					        case 'personal': {
 | 
				
			||||||
            router.push('/personal');
 | 
					            router.push('/personal');
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case 'mongoNum': {
 | 
					        case 'mongo': {
 | 
				
			||||||
            router.push('/mongo/mongo-data-operation');
 | 
					            useAutoOpenResource().setMongoCodePath(codePath);
 | 
				
			||||||
 | 
					            path = '/mongo/mongo-data-operation';
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case 'machineNum': {
 | 
					        case 'machine': {
 | 
				
			||||||
            router.push('/machine/machines-op');
 | 
					            useAutoOpenResource().setMachineCodePath(codePath);
 | 
				
			||||||
 | 
					            path = '/machine/machines-op';
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case 'dbNum': {
 | 
					        case 'db': {
 | 
				
			||||||
            router.push('/dbms/sql-exec');
 | 
					            useAutoOpenResource().setDbCodePath(codePath);
 | 
				
			||||||
 | 
					            path = '/dbms/sql-exec';
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case 'redisNum': {
 | 
					        case 'redis': {
 | 
				
			||||||
            router.push('/redis/data-operation');
 | 
					            useAutoOpenResource().setRedisCodePath(codePath);
 | 
				
			||||||
 | 
					            path = '/redis/data-operation';
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 页面加载时
 | 
					    router.push({ path });
 | 
				
			||||||
onMounted(() => {
 | 
					};
 | 
				
			||||||
    initNumCountUp();
 | 
					 | 
				
			||||||
    // initHomeLaboratory();
 | 
					 | 
				
			||||||
    // initHomeOvertime();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
 | 
					@import '@/theme/mixins/index.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.personal {
 | 
				
			||||||
 | 
					    .personal-user {
 | 
				
			||||||
 | 
					        height: 130px;
 | 
				
			||||||
 | 
					        display: flex;
 | 
				
			||||||
 | 
					        align-items: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .personal-user-left {
 | 
				
			||||||
 | 
					            width: 100px;
 | 
				
			||||||
 | 
					            height: 130px;
 | 
				
			||||||
 | 
					            border-radius: 3px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ::v-deep(.el-upload) {
 | 
				
			||||||
 | 
					                height: 100%;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .personal-user-left-upload {
 | 
				
			||||||
 | 
					                img {
 | 
				
			||||||
 | 
					                    width: 100%;
 | 
				
			||||||
 | 
					                    height: 100%;
 | 
				
			||||||
 | 
					                    border-radius: 3px;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                &:hover {
 | 
				
			||||||
 | 
					                    img {
 | 
				
			||||||
 | 
					                        animation: logoAnimation 0.3s ease-in-out;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .personal-user-right {
 | 
				
			||||||
 | 
					            flex: 1;
 | 
				
			||||||
 | 
					            padding: 0 15px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .personal-title {
 | 
				
			||||||
 | 
					                font-size: 18px;
 | 
				
			||||||
 | 
					                @include text-ellipsis(1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .personal-item {
 | 
				
			||||||
 | 
					                display: flex;
 | 
				
			||||||
 | 
					                align-items: center;
 | 
				
			||||||
 | 
					                font-size: 13px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                .personal-item-label {
 | 
				
			||||||
 | 
					                    color: gray;
 | 
				
			||||||
 | 
					                    @include text-ellipsis(1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                .personal-item-value {
 | 
				
			||||||
 | 
					                    @include text-ellipsis(1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .personal-info {
 | 
				
			||||||
 | 
					        .personal-info-more {
 | 
				
			||||||
 | 
					            float: right;
 | 
				
			||||||
 | 
					            color: gray;
 | 
				
			||||||
 | 
					            font-size: 13px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            &:hover {
 | 
				
			||||||
 | 
					                color: var(--el-color-primary);
 | 
				
			||||||
 | 
					                cursor: pointer;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .personal-info-box {
 | 
				
			||||||
 | 
					            height: 130px;
 | 
				
			||||||
 | 
					            overflow: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .personal-info-ul {
 | 
				
			||||||
 | 
					                list-style: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                .personal-info-li {
 | 
				
			||||||
 | 
					                    font-size: 13px;
 | 
				
			||||||
 | 
					                    padding-bottom: 10px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    .personal-info-li-title {
 | 
				
			||||||
 | 
					                        display: inline-block;
 | 
				
			||||||
 | 
					                        @include text-ellipsis(1);
 | 
				
			||||||
 | 
					                        color: grey;
 | 
				
			||||||
 | 
					                        text-decoration: none;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    & a:hover {
 | 
				
			||||||
 | 
					                        color: var(--el-color-primary);
 | 
				
			||||||
 | 
					                        cursor: pointer;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.resource-info {
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ::v-deep(.el-card__header) {
 | 
				
			||||||
 | 
					        padding: 2px 20px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .resource-num {
 | 
				
			||||||
 | 
					        font-weight: 700;
 | 
				
			||||||
 | 
					        font-size: 2vw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.home-container {
 | 
					.home-container {
 | 
				
			||||||
    overflow-x: hidden;
 | 
					    overflow-x: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -182,7 +586,7 @@ onMounted(() => {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            .home-card-item-title-num {
 | 
					            .home-card-item-title-num {
 | 
				
			||||||
                font-size: 18px;
 | 
					                font-size: 2vw;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            .home-card-item-tip-num {
 | 
					            .home-card-item-tip-num {
 | 
				
			||||||
@@ -190,124 +594,5 @@ onMounted(() => {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    .home-card-first {
 | 
					 | 
				
			||||||
        background: var(--bg-main-color);
 | 
					 | 
				
			||||||
        border: 1px solid var(--el-border-color-light, #ebeef5);
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        img {
 | 
					 | 
				
			||||||
            width: 60px;
 | 
					 | 
				
			||||||
            height: 60px;
 | 
					 | 
				
			||||||
            border-radius: 100%;
 | 
					 | 
				
			||||||
            border: 2px solid var(--el-color-primary-light-5);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .home-card-first-right {
 | 
					 | 
				
			||||||
            flex: 1;
 | 
					 | 
				
			||||||
            display: flex;
 | 
					 | 
				
			||||||
            flex-direction: column;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .home-card-first-right-msg {
 | 
					 | 
				
			||||||
                font-size: 13px;
 | 
					 | 
				
			||||||
                color: gray;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .home-monitor {
 | 
					 | 
				
			||||||
        height: 200px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .flex-warp-item {
 | 
					 | 
				
			||||||
            width: 50%;
 | 
					 | 
				
			||||||
            height: 100px;
 | 
					 | 
				
			||||||
            display: flex;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .flex-warp-item-box {
 | 
					 | 
				
			||||||
                margin: auto;
 | 
					 | 
				
			||||||
                height: auto;
 | 
					 | 
				
			||||||
                text-align: center;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .home-warning-card {
 | 
					 | 
				
			||||||
        height: 292px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ::v-deep(.el-card) {
 | 
					 | 
				
			||||||
            height: 100%;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .home-dynamic {
 | 
					 | 
				
			||||||
        height: 200px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .home-dynamic-item {
 | 
					 | 
				
			||||||
            display: flex;
 | 
					 | 
				
			||||||
            width: 100%;
 | 
					 | 
				
			||||||
            height: 60px;
 | 
					 | 
				
			||||||
            overflow: hidden;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            &:first-of-type {
 | 
					 | 
				
			||||||
                .home-dynamic-item-line {
 | 
					 | 
				
			||||||
                    i {
 | 
					 | 
				
			||||||
                        color: orange !important;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .home-dynamic-item-left {
 | 
					 | 
				
			||||||
                text-align: right;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .home-dynamic-item-left-time1 {
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .home-dynamic-item-left-time2 {
 | 
					 | 
				
			||||||
                    font-size: 13px;
 | 
					 | 
				
			||||||
                    color: gray;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .home-dynamic-item-line {
 | 
					 | 
				
			||||||
                height: 60px;
 | 
					 | 
				
			||||||
                border-right: 2px dashed #dfdfdf;
 | 
					 | 
				
			||||||
                margin: 0 20px;
 | 
					 | 
				
			||||||
                position: relative;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                i {
 | 
					 | 
				
			||||||
                    color: var(--el-color-primary);
 | 
					 | 
				
			||||||
                    font-size: 12px;
 | 
					 | 
				
			||||||
                    position: absolute;
 | 
					 | 
				
			||||||
                    top: 1px;
 | 
					 | 
				
			||||||
                    left: -6px;
 | 
					 | 
				
			||||||
                    transform: rotate(46deg);
 | 
					 | 
				
			||||||
                    background: white;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .home-dynamic-item-right {
 | 
					 | 
				
			||||||
                flex: 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .home-dynamic-item-right-title {
 | 
					 | 
				
			||||||
                    i {
 | 
					 | 
				
			||||||
                        margin-right: 5px;
 | 
					 | 
				
			||||||
                        border: 1px solid #dfdfdf;
 | 
					 | 
				
			||||||
                        width: 20px;
 | 
					 | 
				
			||||||
                        height: 20px;
 | 
					 | 
				
			||||||
                        border-radius: 100%;
 | 
					 | 
				
			||||||
                        padding: 3px 2px 2px;
 | 
					 | 
				
			||||||
                        text-align: center;
 | 
					 | 
				
			||||||
                        color: var(--el-color-primary);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .home-dynamic-item-right-label {
 | 
					 | 
				
			||||||
                    font-size: 13px;
 | 
					 | 
				
			||||||
                    color: gray;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -191,12 +191,13 @@ const showResourceEdit = computed(() => {
 | 
				
			|||||||
    return state.form.type != AuthCertTypeEnum.Public.value && !props.resourceEdit;
 | 
					    return state.form.type != AuthCertTypeEnum.Public.value && !props.resourceEdit;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(
 | 
					watch(dialogVisible, (val: any) => {
 | 
				
			||||||
    () => props.authCert,
 | 
					    if (val) {
 | 
				
			||||||
    (val: any) => {
 | 
					        setForm(props.authCert);
 | 
				
			||||||
        setForm(val);
 | 
					    } else {
 | 
				
			||||||
 | 
					        cancelEdit();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
);
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const setForm = (val: any) => {
 | 
					const setForm = (val: any) => {
 | 
				
			||||||
    val = { ...val };
 | 
					    val = { ...val };
 | 
				
			||||||
@@ -246,10 +247,11 @@ const getCiphertext = async () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const cancelEdit = () => {
 | 
					const cancelEdit = () => {
 | 
				
			||||||
    dialogVisible.value = false;
 | 
					    dialogVisible.value = false;
 | 
				
			||||||
    emit('cancel');
 | 
					
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
        acForm.value?.resetFields();
 | 
					 | 
				
			||||||
        state.form = { ...DefaultForm };
 | 
					        state.form = { ...DefaultForm };
 | 
				
			||||||
 | 
					        acForm.value?.resetFields();
 | 
				
			||||||
 | 
					        emit('cancel');
 | 
				
			||||||
    }, 300);
 | 
					    }, 300);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,9 +113,6 @@ const deleteRow = (idx: any) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const cancelEdit = () => {
 | 
					const cancelEdit = () => {
 | 
				
			||||||
    state.dvisible = false;
 | 
					    state.dvisible = false;
 | 
				
			||||||
    setTimeout(() => {
 | 
					 | 
				
			||||||
        state.form = {};
 | 
					 | 
				
			||||||
    }, 300);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const btnOk = async (authCert: any) => {
 | 
					const btnOk = async (authCert: any) => {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										87
									
								
								mayfly_go_web/src/views/ops/component/TagCodePath.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								mayfly_go_web/src/views/ops/component/TagCodePath.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div v-if="paths">
 | 
				
			||||||
 | 
					        <el-row v-for="(path, idx) in paths?.slice(0, 1)" :key="idx">
 | 
				
			||||||
 | 
					            <span v-for="item in parseTagPath(path)" :key="item.code">
 | 
				
			||||||
 | 
					                <SvgIcon
 | 
				
			||||||
 | 
					                    :name="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.icon"
 | 
				
			||||||
 | 
					                    :color="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.iconColor"
 | 
				
			||||||
 | 
					                    class="mr2"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <span> {{ item.code }}</span>
 | 
				
			||||||
 | 
					                <SvgIcon v-if="!item.isEnd" class="mr5 ml5" name="arrow-right" />
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 展示剩余的标签信息 -->
 | 
				
			||||||
 | 
					            <el-popover :show-after="300" v-if="paths.length > 1 && idx == 0" placement="bottom" width="500" trigger="hover">
 | 
				
			||||||
 | 
					                <template #reference>
 | 
				
			||||||
 | 
					                    <SvgIcon class="mt5 ml5" color="var(--el-color-primary)" name="MoreFilled" />
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-row v-for="i in paths.slice(1)" :key="i">
 | 
				
			||||||
 | 
					                    <span v-for="item in parseTagPath(i)" :key="item.code">
 | 
				
			||||||
 | 
					                        <SvgIcon
 | 
				
			||||||
 | 
					                            :name="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.icon"
 | 
				
			||||||
 | 
					                            :color="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.iconColor"
 | 
				
			||||||
 | 
					                            class="mr2"
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                        <span> {{ item.code }}</span>
 | 
				
			||||||
 | 
					                        <SvgIcon v-if="!item.isEnd" class="mr5 ml5" name="arrow-right" />
 | 
				
			||||||
 | 
					                    </span>
 | 
				
			||||||
 | 
					                </el-row>
 | 
				
			||||||
 | 
					            </el-popover>
 | 
				
			||||||
 | 
					        </el-row>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
 | 
					import EnumValue from '@/common/Enum';
 | 
				
			||||||
 | 
					import { computed } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps({
 | 
				
			||||||
 | 
					    path: {
 | 
				
			||||||
 | 
					        type: [String, Array<string>],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const paths = computed(() => {
 | 
				
			||||||
 | 
					    if (Array.isArray(props.path)) {
 | 
				
			||||||
 | 
					        return props.path;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return [props.path];
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const parseTagPath = (tagPath: string = '') => {
 | 
				
			||||||
 | 
					    if (!tagPath) {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const res = [] as any;
 | 
				
			||||||
 | 
					    const codes = tagPath.split('/');
 | 
				
			||||||
 | 
					    for (let code of codes) {
 | 
				
			||||||
 | 
					        const typeAndCode = code.split('|');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (typeAndCode.length == 1) {
 | 
				
			||||||
 | 
					            const tagCode = typeAndCode[0];
 | 
				
			||||||
 | 
					            if (!tagCode) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            res.push({
 | 
				
			||||||
 | 
					                type: TagResourceTypeEnum.Tag.value,
 | 
				
			||||||
 | 
					                code: typeAndCode[0],
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res.push({
 | 
				
			||||||
 | 
					            type: typeAndCode[0],
 | 
				
			||||||
 | 
					            code: typeAndCode[1],
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res[res.length - 1].isEnd = true;
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss"></style>
 | 
				
			||||||
@@ -203,8 +203,14 @@ const getNode = (nodeKey: any) => {
 | 
				
			|||||||
    return node;
 | 
					    return node;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const setCurrentKey = (nodeKey: any) => {
 | 
				
			||||||
 | 
					    treeRef.value.setCurrentKey(nodeKey);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({
 | 
					defineExpose({
 | 
				
			||||||
    reloadNode,
 | 
					    reloadNode,
 | 
				
			||||||
 | 
					    getNode,
 | 
				
			||||||
 | 
					    setCurrentKey,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										153
									
								
								mayfly_go_web/src/views/ops/component/TagTreeCheck.vue
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										153
									
								
								mayfly_go_web/src/views/ops/component/TagTreeCheck.vue
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,153 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div class="w100" style="border: 1px solid var(--el-border-color)">
 | 
				
			||||||
 | 
					        <el-input v-model="filterTag" clearable placeholder="输入关键字过滤" size="small" />
 | 
				
			||||||
 | 
					        <el-scrollbar :style="{ height: props.height }">
 | 
				
			||||||
 | 
					            <el-tree
 | 
				
			||||||
 | 
					                v-bind="$attrs"
 | 
				
			||||||
 | 
					                ref="tagTreeRef"
 | 
				
			||||||
 | 
					                style="width: 100%"
 | 
				
			||||||
 | 
					                :data="state.tags"
 | 
				
			||||||
 | 
					                :default-expanded-keys="checkedTags"
 | 
				
			||||||
 | 
					                :default-checked-keys="checkedTags"
 | 
				
			||||||
 | 
					                multiple
 | 
				
			||||||
 | 
					                :render-after-expand="true"
 | 
				
			||||||
 | 
					                show-checkbox
 | 
				
			||||||
 | 
					                check-strictly
 | 
				
			||||||
 | 
					                :node-key="$props.nodeKey"
 | 
				
			||||||
 | 
					                :props="{
 | 
				
			||||||
 | 
					                    value: $props.nodeKey,
 | 
				
			||||||
 | 
					                    label: 'codePath',
 | 
				
			||||||
 | 
					                    children: 'children',
 | 
				
			||||||
 | 
					                    disabled: 'disabled',
 | 
				
			||||||
 | 
					                }"
 | 
				
			||||||
 | 
					                @check="tagTreeNodeCheck"
 | 
				
			||||||
 | 
					                :filter-node-method="filterNode"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <template #default="{ data }">
 | 
				
			||||||
 | 
					                    <span class="custom-tree-node">
 | 
				
			||||||
 | 
					                        <SvgIcon
 | 
				
			||||||
 | 
					                            :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon"
 | 
				
			||||||
 | 
					                            :color="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.iconColor"
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <span class="font13 ml5">
 | 
				
			||||||
 | 
					                            {{ data.code }}
 | 
				
			||||||
 | 
					                            <span style="color: #3c8dbc">【</span>
 | 
				
			||||||
 | 
					                            {{ data.name }}
 | 
				
			||||||
 | 
					                            <span style="color: #3c8dbc">】</span>
 | 
				
			||||||
 | 
					                            <el-tag v-if="data.children !== null" size="small">{{ data.children.length }} </el-tag>
 | 
				
			||||||
 | 
					                        </span>
 | 
				
			||||||
 | 
					                    </span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-tree>
 | 
				
			||||||
 | 
					        </el-scrollbar>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { ref, reactive, onMounted, watch } from 'vue';
 | 
				
			||||||
 | 
					import { tagApi } from '../tag/api';
 | 
				
			||||||
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
 | 
					import EnumValue from '@/common/Enum';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps({
 | 
				
			||||||
 | 
					    height: {
 | 
				
			||||||
 | 
					        type: [String, Number],
 | 
				
			||||||
 | 
					        default: 'calc(100vh - 330px)',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    tagType: {
 | 
				
			||||||
 | 
					        type: Number,
 | 
				
			||||||
 | 
					        default: TagResourceTypeEnum.Tag.value,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    nodeKey: {
 | 
				
			||||||
 | 
					        type: String,
 | 
				
			||||||
 | 
					        default: 'codePath',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const checkedTags = defineModel<Array<any>>('modelValue', {
 | 
				
			||||||
 | 
					    default: () => [],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tagTreeRef: any = ref(null);
 | 
				
			||||||
 | 
					const filterTag = ref('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const state = reactive({
 | 
				
			||||||
 | 
					    tags: [],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					    search();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const search = async () => {
 | 
				
			||||||
 | 
					    state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					        const checkedNodes = tagTreeRef.value.getCheckedNodes();
 | 
				
			||||||
 | 
					        console.log('check nodes: ', checkedNodes);
 | 
				
			||||||
 | 
					        // 禁用选中节点的所有父节点,不可选中
 | 
				
			||||||
 | 
					        for (let checkNodeData of checkedNodes) {
 | 
				
			||||||
 | 
					            disableParentNodes(tagTreeRef.value.getNode(checkNodeData.codePath).parent);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }, 200);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(filterTag, (val) => {
 | 
				
			||||||
 | 
					    tagTreeRef.value!.filter(val);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const filterNode = (value: string, data: any) => {
 | 
				
			||||||
 | 
					    if (!value) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return data.codePath.toLowerCase().includes(value) || data.name.includes(value);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tagTreeNodeCheck = (data: any) => {
 | 
				
			||||||
 | 
					    const node = tagTreeRef.value.getNode(data.codePath);
 | 
				
			||||||
 | 
					    console.log('check node: ', node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (node.checked) {
 | 
				
			||||||
 | 
					        // 如果选中了子节点,则需要将父节点全部取消选中,并禁用父节点
 | 
				
			||||||
 | 
					        unCheckParentNodes(node.parent);
 | 
				
			||||||
 | 
					        disableParentNodes(node.parent);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // 如果取消了选中,则需要根据条件恢复父节点的选中状态
 | 
				
			||||||
 | 
					        disableParentNodes(node.parent, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新绑定的值
 | 
				
			||||||
 | 
					    checkedTags.value = tagTreeRef.value.getCheckedKeys(false);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unCheckParentNodes = (node: any) => {
 | 
				
			||||||
 | 
					    if (!node) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    tagTreeRef.value.setChecked(node, false, false);
 | 
				
			||||||
 | 
					    unCheckParentNodes(node.parent);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 禁用该节点以及所有父节点
 | 
				
			||||||
 | 
					 * @param node 节点
 | 
				
			||||||
 | 
					 * @param disable 是否禁用
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const disableParentNodes = (node: any, disable = true) => {
 | 
				
			||||||
 | 
					    if (!node) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!disable) {
 | 
				
			||||||
 | 
					        // 恢复为非禁用状态时,若同层级存在一个选中状态或者禁用状态,则继续禁用 不恢复非禁用状态。
 | 
				
			||||||
 | 
					        for (let oneLevelNodes of node.childNodes) {
 | 
				
			||||||
 | 
					            if (oneLevelNodes.checked || oneLevelNodes.data.disabled) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    node.data.disabled = disable;
 | 
				
			||||||
 | 
					    disableParentNodes(node.parent, disable);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss" scoped></style>
 | 
				
			||||||
@@ -6,11 +6,9 @@
 | 
				
			|||||||
            @change="changeTag"
 | 
					            @change="changeTag"
 | 
				
			||||||
            :data="tags"
 | 
					            :data="tags"
 | 
				
			||||||
            placeholder="请选择关联标签"
 | 
					            placeholder="请选择关联标签"
 | 
				
			||||||
            :render-after-expand="true"
 | 
					            :default-expanded-keys="defaultExpandedKeys"
 | 
				
			||||||
            :default-expanded-keys="[state.selectTags]"
 | 
					 | 
				
			||||||
            show-checkbox
 | 
					            show-checkbox
 | 
				
			||||||
            node-key="codePath"
 | 
					            node-key="codePath"
 | 
				
			||||||
            :check-strictly="props.checkStrictly"
 | 
					 | 
				
			||||||
            :props="{
 | 
					            :props="{
 | 
				
			||||||
                value: 'codePath',
 | 
					                value: 'codePath',
 | 
				
			||||||
                label: 'codePath',
 | 
					                label: 'codePath',
 | 
				
			||||||
@@ -19,6 +17,7 @@
 | 
				
			|||||||
        >
 | 
					        >
 | 
				
			||||||
            <template #default="{ data }">
 | 
					            <template #default="{ data }">
 | 
				
			||||||
                <span class="custom-tree-node">
 | 
					                <span class="custom-tree-node">
 | 
				
			||||||
 | 
					                    <SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" class="mr2" />
 | 
				
			||||||
                    <span style="font-size: 13px">
 | 
					                    <span style="font-size: 13px">
 | 
				
			||||||
                        {{ data.code }}
 | 
					                        {{ data.code }}
 | 
				
			||||||
                        <span style="color: #3c8dbc">【</span>
 | 
					                        <span style="color: #3c8dbc">【</span>
 | 
				
			||||||
@@ -33,25 +32,22 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { toRefs, reactive, onMounted } from 'vue';
 | 
					import { toRefs, reactive, onMounted, computed } from 'vue';
 | 
				
			||||||
import { tagApi } from '../tag/api';
 | 
					import { tagApi } from '../tag/api';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
 | 
					import EnumValue from '@/common/Enum';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//定义事件
 | 
					//定义事件
 | 
				
			||||||
const emit = defineEmits(['update:modelValue', 'changeTag', 'input']);
 | 
					const emit = defineEmits(['update:modelValue', 'changeTag', 'input']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
    selectTags: {
 | 
					    selectTags: {
 | 
				
			||||||
        type: [Array<any>],
 | 
					        type: [Array<any>, Object],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    tagType: {
 | 
					    tagType: {
 | 
				
			||||||
        type: Number,
 | 
					        type: Number,
 | 
				
			||||||
        default: TagResourceTypeEnum.Tag.value,
 | 
					        default: TagResourceTypeEnum.Tag.value,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    checkStrictly: {
 | 
					 | 
				
			||||||
        type: Boolean,
 | 
					 | 
				
			||||||
        default: false,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
@@ -62,6 +58,16 @@ const state = reactive({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const { tags } = toRefs(state);
 | 
					const { tags } = toRefs(state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const defaultExpandedKeys = computed(() => {
 | 
				
			||||||
 | 
					    if (Array.isArray(state.selectTags)) {
 | 
				
			||||||
 | 
					        // 如果 state.selectTags 是数组,直接返回
 | 
				
			||||||
 | 
					        return state.selectTags;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果 state.selectTags 不是数组,转换为包含 state.selectTags 的数组
 | 
				
			||||||
 | 
					    return [state.selectTags];
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
    state.selectTags = props.selectTags;
 | 
					    state.selectTags = props.selectTags;
 | 
				
			||||||
    state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
 | 
					    state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,3 +171,31 @@ export function getTagPathSearchItem(resourceType: number) {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 根据标签路径获取对应的类型与编号数组
 | 
				
			||||||
 | 
					 * @param codePath 编号路径  tag1/tag2/1|xxx/11|yyy/
 | 
				
			||||||
 | 
					 * @returns {1: ['xxx'], 11: ['yyy']}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function getTagTypeCodeByPath(codePath: string) {
 | 
				
			||||||
 | 
					    const result = {};
 | 
				
			||||||
 | 
					    const parts = codePath.split('/'); // 切分字符串并保留数字和对应的值部分
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (let part of parts) {
 | 
				
			||||||
 | 
					        if (!part) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let [key, value] = part.split('|'); // 分割数字和值部分
 | 
				
			||||||
 | 
					        // 如果不存在第二个参数,则说明为标签类型
 | 
				
			||||||
 | 
					        if (!value) {
 | 
				
			||||||
 | 
					            value = key;
 | 
				
			||||||
 | 
					            key = '-1';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!result[key]) {
 | 
				
			||||||
 | 
					            result[key] = [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        result[key].push(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,12 @@
 | 
				
			|||||||
    <div class="db-sql-exec">
 | 
					    <div class="db-sql-exec">
 | 
				
			||||||
        <Splitpanes class="default-theme">
 | 
					        <Splitpanes class="default-theme">
 | 
				
			||||||
            <Pane size="20" max-size="30">
 | 
					            <Pane size="20" max-size="30">
 | 
				
			||||||
                <tag-tree :resource-type="TagResourceTypeEnum.DbName.value" :tag-path-node-type="NodeTypeTagPath" ref="tagTreeRef">
 | 
					                <tag-tree
 | 
				
			||||||
 | 
					                    :default-expanded-keys="state.defaultExpendKey"
 | 
				
			||||||
 | 
					                    :resource-type="TagResourceTypeEnum.DbName.value"
 | 
				
			||||||
 | 
					                    :tag-path-node-type="NodeTypeTagPath"
 | 
				
			||||||
 | 
					                    ref="tagTreeRef"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
                    <template #prefix="{ data }">
 | 
					                    <template #prefix="{ data }">
 | 
				
			||||||
                        <span v-if="data.type.value == SqlExecNodeType.DbInst">
 | 
					                        <span v-if="data.type.value == SqlExecNodeType.DbInst">
 | 
				
			||||||
                            <el-popover
 | 
					                            <el-popover
 | 
				
			||||||
@@ -167,11 +172,11 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { defineAsyncComponent, h, onBeforeUnmount, onMounted, reactive, ref, toRefs } from 'vue';
 | 
					import { defineAsyncComponent, h, onBeforeUnmount, onMounted, reactive, ref, toRefs, watch } from 'vue';
 | 
				
			||||||
import { ElCheckbox, ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElCheckbox, ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { formatByteSize } from '@/common/utils/format';
 | 
					import { formatByteSize } from '@/common/utils/format';
 | 
				
			||||||
import { DbInst, registerDbCompletionItemProvider, TabInfo, TabType } from './db';
 | 
					import { DbInst, registerDbCompletionItemProvider, TabInfo, TabType } from './db';
 | 
				
			||||||
import { NodeType, TagTreeNode } from '../component/tag';
 | 
					import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { dbApi } from './api';
 | 
					import { dbApi } from './api';
 | 
				
			||||||
import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
 | 
					import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
 | 
				
			||||||
@@ -183,6 +188,8 @@ import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			|||||||
import { Pane, Splitpanes } from 'splitpanes';
 | 
					import { Pane, Splitpanes } from 'splitpanes';
 | 
				
			||||||
import { useEventListener } from '@vueuse/core';
 | 
					import { useEventListener } from '@vueuse/core';
 | 
				
			||||||
import SqlExecBox from '@/views/ops/db/component/sqleditor/SqlExecBox';
 | 
					import SqlExecBox from '@/views/ops/db/component/sqleditor/SqlExecBox';
 | 
				
			||||||
 | 
					import { useAutoOpenResource } from '@/store/autoOpenResource';
 | 
				
			||||||
 | 
					import { storeToRefs } from 'pinia';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DbTableOp = defineAsyncComponent(() => import('./component/table/DbTableOp.vue'));
 | 
					const DbTableOp = defineAsyncComponent(() => import('./component/table/DbTableOp.vue'));
 | 
				
			||||||
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
 | 
					const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
 | 
				
			||||||
@@ -258,7 +265,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath)
 | 
				
			|||||||
        await sleep(100);
 | 
					        await sleep(100);
 | 
				
			||||||
        return dbInfos?.map((x: any) => {
 | 
					        return dbInfos?.map((x: any) => {
 | 
				
			||||||
            x.tagPath = parentNode.key;
 | 
					            x.tagPath = parentNode.key;
 | 
				
			||||||
            return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeDbInst).withParams(x);
 | 
					            return new TagTreeNode(`${x.code}`, x.name, NodeTypeDbInst).withParams(x);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .withContextMenuItems([ContextmenuItemRefresh]);
 | 
					    .withContextMenuItems([ContextmenuItemRefresh]);
 | 
				
			||||||
@@ -267,6 +274,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath)
 | 
				
			|||||||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((parentNode: TagTreeNode) => {
 | 
					const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((parentNode: TagTreeNode) => {
 | 
				
			||||||
    const params = parentNode.params;
 | 
					    const params = parentNode.params;
 | 
				
			||||||
    const dbs = params.database.split(' ')?.sort();
 | 
					    const dbs = params.database.split(' ')?.sort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return dbs.map((x: any) => {
 | 
					    return dbs.map((x: any) => {
 | 
				
			||||||
        return new TagTreeNode(`${parentNode.key}.${x}`, x, NodeTypeDb)
 | 
					        return new TagTreeNode(`${parentNode.key}.${x}`, x, NodeTypeDb)
 | 
				
			||||||
            .withParams({
 | 
					            .withParams({
 | 
				
			||||||
@@ -418,6 +426,7 @@ const tagTreeRef: any = ref(null);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const tabs: Map<string, TabInfo> = new Map();
 | 
					const tabs: Map<string, TabInfo> = new Map();
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
 | 
					    defaultExpendKey: [] as any,
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 当前操作的数据库实例
 | 
					     * 当前操作的数据库实例
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -452,7 +461,11 @@ const serverInfoReqParam = ref({
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
const { execute: getDbServerInfo, isFetching: loadingServerInfo, data: dbServerInfo } = dbApi.getInstanceServerInfo.useApi<any>(serverInfoReqParam);
 | 
					const { execute: getDbServerInfo, isFetching: loadingServerInfo, data: dbServerInfo } = dbApi.getInstanceServerInfo.useApi<any>(serverInfoReqParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const autoOpenResourceStore = useAutoOpenResource();
 | 
				
			||||||
 | 
					const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					    autoOpenDb(autoOpenResource.value.dbCodePath);
 | 
				
			||||||
    setHeight();
 | 
					    setHeight();
 | 
				
			||||||
    // 监听浏览器窗口大小变化,更新对应组件高度
 | 
					    // 监听浏览器窗口大小变化,更新对应组件高度
 | 
				
			||||||
    useEventListener(window, 'resize', setHeight);
 | 
					    useEventListener(window, 'resize', setHeight);
 | 
				
			||||||
@@ -462,6 +475,31 @@ onBeforeUnmount(() => {
 | 
				
			|||||||
    dispposeCompletionItemProvider('sql');
 | 
					    dispposeCompletionItemProvider('sql');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					    () => autoOpenResource.value.dbCodePath,
 | 
				
			||||||
 | 
					    (codePath: any) => {
 | 
				
			||||||
 | 
					        autoOpenDb(codePath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const autoOpenDb = (codePath: string) => {
 | 
				
			||||||
 | 
					    if (!codePath) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const typeAndCodes = getTagTypeCodeByPath(codePath);
 | 
				
			||||||
 | 
					    const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const dbCode = typeAndCodes[TagResourceTypeEnum.DbName.value][0];
 | 
				
			||||||
 | 
					    state.defaultExpendKey = [tagPath, dbCode];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					        // 置空
 | 
				
			||||||
 | 
					        autoOpenResourceStore.setDbCodePath('');
 | 
				
			||||||
 | 
					        tagTreeRef.value.setCurrentKey(dbCode);
 | 
				
			||||||
 | 
					    }, 600);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 设置editor高度和数据表高度
 | 
					 * 设置editor高度和数据表高度
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -807,7 +845,7 @@ const getNowDbInfo = () => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .db-op {
 | 
					    .db-op {
 | 
				
			||||||
        height: calc(100vh - 108px);
 | 
					        height: calc(100vh - 106px);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #data-exec {
 | 
					    #data-exec {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,7 +106,9 @@ const props = defineProps({
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//定义事件
 | 
					//定义事件
 | 
				
			||||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
 | 
					const emit = defineEmits(['cancel', 'val-change']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dialogVisible = defineModel<boolean>('visible', { default: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const rules = {
 | 
					const rules = {
 | 
				
			||||||
    tagCodePaths: [
 | 
					    tagCodePaths: [
 | 
				
			||||||
@@ -170,23 +172,19 @@ const defaultForm = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
    dialogVisible: false,
 | 
					 | 
				
			||||||
    sshTunnelMachineList: [] as any,
 | 
					    sshTunnelMachineList: [] as any,
 | 
				
			||||||
    form: defaultForm,
 | 
					    form: defaultForm,
 | 
				
			||||||
    submitForm: {} as any,
 | 
					    submitForm: {} as any,
 | 
				
			||||||
    pwd: '',
 | 
					    pwd: '',
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { dialogVisible, form, submitForm } = toRefs(state);
 | 
					const { form, submitForm } = toRefs(state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { isFetching: testConnBtnLoading, execute: testConnExec } = machineApi.testConn.useApi(submitForm);
 | 
					const { isFetching: testConnBtnLoading, execute: testConnExec } = machineApi.testConn.useApi(submitForm);
 | 
				
			||||||
const { isFetching: saveBtnLoading, execute: saveMachineExec } = machineApi.saveMachine.useApi(submitForm);
 | 
					const { isFetching: saveBtnLoading, execute: saveMachineExec } = machineApi.saveMachine.useApi(submitForm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watchEffect(() => {
 | 
					watchEffect(() => {
 | 
				
			||||||
    state.dialogVisible = props.visible;
 | 
					    if (!dialogVisible.value) {
 | 
				
			||||||
    if (!state.dialogVisible) {
 | 
					 | 
				
			||||||
        state.form = { ...defaultForm };
 | 
					 | 
				
			||||||
        state.form.authCerts = [];
 | 
					 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const machine: any = props.machine;
 | 
					    const machine: any = props.machine;
 | 
				
			||||||
@@ -194,6 +192,9 @@ watchEffect(() => {
 | 
				
			|||||||
        state.form = { ...machine };
 | 
					        state.form = { ...machine };
 | 
				
			||||||
        state.form.tagCodePaths = machine.tags.map((t: any) => t.codePath);
 | 
					        state.form.tagCodePaths = machine.tags.map((t: any) => t.codePath);
 | 
				
			||||||
        state.form.authCerts = machine.authCerts || [];
 | 
					        state.form.authCerts = machine.authCerts || [];
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        state.form = { ...defaultForm };
 | 
				
			||||||
 | 
					        state.form.authCerts = [];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -250,7 +251,7 @@ const handleChangeProtocol = (val: any) => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cancel = () => {
 | 
					const cancel = () => {
 | 
				
			||||||
    emit('update:visible', false);
 | 
					    dialogVisible.value = false;
 | 
				
			||||||
    emit('cancel');
 | 
					    emit('cancel');
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@
 | 
				
			|||||||
                    ref="tagTreeRef"
 | 
					                    ref="tagTreeRef"
 | 
				
			||||||
                    :resource-type="TagResourceTypeEnum.MachineAuthCert.value"
 | 
					                    :resource-type="TagResourceTypeEnum.MachineAuthCert.value"
 | 
				
			||||||
                    :tag-path-node-type="NodeTypeTagPath"
 | 
					                    :tag-path-node-type="NodeTypeTagPath"
 | 
				
			||||||
 | 
					                    :default-expanded-keys="state.defaultExpendKey"
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <template #prefix="{ data }">
 | 
					                    <template #prefix="{ data }">
 | 
				
			||||||
                        <SvgIcon
 | 
					                        <SvgIcon
 | 
				
			||||||
@@ -153,13 +154,13 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { defineAsyncComponent, nextTick, reactive, ref, toRefs, watch } from 'vue';
 | 
					import { defineAsyncComponent, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue';
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from 'vue-router';
 | 
				
			||||||
import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
					import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { dateFormat } from '@/common/utils/date';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
import { NodeType, TagTreeNode } from '../component/tag';
 | 
					import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { Pane, Splitpanes } from 'splitpanes';
 | 
					import { Pane, Splitpanes } from 'splitpanes';
 | 
				
			||||||
import { ContextmenuItem } from '@/components/contextmenu/index';
 | 
					import { ContextmenuItem } from '@/components/contextmenu/index';
 | 
				
			||||||
@@ -169,6 +170,8 @@ import MachineRdp from '@/components/terminal-rdp/MachineRdp.vue';
 | 
				
			|||||||
import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
 | 
					import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
 | 
				
			||||||
import ResourceTags from '../component/ResourceTags.vue';
 | 
					import ResourceTags from '../component/ResourceTags.vue';
 | 
				
			||||||
import { MachineProtocolEnum } from './enums';
 | 
					import { MachineProtocolEnum } from './enums';
 | 
				
			||||||
 | 
					import { useAutoOpenResource } from '@/store/autoOpenResource';
 | 
				
			||||||
 | 
					import { storeToRefs } from 'pinia';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 组件
 | 
					// 组件
 | 
				
			||||||
const ScriptManage = defineAsyncComponent(() => import('./ScriptManage.vue'));
 | 
					const ScriptManage = defineAsyncComponent(() => import('./ScriptManage.vue'));
 | 
				
			||||||
@@ -196,6 +199,7 @@ class MachineNodeType {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
 | 
					    defaultExpendKey: [] as any,
 | 
				
			||||||
    params: {
 | 
					    params: {
 | 
				
			||||||
        pageNum: 1,
 | 
					        pageNum: 1,
 | 
				
			||||||
        pageSize: 0,
 | 
					        pageSize: 0,
 | 
				
			||||||
@@ -252,6 +256,9 @@ const { infoDialog, serviceDialog, processDialog, fileDialog, machineStatsDialog
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const tagTreeRef: any = ref(null);
 | 
					const tagTreeRef: any = ref(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const autoOpenResourceStore = useAutoOpenResource();
 | 
				
			||||||
 | 
					const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let openIds = {};
 | 
					let openIds = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (node: TagTreeNode) => {
 | 
					const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (node: TagTreeNode) => {
 | 
				
			||||||
@@ -263,7 +270,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
 | 
				
			|||||||
    // 把list 根据name字段排序
 | 
					    // 把list 根据name字段排序
 | 
				
			||||||
    res.list = res.list.sort((a: any, b: any) => a.name.localeCompare(b.name));
 | 
					    res.list = res.list.sort((a: any, b: any) => a.name.localeCompare(b.name));
 | 
				
			||||||
    return res.list.map((x: any) =>
 | 
					    return res.list.map((x: any) =>
 | 
				
			||||||
        new TagTreeNode(x.id, x.name, NodeTypeMachine)
 | 
					        new TagTreeNode(x.code, x.name, NodeTypeMachine)
 | 
				
			||||||
            .withParams(x)
 | 
					            .withParams(x)
 | 
				
			||||||
            .withDisabled(x.status == -1 && x.protocol == MachineProtocolEnum.Ssh.value)
 | 
					            .withDisabled(x.status == -1 && x.protocol == MachineProtocolEnum.Ssh.value)
 | 
				
			||||||
            .withIcon({
 | 
					            .withIcon({
 | 
				
			||||||
@@ -279,7 +286,7 @@ const NodeTypeMachine = new NodeType(MachineNodeType.Machine)
 | 
				
			|||||||
        // 获取授权凭证列表
 | 
					        // 获取授权凭证列表
 | 
				
			||||||
        const authCerts = machine.authCerts;
 | 
					        const authCerts = machine.authCerts;
 | 
				
			||||||
        return authCerts.map((x: any) =>
 | 
					        return authCerts.map((x: any) =>
 | 
				
			||||||
            new TagTreeNode(x.id, x.username, NodeTypeAuthCert)
 | 
					            new TagTreeNode(x.name, x.username, NodeTypeAuthCert)
 | 
				
			||||||
                .withParams({ ...machine, selectAuthCert: x })
 | 
					                .withParams({ ...machine, selectAuthCert: x })
 | 
				
			||||||
                .withDisabled(machine.status == -1 && machine.protocol == MachineProtocolEnum.Ssh.value)
 | 
					                .withDisabled(machine.status == -1 && machine.protocol == MachineProtocolEnum.Ssh.value)
 | 
				
			||||||
                .withIcon({
 | 
					                .withIcon({
 | 
				
			||||||
@@ -323,6 +330,47 @@ const NodeTypeAuthCert = new NodeType(MachineNodeType.AuthCert)
 | 
				
			|||||||
            .withOnClick((node: any) => serviceManager(node.params)),
 | 
					            .withOnClick((node: any) => serviceManager(node.params)),
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					    () => autoOpenResource.value.machineCodePath,
 | 
				
			||||||
 | 
					    (codePath: any) => {
 | 
				
			||||||
 | 
					        autoOpenTerminal(codePath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					    () => state.activeTermName,
 | 
				
			||||||
 | 
					    (newValue, oldValue) => {
 | 
				
			||||||
 | 
					        oldValue && terminalRefs[oldValue]?.blur && terminalRefs[oldValue]?.blur();
 | 
				
			||||||
 | 
					        terminalRefs[newValue]?.focus && terminalRefs[newValue]?.focus();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					    autoOpenTerminal(autoOpenResource.value.machineCodePath);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const autoOpenTerminal = (codePath: string) => {
 | 
				
			||||||
 | 
					    if (!codePath) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const typeAndCodes = getTagTypeCodeByPath(codePath);
 | 
				
			||||||
 | 
					    const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const machineCode = typeAndCodes[TagResourceTypeEnum.Machine.value][0];
 | 
				
			||||||
 | 
					    state.defaultExpendKey = [tagPath, machineCode];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const authCertName = typeAndCodes[TagResourceTypeEnum.MachineAuthCert.value][0];
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					        // 置空
 | 
				
			||||||
 | 
					        autoOpenResourceStore.setMachineCodePath('');
 | 
				
			||||||
 | 
					        tagTreeRef.value.setCurrentKey(authCertName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const acNode = tagTreeRef.value.getNode(authCertName);
 | 
				
			||||||
 | 
					        openTerminal(acNode.data.params);
 | 
				
			||||||
 | 
					    }, 600);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const openTerminal = (machine: any, ex?: boolean) => {
 | 
					const openTerminal = (machine: any, ex?: boolean) => {
 | 
				
			||||||
    // 授权凭证名
 | 
					    // 授权凭证名
 | 
				
			||||||
    const ac = machine.selectAuthCert.name;
 | 
					    const ac = machine.selectAuthCert.name;
 | 
				
			||||||
@@ -465,15 +513,6 @@ const onRemoveTab = (targetName: string) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(
 | 
					 | 
				
			||||||
    () => state.activeTermName,
 | 
					 | 
				
			||||||
    (newValue, oldValue) => {
 | 
					 | 
				
			||||||
        console.log('oldValue', oldValue);
 | 
					 | 
				
			||||||
        oldValue && terminalRefs[oldValue]?.blur && terminalRefs[oldValue]?.blur();
 | 
					 | 
				
			||||||
        terminalRefs[newValue]?.focus && terminalRefs[newValue]?.focus();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const terminalStatusChange = (key: string, status: TerminalStatus) => {
 | 
					const terminalStatusChange = (key: string, status: TerminalStatus) => {
 | 
				
			||||||
    state.tabs.get(key).status = status;
 | 
					    state.tabs.get(key).status = status;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,6 +58,12 @@ export const cronJobApi = {
 | 
				
			|||||||
    execList: Api.newGet('/machine-cronjobs/execs'),
 | 
					    execList: Api.newGet('/machine-cronjobs/execs'),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const cmdConfApi = {
 | 
				
			||||||
 | 
					    list: Api.newGet('/machine/security/cmd-confs'),
 | 
				
			||||||
 | 
					    save: Api.newPost('/machine/security/cmd-confs'),
 | 
				
			||||||
 | 
					    delete: Api.newDelete('/machine/security/cmd-confs/{id}'),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getMachineTerminalSocketUrl(authCertName: any) {
 | 
					export function getMachineTerminalSocketUrl(authCertName: any) {
 | 
				
			||||||
    return `${config.baseWsUrl}/machines/terminal/${authCertName}?${joinClientParams()}`;
 | 
					    return `${config.baseWsUrl}/machines/terminal/${authCertName}?${joinClientParams()}`;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										222
									
								
								mayfly_go_web/src/views/ops/machine/security/CmdConfList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								mayfly_go_web/src/views/ops/machine/security/CmdConfList.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <el-table :data="cmdConfs" stripe>
 | 
				
			||||||
 | 
					            <el-table-column prop="name" label="名称" show-overflow-tooltip min-width="100px"> </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="cmds" label="过滤命令" min-width="320px" show-overflow-tooltip>
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-tag class="ml2 mt2" v-for="cmd in scope.row.cmds" :key="cmd" type="danger">
 | 
				
			||||||
 | 
					                        {{ cmd }}
 | 
				
			||||||
 | 
					                    </el-tag>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="codePaths" label="关联机器" min-width="220px" show-overflow-tooltip>
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <TagCodePath :path="scope.row.tags.map((tag: any) => tag.codePath)" />
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="remark" label="备注" show-overflow-tooltip width="120px"> </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="creator" label="创建者" show-overflow-tooltip width="100px"> </el-table-column>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-table-column label="操作" min-wdith="100px">
 | 
				
			||||||
 | 
					                <template #header>
 | 
				
			||||||
 | 
					                    <el-text tag="b">操作</el-text>
 | 
				
			||||||
 | 
					                    <el-button v-auth="'cmdconf:save'" class="ml5" type="primary" circle size="small" icon="Plus" @click="openFormDialog(false)"> </el-button>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-button v-auth="'cmdconf:save'" @click="openFormDialog(scope.row)" type="primary" link>编辑</el-button>
 | 
				
			||||||
 | 
					                    <el-button v-auth="'cmdconf:del'" @click="deleteCmdConf(scope.row)" type="danger" link>删除</el-button>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					        </el-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-drawer title="命令配置" v-model="dialogVisible" :show-close="false" width="600px" :destroy-on-close="true" :close-on-click-modal="false">
 | 
				
			||||||
 | 
					            <template #header>
 | 
				
			||||||
 | 
					                <DrawerHeader header="命令配置" :back="cancelEdit" />
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-form ref="formRef" :model="state.form" :rules="rules" label-width="auto">
 | 
				
			||||||
 | 
					                <el-form-item prop="name" label="名称" required>
 | 
				
			||||||
 | 
					                    <el-input v-model="form.name" placeholder="名称"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item prop="cmds" label="过滤命令" required>
 | 
				
			||||||
 | 
					                    <el-row>
 | 
				
			||||||
 | 
					                        <el-tag
 | 
				
			||||||
 | 
					                            class="ml2 mt2"
 | 
				
			||||||
 | 
					                            v-for="tag in form.cmds"
 | 
				
			||||||
 | 
					                            :key="tag"
 | 
				
			||||||
 | 
					                            closable
 | 
				
			||||||
 | 
					                            :disable-transitions="false"
 | 
				
			||||||
 | 
					                            @close="handleCmdClose(tag)"
 | 
				
			||||||
 | 
					                            type="danger"
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            {{ tag }}
 | 
				
			||||||
 | 
					                        </el-tag>
 | 
				
			||||||
 | 
					                        <el-input
 | 
				
			||||||
 | 
					                            v-if="state.inputCmdVisible"
 | 
				
			||||||
 | 
					                            ref="cmdInputRef"
 | 
				
			||||||
 | 
					                            v-model="state.cmdInputValue"
 | 
				
			||||||
 | 
					                            class="mt3"
 | 
				
			||||||
 | 
					                            size="small"
 | 
				
			||||||
 | 
					                            @keyup.enter="handleCmdInputConfirm"
 | 
				
			||||||
 | 
					                            @blur="handleCmdInputConfirm"
 | 
				
			||||||
 | 
					                            placeholder="请输入命令正则表达式"
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                        <el-button v-else class="ml2 mt2" size="small" @click="showCmdInput"> + 新建命令 </el-button>
 | 
				
			||||||
 | 
					                    </el-row>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item label="备注">
 | 
				
			||||||
 | 
					                    <el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item ref="tagSelectRef" prop="codePaths" label="关联机器">
 | 
				
			||||||
 | 
					                    <tag-tree-check height="calc(100vh - 430px)" :tag-type="TagResourceTypeEnum.MachineAuthCert.value" v-model="form.codePaths" />
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					            </el-form>
 | 
				
			||||||
 | 
					            <template #footer>
 | 
				
			||||||
 | 
					                <div class="dialog-footer">
 | 
				
			||||||
 | 
					                    <el-button :loading="submiting" @click="cancelEdit">取 消</el-button>
 | 
				
			||||||
 | 
					                    <el-button v-auth="'cmdconf:save'" type="primary" :loading="submiting" @click="submitForm">确 定</el-button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					        </el-drawer>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { ref, toRefs, reactive, onMounted, nextTick } from 'vue';
 | 
				
			||||||
 | 
					import TagTreeCheck from '../../component/TagTreeCheck.vue';
 | 
				
			||||||
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
 | 
					import { cmdConfApi } from '../api';
 | 
				
			||||||
 | 
					import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
				
			||||||
 | 
					import TagCodePath from '../../component/TagCodePath.vue';
 | 
				
			||||||
 | 
					import _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const rules = {
 | 
				
			||||||
 | 
					    tags: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            required: true,
 | 
				
			||||||
 | 
					            message: '请选择关联的机器',
 | 
				
			||||||
 | 
					            trigger: ['change'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    cmds: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            required: true,
 | 
				
			||||||
 | 
					            message: '请创建命令',
 | 
				
			||||||
 | 
					            trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    name: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            required: true,
 | 
				
			||||||
 | 
					            message: '请输入名称',
 | 
				
			||||||
 | 
					            trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tagSelectRef: any = ref(null);
 | 
				
			||||||
 | 
					const formRef: any = ref(null);
 | 
				
			||||||
 | 
					const cmdInputRef: any = ref(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DefaultForm = {
 | 
				
			||||||
 | 
					    id: 0,
 | 
				
			||||||
 | 
					    name: '',
 | 
				
			||||||
 | 
					    codePaths: [],
 | 
				
			||||||
 | 
					    cmds: [] as any,
 | 
				
			||||||
 | 
					    remark: '',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const state = reactive({
 | 
				
			||||||
 | 
					    cmdConfs: [],
 | 
				
			||||||
 | 
					    dialogVisible: false,
 | 
				
			||||||
 | 
					    form: DefaultForm,
 | 
				
			||||||
 | 
					    submiting: false,
 | 
				
			||||||
 | 
					    inputCmdVisible: false,
 | 
				
			||||||
 | 
					    cmdInputValue: '',
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { cmdConfs, dialogVisible, form, submiting } = toRefs(state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(async () => {
 | 
				
			||||||
 | 
					    getCmdConfs();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getCmdConfs = async () => {
 | 
				
			||||||
 | 
					    state.cmdConfs = await cmdConfApi.list.request();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const handleCmdClose = (tag: string) => {
 | 
				
			||||||
 | 
					    state.form.cmds.splice(state.form.cmds.indexOf(tag), 1);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const showCmdInput = () => {
 | 
				
			||||||
 | 
					    state.inputCmdVisible = true;
 | 
				
			||||||
 | 
					    nextTick(() => {
 | 
				
			||||||
 | 
					        cmdInputRef.value!.input!.focus();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const handleCmdInputConfirm = () => {
 | 
				
			||||||
 | 
					    if (state.cmdInputValue) {
 | 
				
			||||||
 | 
					        state.form.cmds.push(state.cmdInputValue);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    state.inputCmdVisible = false;
 | 
				
			||||||
 | 
					    state.cmdInputValue = '';
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const openFormDialog = (data: any) => {
 | 
				
			||||||
 | 
					    if (!data) {
 | 
				
			||||||
 | 
					        state.form = { ...DefaultForm };
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        state.form = _.cloneDeep(data);
 | 
				
			||||||
 | 
					        state.form.codePaths = data.tags.map((tag: any) => tag.codePath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    state.dialogVisible = true;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const deleteCmdConf = async (data: any) => {
 | 
				
			||||||
 | 
					    await ElMessageBox.confirm(`确定删除该[${data.name}]命令配置?`, '提示', {
 | 
				
			||||||
 | 
					        confirmButtonText: '确定',
 | 
				
			||||||
 | 
					        cancelButtonText: '取消',
 | 
				
			||||||
 | 
					        type: 'warning',
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await cmdConfApi.delete.request({ id: data.id });
 | 
				
			||||||
 | 
					    ElMessage.success('操作成功');
 | 
				
			||||||
 | 
					    getCmdConfs();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const cancelEdit = () => {
 | 
				
			||||||
 | 
					    state.dialogVisible = false;
 | 
				
			||||||
 | 
					    // 取消表单的校验
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					        state.form = { ...DefaultForm };
 | 
				
			||||||
 | 
					        formRef.value.resetFields();
 | 
				
			||||||
 | 
					    }, 200);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const submitForm = () => {
 | 
				
			||||||
 | 
					    formRef.value.validate(async (valid: boolean) => {
 | 
				
			||||||
 | 
					        if (!valid) {
 | 
				
			||||||
 | 
					            ElMessage.error('请正确填写信息');
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            state.submiting = true;
 | 
				
			||||||
 | 
					            await cmdConfApi.save.request(state.form);
 | 
				
			||||||
 | 
					            ElMessage.success('操作成功');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cancelEdit();
 | 
				
			||||||
 | 
					            getCmdConfs();
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            state.submiting = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style></style>
 | 
				
			||||||
@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div class="card">
 | 
				
			||||||
 | 
					        <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleTabChange">
 | 
				
			||||||
 | 
					            <el-tab-pane label="命令配置" :name="CmdConfTab">
 | 
				
			||||||
 | 
					                <CmdConfList />
 | 
				
			||||||
 | 
					            </el-tab-pane>
 | 
				
			||||||
 | 
					        </el-tabs>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { toRefs, reactive, onMounted, defineAsyncComponent } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CmdConfList = defineAsyncComponent(() => import('./CmdConfList.vue'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CmdConfTab = 'cmdConf';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const state = reactive({
 | 
				
			||||||
 | 
					    activeName: CmdConfTab,
 | 
				
			||||||
 | 
					    cmdConfs: [],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { activeName } = toRefs(state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(async () => {
 | 
				
			||||||
 | 
					    state.activeName = CmdConfTab;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const handleTabChange = (tabName: any) => {
 | 
				
			||||||
 | 
					    if (tabName == CmdConfTab) {
 | 
				
			||||||
 | 
					        console.log('get cmd confs');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    console.log(tabName);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style></style>
 | 
				
			||||||
@@ -2,7 +2,12 @@
 | 
				
			|||||||
    <div class="flex-all-center">
 | 
					    <div class="flex-all-center">
 | 
				
			||||||
        <Splitpanes class="default-theme">
 | 
					        <Splitpanes class="default-theme">
 | 
				
			||||||
            <Pane size="20" max-size="30">
 | 
					            <Pane size="20" max-size="30">
 | 
				
			||||||
                <tag-tree :resource-type="TagResourceTypeEnum.Mongo.value" :tag-path-node-type="NodeTypeTagPath">
 | 
					                <tag-tree
 | 
				
			||||||
 | 
					                    ref="tagTreeRef"
 | 
				
			||||||
 | 
					                    :default-expanded-keys="state.defaultExpendKey"
 | 
				
			||||||
 | 
					                    :resource-type="TagResourceTypeEnum.Mongo.value"
 | 
				
			||||||
 | 
					                    :tag-path-node-type="NodeTypeTagPath"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
                    <template #prefix="{ data }">
 | 
					                    <template #prefix="{ data }">
 | 
				
			||||||
                        <span v-if="data.type.value == MongoNodeType.Mongo">
 | 
					                        <span v-if="data.type.value == MongoNodeType.Mongo">
 | 
				
			||||||
                            <el-popover :show-after="500" placement="right-start" title="mongo实例信息" trigger="hover" :width="250">
 | 
					                            <el-popover :show-after="500" placement="right-start" title="mongo实例信息" trigger="hover" :width="250">
 | 
				
			||||||
@@ -168,16 +173,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { mongoApi } from './api';
 | 
					import { mongoApi } from './api';
 | 
				
			||||||
import { computed, defineAsyncComponent, reactive, ref, toRefs } from 'vue';
 | 
					import { computed, defineAsyncComponent, onMounted, reactive, ref, toRefs, watch } from 'vue';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { isTrue, notBlank } from '@/common/assert';
 | 
					import { isTrue, notBlank } from '@/common/assert';
 | 
				
			||||||
import { TagTreeNode, NodeType } from '../component/tag';
 | 
					import { TagTreeNode, NodeType, getTagTypeCodeByPath } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { formatByteSize } from '@/common/utils/format';
 | 
					import { formatByteSize } from '@/common/utils/format';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
import { sleep } from '@/common/utils/loading';
 | 
					import { sleep } from '@/common/utils/loading';
 | 
				
			||||||
import { Splitpanes, Pane } from 'splitpanes';
 | 
					import { Splitpanes, Pane } from 'splitpanes';
 | 
				
			||||||
 | 
					import { useAutoOpenResource } from '@/store/autoOpenResource';
 | 
				
			||||||
 | 
					import { storeToRefs } from 'pinia';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MonacoEditor = defineAsyncComponent(() => import('@/components/monaco/MonacoEditor.vue'));
 | 
					const MonacoEditor = defineAsyncComponent(() => import('@/components/monaco/MonacoEditor.vue'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -207,7 +214,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
 | 
				
			|||||||
    await sleep(100);
 | 
					    await sleep(100);
 | 
				
			||||||
    return mongoInfos?.map((x: any) => {
 | 
					    return mongoInfos?.map((x: any) => {
 | 
				
			||||||
        x.tagPath = parentNode.key;
 | 
					        x.tagPath = parentNode.key;
 | 
				
			||||||
        return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeMongo).withParams(x);
 | 
					        return new TagTreeNode(`${x.code}`, x.name, NodeTypeMongo).withParams(x);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -250,7 +257,10 @@ const NodeTypeColl = new NodeType(MongoNodeType.Coll).withNodeClickFunc((nodeDat
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const findParamInputRef: any = ref(null);
 | 
					const findParamInputRef: any = ref(null);
 | 
				
			||||||
 | 
					const tagTreeRef: any = ref(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
 | 
					    defaultExpendKey: [] as any,
 | 
				
			||||||
    tags: [],
 | 
					    tags: [],
 | 
				
			||||||
    mongoList: [] as any,
 | 
					    mongoList: [] as any,
 | 
				
			||||||
    activeName: '', // 当前操作的tab
 | 
					    activeName: '', // 当前操作的tab
 | 
				
			||||||
@@ -282,10 +292,42 @@ const state = reactive({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const { findDialog, docEditDialog } = toRefs(state);
 | 
					const { findDialog, docEditDialog } = toRefs(state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const autoOpenResourceStore = useAutoOpenResource();
 | 
				
			||||||
 | 
					const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const nowColl = computed(() => {
 | 
					const nowColl = computed(() => {
 | 
				
			||||||
    return getNowDataTab();
 | 
					    return getNowDataTab();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					    () => autoOpenResource.value.mongoCodePath,
 | 
				
			||||||
 | 
					    (codePath: any) => {
 | 
				
			||||||
 | 
					        autoOpenMongo(codePath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					    autoOpenMongo(autoOpenResource.value.mongoCodePath);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const autoOpenMongo = (codePath: string) => {
 | 
				
			||||||
 | 
					    if (!codePath) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const typeAndCodes = getTagTypeCodeByPath(codePath);
 | 
				
			||||||
 | 
					    const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const mongoCode = typeAndCodes[TagResourceTypeEnum.Mongo.value][0];
 | 
				
			||||||
 | 
					    state.defaultExpendKey = [tagPath, mongoCode];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					        // 置空
 | 
				
			||||||
 | 
					        autoOpenResourceStore.setMongoCodePath('');
 | 
				
			||||||
 | 
					        tagTreeRef.value.setCurrentKey(mongoCode);
 | 
				
			||||||
 | 
					    }, 600);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const changeCollection = async (id: any, schema: string, collection: string) => {
 | 
					const changeCollection = async (id: any, schema: string, collection: string) => {
 | 
				
			||||||
    const label = `${id}:\`${schema}\`.${collection}`;
 | 
					    const label = `${id}:\`${schema}\`.${collection}`;
 | 
				
			||||||
    let dataTab = state.dataTabs[label];
 | 
					    let dataTab = state.dataTabs[label];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,12 @@
 | 
				
			|||||||
    <div class="redis-data-op flex-all-center">
 | 
					    <div class="redis-data-op flex-all-center">
 | 
				
			||||||
        <Splitpanes class="default-theme">
 | 
					        <Splitpanes class="default-theme">
 | 
				
			||||||
            <Pane size="20" max-size="30">
 | 
					            <Pane size="20" max-size="30">
 | 
				
			||||||
                <tag-tree :resource-type="TagResourceTypeEnum.Redis.value" :tag-path-node-type="NodeTypeTagPath">
 | 
					                <tag-tree
 | 
				
			||||||
 | 
					                    ref="tagTreeRef"
 | 
				
			||||||
 | 
					                    :default-expanded-keys="state.defaultExpendKey"
 | 
				
			||||||
 | 
					                    :resource-type="TagResourceTypeEnum.Redis.value"
 | 
				
			||||||
 | 
					                    :tag-path-node-type="NodeTypeTagPath"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
                    <template #prefix="{ data }">
 | 
					                    <template #prefix="{ data }">
 | 
				
			||||||
                        <span v-if="data.type.value == RedisNodeType.Redis">
 | 
					                        <span v-if="data.type.value == RedisNodeType.Redis">
 | 
				
			||||||
                            <el-popover :show-after="500" placement="right-start" title="redis实例信息" trigger="hover" :width="250">
 | 
					                            <el-popover :show-after="500" placement="right-start" title="redis实例信息" trigger="hover" :width="250">
 | 
				
			||||||
@@ -178,11 +183,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { redisApi } from './api';
 | 
					import { redisApi } from './api';
 | 
				
			||||||
import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick, Ref } from 'vue';
 | 
					import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick, Ref, watch } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { isTrue, notBlank, notNull } from '@/common/assert';
 | 
					import { isTrue, notBlank, notNull } from '@/common/assert';
 | 
				
			||||||
import { copyToClipboard } from '@/common/utils/string';
 | 
					import { copyToClipboard } from '@/common/utils/string';
 | 
				
			||||||
import { TagTreeNode, NodeType } from '../component/tag';
 | 
					import { TagTreeNode, NodeType, getTagTypeCodeByPath } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { keysToTree, sortByTreeNodes, keysToList } from './utils';
 | 
					import { keysToTree, sortByTreeNodes, keysToList } from './utils';
 | 
				
			||||||
import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
 | 
					import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
 | 
				
			||||||
@@ -190,6 +195,8 @@ import { sleep } from '@/common/utils/loading';
 | 
				
			|||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
import { Splitpanes, Pane } from 'splitpanes';
 | 
					import { Splitpanes, Pane } from 'splitpanes';
 | 
				
			||||||
import { RedisInst } from './redis';
 | 
					import { RedisInst } from './redis';
 | 
				
			||||||
 | 
					import { useAutoOpenResource } from '@/store/autoOpenResource';
 | 
				
			||||||
 | 
					import { storeToRefs } from 'pinia';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
 | 
					const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -230,7 +237,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
 | 
				
			|||||||
    await sleep(100);
 | 
					    await sleep(100);
 | 
				
			||||||
    return redisInfos.map((x: any) => {
 | 
					    return redisInfos.map((x: any) => {
 | 
				
			||||||
        x.tagPath = parentNode.key;
 | 
					        x.tagPath = parentNode.key;
 | 
				
			||||||
        return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeRedis).withParams(x);
 | 
					        return new TagTreeNode(`${x.code}`, x.name, NodeTypeRedis).withParams(x);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -288,9 +295,11 @@ const treeProps = {
 | 
				
			|||||||
const defaultCount = 250;
 | 
					const defaultCount = 250;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const keyTreeRef: any = ref(null);
 | 
					const keyTreeRef: any = ref(null);
 | 
				
			||||||
 | 
					const tagTreeRef: any = ref(null);
 | 
				
			||||||
const redisInst: Ref<RedisInst> = ref(new RedisInst());
 | 
					const redisInst: Ref<RedisInst> = ref(new RedisInst());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
 | 
					    defaultExpendKey: [] as any,
 | 
				
			||||||
    tags: [],
 | 
					    tags: [],
 | 
				
			||||||
    redisList: [] as any,
 | 
					    redisList: [] as any,
 | 
				
			||||||
    dbList: [],
 | 
					    dbList: [],
 | 
				
			||||||
@@ -331,7 +340,37 @@ const state = reactive({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const { scanParam, keyTreeData, newKeyDialog } = toRefs(state);
 | 
					const { scanParam, keyTreeData, newKeyDialog } = toRefs(state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(async () => {});
 | 
					const autoOpenResourceStore = useAutoOpenResource();
 | 
				
			||||||
 | 
					const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(async () => {
 | 
				
			||||||
 | 
					    autoOpenRedis(autoOpenResource.value.redisCodePath);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					    () => autoOpenResource.value.redisCodePath,
 | 
				
			||||||
 | 
					    (codePath: any) => {
 | 
				
			||||||
 | 
					        autoOpenRedis(codePath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const autoOpenRedis = (codePath: string) => {
 | 
				
			||||||
 | 
					    if (!codePath) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const typeAndCodes = getTagTypeCodeByPath(codePath);
 | 
				
			||||||
 | 
					    const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const redisCode = typeAndCodes[TagResourceTypeEnum.Redis.value][0];
 | 
				
			||||||
 | 
					    state.defaultExpendKey = [tagPath, redisCode];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					        // 置空
 | 
				
			||||||
 | 
					        autoOpenResourceStore.setRedisCodePath('');
 | 
				
			||||||
 | 
					        tagTreeRef.value.setCurrentKey(redisCode);
 | 
				
			||||||
 | 
					    }, 600);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scan = async (appendKey = false) => {
 | 
					const scan = async (appendKey = false) => {
 | 
				
			||||||
    isTrue(state.scanParam.id != null, '请先选择redis');
 | 
					    isTrue(state.scanParam.id != null, '请先选择redis');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,11 +75,7 @@
 | 
				
			|||||||
                                <el-descriptions-item label="code">{{ currentTag.code }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="code">{{ currentTag.code }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                <el-descriptions-item label="路径" :span="2">
 | 
					                                <el-descriptions-item label="路径" :span="2">
 | 
				
			||||||
                                    <span v-for="item in parseTagPath(currentTag.codePath)" :key="item.code">
 | 
					                                    <TagCodePath :path="currentTag.codePath" />
 | 
				
			||||||
                                        <SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.icon" class="mr2" />
 | 
					 | 
				
			||||||
                                        <span> {{ item.code }}</span>
 | 
					 | 
				
			||||||
                                        <SvgIcon v-if="!item.isEnd" class="mr5 ml5" name="arrow-right" />
 | 
					 | 
				
			||||||
                                    </span>
 | 
					 | 
				
			||||||
                                </el-descriptions-item>
 | 
					                                </el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                <el-descriptions-item label="名称">{{ currentTag.name }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="名称">{{ currentTag.name }}</el-descriptions-item>
 | 
				
			||||||
@@ -163,6 +159,7 @@ import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			|||||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
					import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
				
			||||||
import EnumValue from '@/common/Enum';
 | 
					import EnumValue from '@/common/Enum';
 | 
				
			||||||
import InstanceList from '../db/InstanceList.vue';
 | 
					import InstanceList from '../db/InstanceList.vue';
 | 
				
			||||||
 | 
					import TagCodePath from '../component/TagCodePath.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Tree {
 | 
					interface Tree {
 | 
				
			||||||
    id: number;
 | 
					    id: number;
 | 
				
			||||||
@@ -345,38 +342,6 @@ const handleDrop = async (draggingNode: any, dropNode: any) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const parseTagPath = (tagPath: string) => {
 | 
					 | 
				
			||||||
    if (!tagPath) {
 | 
					 | 
				
			||||||
        return [];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const res = [] as any;
 | 
					 | 
				
			||||||
    const codes = tagPath.split('/');
 | 
					 | 
				
			||||||
    for (let code of codes) {
 | 
					 | 
				
			||||||
        const typeAndCode = code.split('|');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (typeAndCode.length == 1) {
 | 
					 | 
				
			||||||
            const tagCode = typeAndCode[0];
 | 
					 | 
				
			||||||
            if (!tagCode) {
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            res.push({
 | 
					 | 
				
			||||||
                type: TagResourceTypeEnum.Tag.value,
 | 
					 | 
				
			||||||
                code: typeAndCode[0],
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        res.push({
 | 
					 | 
				
			||||||
            type: typeAndCode[0],
 | 
					 | 
				
			||||||
            code: typeAndCode[1],
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    res[res.length - 1].isEnd = true;
 | 
					 | 
				
			||||||
    return res;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const tabChange = () => {
 | 
					const tabChange = () => {
 | 
				
			||||||
    setNowTabData();
 | 
					    setNowTabData();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,11 +14,8 @@
 | 
				
			|||||||
                <el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="deleteTeam()" type="danger" icon="delete">删除</el-button>
 | 
					                <el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="deleteTeam()" type="danger" icon="delete">删除</el-button>
 | 
				
			||||||
            </template>
 | 
					            </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <template #tagPath="{ data }">
 | 
					            <template #tags="{ data }">
 | 
				
			||||||
                <tag-info :tag-path="data.tagPath" />
 | 
					                <TagCodePath :path="data.tags?.map((tag: any) => tag.codePath)" />
 | 
				
			||||||
                <span class="ml5">
 | 
					 | 
				
			||||||
                    {{ data.tagPath }}
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
            </template>
 | 
					            </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <template #action="{ data }">
 | 
					            <template #action="{ data }">
 | 
				
			||||||
@@ -48,45 +45,7 @@
 | 
				
			|||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-form-item prop="tag" label="标签">
 | 
					                <el-form-item prop="tag" label="标签">
 | 
				
			||||||
                    <div class="w100" style="border: 1px solid var(--el-border-color)">
 | 
					                    <TagTreeCheck v-model="state.addTeamDialog.form.codePaths" :tag-type="0" />
 | 
				
			||||||
                        <el-input v-model="filterTag" clearable placeholder="输入关键字过滤" size="small" />
 | 
					 | 
				
			||||||
                        <el-scrollbar style="height: calc(100vh - 330px)">
 | 
					 | 
				
			||||||
                            <el-tree
 | 
					 | 
				
			||||||
                                ref="tagTreeRef"
 | 
					 | 
				
			||||||
                                style="width: 100%"
 | 
					 | 
				
			||||||
                                :data="state.tags"
 | 
					 | 
				
			||||||
                                :default-expanded-keys="state.addTeamDialog.form.tags"
 | 
					 | 
				
			||||||
                                :default-checked-keys="state.addTeamDialog.form.tags"
 | 
					 | 
				
			||||||
                                multiple
 | 
					 | 
				
			||||||
                                :render-after-expand="true"
 | 
					 | 
				
			||||||
                                show-checkbox
 | 
					 | 
				
			||||||
                                check-strictly
 | 
					 | 
				
			||||||
                                node-key="id"
 | 
					 | 
				
			||||||
                                :props="{
 | 
					 | 
				
			||||||
                                    value: 'id',
 | 
					 | 
				
			||||||
                                    label: 'codePath',
 | 
					 | 
				
			||||||
                                    children: 'children',
 | 
					 | 
				
			||||||
                                    disabled: 'disabled',
 | 
					 | 
				
			||||||
                                }"
 | 
					 | 
				
			||||||
                                @check="tagTreeNodeCheck"
 | 
					 | 
				
			||||||
                                :filter-node-method="filterNode"
 | 
					 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                <template #default="{ data }">
 | 
					 | 
				
			||||||
                                    <span class="custom-tree-node">
 | 
					 | 
				
			||||||
                                        <SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                        <span class="font13 ml5">
 | 
					 | 
				
			||||||
                                            {{ data.code }}
 | 
					 | 
				
			||||||
                                            <span style="color: #3c8dbc">【</span>
 | 
					 | 
				
			||||||
                                            {{ data.name }}
 | 
					 | 
				
			||||||
                                            <span style="color: #3c8dbc">】</span>
 | 
					 | 
				
			||||||
                                            <el-tag v-if="data.children !== null" size="small">{{ data.children.length }} </el-tag>
 | 
					 | 
				
			||||||
                                        </span>
 | 
					 | 
				
			||||||
                                    </span>
 | 
					 | 
				
			||||||
                                </template>
 | 
					 | 
				
			||||||
                            </el-tree>
 | 
					 | 
				
			||||||
                        </el-scrollbar>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
            </el-form>
 | 
					            </el-form>
 | 
				
			||||||
            <template #footer>
 | 
					            <template #footer>
 | 
				
			||||||
@@ -131,7 +90,7 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { ref, toRefs, reactive, onMounted, Ref, watch } from 'vue';
 | 
					import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
 | 
				
			||||||
import { tagApi } from './api';
 | 
					import { tagApi } from './api';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { notBlank } from '@/common/assert';
 | 
					import { notBlank } from '@/common/assert';
 | 
				
			||||||
@@ -139,20 +98,19 @@ import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			|||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { SearchItem } from '@/components/SearchForm';
 | 
					import { SearchItem } from '@/components/SearchForm';
 | 
				
			||||||
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
 | 
					import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					 | 
				
			||||||
import EnumValue from '@/common/Enum';
 | 
					 | 
				
			||||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
					import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
				
			||||||
 | 
					import TagTreeCheck from '../component/TagTreeCheck.vue';
 | 
				
			||||||
 | 
					import TagCodePath from '../component/TagCodePath.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const teamForm: any = ref(null);
 | 
					const teamForm: any = ref(null);
 | 
				
			||||||
const tagTreeRef: any = ref(null);
 | 
					 | 
				
			||||||
const pageTableRef: Ref<any> = ref(null);
 | 
					const pageTableRef: Ref<any> = ref(null);
 | 
				
			||||||
const showMemPageTableRef: Ref<any> = ref(null);
 | 
					const showMemPageTableRef: Ref<any> = ref(null);
 | 
				
			||||||
const filterTag = ref('');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const searchItems = [SearchItem.input('name', '团队名称')];
 | 
					const searchItems = [SearchItem.input('name', '团队名称')];
 | 
				
			||||||
const columns = [
 | 
					const columns = [
 | 
				
			||||||
    TableColumn.new('name', '团队名称'),
 | 
					    TableColumn.new('name', '团队名称'),
 | 
				
			||||||
    TableColumn.new('remark', '备注'),
 | 
					    TableColumn.new('remark', '备注'),
 | 
				
			||||||
 | 
					    TableColumn.new('tags', '分配标签').isSlot().setAddWidth(40),
 | 
				
			||||||
    TableColumn.new('creator', '创建者'),
 | 
					    TableColumn.new('creator', '创建者'),
 | 
				
			||||||
    TableColumn.new('createTime', '创建时间').isTime(),
 | 
					    TableColumn.new('createTime', '创建时间').isTime(),
 | 
				
			||||||
    TableColumn.new('modifier', '修改者'),
 | 
					    TableColumn.new('modifier', '修改者'),
 | 
				
			||||||
@@ -162,10 +120,9 @@ const columns = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
    currentEditPermissions: false,
 | 
					    currentEditPermissions: false,
 | 
				
			||||||
    tags: [],
 | 
					 | 
				
			||||||
    addTeamDialog: {
 | 
					    addTeamDialog: {
 | 
				
			||||||
        visible: false,
 | 
					        visible: false,
 | 
				
			||||||
        form: { id: 0, name: '', remark: '', tags: [] },
 | 
					        form: { id: 0, name: '', remark: '', codePaths: [] },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    query: {
 | 
					    query: {
 | 
				
			||||||
        pageNum: 1,
 | 
					        pageNum: 1,
 | 
				
			||||||
@@ -211,34 +168,13 @@ const search = async () => {
 | 
				
			|||||||
    pageTableRef.value.search();
 | 
					    pageTableRef.value.search();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(filterTag, (val) => {
 | 
					 | 
				
			||||||
    tagTreeRef.value!.filter(val);
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const filterNode = (value: string, data: any) => {
 | 
					 | 
				
			||||||
    if (!value) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return data.codePath.toLowerCase().includes(value) || data.name.includes(value);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const showSaveTeamDialog = async (data: any) => {
 | 
					const showSaveTeamDialog = async (data: any) => {
 | 
				
			||||||
    state.tags = await tagApi.getTagTrees.request(null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (data) {
 | 
					    if (data) {
 | 
				
			||||||
        state.addTeamDialog.form.id = data.id;
 | 
					        state.addTeamDialog.form.id = data.id;
 | 
				
			||||||
        state.addTeamDialog.form.name = data.name;
 | 
					        state.addTeamDialog.form.name = data.name;
 | 
				
			||||||
        state.addTeamDialog.form.remark = data.remark;
 | 
					        state.addTeamDialog.form.remark = data.remark;
 | 
				
			||||||
        state.addTeamDialog.form.tags = await tagApi.getTeamTagIds.request({ teamId: data.id });
 | 
					        state.addTeamDialog.form.codePaths = data.tags?.map((tag: any) => tag.codePath);
 | 
				
			||||||
 | 
					        // state.addTeamDialog.form.tags = await tagApi.getRelateTagIds.request({ relateType: TagTreeRelateTypeEnum.Team.value, relateId: data.id });
 | 
				
			||||||
        setTimeout(() => {
 | 
					 | 
				
			||||||
            const checkedNodes = tagTreeRef.value.getCheckedNodes();
 | 
					 | 
				
			||||||
            console.log('check nodes: ', checkedNodes);
 | 
					 | 
				
			||||||
            // 禁用选中节点的所有父节点,不可选中
 | 
					 | 
				
			||||||
            for (let checkNodeData of checkedNodes) {
 | 
					 | 
				
			||||||
                disableParentNodes(tagTreeRef.value.getNode(checkNodeData.id).parent);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }, 200);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    state.addTeamDialog.visible = true;
 | 
					    state.addTeamDialog.visible = true;
 | 
				
			||||||
@@ -248,7 +184,6 @@ const saveTeam = async () => {
 | 
				
			|||||||
    teamForm.value.validate(async (valid: any) => {
 | 
					    teamForm.value.validate(async (valid: any) => {
 | 
				
			||||||
        if (valid) {
 | 
					        if (valid) {
 | 
				
			||||||
            const form = state.addTeamDialog.form;
 | 
					            const form = state.addTeamDialog.form;
 | 
				
			||||||
            form.tags = tagTreeRef.value.getCheckedKeys(false);
 | 
					 | 
				
			||||||
            await tagApi.saveTeam.request(form);
 | 
					            await tagApi.saveTeam.request(form);
 | 
				
			||||||
            ElMessage.success('保存成功');
 | 
					            ElMessage.success('保存成功');
 | 
				
			||||||
            search();
 | 
					            search();
 | 
				
			||||||
@@ -318,48 +253,5 @@ const cancelAddMember = () => {
 | 
				
			|||||||
    state.showMemDialog.memForm = {} as any;
 | 
					    state.showMemDialog.memForm = {} as any;
 | 
				
			||||||
    state.showMemDialog.addVisible = false;
 | 
					    state.showMemDialog.addVisible = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
const tagTreeNodeCheck = (data: any) => {
 | 
					 | 
				
			||||||
    const node = tagTreeRef.value.getNode(data.id);
 | 
					 | 
				
			||||||
    console.log('check node: ', node);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (node.checked) {
 | 
					 | 
				
			||||||
        // 如果选中了子节点,则需要将父节点全部取消选中,并禁用父节点
 | 
					 | 
				
			||||||
        unCheckParentNodes(node.parent);
 | 
					 | 
				
			||||||
        disableParentNodes(node.parent);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        // 如果取消了选中,则需要根据条件恢复父节点的选中状态
 | 
					 | 
				
			||||||
        disableParentNodes(node.parent, false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const unCheckParentNodes = (node: any) => {
 | 
					 | 
				
			||||||
    if (!node) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    tagTreeRef.value.setChecked(node, false, false);
 | 
					 | 
				
			||||||
    unCheckParentNodes(node.parent);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 禁用该节点以及所有父节点
 | 
					 | 
				
			||||||
 * @param node 节点
 | 
					 | 
				
			||||||
 * @param disable 是否禁用
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const disableParentNodes = (node: any, disable = true) => {
 | 
					 | 
				
			||||||
    if (!node) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!disable) {
 | 
					 | 
				
			||||||
        // 恢复为非禁用状态时,若同层级存在一个选中状态或者禁用状态,则继续禁用 不恢复非禁用状态。
 | 
					 | 
				
			||||||
        for (let oneLevelNodes of node.childNodes) {
 | 
					 | 
				
			||||||
            if (oneLevelNodes.checked || oneLevelNodes.data.disabled) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    node.data.disabled = disable;
 | 
					 | 
				
			||||||
    disableParentNodes(node.parent, disable);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<style lang="scss" scoped></style>
 | 
					<style lang="scss" scoped></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ export const tagApi = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    getResourceTagPaths: Api.newGet('/tag-trees/resources/{resourceType}/tag-paths'),
 | 
					    getResourceTagPaths: Api.newGet('/tag-trees/resources/{resourceType}/tag-paths'),
 | 
				
			||||||
    countTagResource: Api.newGet('/tag-trees/resources/count'),
 | 
					    countTagResource: Api.newGet('/tag-trees/resources/count'),
 | 
				
			||||||
 | 
					    getRelateTagIds: Api.newGet('/tag-trees/relate/{relateType}/{relateId}'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getTeams: Api.newGet('/teams'),
 | 
					    getTeams: Api.newGet('/teams'),
 | 
				
			||||||
    saveTeam: Api.newPost('/teams'),
 | 
					    saveTeam: Api.newPost('/teams'),
 | 
				
			||||||
@@ -17,8 +18,6 @@ export const tagApi = {
 | 
				
			|||||||
    getTeamMem: Api.newGet('/teams/{teamId}/members'),
 | 
					    getTeamMem: Api.newGet('/teams/{teamId}/members'),
 | 
				
			||||||
    saveTeamMem: Api.newPost('/teams/{teamId}/members'),
 | 
					    saveTeamMem: Api.newPost('/teams/{teamId}/members'),
 | 
				
			||||||
    delTeamMem: Api.newDelete('/teams/{teamId}/members/{accountId}'),
 | 
					    delTeamMem: Api.newDelete('/teams/{teamId}/members/{accountId}'),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    getTeamTagIds: Api.newGet('/teams/{teamId}/tags'),
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const resourceAuthCertApi = {
 | 
					export const resourceAuthCertApi = {
 | 
				
			||||||
@@ -27,3 +26,7 @@ export const resourceAuthCertApi = {
 | 
				
			|||||||
    save: Api.newPost('/auth-certs'),
 | 
					    save: Api.newPost('/auth-certs'),
 | 
				
			||||||
    delete: Api.newDelete('/auth-certs/{id}'),
 | 
					    delete: Api.newDelete('/auth-certs/{id}'),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const resourceOpLogApi = {
 | 
				
			||||||
 | 
					    getAccountResourceOpLogs: Api.newGet('/resource-op-logs/account'),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,3 +14,7 @@ export const AuthCertCiphertextTypeEnum = {
 | 
				
			|||||||
    PrivateKey: EnumValue.of(2, '秘钥').tagTypeSuccess(),
 | 
					    PrivateKey: EnumValue.of(2, '秘钥').tagTypeSuccess(),
 | 
				
			||||||
    Public: EnumValue.of(-1, '公共凭证').tagTypeSuccess(),
 | 
					    Public: EnumValue.of(-1, '公共凭证').tagTypeSuccess(),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TagTreeRelateTypeEnum = {
 | 
				
			||||||
 | 
					    Team: EnumValue.of(1, '团队'),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,112 +1,6 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="personal">
 | 
					    <div class="personal">
 | 
				
			||||||
        <el-row>
 | 
					        <el-row>
 | 
				
			||||||
            <!-- 个人信息 -->
 | 
					 | 
				
			||||||
            <el-col :xs="24" :sm="16">
 | 
					 | 
				
			||||||
                <el-card shadow="hover" header="个人信息">
 | 
					 | 
				
			||||||
                    <div class="personal-user">
 | 
					 | 
				
			||||||
                        <div class="personal-user-left">
 | 
					 | 
				
			||||||
                            <el-upload class="h100 personal-user-left-upload" action="" multiple :limit="1">
 | 
					 | 
				
			||||||
                                <img :src="userInfo.photo" />
 | 
					 | 
				
			||||||
                            </el-upload>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                        <div class="personal-user-right">
 | 
					 | 
				
			||||||
                            <el-row>
 | 
					 | 
				
			||||||
                                <el-col :span="24" class="personal-title mb18"
 | 
					 | 
				
			||||||
                                    >{{ currentTime }},{{ userInfo.name }},生活变的再糟糕,也不妨碍我变得更好!
 | 
					 | 
				
			||||||
                                </el-col>
 | 
					 | 
				
			||||||
                                <el-col :span="24">
 | 
					 | 
				
			||||||
                                    <el-row>
 | 
					 | 
				
			||||||
                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
					 | 
				
			||||||
                                            <div class="personal-item-label">用户名:</div>
 | 
					 | 
				
			||||||
                                            <div class="personal-item-value">{{ userInfo.username }}</div>
 | 
					 | 
				
			||||||
                                        </el-col>
 | 
					 | 
				
			||||||
                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
					 | 
				
			||||||
                                            <div class="personal-item-label">角色:</div>
 | 
					 | 
				
			||||||
                                            <div class="personal-item-value">{{ roleInfo }}</div>
 | 
					 | 
				
			||||||
                                        </el-col>
 | 
					 | 
				
			||||||
                                    </el-row>
 | 
					 | 
				
			||||||
                                </el-col>
 | 
					 | 
				
			||||||
                                <el-col :span="24">
 | 
					 | 
				
			||||||
                                    <el-row>
 | 
					 | 
				
			||||||
                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
					 | 
				
			||||||
                                            <div class="personal-item-label">上次登录IP:</div>
 | 
					 | 
				
			||||||
                                            <div class="personal-item-value">{{ userInfo.lastLoginIp }}</div>
 | 
					 | 
				
			||||||
                                        </el-col>
 | 
					 | 
				
			||||||
                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
					 | 
				
			||||||
                                            <div class="personal-item-label">上次登录时间:</div>
 | 
					 | 
				
			||||||
                                            <div class="personal-item-value">{{ dateFormat(userInfo.lastLoginTime) }}</div>
 | 
					 | 
				
			||||||
                                        </el-col>
 | 
					 | 
				
			||||||
                                    </el-row>
 | 
					 | 
				
			||||||
                                </el-col>
 | 
					 | 
				
			||||||
                            </el-row>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </el-card>
 | 
					 | 
				
			||||||
            </el-col>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 消息通知 -->
 | 
					 | 
				
			||||||
            <el-col :xs="24" :sm="8" class="pl15 personal-info">
 | 
					 | 
				
			||||||
                <el-card shadow="hover">
 | 
					 | 
				
			||||||
                    <template #header>
 | 
					 | 
				
			||||||
                        <span>消息通知</span>
 | 
					 | 
				
			||||||
                        <span @click="showMsgs" class="personal-info-more">更多</span>
 | 
					 | 
				
			||||||
                    </template>
 | 
					 | 
				
			||||||
                    <div class="personal-info-box">
 | 
					 | 
				
			||||||
                        <ul class="personal-info-ul">
 | 
					 | 
				
			||||||
                            <li v-for="(v, k) in msgDialog.msgs.list as any" :key="k" class="personal-info-li">
 | 
					 | 
				
			||||||
                                <a class="personal-info-li-title">{{ `[${getMsgTypeDesc(v.type)}] ${v.msg}` }}</a>
 | 
					 | 
				
			||||||
                            </li>
 | 
					 | 
				
			||||||
                        </ul>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </el-card>
 | 
					 | 
				
			||||||
            </el-col>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <el-dialog width="900px" title="消息" v-model="msgDialog.visible">
 | 
					 | 
				
			||||||
                <el-table border :data="msgDialog.msgs.list" size="small">
 | 
					 | 
				
			||||||
                    <el-table-column property="type" label="类型" width="60">
 | 
					 | 
				
			||||||
                        <template #default="scope">
 | 
					 | 
				
			||||||
                            {{ getMsgTypeDesc(scope.row.type) }}
 | 
					 | 
				
			||||||
                        </template>
 | 
					 | 
				
			||||||
                    </el-table-column>
 | 
					 | 
				
			||||||
                    <el-table-column property="msg" label="消息"></el-table-column>
 | 
					 | 
				
			||||||
                    <el-table-column property="createTime" label="时间" width="150">
 | 
					 | 
				
			||||||
                        <template #default="scope">
 | 
					 | 
				
			||||||
                            {{ dateFormat(scope.row.createTime) }}
 | 
					 | 
				
			||||||
                        </template>
 | 
					 | 
				
			||||||
                    </el-table-column>
 | 
					 | 
				
			||||||
                </el-table>
 | 
					 | 
				
			||||||
                <el-row type="flex" class="mt5" justify="center">
 | 
					 | 
				
			||||||
                    <el-pagination
 | 
					 | 
				
			||||||
                        small
 | 
					 | 
				
			||||||
                        @current-change="getMsgs"
 | 
					 | 
				
			||||||
                        style="text-align: center"
 | 
					 | 
				
			||||||
                        background
 | 
					 | 
				
			||||||
                        layout="prev, pager, next, total, jumper"
 | 
					 | 
				
			||||||
                        :total="msgDialog.msgs.total"
 | 
					 | 
				
			||||||
                        v-model:current-page="msgDialog.query.pageNum"
 | 
					 | 
				
			||||||
                        :page-size="msgDialog.query.pageSize"
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                </el-row>
 | 
					 | 
				
			||||||
            </el-dialog>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 营销推荐 -->
 | 
					 | 
				
			||||||
            <!-- <el-col :span="24">
 | 
					 | 
				
			||||||
                <el-card shadow="hover" class="mt15" header="营销推荐">
 | 
					 | 
				
			||||||
                    <el-row :gutter="15" class="personal-recommend-row">
 | 
					 | 
				
			||||||
                        <el-col :sm="6" v-for="(v, k) in recommendList" :key="k" class="personal-recommend-col">
 | 
					 | 
				
			||||||
                            <div class="personal-recommend" :style="{ 'background-color': v.bg }">
 | 
					 | 
				
			||||||
                                <i :class="v.icon" :style="{ color: v.iconColor }"></i>
 | 
					 | 
				
			||||||
                                <div class="personal-recommend-auto">
 | 
					 | 
				
			||||||
                                    <div>{{ v.title }}</div>
 | 
					 | 
				
			||||||
                                    <div class="personal-recommend-msg">{{ v.msg }}</div>
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </el-col>
 | 
					 | 
				
			||||||
                    </el-row>
 | 
					 | 
				
			||||||
                </el-card>
 | 
					 | 
				
			||||||
            </el-col> -->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 更新信息 -->
 | 
					            <!-- 更新信息 -->
 | 
				
			||||||
            <el-col :span="24">
 | 
					            <el-col :span="24">
 | 
				
			||||||
                <el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
 | 
					                <el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
 | 
				
			||||||
@@ -142,28 +36,6 @@
 | 
				
			|||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </span>
 | 
					                    </span>
 | 
				
			||||||
                    <!-- <div class="personal-edit-safe-box">
 | 
					 | 
				
			||||||
                        <div class="personal-edit-safe-item">
 | 
					 | 
				
			||||||
                            <div class="personal-edit-safe-item-left">
 | 
					 | 
				
			||||||
                                <div class="personal-edit-safe-item-left-label">密保手机</div>
 | 
					 | 
				
			||||||
                                <div class="personal-edit-safe-item-left-value">已绑定手机:132****4108</div>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                            <div class="personal-edit-safe-item-right">
 | 
					 | 
				
			||||||
                                <el-button type="text">立即修改</el-button>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="personal-edit-safe-box">
 | 
					 | 
				
			||||||
                        <div class="personal-edit-safe-item">
 | 
					 | 
				
			||||||
                            <div class="personal-edit-safe-item-left">
 | 
					 | 
				
			||||||
                                <div class="personal-edit-safe-item-left-label">密保问题</div>
 | 
					 | 
				
			||||||
                                <div class="personal-edit-safe-item-left-value">已设置密保问题,账号安全大幅度提升</div>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                            <div class="personal-edit-safe-item-right">
 | 
					 | 
				
			||||||
                                <el-button type="text">立即设置</el-button>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </div> -->
 | 
					 | 
				
			||||||
                </el-card>
 | 
					                </el-card>
 | 
				
			||||||
            </el-col>
 | 
					            </el-col>
 | 
				
			||||||
        </el-row>
 | 
					        </el-row>
 | 
				
			||||||
@@ -171,33 +43,16 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { toRefs, reactive, computed, onMounted } from 'vue';
 | 
					import { toRefs, reactive, onMounted } from 'vue';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
import { formatAxis } from '@/common/utils/format';
 | 
					 | 
				
			||||||
import { personApi } from './api';
 | 
					import { personApi } from './api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					 | 
				
			||||||
import { storeToRefs } from 'pinia';
 | 
					 | 
				
			||||||
import { useUserInfo } from '@/store/userInfo';
 | 
					 | 
				
			||||||
import config from '@/common/config';
 | 
					import config from '@/common/config';
 | 
				
			||||||
import { joinClientParams } from '@/common/request';
 | 
					import { joinClientParams } from '@/common/request';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { userInfo } = storeToRefs(useUserInfo());
 | 
					 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
    accountInfo: {
 | 
					    accountInfo: {
 | 
				
			||||||
        roles: [],
 | 
					        roles: [],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    msgs: [],
 | 
					 | 
				
			||||||
    msgDialog: {
 | 
					 | 
				
			||||||
        visible: false,
 | 
					 | 
				
			||||||
        query: {
 | 
					 | 
				
			||||||
            pageSize: 10,
 | 
					 | 
				
			||||||
            pageNum: 1,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        msgs: {
 | 
					 | 
				
			||||||
            list: [],
 | 
					 | 
				
			||||||
            total: null,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    recommendList: [],
 | 
					    recommendList: [],
 | 
				
			||||||
    accountForm: {
 | 
					    accountForm: {
 | 
				
			||||||
        password: '',
 | 
					        password: '',
 | 
				
			||||||
@@ -208,27 +63,10 @@ const state = reactive({
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { msgDialog, accountForm, authStatus } = toRefs(state);
 | 
					const { accountForm, authStatus } = toRefs(state);
 | 
				
			||||||
 | 
					 | 
				
			||||||
// 当前时间提示语
 | 
					 | 
				
			||||||
const currentTime = computed(() => {
 | 
					 | 
				
			||||||
    return formatAxis(new Date());
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const showMsgs = () => {
 | 
					 | 
				
			||||||
    state.msgDialog.visible = true;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const roleInfo = computed(() => {
 | 
					 | 
				
			||||||
    if (state.accountInfo.roles.length == 0) {
 | 
					 | 
				
			||||||
        return '';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return state.accountInfo.roles.map((val: any) => val.name).join('、');
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
    getAccountInfo();
 | 
					    getAccountInfo();
 | 
				
			||||||
    getMsgs();
 | 
					 | 
				
			||||||
    state.authStatus = await personApi.authStatus.request();
 | 
					    state.authStatus = await personApi.authStatus.request();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -277,162 +115,11 @@ const unbindOAuth2 = async () => {
 | 
				
			|||||||
    ElMessage.success('解绑成功');
 | 
					    ElMessage.success('解绑成功');
 | 
				
			||||||
    state.authStatus = await personApi.authStatus.request();
 | 
					    state.authStatus = await personApi.authStatus.request();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
const getMsgs = async () => {
 | 
					 | 
				
			||||||
    const res = await personApi.getMsgs.request(state.msgDialog.query);
 | 
					 | 
				
			||||||
    state.msgDialog.msgs = res;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getMsgTypeDesc = (type: number) => {
 | 
					 | 
				
			||||||
    if (type == 1) {
 | 
					 | 
				
			||||||
        return '登录';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (type == 2) {
 | 
					 | 
				
			||||||
        return '通知';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
@import '../../theme/mixins/index.scss';
 | 
					@import '../../theme/mixins/index.scss';
 | 
				
			||||||
 | 
					 | 
				
			||||||
.personal {
 | 
					.personal {
 | 
				
			||||||
    .personal-user {
 | 
					 | 
				
			||||||
        height: 130px;
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .personal-user-left {
 | 
					 | 
				
			||||||
            width: 100px;
 | 
					 | 
				
			||||||
            height: 130px;
 | 
					 | 
				
			||||||
            border-radius: 3px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ::v-deep(.el-upload) {
 | 
					 | 
				
			||||||
                height: 100%;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .personal-user-left-upload {
 | 
					 | 
				
			||||||
                img {
 | 
					 | 
				
			||||||
                    width: 100%;
 | 
					 | 
				
			||||||
                    height: 100%;
 | 
					 | 
				
			||||||
                    border-radius: 3px;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                &:hover {
 | 
					 | 
				
			||||||
                    img {
 | 
					 | 
				
			||||||
                        animation: logoAnimation 0.3s ease-in-out;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .personal-user-right {
 | 
					 | 
				
			||||||
            flex: 1;
 | 
					 | 
				
			||||||
            padding: 0 15px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .personal-title {
 | 
					 | 
				
			||||||
                font-size: 18px;
 | 
					 | 
				
			||||||
                @include text-ellipsis(1);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .personal-item {
 | 
					 | 
				
			||||||
                display: flex;
 | 
					 | 
				
			||||||
                align-items: center;
 | 
					 | 
				
			||||||
                font-size: 13px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .personal-item-label {
 | 
					 | 
				
			||||||
                    color: gray;
 | 
					 | 
				
			||||||
                    @include text-ellipsis(1);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .personal-item-value {
 | 
					 | 
				
			||||||
                    @include text-ellipsis(1);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .personal-info {
 | 
					 | 
				
			||||||
        .personal-info-more {
 | 
					 | 
				
			||||||
            float: right;
 | 
					 | 
				
			||||||
            color: gray;
 | 
					 | 
				
			||||||
            font-size: 13px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            &:hover {
 | 
					 | 
				
			||||||
                color: var(--el-color-primary);
 | 
					 | 
				
			||||||
                cursor: pointer;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .personal-info-box {
 | 
					 | 
				
			||||||
            height: 130px;
 | 
					 | 
				
			||||||
            overflow: hidden;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .personal-info-ul {
 | 
					 | 
				
			||||||
                list-style: none;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .personal-info-li {
 | 
					 | 
				
			||||||
                    font-size: 13px;
 | 
					 | 
				
			||||||
                    padding-bottom: 10px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    .personal-info-li-title {
 | 
					 | 
				
			||||||
                        display: inline-block;
 | 
					 | 
				
			||||||
                        @include text-ellipsis(1);
 | 
					 | 
				
			||||||
                        color: grey;
 | 
					 | 
				
			||||||
                        text-decoration: none;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    & a:hover {
 | 
					 | 
				
			||||||
                        color: var(--el-color-primary);
 | 
					 | 
				
			||||||
                        cursor: pointer;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .personal-recommend-row {
 | 
					 | 
				
			||||||
        .personal-recommend-col {
 | 
					 | 
				
			||||||
            .personal-recommend {
 | 
					 | 
				
			||||||
                position: relative;
 | 
					 | 
				
			||||||
                height: 100px;
 | 
					 | 
				
			||||||
                color: #ffffff;
 | 
					 | 
				
			||||||
                border-radius: 3px;
 | 
					 | 
				
			||||||
                overflow: hidden;
 | 
					 | 
				
			||||||
                cursor: pointer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                &:hover {
 | 
					 | 
				
			||||||
                    i {
 | 
					 | 
				
			||||||
                        right: 0px !important;
 | 
					 | 
				
			||||||
                        bottom: 0px !important;
 | 
					 | 
				
			||||||
                        transition: all ease 0.3s;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                i {
 | 
					 | 
				
			||||||
                    position: absolute;
 | 
					 | 
				
			||||||
                    right: -10px;
 | 
					 | 
				
			||||||
                    bottom: -10px;
 | 
					 | 
				
			||||||
                    font-size: 70px;
 | 
					 | 
				
			||||||
                    transform: rotate(-30deg);
 | 
					 | 
				
			||||||
                    transition: all ease 0.3s;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .personal-recommend-auto {
 | 
					 | 
				
			||||||
                    padding: 15px;
 | 
					 | 
				
			||||||
                    position: absolute;
 | 
					 | 
				
			||||||
                    left: 0;
 | 
					 | 
				
			||||||
                    top: 5%;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    .personal-recommend-msg {
 | 
					 | 
				
			||||||
                        font-size: 12px;
 | 
					 | 
				
			||||||
                        margin-top: 10px;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .personal-edit {
 | 
					    .personal-edit {
 | 
				
			||||||
        .personal-edit-title {
 | 
					        .personal-edit-title {
 | 
				
			||||||
            position: relative;
 | 
					            position: relative;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,7 +128,7 @@
 | 
				
			|||||||
                    </el-col>
 | 
					                    </el-col>
 | 
				
			||||||
                </el-row>
 | 
					                </el-row>
 | 
				
			||||||
            </el-form>
 | 
					            </el-form>
 | 
				
			||||||
            e
 | 
					
 | 
				
			||||||
            <template #footer>
 | 
					            <template #footer>
 | 
				
			||||||
                <div>
 | 
					                <div>
 | 
				
			||||||
                    <el-button @click="cancel()">取 消</el-button>
 | 
					                    <el-button @click="cancel()">取 消</el-button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ require (
 | 
				
			|||||||
	github.com/pquerna/otp v1.4.0
 | 
						github.com/pquerna/otp v1.4.0
 | 
				
			||||||
	github.com/redis/go-redis/v9 v9.5.1
 | 
						github.com/redis/go-redis/v9 v9.5.1
 | 
				
			||||||
	github.com/robfig/cron/v3 v3.0.1 // 定时任务
 | 
						github.com/robfig/cron/v3 v3.0.1 // 定时任务
 | 
				
			||||||
	github.com/sijms/go-ora/v2 v2.8.12
 | 
						github.com/sijms/go-ora/v2 v2.8.13
 | 
				
			||||||
	github.com/stretchr/testify v1.8.4
 | 
						github.com/stretchr/testify v1.8.4
 | 
				
			||||||
	github.com/veops/go-ansiterm v0.0.5
 | 
						github.com/veops/go-ansiterm v0.0.5
 | 
				
			||||||
	go.mongodb.org/mongo-driver v1.15.0 // mongo
 | 
						go.mongodb.org/mongo-driver v1.15.0 // mongo
 | 
				
			||||||
@@ -39,7 +39,7 @@ require (
 | 
				
			|||||||
	gopkg.in/yaml.v3 v3.0.1
 | 
						gopkg.in/yaml.v3 v3.0.1
 | 
				
			||||||
	// gorm
 | 
						// gorm
 | 
				
			||||||
	gorm.io/driver/mysql v1.5.6
 | 
						gorm.io/driver/mysql v1.5.6
 | 
				
			||||||
	gorm.io/gorm v1.25.9
 | 
						gorm.io/gorm v1.25.10
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,4 @@ const (
 | 
				
			|||||||
	ResourceTypeDb      int8 = 2
 | 
						ResourceTypeDb      int8 = 2
 | 
				
			||||||
	ResourceTypeRedis   int8 = 3
 | 
						ResourceTypeRedis   int8 = 3
 | 
				
			||||||
	ResourceTypeMongo   int8 = 4
 | 
						ResourceTypeMongo   int8 = 4
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 删除机器的事件主题名
 | 
					 | 
				
			||||||
	DeleteMachineEventTopic = "machine:delete"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,11 +12,13 @@ import (
 | 
				
			|||||||
	"mayfly-go/internal/db/config"
 | 
						"mayfly-go/internal/db/config"
 | 
				
			||||||
	"mayfly-go/internal/db/dbm/dbi"
 | 
						"mayfly-go/internal/db/dbm/dbi"
 | 
				
			||||||
	"mayfly-go/internal/db/domain/entity"
 | 
						"mayfly-go/internal/db/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/internal/event"
 | 
				
			||||||
	msgapp "mayfly-go/internal/msg/application"
 | 
						msgapp "mayfly-go/internal/msg/application"
 | 
				
			||||||
	msgdto "mayfly-go/internal/msg/application/dto"
 | 
						msgdto "mayfly-go/internal/msg/application/dto"
 | 
				
			||||||
	tagapp "mayfly-go/internal/tag/application"
 | 
						tagapp "mayfly-go/internal/tag/application"
 | 
				
			||||||
	tagentity "mayfly-go/internal/tag/domain/entity"
 | 
						tagentity "mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
	"mayfly-go/pkg/biz"
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/global"
 | 
				
			||||||
	"mayfly-go/pkg/logx"
 | 
						"mayfly-go/pkg/logx"
 | 
				
			||||||
	"mayfly-go/pkg/model"
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
	"mayfly-go/pkg/req"
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
@@ -97,6 +99,8 @@ func (d *Db) ExecSql(rc *req.Ctx) {
 | 
				
			|||||||
	biz.ErrIsNil(err)
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
	biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
 | 
						biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, dbConn.Info.TagPath[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sqlBytes, err := base64.StdEncoding.DecodeString(form.Sql)
 | 
						sqlBytes, err := base64.StdEncoding.DecodeString(form.Sql)
 | 
				
			||||||
	biz.ErrIsNilAppendErr(err, "sql解码失败: %s")
 | 
						biz.ErrIsNilAppendErr(err, "sql解码失败: %s")
 | 
				
			||||||
	// 去除前后空格及换行符
 | 
						// 去除前后空格及换行符
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ type InstanceDbNamesForm struct {
 | 
				
			|||||||
	Host               string                      `binding:"required" json:"host"`
 | 
						Host               string                      `binding:"required" json:"host"`
 | 
				
			||||||
	Port               int                         `json:"port"`
 | 
						Port               int                         `json:"port"`
 | 
				
			||||||
	Params             string                      `json:"params"`
 | 
						Params             string                      `json:"params"`
 | 
				
			||||||
 | 
						Extra              string                      `json:"extra"`
 | 
				
			||||||
	SshTunnelMachineId int                         `json:"sshTunnelMachineId"`
 | 
						SshTunnelMachineId int                         `json:"sshTunnelMachineId"`
 | 
				
			||||||
	AuthCert           *tagentity.ResourceAuthCert `json:"authCert" binding:"required"` // 资产授权凭证信息
 | 
						AuthCert           *tagentity.ResourceAuthCert `json:"authCert" binding:"required"` // 资产授权凭证信息
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								server/internal/event/topic.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								server/internal/event/topic.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					package event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						EventTopicDeleteMachine = "machine:delete" // 删除机器的事件主题名
 | 
				
			||||||
 | 
						EventTopicResourceOp    = "resource:op"    // 资源操作主题
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -44,3 +44,14 @@ type MachineCronJobForm struct {
 | 
				
			|||||||
	MachineIds      []uint64 `json:"machineIds"`
 | 
						MachineIds      []uint64 `json:"machineIds"`
 | 
				
			||||||
	Remark          string   `json:"remark"`
 | 
						Remark          string   `json:"remark"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MachineCmdConfForm struct {
 | 
				
			||||||
 | 
						Id       uint64   `json:"id"`
 | 
				
			||||||
 | 
						Name     string   `json:"name"`
 | 
				
			||||||
 | 
						Cmds     []string `json:"cmds"`     // 命令配置
 | 
				
			||||||
 | 
						Status   int8     `json:"execCmds"` // 状态
 | 
				
			||||||
 | 
						Stratege string   `json:"stratege"` // 策略,空禁用
 | 
				
			||||||
 | 
						Remark   string   `json:"remark"`   // 备注
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CodePaths []string `json:"codePaths"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/internal/common/consts"
 | 
						"mayfly-go/internal/common/consts"
 | 
				
			||||||
 | 
						"mayfly-go/internal/event"
 | 
				
			||||||
	"mayfly-go/internal/machine/api/form"
 | 
						"mayfly-go/internal/machine/api/form"
 | 
				
			||||||
	"mayfly-go/internal/machine/api/vo"
 | 
						"mayfly-go/internal/machine/api/vo"
 | 
				
			||||||
	"mayfly-go/internal/machine/application"
 | 
						"mayfly-go/internal/machine/application"
 | 
				
			||||||
@@ -15,6 +16,7 @@ import (
 | 
				
			|||||||
	tagentity "mayfly-go/internal/tag/domain/entity"
 | 
						tagentity "mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
	"mayfly-go/pkg/biz"
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
	"mayfly-go/pkg/errorx"
 | 
						"mayfly-go/pkg/errorx"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/global"
 | 
				
			||||||
	"mayfly-go/pkg/logx"
 | 
						"mayfly-go/pkg/logx"
 | 
				
			||||||
	"mayfly-go/pkg/model"
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
	"mayfly-go/pkg/req"
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
@@ -192,7 +194,9 @@ func (m *Machine) WsSSH(g *gin.Context) {
 | 
				
			|||||||
	cli, err := m.MachineApp.NewCli(GetMachineAc(rc))
 | 
						cli, err := m.MachineApp.NewCli(GetMachineAc(rc))
 | 
				
			||||||
	biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("获取客户端连接失败: %s"))
 | 
						biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("获取客户端连接失败: %s"))
 | 
				
			||||||
	defer cli.Close()
 | 
						defer cli.Close()
 | 
				
			||||||
	biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
 | 
						biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), mcm.GetErrorContentRn("%s"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, cli.Info.TagPath[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cols := rc.QueryIntDefault("cols", 80)
 | 
						cols := rc.QueryIntDefault("cols", 80)
 | 
				
			||||||
	rows := rc.QueryIntDefault("rows", 32)
 | 
						rows := rc.QueryIntDefault("rows", 32)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										49
									
								
								server/internal/machine/api/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								server/internal/machine/api/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/api/form"
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/api/vo"
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/application"
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/domain/entity"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagapp "mayfly-go/internal/tag/application"
 | 
				
			||||||
 | 
						tagentity "mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/utils/collx"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MachineCmdConf struct {
 | 
				
			||||||
 | 
						MachineCmdConfApp application.MachineCmdConf `inject:""`
 | 
				
			||||||
 | 
						TagTreeRelateApp  tagapp.TagTreeRelate       `inject:"TagTreeRelateApp"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *MachineCmdConf) MachineCmdConfs(rc *req.Ctx) {
 | 
				
			||||||
 | 
						cond := req.BindQuery(rc, new(entity.MachineCmdConf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var vos []*vo.MachineCmdConfVO
 | 
				
			||||||
 | 
						err := m.MachineCmdConfApp.ListByCond(cond, &vos)
 | 
				
			||||||
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m.TagTreeRelateApp.FillTagInfo(tagentity.TagRelateTypeMachineCmd, collx.ArrayMap(vos, func(mvo *vo.MachineCmdConfVO) tagentity.IRelateTag {
 | 
				
			||||||
 | 
							return mvo
 | 
				
			||||||
 | 
						})...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc.ResData = vos
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *MachineCmdConf) Save(rc *req.Ctx) {
 | 
				
			||||||
 | 
						cmdForm := new(form.MachineCmdConfForm)
 | 
				
			||||||
 | 
						mcj := req.BindJsonAndCopyTo[*entity.MachineCmdConf](rc, cmdForm, new(entity.MachineCmdConf))
 | 
				
			||||||
 | 
						rc.ReqParam = cmdForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := m.MachineCmdConfApp.SaveCmdConf(rc.MetaCtx, &application.SaveMachineCmdConfParam{
 | 
				
			||||||
 | 
							CmdConf:   mcj,
 | 
				
			||||||
 | 
							CodePaths: cmdForm.CodePaths,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *MachineCmdConf) Delete(rc *req.Ctx) {
 | 
				
			||||||
 | 
						m.MachineCmdConfApp.DeleteCmdConf(rc.MetaCtx, uint64(rc.PathParamInt("id")))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,6 +2,7 @@ package vo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	tagentity "mayfly-go/internal/tag/domain/entity"
 | 
						tagentity "mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,3 +86,18 @@ func (s MachineFileInfos) Less(i, j int) bool {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return s[i].Name < s[j].Name
 | 
						return s[i].Name < s[j].Name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MachineCmdConfVO struct {
 | 
				
			||||||
 | 
						tagentity.RelateTags // 标签信息
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Name     string              `json:"name"`
 | 
				
			||||||
 | 
						Cmds     model.Slice[string] `json:"cmds"`     // 命令配置
 | 
				
			||||||
 | 
						Status   int8                `json:"execCmds"` // 状态
 | 
				
			||||||
 | 
						Stratege string              `json:"stratege"` // 策略,空禁用
 | 
				
			||||||
 | 
						Remark   string              `json:"remark"`   // 备注
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mcc *MachineCmdConfVO) GetRelateId() uint64 {
 | 
				
			||||||
 | 
						return mcc.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ func InitIoc() {
 | 
				
			|||||||
	ioc.Register(new(machineScriptAppImpl), ioc.WithComponentName("MachineScriptApp"))
 | 
						ioc.Register(new(machineScriptAppImpl), ioc.WithComponentName("MachineScriptApp"))
 | 
				
			||||||
	ioc.Register(new(machineCronJobAppImpl), ioc.WithComponentName("MachineCronJobApp"))
 | 
						ioc.Register(new(machineCronJobAppImpl), ioc.WithComponentName("MachineCronJobApp"))
 | 
				
			||||||
	ioc.Register(new(machineTermOpAppImpl), ioc.WithComponentName("MachineTermOpApp"))
 | 
						ioc.Register(new(machineTermOpAppImpl), ioc.WithComponentName("MachineTermOpApp"))
 | 
				
			||||||
 | 
						ioc.Register(new(machineCmdConfAppImpl), ioc.WithComponentName("MachineCmdConfApp"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetMachineApp() Machine {
 | 
					func GetMachineApp() Machine {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ package application
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/internal/common/consts"
 | 
						"mayfly-go/internal/event"
 | 
				
			||||||
	"mayfly-go/internal/machine/api/vo"
 | 
						"mayfly-go/internal/machine/api/vo"
 | 
				
			||||||
	"mayfly-go/internal/machine/domain/entity"
 | 
						"mayfly-go/internal/machine/domain/entity"
 | 
				
			||||||
	"mayfly-go/internal/machine/domain/repository"
 | 
						"mayfly-go/internal/machine/domain/repository"
 | 
				
			||||||
@@ -210,7 +210,7 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
 | 
				
			|||||||
	mcm.DeleteCli(id)
 | 
						mcm.DeleteCli(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 发布机器删除事件
 | 
						// 发布机器删除事件
 | 
				
			||||||
	global.EventBus.Publish(ctx, consts.DeleteMachineEventTopic, machine)
 | 
						global.EventBus.Publish(ctx, event.EventTopicDeleteMachine, machine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resourceType := tagentity.TagTypeMachine
 | 
						resourceType := tagentity.TagTypeMachine
 | 
				
			||||||
	return m.Tx(ctx,
 | 
						return m.Tx(ctx,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								server/internal/machine/application/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								server/internal/machine/application/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					package application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/domain/repository"
 | 
				
			||||||
 | 
						tagapp "mayfly-go/internal/tag/application"
 | 
				
			||||||
 | 
						tagentity "mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/errorx"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/logx"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SaveMachineCmdConfParam struct {
 | 
				
			||||||
 | 
						CmdConf   *entity.MachineCmdConf
 | 
				
			||||||
 | 
						CodePaths []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MachineCmd struct {
 | 
				
			||||||
 | 
						CmdRegexp *regexp.Regexp // 命令正则表达式
 | 
				
			||||||
 | 
						Stratege  string         // 策略(拒绝或审批等)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MachineCmdConf interface {
 | 
				
			||||||
 | 
						base.App[*entity.MachineCmdConf]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SaveCmdConf(ctx context.Context, cmdConf *SaveMachineCmdConfParam) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DeleteCmdConf(ctx context.Context, id uint64) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GetCmdConfsByMachineTags(tagPaths ...string) []*MachineCmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type machineCmdConfAppImpl struct {
 | 
				
			||||||
 | 
						base.AppImpl[*entity.MachineCmdConf, repository.MachineCmdConf]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagTreeRelateApp tagapp.TagTreeRelate `inject:"TagTreeRelateApp"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ (MachineCmdConf) = (*machineCmdConfAppImpl)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 注入MachineCmdConfRepo
 | 
				
			||||||
 | 
					func (m *machineCmdConfAppImpl) InjectMachineCmdConfRepo(repo repository.MachineCmdConf) {
 | 
				
			||||||
 | 
						m.Repo = repo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *machineCmdConfAppImpl) SaveCmdConf(ctx context.Context, cmdConfParam *SaveMachineCmdConfParam) error {
 | 
				
			||||||
 | 
						cmdConf := cmdConfParam.CmdConf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return m.Tx(ctx, func(ctx context.Context) error {
 | 
				
			||||||
 | 
							return m.Save(ctx, cmdConf)
 | 
				
			||||||
 | 
						}, func(ctx context.Context) error {
 | 
				
			||||||
 | 
							return m.tagTreeRelateApp.RelateTag(ctx, tagentity.TagRelateTypeMachineCmd, cmdConf.Id, cmdConfParam.CodePaths...)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *machineCmdConfAppImpl) DeleteCmdConf(ctx context.Context, id uint64) error {
 | 
				
			||||||
 | 
						_, err := m.GetById(new(entity.MachineCmdConf), id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return errorx.NewBiz("该命令配置不存在")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return m.Tx(ctx, func(ctx context.Context) error {
 | 
				
			||||||
 | 
							return m.DeleteById(ctx, id)
 | 
				
			||||||
 | 
						}, func(ctx context.Context) error {
 | 
				
			||||||
 | 
							return m.tagTreeRelateApp.DeleteByCond(ctx, &tagentity.TagTreeRelate{
 | 
				
			||||||
 | 
								RelateType: tagentity.TagRelateTypeMachineCmd,
 | 
				
			||||||
 | 
								RelateId:   id,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *machineCmdConfAppImpl) GetCmdConfsByMachineTags(tagPaths ...string) []*MachineCmd {
 | 
				
			||||||
 | 
						var cmds []*MachineCmd
 | 
				
			||||||
 | 
						cmdConfIds, err := m.tagTreeRelateApp.GetRelateIds(tagentity.TagRelateTypeMachineCmd, tagPaths...)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logx.Errorf("获取命令配置信息失败: %s", err.Error())
 | 
				
			||||||
 | 
							return cmds
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(cmdConfIds) == 0 {
 | 
				
			||||||
 | 
							return cmds
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var cmdConfs []*entity.MachineCmdConf
 | 
				
			||||||
 | 
						m.GetByIdIn(&cmdConfs, cmdConfIds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, cmdConf := range cmdConfs {
 | 
				
			||||||
 | 
							for _, cmd := range cmdConf.Cmds {
 | 
				
			||||||
 | 
								if p, err := regexp.Compile(cmd); err != nil {
 | 
				
			||||||
 | 
									logx.Errorf("命令配置[%s],正则编译失败", cmd)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									cmds = append(cmds, &MachineCmd{CmdRegexp: p})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cmds
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -36,6 +36,8 @@ type MachineTermOp interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type machineTermOpAppImpl struct {
 | 
					type machineTermOpAppImpl struct {
 | 
				
			||||||
	base.AppImpl[*entity.MachineTermOp, repository.MachineTermOp]
 | 
						base.AppImpl[*entity.MachineTermOp, repository.MachineTermOp]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						machineCmdConfApp MachineCmdConf `inject:"MachineCmdConfApp"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 注入MachineTermOpRepo
 | 
					// 注入MachineTermOpRepo
 | 
				
			||||||
@@ -87,12 +89,17 @@ func (m *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsCon
 | 
				
			|||||||
		LogCmd:    cli.Info.EnableRecorder == 1,
 | 
							LogCmd:    cli.Info.EnableRecorder == 1,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// createTsParam.CmdFilterFuncs = []mcm.CmdFilterFunc{func(cmd string) error {
 | 
						cmdConfs := m.machineCmdConfApp.GetCmdConfsByMachineTags(cli.Info.TagPath...)
 | 
				
			||||||
	// 	if strings.HasPrefix(cmd, "rm") {
 | 
						if len(cmdConfs) > 0 {
 | 
				
			||||||
	// 		return errorx.NewBiz("该命令已被禁用...")
 | 
							createTsParam.CmdFilterFuncs = []mcm.CmdFilterFunc{func(cmd string) error {
 | 
				
			||||||
	// 	}
 | 
								for _, cmdConf := range cmdConfs {
 | 
				
			||||||
	// 	return nil
 | 
									if cmdConf.CmdRegexp.Match([]byte(cmd)) {
 | 
				
			||||||
	// }}
 | 
										return errorx.NewBiz("该命令已被禁用...")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mts, err := mcm.NewTerminalSession(createTsParam)
 | 
						mts, err := mcm.NewTerminalSession(createTsParam)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								server/internal/machine/domain/entity/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								server/internal/machine/domain/entity/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 机器命令过滤配置
 | 
				
			||||||
 | 
					type MachineCmdConf struct {
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Name     string              `json:"name"`
 | 
				
			||||||
 | 
						Cmds     model.Slice[string] `json:"cmds"`     // 命令配置
 | 
				
			||||||
 | 
						Status   int8                `json:"execCmds"` // 状态
 | 
				
			||||||
 | 
						Stratege string              `json:"stratege"` // 策略,空禁用
 | 
				
			||||||
 | 
						Remark   string              `json:"remark"`   // 备注
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MachineCmdConf interface {
 | 
				
			||||||
 | 
						base.Repo[*entity.MachineCmdConf]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package persistence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/domain/repository"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type machineCmdConfRepoImpl struct {
 | 
				
			||||||
 | 
						base.RepoImpl[*entity.MachineCmdConf]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newMachineCmdConfRepo() repository.MachineCmdConf {
 | 
				
			||||||
 | 
						return &machineCmdConfRepoImpl{base.RepoImpl[*entity.MachineCmdConf]{M: new(entity.MachineCmdConf)}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -12,4 +12,5 @@ func InitIoc() {
 | 
				
			|||||||
	ioc.Register(newMachineCronJobExecRepo(), ioc.WithComponentName("MachineCronJobExecRepo"))
 | 
						ioc.Register(newMachineCronJobExecRepo(), ioc.WithComponentName("MachineCronJobExecRepo"))
 | 
				
			||||||
	ioc.Register(newMachineCronJobRelateRepo(), ioc.WithComponentName("MachineCronJobRelateRepo"))
 | 
						ioc.Register(newMachineCronJobRelateRepo(), ioc.WithComponentName("MachineCronJobRelateRepo"))
 | 
				
			||||||
	ioc.Register(newMachineTermOpRepoImpl(), ioc.WithComponentName("MachineTermOpRepo"))
 | 
						ioc.Register(newMachineTermOpRepoImpl(), ioc.WithComponentName("MachineTermOpRepo"))
 | 
				
			||||||
 | 
						ioc.Register(newMachineCmdConfRepo(), ioc.WithComponentName("MachineCmdConfRepo"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ package init
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"mayfly-go/initialize"
 | 
						"mayfly-go/initialize"
 | 
				
			||||||
	"mayfly-go/internal/common/consts"
 | 
						"mayfly-go/internal/event"
 | 
				
			||||||
	"mayfly-go/internal/machine/application"
 | 
						"mayfly-go/internal/machine/application"
 | 
				
			||||||
	"mayfly-go/internal/machine/domain/entity"
 | 
						"mayfly-go/internal/machine/domain/entity"
 | 
				
			||||||
	"mayfly-go/internal/machine/infrastructure/persistence"
 | 
						"mayfly-go/internal/machine/infrastructure/persistence"
 | 
				
			||||||
@@ -28,17 +28,17 @@ func Init() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	application.GetMachineTermOpApp().TimerDeleteTermOp()
 | 
						application.GetMachineTermOpApp().TimerDeleteTermOp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineFile", func(ctx context.Context, event *eventbus.Event) error {
 | 
						global.EventBus.Subscribe(event.EventTopicDeleteMachine, "machineFile", func(ctx context.Context, event *eventbus.Event) error {
 | 
				
			||||||
		me := event.Val.(*entity.Machine)
 | 
							me := event.Val.(*entity.Machine)
 | 
				
			||||||
		return application.GetMachineFileApp().DeleteByCond(ctx, &entity.MachineFile{MachineId: me.Id})
 | 
							return application.GetMachineFileApp().DeleteByCond(ctx, &entity.MachineFile{MachineId: me.Id})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineScript", func(ctx context.Context, event *eventbus.Event) error {
 | 
						global.EventBus.Subscribe(event.EventTopicDeleteMachine, "machineScript", func(ctx context.Context, event *eventbus.Event) error {
 | 
				
			||||||
		me := event.Val.(*entity.Machine)
 | 
							me := event.Val.(*entity.Machine)
 | 
				
			||||||
		return application.GetMachineScriptApp().DeleteByCond(ctx, &entity.MachineScript{MachineId: me.Id})
 | 
							return application.GetMachineScriptApp().DeleteByCond(ctx, &entity.MachineScript{MachineId: me.Id})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineCronJob", func(ctx context.Context, event *eventbus.Event) error {
 | 
						global.EventBus.Subscribe(event.EventTopicDeleteMachine, "machineCronJob", func(ctx context.Context, event *eventbus.Event) error {
 | 
				
			||||||
		me := event.Val.(*entity.Machine)
 | 
							me := event.Val.(*entity.Machine)
 | 
				
			||||||
		var jobIds []uint64
 | 
							var jobIds []uint64
 | 
				
			||||||
		application.GetMachineCronJobApp().MachineRelateCronJobs(ctx, me.Id, jobIds)
 | 
							application.GetMachineCronJobApp().MachineRelateCronJobs(ctx, me.Id, jobIds)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								server/internal/machine/router/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server/internal/machine/router/machine_cmd_conf.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					package router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/machine/api"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/ioc"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InitMachineCmdConfRouter(router *gin.RouterGroup) {
 | 
				
			||||||
 | 
						mccs := router.Group("machine/security/cmd-confs")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mcc := new(api.MachineCmdConf)
 | 
				
			||||||
 | 
						biz.ErrIsNil(ioc.Inject(mcc))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reqs := [...]*req.Conf{
 | 
				
			||||||
 | 
							req.NewGet("", mcc.MachineCmdConfs),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req.NewPost("", mcc.Save).Log(req.NewLogSave("机器命令配置-保存")).RequiredPermissionCode("cmdconf:save"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req.NewDelete(":id", mcc.Delete).Log(req.NewLogSave("机器命令配置-删除")).RequiredPermissionCode("cmdconf:del"),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req.BatchSetGroup(mccs, reqs[:])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,4 +7,5 @@ func Init(router *gin.RouterGroup) {
 | 
				
			|||||||
	InitMachineFileRouter(router)
 | 
						InitMachineFileRouter(router)
 | 
				
			||||||
	InitMachineScriptRouter(router)
 | 
						InitMachineScriptRouter(router)
 | 
				
			||||||
	InitMachineCronJobRouter(router)
 | 
						InitMachineCronJobRouter(router)
 | 
				
			||||||
 | 
						InitMachineCmdConfRouter(router)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ package api
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"mayfly-go/internal/common/consts"
 | 
						"mayfly-go/internal/common/consts"
 | 
				
			||||||
 | 
						"mayfly-go/internal/event"
 | 
				
			||||||
	"mayfly-go/internal/mongo/api/form"
 | 
						"mayfly-go/internal/mongo/api/form"
 | 
				
			||||||
	"mayfly-go/internal/mongo/api/vo"
 | 
						"mayfly-go/internal/mongo/api/vo"
 | 
				
			||||||
	"mayfly-go/internal/mongo/application"
 | 
						"mayfly-go/internal/mongo/application"
 | 
				
			||||||
@@ -10,6 +11,7 @@ import (
 | 
				
			|||||||
	tagapp "mayfly-go/internal/tag/application"
 | 
						tagapp "mayfly-go/internal/tag/application"
 | 
				
			||||||
	tagentity "mayfly-go/internal/tag/domain/entity"
 | 
						tagentity "mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
	"mayfly-go/pkg/biz"
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/global"
 | 
				
			||||||
	"mayfly-go/pkg/model"
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
	"mayfly-go/pkg/req"
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
	"mayfly-go/pkg/utils/collx"
 | 
						"mayfly-go/pkg/utils/collx"
 | 
				
			||||||
@@ -93,6 +95,9 @@ func (m *Mongo) Databases(rc *req.Ctx) {
 | 
				
			|||||||
func (m *Mongo) Collections(rc *req.Ctx) {
 | 
					func (m *Mongo) Collections(rc *req.Ctx) {
 | 
				
			||||||
	conn, err := m.MongoApp.GetMongoConn(m.GetMongoId(rc))
 | 
						conn, err := m.MongoApp.GetMongoConn(m.GetMongoId(rc))
 | 
				
			||||||
	biz.ErrIsNil(err)
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, conn.Info.TagPath[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := rc.Query("database")
 | 
						db := rc.Query("database")
 | 
				
			||||||
	biz.NotEmpty(db, "database不能为空")
 | 
						biz.NotEmpty(db, "database不能为空")
 | 
				
			||||||
	ctx := context.TODO()
 | 
						ctx := context.TODO()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
package api
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/event"
 | 
				
			||||||
	"mayfly-go/internal/redis/api/form"
 | 
						"mayfly-go/internal/redis/api/form"
 | 
				
			||||||
	"mayfly-go/internal/redis/application"
 | 
						"mayfly-go/internal/redis/application"
 | 
				
			||||||
	"mayfly-go/pkg/biz"
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/global"
 | 
				
			||||||
	"mayfly-go/pkg/req"
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
	"mayfly-go/pkg/utils/collx"
 | 
						"mayfly-go/pkg/utils/collx"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -14,8 +16,11 @@ func (r *Redis) RunCmd(rc *req.Ctx) {
 | 
				
			|||||||
	biz.IsTrue(len(cmdReq.Cmd) > 0, "redis命令不能为空")
 | 
						biz.IsTrue(len(cmdReq.Cmd) > 0, "redis命令不能为空")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	redisConn := r.getRedisConn(rc)
 | 
						redisConn := r.getRedisConn(rc)
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, redisConn.Info.TagPath...), "%s")
 | 
				
			||||||
	rc.ReqParam = collx.Kvs("redis", redisConn.Info, "cmd", cmdReq.Cmd)
 | 
						rc.ReqParam = collx.Kvs("redis", redisConn.Info, "cmd", cmdReq.Cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, redisConn.Info.TagPath[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := r.RedisApp.RunCmd(rc.MetaCtx, redisConn, runCmdParam)
 | 
						res, err := r.RedisApp.RunCmd(rc.MetaCtx, redisConn, runCmdParam)
 | 
				
			||||||
	biz.ErrIsNil(err)
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
	rc.ResData = res
 | 
						rc.ResData = res
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								server/internal/tag/api/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								server/internal/tag/api/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/application"
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ResourceOpLog struct {
 | 
				
			||||||
 | 
						ResourceOpLogApp application.ResourceOpLog `inject:""`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *ResourceOpLog) PageAccountOpLog(rc *req.Ctx) {
 | 
				
			||||||
 | 
						cond := new(entity.ResourceOpLog)
 | 
				
			||||||
 | 
						cond.ResourceCode = rc.Query("resourceCode")
 | 
				
			||||||
 | 
						cond.ResourceType = int8(rc.QueryInt("resourceType"))
 | 
				
			||||||
 | 
						cond.CreatorId = rc.GetLoginAccount().Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var rols []*entity.ResourceOpLog
 | 
				
			||||||
 | 
						res, err := r.ResourceOpLogApp.PageQuery(cond, rc.GetPageParam(), &rols)
 | 
				
			||||||
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
 | 
						rc.ResData = res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -15,7 +15,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TagTree struct {
 | 
					type TagTree struct {
 | 
				
			||||||
	TagTreeApp application.TagTree `inject:""`
 | 
						TagTreeApp       application.TagTree       `inject:""`
 | 
				
			||||||
 | 
						TagTreeRelateApp application.TagTreeRelate `inject:""`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *TagTree) GetTagTree(rc *req.Ctx) {
 | 
					func (p *TagTree) GetTagTree(rc *req.Ctx) {
 | 
				
			||||||
@@ -123,3 +124,8 @@ func (p *TagTree) CountTagResource(rc *req.Ctx) {
 | 
				
			|||||||
		"mongo":   len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)),
 | 
							"mongo":   len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取关联的标签id
 | 
				
			||||||
 | 
					func (p *TagTree) GetRelateTagIds(rc *req.Ctx) {
 | 
				
			||||||
 | 
						rc.ResData = p.TagTreeRelateApp.GetTagPathsByRelate(entity.TagRelateType(rc.PathParamInt("relateType")), uint64(rc.PathParamInt("relateId")))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,22 +10,29 @@ import (
 | 
				
			|||||||
	"mayfly-go/internal/tag/domain/entity"
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
	"mayfly-go/pkg/biz"
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
	"mayfly-go/pkg/req"
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/utils/collx"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/may-fly/cast"
 | 
						"github.com/may-fly/cast"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Team struct {
 | 
					type Team struct {
 | 
				
			||||||
	TeamApp    application.Team        `inject:""`
 | 
						TeamApp          application.Team          `inject:""`
 | 
				
			||||||
	TagTreeApp application.TagTree     `inject:""`
 | 
						TagTreeApp       application.TagTree       `inject:""`
 | 
				
			||||||
	AccountApp sys_applicaiton.Account `inject:""`
 | 
						TagTreeRelateApp application.TagTreeRelate `inject:""`
 | 
				
			||||||
 | 
						AccountApp       sys_applicaiton.Account   `inject:""`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Team) GetTeams(rc *req.Ctx) {
 | 
					func (p *Team) GetTeams(rc *req.Ctx) {
 | 
				
			||||||
	queryCond, page := req.BindQueryAndPage(rc, new(entity.TeamQuery))
 | 
						queryCond, page := req.BindQueryAndPage(rc, new(entity.TeamQuery))
 | 
				
			||||||
	teams := &[]entity.Team{}
 | 
						var teams []*vo.Team
 | 
				
			||||||
	res, err := p.TeamApp.GetPageList(queryCond, page, teams)
 | 
						res, err := p.TeamApp.GetPageList(queryCond, page, &teams)
 | 
				
			||||||
	biz.ErrIsNil(err)
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.TagTreeRelateApp.FillTagInfo(entity.TagRelateTypeTeam, collx.ArrayMap(teams, func(mvo *vo.Team) entity.IRelateTag {
 | 
				
			||||||
 | 
							return mvo
 | 
				
			||||||
 | 
						})...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc.ResData = res
 | 
						rc.ResData = res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -89,8 +96,3 @@ func (p *Team) DelTeamMember(rc *req.Ctx) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	p.TeamApp.DeleteMember(rc.MetaCtx, uint64(tid), uint64(aid))
 | 
						p.TeamApp.DeleteMember(rc.MetaCtx, uint64(tid), uint64(aid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// 获取团队关联的标签id
 | 
					 | 
				
			||||||
func (p *Team) GetTagIds(rc *req.Ctx) {
 | 
					 | 
				
			||||||
	rc.ResData = p.TeamApp.ListTagIds(uint64(rc.PathParamInt("id")))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,22 @@
 | 
				
			|||||||
package vo
 | 
					package vo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "time"
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Team struct {
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
						entity.RelateTags // 标签信息
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Name   string `json:"name"`   // 名称
 | 
				
			||||||
 | 
						Remark string `json:"remark"` // 备注说明
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Team) GetRelateId() uint64 {
 | 
				
			||||||
 | 
						return t.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 团队成员信息
 | 
					// 团队成员信息
 | 
				
			||||||
type TeamMember struct {
 | 
					type TeamMember struct {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,4 +8,10 @@ func InitIoc() {
 | 
				
			|||||||
	ioc.Register(new(tagTreeAppImpl), ioc.WithComponentName("TagTreeApp"))
 | 
						ioc.Register(new(tagTreeAppImpl), ioc.WithComponentName("TagTreeApp"))
 | 
				
			||||||
	ioc.Register(new(teamAppImpl), ioc.WithComponentName("TeamApp"))
 | 
						ioc.Register(new(teamAppImpl), ioc.WithComponentName("TeamApp"))
 | 
				
			||||||
	ioc.Register(new(resourceAuthCertAppImpl), ioc.WithComponentName("ResourceAuthCertApp"))
 | 
						ioc.Register(new(resourceAuthCertAppImpl), ioc.WithComponentName("ResourceAuthCertApp"))
 | 
				
			||||||
 | 
						ioc.Register(new(resourceOpLogAppImpl), ioc.WithComponentName("ResourceOpLogApp"))
 | 
				
			||||||
 | 
						ioc.Register(new(tagTreeRelateAppImpl), ioc.WithComponentName("TagTreeRelateApp"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetResourceOpLogApp() ResourceOpLog {
 | 
				
			||||||
 | 
						return ioc.Get[ResourceOpLog]("ResourceOpLogApp")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								server/internal/tag/application/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								server/internal/tag/application/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					package application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/repository"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/contextx"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/errorx"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/utils/collx"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ResourceOpLog interface {
 | 
				
			||||||
 | 
						base.App[*entity.ResourceOpLog]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// AddResourceOpLog 新增资源操作记录
 | 
				
			||||||
 | 
						AddResourceOpLog(ctx context.Context, codePath string) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type resourceOpLogAppImpl struct {
 | 
				
			||||||
 | 
						base.AppImpl[*entity.ResourceOpLog, repository.ResourceOpLog]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagTreeApp TagTree `inject:"TagTreeApp"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ (ResourceOpLog) = (*resourceOpLogAppImpl)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 注入ResourceOpLogRepo
 | 
				
			||||||
 | 
					func (rol *resourceOpLogAppImpl) InjectResourceOpLogRepo(resourceOpLogRepo repository.ResourceOpLog) {
 | 
				
			||||||
 | 
						rol.Repo = resourceOpLogRepo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (rol *resourceOpLogAppImpl) AddResourceOpLog(ctx context.Context, codePath string) error {
 | 
				
			||||||
 | 
						loginAccount := contextx.GetLoginAccount(ctx)
 | 
				
			||||||
 | 
						if loginAccount == nil {
 | 
				
			||||||
 | 
							return errorx.NewBiz("当前上下文不存在登录信息")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var logs []*entity.ResourceOpLog
 | 
				
			||||||
 | 
						if err := rol.ListByWheres(collx.Kvs("create_time > ?", time.Now().Add(-5*time.Minute), "creator_id = ?", loginAccount.Id, "code_path = ?", codePath), &logs); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 指定时间内多次操作则不记录
 | 
				
			||||||
 | 
						if len(logs) > 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tagTree := &entity.TagTree{CodePath: codePath}
 | 
				
			||||||
 | 
						if err := rol.tagTreeApp.GetBy(tagTree); err != nil {
 | 
				
			||||||
 | 
							return errorx.NewBiz("资源不存在")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rol.Save(ctx, &entity.ResourceOpLog{
 | 
				
			||||||
 | 
							ResourceCode: tagTree.Code,
 | 
				
			||||||
 | 
							ResourceType: int8(tagTree.Type),
 | 
				
			||||||
 | 
							CodePath:     tagTree.CodePath,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -99,7 +99,7 @@ type TagTree interface {
 | 
				
			|||||||
type tagTreeAppImpl struct {
 | 
					type tagTreeAppImpl struct {
 | 
				
			||||||
	base.AppImpl[*entity.TagTree, repository.TagTree]
 | 
						base.AppImpl[*entity.TagTree, repository.TagTree]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tagTreeTeamRepo repository.TagTreeTeam `inject:"TagTreeTeamRepo"`
 | 
						tagTreeRelateApp TagTreeRelate `inject:"TagTreeRelateApp"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 注入TagTreeRepo
 | 
					// 注入TagTreeRepo
 | 
				
			||||||
@@ -450,7 +450,7 @@ func (p *tagTreeAppImpl) ListTagPathByTypeAndCode(resourceType int8, resourceCod
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string {
 | 
					func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string {
 | 
				
			||||||
	return p.tagTreeTeamRepo.SelectTagPathsByAccountId(accountId)
 | 
						return p.tagTreeRelateApp.GetTagPathsByAccountId(accountId)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath ...string) error {
 | 
					func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath ...string) error {
 | 
				
			||||||
@@ -486,7 +486,7 @@ func (p *tagTreeAppImpl) FillTagInfo(resourceTagType entity.TagType, resources .
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, tr := range tagResources {
 | 
						for _, tr := range tagResources {
 | 
				
			||||||
		// 赋值标签信息
 | 
							// 赋值标签信息
 | 
				
			||||||
		resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{CodePath: tr.GetTagPath()})
 | 
							resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{TagId: tr.Id, CodePath: tr.GetTagPath()})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -552,6 +552,8 @@ func (p *tagTreeAppImpl) deleteByIds(ctx context.Context, tagIds []uint64) error
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 删除team关联的标签
 | 
						// 删除与标签有关联信息的记录(如团队关联的标签等)
 | 
				
			||||||
	return p.tagTreeTeamRepo.DeleteByWheres(ctx, collx.M{"tag_id in ?": tagIds})
 | 
						return p.tagTreeRelateApp.DeleteByWheres(ctx, collx.M{
 | 
				
			||||||
 | 
							"tag_id in ?": tagIds,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										130
									
								
								server/internal/tag/application/tag_tree_relate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								server/internal/tag/application/tag_tree_relate.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					package application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/repository"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/errorx"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/utils/collx"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TagTreeRelate interface {
 | 
				
			||||||
 | 
						base.App[*entity.TagTreeRelate]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RelateTag 关联标签
 | 
				
			||||||
 | 
						RelateTag(ctx context.Context, relateType entity.TagRelateType, relateId uint64, tagCodePaths ...string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetRelateIds 根据标签路径获取对应关联的id
 | 
				
			||||||
 | 
						GetRelateIds(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetTagPathsByAccountId 根据账号id获取该账号可操作的标签code路径
 | 
				
			||||||
 | 
						GetTagPathsByAccountId(accountId uint64) []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetTagPathsByRelate 根据关联信息获取关联的标签codePaths
 | 
				
			||||||
 | 
						GetTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// FillTagInfo 填充关联的标签信息
 | 
				
			||||||
 | 
						FillTagInfo(relateType entity.TagRelateType, relates ...entity.IRelateTag)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type tagTreeRelateAppImpl struct {
 | 
				
			||||||
 | 
						base.AppImpl[*entity.TagTreeRelate, repository.TagTreeRelate]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagTreeRelateRepo repository.TagTreeRelate `inject:"TagTreeRelateRepo"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagTreeApp TagTree `inject:"TagTreeApp"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ (TagTreeRelate) = (*tagTreeRelateAppImpl)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 注入TagTreeRelateRepo
 | 
				
			||||||
 | 
					func (p *tagTreeRelateAppImpl) InjectTagTreeRelateRepo(tagTreeRelateRepo repository.TagTreeRelate) {
 | 
				
			||||||
 | 
						p.Repo = tagTreeRelateRepo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateAppImpl) RelateTag(ctx context.Context, relateType entity.TagRelateType, relateId uint64, tagCodePaths ...string) error {
 | 
				
			||||||
 | 
						var tags []*entity.TagTree
 | 
				
			||||||
 | 
						tr.tagTreeApp.ListByQuery(&entity.TagTreeQuery{CodePaths: tagCodePaths}, &tags)
 | 
				
			||||||
 | 
						if len(tags) != len(tagCodePaths) {
 | 
				
			||||||
 | 
							return errorx.NewBiz("存在错误标签路径")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var oldRelates []*entity.TagTreeRelate
 | 
				
			||||||
 | 
						tr.ListByCond(&entity.TagTreeRelate{RelateType: relateType, RelateId: relateId}, &oldRelates)
 | 
				
			||||||
 | 
						oldTagIds := collx.ArrayMap[*entity.TagTreeRelate, uint64](oldRelates, func(val *entity.TagTreeRelate) uint64 {
 | 
				
			||||||
 | 
							return val.TagId
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						newTagIds := collx.ArrayMap[*entity.TagTree, uint64](tags, func(val *entity.TagTree) uint64 {
 | 
				
			||||||
 | 
							return val.Id
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						addTagIds, delTagIds, _ := collx.ArrayCompare(newTagIds, oldTagIds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(addTagIds) > 0 {
 | 
				
			||||||
 | 
							trs := make([]*entity.TagTreeRelate, 0)
 | 
				
			||||||
 | 
							for _, tagId := range addTagIds {
 | 
				
			||||||
 | 
								trs = append(trs, &entity.TagTreeRelate{
 | 
				
			||||||
 | 
									TagId:      tagId,
 | 
				
			||||||
 | 
									RelateType: relateType,
 | 
				
			||||||
 | 
									RelateId:   relateId,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := tr.BatchInsert(ctx, trs); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(delTagIds) > 0 {
 | 
				
			||||||
 | 
							if err := tr.DeleteByWheres(ctx, collx.Kvs("relate_type=?", relateType, "relate_id=?", relateId, "tag_id in ?", delTagIds)); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateAppImpl) GetRelateIds(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error) {
 | 
				
			||||||
 | 
						poisibleTagPaths := make([]string, 0)
 | 
				
			||||||
 | 
						for _, tagPath := range tagPaths {
 | 
				
			||||||
 | 
							// 追加可能关联的标签路径,如tagPath = tag1/tag2/1|xxx/,需要获取所有关联的自身及父标签(tag1/  tag1/tag2/ tag1/tag2/1|xxx)
 | 
				
			||||||
 | 
							poisibleTagPaths = append(poisibleTagPaths, entity.GetAllCodePath(tagPath)...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return tr.tagTreeRelateRepo.SelectRelateIdsByTagPaths(relateType, poisibleTagPaths...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateAppImpl) GetTagPathsByAccountId(accountId uint64) []string {
 | 
				
			||||||
 | 
						return tr.tagTreeRelateRepo.SelectTagPathsByAccountId(accountId)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateAppImpl) GetTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string {
 | 
				
			||||||
 | 
						return tr.tagTreeRelateRepo.SelectTagPathsByRelate(relateType, relateId)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateAppImpl) FillTagInfo(relateType entity.TagRelateType, relates ...entity.IRelateTag) {
 | 
				
			||||||
 | 
						if len(relates) == 0 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 关联id -> 关联信息
 | 
				
			||||||
 | 
						relateIds2Relate := collx.ArrayToMap(relates, func(rt entity.IRelateTag) uint64 {
 | 
				
			||||||
 | 
							return rt.GetRelateId()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var relateTags []*entity.TagTreeRelate
 | 
				
			||||||
 | 
						tr.ListByWheres(collx.Kvs("relate_type=?", relateType, "relate_id in ?", collx.MapKeys(relateIds2Relate)), &relateTags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagIds := collx.ArrayMap(relateTags, func(rt *entity.TagTreeRelate) uint64 {
 | 
				
			||||||
 | 
							return rt.TagId
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						var tags []*entity.TagTree
 | 
				
			||||||
 | 
						tr.tagTreeApp.GetByIdIn(&tags, tagIds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagId2Tag := collx.ArrayToMap(tags, func(t *entity.TagTree) uint64 {
 | 
				
			||||||
 | 
							return t.Id
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						for _, rt := range relateTags {
 | 
				
			||||||
 | 
							// 赋值标签信息
 | 
				
			||||||
 | 
							tag := tagId2Tag[rt.TagId]
 | 
				
			||||||
 | 
							relateIds2Relate[rt.RelateId].SetTagInfo(entity.ResourceTag{CodePath: tag.CodePath, TagId: tag.Id})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,7 +10,6 @@ import (
 | 
				
			|||||||
	"mayfly-go/pkg/gormx"
 | 
						"mayfly-go/pkg/gormx"
 | 
				
			||||||
	"mayfly-go/pkg/logx"
 | 
						"mayfly-go/pkg/logx"
 | 
				
			||||||
	"mayfly-go/pkg/model"
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
	"mayfly-go/pkg/utils/collx"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -20,7 +19,7 @@ type SaveTeamParam struct {
 | 
				
			|||||||
	Name   string `json:"name" binding:"required"` // 名称
 | 
						Name   string `json:"name" binding:"required"` // 名称
 | 
				
			||||||
	Remark string `json:"remark"`                  // 备注说明
 | 
						Remark string `json:"remark"`                  // 备注说明
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Tags []uint64 `json:"tags"` // 关联标签信息
 | 
						CodePaths []string `json:"codePaths"` // 关联标签信息
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Team interface {
 | 
					type Team interface {
 | 
				
			||||||
@@ -42,17 +41,14 @@ type Team interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	IsExistMember(teamId, accounId uint64) bool
 | 
						IsExistMember(teamId, accounId uint64) bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//--------------- 关联项目相关接口 ---------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ListTagIds(teamId uint64) []uint64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DeleteTag(tx context.Context, teamId, tagId uint64) error
 | 
						DeleteTag(tx context.Context, teamId, tagId uint64) error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type teamAppImpl struct {
 | 
					type teamAppImpl struct {
 | 
				
			||||||
	teamRepo        repository.Team        `inject:"TeamRepo"`
 | 
						teamRepo       repository.Team       `inject:"TeamRepo"`
 | 
				
			||||||
	teamMemberRepo  repository.TeamMember  `inject:"TeamMemberRepo"`
 | 
						teamMemberRepo repository.TeamMember `inject:"TeamMemberRepo"`
 | 
				
			||||||
	tagTreeTeamRepo repository.TagTreeTeam `inject:"TagTreeTeamRepo"`
 | 
					
 | 
				
			||||||
 | 
						tagTreeRelateApp TagTreeRelate `inject:"TagTreeRelateApp"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *teamAppImpl) GetPageList(condition *entity.TeamQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
 | 
					func (p *teamAppImpl) GetPageList(condition *entity.TeamQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
 | 
				
			||||||
@@ -89,35 +85,7 @@ func (p *teamAppImpl) Save(ctx context.Context, saveParam *SaveTeamParam) error
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 保存团队关联的标签信息
 | 
						// 保存团队关联的标签信息
 | 
				
			||||||
	teamId := team.Id
 | 
						return p.tagTreeRelateApp.RelateTag(ctx, entity.TagRelateTypeTeam, team.Id, saveParam.CodePaths...)
 | 
				
			||||||
	var addIds, delIds []uint64
 | 
					 | 
				
			||||||
	if saveParam.Id == 0 {
 | 
					 | 
				
			||||||
		addIds = saveParam.Tags
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		// 将[]uint64转为[]any
 | 
					 | 
				
			||||||
		oIds := p.ListTagIds(team.Id)
 | 
					 | 
				
			||||||
		// 比较新旧两合集
 | 
					 | 
				
			||||||
		addIds, delIds, _ = collx.ArrayCompare(saveParam.Tags, oIds)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	addTeamTags := make([]*entity.TagTreeTeam, 0)
 | 
					 | 
				
			||||||
	for _, v := range addIds {
 | 
					 | 
				
			||||||
		ptt := &entity.TagTreeTeam{TeamId: teamId, TagId: v}
 | 
					 | 
				
			||||||
		addTeamTags = append(addTeamTags, ptt)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(addTeamTags) > 0 {
 | 
					 | 
				
			||||||
		logx.DebugfContext(ctx, "团队[%s]新增关联的标签信息: [%v]", team.Name, addTeamTags)
 | 
					 | 
				
			||||||
		p.tagTreeTeamRepo.BatchInsert(ctx, addTeamTags)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, v := range delIds {
 | 
					 | 
				
			||||||
		p.DeleteTag(ctx, teamId, v)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(delIds) > 0 {
 | 
					 | 
				
			||||||
		logx.DebugfContext(ctx, "团队[%s]删除关联的标签信息: [%v]", team.Name, delIds)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *teamAppImpl) Delete(ctx context.Context, id uint64) error {
 | 
					func (p *teamAppImpl) Delete(ctx context.Context, id uint64) error {
 | 
				
			||||||
@@ -129,7 +97,7 @@ func (p *teamAppImpl) Delete(ctx context.Context, id uint64) error {
 | 
				
			|||||||
			return p.teamMemberRepo.DeleteByCondWithDb(ctx, db, &entity.TeamMember{TeamId: id})
 | 
								return p.teamMemberRepo.DeleteByCondWithDb(ctx, db, &entity.TeamMember{TeamId: id})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		func(db *gorm.DB) error {
 | 
							func(db *gorm.DB) error {
 | 
				
			||||||
			return p.tagTreeTeamRepo.DeleteByCondWithDb(ctx, db, &entity.TagTreeTeam{TeamId: id})
 | 
								return p.tagTreeRelateApp.DeleteByCondWithDb(ctx, db, &entity.TagTreeRelate{RelateType: entity.TagRelateTypeTeam, RelateId: id})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -158,17 +126,7 @@ func (p *teamAppImpl) IsExistMember(teamId, accounId uint64) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//--------------- 标签相关接口 ---------------
 | 
					//--------------- 标签相关接口 ---------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *teamAppImpl) ListTagIds(teamId uint64) []uint64 {
 | 
					// 删除关联标签信息
 | 
				
			||||||
	tags := &[]entity.TagTreeTeam{}
 | 
					 | 
				
			||||||
	p.tagTreeTeamRepo.ListByCondOrder(&entity.TagTreeTeam{TeamId: teamId}, tags)
 | 
					 | 
				
			||||||
	ids := make([]uint64, 0)
 | 
					 | 
				
			||||||
	for _, v := range *tags {
 | 
					 | 
				
			||||||
		ids = append(ids, v.TagId)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ids
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 删除关联项目信息
 | 
					 | 
				
			||||||
func (p *teamAppImpl) DeleteTag(ctx context.Context, teamId, tagId uint64) error {
 | 
					func (p *teamAppImpl) DeleteTag(ctx context.Context, teamId, tagId uint64) error {
 | 
				
			||||||
	return p.tagTreeTeamRepo.DeleteByCond(ctx, &entity.TagTreeTeam{TeamId: teamId, TagId: tagId})
 | 
						return p.tagTreeRelateApp.DeleteByCond(ctx, &entity.TagTreeRelate{RelateType: entity.TagRelateTypeTeam, RelateId: teamId, TagId: tagId})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								server/internal/tag/domain/entity/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								server/internal/tag/domain/entity/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/pkg/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 资源操作日志记录
 | 
				
			||||||
 | 
					type ResourceOpLog struct {
 | 
				
			||||||
 | 
						model.CreateModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CodePath     string `json:"codePath"`     // 标签路径
 | 
				
			||||||
 | 
						ResourceCode string `json:"resourceCode"` // 资源编号
 | 
				
			||||||
 | 
						ResourceType int8   `json:"relateType"`   // 资源类型
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -89,11 +89,13 @@ type ITagResource interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 资源关联的标签信息
 | 
					// 资源关联的标签信息
 | 
				
			||||||
type ResourceTag struct {
 | 
					type ResourceTag struct {
 | 
				
			||||||
 | 
						TagId    uint64 `json:"tagId" gorm:"-"`
 | 
				
			||||||
	CodePath string `json:"codePath" gorm:"-"` // 标签路径
 | 
						CodePath string `json:"codePath" gorm:"-"` // 标签路径
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *ResourceTag) SetTagInfo(rt ResourceTag) {
 | 
					func (r *ResourceTag) SetTagInfo(rt ResourceTag) {
 | 
				
			||||||
	r.CodePath = rt.CodePath
 | 
						r.CodePath = rt.CodePath
 | 
				
			||||||
 | 
						r.TagId = rt.TagId
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 资源标签列表
 | 
					// 资源标签列表
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								server/internal/tag/domain/entity/tag_tree_relate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								server/internal/tag/domain/entity/tag_tree_relate.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/pkg/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 与标签树有关联关系的实体
 | 
				
			||||||
 | 
					type TagTreeRelate struct {
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TagId      uint64        `json:"tagId"`
 | 
				
			||||||
 | 
						RelateId   uint64        `json:"relateId"`   // 关联的id
 | 
				
			||||||
 | 
						RelateType TagRelateType `json:"relateType"` // 关联的类型
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TagRelateType int8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						TagRelateTypeTeam       TagRelateType = 1 // 关联团队
 | 
				
			||||||
 | 
						TagRelateTypeMachineCmd TagRelateType = 2 // 关联机器命令配置
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 关联标签信息,如果要实现填充关联标签信息,则结构体需要实现该接口
 | 
				
			||||||
 | 
					type IRelateTag interface {
 | 
				
			||||||
 | 
						// 获取关联id
 | 
				
			||||||
 | 
						GetRelateId() uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 赋值标签路径
 | 
				
			||||||
 | 
						SetTagInfo(tag ResourceTag)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 关联的标签信息
 | 
				
			||||||
 | 
					type RelateTags struct {
 | 
				
			||||||
 | 
						Tags []ResourceTag `json:"tags" gorm:"-"` // 标签路径
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RelateTags) SetTagInfo(rt ResourceTag) {
 | 
				
			||||||
 | 
						if r.Tags == nil {
 | 
				
			||||||
 | 
							r.Tags = make([]ResourceTag, 0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r.Tags = append(r.Tags, rt)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,11 +0,0 @@
 | 
				
			|||||||
package entity
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "mayfly-go/pkg/model"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 标签树与团队关联信息
 | 
					 | 
				
			||||||
type TagTreeTeam struct {
 | 
					 | 
				
			||||||
	model.Model
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TagId  uint64 `json:"tagId"`
 | 
					 | 
				
			||||||
	TeamId uint64 `json:"teamId"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										10
									
								
								server/internal/tag/domain/repository/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								server/internal/tag/domain/repository/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ResourceOpLog interface {
 | 
				
			||||||
 | 
						base.Repo[*entity.ResourceOpLog]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								server/internal/tag/domain/repository/tag_tree_relate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								server/internal/tag/domain/repository/tag_tree_relate.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TagTreeRelate interface {
 | 
				
			||||||
 | 
						base.Repo[*entity.TagTreeRelate]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// SelectRelateIdsByTagPaths 根据标签路径查询相关联的id
 | 
				
			||||||
 | 
						SelectRelateIdsByTagPaths(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// SelectTagPathsByAccountId 根据账号id获取该账号可访问操作的标签codePaths(该方法调用较频繁,故不使用下列方法获取)
 | 
				
			||||||
 | 
						SelectTagPathsByAccountId(accountId uint64) []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// SelectTagPathsByRelate 根据关联信息查询对应的关联的标签路径
 | 
				
			||||||
 | 
						SelectTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
package repository
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"mayfly-go/internal/tag/domain/entity"
 | 
					 | 
				
			||||||
	"mayfly-go/pkg/base"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type TagTreeTeam interface {
 | 
					 | 
				
			||||||
	base.Repo[*entity.TagTreeTeam]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	SelectTagPathsByAccountId(accountId uint64) []string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -6,8 +6,9 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func InitIoc() {
 | 
					func InitIoc() {
 | 
				
			||||||
	ioc.Register(newTagTreeRepo(), ioc.WithComponentName("TagTreeRepo"))
 | 
						ioc.Register(newTagTreeRepo(), ioc.WithComponentName("TagTreeRepo"))
 | 
				
			||||||
	ioc.Register(newTagTreeTeamRepo(), ioc.WithComponentName("TagTreeTeamRepo"))
 | 
					 | 
				
			||||||
	ioc.Register(newTeamRepo(), ioc.WithComponentName("TeamRepo"))
 | 
						ioc.Register(newTeamRepo(), ioc.WithComponentName("TeamRepo"))
 | 
				
			||||||
	ioc.Register(newTeamMemberRepo(), ioc.WithComponentName("TeamMemberRepo"))
 | 
						ioc.Register(newTeamMemberRepo(), ioc.WithComponentName("TeamMemberRepo"))
 | 
				
			||||||
	ioc.Register(newResourceAuthCertRepoImpl(), ioc.WithComponentName("ResourceAuthCertRepo"))
 | 
						ioc.Register(newResourceAuthCertRepoImpl(), ioc.WithComponentName("ResourceAuthCertRepo"))
 | 
				
			||||||
 | 
						ioc.Register(newResourceOpLogRepo(), ioc.WithComponentName("ResourceOpLogRepo"))
 | 
				
			||||||
 | 
						ioc.Register(newTagTreeRelateRepo(), ioc.WithComponentName("TagTreeRelateRepo"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package persistence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/repository"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type resourceOpLogRepoImpl struct {
 | 
				
			||||||
 | 
						base.RepoImpl[*entity.ResourceOpLog]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newResourceOpLogRepo() repository.ResourceOpLog {
 | 
				
			||||||
 | 
						return &resourceOpLogRepoImpl{base.RepoImpl[*entity.ResourceOpLog]{M: new(entity.ResourceOpLog)}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					package persistence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/domain/repository"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/gormx"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type tagTreeRelateRepoImpl struct {
 | 
				
			||||||
 | 
						base.RepoImpl[*entity.TagTreeRelate]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newTagTreeRelateRepo() repository.TagTreeRelate {
 | 
				
			||||||
 | 
						return &tagTreeRelateRepoImpl{base.RepoImpl[*entity.TagTreeRelate]{M: new(entity.TagTreeRelate)}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SelectRelateIdsByTagPaths 根据标签路径查询相关联的id
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateRepoImpl) SelectRelateIdsByTagPaths(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error) {
 | 
				
			||||||
 | 
						var res []uint64
 | 
				
			||||||
 | 
						sql := `
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
						t1.relate_id
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
						t_tag_tree_relate t1
 | 
				
			||||||
 | 
					JOIN t_tag_tree t ON
 | 
				
			||||||
 | 
						t.id = t1.tag_id
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
						t1.relate_type = ?
 | 
				
			||||||
 | 
						AND t.code_path in ?
 | 
				
			||||||
 | 
						AND t.is_deleted = 0
 | 
				
			||||||
 | 
						AND t1.is_deleted = 0
 | 
				
			||||||
 | 
					ORDER BY
 | 
				
			||||||
 | 
						t.code_path
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
						if err := gormx.GetListBySql2Model(sql, &res, relateType, tagPaths); err != nil {
 | 
				
			||||||
 | 
							return res, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateRepoImpl) SelectTagPathsByAccountId(accountId uint64) []string {
 | 
				
			||||||
 | 
						var res []string
 | 
				
			||||||
 | 
						sql := `
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
						DISTINCT(t.code_path)
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
						t_tag_tree_relate t1
 | 
				
			||||||
 | 
					JOIN t_team_member t2 ON
 | 
				
			||||||
 | 
						t1.relate_id = t2.team_id
 | 
				
			||||||
 | 
					JOIN t_tag_tree t ON
 | 
				
			||||||
 | 
						t.id = t1.tag_id
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
						t1.relate_type = ?
 | 
				
			||||||
 | 
						AND t2.account_id = ?
 | 
				
			||||||
 | 
						AND t1.is_deleted = 0
 | 
				
			||||||
 | 
						AND t2.is_deleted = 0
 | 
				
			||||||
 | 
						AND t.is_deleted = 0
 | 
				
			||||||
 | 
					ORDER BY
 | 
				
			||||||
 | 
						t.code_path
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
						gormx.GetListBySql2Model(sql, &res, entity.TagRelateTypeTeam, accountId)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SelectTagPathsByRelate 根据关联信息查询对应的关联的标签路径
 | 
				
			||||||
 | 
					func (tr *tagTreeRelateRepoImpl) SelectTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string {
 | 
				
			||||||
 | 
						var res []string
 | 
				
			||||||
 | 
						sql := `
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
						DISTINCT(t.code_path)
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
						t_tag_tree_relate t1
 | 
				
			||||||
 | 
					JOIN t_tag_tree t ON
 | 
				
			||||||
 | 
						t.id = t1.tag_id
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
						t1.relate_id = ?
 | 
				
			||||||
 | 
						AND t1.relate_type = ?
 | 
				
			||||||
 | 
						AND t.is_deleted = 0
 | 
				
			||||||
 | 
						AND t1.is_deleted = 0
 | 
				
			||||||
 | 
					ORDER BY
 | 
				
			||||||
 | 
						t.code_path
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
						gormx.GetListBySql2Model(sql, &res, relateId, relateType)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,39 +0,0 @@
 | 
				
			|||||||
package persistence
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"mayfly-go/internal/tag/domain/entity"
 | 
					 | 
				
			||||||
	"mayfly-go/internal/tag/domain/repository"
 | 
					 | 
				
			||||||
	"mayfly-go/pkg/base"
 | 
					 | 
				
			||||||
	"mayfly-go/pkg/gormx"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type tagTreeTeamRepoImpl struct {
 | 
					 | 
				
			||||||
	base.RepoImpl[*entity.TagTreeTeam]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newTagTreeTeamRepo() repository.TagTreeTeam {
 | 
					 | 
				
			||||||
	return &tagTreeTeamRepoImpl{base.RepoImpl[*entity.TagTreeTeam]{M: new(entity.TagTreeTeam)}}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *tagTreeTeamRepoImpl) SelectTagPathsByAccountId(accountId uint64) []string {
 | 
					 | 
				
			||||||
	var res []string
 | 
					 | 
				
			||||||
	sql := `
 | 
					 | 
				
			||||||
SELECT
 | 
					 | 
				
			||||||
	DISTINCT(t.code_path)
 | 
					 | 
				
			||||||
FROM
 | 
					 | 
				
			||||||
	t_tag_tree_team t1
 | 
					 | 
				
			||||||
JOIN t_team_member t2 ON
 | 
					 | 
				
			||||||
	t1.team_id = t2.team_id
 | 
					 | 
				
			||||||
JOIN t_tag_tree t ON
 | 
					 | 
				
			||||||
	t.id = t1.tag_id
 | 
					 | 
				
			||||||
WHERE
 | 
					 | 
				
			||||||
	t2.account_id = ?
 | 
					 | 
				
			||||||
	AND t1.is_deleted = 0
 | 
					 | 
				
			||||||
	AND t2.is_deleted = 0
 | 
					 | 
				
			||||||
	AND t.is_deleted = 0
 | 
					 | 
				
			||||||
ORDER BY
 | 
					 | 
				
			||||||
	t.code_path
 | 
					 | 
				
			||||||
	`
 | 
					 | 
				
			||||||
	gormx.GetListBySql2Model(sql, &res, accountId)
 | 
					 | 
				
			||||||
	return res
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,10 +1,14 @@
 | 
				
			|||||||
package init
 | 
					package init
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"mayfly-go/initialize"
 | 
						"mayfly-go/initialize"
 | 
				
			||||||
 | 
						"mayfly-go/internal/event"
 | 
				
			||||||
	"mayfly-go/internal/tag/application"
 | 
						"mayfly-go/internal/tag/application"
 | 
				
			||||||
	"mayfly-go/internal/tag/infrastructure/persistence"
 | 
						"mayfly-go/internal/tag/infrastructure/persistence"
 | 
				
			||||||
	"mayfly-go/internal/tag/router"
 | 
						"mayfly-go/internal/tag/router"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/eventbus"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/global"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
@@ -13,4 +17,14 @@ func init() {
 | 
				
			|||||||
		application.InitIoc()
 | 
							application.InitIoc()
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	initialize.AddInitRouterFunc(router.Init)
 | 
						initialize.AddInitRouterFunc(router.Init)
 | 
				
			||||||
 | 
						initialize.AddInitFunc(Init)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Init() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						global.EventBus.SubscribeAsync(event.EventTopicResourceOp, "ResourceOpLogApp", func(ctx context.Context, event *eventbus.Event) error {
 | 
				
			||||||
 | 
							codePath := event.Val.(string)
 | 
				
			||||||
 | 
							return application.GetResourceOpLogApp().AddResourceOpLog(ctx, codePath)
 | 
				
			||||||
 | 
						}, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								server/internal/tag/router/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								server/internal/tag/router/resource_op_log.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					package router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/internal/tag/api"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/ioc"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/req"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InitResourceOpLogRouter(router *gin.RouterGroup) {
 | 
				
			||||||
 | 
						m := new(api.ResourceOpLog)
 | 
				
			||||||
 | 
						biz.ErrIsNil(ioc.Inject(m))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resourceOpLog := router.Group("/resource-op-logs")
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							reqs := [...]*req.Conf{
 | 
				
			||||||
 | 
								req.NewGet("/account", m.PageAccountOpLog),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req.BatchSetGroup(resourceOpLog, reqs[:])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,4 +6,5 @@ func Init(router *gin.RouterGroup) {
 | 
				
			|||||||
	InitTagTreeRouter(router)
 | 
						InitTagTreeRouter(router)
 | 
				
			||||||
	InitTeamRouter(router)
 | 
						InitTeamRouter(router)
 | 
				
			||||||
	InitResourceAuthCertRouter(router)
 | 
						InitResourceAuthCertRouter(router)
 | 
				
			||||||
 | 
						InitResourceOpLogRouter(router)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,9 @@ func InitTagTreeRouter(router *gin.RouterGroup) {
 | 
				
			|||||||
			req.NewGet("/resources/:rtype/tag-paths", m.TagResources),
 | 
								req.NewGet("/resources/:rtype/tag-paths", m.TagResources),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			req.NewGet("/resources/count", m.CountTagResource),
 | 
								req.NewGet("/resources/count", m.CountTagResource),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 获取关联的标签id列表
 | 
				
			||||||
 | 
								req.NewGet("/relate/:relateType/:relateId", m.GetRelateTagIds),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		req.BatchSetGroup(tagTree, reqs[:])
 | 
							req.BatchSetGroup(tagTree, reqs[:])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,9 +29,6 @@ func InitTeamRouter(router *gin.RouterGroup) {
 | 
				
			|||||||
			req.NewPost("/:id/members", m.SaveTeamMember).Log(req.NewLogSave("团队-新增成员")).RequiredPermissionCode("team:member:save"),
 | 
								req.NewPost("/:id/members", m.SaveTeamMember).Log(req.NewLogSave("团队-新增成员")).RequiredPermissionCode("team:member:save"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			req.NewDelete("/:id/members/:accountId", m.DelTeamMember).Log(req.NewLogSave("团队-删除成员")).RequiredPermissionCode("team:member:del"),
 | 
								req.NewDelete("/:id/members/:accountId", m.DelTeamMember).Log(req.NewLogSave("团队-删除成员")).RequiredPermissionCode("team:member:del"),
 | 
				
			||||||
 | 
					 | 
				
			||||||
			// 获取团队关联的标签id列表
 | 
					 | 
				
			||||||
			req.NewGet("/:id/tags", m.GetTagIds),
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		req.BatchSetGroup(team, reqs[:])
 | 
							req.BatchSetGroup(team, reqs[:])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -89,9 +89,6 @@ func T2022() *gormigrate.Migration {
 | 
				
			|||||||
			if err := tx.AutoMigrate(&entity7.TagTree{}); err != nil {
 | 
								if err := tx.AutoMigrate(&entity7.TagTree{}); err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if err := tx.AutoMigrate(&entity7.TagTreeTeam{}); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if err := tx.AutoMigrate(&entity7.Team{}); err != nil {
 | 
								if err := tx.AutoMigrate(&entity7.Team{}); err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,9 +138,18 @@ type Map[K comparable, V any] map[K]V
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (m *Map[K, V]) Scan(value any) error {
 | 
					func (m *Map[K, V]) Scan(value any) error {
 | 
				
			||||||
	return json.Unmarshal(value.([]byte), m)
 | 
						return json.Unmarshal(value.([]byte), m)
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m Map[K, V]) Value() (driver.Value, error) {
 | 
					func (m Map[K, V]) Value() (driver.Value, error) {
 | 
				
			||||||
	return json.Marshal(m)
 | 
						return json.Marshal(m)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Slice[T int | string | Map[string, any]] []T
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Slice[T]) Scan(value any) error {
 | 
				
			||||||
 | 
						return json.Unmarshal(value.([]byte), s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Slice[T]) Value() (driver.Value, error) {
 | 
				
			||||||
 | 
						return json.Marshal(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -503,6 +503,25 @@ CREATE TABLE `t_machine_term_op` (
 | 
				
			|||||||
  PRIMARY KEY (`id`)
 | 
					  PRIMARY KEY (`id`)
 | 
				
			||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
 | 
					) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP TABLE IF EXISTS `t_machine_cmd_conf`;
 | 
				
			||||||
 | 
					CREATE TABLE `t_machine_cmd_conf` (
 | 
				
			||||||
 | 
					  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 | 
				
			||||||
 | 
					  `name` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '名称',
 | 
				
			||||||
 | 
					  `cmds` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '命令配置',
 | 
				
			||||||
 | 
					  `status` tinyint(4) DEFAULT NULL COMMENT '状态',
 | 
				
			||||||
 | 
					  `stratege` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '策略',
 | 
				
			||||||
 | 
					  `remark` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '备注',
 | 
				
			||||||
 | 
					  `create_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `creator_id` bigint(20) NOT NULL,
 | 
				
			||||||
 | 
					  `creator` varchar(36) COLLATE utf8_bin NOT NULL,
 | 
				
			||||||
 | 
					  `update_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `modifier_id` bigint(20) NOT NULL,
 | 
				
			||||||
 | 
					  `modifier` varchar(36) COLLATE utf8_bin NOT NULL,
 | 
				
			||||||
 | 
					  `is_deleted` tinyint(4) DEFAULT '0',
 | 
				
			||||||
 | 
					  `delete_time` datetime DEFAULT NULL,
 | 
				
			||||||
 | 
					  PRIMARY KEY (`id`)
 | 
				
			||||||
 | 
					) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='机器命令配置';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
-- Table structure for t_mongo
 | 
					-- Table structure for t_mongo
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
@@ -826,7 +845,9 @@ INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `we
 | 
				
			|||||||
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196723, 1709194669, 2, 1, '启停', 'db:transfer:status', 1709196723, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:04', '2024-02-29 16:52:04', 'SmLcpu6c/hGiLN1VT/', 0, NULL);
 | 
					INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196723, 1709194669, 2, 1, '启停', 'db:transfer:status', 1709196723, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:04', '2024-02-29 16:52:04', 'SmLcpu6c/hGiLN1VT/', 0, NULL);
 | 
				
			||||||
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196737, 1709194669, 2, 1, '日志', 'db:transfer:log', 1709196737, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:17', '2024-02-29 16:52:17', 'SmLcpu6c/CZhNIbWg/', 0, NULL);
 | 
					INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196737, 1709194669, 2, 1, '日志', 'db:transfer:log', 1709196737, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:17', '2024-02-29 16:52:17', 'SmLcpu6c/CZhNIbWg/', 0, NULL);
 | 
				
			||||||
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196755, 1709194669, 2, 1, '运行', 'db:transfer:run', 1709196755, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:36', '2024-02-29 16:52:36', 'SmLcpu6c/b6yHt6V2/', 0, NULL);
 | 
					INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196755, 1709194669, 2, 1, '运行', 'db:transfer:run', 1709196755, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:36', '2024-02-29 16:52:36', 'SmLcpu6c/b6yHt6V2/', 0, NULL);
 | 
				
			||||||
 | 
					INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714032002, 1713875842, '12sSjal1/UnWIUhW0/0tJwC3Gf/', 2, 1, '命令配置-删除', 'cmdconf:del', 1714032002, 'null', 1, 'admin', 1, 'admin', '2024-04-25 16:00:02', '2024-04-25 16:00:02', 0, NULL);
 | 
				
			||||||
 | 
					INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714031981, 1713875842, '12sSjal1/UnWIUhW0/tEzIKecl/', 2, 1, '命令配置-保存', 'cmdconf:save', 1714031981, 'null', 1, 'admin', 1, 'admin', '2024-04-25 15:59:41', '2024-04-25 15:59:41', 0, NULL);
 | 
				
			||||||
 | 
					INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1713875842, 2, '12sSjal1/UnWIUhW0/', 1, 1, '安全配置', 'security', 1713875842, '{"component":"ops/machine/security/SecurityConfList","icon":"Setting","isKeepAlive":true,"routeName":"SecurityConfList"}', 1, 'admin', 1, 'admin', '2024-04-23 20:37:22', '2024-04-23 20:37:22', 0, NULL);
 | 
				
			||||||
COMMIT;
 | 
					COMMIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
@@ -913,31 +934,29 @@ BEGIN;
 | 
				
			|||||||
INSERT INTO `t_tag_tree` VALUES (1, -1, 'default', 'default/', '默认', '默认标签', '2022-10-26 20:04:19', 1, 'admin', '2022-10-26 20:04:19', 1, 'admin', 0, NULL);
 | 
					INSERT INTO `t_tag_tree` VALUES (1, -1, 'default', 'default/', '默认', '默认标签', '2022-10-26 20:04:19', 1, 'admin', '2022-10-26 20:04:19', 1, 'admin', 0, NULL);
 | 
				
			||||||
COMMIT;
 | 
					COMMIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- ----------------------------
 | 
					DROP TABLE IF EXISTS `t_tag_tree_relate`;
 | 
				
			||||||
-- Table structure for t_tag_tree_team
 | 
					CREATE TABLE `t_tag_tree_relate` (
 | 
				
			||||||
-- ----------------------------
 | 
					  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
 | 
				
			||||||
DROP TABLE IF EXISTS `t_tag_tree_team`;
 | 
					  `tag_id` bigint NOT NULL COMMENT '标签树id',
 | 
				
			||||||
CREATE TABLE `t_tag_tree_team` (
 | 
					  `relate_id` bigint NOT NULL COMMENT '关联',
 | 
				
			||||||
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 | 
					  `relate_type` tinyint NOT NULL COMMENT '关联类型',
 | 
				
			||||||
  `tag_id` bigint(20) NOT NULL COMMENT '项目树id',
 | 
					 | 
				
			||||||
  `team_id` bigint(20) NOT NULL COMMENT '团队id',
 | 
					 | 
				
			||||||
  `create_time` datetime NOT NULL,
 | 
					  `create_time` datetime NOT NULL,
 | 
				
			||||||
  `creator_id` bigint(20) NOT NULL,
 | 
					  `creator_id` bigint NOT NULL,
 | 
				
			||||||
  `creator` varchar(36) NOT NULL,
 | 
					  `creator` varchar(36) COLLATE utf8mb4_bin NOT NULL,
 | 
				
			||||||
  `update_time` datetime NOT NULL,
 | 
					  `update_time` datetime NOT NULL,
 | 
				
			||||||
  `modifier_id` bigint(20) NOT NULL,
 | 
					  `modifier_id` bigint NOT NULL,
 | 
				
			||||||
  `modifier` varchar(36) NOT NULL,
 | 
					  `modifier` varchar(36) COLLATE utf8mb4_bin NOT NULL,
 | 
				
			||||||
  `is_deleted` tinyint(8) NOT NULL DEFAULT 0,
 | 
					  `is_deleted` tinyint NOT NULL DEFAULT '0',
 | 
				
			||||||
  `delete_time` datetime DEFAULT NULL,
 | 
					  `delete_time` datetime DEFAULT NULL,
 | 
				
			||||||
  PRIMARY KEY (`id`),
 | 
					  PRIMARY KEY (`id`),
 | 
				
			||||||
  KEY `idx_tag_id` (`tag_id`) USING BTREE
 | 
					  KEY `idx_tag_id` (`tag_id`) USING BTREE
 | 
				
			||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签树团队关联信息';
 | 
					) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='与标签树有关联关系的表';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
-- Records of t_tag_tree_team
 | 
					-- Records of t_tag_tree_relate
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
BEGIN;
 | 
					BEGIN;
 | 
				
			||||||
INSERT INTO `t_tag_tree_team` VALUES (1, 1, 1, '2022-10-26 20:04:45', 1, 'admin', '2022-10-26 20:04:45', 1, 'admin', 0, NULL);
 | 
					INSERT INTO `t_tag_tree_relate` VALUES (1, 1, 1, 1, '2022-10-26 20:04:45', 1, 'admin', '2022-10-26 20:04:45', 1, 'admin', 0, NULL);
 | 
				
			||||||
COMMIT;
 | 
					COMMIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
@@ -1019,6 +1038,21 @@ CREATE TABLE `t_resource_auth_cert` (
 | 
				
			|||||||
    KEY `idx_name` (`name`) USING BTREE
 | 
					    KEY `idx_name` (`name`) USING BTREE
 | 
				
			||||||
) COMMENT='资源授权凭证表';
 | 
					) COMMENT='资源授权凭证表';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP TABLE IF EXISTS `t_resource_op_log`;
 | 
				
			||||||
 | 
					CREATE TABLE `t_resource_op_log` (
 | 
				
			||||||
 | 
					  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
 | 
				
			||||||
 | 
					  `code_path` varchar(600) NOT NULL COMMENT '资源标签路径',
 | 
				
			||||||
 | 
					  `resource_code` varchar(32) NOT NULL COMMENT '资源编号',
 | 
				
			||||||
 | 
					  `resource_type` tinyint NOT NULL COMMENT '资源类型',
 | 
				
			||||||
 | 
					  `create_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `creator_id` bigint NOT NULL,
 | 
				
			||||||
 | 
					  `creator` varchar(36) NOT NULL,
 | 
				
			||||||
 | 
					  `is_deleted` tinyint NOT NULL DEFAULT '0',
 | 
				
			||||||
 | 
					  `delete_time` datetime DEFAULT NULL,
 | 
				
			||||||
 | 
					  PRIMARY KEY (`id`),
 | 
				
			||||||
 | 
					  KEY `idx_resource_code` (`resource_code`) USING BTREE
 | 
				
			||||||
 | 
					) ENGINE=InnoDB COMMENT='资源操作记录';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DROP TABLE IF EXISTS `t_flow_procdef`;
 | 
					DROP TABLE IF EXISTS `t_flow_procdef`;
 | 
				
			||||||
-- 工单流程相关表
 | 
					-- 工单流程相关表
 | 
				
			||||||
CREATE TABLE `t_flow_procdef` (
 | 
					CREATE TABLE `t_flow_procdef` (
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										84
									
								
								server/resources/script/sql/v1.8/v1.8.2.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								server/resources/script/sql/v1.8/v1.8.2.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					CREATE TABLE `t_tag_tree_relate` (
 | 
				
			||||||
 | 
					  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
 | 
				
			||||||
 | 
					  `tag_id` bigint NOT NULL COMMENT '标签树id',
 | 
				
			||||||
 | 
					  `relate_id` bigint NOT NULL COMMENT '关联',
 | 
				
			||||||
 | 
					  `relate_type` tinyint NOT NULL COMMENT '关联类型',
 | 
				
			||||||
 | 
					  `create_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `creator_id` bigint NOT NULL,
 | 
				
			||||||
 | 
					  `creator` varchar(36) COLLATE utf8mb4_bin NOT NULL,
 | 
				
			||||||
 | 
					  `update_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `modifier_id` bigint NOT NULL,
 | 
				
			||||||
 | 
					  `modifier` varchar(36) COLLATE utf8mb4_bin NOT NULL,
 | 
				
			||||||
 | 
					  `is_deleted` tinyint NOT NULL DEFAULT '0',
 | 
				
			||||||
 | 
					  `delete_time` datetime DEFAULT NULL,
 | 
				
			||||||
 | 
					  PRIMARY KEY (`id`),
 | 
				
			||||||
 | 
					  KEY `idx_tag_id` (`tag_id`) USING BTREE
 | 
				
			||||||
 | 
					) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='与标签树有关联关系的表';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE TABLE `t_machine_cmd_conf` (
 | 
				
			||||||
 | 
					  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 | 
				
			||||||
 | 
					  `name` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '名称',
 | 
				
			||||||
 | 
					  `cmds` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '命令配置',
 | 
				
			||||||
 | 
					  `status` tinyint(4) DEFAULT NULL COMMENT '状态',
 | 
				
			||||||
 | 
					  `stratege` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '策略',
 | 
				
			||||||
 | 
					  `remark` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '备注',
 | 
				
			||||||
 | 
					  `create_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `creator_id` bigint(20) NOT NULL,
 | 
				
			||||||
 | 
					  `creator` varchar(36) COLLATE utf8_bin NOT NULL,
 | 
				
			||||||
 | 
					  `update_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `modifier_id` bigint(20) NOT NULL,
 | 
				
			||||||
 | 
					  `modifier` varchar(36) COLLATE utf8_bin NOT NULL,
 | 
				
			||||||
 | 
					  `is_deleted` tinyint(4) DEFAULT '0',
 | 
				
			||||||
 | 
					  `delete_time` datetime DEFAULT NULL,
 | 
				
			||||||
 | 
					  PRIMARY KEY (`id`)
 | 
				
			||||||
 | 
					) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='机器命令配置';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714032002, 1713875842, '12sSjal1/UnWIUhW0/0tJwC3Gf/', 2, 1, '命令配置-删除', 'cmdconf:del', 1714032002, 'null', 1, 'admin', 1, 'admin', '2024-04-25 16:00:02', '2024-04-25 16:00:02', 0, NULL);
 | 
				
			||||||
 | 
					INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714031981, 1713875842, '12sSjal1/UnWIUhW0/tEzIKecl/', 2, 1, '命令配置-保存', 'cmdconf:save', 1714031981, 'null', 1, 'admin', 1, 'admin', '2024-04-25 15:59:41', '2024-04-25 15:59:41', 0, NULL);
 | 
				
			||||||
 | 
					INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1713875842, 2, '12sSjal1/UnWIUhW0/', 1, 1, '安全配置', 'security', 1713875842, '{"component":"ops/machine/security/SecurityConfList","icon":"Setting","isKeepAlive":true,"routeName":"SecurityConfList"}', 1, 'admin', 1, 'admin', '2024-04-23 20:37:22', '2024-04-23 20:37:22', 0, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INSERT
 | 
				
			||||||
 | 
						INTO
 | 
				
			||||||
 | 
						t_tag_tree_relate (tag_id,
 | 
				
			||||||
 | 
						relate_id,
 | 
				
			||||||
 | 
						relate_type,
 | 
				
			||||||
 | 
						create_time,
 | 
				
			||||||
 | 
						creator_id,
 | 
				
			||||||
 | 
						creator,
 | 
				
			||||||
 | 
						update_time,
 | 
				
			||||||
 | 
						modifier_id,
 | 
				
			||||||
 | 
						modifier,
 | 
				
			||||||
 | 
						is_deleted )
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
						tt.tag_id ,
 | 
				
			||||||
 | 
						tt.team_id ,
 | 
				
			||||||
 | 
						1,
 | 
				
			||||||
 | 
						DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
 | 
				
			||||||
 | 
						1,
 | 
				
			||||||
 | 
						'admin',
 | 
				
			||||||
 | 
						DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
 | 
				
			||||||
 | 
						1,
 | 
				
			||||||
 | 
						'admin',
 | 
				
			||||||
 | 
						0
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
						`t_tag_tree_team` tt
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
						tt.`is_deleted` = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP TABLE t_tag_tree_team;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE TABLE `t_resource_op_log` (
 | 
				
			||||||
 | 
					  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
 | 
				
			||||||
 | 
					  `code_path` varchar(600) NOT NULL COMMENT '资源标签路径',
 | 
				
			||||||
 | 
					  `resource_code` varchar(32) NOT NULL COMMENT '资源编号',
 | 
				
			||||||
 | 
					  `resource_type` tinyint NOT NULL COMMENT '资源类型',
 | 
				
			||||||
 | 
					  `create_time` datetime NOT NULL,
 | 
				
			||||||
 | 
					  `creator_id` bigint NOT NULL,
 | 
				
			||||||
 | 
					  `creator` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
 | 
				
			||||||
 | 
					  `is_deleted` tinyint NOT NULL DEFAULT '0',
 | 
				
			||||||
 | 
					  `delete_time` datetime DEFAULT NULL,
 | 
				
			||||||
 | 
					  PRIMARY KEY (`id`),
 | 
				
			||||||
 | 
					  KEY `idx_resource_code` (`resource_code`) USING BTREE
 | 
				
			||||||
 | 
					) ENGINE=InnoDB COMMENT='资源操作记录';
 | 
				
			||||||
		Reference in New Issue
	
	Block a user