mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	reafctor: 团队管理与授权凭证优化
This commit is contained in:
		@@ -89,13 +89,21 @@ type RouterConvCallbackFunc = (router: any) => void;
 | 
			
		||||
 * @param meta.link ==> 外链地址
 | 
			
		||||
 * */
 | 
			
		||||
export function backEndRouterConverter(routes: any, callbackFunc: RouterConvCallbackFunc = null as any, parentPath: string = '/') {
 | 
			
		||||
    if (!routes) return [];
 | 
			
		||||
    return routes.map((item: any) => {
 | 
			
		||||
    if (!routes) {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const routeItems = [];
 | 
			
		||||
    for (let item of routes) {
 | 
			
		||||
        if (!item.meta) {
 | 
			
		||||
            return item;
 | 
			
		||||
        }
 | 
			
		||||
        // 将json字符串的meta转为对象
 | 
			
		||||
        item.meta = JSON.parse(item.meta);
 | 
			
		||||
        if (item.meta.isHide) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 将meta.comoponet 解析为route.component
 | 
			
		||||
        if (item.meta.component) {
 | 
			
		||||
            item.component = dynamicImport(dynamicViewsModules, item.meta.component);
 | 
			
		||||
@@ -126,8 +134,10 @@ export function backEndRouterConverter(routes: any, callbackFunc: RouterConvCall
 | 
			
		||||
        // 存在回调,则执行回调
 | 
			
		||||
        callbackFunc && callbackFunc(item);
 | 
			
		||||
        item.children && backEndRouterConverter(item.children, callbackFunc, item.path);
 | 
			
		||||
        return item;
 | 
			
		||||
    });
 | 
			
		||||
        routeItems.push(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return routeItems;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -1,59 +1,87 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div class="auth-cert-manage">
 | 
			
		||||
        <el-table :data="authCerts" max-height="180" stripe style="width: 100%" size="small">
 | 
			
		||||
            <el-table-column min-wdith="120px">
 | 
			
		||||
                <template #header>
 | 
			
		||||
                    <el-button class="ml0" type="primary" circle size="small" icon="Plus" @click="edit(null)"> </el-button>
 | 
			
		||||
                </template>
 | 
			
		||||
                <template #default="scope">
 | 
			
		||||
                    <el-link @click="edit(scope.row)" type="primary" icon="edit"></el-link>
 | 
			
		||||
                    <el-link class="ml5" v-auth="'machine:file:del'" type="danger" @click="deleteRow(scope.$index)" icon="delete"></el-link>
 | 
			
		||||
                </template>
 | 
			
		||||
            </el-table-column>
 | 
			
		||||
 | 
			
		||||
            <el-table-column prop="name" label="名称" min-width="100px"> </el-table-column>
 | 
			
		||||
            <el-table-column prop="username" label="用户名" min-width="120px" show-overflow-tooltip> </el-table-column>
 | 
			
		||||
            <el-table-column prop="ciphertextType" label="密文类型" width="100px">
 | 
			
		||||
                <template #default="scope">
 | 
			
		||||
                    <EnumTag :value="scope.row.ciphertextType" :enums="AuthCertCiphertextTypeEnum" />
 | 
			
		||||
                </template>
 | 
			
		||||
            </el-table-column>
 | 
			
		||||
            <el-table-column prop="type" label="凭证类型" width="100px">
 | 
			
		||||
                <template #default="scope">
 | 
			
		||||
                    <EnumTag :value="scope.row.type" :enums="AuthCertTypeEnum" />
 | 
			
		||||
                </template>
 | 
			
		||||
            </el-table-column>
 | 
			
		||||
        </el-table>
 | 
			
		||||
 | 
			
		||||
        <el-dialog title="凭证保存" v-model="state.dvisible" :show-close="false" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
 | 
			
		||||
            <el-form ref="acForm" :model="state.form" label-width="auto">
 | 
			
		||||
                <el-form-item prop="type" label="凭证类型" required>
 | 
			
		||||
                    <el-select style="width: 100%" v-model="form.type" placeholder="请选择凭证类型">
 | 
			
		||||
                        <el-option v-for="item in AuthCertTypeEnum" :key="item.value" :label="item.label" :value="item.value"> </el-option>
 | 
			
		||||
    <div class="auth-cert-edit">
 | 
			
		||||
        <el-dialog title="凭证保存" v-model="dialogVisible" :show-close="false" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
 | 
			
		||||
            <el-form ref="acForm" :model="state.form" label-width="auto" :rules="rules">
 | 
			
		||||
                <el-form-item prop="ciphertextType" label="密文类型" required>
 | 
			
		||||
                    <el-select
 | 
			
		||||
                        :disabled="form.id && props.resourceEdit"
 | 
			
		||||
                        v-model="form.ciphertextType"
 | 
			
		||||
                        placeholder="请选择密文类型"
 | 
			
		||||
                        @change="changeCiphertextType"
 | 
			
		||||
                    >
 | 
			
		||||
                        <el-option
 | 
			
		||||
                            v-for="item in AuthCertCiphertextTypeEnum"
 | 
			
		||||
                            :key="item.value"
 | 
			
		||||
                            :label="item.label"
 | 
			
		||||
                            :value="item.value"
 | 
			
		||||
                            v-show="!props.disableCiphertextType?.includes(item.value)"
 | 
			
		||||
                        >
 | 
			
		||||
                        </el-option>
 | 
			
		||||
                    </el-select>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item prop="ciphertextType" label="密文类型" required>
 | 
			
		||||
                    <el-select style="width: 100%" v-model="form.ciphertextType" placeholder="请选择密文类型">
 | 
			
		||||
                        <el-option v-for="item in AuthCertCiphertextTypeEnum" :key="item.value" :label="item.label" :value="item.value"> </el-option>
 | 
			
		||||
 | 
			
		||||
                <el-form-item prop="type" label="凭证类型" required>
 | 
			
		||||
                    <el-select style="width: 100%" v-model="form.type" placeholder="请选择凭证类型">
 | 
			
		||||
                        <el-option
 | 
			
		||||
                            v-for="item in AuthCertTypeEnum"
 | 
			
		||||
                            :key="item.value"
 | 
			
		||||
                            :label="item.label"
 | 
			
		||||
                            :value="item.value"
 | 
			
		||||
                            v-show="!props.disableType?.includes(item.value)"
 | 
			
		||||
                        >
 | 
			
		||||
                        </el-option>
 | 
			
		||||
                    </el-select>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
 | 
			
		||||
                <el-form-item prop="name" label="名称" required>
 | 
			
		||||
                    <el-input :disabled="form.id" v-model="form.name"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item prop="username" label="用户名">
 | 
			
		||||
                    <el-input v-model="form.username"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.Password.value" prop="ciphertext" label="密码">
 | 
			
		||||
                    <el-input type="password" show-password clearable v-model.trim="form.ciphertext" placeholder="请输入密码" autocomplete="new-password">
 | 
			
		||||
                    </el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="ciphertext" label="秘钥">
 | 
			
		||||
                    <el-input type="textarea" :rows="5" v-model="form.ciphertext" placeholder="请将私钥文件内容拷贝至此"> </el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="passphrase" label="秘钥密码">
 | 
			
		||||
                    <el-input type="password" v-model="form.extra.passphrase"> </el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
 | 
			
		||||
                <template v-if="form.ciphertextType != AuthCertCiphertextTypeEnum.Public.value">
 | 
			
		||||
                    <el-form-item prop="username" label="用户名">
 | 
			
		||||
                        <el-input :disabled="form.id && props.resourceEdit" v-model="form.username"></el-input>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
 | 
			
		||||
                    <el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.Password.value" prop="ciphertext" label="密码">
 | 
			
		||||
                        <el-input type="password" show-password clearable v-model.trim="form.ciphertext" placeholder="请输入密码" autocomplete="new-password">
 | 
			
		||||
                            <template #suffix>
 | 
			
		||||
                                <SvgIcon v-if="form.id" v-auth="'authcert:showciphertext'" @click="getCiphertext" name="search" />
 | 
			
		||||
                            </template>
 | 
			
		||||
                        </el-input>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
 | 
			
		||||
                    <el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="ciphertext" label="秘钥">
 | 
			
		||||
                        <div class="w100" style="position: relative">
 | 
			
		||||
                            <SvgIcon
 | 
			
		||||
                                v-if="form.id"
 | 
			
		||||
                                v-auth="'authcert:showciphertext'"
 | 
			
		||||
                                @click="getCiphertext"
 | 
			
		||||
                                name="search"
 | 
			
		||||
                                style="position: absolute; top: 5px; right: 5px; cursor: pointer; z-index: 1"
 | 
			
		||||
                            />
 | 
			
		||||
                            <el-input type="textarea" :rows="5" v-model="form.ciphertext" placeholder="请将私钥文件内容拷贝至此"> </el-input>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
 | 
			
		||||
                    <el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="passphrase" label="秘钥密码">
 | 
			
		||||
                        <el-input type="password" show-password v-model="form.extra.passphrase"> </el-input>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
                </template>
 | 
			
		||||
 | 
			
		||||
                <template v-else>
 | 
			
		||||
                    <el-form-item label="公共凭证">
 | 
			
		||||
                        <el-select default-first-option filterable v-model="form.ciphertext" @change="changePublicAuthCert">
 | 
			
		||||
                            <el-option v-for="item in state.publicAuthCerts" :key="item.name" :label="item.name" :value="item.name">
 | 
			
		||||
                                {{ item.name }}
 | 
			
		||||
                                <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
                                {{ item.username }}
 | 
			
		||||
                                <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
                                <EnumTag :value="item.ciphertextType" :enums="AuthCertCiphertextTypeEnum" />
 | 
			
		||||
                            </el-option>
 | 
			
		||||
                        </el-select>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
                </template>
 | 
			
		||||
 | 
			
		||||
                <el-form-item label="备注">
 | 
			
		||||
                    <el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
@@ -69,21 +97,29 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { onMounted, reactive, ref, toRefs } from 'vue';
 | 
			
		||||
import { reactive, ref, toRefs, onMounted, watch } from 'vue';
 | 
			
		||||
import { AuthCertTypeEnum, AuthCertCiphertextTypeEnum } from '../tag/enums';
 | 
			
		||||
import { resourceAuthCertApi } from '../tag/api';
 | 
			
		||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
			
		||||
import { ElMessage } from 'element-plus';
 | 
			
		||||
import { resourceAuthCertApi } from '../tag/api';
 | 
			
		||||
import { ResourceCodePattern } from '@/common/pattern';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    resourceType: { type: Number },
 | 
			
		||||
    resourceCode: { type: String },
 | 
			
		||||
    authCert: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
    },
 | 
			
		||||
    disableCiphertextType: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
    },
 | 
			
		||||
    disableType: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
    },
 | 
			
		||||
    // 是否为资源编辑该授权凭证
 | 
			
		||||
    resourceEdit: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        default: true,
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const authCerts = defineModel<any>('modelValue', { required: true, default: [] });
 | 
			
		||||
 | 
			
		||||
const acForm: any = ref(null);
 | 
			
		||||
 | 
			
		||||
const DefaultForm = {
 | 
			
		||||
    id: null,
 | 
			
		||||
    name: '',
 | 
			
		||||
@@ -94,47 +130,85 @@ const DefaultForm = {
 | 
			
		||||
    extra: {} as any,
 | 
			
		||||
    remark: '',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const rules = {
 | 
			
		||||
    name: [
 | 
			
		||||
        {
 | 
			
		||||
            required: true,
 | 
			
		||||
            message: '请输入凭证名',
 | 
			
		||||
            trigger: ['change', 'blur'],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            pattern: ResourceCodePattern.pattern,
 | 
			
		||||
            message: ResourceCodePattern.message,
 | 
			
		||||
            trigger: ['blur'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['confirm']);
 | 
			
		||||
 | 
			
		||||
const dialogVisible = defineModel<boolean>('visible', { default: false });
 | 
			
		||||
 | 
			
		||||
const acForm: any = ref(null);
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    dvisible: false,
 | 
			
		||||
    params: [] as any,
 | 
			
		||||
    form: { ...DefaultForm },
 | 
			
		||||
    btnLoading: false,
 | 
			
		||||
    edit: false,
 | 
			
		||||
    publicAuthCerts: [] as any,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
    setForm(props.authCert);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
    () => props.authCert,
 | 
			
		||||
    (val: any) => {
 | 
			
		||||
        setForm(val);
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const setForm = (val: any) => {
 | 
			
		||||
    if (!val.extra) {
 | 
			
		||||
        val.extra = {};
 | 
			
		||||
    }
 | 
			
		||||
    state.form = val;
 | 
			
		||||
    if (state.form.ciphertextType == AuthCertCiphertextTypeEnum.Public.value) {
 | 
			
		||||
        getPublicAuthCerts();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const { form, btnLoading } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
    getAuthCerts();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const getAuthCerts = async () => {
 | 
			
		||||
    if (!props.resourceCode || !props.resourceType) {
 | 
			
		||||
        return;
 | 
			
		||||
const changeCiphertextType = (val: any) => {
 | 
			
		||||
    if (val == AuthCertCiphertextTypeEnum.Public.value) {
 | 
			
		||||
        getPublicAuthCerts();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const changePublicAuthCert = (val: string) => {
 | 
			
		||||
    // 使用公共授权凭证名称赋值username
 | 
			
		||||
    state.form.username = val;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getPublicAuthCerts = async () => {
 | 
			
		||||
    const res = await resourceAuthCertApi.listByQuery.request({
 | 
			
		||||
        resourceCode: props.resourceCode,
 | 
			
		||||
        resourceType: props.resourceType,
 | 
			
		||||
        type: AuthCertTypeEnum.Public.value,
 | 
			
		||||
        pageNum: 1,
 | 
			
		||||
        pageSize: 100,
 | 
			
		||||
    });
 | 
			
		||||
    authCerts.value = res.list?.reverse() || [];
 | 
			
		||||
    state.publicAuthCerts = res.list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const edit = (form: any) => {
 | 
			
		||||
    if (form) {
 | 
			
		||||
        state.form = form;
 | 
			
		||||
        state.edit = true;
 | 
			
		||||
    }
 | 
			
		||||
    state.dvisible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteRow = (idx: any) => {
 | 
			
		||||
    authCerts.value.splice(idx, 1);
 | 
			
		||||
const getCiphertext = async () => {
 | 
			
		||||
    const res = await resourceAuthCertApi.detail.request({ name: state.form.name });
 | 
			
		||||
    state.form.ciphertext = res.ciphertext;
 | 
			
		||||
    state.form.extra.passphrase = res.extra?.passphrase;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const cancelEdit = () => {
 | 
			
		||||
    state.dvisible = false;
 | 
			
		||||
    dialogVisible.value = false;
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
        state.form = { ...DefaultForm };
 | 
			
		||||
    }, 300);
 | 
			
		||||
@@ -143,28 +217,7 @@ const cancelEdit = () => {
 | 
			
		||||
const btnOk = async () => {
 | 
			
		||||
    acForm.value.validate(async (valid: boolean) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
            const isEdit = state.form.id;
 | 
			
		||||
            if (isEdit || state.edit) {
 | 
			
		||||
                cancelEdit();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (authCerts.value?.filter((x: any) => x.username == state.form.username || x.name == state.form.name).length > 0) {
 | 
			
		||||
                ElMessage.error('该名称或用户名已存在于该账号列表中');
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            const res = await resourceAuthCertApi.listByQuery.request({
 | 
			
		||||
                name: state.form.name,
 | 
			
		||||
                pageNum: 1,
 | 
			
		||||
                pageSize: 100,
 | 
			
		||||
            });
 | 
			
		||||
            if (res.total) {
 | 
			
		||||
                ElMessage.error('该授权凭证名称已存在');
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            authCerts.value.push(state.form);
 | 
			
		||||
            cancelEdit();
 | 
			
		||||
            emit('confirm', state.form);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,136 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div class="auth-cert-manage">
 | 
			
		||||
        <el-table :data="authCerts" max-height="180" stripe size="small">
 | 
			
		||||
            <el-table-column min-wdith="120px">
 | 
			
		||||
                <template #header>
 | 
			
		||||
                    <el-button v-auth="'authcert:save'" class="ml0" type="primary" circle size="small" icon="Plus" @click="edit(null)"> </el-button>
 | 
			
		||||
                </template>
 | 
			
		||||
                <template #default="scope">
 | 
			
		||||
                    <el-button v-auth="'authcert:save'" @click="edit({ ...scope.row }, scope.$index)" type="primary" icon="edit" link></el-button>
 | 
			
		||||
                    <el-button class="ml1" v-auth="'authcert:del'" type="danger" @click="deleteRow(scope.$index)" icon="delete" link></el-button>
 | 
			
		||||
 | 
			
		||||
                    <el-button
 | 
			
		||||
                        title="测试连接"
 | 
			
		||||
                        :loading="props.testConnBtnLoading && scope.$index == state.idx"
 | 
			
		||||
                        :disabled="props.testConnBtnLoading"
 | 
			
		||||
                        class="ml1"
 | 
			
		||||
                        type="success"
 | 
			
		||||
                        @click="testConn(scope.row, scope.$index)"
 | 
			
		||||
                        icon="Link"
 | 
			
		||||
                        link
 | 
			
		||||
                    ></el-button>
 | 
			
		||||
                </template>
 | 
			
		||||
            </el-table-column>
 | 
			
		||||
 | 
			
		||||
            <el-table-column prop="name" label="名称" show-overflow-tooltip min-width="100px"> </el-table-column>
 | 
			
		||||
            <el-table-column prop="username" label="用户名" min-width="120px" show-overflow-tooltip> </el-table-column>
 | 
			
		||||
            <el-table-column prop="ciphertextType" label="密文类型" width="100px">
 | 
			
		||||
                <template #default="scope">
 | 
			
		||||
                    <EnumTag :value="scope.row.ciphertextType" :enums="AuthCertCiphertextTypeEnum" />
 | 
			
		||||
                </template>
 | 
			
		||||
            </el-table-column>
 | 
			
		||||
            <el-table-column prop="type" label="凭证类型" width="100px">
 | 
			
		||||
                <template #default="scope">
 | 
			
		||||
                    <EnumTag :value="scope.row.type" :enums="AuthCertTypeEnum" />
 | 
			
		||||
                </template>
 | 
			
		||||
            </el-table-column>
 | 
			
		||||
        </el-table>
 | 
			
		||||
 | 
			
		||||
        <ResourceAuthCertEdit v-model:visible="state.dvisible" :auth-cert="state.form" @confirm="btnOk" :disable-type="[AuthCertTypeEnum.Public.value]" />
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { onMounted, reactive } from 'vue';
 | 
			
		||||
import { AuthCertTypeEnum, AuthCertCiphertextTypeEnum } from '../tag/enums';
 | 
			
		||||
import { resourceAuthCertApi } from '../tag/api';
 | 
			
		||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
			
		||||
import ResourceAuthCertEdit from './ResourceAuthCertEdit.vue';
 | 
			
		||||
import { ElMessage } from 'element-plus';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    resourceType: { type: Number },
 | 
			
		||||
    resourceCode: { type: String },
 | 
			
		||||
    testConnBtnLoading: { type: Boolean },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const authCerts = defineModel<any>('modelValue', { required: true, default: [] });
 | 
			
		||||
const emit = defineEmits(['testConn']);
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    dvisible: false,
 | 
			
		||||
    params: [] as any,
 | 
			
		||||
    form: {},
 | 
			
		||||
    idx: -1,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
    getAuthCerts();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const getAuthCerts = async () => {
 | 
			
		||||
    if (!props.resourceCode || !props.resourceType) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const res = await resourceAuthCertApi.listByQuery.request({
 | 
			
		||||
        resourceCode: props.resourceCode,
 | 
			
		||||
        resourceType: props.resourceType,
 | 
			
		||||
        pageNum: 1,
 | 
			
		||||
        pageSize: 100,
 | 
			
		||||
    });
 | 
			
		||||
    authCerts.value = res.list?.reverse() || [];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const testConn = async (row: any, idx: number) => {
 | 
			
		||||
    state.idx = idx;
 | 
			
		||||
    emit('testConn', row);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const edit = (form: any, idx = -1) => {
 | 
			
		||||
    state.idx = idx;
 | 
			
		||||
    if (form) {
 | 
			
		||||
        state.form = form;
 | 
			
		||||
    } else {
 | 
			
		||||
        state.form = { ciphertextType: AuthCertCiphertextTypeEnum.Password.value, type: AuthCertTypeEnum.Private.value, extra: {} };
 | 
			
		||||
    }
 | 
			
		||||
    state.dvisible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteRow = (idx: any) => {
 | 
			
		||||
    authCerts.value.splice(idx, 1);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const cancelEdit = () => {
 | 
			
		||||
    state.dvisible = false;
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
        state.form = {};
 | 
			
		||||
    }, 300);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const btnOk = async (authCert: any) => {
 | 
			
		||||
    const isEdit = authCert.id;
 | 
			
		||||
    if (isEdit || state.idx > 0) {
 | 
			
		||||
        authCerts.value[state.idx] = authCert;
 | 
			
		||||
        cancelEdit();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (authCerts.value?.filter((x: any) => x.username == authCert.username || x.name == authCert.name).length > 0) {
 | 
			
		||||
        ElMessage.error('该名称或用户名已存在于该账号列表中');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const res = await resourceAuthCertApi.listByQuery.request({
 | 
			
		||||
        name: authCert.name,
 | 
			
		||||
        pageNum: 1,
 | 
			
		||||
        pageSize: 100,
 | 
			
		||||
    });
 | 
			
		||||
    if (res.total) {
 | 
			
		||||
        ElMessage.error('该授权凭证名称已存在');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    authCerts.value.push(authCert);
 | 
			
		||||
    cancelEdit();
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss"></style>
 | 
			
		||||
@@ -48,10 +48,15 @@
 | 
			
		||||
 | 
			
		||||
                <el-divider content-position="left">账号</el-divider>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <ResourceAuthCertEdit v-model="form.authCerts" :resource-code="form.code" :resource-type="TagResourceTypeEnum.Machine.value" />
 | 
			
		||||
                    <ResourceAuthCertTableEdit
 | 
			
		||||
                        v-model="form.authCerts"
 | 
			
		||||
                        :resource-code="form.code"
 | 
			
		||||
                        :resource-type="TagResourceTypeEnum.Machine.value"
 | 
			
		||||
                        :test-conn-btn-loading="testConnBtnLoading"
 | 
			
		||||
                        @test-conn="testConn"
 | 
			
		||||
                    />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <!-- <el-tab-pane label="其他配置" name="other"> -->
 | 
			
		||||
                <el-divider content-position="left">其他</el-divider>
 | 
			
		||||
                <el-form-item prop="enableRecorder" label="终端回放">
 | 
			
		||||
                    <el-checkbox v-model="form.enableRecorder" :true-value="1" :false-value="-1"></el-checkbox>
 | 
			
		||||
@@ -64,7 +69,6 @@
 | 
			
		||||
 | 
			
		||||
            <template #footer>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <el-button @click="testConn" :loading="testConnBtnLoading" type="success">测试连接</el-button>
 | 
			
		||||
                    <el-button @click="cancel()">取 消</el-button>
 | 
			
		||||
                    <el-button type="primary" :loading="saveBtnLoading" @click="btnOk">确 定</el-button>
 | 
			
		||||
                </div>
 | 
			
		||||
@@ -78,7 +82,7 @@ import { reactive, ref, toRefs, watch } from 'vue';
 | 
			
		||||
import { machineApi } from './api';
 | 
			
		||||
import { ElMessage } from 'element-plus';
 | 
			
		||||
import TagTreeSelect from '../component/TagTreeSelect.vue';
 | 
			
		||||
import ResourceAuthCertEdit from '../component/ResourceAuthCertEdit.vue';
 | 
			
		||||
import ResourceAuthCertTableEdit from '../component/ResourceAuthCertTableEdit.vue';
 | 
			
		||||
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
 | 
			
		||||
import { MachineProtocolEnum } from './enums';
 | 
			
		||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
			
		||||
@@ -165,7 +169,7 @@ const state = reactive({
 | 
			
		||||
    dialogVisible: false,
 | 
			
		||||
    sshTunnelMachineList: [] as any,
 | 
			
		||||
    form: defaultForm,
 | 
			
		||||
    submitForm: {},
 | 
			
		||||
    submitForm: {} as any,
 | 
			
		||||
    pwd: '',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -187,7 +191,7 @@ watch(props, async (newValue: any) => {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const testConn = async () => {
 | 
			
		||||
const testConn = async (authCert: any) => {
 | 
			
		||||
    machineForm.value.validate(async (valid: boolean) => {
 | 
			
		||||
        if (!valid) {
 | 
			
		||||
            ElMessage.error('请正确填写信息');
 | 
			
		||||
@@ -195,6 +199,7 @@ const testConn = async () => {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        state.submitForm = getReqForm();
 | 
			
		||||
        state.submitForm.authCerts = [authCert];
 | 
			
		||||
        await testConnExec();
 | 
			
		||||
        ElMessage.success('连接成功');
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,9 @@
 | 
			
		||||
                    <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
                </span>
 | 
			
		||||
 | 
			
		||||
                <el-button :disabled="data.status == -1" type="warning" @click="serviceManager(data)" link>脚本</el-button>
 | 
			
		||||
                <el-button v-if="data.protocol == MachineProtocolEnum.Ssh.value" :disabled="data.status == -1" type="warning" @click="serviceManager(data)" link
 | 
			
		||||
                    >脚本</el-button
 | 
			
		||||
                >
 | 
			
		||||
                <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
 | 
			
		||||
                <el-dropdown @command="handleCommand">
 | 
			
		||||
@@ -120,11 +122,19 @@
 | 
			
		||||
                        <el-dropdown-menu>
 | 
			
		||||
                            <el-dropdown-item :command="{ type: 'detail', data }"> 详情 </el-dropdown-item>
 | 
			
		||||
 | 
			
		||||
                            <el-dropdown-item :command="{ type: 'rdp-blank', data }" v-if="data.protocol == 2"> RDP(新窗口) </el-dropdown-item>
 | 
			
		||||
                            <el-dropdown-item :command="{ type: 'rdp-blank', data }" v-if="data.protocol == MachineProtocolEnum.Rdp.value">
 | 
			
		||||
                                RDP(新窗口)
 | 
			
		||||
                            </el-dropdown-item>
 | 
			
		||||
 | 
			
		||||
                            <el-dropdown-item :command="{ type: 'edit', data }" v-if="actionBtns[perms.updateMachine]"> 编辑 </el-dropdown-item>
 | 
			
		||||
 | 
			
		||||
                            <el-dropdown-item :command="{ type: 'process', data }" :disabled="data.status == -1"> 进程 </el-dropdown-item>
 | 
			
		||||
                            <el-dropdown-item
 | 
			
		||||
                                v-if="data.protocol == MachineProtocolEnum.Ssh.value"
 | 
			
		||||
                                :command="{ type: 'process', data }"
 | 
			
		||||
                                :disabled="data.status == -1"
 | 
			
		||||
                            >
 | 
			
		||||
                                进程
 | 
			
		||||
                            </el-dropdown-item>
 | 
			
		||||
 | 
			
		||||
                            <el-dropdown-item :command="{ type: 'terminalRec', data }" v-if="actionBtns[perms.updateMachine] && data.enableRecorder == 1">
 | 
			
		||||
                                终端回放
 | 
			
		||||
@@ -177,7 +187,12 @@
 | 
			
		||||
 | 
			
		||||
        <process-list v-model:visible="processDialog.visible" v-model:machineId="processDialog.machineId" />
 | 
			
		||||
 | 
			
		||||
        <script-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
 | 
			
		||||
        <script-manage
 | 
			
		||||
            :title="serviceDialog.title"
 | 
			
		||||
            v-model:visible="serviceDialog.visible"
 | 
			
		||||
            v-model:machineId="serviceDialog.machineId"
 | 
			
		||||
            :auth-cert-name="serviceDialog.authCertName"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <file-conf-list
 | 
			
		||||
            :title="fileDialog.title"
 | 
			
		||||
@@ -298,6 +313,7 @@ const state = reactive({
 | 
			
		||||
    serviceDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
        machineId: 0,
 | 
			
		||||
        authCertName: '',
 | 
			
		||||
        title: '',
 | 
			
		||||
    },
 | 
			
		||||
    processDialog: {
 | 
			
		||||
@@ -468,9 +484,11 @@ const deleteMachine = async () => {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const serviceManager = (row: any) => {
 | 
			
		||||
    const authCert = row.selectAuthCert;
 | 
			
		||||
    state.serviceDialog.machineId = row.id;
 | 
			
		||||
    state.serviceDialog.authCertName = authCert.name;
 | 
			
		||||
    state.serviceDialog.visible = true;
 | 
			
		||||
    state.serviceDialog.title = `${row.name} => ${row.ip}`;
 | 
			
		||||
    state.serviceDialog.title = `${row.name} => ${authCert.username}@${row.ip}`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -111,10 +111,16 @@
 | 
			
		||||
 | 
			
		||||
                    <process-list v-model:visible="processDialog.visible" v-model:machineId="processDialog.machineId" />
 | 
			
		||||
 | 
			
		||||
                    <script-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
 | 
			
		||||
                    <script-manage
 | 
			
		||||
                        :title="serviceDialog.title"
 | 
			
		||||
                        v-model:visible="serviceDialog.visible"
 | 
			
		||||
                        v-model:machineId="serviceDialog.machineId"
 | 
			
		||||
                        :auth-cert-name="serviceDialog.authCertName"
 | 
			
		||||
                    />
 | 
			
		||||
 | 
			
		||||
                    <file-conf-list
 | 
			
		||||
                        :title="fileDialog.title"
 | 
			
		||||
                        :auth-cert-name="fileDialog.authCertName"
 | 
			
		||||
                        v-model:visible="fileDialog.visible"
 | 
			
		||||
                        v-model:machineId="fileDialog.machineId"
 | 
			
		||||
                        :protocol="fileDialog.protocol"
 | 
			
		||||
@@ -129,6 +135,7 @@
 | 
			
		||||
                    >
 | 
			
		||||
                        <machine-file
 | 
			
		||||
                            :machine-id="state.filesystemDialog.machineId"
 | 
			
		||||
                            :auth-cert-name="state.filesystemDialog.authCertName"
 | 
			
		||||
                            :protocol="state.filesystemDialog.protocol"
 | 
			
		||||
                            :file-id="state.filesystemDialog.fileId"
 | 
			
		||||
                            :path="state.filesystemDialog.path"
 | 
			
		||||
@@ -202,6 +209,7 @@ const state = reactive({
 | 
			
		||||
    serviceDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
        machineId: 0,
 | 
			
		||||
        authCertName: '',
 | 
			
		||||
        title: '',
 | 
			
		||||
    },
 | 
			
		||||
    processDialog: {
 | 
			
		||||
@@ -213,10 +221,12 @@ const state = reactive({
 | 
			
		||||
        machineId: 0,
 | 
			
		||||
        protocol: 1,
 | 
			
		||||
        title: '',
 | 
			
		||||
        authCertName: '',
 | 
			
		||||
    },
 | 
			
		||||
    filesystemDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
        machineId: 0,
 | 
			
		||||
        authCertName: '',
 | 
			
		||||
        protocol: 1,
 | 
			
		||||
        title: '',
 | 
			
		||||
        fileId: 0,
 | 
			
		||||
@@ -280,8 +290,17 @@ const NodeTypeMachine = new NodeType(MachineNodeType.Machine)
 | 
			
		||||
    })
 | 
			
		||||
    .withContextMenuItems([
 | 
			
		||||
        new ContextmenuItem('detail', '详情').withIcon('More').withOnClick((node: any) => showInfo(node.params)),
 | 
			
		||||
        new ContextmenuItem('status', '状态').withIcon('Compass').withOnClick((node: any) => showMachineStats(node.params)),
 | 
			
		||||
        new ContextmenuItem('process', '进程').withIcon('DataLine').withOnClick((node: any) => showProcess(node.params)),
 | 
			
		||||
 | 
			
		||||
        new ContextmenuItem('status', '状态')
 | 
			
		||||
            .withIcon('Compass')
 | 
			
		||||
            .withHideFunc((node: any) => node.params.protocol != MachineProtocolEnum.Ssh.value)
 | 
			
		||||
            .withOnClick((node: any) => showMachineStats(node.params)),
 | 
			
		||||
 | 
			
		||||
        new ContextmenuItem('process', '进程')
 | 
			
		||||
            .withIcon('DataLine')
 | 
			
		||||
            .withHideFunc((node: any) => node.params.protocol != MachineProtocolEnum.Ssh.value)
 | 
			
		||||
            .withOnClick((node: any) => showProcess(node.params)),
 | 
			
		||||
 | 
			
		||||
        new ContextmenuItem('edit', '终端回放')
 | 
			
		||||
            .withIcon('Compass')
 | 
			
		||||
            .withOnClick((node: any) => showRec(node.params))
 | 
			
		||||
@@ -296,7 +315,11 @@ const NodeTypeAuthCert = new NodeType(MachineNodeType.AuthCert)
 | 
			
		||||
        new ContextmenuItem('term', '打开终端').withIcon('Monitor').withOnClick((node: any) => openTerminal(node.params)),
 | 
			
		||||
        new ContextmenuItem('term-ex', '打开终端(新窗口)').withIcon('Monitor').withOnClick((node: any) => openTerminal(node.params, true)),
 | 
			
		||||
        new ContextmenuItem('files', '文件管理').withIcon('FolderOpened').withOnClick((node: any) => showFileManage(node.params)),
 | 
			
		||||
        new ContextmenuItem('scripts', '脚本管理').withIcon('Files').withOnClick((node: any) => serviceManager(node.params)),
 | 
			
		||||
 | 
			
		||||
        new ContextmenuItem('scripts', '脚本管理')
 | 
			
		||||
            .withIcon('Files')
 | 
			
		||||
            .withHideFunc((node: any) => node.params.protocol != MachineProtocolEnum.Ssh.value)
 | 
			
		||||
            .withOnClick((node: any) => serviceManager(node.params)),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
const openTerminal = (machine: any, ex?: boolean) => {
 | 
			
		||||
@@ -356,9 +379,11 @@ const openTerminal = (machine: any, ex?: boolean) => {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const serviceManager = (row: any) => {
 | 
			
		||||
    const authCert = row.selectAuthCert;
 | 
			
		||||
    state.serviceDialog.machineId = row.id;
 | 
			
		||||
    state.serviceDialog.visible = true;
 | 
			
		||||
    state.serviceDialog.title = `${row.name} => ${row.ip}`;
 | 
			
		||||
    state.serviceDialog.authCertName = authCert.name;
 | 
			
		||||
    state.serviceDialog.title = `${row.name} => ${authCert.username}@${row.ip}`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -376,16 +401,19 @@ const search = async () => {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const showFileManage = (selectionData: any) => {
 | 
			
		||||
    const authCert = selectionData.selectAuthCert;
 | 
			
		||||
    if (selectionData.protocol == 1) {
 | 
			
		||||
        state.fileDialog.visible = true;
 | 
			
		||||
        state.fileDialog.protocol = selectionData.protocol;
 | 
			
		||||
        state.fileDialog.machineId = selectionData.id;
 | 
			
		||||
        state.fileDialog.title = `${selectionData.name} => ${selectionData.ip}`;
 | 
			
		||||
        state.fileDialog.authCertName = authCert.name;
 | 
			
		||||
        state.fileDialog.title = `${selectionData.name} => ${authCert.username}@${selectionData.ip}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (selectionData.protocol == 2) {
 | 
			
		||||
        state.filesystemDialog.protocol = 2;
 | 
			
		||||
        state.filesystemDialog.machineId = selectionData.id;
 | 
			
		||||
        state.filesystemDialog.authCertName = authCert.name;
 | 
			
		||||
        state.filesystemDialog.fileId = selectionData.id;
 | 
			
		||||
        state.filesystemDialog.path = '/';
 | 
			
		||||
        state.filesystemDialog.title = `远程桌面文件管理`;
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@
 | 
			
		||||
            draggable
 | 
			
		||||
            append-to-body
 | 
			
		||||
        >
 | 
			
		||||
            <TerminalBody ref="terminal" :cmd="terminalDialog.cmd" :socket-url="getMachineTerminalSocketUrl(terminalDialog.machineId)" height="560px" />
 | 
			
		||||
            <TerminalBody ref="terminal" :cmd="terminalDialog.cmd" :socket-url="getMachineTerminalSocketUrl(props.authCertName)" height="560px" />
 | 
			
		||||
        </el-dialog>
 | 
			
		||||
 | 
			
		||||
        <script-edit
 | 
			
		||||
@@ -100,6 +100,7 @@ import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    visible: { type: Boolean },
 | 
			
		||||
    machineId: { type: Number },
 | 
			
		||||
    authCertName: { type: String },
 | 
			
		||||
    title: { type: String },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +144,6 @@ const state = reactive({
 | 
			
		||||
    terminalDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
        cmd: '',
 | 
			
		||||
        machineId: 0,
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -195,6 +195,7 @@ const run = async (script: any) => {
 | 
			
		||||
    if (script.type == ScriptResultEnum.Result.value || noResult) {
 | 
			
		||||
        const res = await machineApi.runScript.request({
 | 
			
		||||
            machineId: props.machineId,
 | 
			
		||||
            ac: props.authCertName,
 | 
			
		||||
            scriptId: script.id,
 | 
			
		||||
            params: JSON.stringify(state.scriptParamsDialog.params),
 | 
			
		||||
        });
 | 
			
		||||
@@ -215,7 +216,6 @@ const run = async (script: any) => {
 | 
			
		||||
        }
 | 
			
		||||
        state.terminalDialog.cmd = script;
 | 
			
		||||
        state.terminalDialog.visible = true;
 | 
			
		||||
        state.terminalDialog.machineId = props.machineId as any;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -236,7 +236,6 @@ function templateResolve(template: string, param: any) {
 | 
			
		||||
 | 
			
		||||
const closeTermnial = () => {
 | 
			
		||||
    state.terminalDialog.visible = false;
 | 
			
		||||
    state.terminalDialog.machineId = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const editScript = (data: any) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ export const machineApi = {
 | 
			
		||||
    // 删除机器
 | 
			
		||||
    del: Api.newDelete('/machines/{id}'),
 | 
			
		||||
    scripts: Api.newGet('/machines/{machineId}/scripts'),
 | 
			
		||||
    runScript: Api.newGet('/machines/{machineId}/scripts/{scriptId}/run'),
 | 
			
		||||
    runScript: Api.newGet('/machines/scripts/{scriptId}/{ac}/run'),
 | 
			
		||||
    saveScript: Api.newPost('/machines/{machineId}/scripts'),
 | 
			
		||||
    deleteScript: Api.newDelete('/machines/{machineId}/scripts/{scriptId}'),
 | 
			
		||||
    // 获取配置文件列表
 | 
			
		||||
@@ -48,13 +48,6 @@ export const machineApi = {
 | 
			
		||||
    termOpRec: Api.newGet('/machines/{id}/term-recs/{recId}'),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const authCertApi = {
 | 
			
		||||
    baseList: Api.newGet('/sys/authcerts/base'),
 | 
			
		||||
    list: Api.newGet('/sys/authcerts'),
 | 
			
		||||
    save: Api.newPost('/sys/authcerts'),
 | 
			
		||||
    delete: Api.newDelete('/sys/authcerts/{id}'),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const cronJobApi = {
 | 
			
		||||
    list: Api.newGet('/machine-cronjobs'),
 | 
			
		||||
    relateMachineIds: Api.newGet('/machine-cronjobs/machine-ids'),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,118 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="500px" :destroy-on-close="true">
 | 
			
		||||
            <el-form ref="acForm" :rules="rules" :model="form" label-width="auto">
 | 
			
		||||
                <el-form-item prop="name" label="名称" required>
 | 
			
		||||
                    <el-input v-model="form.name"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item prop="authMethod" label="认证方式" required>
 | 
			
		||||
                    <el-select style="width: 100%" v-model="form.authMethod" placeholder="请选择认证方式">
 | 
			
		||||
                        <el-option key="1" label="密码" :value="1"> </el-option>
 | 
			
		||||
                        <el-option key="2" label="密钥" :value="2"> </el-option>
 | 
			
		||||
                    </el-select>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item v-if="form.authMethod == 1" prop="password" label="密码">
 | 
			
		||||
                    <el-input type="password" show-password clearable v-model.trim="form.password" placeholder="请输入密码" autocomplete="new-password">
 | 
			
		||||
                    </el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item v-if="form.authMethod == 2" prop="password" label="秘钥">
 | 
			
		||||
                    <el-input type="textarea" :rows="5" v-model="form.password" placeholder="请将私钥文件内容拷贝至此"> </el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item v-if="form.authMethod == 2" prop="passphrase" label="秘钥密码">
 | 
			
		||||
                    <el-input type="password" v-model="form.passphrase"> </el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
 | 
			
		||||
                <el-form-item label="备注">
 | 
			
		||||
                    <el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
            </el-form>
 | 
			
		||||
            <template #footer>
 | 
			
		||||
                <div class="dialog-footer">
 | 
			
		||||
                    <el-button @click="cancel()">取 消</el-button>
 | 
			
		||||
                    <el-button type="primary" :loading="btnLoading" @click="btnOk">确 定</el-button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </template>
 | 
			
		||||
        </el-dialog>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ref, toRefs, reactive, watch } from 'vue';
 | 
			
		||||
import { authCertApi } from '../api';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    visible: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
    data: {
 | 
			
		||||
        type: [Boolean, Object],
 | 
			
		||||
    },
 | 
			
		||||
    title: {
 | 
			
		||||
        type: String,
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
//定义事件
 | 
			
		||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
 | 
			
		||||
 | 
			
		||||
const acForm: any = ref(null);
 | 
			
		||||
 | 
			
		||||
const rules = {
 | 
			
		||||
    name: [
 | 
			
		||||
        {
 | 
			
		||||
            required: true,
 | 
			
		||||
            message: '授权凭证名称不能为空',
 | 
			
		||||
            trigger: ['change', 'blur'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    dvisible: false,
 | 
			
		||||
    params: [] as any,
 | 
			
		||||
    form: {
 | 
			
		||||
        id: null,
 | 
			
		||||
        name: '',
 | 
			
		||||
        authMethod: 1,
 | 
			
		||||
        password: '',
 | 
			
		||||
        passphrase: '',
 | 
			
		||||
        remark: '',
 | 
			
		||||
    },
 | 
			
		||||
    btnLoading: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const { dvisible, form, btnLoading } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
watch(props, (newValue: any) => {
 | 
			
		||||
    state.dvisible = newValue.visible;
 | 
			
		||||
    if (newValue.data) {
 | 
			
		||||
        state.form = { ...newValue.data };
 | 
			
		||||
    } else {
 | 
			
		||||
        state.form = { authMethod: 1 } as any;
 | 
			
		||||
        state.params = [];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const cancel = () => {
 | 
			
		||||
    // 更新父组件visible prop对应的值为false
 | 
			
		||||
    emit('update:visible', false);
 | 
			
		||||
    // 若父组件有取消事件,则调用
 | 
			
		||||
    emit('cancel');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const btnOk = async () => {
 | 
			
		||||
    acForm.value.validate(async (valid: boolean) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
            state.btnLoading = true;
 | 
			
		||||
            try {
 | 
			
		||||
                await authCertApi.save.request(state.form);
 | 
			
		||||
                emit('val-change', state.form);
 | 
			
		||||
                cancel();
 | 
			
		||||
            } finally {
 | 
			
		||||
                state.btnLoading = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss"></style>
 | 
			
		||||
@@ -1,106 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <page-table
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="authCertApi.list"
 | 
			
		||||
            :search-items="state.searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
            :columns="state.columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button type="primary" icon="plus" @click="edit(false)">添加</el-button>
 | 
			
		||||
                <el-button :disabled="selectionData.length < 1" @click="deleteAc(selectionData)" type="danger" icon="delete">删除 </el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template #action="{ data }">
 | 
			
		||||
                <el-button @click="edit(data)" type="primary" link>编辑 </el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
        </page-table>
 | 
			
		||||
 | 
			
		||||
        <auth-cert-edit :title="editor.title" v-model:visible="editor.visible" :data="editor.authcert" @val-change="editChange" />
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { toRefs, reactive, onMounted, ref, Ref } from 'vue';
 | 
			
		||||
import AuthCertEdit from './AuthCertEdit.vue';
 | 
			
		||||
import { authCertApi } from '../api';
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { AuthMethodEnum } from '../enums';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    query: {
 | 
			
		||||
        pageNum: 1,
 | 
			
		||||
        pageSize: 0,
 | 
			
		||||
        name: null,
 | 
			
		||||
    },
 | 
			
		||||
    searchItems: [SearchItem.input('name', '凭证名称')],
 | 
			
		||||
    columns: [
 | 
			
		||||
        TableColumn.new('name', '名称'),
 | 
			
		||||
        TableColumn.new('authMethod', '认证方式').typeTag(AuthMethodEnum),
 | 
			
		||||
        TableColumn.new('remark', '备注'),
 | 
			
		||||
        TableColumn.new('creator', '创建人'),
 | 
			
		||||
        TableColumn.new('createTime', '创建时间').isTime(),
 | 
			
		||||
        TableColumn.new('creator', '修改者'),
 | 
			
		||||
        TableColumn.new('createTime', '修改时间').isTime(),
 | 
			
		||||
        TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(),
 | 
			
		||||
    ],
 | 
			
		||||
    selectionData: [],
 | 
			
		||||
    paramsDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
        config: null as any,
 | 
			
		||||
        params: {},
 | 
			
		||||
        paramsFormItem: [] as any,
 | 
			
		||||
    },
 | 
			
		||||
    editor: {
 | 
			
		||||
        title: '授权凭证保存',
 | 
			
		||||
        visible: false,
 | 
			
		||||
        authcert: {},
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const { query, selectionData, editor } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {});
 | 
			
		||||
 | 
			
		||||
const search = async () => {
 | 
			
		||||
    pageTableRef.value.search();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const editChange = () => {
 | 
			
		||||
    ElMessage.success('保存成功');
 | 
			
		||||
    search();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const edit = (data: any) => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
        state.editor.authcert = data;
 | 
			
		||||
    } else {
 | 
			
		||||
        state.editor.authcert = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.editor.visible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteAc = async (data: any) => {
 | 
			
		||||
    try {
 | 
			
		||||
        await ElMessageBox.confirm(`确定删除该【${data.map((x: any) => x.name).join(', ')}授权凭证?`, '提示', {
 | 
			
		||||
            confirmButtonText: '确定',
 | 
			
		||||
            cancelButtonText: '取消',
 | 
			
		||||
            type: 'warning',
 | 
			
		||||
        });
 | 
			
		||||
        await authCertApi.delete.request({ id: data.map((x: any) => x.id).join(',') });
 | 
			
		||||
        ElMessage.success('删除成功');
 | 
			
		||||
        search();
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        //
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss"></style>
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div style="width: 100%">
 | 
			
		||||
        <el-select @change="changeValue" v-model="id" filterable placeholder="请选择授权凭证,可前往[机器管理->授权凭证]添加" style="width: 100%">
 | 
			
		||||
            <el-option v-for="ac in acs" :key="ac.id" :value="ac.id" :label="ac.name">
 | 
			
		||||
                <el-tag v-if="ac.authMethod == 1" type="success" size="small">密码</el-tag>
 | 
			
		||||
                <el-tag v-if="ac.authMethod == 2" size="small">密钥</el-tag>
 | 
			
		||||
 | 
			
		||||
                <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
                {{ ac.name }}
 | 
			
		||||
 | 
			
		||||
                <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
                {{ ac.remark }}
 | 
			
		||||
            </el-option>
 | 
			
		||||
        </el-select>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { reactive, toRefs, onMounted } from 'vue';
 | 
			
		||||
import { authCertApi } from '../api';
 | 
			
		||||
 | 
			
		||||
//定义事件
 | 
			
		||||
const emit = defineEmits(['update:modelValue', 'change']);
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    modelValue: {
 | 
			
		||||
        type: [Number],
 | 
			
		||||
        required: true,
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    acs: [] as any,
 | 
			
		||||
    id: null as any,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const { acs, id } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
    await getAcs();
 | 
			
		||||
    if (props.modelValue) {
 | 
			
		||||
        state.id = props.modelValue;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const changeValue = (val: any) => {
 | 
			
		||||
    emit('update:modelValue', val);
 | 
			
		||||
    emit('change', val);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getAcs = async () => {
 | 
			
		||||
    const acs = await authCertApi.baseList.request({ pageSize: 100, type: 2 });
 | 
			
		||||
    state.acs = acs.list;
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss"></style>
 | 
			
		||||
							
								
								
									
										132
									
								
								mayfly_go_web/src/views/ops/tag/AuthCertList.vue
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										132
									
								
								mayfly_go_web/src/views/ops/tag/AuthCertList.vue
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <page-table
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="resourceAuthCertApi.listByQuery"
 | 
			
		||||
            :search-items="state.searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
            :columns="state.columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="'authcert:save'" type="primary" icon="plus" @click="edit(false)">添加</el-button>
 | 
			
		||||
                <el-button v-auth="'authcert:del'" :disabled="disabledDelBtn" @click="deleteAc(selectionData)" type="danger" icon="delete">删除 </el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template #action="{ data }">
 | 
			
		||||
                <el-button v-auth="'authcert:save'" v-if="data.type == AuthCertTypeEnum.Public.value" @click="edit(data)" type="primary" link>编辑 </el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
        </page-table>
 | 
			
		||||
 | 
			
		||||
        <ResourceAuthCertEdit
 | 
			
		||||
            v-model:visible="editor.visible"
 | 
			
		||||
            :auth-cert="editor.authcert"
 | 
			
		||||
            @confirm="confirmSave"
 | 
			
		||||
            :resource-edit="false"
 | 
			
		||||
            :disable-ciphertext-type="[AuthCertCiphertextTypeEnum.Public.value]"
 | 
			
		||||
            :disable-type="[AuthCertTypeEnum.Private.value, AuthCertTypeEnum.PrivateDefault.value, AuthCertTypeEnum.Privileged.value]"
 | 
			
		||||
        />
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { toRefs, reactive, onMounted, ref, Ref, computed } from 'vue';
 | 
			
		||||
import { resourceAuthCertApi } from './api';
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
import { AuthCertCiphertextTypeEnum, AuthCertTypeEnum } from './enums';
 | 
			
		||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
			
		||||
import ResourceAuthCertEdit from '../component/ResourceAuthCertEdit.vue';
 | 
			
		||||
 | 
			
		||||
const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    query: {
 | 
			
		||||
        pageNum: 1,
 | 
			
		||||
        pageSize: 0,
 | 
			
		||||
        name: null,
 | 
			
		||||
    },
 | 
			
		||||
    searchItems: [
 | 
			
		||||
        SearchItem.input('name', '凭证名称'),
 | 
			
		||||
        SearchItem.select('type', '凭证类型').withEnum(AuthCertTypeEnum),
 | 
			
		||||
        SearchItem.select('ciphertextType', '密文类型').withEnum(AuthCertCiphertextTypeEnum),
 | 
			
		||||
    ],
 | 
			
		||||
    columns: [
 | 
			
		||||
        TableColumn.new('name', '名称'),
 | 
			
		||||
        TableColumn.new('username', '用户名'),
 | 
			
		||||
        TableColumn.new('ciphertextType', '密文类型').typeTag(AuthCertCiphertextTypeEnum),
 | 
			
		||||
        TableColumn.new('type', '凭证类型').typeTag(AuthCertTypeEnum),
 | 
			
		||||
        TableColumn.new('resourceType', '资源类型').typeTag(TagResourceTypeEnum),
 | 
			
		||||
        TableColumn.new('resourceCode', '资源编号'),
 | 
			
		||||
        TableColumn.new('remark', '备注'),
 | 
			
		||||
        TableColumn.new('creator', '创建人'),
 | 
			
		||||
        TableColumn.new('createTime', '创建时间').isTime(),
 | 
			
		||||
        TableColumn.new('creator', '修改者'),
 | 
			
		||||
        TableColumn.new('createTime', '修改时间').isTime(),
 | 
			
		||||
        TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(),
 | 
			
		||||
    ],
 | 
			
		||||
    selectionData: [],
 | 
			
		||||
    paramsDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
        config: null as any,
 | 
			
		||||
        params: {},
 | 
			
		||||
        paramsFormItem: [] as any,
 | 
			
		||||
    },
 | 
			
		||||
    editor: {
 | 
			
		||||
        title: '授权凭证保存',
 | 
			
		||||
        visible: false,
 | 
			
		||||
        authcert: {},
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const { query, selectionData, editor } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {});
 | 
			
		||||
 | 
			
		||||
const search = async () => {
 | 
			
		||||
    pageTableRef.value.search();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const disabledDelBtn = computed(() => {
 | 
			
		||||
    return state.selectionData.length < 1 || state.selectionData.find((item: any) => item.type != AuthCertTypeEnum.Public.value);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const edit = (data: any) => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
        state.editor.authcert = data;
 | 
			
		||||
    } else {
 | 
			
		||||
        state.editor.authcert = {
 | 
			
		||||
            type: AuthCertTypeEnum.Public.value,
 | 
			
		||||
            ciphertextType: AuthCertCiphertextTypeEnum.Password.value,
 | 
			
		||||
            extra: {},
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.editor.visible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const confirmSave = async (authCert: any) => {
 | 
			
		||||
    await resourceAuthCertApi.save.request(authCert);
 | 
			
		||||
    ElMessage.success('保存成功');
 | 
			
		||||
    state.editor.visible = false;
 | 
			
		||||
    search();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteAc = async (data: any) => {
 | 
			
		||||
    try {
 | 
			
		||||
        await ElMessageBox.confirm(`确定删除该【${data.map((x: any) => x.name).join(', ')}授权凭证?`, '提示', {
 | 
			
		||||
            confirmButtonText: '确定',
 | 
			
		||||
            cancelButtonText: '取消',
 | 
			
		||||
            type: 'warning',
 | 
			
		||||
        });
 | 
			
		||||
        await resourceAuthCertApi.delete.request({ id: data.map((x: any) => x.id).join(',') });
 | 
			
		||||
        ElMessage.success('删除成功');
 | 
			
		||||
        search();
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        //
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss"></style>
 | 
			
		||||
@@ -24,20 +24,59 @@
 | 
			
		||||
            <template #action="{ data }">
 | 
			
		||||
                <el-button @click.prevent="showMembers(data)" link type="primary">成员</el-button>
 | 
			
		||||
 | 
			
		||||
                <el-button @click.prevent="showTags(data)" link type="success">标签</el-button>
 | 
			
		||||
 | 
			
		||||
                <el-button v-auth="'team:save'" @click.prevent="showSaveTeamDialog(data)" link type="warning">编辑</el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
        </page-table>
 | 
			
		||||
 | 
			
		||||
        <el-dialog width="400px" title="团队编辑" :before-close="cancelSaveTeam" v-model="addTeamDialog.visible">
 | 
			
		||||
        <el-drawer title="团队编辑" v-model="addTeamDialog.visible" :before-close="cancelSaveTeam" :destroy-on-close="true" :close-on-click-modal="false">
 | 
			
		||||
            <template #header>
 | 
			
		||||
                <DrawerHeader header="团队编辑" :back="cancelSaveTeam" />
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <el-form ref="teamForm" :model="addTeamDialog.form" label-width="auto">
 | 
			
		||||
                <el-form-item prop="name" label="团队名" required>
 | 
			
		||||
                    <el-input v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
 | 
			
		||||
                    <el-input :disabled="addTeamDialog.form.id" v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item label="备注">
 | 
			
		||||
                    <el-input v-model="addTeamDialog.form.remark" auto-complete="off"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
 | 
			
		||||
                <el-form-item prop="tag" label="标签">
 | 
			
		||||
                    <el-scrollbar style="height: calc(100vh - 300px); border: 1px solid var(--el-border-color)">
 | 
			
		||||
                        <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',
 | 
			
		||||
                            }"
 | 
			
		||||
                            @check="tagTreeNodeCheck"
 | 
			
		||||
                        >
 | 
			
		||||
                            <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>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
            </el-form>
 | 
			
		||||
            <template #footer>
 | 
			
		||||
                <div class="dialog-footer">
 | 
			
		||||
@@ -45,48 +84,7 @@
 | 
			
		||||
                    <el-button @click="saveTeam" type="primary">确 定</el-button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </template>
 | 
			
		||||
        </el-dialog>
 | 
			
		||||
 | 
			
		||||
        <el-dialog width="500px" :title="showTagDialog.title" :before-close="closeTagDialog" v-model="showTagDialog.visible">
 | 
			
		||||
            <el-form label-width="auto">
 | 
			
		||||
                <el-form-item prop="tag" label="标签">
 | 
			
		||||
                    <el-tree-select
 | 
			
		||||
                        ref="tagTreeRef"
 | 
			
		||||
                        style="width: 100%"
 | 
			
		||||
                        v-model="showTagDialog.tagTreeTeams"
 | 
			
		||||
                        :data="showTagDialog.tags"
 | 
			
		||||
                        :default-expanded-keys="showTagDialog.tagTreeTeams"
 | 
			
		||||
                        multiple
 | 
			
		||||
                        :render-after-expand="true"
 | 
			
		||||
                        show-checkbox
 | 
			
		||||
                        check-strictly
 | 
			
		||||
                        node-key="id"
 | 
			
		||||
                        :props="showTagDialog.props"
 | 
			
		||||
                        @check="tagTreeNodeCheck"
 | 
			
		||||
                    >
 | 
			
		||||
                        <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-select>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
            </el-form>
 | 
			
		||||
            <template #footer>
 | 
			
		||||
                <div class="dialog-footer">
 | 
			
		||||
                    <el-button @click="closeTagDialog()">取 消</el-button>
 | 
			
		||||
                    <el-button v-auth="'team:tag:save'" @click="saveTags()" type="primary">确 定</el-button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </template>
 | 
			
		||||
        </el-dialog>
 | 
			
		||||
        </el-drawer>
 | 
			
		||||
 | 
			
		||||
        <el-dialog @open="setMemebers" width="50%" :title="showMemDialog.title" v-model="showMemDialog.visible">
 | 
			
		||||
            <page-table
 | 
			
		||||
@@ -132,6 +130,7 @@ import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
const teamForm: any = ref(null);
 | 
			
		||||
const tagTreeRef: any = ref(null);
 | 
			
		||||
@@ -142,17 +141,20 @@ const searchItems = [SearchItem.input('name', '团队名称')];
 | 
			
		||||
const columns = [
 | 
			
		||||
    TableColumn.new('name', '团队名称'),
 | 
			
		||||
    TableColumn.new('remark', '备注'),
 | 
			
		||||
    TableColumn.new('creator', '创建者'),
 | 
			
		||||
    TableColumn.new('createTime', '创建时间').isTime(),
 | 
			
		||||
    TableColumn.new('creator', '创建人'),
 | 
			
		||||
    TableColumn.new('modifier', '修改者'),
 | 
			
		||||
    TableColumn.new('updateTime', '修改时间').isTime(),
 | 
			
		||||
    TableColumn.new('action', '操作').isSlot().setMinWidth(120).fixedRight().alignCenter(),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    currentEditPermissions: false,
 | 
			
		||||
    tags: [],
 | 
			
		||||
    addTeamDialog: {
 | 
			
		||||
        title: '新增团队',
 | 
			
		||||
        visible: false,
 | 
			
		||||
        form: { id: 0, name: '', remark: '' },
 | 
			
		||||
        form: { id: 0, name: '', remark: '', tags: [] },
 | 
			
		||||
    },
 | 
			
		||||
    query: {
 | 
			
		||||
        pageNum: 1,
 | 
			
		||||
@@ -188,21 +190,9 @@ const state = reactive({
 | 
			
		||||
        },
 | 
			
		||||
        accounts: Array(),
 | 
			
		||||
    },
 | 
			
		||||
    showTagDialog: {
 | 
			
		||||
        title: '项目信息',
 | 
			
		||||
        visible: false,
 | 
			
		||||
        tags: [],
 | 
			
		||||
        teamId: 0,
 | 
			
		||||
        tagTreeTeams: [] as any,
 | 
			
		||||
        props: {
 | 
			
		||||
            value: 'id',
 | 
			
		||||
            label: 'codePath',
 | 
			
		||||
            children: 'children',
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const { query, addTeamDialog, selectionData, showMemDialog, showTagDialog } = toRefs(state);
 | 
			
		||||
const { query, addTeamDialog, selectionData, showMemDialog } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {});
 | 
			
		||||
 | 
			
		||||
@@ -210,13 +200,19 @@ const search = async () => {
 | 
			
		||||
    pageTableRef.value.search();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const showSaveTeamDialog = (data: any) => {
 | 
			
		||||
const showSaveTeamDialog = async (data: any) => {
 | 
			
		||||
    if (state.tags.length == 0) {
 | 
			
		||||
        state.tags = await tagApi.getTagTrees.request(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (data) {
 | 
			
		||||
        state.addTeamDialog.form.id = data.id;
 | 
			
		||||
        state.addTeamDialog.form.name = data.name;
 | 
			
		||||
        state.addTeamDialog.form.remark = data.remark;
 | 
			
		||||
        state.addTeamDialog.title = `修改 [${data.codePath}] 信息`;
 | 
			
		||||
        state.addTeamDialog.form.tags = await tagApi.getTeamTagIds.request({ teamId: data.id });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.addTeamDialog.visible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -224,6 +220,7 @@ const saveTeam = async () => {
 | 
			
		||||
    teamForm.value.validate(async (valid: any) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
            const form = state.addTeamDialog.form;
 | 
			
		||||
            form.tags = tagTreeRef.value.getCheckedKeys(false);
 | 
			
		||||
            await tagApi.saveTeam.request(form);
 | 
			
		||||
            ElMessage.success('保存成功');
 | 
			
		||||
            search();
 | 
			
		||||
@@ -292,32 +289,6 @@ const cancelAddMember = () => {
 | 
			
		||||
    state.showMemDialog.addVisible = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/********** 标签相关 ***********/
 | 
			
		||||
 | 
			
		||||
const showTags = async (team: any) => {
 | 
			
		||||
    state.showTagDialog.tags = await tagApi.getTagTrees.request(null);
 | 
			
		||||
    state.showTagDialog.tagTreeTeams = await tagApi.getTeamTagIds.request({ teamId: team.id });
 | 
			
		||||
    state.showTagDialog.title = `[${team.name}] 团队标签信息`;
 | 
			
		||||
    state.showTagDialog.teamId = team.id;
 | 
			
		||||
    state.showTagDialog.visible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const closeTagDialog = () => {
 | 
			
		||||
    state.showTagDialog.visible = false;
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
        state.showTagDialog.tagTreeTeams = [];
 | 
			
		||||
    }, 500);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const saveTags = async () => {
 | 
			
		||||
    await tagApi.saveTeamTags.request({
 | 
			
		||||
        teamId: state.showTagDialog.teamId,
 | 
			
		||||
        tagIds: state.showTagDialog.tagTreeTeams,
 | 
			
		||||
    });
 | 
			
		||||
    ElMessage.success('保存成功');
 | 
			
		||||
    closeTagDialog();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const tagTreeNodeCheck = () => {
 | 
			
		||||
    // const node = tagTreeRef.value.getNode(data.id);
 | 
			
		||||
    // console.log(node);
 | 
			
		||||
@@ -348,4 +319,4 @@ const tagTreeNodeCheck = () => {
 | 
			
		||||
//     console.log(state.showTagDialog.tagTreeTeams);
 | 
			
		||||
// }
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss"></style>
 | 
			
		||||
<style lang="scss" scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,11 @@ export const tagApi = {
 | 
			
		||||
    delTeamMem: Api.newDelete('/teams/{teamId}/members/{accountId}'),
 | 
			
		||||
 | 
			
		||||
    getTeamTagIds: Api.newGet('/teams/{teamId}/tags'),
 | 
			
		||||
    saveTeamTags: Api.newPost('/teams/{teamId}/tags'),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const resourceAuthCertApi = {
 | 
			
		||||
    detail: Api.newGet('/auth-certs/detail'),
 | 
			
		||||
    listByQuery: Api.newGet('/auth-certs'),
 | 
			
		||||
    save: Api.newPost('/auth-certs'),
 | 
			
		||||
    delete: Api.newDelete('/auth-certs/{id}'),
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import { EnumValue } from '@/common/Enum';
 | 
			
		||||
 | 
			
		||||
// 授权凭证类型
 | 
			
		||||
export const AuthCertTypeEnum = {
 | 
			
		||||
    Public: EnumValue.of(2, '公共凭证').tagTypeSuccess(),
 | 
			
		||||
    Private: EnumValue.of(1, '普通账号').tagTypeSuccess(),
 | 
			
		||||
    Privileged: EnumValue.of(11, '特权账号').tagTypeSuccess(),
 | 
			
		||||
    PrivateDefault: EnumValue.of(12, '默认账号').tagTypeSuccess(),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user