otp: 样式优化

This commit is contained in:
meilin.huang
2025-04-26 17:37:09 +08:00
parent 2170509d92
commit 44d379a016
41 changed files with 679 additions and 738 deletions

View File

@@ -63,7 +63,7 @@
"sass": "^1.87.0",
"tailwindcss": "^4.1.4",
"typescript": "^5.8.2",
"vite": "^6.3.2",
"vite": "^6.3.3",
"vite-plugin-progress": "0.0.7",
"vue-eslint-parser": "^10.1.3"
},

View File

@@ -1,5 +1,5 @@
<template>
<div class="monaco-editor-custom h-full" style="border: 1px solid var(--el-border-color-light, #ebeef5)">
<div class="monaco-editor-custom relative h-full" style="border: 1px solid var(--el-border-color-light, #ebeef5)">
<div class="monaco-editor-content" ref="monacoTextareaRef" :style="{ height: height }"></div>
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage" filterable>
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>

View File

@@ -10,7 +10,7 @@
</SearchForm>
</transition>
<el-card class="h-full" body-class="h-full">
<el-card class="h-full" body-class="h-full flex flex-col">
<!-- 表格头部 操作按钮 -->
<div class="flex justify-between">
<div>
@@ -79,8 +79,9 @@
</slot>
</div>
<div class="h-[calc(100%-90px)]">
<div class="flex-1 overflow-auto">
<el-table
v-show="showTable"
ref="tableRef"
v-bind="$attrs"
height="100%"
@@ -152,7 +153,6 @@
:small="props.size == 'small'"
@current-change="pageNumChange"
@size-change="pageSizeChange"
style="text-align: right"
layout="prev, pager, next, total, sizes"
:total="total"
v-model:current-page="queryForm.pageNum"
@@ -165,7 +165,7 @@
</template>
<script lang="ts" setup>
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots, toValue } from 'vue';
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots, toValue, h } from 'vue';
import { TableColumn } from './index';
import EnumTag from '@/components/enumtag/EnumTag.vue';
import { useThemeConfig } from '@/store/themeConfig';
@@ -176,7 +176,7 @@ import { SearchItem } from '../SearchForm/index';
import SearchFormItem from '../SearchForm/components/SearchFormItem.vue';
import SvgIcon from '@/components/svgIcon/index.vue';
import { usePageTable } from '@/hooks/usePageTable';
import { ElTable } from 'element-plus';
import { ElInput, ElTable } from 'element-plus';
const emit = defineEmits(['update:selectionData', 'pageSizeChange', 'pageNumChange']);
@@ -240,6 +240,10 @@ const showToolButton = (key: 'setting' | 'search') => {
const nowSearchItem: Ref<SearchItem> = ref(null) as any;
// 是否已经计算列宽度
const isCalculatedWidth: Ref<boolean> = ref(false);
const showTable: Ref<boolean> = ref(false);
/**
* 改变当前的搜索项
* @param searchItem 当前点击的搜索项
@@ -276,15 +280,31 @@ const state = reactive({
const { pageSizes, formatVal } = toRefs(state);
watch(tableData, (newValue: any) => {
if (newValue && newValue.length > 0) {
props.columns.forEach((item) => {
if (item.autoWidth && item.show) {
item.autoCalculateMinWidth(tableData.value);
}
});
calculateTableColumnMinWidth();
// 需要计算完才能显示表格,否则会有表格闪烁的问题
if (!showTable.value) {
showTable.value = true;
}
});
/**
* 计算表格列宽
*/
const calculateTableColumnMinWidth = () => {
if (isCalculatedWidth.value || !tableData.value || tableData.value.length === 0) {
return;
}
// 计算表格列宽
props.columns.forEach((item) => {
if (item.autoWidth && item.show) {
item.autoCalculateMinWidth(tableData.value);
}
});
isCalculatedWidth.value = true;
};
watch(
() => props.data,
(newValue: any) => {

View File

@@ -1,6 +1,6 @@
<template>
<div id="terminal-body" :style="{ height }">
<div ref="terminalRef" class="terminal" />
<div class="h-full w-full flex">
<div ref="terminalRef" class="h-full w-full" :style="{ background: getTerminalTheme().background }" />
<TerminalSearch ref="terminalSearchRef" :search-addon="state.addon.search" @close="focus" />
</div>
@@ -18,7 +18,7 @@ import { useThemeConfig } from '@/store/themeConfig';
import { ref, nextTick, reactive, onMounted, onBeforeUnmount, watch } from 'vue';
import TerminalSearch from './TerminalSearch.vue';
import { TerminalStatus } from './common';
import { useDebounceFn, useEventListener } from '@vueuse/core';
import { useDebounceFn, useEventListener, useIntervalFn } from '@vueuse/core';
import themes from './themes';
import { TrzszFilter } from 'trzsz';
import { useI18n } from 'vue-i18n';
@@ -41,13 +41,6 @@ const props = defineProps({
socketUrl: {
type: String,
},
/**
* 高度
*/
height: {
type: [String, Number],
default: '100%',
},
});
const emit = defineEmits(['statusChange']);
@@ -60,7 +53,6 @@ const { themeConfig } = storeToRefs(useThemeConfig());
// 终端实例
let term: Terminal;
let socket: WebSocket;
let pingInterval: any;
const state = reactive({
// 插件
@@ -89,7 +81,9 @@ watch(
watch(
() => themeConfig.value.terminalTheme,
() => {
term.options.theme = getTerminalTheme();
if (term) {
term.options.theme = getTerminalTheme();
}
}
);
@@ -154,7 +148,8 @@ function initSocket() {
// 监听socket连接
socket.onopen = () => {
// 注册心跳
pingInterval = setInterval(sendPing, 15000);
useIntervalFn(sendPing, 15000);
state.status = TerminalStatus.Connected;
focus();
@@ -289,8 +284,6 @@ function sendCmd(key: any) {
function closeSocket() {
// 关闭 websocket
socket && socket.readyState === 1 && socket.close();
// 清除 ping
pingInterval && clearInterval(pingInterval);
}
function close() {
@@ -310,17 +303,4 @@ const getStatus = (): TerminalStatus => {
defineExpose({ init, fitTerminal, focus, clear, close, getStatus, sendResize, write2Term, writeln2Term });
</script>
<style lang="scss">
#terminal-body {
width: 100%;
.terminal {
width: 100%;
height: 100%;
// .xterm .xterm-viewport {
// overflow-y: hidden;
// }
}
}
</style>
<style lang="scss"></style>

View File

@@ -1,6 +1,6 @@
<template>
<div>
<el-drawer v-model="visible" :before-close="cancel" size="50%">
<el-drawer v-model="visible" :before-close="cancel" size="50%" body-class="flex flex-col">
<template #header>
<DrawerHeader :header="props.title" :back="cancel">
<template #extra>
@@ -13,7 +13,7 @@
<el-descriptions-item v-for="(value, key) in extra" :key="key" :span="1" :label="key">{{ value }}</el-descriptions-item>
</el-descriptions>
<TerminalBody class="mb-2" ref="terminalRef" height="calc(100vh - 220px)" />
<TerminalBody class="mb-2 flex-1" ref="terminalRef" />
</el-drawer>
</div>
</template>

View File

@@ -22,7 +22,7 @@ export const usePageTable = (
) => {
const state = reactive({
// 表格数据
tableData: [],
tableData: [{}],
// 总数量
total: 0,
// 查询参数,包含分页参数

View File

@@ -2,11 +2,11 @@ import router from '@/router';
import { clearUser, getClientId, getRefreshToken, getToken, saveRefreshToken, saveToken } from '@/common/utils/storage';
import { templateResolve } from '@/common/utils/string';
import { ElMessage } from 'element-plus';
import { createFetch } from '@vueuse/core';
import { createFetch, UseFetchReturn } from '@vueuse/core';
import Api from '@/common/Api';
import { Result, ResultEnum } from '@/common/request';
import config from '@/common/config';
import { unref } from 'vue';
import { ref, unref } from 'vue';
import { URL_401 } from '@/router/staticRouter';
import openApi from '@/common/openApi';
import { useThemeConfig } from '@/store/themeConfig';
@@ -92,12 +92,15 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
},
});
// 统一处理后的返回结果如果直接使用uaf.data则数据会出现由{code: x, data: {}} -> data 的变化导致某些结果绑定报错
const data = ref<T | null>(null);
return {
execute: async function () {
return execCustomFetch(uaf);
await execCustomFetch(uaf);
data.value = uaf.data.value;
},
isFetching: uaf.isFetching,
data: uaf.data,
data: data,
abort: uaf.abort,
};
}
@@ -105,7 +108,7 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
let refreshingToken = false;
let queue: any[] = [];
async function execCustomFetch(uaf: any) {
async function execCustomFetch(uaf: UseFetchReturn<any>) {
try {
await uaf.execute(true);
} catch (e: any) {
@@ -118,22 +121,22 @@ async function execCustomFetch(uaf: any) {
const respStatus = uaf.response.value?.status;
if (respStatus == 404) {
ElMessage.error('请求接口不存在');
ElMessage.error('url not found');
return rejectPromise;
}
if (respStatus == 500) {
ElMessage.error('服务器响应异常');
ElMessage.error('server error');
return rejectPromise;
}
console.error(e);
ElMessage.error('网络请求错误');
ElMessage.error('network error');
return rejectPromise;
}
const result: Result = uaf.data.value as any;
if (!result) {
ElMessage.error('网络请求失败');
ElMessage.error('network request failed');
return Promise.reject(result);
}

View File

@@ -1,7 +1,7 @@
<template>
<el-container class="layout-container flex-center">
<Header />
<el-container class="h-[calc(100vh-50px)]">
<el-container class="flex-1 overflow-auto">
<Aside />
<div class="flex-center layout-backtop">
<TagsView v-if="themeConfig.isTagsview" />

View File

@@ -34,8 +34,6 @@ body,
-webkit-tap-highlight-color: transparent;
background-color: var(--bg-main-color);
font-size: 14px;
// overflow: hidden;
// position: relative;
}
/* 主布局样式

View File

@@ -22,14 +22,21 @@
<el-table-column prop="res" :label="$t('flow.runResult')" :min-width="30" show-overflow-tooltip>
<template #default="scope">
<el-popover placement="top" :width="400" trigger="hover">
<el-popover placement="top" width="50%" trigger="hover">
<template #reference>
<el-link icon="view" :type="scope.row.errorMsg ? 'danger' : 'success'" :underline="false"> </el-link>
</template>
<el-text v-if="scope.row.errorMsg">{{ scope.row.errorMsg }}</el-text>
<el-table v-else :data="scope.row.res" size="small">
<el-table-column v-for="col in scope.row.columns" :key="col.name" :label="col.name" :prop="col.name" />
<el-table max-height="600px" v-else :data="scope.row.res" size="small">
<el-table-column
:width="DbInst.flexColumnWidth(col.name, scope.row.res)"
v-for="col in scope.row.columns"
:key="col.name"
:label="col.name"
:prop="col.name"
show-overflow-tooltip
/>
</el-table>
</el-popover>
</template>
@@ -50,6 +57,7 @@ import { tagApi } from '@/views/ops/tag/api';
import { TagResourceTypeEnum } from '@/common/commonEnum';
import TagCodePath from '@/views/ops/component/TagCodePath.vue';
import SvgIcon from '@/components/svgIcon/index.vue';
import { DbInst } from '@/views/ops/db/db';
const props = defineProps({
procinst: {

View File

@@ -1,5 +1,5 @@
<template>
<el-card class="h-full flex" body-class="!p-1 flex flex-col flex-1 overflow-auto">
<el-card class="h-full flex" body-class="!p-1 flex flex-col w-full">
<el-input v-model="filterText" :placeholder="$t('tag.tagFilterPlaceholder')" clearable size="small" class="!mb-1 w-full" />
<el-scrollbar>
<el-tree
@@ -22,7 +22,7 @@
<span
:id="node.key"
@dblclick="treeNodeDblclick(data, node)"
class="node-container flex items-center w-full cursor-pointer select-none"
class="node-container flex items-center cursor-pointer select-none"
:class="data.type.nodeDblclickFunc ? 'select-none' : ''"
>
<span v-if="data.type.value == TagTreeNode.TagPath">

View File

@@ -65,10 +65,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" @click="btnOk">{{ $t('common.confirm') }}</el-button>
</div>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" @click="btnOk">{{ $t('common.confirm') }}</el-button>
</template>
</el-dialog>
</div>
@@ -90,9 +88,6 @@ import { useI18nFormValidate } from '@/hooks/useI18n';
import { Rules } from '@/common/rule';
const props = defineProps({
visible: {
type: Boolean,
},
instance: {
type: [Boolean, Object],
},
@@ -104,8 +99,10 @@ const props = defineProps({
},
});
const dialogVisible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change', 'confirm']);
const emit = defineEmits(['cancel', 'val-change', 'confirm']);
const rules = {
instanceId: [Rules.requiredSelect('db.dbInst')],
@@ -121,7 +118,6 @@ const dbForm: any = ref(null);
// const tagSelectRef: any = ref(null);
const state = reactive({
dialogVisible: false,
allDatabases: [] as any,
dbNamesSelected: [] as any,
dbNamesFiltered: [] as any,
@@ -142,28 +138,24 @@ const state = reactive({
loadingDbNames: false,
});
const { dialogVisible, allDatabases, form, dbNamesSelected } = toRefs(state);
const { allDatabases, form, dbNamesSelected } = toRefs(state);
watch(
() => props.visible,
() => {
state.dialogVisible = props.visible;
if (!state.dialogVisible) {
return;
}
const db: any = props.db;
if (db.code) {
state.form = { ...db };
if (db.getDatabaseMode == DbGetDbNamesMode.Assign.value) {
// 将数据库名使用空格切割,获取所有数据库列表
state.dbNamesSelected = db.database.split(' ');
}
} else {
state.form = { getDatabaseMode: DbGetDbNamesMode.Auto.value } as any;
state.dbNamesSelected = [];
}
watch(dialogVisible, () => {
if (!dialogVisible.value) {
return;
}
);
const db: any = props.db;
if (db.code) {
state.form = { ...db };
if (db.getDatabaseMode == DbGetDbNamesMode.Assign.value) {
// 将数据库名使用空格切割,获取所有数据库列表
state.dbNamesSelected = db.database.split(' ');
}
} else {
state.form = { getDatabaseMode: DbGetDbNamesMode.Auto.value } as any;
state.dbNamesSelected = [];
}
});
const onChangeGetDatabaseMode = (val: any) => {
if (val == DbGetDbNamesMode.Auto.value) {
@@ -218,7 +210,7 @@ const resetInputDb = () => {
};
const cancel = () => {
emit('update:visible', false);
dialogVisible.value = false;
emit('cancel');
setTimeout(() => {
resetInputDb();

View File

@@ -5,7 +5,7 @@
<DrawerHeader :header="title" :back="cancel" />
</template>
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
<el-form :model="form" ref="dbFormRef" :rules="rules" label-width="auto">
<el-divider content-position="left">{{ $t('common.basic') }}</el-divider>
<el-form-item ref="tagSelectRef" prop="tagCodePaths" :label="$t('tag.relateTag')">
@@ -72,7 +72,7 @@
<el-option label="SID" :value="2" />
</el-select>
</el-col>
<el-col style="text-align: center" :span="1">:</el-col>
<el-col class="text-center" :span="1">:</el-col>
<el-col :span="18">
<el-input v-if="state.extra.stype == 1" v-model="state.extra.serviceName" placeholder="Service Name"> </el-input>
<el-input v-else v-model="state.extra.sid" placeholder="SID"> </el-input>
@@ -114,7 +114,7 @@
</template>
<script lang="ts" setup>
import { reactive, ref, toRefs, watchEffect } from 'vue';
import { computed, reactive, ref, toRefs, watchEffect } from 'vue';
import { dbApi } from './api';
import { ElMessage } from 'element-plus';
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
@@ -133,9 +133,6 @@ import { Rules } from '@/common/rule';
const { t } = useI18n();
const props = defineProps({
visible: {
type: Boolean,
},
data: {
type: [Boolean, Object],
},
@@ -144,8 +141,10 @@ const props = defineProps({
},
});
const dialogVisible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
const emit = defineEmits(['cancel', 'val-change']);
const rules = {
tagCodePaths: [Rules.requiredSelect('tag.relateTag')],
@@ -154,7 +153,7 @@ const rules = {
host: [Rules.requiredInput('Host:Port')],
};
const dbForm: any = ref(null);
const dbFormRef: any = ref(null);
const tagSelectRef: any = ref(null);
const DefaultForm = {
@@ -173,20 +172,30 @@ const DefaultForm = {
};
const state = reactive({
dialogVisible: false,
extra: {} as any, // 连接需要的额外参数json
form: DefaultForm,
submitForm: {} as any,
});
const { dialogVisible, form, submitForm } = toRefs(state);
const { form } = toRefs(state);
const submitForm = computed(() => {
const reqForm: any = { ...state.form };
reqForm.selectAuthCert = null;
reqForm.tags = null;
if (!state.form.sshTunnelMachineId) {
reqForm.sshTunnelMachineId = -1;
}
if (Object.keys(state.extra).length > 0) {
reqForm.extra = state.extra;
}
return reqForm;
});
const { isFetching: saveBtnLoading, execute: saveInstanceExec, data: saveInstanceRes } = dbApi.saveInstance.useApi(submitForm);
const { isFetching: testConnBtnLoading, execute: testConnExec } = dbApi.testConn.useApi(submitForm);
watchEffect(() => {
state.dialogVisible = props.visible;
if (!state.dialogVisible) {
if (!dialogVisible.value) {
return;
}
const dbInst: any = props.data;
@@ -200,31 +209,16 @@ watchEffect(() => {
}
});
const getReqForm = async () => {
const reqForm: any = { ...state.form };
reqForm.selectAuthCert = null;
reqForm.tags = null;
if (!state.form.sshTunnelMachineId) {
reqForm.sshTunnelMachineId = -1;
}
if (Object.keys(state.extra).length > 0) {
reqForm.extra = state.extra;
}
return reqForm;
};
const testConn = async (authCert: any) => {
await useI18nFormValidate(dbForm);
state.submitForm = await getReqForm();
state.submitForm.authCerts = [authCert];
await useI18nFormValidate(dbFormRef);
submitForm.value.authCerts = [authCert];
await testConnExec();
ElMessage.success(t('db.connSuccess'));
};
const btnOk = async () => {
await useI18nFormValidate(dbForm);
state.submitForm = await getReqForm();
notBlankI18n(state.submitForm.authCerts, 'db.acName');
await useI18nFormValidate(dbFormRef);
notBlankI18n(submitForm.value.authCerts, 'db.acName');
await saveInstanceExec();
useI18nSaveSuccessMsg();
state.form.id = saveInstanceRes as any;
@@ -233,7 +227,7 @@ const btnOk = async () => {
};
const cancel = () => {
emit('update:visible', false);
dialogVisible.value = false;
emit('cancel');
state.extra = {};
};

View File

@@ -4,23 +4,23 @@
<DrawerHeader :header="title" :back="cancel" />
</template>
<el-form label-position="left" ref="formRef" :model="tableData" label-width="80px">
<el-row>
<el-form label-position="left" ref="formRef" :model="tableData" label-width="auto">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item prop="tableName" :label="$t('db.tableName')">
<el-input style="width: 80%" v-model="tableData.tableName" size="small"></el-input>
<el-input v-model="tableData.tableName" size="small"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="tableComment" :label="$t('db.comment')">
<el-input style="width: 80%" v-model="tableData.tableComment" size="small"></el-input>
<el-input v-model="tableData.tableComment" size="small"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('db.column')" name="1">
<el-table ref="tableRef" :data="tableData.fields.res" :max-height="tableData.height">
<el-table ref="tableRef" :data="tableData.fields.res" :height="tableHeight">
<el-table-column
:prop="item.prop"
:label="$t(item.label)"
@@ -72,13 +72,13 @@
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px">
<el-row class="mt-4">
<el-button @click="addDefaultRows()" link type="warning" icon="plus">{{ $t('db.addDefaultColumn') }}</el-button>
<el-button @click="addRow()" link type="primary" icon="plus">{{ $t('db.addColumn') }}</el-button>
</el-row>
</el-tab-pane>
<el-tab-pane :label="$t('db.index')" name="2">
<el-table :data="tableData.indexs.res" :max-height="tableData.height">
<el-table :data="tableData.indexs.res" :height="tableHeight">
<el-table-column :prop="item.prop" :label="$t(item.label)" v-for="item in tableData.indexs.colNames" :key="item.prop">
<template #default="scope">
<el-input v-if="item.prop === 'indexName'" size="small" disabled v-model="scope.row.indexName"></el-input>
@@ -90,8 +90,8 @@
collapse-tags
collapse-tags-tooltip
filterable
size="small"
@change="indexChanges(scope.row)"
style="width: 100%"
>
<el-option v-for="cl in tableData.indexs.columns" :key="cl.name" :label="cl.name" :value="cl.name">
{{ cl.name + ' - ' + (cl.remark || '') }}
@@ -114,7 +114,7 @@
</el-table-column>
</el-table>
<el-row style="margin-top: 20px">
<el-row class="mt-4">
<el-button @click="addIndex()" link type="primary" icon="plus">{{ $t('db.addIndex') }}</el-button>
</el-row>
</el-tab-pane>
@@ -173,6 +173,8 @@ type ColName = {
width?: number;
};
const tableHeight = 'calc(100vh - 320px)';
const formRef: any = ref();
const tableRef: any = useTemplateRef('tableRef');
@@ -272,7 +274,6 @@ const state = reactive({
tableComment: '',
oldTableName: '',
oldTableComment: '',
height: 'calc(100vh - 310px)',
db: '',
},
});

View File

@@ -1,9 +1,8 @@
<template>
<div class="flex-all-center h-full">
<div class="h-full">
<ResourceOpPanel @resized="onResizeTagTree">
<template #left>
<tag-tree
class="machine-terminal-tree"
ref="tagTreeRef"
:resource-type="TagResourceTypePath.MachineAuthCert"
:tag-path-node-type="NodeTypeTagPath"
@@ -34,7 +33,7 @@
<template #right>
<el-card class="h-full" body-class="machine-terminal-tabs h-full !p-1 flex flex-col flex-1">
<el-tabs v-if="state.tabs.size > 0" type="card" @tab-remove="onRemoveTab" v-model="state.activeTermName" class="!h-full w-full">
<el-tab-pane class="!h-full" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
<el-tab-pane class="!h-full flex flex-col" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
<template #label>
<el-popconfirm @confirm="handleReconnect(dt, true)" :title="$t('machine.reConnTips')">
<template #reference>
@@ -63,7 +62,7 @@
</el-popover>
</template>
<div :ref="(el: any) => setTerminalWrapperRef(el, dt.key)" class="terminal-wrapper" style="height: calc(100vh - 155px)">
<div :ref="(el: any) => setTerminalWrapperRef(el, dt.key)" class="terminal-wrapper flex-1 h-[calc(100vh-155px)]">
<TerminalBody
v-if="dt.params.protocol == MachineProtocolEnum.Ssh.value"
:mount-init="false"

View File

@@ -1,5 +1,5 @@
<template>
<div class="mock-data-dialog">
<div>
<el-dialog
:title="title"
v-model="dialogVisible"
@@ -35,8 +35,8 @@
<dynamic-form-edit v-model="params" />
</el-form-item>
<el-form-item required prop="script" class="100w">
<div style="width: 100%">
<el-form-item required prop="script">
<div class="w-full">
<monaco-editor v-model="form.script" language="shell" height="300px" />
</div>
</el-form-item>
@@ -66,9 +66,6 @@ import { useI18nFormValidate, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
import { Rules } from '@/common/rule';
const props = defineProps({
visible: {
type: Boolean,
},
data: {
type: Object,
},
@@ -83,7 +80,9 @@ const props = defineProps({
},
});
const emit = defineEmits(['update:visible', 'cancel', 'submitSuccess']);
const dialogVisible = defineModel<boolean>('visible', { default: false });
const emit = defineEmits(['cancel', 'submitSuccess']);
const rules = {
name: [Rules.requiredInput('common.name')],
@@ -96,7 +95,6 @@ const { isCommon, machineId } = toRefs(props);
const scriptForm: any = ref(null);
const state = reactive({
dialogVisible: false,
params: [] as any,
form: {
id: null,
@@ -110,11 +108,10 @@ const state = reactive({
btnLoading: false,
});
const { dialogVisible, params, form, btnLoading } = toRefs(state);
const { params, form, btnLoading } = toRefs(state);
watch(props, (newValue: any) => {
state.dialogVisible = newValue.visible;
if (!newValue.visible) {
if (!dialogVisible.value) {
return;
}
if (newValue.data) {
@@ -142,7 +139,7 @@ const btnOk = async () => {
};
const cancel = () => {
emit('update:visible', false);
dialogVisible.value = false;
emit('cancel');
state.params = [];
};

View File

@@ -1,5 +1,5 @@
<template>
<div class="file-manage">
<div>
<el-dialog
@open="getScripts()"
:title="title"
@@ -68,10 +68,11 @@
:close-on-click-modal="false"
:modal="false"
@close="closeTermnial"
body-class="h-[560px]"
draggable
append-to-body
>
<TerminalBody ref="terminal" :cmd="terminalDialog.cmd" :socket-url="getMachineTerminalSocketUrl(props.authCertName)" height="560px" />
<TerminalBody ref="terminal" :cmd="terminalDialog.cmd" :socket-url="getMachineTerminalSocketUrl(props.authCertName)" />
</el-dialog>
<script-edit
@@ -86,7 +87,7 @@
</template>
<script lang="ts" setup>
import { ref, toRefs, reactive, watch, Ref } from 'vue';
import { ref, toRefs, reactive, Ref } from 'vue';
import { ElMessage } from 'element-plus';
import TerminalBody from '@/components/terminal/TerminalBody.vue';
import { getMachineTerminalSocketUrl, machineApi } from './api';
@@ -102,19 +103,19 @@ import { useI18nCreateTitle, useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI
const { t } = useI18n();
const props = defineProps({
visible: { type: Boolean },
machineId: { type: Number },
authCertName: { type: String },
title: { type: String },
});
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId']);
const dialogVisible = defineModel<boolean>('visible', { default: false });
const emit = defineEmits(['cancel', 'update:machineId']);
const paramsForm: any = ref(null);
const pageTableRef: Ref<any> = ref(null);
const state = reactive({
dialogVisible: false,
selectionData: [],
searchItems: [SearchItem.select('type', 'common.type').withEnum(ScriptTypeEnum)],
columns: [
@@ -151,11 +152,7 @@ const state = reactive({
},
});
const { dialogVisible, columns, selectionData, query, editDialog, scriptParamsDialog, resultDialog, terminalDialog } = toRefs(state);
watch(props, async (newValue) => {
state.dialogVisible = newValue.visible;
});
const { columns, selectionData, query, editDialog, scriptParamsDialog, resultDialog, terminalDialog } = toRefs(state);
const getScripts = async () => {
pageTableRef.value.search();
@@ -271,7 +268,7 @@ const deleteRow = async (rows: any) => {
* 关闭取消按钮触发的事件
*/
const handleClose = () => {
emit('update:visible', false);
dialogVisible.value = false;
emit('update:machineId', null);
emit('cancel');
state.query.type = ScriptTypeEnum.Private.value;

View File

@@ -1,7 +1,7 @@
<template>
<div class="file-manage">
<div>
<el-dialog v-if="dialogVisible" :title="title" v-model="dialogVisible" :show-close="true" :before-close="handleClose" width="50%">
<el-table :data="fileTable" stripe style="width: 100%" v-loading="loading">
<el-table :data="fileTable" stripe v-loading="loading">
<el-table-column prop="name" :label="$t('common.name')" min-width="100px">
<template #header>
<el-button class="ml0" type="primary" circle size="small" icon="Plus" @click="add()"> </el-button>
@@ -29,9 +29,8 @@
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 10px" type="flex" justify="end">
<el-row class="mt-2" type="flex" justify="end">
<el-pagination
style="text-align: center"
:total="total"
layout="prev, pager, next, total, jumper"
v-model:current-page="query.pageNum"
@@ -41,7 +40,7 @@
</el-pagination>
</el-row>
<el-dialog destroy-on-close :title="fileDialog.title" v-model="fileDialog.visible" :close-on-click-modal="false" width="70%">
<el-drawer destroy-on-close :title="fileDialog.title" v-model="fileDialog.visible" :close-on-click-modal="false" size="70%" header-class="!mb-0">
<machine-file
:title="fileDialog.title"
:machine-id="machineId"
@@ -50,7 +49,7 @@
:path="fileDialog.path"
:protocol="protocol"
/>
</el-dialog>
</el-drawer>
<machine-file-content
:title="fileContent.title"
@@ -66,33 +65,29 @@
<script lang="ts" setup>
import { reactive, toRefs, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { machineApi } from '../api';
import { FileTypeEnum } from '../enums';
import MachineFile from './MachineFile.vue';
import MachineFileContent from './MachineFileContent.vue';
import { useI18n } from 'vue-i18n';
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
import { useI18nDeleteConfirm, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
const { t } = useI18n();
const props = defineProps({
visible: { type: Boolean },
protocol: { type: Number, default: 1 },
machineId: { type: Number },
authCertName: { type: String },
title: { type: String },
});
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId']);
const dialogVisible = defineModel<boolean>('visible', { default: false });
const emit = defineEmits(['cancel', 'update:machineId']);
const addFile = machineApi.addConf;
const delFile = machineApi.delConf;
const files = machineApi.files;
const state = reactive({
dialogVisible: false,
query: {
id: 0,
pageNum: 1,
@@ -122,11 +117,10 @@ const state = reactive({
},
});
const { dialogVisible, loading, query, total, fileTable, fileDialog, fileContent } = toRefs(state);
const { loading, query, total, fileTable, fileDialog, fileContent } = toRefs(state);
watch(props, async (newValue) => {
state.dialogVisible = newValue.visible;
if (newValue.machineId && newValue.visible) {
if (newValue.machineId && dialogVisible.value) {
await getFiles();
}
});
@@ -198,28 +192,10 @@ const showFileContent = async (fileId: number, path: string) => {
* 关闭取消按钮触发的事件
*/
const handleClose = () => {
emit('update:visible', false);
dialogVisible.value = false;
emit('update:machineId', null);
emit('cancel');
state.fileTable = [];
};
</script>
<style lang="scss">
.machine-file-upload-exec {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
vertical-align: middle;
position: relative;
text-decoration: none;
}
.inline-block {
display: inline-block;
margin-right: 10px;
}
.margin-change {
display: inline-block;
margin-left: 10px;
}
</style>
<style lang="scss"></style>

View File

@@ -1,9 +1,11 @@
<template>
<div class="machine-file">
<div>
<el-progress v-if="uploadProgressShow" style="width: 90%; margin-left: 20px" :text-inside="true" :stroke-width="20" :percentage="progressNum" />
<div class="machine-file h-full">
<div class="h-full flex flex-col">
<!-- 文件上传进度条 -->
<el-progress v-if="uploadProgressShow" class="ml-4 w-[90%]" :text-inside="true" :stroke-width="20" :percentage="progressNum" />
<el-row class="mb-2">
<!-- 文件路径 -->
<el-row class="mb-2 ml-4">
<el-breadcrumb separator-icon="ArrowRight">
<el-breadcrumb-item v-for="path in filePathNav" :key="path">
<el-link @click="setFiles(path.path)" style="font-weight: bold">{{ path.name }}</el-link>
@@ -11,251 +13,259 @@
</el-breadcrumb>
</el-row>
<el-table
ref="fileTableRef"
@cell-dblclick="cellDbclick"
@selection-change="handleSelectionChange"
height="65vh"
:data="filterFiles"
highlight-current-row
v-loading="loading"
>
<el-table-column type="selection" width="30" />
<!-- 文件列表 -->
<div class="flex-1 overflow-auto">
<el-table
ref="fileTableRef"
@cell-dblclick="cellDbclick"
@selection-change="handleSelectionChange"
height="100%"
:data="filterFiles"
highlight-current-row
v-loading="loading"
>
<el-table-column type="selection" width="30" />
<el-table-column prop="name" :label="$t('common.name')" min-width="380">
<template #header>
<div class="machine-file-table-header">
<div>
<el-button :disabled="nowPath == basePath" type="primary" circle size="small" icon="Back" @click="back()"> </el-button>
<el-button class="!ml-1" type="primary" circle size="small" icon="Refresh" @click="refresh()"> </el-button>
<!-- 文件名 -->
<el-table-column prop="name" :label="$t('common.name')" min-width="380">
<template #header>
<div class="machine-file-table-header">
<div>
<el-button :disabled="nowPath == basePath" type="primary" circle size="small" icon="Back" @click="back()"> </el-button>
<el-button class="!ml-1" type="primary" circle size="small" icon="Refresh" @click="refresh()"> </el-button>
<!-- 文件&文件夹上传 -->
<el-dropdown class="machine-file-upload-exec" trigger="click" size="small">
<span>
<el-button
v-auth="'machine:file:upload'"
class="!ml-1"
type="primary"
circle
size="small"
icon="Upload"
:title="$t('machine.upload')"
></el-button>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-upload
:before-upload="beforeUpload"
:on-success="uploadSuccess"
action=""
:http-request="uploadFile"
:headers="{ token }"
:show-file-list="false"
name="file"
class="machine-file-upload-exec"
>
<el-link>{{ $t('machine.file') }}</el-link>
</el-upload>
</el-dropdown-item>
<!-- 文件&文件夹上传 -->
<el-dropdown class="machine-file-upload-exec" trigger="click" size="small">
<span>
<el-button
v-auth="'machine:file:upload'"
class="!ml-1"
type="primary"
circle
size="small"
icon="Upload"
:title="$t('machine.upload')"
></el-button>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-upload
:before-upload="beforeUpload"
:on-success="uploadSuccess"
action=""
:http-request="uploadFile"
:headers="{ token }"
:show-file-list="false"
name="file"
class="machine-file-upload-exec"
>
<el-link>{{ $t('machine.file') }}</el-link>
</el-upload>
</el-dropdown-item>
<el-dropdown-item>
<div>
<el-link @click="addFinderToList">{{ $t('machine.folder') }}</el-link>
<input
type="file"
id="folderUploadInput"
ref="folderUploadRef"
webkitdirectory
directory
@change="uploadFolder"
style="display: none"
/>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button
:disabled="state.selectionFiles.length == 0"
v-auth="'machine:file:rm'"
@click="copyFile(state.selectionFiles)"
class="!ml-1"
type="primary"
circle
size="small"
icon="CopyDocument"
:title="$t('machine.copy')"
>
</el-button>
<el-button
:disabled="state.selectionFiles.length == 0"
v-auth="'machine:file:rm'"
@click="mvFile(state.selectionFiles)"
class="!ml-1"
type="primary"
circle
size="small"
icon="Rank"
:title="$t('machine.move')"
>
</el-button>
<el-button
v-auth="'machine:file:write'"
@click="showCreateFileDialog()"
class="!ml-1"
type="primary"
circle
size="small"
icon="FolderAdd"
:title="$t('common.create')"
>
</el-button>
<el-button
:disabled="state.selectionFiles.length == 0"
v-auth="'machine:file:rm'"
@click="deleteFile(state.selectionFiles)"
class="!ml-1"
type="danger"
circle
size="small"
icon="delete"
:title="$t('common.delete')"
>
</el-button>
<el-button-group v-if="state.copyOrMvFile.paths.length > 0" size="small" class="!ml-1">
<el-tooltip effect="customized" raw-content placement="top">
<template #content>
<div v-for="path in state.copyOrMvFile.paths" v-bind:key="path">{{ path }}</div>
<el-dropdown-item>
<div>
<el-link @click="addFinderToList">{{ $t('machine.folder') }}</el-link>
<input
type="file"
id="folderUploadInput"
ref="folderUploadRef"
webkitdirectory
directory
@change="uploadFolder"
style="display: none"
/>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button @click="pasteFile" type="primary">
{{ isCpFile() ? $t('machine.copy') : $t('machine.move') }}
{{ $t('machine.paste') }}{{ state.copyOrMvFile.paths.length }}</el-button
>
</el-tooltip>
<el-button
:disabled="state.selectionFiles.length == 0"
v-auth="'machine:file:rm'"
@click="copyFile(state.selectionFiles)"
class="!ml-1"
type="primary"
circle
size="small"
icon="CopyDocument"
:title="$t('machine.copy')"
>
</el-button>
<el-button icon="CloseBold" @click="cancelCopy" />
</el-button-group>
<el-button
:disabled="state.selectionFiles.length == 0"
v-auth="'machine:file:rm'"
@click="mvFile(state.selectionFiles)"
class="!ml-1"
type="primary"
circle
size="small"
icon="Rank"
:title="$t('machine.move')"
>
</el-button>
<el-button
v-auth="'machine:file:write'"
@click="showCreateFileDialog()"
class="!ml-1"
type="primary"
circle
size="small"
icon="FolderAdd"
:title="$t('common.create')"
>
</el-button>
<el-button
:disabled="state.selectionFiles.length == 0"
v-auth="'machine:file:rm'"
@click="deleteFile(state.selectionFiles)"
class="!ml-1"
type="danger"
circle
size="small"
icon="delete"
:title="$t('common.delete')"
>
</el-button>
<el-button-group v-if="state.copyOrMvFile.paths.length > 0" size="small" class="!ml-1">
<el-tooltip effect="customized" raw-content placement="top">
<template #content>
<div v-for="path in state.copyOrMvFile.paths" v-bind:key="path">{{ path }}</div>
</template>
<el-button @click="pasteFile" type="primary">
{{ isCpFile() ? $t('machine.copy') : $t('machine.move') }}
{{ $t('machine.paste') }}{{ state.copyOrMvFile.paths.length }}</el-button
>
</el-tooltip>
<el-button icon="CloseBold" @click="cancelCopy" />
</el-button-group>
</div>
<div class="w-[150px]">
<el-input v-model="fileNameFilter" size="small" :placeholder="$t('machine.fileNameFilterPlaceholder')" clearable />
</div>
</div>
</template>
<div style="width: 150px">
<el-input v-model="fileNameFilter" size="small" :placeholder="$t('machine.fileNameFilterPlaceholder')" clearable />
<template #default="scope">
<span v-if="scope.row.isFolder">
<SvgIcon :size="15" name="folder" color="#007AFF" />
</span>
<span v-else>
<SvgIcon :size="15" :name="scope.row.icon" />
</span>
<span class="!ml-1 inline-block w-[90%]">
<div v-if="scope.row.nameEdit">
<el-input
@keyup.enter="fileRename(scope.row)"
:ref="(el: any) => el?.focus()"
@blur="filenameBlur(scope.row)"
v-model="scope.row.name"
/>
</div>
<el-link v-else @click="getFile(scope.row)" style="font-weight: bold" :underline="false">{{ scope.row.name }}</el-link>
</span>
</template>
</el-table-column>
<el-table-column prop="size" label="Size" min-width="90" sortable>
<template #default="scope">
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == '-'"> {{ formatByteSize(scope.row.size) }} </span>
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && scope.row.dirSize"> {{ scope.row.dirSize }} </span>
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && !scope.row.dirSize">
<el-button @click="getDirSize(scope.row)" type="primary" link :loading="scope.row.loadingDirSize">
{{ $t('machine.calculate') }}
</el-button>
</span>
</template>
</el-table-column>
<el-table-column prop="mode" :label="$t('machine.attribute')" width="110"> </el-table-column>
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" :label="$t('machine.user')" min-width="70" show-overflow-tooltip>
<template #default="scope">
{{ userMap.get(scope.row.uid)?.uname || scope.row.uid }}
</template>
</el-table-column>
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" :label="$t('machine.group')" min-width="70" show-overflow-tooltip>
<template #default="scope">
{{ groupMap.get(scope.row.gid)?.gname || scope.row.gid }}
</template>
</el-table-column>
<el-table-column prop="modTime" :label="$t('machine.modificationTime')" width="160" sortable> </el-table-column>
<el-table-column :width="100">
<template #header>
<el-popover placement="top" :width="270" trigger="hover">
<template #reference>
<SvgIcon name="QuestionFilled" :size="18" class="pointer-icon mr-2" />
</template>
<div>{{ $t('machine.renameTips') }}</div>
</el-popover>
{{ $t('common.operation') }}
</template>
<template #default="scope">
<div class="flex gap-1.5">
<!-- 基础信息 -->
<el-popover
placement="top-start"
:title="`${scope.row.path} - ${$t('machine.fileDetail')}`"
:width="520"
trigger="click"
@show="showFileStat(scope.row)"
>
<template #reference>
<span style="color: #67c23a; font-weight: bold">
<el-link
@click="showFileStat(scope.row)"
icon="InfoFilled"
:underline="false"
link
:loading="scope.row.loadingStat"
></el-link>
</span>
</template>
<el-input disabled autosize v-model="scope.row.stat" type="textarea" />
</el-popover>
<!-- 下载文件 -->
<el-link
@click="downloadFile(scope.row)"
v-if="scope.row.type == '-'"
v-auth="'machine:file:write'"
type="primary"
icon="download"
:underline="false"
:title="$t('machine.download')"
></el-link>
<!-- 删除文件 -->
<el-link
@click="deleteFile([scope.row])"
v-if="!dontOperate(scope.row)"
v-auth="'machine:file:rm'"
type="danger"
icon="delete"
:underline="false"
:title="$t('common.delete')"
></el-link>
</div>
</div>
</template>
<template #default="scope">
<span v-if="scope.row.isFolder">
<SvgIcon :size="15" name="folder" color="#007AFF" />
</span>
<span v-else>
<SvgIcon :size="15" :name="scope.row.icon" />
</span>
<span class="!ml-1" style="display: inline-block; width: 90%">
<div v-if="scope.row.nameEdit">
<el-input
@keyup.enter="fileRename(scope.row)"
:ref="(el: any) => el?.focus()"
@blur="filenameBlur(scope.row)"
v-model="scope.row.name"
/>
</div>
<el-link v-else @click="getFile(scope.row)" style="font-weight: bold" :underline="false">{{ scope.row.name }}</el-link>
</span>
</template>
</el-table-column>
<el-table-column prop="size" label="Size" min-width="90" sortable>
<template #default="scope">
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == '-'"> {{ formatByteSize(scope.row.size) }} </span>
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && scope.row.dirSize"> {{ scope.row.dirSize }} </span>
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && !scope.row.dirSize">
<el-button @click="getDirSize(scope.row)" type="primary" link :loading="scope.row.loadingDirSize">
{{ $t('machine.calculate') }}
</el-button>
</span>
</template>
</el-table-column>
<el-table-column prop="mode" :label="$t('machine.attribute')" width="110"> </el-table-column>
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" :label="$t('machine.user')" min-width="70" show-overflow-tooltip>
<template #default="scope">
{{ userMap.get(scope.row.uid)?.uname || scope.row.uid }}
</template>
</el-table-column>
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" :label="$t('machine.group')" min-width="70" show-overflow-tooltip>
<template #default="scope">
{{ groupMap.get(scope.row.gid)?.gname || scope.row.gid }}
</template>
</el-table-column>
<el-table-column prop="modTime" :label="$t('machine.modificationTime')" width="160" sortable> </el-table-column>
<el-table-column :width="130">
<template #header>
<el-popover placement="top" :width="270" trigger="hover">
<template #reference>
<SvgIcon name="QuestionFilled" :size="18" class="pointer-icon mr-2" />
</template>
<div>{{ $t('machine.renameTips') }}</div>
</el-popover>
{{ $t('common.operation') }}
</template>
<template #default="scope">
<el-link
@click="deleteFile([scope.row])"
v-if="!dontOperate(scope.row)"
v-auth="'machine:file:rm'"
type="danger"
icon="delete"
:underline="false"
:title="$t('common.delete')"
></el-link>
<el-link
@click="downloadFile(scope.row)"
v-if="scope.row.type == '-'"
v-auth="'machine:file:write'"
type="primary"
icon="download"
:underline="false"
class="ml-2"
:title="$t('machine.download')"
></el-link>
<el-popover
placement="top-start"
:title="`${scope.row.path} - ${$t('machine.fileDetail')}`"
:width="520"
trigger="click"
@show="showFileStat(scope.row)"
>
<template #reference>
<span style="color: #67c23a; font-weight: bold">
<el-link
@click="showFileStat(scope.row)"
icon="InfoFilled"
:underline="false"
link
class="ml-2"
:loading="scope.row.loadingStat"
></el-link>
</span>
</template>
<el-input disabled autosize v-model="scope.row.stat" type="textarea" />
</el-popover>
</template>
</el-table-column>
</el-table>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-dialog

View File

@@ -1,5 +1,5 @@
<template>
<div class="machine-file-content">
<div>
<el-dialog
destroy-on-close
:before-close="handleClose"
@@ -9,28 +9,25 @@
top="5vh"
width="65%"
>
<div>
<monaco-editor :can-change-mode="true" v-model="content" :language="fileType" />
<div v-loading="loadingContent">
<monaco-editor :can-change-mode="true" v-model="fileContent" :language="fileType" />
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'machine:file:write'" type="primary" @click="updateContent">{{ $t('common.save') }}</el-button>
</div>
<el-button @click="handleClose">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'machine:file:write'" type="primary" @click="updateContent">{{ $t('common.save') }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, toRefs, watch } from 'vue';
import { computed, reactive, toRefs, watch } from 'vue';
import { machineApi } from '../api';
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
import { useI18nSaveSuccessMsg } from '@/hooks/useI18n';
const props = defineProps({
visible: { type: Boolean, default: false },
protocol: { type: Number, default: 1 },
title: { type: String, default: '' },
machineId: { type: Number },
@@ -39,41 +36,50 @@ const props = defineProps({
path: { type: String, default: '' },
});
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId']);
const dialogVisible = defineModel<boolean>('visible', { default: false });
const emit = defineEmits(['cancel', 'update:machineId']);
const updateFileContent = machineApi.updateFileContent;
const state = reactive({
dialogVisible: false,
loadingContent: false,
content: '',
fileType: '',
});
const { dialogVisible, content, fileType } = toRefs(state);
const { fileType } = toRefs(state);
const {
isFetching: loadingContent,
execute: getFileContentExec,
data: fileContent,
} = machineApi.fileContent.useApi(
computed(() => {
return {
fileId: props.fileId,
path: props.path,
machineId: props.machineId,
authCertName: props.authCertName,
protocol: props.protocol,
};
})
);
watch(props, async (newValue) => {
if (newValue.visible) {
if (dialogVisible.value) {
await getFileContent();
}
state.dialogVisible = newValue.visible;
});
const getFileContent = async () => {
const path = props.path;
const res = await machineApi.fileContent.request({
fileId: props.fileId,
path,
machineId: props.machineId,
authCertName: props.authCertName,
protocol: props.protocol,
});
state.fileType = getFileType(path);
state.content = res;
fileContent.value = '';
state.fileType = getFileType(props.path);
await getFileContentExec();
};
const handleClose = () => {
state.dialogVisible = false;
emit('update:visible', false);
dialogVisible.value = false;
};
const updateContent = async () => {

View File

@@ -1,5 +1,5 @@
<template>
<div class="redis-data-op flex-all-center h-full">
<div class="redis-data-op h-full">
<ResourceOpPanel>
<template #left>
<tag-tree
@@ -144,9 +144,9 @@
</Pane>
<Pane>
<div class="h-full card !p-1">
<el-tabs @tab-remove="removeDataTab" v-model="state.activeName">
<el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
<div class="h-full card !p-1 key-deatil">
<el-tabs class="h-full" @tab-remove="removeDataTab" v-model="state.activeName">
<el-tab-pane class="h-full" closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
<key-detail :redis="redisInst" :key-info="dt.keyInfo" @change-key="searchKey()" @del-key="delKey" />
</el-tab-pane>
</el-tabs>
@@ -614,6 +614,18 @@ const delKey = async (key: string) => {
</script>
<style lang="scss" scoped>
.key-deatil {
.el-tabs__header {
background-color: var(--el-color-white);
border-bottom: 1px solid var(--el-border-color);
}
::v-deep(.el-tabs__item) {
padding: 0 10px;
height: 29px;
}
}
.redis-data-op {
.key-list-vtree .folder-label {
font-weight: bold;

View File

@@ -1,6 +1,6 @@
<template>
<div class="format-viewer-container">
<div class="mb-1 float-right">
<div class="mb-1 flex justify-end">
<el-select v-model="selectedView" class="format-selector" size="small" placeholder="Text">
<template #prefix>
<SvgIcon name="view" />

View File

@@ -1,21 +1,10 @@
<template>
<div>
<el-container direction="vertical" class="key-tab-container">
<!-- key info -->
<key-header
ref="keyHeader"
:redis="props.redis"
:key-info="state.keyInfo"
@refresh-content="refreshContent"
@del-key="delKey"
@change-key="changeKey"
class="key-header-info"
>
</key-header>
<div class="flex flex-col h-full">
<!-- key info -->
<key-header :redis="props.redis" :key-info="state.keyInfo" @refresh-content="refreshContent" @del-key="delKey" @change-key="changeKey"> </key-header>
<!-- key content -->
<component ref="keyValueRef" :is="components[componentName]" :redis="props.redis" :key-info="keyInfo"> </component>
</el-container>
<!-- key content , 暂不懂为啥要用h-0或者其他随便设个高度h-full就是不行会导致loadMore按钮不显示 -->
<component class="h-0 flex-1" ref="keyValueRef" :is="components[componentName]" :redis="props.redis" :key-info="keyInfo"> </component>
</div>
</template>
<script lang="ts" setup>
@@ -106,24 +95,10 @@ onMounted(() => {
});
</script>
<style lang="scss">
.key-tab-container {
/*padding-left: 5px;*/
}
.key-header-info {
// margin-top: 15px;
}
.key-content-container {
margin-top: 15px;
}
// .key-detail-filter-value {
// width: 90%;
// height: 24px;
// padding: 0 5px;
// }
/*tooltip in table width limit*/
.el-tooltip__popper {
max-width: 50%;

View File

@@ -1,5 +1,5 @@
<template>
<div>
<div class="flex">
<!-- key name -->
<div class="key-header-item key-name-input">
<el-input ref="keyNameInput" v-model="ki.key" :title="$t('redis.renameTips')" placeholder="KeyName">
@@ -180,12 +180,6 @@ const ttlConveter = (ttl: any) => {
cursor: pointer;
}
.key-header-item {
/*padding-right: 15px;*/
/*margin-bottom: 10px;*/
float: left;
}
.key-header-item.key-name-input {
width: calc(100% - 322px);
min-width: 230px;

View File

@@ -1,37 +1,47 @@
<template>
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
<el-table size="small" border :data="hashValues" height="500" min-height="300" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="field" label="field" show-overflow-tooltip min-width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #header>
<el-input
class="key-detail-filter-value"
v-model="state.filterValue"
@keyup.enter="hscan(true, true)"
:placeholder="$t('redis.filterPlaceholder')"
clearable
size="small"
/>
</template>
<template #default="scope">
<el-link @click="showEditDialog(scope.row)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="hdel(scope.row.field, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="flex flex-col">
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
</div>
<div class="flex-1 overflow-auto">
<el-table size="small" border :data="hashValues" height="100%" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="field" label="field" show-overflow-tooltip min-width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #header>
<el-input
v-model="state.filterValue"
@keyup.enter="hscan(true, true)"
:placeholder="$t('redis.filterPlaceholder')"
clearable
size="small"
/>
</template>
<template #default="scope">
<el-link @click="showEditDialog(scope.row)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="hdel(scope.row.field, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<!-- load more content -->
<div class="content-more-container">
<el-button size="small" @click="hscan()" :disabled="loadMoreDisable" class="content-more-btn"> {{ $t('redis.loadMore') }} </el-button>
</div>
<el-dialog :title="$t('redis.addNewLine')" v-model="editDialog.visible" width="600px" :destroy-on-close="true" :close-on-click-modal="false">
<el-dialog
:title="$t('redis.addNewLine')"
v-model="editDialog.visible"
width="600px"
:destroy-on-close="true"
:close-on-click-modal="false"
body-class="p-1"
>
<el-form>
<el-form-item>
<el-input v-model="editDialog.field" placeholder="field" />
@@ -42,7 +52,7 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<div>
<el-button @click="editDialog.visible = false">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'redis:data:save'" type="primary" @click="confirmEditData">{{ $t('common.confirm') }}</el-button>
</div>
@@ -168,18 +178,4 @@ const confirmEditData = async () => {
defineExpose({ initData });
</script>
<style lang="scss">
#string-value-text {
flex-grow: 1;
display: flex;
position: relative;
.text-type-select {
position: absolute;
z-index: 2;
right: 10px;
top: 10px;
max-width: 70px;
}
}
</style>
<style lang="scss"></style>

View File

@@ -1,26 +1,37 @@
<template>
<div>
<el-button @click="showEditDialog(null, -1)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
<el-table size="small" border :data="values" height="450" min-height="300" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #default="scope">
<el-link @click="showEditDialog(scope.row, scope.$index)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="lrem(scope.row, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="flex flex-col">
<div>
<el-button @click="showEditDialog(null, -1)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
</div>
<div class="flex-1 overflow-auto">
<el-table size="small" border :data="values" height="100%" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #default="scope">
<el-link @click="showEditDialog(scope.row, scope.$index)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="lrem(scope.row, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<!-- load more content -->
<div class="content-more-container">
<el-button size="small" @click="getListValue(false)" :disabled="loadMoreDisable" class="content-more-btn"> {{ $t('redis.loadMore') }} </el-button>
</div>
<el-dialog :title="$t('redis.addNewLine')" v-model="editDialog.visible" width="600px" :destroy-on-close="true" :close-on-click-modal="false">
<el-dialog
:title="$t('redis.addNewLine')"
v-model="editDialog.visible"
width="600px"
:destroy-on-close="true"
:close-on-click-modal="false"
body-class="p-1"
>
<el-form>
<el-form-item>
<format-viewer class="!w-full" ref="formatViewerRef" :content="editDialog.content"></format-viewer>

View File

@@ -1,36 +1,46 @@
<template>
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
<el-table size="small" border :data="setDatas" height="450" min-height="300" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #header>
<el-input
class="key-detail-filter-value"
v-model="state.filterValue"
@keyup.enter="sscanData(true, true)"
:placeholder="$t('redis.filterPlaceholder')"
clearable
size="small"
/>
</template>
<template #default="scope">
<el-link @click="showEditDialog(scope.row)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="srem(scope.row, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="flex flex-col">
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
</div>
<div class="flex-1 overflow-auto">
<el-table size="small" border :data="setDatas" height="100%" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #header>
<el-input
v-model="state.filterValue"
@keyup.enter="sscanData(true, true)"
:placeholder="$t('redis.filterPlaceholder')"
clearable
size="small"
/>
</template>
<template #default="scope">
<el-link @click="showEditDialog(scope.row)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="srem(scope.row, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<!-- load more content -->
<div class="content-more-container">
<el-button size="small" @click="sscanData(false)" :disabled="loadMoreDisable" class="content-more-btn"> {{ $t('redis.loadMore') }} </el-button>
</div>
<el-dialog :title="$t('redis.addNewLine')" v-model="editDialog.visible" width="600px" :destroy-on-close="true" :close-on-click-modal="false">
<el-dialog
:title="$t('redis.addNewLine')"
v-model="editDialog.visible"
width="600px"
:destroy-on-close="true"
:close-on-click-modal="false"
body-class="p-1"
>
<el-form>
<el-form-item>
<format-viewer class="!w-full" ref="formatViewerRef" :content="editDialog.content"></format-viewer>

View File

@@ -1,11 +1,9 @@
<template>
<div>
<el-form class="key-content-string" label-width="auto">
<div>
<format-viewer ref="formatViewerRef" height="250px" :content="string.value"></format-viewer>
</div>
<div class="flex flex-col h-full">
<el-form label-width="auto">
<format-viewer ref="formatViewerRef" height="280px" :content="string.value"></format-viewer>
</el-form>
<div class="!mt-2 float-right">
<div class="mt-2 flex justify-end">
<el-button @click="saveValue" type="primary" v-auth="'redis:data:save'">{{ $t('common.save') }}</el-button>
</div>
</div>
@@ -89,19 +87,4 @@ const saveValue = async () => {
defineExpose({ initData });
</script>
<style lang="scss">
// .key-content-string .format-viewer-container {
// min-height: calc(100vh - 253px);
// }
// /*text viewer box*/
// .key-content-string .el-textarea textarea {
// font-size: 14px;
// height: calc(100vh - 436px);
// }
// /*json in monaco editor*/
// .key-content-string .monaco-editor-content {
// height: calc(100vh - 450px) !important;
// }
</style>
<style lang="scss"></style>

View File

@@ -1,37 +1,48 @@
<template>
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
<el-table size="small" border :data="values" height="450" min-height="300" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="score" label="score" show-overflow-tooltip min-width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #header>
<el-input
class="key-detail-filter-value"
v-model="state.filterValue"
@keyup.enter="zscanData(true)"
:placeholder="$t('redis.filterPlaceholder')"
clearable
size="small"
/>
</template>
<template #default="scope">
<el-link @click="showEditDialog(scope.row)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="zrem(scope.row, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="flex flex-col">
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb-2">{{ $t('redis.addNewLine') }}</el-button>
</div>
<div class="flex-1 overflow-auto">
<el-table size="small" border :data="values" height="100%" stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
<el-table-column resizable sortable prop="score" label="score" show-overflow-tooltip min-width="100"> </el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
<el-table-column :label="$t('common.operation')">
<template #header>
<el-input
v-model="state.filterValue"
@keyup.enter="zscanData(true)"
:placeholder="$t('redis.filterPlaceholder')"
clearable
size="small"
/>
</template>
<template #default="scope">
<el-link @click="showEditDialog(scope.row)" :underline="false" type="primary" icon="edit" plain></el-link>
<el-popconfirm :title="$t('redis.deleteConfirm')" @confirm="zrem(scope.row, scope.$index)">
<template #reference>
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml-1"></el-link>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<!-- load more content -->
<div class="content-more-container">
<el-button size="small" @click="loadDatas()" :disabled="loadMoreDisable" class="content-more-btn"> {{ $t('redis.loadMore') }} </el-button>
</div>
<el-dialog :title="$t('redis.addNewLine')" v-model="editDialog.visible" width="600px" :destroy-on-close="true" :close-on-click-modal="false">
<el-dialog
:title="$t('redis.addNewLine')"
v-model="editDialog.visible"
width="600px"
:destroy-on-close="true"
:close-on-click-modal="false"
body-class="p-1"
>
<el-form>
<el-form-item>
<el-input type="number" v-model.number="editDialog.score" placeholder="score" />

View File

@@ -27,6 +27,7 @@
</div>
<el-scrollbar class="tag-tree-data">
<el-tree
class="min-w-full inline-block"
ref="tagTreeRef"
node-key="id"
highlight-current

View File

@@ -1,5 +1,5 @@
<template>
<div class="account-dialog">
<div>
<el-dialog :title="title" v-model="visible" :before-close="cancel" :show-close="false" width="600px" :destroy-on-close="true">
<el-form :model="form" ref="accountForm" :rules="rules" label-width="auto">
<el-form-item prop="name" :label="$t('system.account.name')">
@@ -73,7 +73,7 @@ const props = defineProps({
});
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
const emit = defineEmits(['cancel', 'val-change']);
const visible = defineModel<boolean>('visible', { default: false });

View File

@@ -1,6 +1,6 @@
<template>
<div>
<el-drawer :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" size="1000px" :destroy-on-close="true">
<el-drawer :title="title" v-model="visible" :show-close="false" :before-close="cancel" size="1000px" :destroy-on-close="true">
<template #header>
<DrawerHeader :header="title" :back="cancel" />
</template>
@@ -14,7 +14,6 @@
</el-form-item>
<el-form-item prop="permission" :label="$t('system.sysconf.permission')">
<el-select
style="width: 100%"
remote
:remote-method="getAccount"
v-model="state.permissionAccount"
@@ -58,9 +57,6 @@ const rules = {
};
const props = defineProps({
visible: {
type: Boolean,
},
data: {
type: [Boolean, Object],
},
@@ -69,13 +65,14 @@ const props = defineProps({
},
});
const visible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
const emit = defineEmits(['cancel', 'val-change']);
const configForm: any = ref(null);
const state = reactive({
dvisible: false,
params: [] as any,
accounts: [] as any,
permissionAccount: [] as any,
@@ -90,42 +87,37 @@ const state = reactive({
},
});
const { dvisible, params, form } = toRefs(state);
const { params, form } = toRefs(state);
const { isFetching: saveBtnLoading, execute: saveConfigExec } = configApi.save.useApi(form);
watch(
() => props.visible,
() => {
state.dvisible = props.visible;
if (!state.dvisible) {
return;
}
watch(visible, () => {
if (!visible.value) {
return;
}
if (props.data) {
state.form = { ...(props.data as any) };
if (state.form.params) {
state.params = JSON.parse(state.form.params);
} else {
state.params = [];
}
if (props.data) {
state.form = { ...(props.data as any) };
if (state.form.params) {
state.params = JSON.parse(state.form.params);
} else {
state.form = { permission: 'all' } as any;
state.params = [];
}
if (state.form.permission != 'all') {
const accounts = state.form.permission.split(',');
state.permissionAccount = accounts.slice(0, accounts.length - 1);
} else {
state.permissionAccount = [];
}
} else {
state.form = { permission: 'all' } as any;
state.params = [];
}
);
if (state.form.permission != 'all') {
const accounts = state.form.permission.split(',');
state.permissionAccount = accounts.slice(0, accounts.length - 1);
} else {
state.permissionAccount = [];
}
});
const cancel = () => {
// 更新父组件visible prop对应的值为false
emit('update:visible', false);
visible.value = false;
// 若父组件有取消事件,则调用
emit('cancel');
state.permissionAccount = [];

View File

@@ -53,7 +53,6 @@
import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
import ConfigEdit from './ConfigEdit.vue';
import { configApi } from '../api';
import { ElMessage } from 'element-plus';
import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn } from '@/components/pagetable';
import { hasPerms } from '@/components/auth/auth';

View File

@@ -1,6 +1,6 @@
<template>
<div class="system-menu-dialog-container layout-pd">
<el-dialog :title="title" :destroy-on-close="true" v-model="dialogVisible" width="800px">
<div>
<el-dialog :title="title" :destroy-on-close="true" v-model="visible" width="800px">
<el-form :model="form" :inline="true" ref="menuForm" :rules="rules" label-width="auto">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
@@ -111,9 +111,6 @@ import { Rules } from '@/common/rule';
const { t } = useI18n();
const props = defineProps({
visible: {
type: Boolean,
},
data: {
type: [Boolean, Object],
},
@@ -125,8 +122,10 @@ const props = defineProps({
},
});
const visible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
const emit = defineEmits(['cancel', 'val-change']);
const menuForm: any = ref(null);
@@ -161,7 +160,6 @@ const trueFalseOption = [
];
const state = reactive({
dialogVisible: false,
form: {
id: null,
name: null,
@@ -183,12 +181,14 @@ const state = reactive({
submitForm: {},
});
const { dialogVisible, form, submitForm } = toRefs(state);
const { form, submitForm } = toRefs(state);
const { isFetching: saveBtnLoading, execute: saveResouceExec } = resourceApi.save.useApi(submitForm);
watchEffect(() => {
state.dialogVisible = props.visible;
if (!visible.value) {
return;
}
if (props.data) {
state.form = { ...(props.data as any) };
} else {
@@ -271,7 +271,7 @@ const parseMenuMeta = (meta: any) => {
};
const cancel = () => {
emit('update:visible', false);
visible.value = false;
emit('cancel');
};
</script>

View File

@@ -17,6 +17,7 @@
</div>
<el-scrollbar>
<el-tree
class="inline-block min-w-full"
ref="resourceTreeRef"
:indent="24"
node-key="id"

View File

@@ -44,7 +44,6 @@
import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
import { AccountStatusEnum } from '../enums';
import { accountApi, roleApi } from '../api';
import { ElMessage } from 'element-plus';
import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn } from '@/components/pagetable';
import { hasPerms } from '@/components/auth/auth';

View File

@@ -2,7 +2,7 @@
<div>
<el-dialog
:title="$t('system.role.allocateMenuTitle', { roleName: roleInfo?.name })"
v-model="dialogVisible"
v-model="visible"
:before-close="cancel"
:show-close="false"
width="400px"
@@ -46,9 +46,6 @@ import { getMenuIcon } from '../resource';
const { t } = useI18n();
const props = defineProps({
visible: {
type: Boolean,
},
title: {
type: String,
},
@@ -65,8 +62,10 @@ const props = defineProps({
},
});
const visible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
const emit = defineEmits(['cancel', 'val-change']);
const defaultProps = {
children: 'children',
@@ -76,17 +75,15 @@ const defaultProps = {
const menuTree: any = ref(null);
const state = reactive({
dialogVisible: false,
roleInfo: null as any,
submiting: false,
});
const { dialogVisible, roleInfo } = toRefs(state);
const { roleInfo } = toRefs(state);
watch(
() => props.visible,
() => visible,
(newValue) => {
state.dialogVisible = newValue;
state.roleInfo = props.role;
}
);
@@ -109,8 +106,7 @@ const btnOk = async () => {
};
const cancel = () => {
// 更新父组件visible prop对应的值为false
emit('update:visible', false);
visible.value = false;
emit('cancel');
};
</script>

View File

@@ -1,6 +1,6 @@
<template>
<div class="role-dialog">
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="600px" :destroy-on-close="true">
<div>
<el-dialog :title="title" v-model="visible" :show-close="false" :before-close="cancel" width="600px" :destroy-on-close="true">
<el-form ref="roleForm" :model="form" :rules="rules" label-width="auto">
<el-form-item prop="name" :label="$t('system.role.roleName')" required>
<el-input v-model="form.name" auto-complete="off"></el-input>
@@ -45,9 +45,6 @@ const rules = {
};
const props = defineProps({
visible: {
type: Boolean,
},
data: {
type: [Boolean, Object],
},
@@ -56,12 +53,13 @@ const props = defineProps({
},
});
const visible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
const emit = defineEmits(['cancel', 'val-change']);
const roleForm: any = ref(null);
const state = reactive({
dvisible: false,
form: {
id: null,
name: '',
@@ -71,12 +69,14 @@ const state = reactive({
},
});
const { dvisible, form } = toRefs(state);
const { form } = toRefs(state);
const { isFetching: saveBtnLoading, execute: saveRoleExec } = roleApi.save.useApi(form);
watchEffect(() => {
state.dvisible = props.visible;
if (!visible.value) {
return;
}
if (props.data) {
state.form = { ...(props.data as any) };
} else {
@@ -85,8 +85,7 @@ watchEffect(() => {
});
const cancel = () => {
// 更新父组件visible prop对应的值为false
emit('update:visible', false);
visible.value = false;
// 若父组件有取消事件,则调用
emit('cancel');
};

View File

@@ -54,7 +54,6 @@ import RoleEdit from './RoleEdit.vue';
import ResourceEdit from './ResourceEdit.vue';
import ShowResource from './ShowResource.vue';
import { roleApi, resourceApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus';
import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn } from '@/components/pagetable';
import { hasPerms } from '@/components/auth/auth';

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-dialog @close="closeDialog" :title="title" :before-close="closeDialog" v-model="dialogVisible" width="400px">
<el-tree style="height: 50vh; overflow: auto" :data="resources" node-key="id" :props="defaultProps">
<el-dialog @close="closeDialog" :title="props.title" :before-close="closeDialog" v-model="visible" width="400px">
<el-tree style="height: 50vh; overflow: auto" :data="props.resources" node-key="id" :props="defaultProps">
<template #default="{ node, data }">
<span class="custom-tree-node">
<SvgIcon :name="getMenuIcon(data)" class="mb-0.5 mr-0.5" />
@@ -35,15 +35,11 @@
</template>
<script lang="ts" setup>
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: {
type: Boolean,
},
resources: {
type: Array,
},
@@ -52,29 +48,15 @@ const props = defineProps({
},
});
//定义事件
const emit = defineEmits(['update:visible', 'update:resources']);
const visible = defineModel<boolean>('visible', { default: false });
const defaultProps = {
children: 'children',
label: 'name',
};
const state = reactive({
dialogVisible: false,
});
const { dialogVisible } = toRefs(state);
watch(
() => props.visible,
(newValue) => {
state.dialogVisible = newValue;
}
);
const closeDialog = () => {
emit('update:visible', false);
emit('update:resources', []);
visible.value = false;
};
</script>

View File

@@ -109,7 +109,7 @@ func (p *procinstAppImpl) CancelProc(ctx context.Context, procinstId uint64) err
if la == nil {
return errorx.NewBiz("no login")
}
if procinst.CreatorId != consts.AdminId && procinst.CreatorId != la.Id {
if la.Id != consts.AdminId && procinst.CreatorId != la.Id {
return errorx.NewBizI(ctx, imsg.ErrProcinstCancelSelf)
}
procinst.Status = entity.ProcinstStatusCancelled