feat: 机器新增支持加密方法等

This commit is contained in:
meilin.huang
2025-01-18 13:43:01 +08:00
parent 5a6e9d81a7
commit 30ea36a722
33 changed files with 208 additions and 148 deletions

View File

@@ -11,7 +11,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^12.3.0",
"@vueuse/core": "^12.4.0",
"asciinema-player": "^3.8.1",
"axios": "^1.6.2",
"clipboard": "^2.0.11",
@@ -19,7 +19,7 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^5.6.0",
"element-plus": "^2.9.2",
"element-plus": "^2.9.3",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
@@ -60,8 +60,8 @@
"eslint": "^8.35.0",
"eslint-plugin-vue": "^9.31.0",
"prettier": "^3.2.5",
"sass": "^1.83.1",
"typescript": "^5.7.2",
"sass": "^1.83.4",
"typescript": "^5.7.3",
"vite": "^6.0.7",
"vue-eslint-parser": "^9.4.3"
},

View File

@@ -0,0 +1 @@
<svg t="1737075628303" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10200" width="48" height="48"><path d="M335.36 395.776c0 91.648 69.632 167.424 157.184 179.712v206.336c0 16.384 14.336 30.72 30.72 30.72s30.72-14.336 30.72-30.72v-91.648h71.68c16.384 0 30.72-14.336 30.72-30.72s-14.336-30.72-30.72-30.72h-71.68V573.44c81.92-16.384 144.896-89.6 144.896-177.664 0-99.84-81.92-181.76-181.76-181.76S335.36 295.424 335.36 395.776zM517.12 274.944c67.584 0 120.32 53.248 120.32 120.32 0 67.584-53.248 120.32-120.32 120.32S396.288 462.848 396.288 395.776 449.536 274.944 517.12 274.944z" p-id="10201"></path><path d="M900.608 152.576L522.752 30.208c-6.144-2.048-12.288-2.048-18.432 0L126.976 152.576c-12.288 4.096-20.48 16.384-20.48 28.672v477.696c2.048 16.384 24.576 159.232 396.288 332.8 4.096 2.048 8.192 2.048 12.288 2.048 4.096 0 8.192 0 12.288-2.048 369.664-173.568 394.24-316.416 396.288-332.8V181.248c-2.56-12.288-10.752-24.576-23.04-28.672z m-40.96 502.272c-2.048 10.24-36.864 126.464-347.136 275.456-312.32-148.992-345.088-265.216-347.136-275.456V203.776l347.136-114.176 347.136 114.176v451.072z" p-id="10202"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg t="1737074876174" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8851" width="48" height="48"><path d="M392.92 608.306a224.526 224.526 0 1 1 224.527-224.527 224.526 224.526 0 0 1-224.526 224.527z m0-384.902a160.376 160.376 0 1 0 160.377 160.375A160.376 160.376 0 0 0 392.92 223.404z" p-id="8852"></path><path d="M713.673 864.907h-64.15a256.601 256.601 0 0 0-513.204 0H72.17a320.752 320.752 0 0 1 641.504 0zM665.56 576.231v-64.15a144.338 144.338 0 1 0-38.01-283.706l-16.839-61.905a208.489 208.489 0 1 1 54.849 409.76z" p-id="8853"></path><path d="M954.236 800.757h-64.15A224.526 224.526 0 0 0 665.56 576.23v-64.15a288.677 288.677 0 0 1 288.676 288.676z" p-id="8854"></path></svg>

After

Width:  |  Height:  |  Size: 728 B

View File

@@ -0,0 +1,38 @@
<template>
<el-form-item v-bind="$attrs">
<template #label>
{{ props.label }}
<el-tooltip :placement="props.placement">
<template #content>
<span v-html="props.tooltip"></span>
</template>
<SvgIcon name="QuestionFilled" />
</el-tooltip>
</template>
<!-- 遍历父组件传入的 solts 透传给子组件 -->
<template v-for="(_, key) in useSlots()" v-slot:[key]>
<slot :name="key"></slot>
</template>
</el-form-item>
</template>
<script setup lang="ts">
import { useSlots } from 'vue';
const props = defineProps({
label: {
type: String,
require: true,
},
tooltip: {
type: String,
require: true,
},
placement: {
type: String,
default: 'top',
},
});
</script>

View File

@@ -32,6 +32,9 @@ const MonacoEditorBox = (props: MonacoEditorDialogProps): void => {
if (props.canChangeLang === undefined) {
props.canChangeLang = true;
}
if (props.content === undefined) {
props.content = '';
}
// 创建 虚拟dom
boxInstance = h(MonacoEditorDialog, {
@@ -51,7 +54,6 @@ const MonacoEditorBox = (props: MonacoEditorDialogProps): void => {
// 移除 container DOM 元素
document.body.removeChild(container);
props.closeFn && props.closeFn();
console.log('close editor');
},
onConfirm: () => {
let value = props.content;

View File

@@ -31,6 +31,9 @@ export default {
ipAndPort: 'ip and port',
connSuccess: 'be connected successfully',
noAcErrMsg: 'Please complete the voucher account information',
ciphers: 'Ciphers',
keyExchanges: 'Key Exchanges',
multiValuePlaceholder: `multiple values are separated by ','`,
// MachineRec
playback: 'Playback',

View File

@@ -35,6 +35,7 @@ export default {
linkAddress: 'Link Address',
linkPlaceholder: 'External/embedded links (http://xxx.com)',
menuNameRuleMsg: 'Please enter a resource name',
codeRuleMsg: 'Please enter a resource code',
routeNameNotEmpty: 'Route names cannot be empty',
resourceCodePatternErrMsg: 'Only 1-32 uppercase letters, numbers, and -.: characters are allowed',
assignedRole: 'Assigned Role',

View File

@@ -32,6 +32,9 @@ export default {
ipAndPort: 'ip和port',
connSuccess: '连接成功',
noAcErrMsg: '请完善授权凭证账号信息',
ciphers: '加密算法',
keyExchanges: '密钥交换',
multiValuePlaceholder: `多个值用 ',' 隔开`,
// MachineRec
playback: '回放',

View File

@@ -34,6 +34,7 @@ export default {
linkAddress: '链接地址',
linkPlaceholder: '外链/内嵌的链接地址http://xxx.com',
menuNameRuleMsg: '请输入资源名称',
codeRuleMsg: '请输入code',
routeNameNotEmpty: '路由名不能为空',
resourceCodePatternErrMsg: '只允许输入1-32位大小写字母、数字、_-.:',
assignedRole: '已分配角色',

View File

@@ -15,14 +15,8 @@
<el-form-item prop="status" :label="$t('common.status')">
<EnumSelect :enums="ProcdefStatus" v-model="form.status" />
</el-form-item>
<el-form-item prop="condition" :label="$t('flow.triggeringCondition')">
<template #label>
{{ $t('flow.triggeringCondition') }}
<el-tooltip :content="$t('flow.triggeringConditionTips')" placement="top">
<SvgIcon name="question-filled" />
</el-tooltip>
</template>
<FormItemTooltip prop="condition" :label="$t('flow.triggeringCondition')" :tooltip="$t('flow.triggeringConditionTips')">
<el-input
v-model="form.condition"
:rows="10"
@@ -31,7 +25,8 @@
auto-complete="off"
clearable
></el-input>
</el-form-item>
</FormItemTooltip>
<el-form-item prop="remark" :label="$t('common.remark')">
<el-input v-model.trim="form.remark" auto-complete="off" clearable></el-input>
</el-form-item>
@@ -98,6 +93,7 @@ import { TagResourceTypeEnum, TagResourceTypePath } from '@/common/commonEnum';
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
import { useI18nFormValidate, useI18nPleaseInput, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
import { useI18n } from 'vue-i18n';
import FormItemTooltip from '@/components/form/FormItemTooltip.vue';
const { t } = useI18n();

View File

@@ -1,34 +1,32 @@
<template>
<div>
<el-tree-select
v-bind="$attrs"
v-model="state.selectTags"
@change="changeTag"
:data="tags"
:placeholder="$t('tag.selectTagPlaceholder')"
:default-expanded-keys="defaultExpandedKeys"
show-checkbox
node-key="codePath"
:props="{
value: 'codePath',
label: 'codePath',
children: 'children',
}"
>
<template #default="{ data }">
<span class="custom-tree-node">
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" class="mr2" />
<span style="font-size: 13px">
{{ 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>
<el-tree-select
v-bind="$attrs"
v-model="state.selectTags"
@change="changeTag"
:data="tags"
:placeholder="$t('tag.selectTagPlaceholder')"
:default-expanded-keys="defaultExpandedKeys"
show-checkbox
node-key="codePath"
:props="{
value: 'codePath',
label: 'codePath',
children: 'children',
}"
>
<template #default="{ data }">
<span class="custom-tree-node">
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" class="mr2" />
<span style="font-size: 13px">
{{ 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>
</template>
</el-tree-select>
</div>
</span>
</template>
</el-tree-select>
</template>
<script lang="ts" setup>

View File

@@ -18,7 +18,6 @@
}
"
:select-tags="form.tagCodePaths"
style="width: 100%"
/>
</el-form-item>

View File

@@ -82,45 +82,23 @@
<el-row>
<el-col :span="12">
<el-form-item class="w100" prop="updField">
<template #label>
{{ $t('db.updateField') }}
<el-tooltip :content="$t('db.updateFieldTips')" placement="top">
<SvgIcon name="question-filled" />
</el-tooltip>
</template>
<FormItemTooltip :label="$t('db.updateField')" prop="updField" :tooltip="$t('db.updateFieldTips')">
<el-input v-model.trim="form.updField" :placeholder="$t('db.updateFiledPlaceholder')" auto-complete="off" />
</el-form-item>
</FormItemTooltip>
</el-col>
<el-col :span="12">
<el-form-item class="w100" prop="updFieldVal">
<template #label>
{{ $t('db.updateFieldValue') }}
<el-tooltip :content="$t('db.updateFieldValueTips')" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip :label="$t('db.updateFieldValue')" prop="updFieldVal" :tooltip="$t('db.updateFieldValueTips')">
<el-input v-model.trim="form.updFieldVal" :placeholder="$t('db.updateFieldValuePlaceholder')" auto-complete="off" />
</el-form-item>
</FormItemTooltip>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item class="w100" prop="updFieldSrc">
<template #label>
{{ $t('db.fieldValueSrc') }}
<el-tooltip :content="$t('db.fieldValueSrcTips')" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip :label="$t('db.fieldValueSrc')" prop="updFieldSrc" :tooltip="$t('db.fieldValueSrcTips')">
<el-input v-model.trim="form.updFieldSrc" :placeholder="$t('db.fieldValueSrcPlaceholder')" auto-complete="off" />
</el-form-item>
</FormItemTooltip>
</el-col>
</el-row>
</el-tab-pane>
@@ -228,6 +206,7 @@ import EnumSelect from '@/components/enumselect/EnumSelect.vue';
import { DbDataSyncDuplicateStrategyEnum } from './enums';
import { useI18nFormValidate, useI18nPleaseInput, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
import { useI18n } from 'vue-i18n';
import FormItemTooltip from '@/components/form/FormItemTooltip.vue';
const { t } = useI18n();

View File

@@ -17,7 +17,6 @@
}
"
:select-tags="form.tagCodePaths"
style="width: 100%"
/>
</el-form-item>
<el-form-item prop="name" :label="$t('common.name')" required>
@@ -61,6 +60,13 @@
<el-form-item prop="sshTunnelMachineId" :label="$t('machine.sshTunnel')">
<ssh-tunnel-select v-model="form.sshTunnelMachineId" />
</el-form-item>
<el-form-item prop="ciphers" :label="$t('machine.ciphers')">
<el-input v-model="form.extra.ciphers" :placeholder="$t('machine.multiValuePlaceholder')"></el-input>
</el-form-item>
<el-form-item prop="keyExchanges" :label="$t('machine.keyExchanges')">
<el-input v-model="form.extra.keyExchanges" :placeholder="$t('machine.multiValuePlaceholder')"></el-input>
</el-form-item>
</el-form>
<template #footer>
@@ -152,6 +158,7 @@ const defaultForm = {
remark: '',
sshTunnelMachineId: null as any,
enableRecorder: -1,
extra: { ciphers: '', keyExchanges: '' },
};
const state = reactive({
@@ -175,6 +182,7 @@ watchEffect(() => {
state.form = { ...machine };
state.form.tagCodePaths = machine.tags.map((t: any) => t.codePath);
state.form.authCerts = machine.authCerts || [];
state.form.extra = machine.extra || {};
} else {
state.form = { ...defaultForm };
state.form.authCerts = [];

View File

@@ -14,7 +14,6 @@
"
multiple
:select-tags="form.tagCodePaths"
style="width: 100%"
/>
</el-form-item>

View File

@@ -18,7 +18,6 @@
"
multiple
:select-tags="form.tagCodePaths"
style="width: 100%"
/>
</el-form-item>
<el-form-item prop="name" :label="$t('common.name')" required>

View File

@@ -67,6 +67,8 @@
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<SvgIcon :name="getMenuIcon(data)" class="mb3 mr3" />
<span v-if="data.type == ResourceTypeEnum.Menu.value">{{ $t(node.label) }}</span>
<span v-if="data.type == ResourceTypeEnum.Permission.value" style="color: #67c23a">{{ $t(node.label) }}</span>
</span>
@@ -86,6 +88,7 @@ import { TableColumn } from '@/components/pagetable';
import { SearchItem } from '@/components/SearchForm';
import { ResourceTypeEnum, RoleStatusEnum } from '../enums';
import { useI18n } from 'vue-i18n';
import { getMenuIcon } from '../resource/index';
const { t } = useI18n();

View File

@@ -14,17 +14,9 @@
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item class="w100" prop="code" label="path|code">
<template #label>
path|code
<el-tooltip :content="$t('system.menu.menuCodeTips')" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip class="w100" label="path|code" prop="code" :tooltip="$t('system.menu.menuCodeTips')">
<el-input v-model.trim="form.code" :placeholder="$t('system.menu.menuCodePlaceholder')" auto-complete="off"></el-input>
</el-form-item>
</FormItemTooltip>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue">
<el-form-item class="w100" :label="$t('system.menu.icon')">
@@ -32,87 +24,57 @@
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue">
<el-form-item class="w100">
<template #label>
{{ $t('system.menu.routerName') }}
<el-tooltip :content="$t('system.menu.routerNameTips')" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip class="w100" :label="$t('system.menu.routerName')" prop="meta.routeName" :tooltip="$t('system.menu.routerNameTips')">
<el-input v-model.trim="form.meta.routeName"></el-input>
</el-form-item>
</FormItemTooltip>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue">
<el-form-item class="w100" prop="code">
<template #label>
{{ $t('system.menu.componentPath') }}
<el-tooltip :content="$t('system.menu.componentPathTips')" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip
class="w100"
:label="$t('system.menu.routerName')"
prop="meta.component"
:tooltip="$t('system.menu.componentPathTips')"
>
<el-input v-model.trim="form.meta.component"></el-input>
</el-form-item>
</FormItemTooltip>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue">
<el-form-item class="w100" prop="isKeepAlive">
<template #label>
{{ $t('system.menu.isCache') }}
<el-tooltip :content="$t('system.menu.isCacheTips')" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip class="w100" :label="$t('system.menu.isCache')" prop="meta.isKeepAlive" :tooltip="$t('system.menu.isCacheTips')">
<el-select v-model="form.meta.isKeepAlive" class="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
</el-form-item>
</FormItemTooltip>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue">
<el-form-item class="w100">
<template #label>
{{ $t('system.menu.isHide') }}
<el-tooltip :content="$t('system.menu.isHideTips')" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip class="w100" :label="$t('system.menu.isHide')" prop="meta.isHide" :tooltip="$t('system.menu.isHideTips')">
<el-select v-model="form.meta.isHide" class="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
</el-form-item>
</FormItemTooltip>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue">
<el-form-item class="w100" prop="code" :label="$t('system.menu.tagIsDelete')">
<el-form-item class="w100" prop="meta.isAffix" :label="$t('system.menu.tagIsDelete')">
<el-select v-model="form.meta.isAffix" class="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue">
<el-form-item class="w100" prop="linkType">
<template #label>
{{ $t('system.menu.externalLink') }}
<el-tooltip content="" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<FormItemTooltip
class="w100"
:label="$t('system.menu.externalLink')"
prop="meta.linkType"
:tooltip="$t('system.menu.externalLinkTips')"
>
<el-select class="w100" @change="changeLinkType" v-model="form.meta.linkType">
<el-option :key="0" :label="$t('system.menu.no')" :value="0"> </el-option>
<el-option :key="1" :label="$t('system.menu.inline')" :value="1"> </el-option>
<el-option :key="2" :label="$t('system.menu.externalLink')" :value="2"> </el-option>
</el-select>
</el-form-item>
</FormItemTooltip>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="form.type === menuTypeValue && form.meta.linkType > 0">
<el-form-item prop="code" :label="$t('system.menu.linkAddress')" class="w100">
<el-form-item prop="meta.link" :label="$t('system.menu.linkAddress')" class="w100">
<el-input v-model.trim="form.meta.link" :placeholder="$t('system.menu.linkPlaceholder')"></el-input>
</el-form-item>
</el-col>
@@ -138,6 +100,7 @@ import { notEmpty } from '@/common/assert';
import iconSelector from '@/components/iconSelector/index.vue';
import { useI18n } from 'vue-i18n';
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
import FormItemTooltip from '@/components/form/FormItemTooltip.vue';
const { t } = useI18n();
@@ -183,6 +146,13 @@ const rules = {
trigger: ['change', 'blur'],
},
],
code: [
{
required: true,
message: t('system.menu.codeRuleMsg'),
trigger: ['change', 'blur'],
},
],
};
const trueFalseOption = [

View File

@@ -37,6 +37,8 @@
>
<template #default="{ data }">
<span class="custom-tree-node">
<SvgIcon :name="getMenuIcon(data)" class="mb3" />
<span style="font-size: 13px" v-if="data.type === menuTypeValue">
<span style="color: #3c8dbc"></span>
<span v-if="data.status == 1">{{ $t(data.name) }}</span>
@@ -46,6 +48,7 @@
{{ data.children.length }}
</el-tag>
</span>
<span style="font-size: 13px" v-if="data.type === permissionTypeValue">
<span style="color: #3c8dbc"></span>
<span :style="data.status == 1 ? 'color: #67c23a;' : 'color: #f67c6c;'">
@@ -142,7 +145,7 @@
<script lang="ts" setup>
import { ref, toRefs, reactive, onMounted, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { ElMessage } from 'element-plus';
import ResourceEdit from './ResourceEdit.vue';
import { ResourceTypeEnum, RoleStatusEnum } from '../enums';
import { resourceApi } from '../api';
@@ -153,6 +156,7 @@ import { Splitpanes, Pane } from 'splitpanes';
import { isPrefixSubsequence } from '@/common/utils/string';
import { useI18n } from 'vue-i18n';
import { useI18nDeleteConfirm, useI18nDeleteSuccessMsg } from '@/hooks/useI18n';
import { getMenuIcon } from './index';
const { t } = useI18n();

View File

@@ -1 +1,22 @@
import { ResourceTypeEnum } from '../enums';
export { default } from './ResourceList.vue';
/**
* 获取menu icon
* @param menu menu信息
* @returns icon name
*/
export function getMenuIcon(menu: any) {
if (menu.type == ResourceTypeEnum.Permission.value) {
return 'icon menu/permission';
}
if (!menu.meta) {
return '';
}
const meta = JSON.parse(menu.meta);
if (meta) {
return meta.icon;
}
return '';
}

View File

@@ -18,6 +18,7 @@
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<SvgIcon :name="getMenuIcon(data)" class="mb3 mr3" />
<span v-if="data.type == ResourceTypeEnum.Menu.value">{{ $t(node.label) }}</span>
<span v-if="data.type == ResourceTypeEnum.Permission.value" style="color: #67c23a">{{ $t(node.label) }}</span>
</span>
@@ -39,6 +40,8 @@ import { ElMessage } from 'element-plus';
import { roleApi } from '../api';
import { ResourceTypeEnum } from '../enums';
import { useI18n } from 'vue-i18n';
import SvgIcon from '@/components/svgIcon/index.vue';
import { getMenuIcon } from '../resource';
const { t } = useI18n();

View File

@@ -4,6 +4,8 @@
<el-tree style="height: 50vh; overflow: auto" :data="resources" node-key="id" :props="defaultProps">
<template #default="{ node, data }">
<span class="custom-tree-node">
<SvgIcon :name="getMenuIcon(data)" class="mb3 mr3" />
<span v-if="data.type == ResourceTypeEnum.Menu.value">{{ $t(node.label) }}</span>
<span v-if="data.type == ResourceTypeEnum.Permission.value" style="color: #67c23a">{{ $t(node.label) }}</span>
@@ -36,6 +38,7 @@
import { toRefs, reactive, watch } from 'vue';
import { ResourceTypeEnum } from '../enums';
import { formatDate } from '@/common/utils/format';
import { getMenuIcon } from '../resource/index';
const props = defineProps({
visible: {