feat: 1.前端组件升级 2.redis数据操作支持string,hash,set的新增修改

This commit is contained in:
meilin.huang
2022-01-12 16:00:31 +08:00
parent 468ecb5623
commit 1216ce3b52
52 changed files with 1808 additions and 645 deletions

View File

@@ -54,18 +54,9 @@ func (l *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
timestamp := time.Now().Local().Format("2006-01-02 15:04:05.000") timestamp := time.Now().Local().Format("2006-01-02 15:04:05.000")
level := entry.Level level := entry.Level
logMsg := fmt.Sprintf("%s [%s]", timestamp, strings.ToUpper(level.String())) logMsg := fmt.Sprintf("%s [%s]", timestamp, strings.ToUpper(level.String()))
// 如果存在调用信息,且为error级别以上记录文件及行号 // 如果存在调用信息,记录方法信息及行号
if caller := entry.Caller; caller != nil { if caller := entry.Caller; caller != nil {
var fp string logMsg = logMsg + fmt.Sprintf(" [%s:%d]", caller.Function, caller.Line)
// 全路径切割,只获取项目相关路径,
// 即/Users/hml/Desktop/project/go/mayfly-go/server/test.go只获取/server/test.go
ps := strings.Split(caller.File, "mayfly-go/")
if len(ps) >= 2 {
fp = ps[1]
} else {
fp = ps[0]
}
logMsg = logMsg + fmt.Sprintf(" [%s:%d]", fp, caller.Line)
} }
for k, v := range entry.Data { for k, v := range entry.Data {
logMsg = logMsg + fmt.Sprintf(" [%s=%v]", k, v) logMsg = logMsg + fmt.Sprintf(" [%s=%v]", k, v)

View File

@@ -7,29 +7,29 @@
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/" "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
}, },
"dependencies": { "dependencies": {
"@types/lodash": "^4.14.178", "axios": "^0.24.0",
"axios": "^0.21.1",
"codemirror": "^5.61.0", "codemirror": "^5.61.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"countup.js": "^2.0.7", "countup.js": "^2.0.7",
"cropperjs": "^1.5.11", "cropperjs": "^1.5.11",
"echarts": "^5.1.1", "echarts": "^5.1.1",
"echarts-wordcloud": "^2.0.0", "element-plus": "^1.3.0-beta.2",
"element-plus": "^1.0.2-beta.44", "@element-plus/icons-vue": "^0.2.4",
"jsonlint": "^1.6.3", "jsonlint": "^1.6.3",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mitt": "^2.1.0", "mitt": "^3.0.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"screenfull": "^5.1.0", "screenfull": "^5.1.0",
"sortablejs": "^1.13.0", "sortablejs": "^1.13.0",
"sql-formatter": "^2.3.3", "sql-formatter": "^4.0.2",
"vue": "^3.0.5", "vue": "^3.2.20",
"vue-router": "^4.0.2", "vue-router": "^4.0.12",
"vuex": "^4.0.0-rc.2", "vuex": "^4.0.2",
"xterm": "^4.9.0", "xterm": "^4.16.0",
"xterm-addon-fit": "^0.4.0" "xterm-addon-fit": "^0.5.0"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.14.178",
"@types/axios": "^0.14.0", "@types/axios": "^0.14.0",
"@types/clipboard": "^2.0.1", "@types/clipboard": "^2.0.1",
"@types/node": "^15.6.0", "@types/node": "^15.6.0",
@@ -40,14 +40,14 @@
"@vitejs/plugin-vue": "^1.2.2", "@vitejs/plugin-vue": "^1.2.2",
"@vue/compiler-sfc": "^3.0.11", "@vue/compiler-sfc": "^3.0.11",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"eslint": "^7.27.0", "eslint": "^8.5.0",
"eslint-plugin-vue": "^7.9.0", "eslint-plugin-vue": "^8.2.0",
"prettier": "^2.3.0", "prettier": "^2.3.0",
"sass": "^1.34.0", "sass": "^1.45.1",
"sass-loader": "^11.1.1", "sass-loader": "^12.4.0",
"typescript": "^4.2.4", "typescript": "^4.2.4",
"vite": "^2.3.2", "vite": "^2.7.4",
"vue-eslint-parser": "^7.6.0" "vue-eslint-parser": "^8.0.1"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@@ -1,4 +1,5 @@
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import * as svg from '@element-plus/icons-vue';
// 获取阿里字体图标 // 获取阿里字体图标
const getAlicdnIconfont = () => { const getAlicdnIconfont = () => {
@@ -30,22 +31,16 @@ const getAlicdnIconfont = () => {
// 初始化获取 css 样式,获取 element plus 自带图标 // 初始化获取 css 样式,获取 element plus 自带图标
const elementPlusIconfont = () => { const elementPlusIconfont = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
nextTick(() => { nextTick(() => {
const styles: any = document.styleSheets; const icons = svg as any;
let sheetsIconList = []; const sheetsIconList = [];
for (let i = 0; i < styles.length; i++) { for (const i in icons) {
for (let j = 0; j < styles[i].cssRules.length; j++) { sheetsIconList.push(`${icons[i].name}`);
if (styles[i].cssRules[j].selectorText && styles[i].cssRules[j].selectorText.indexOf('.el-icon-') === 0) { }
sheetsIconList.push( if (sheetsIconList.length > 0) resolve(sheetsIconList);
`${styles[i].cssRules[j].selectorText.substring(1, styles[i].cssRules[j].selectorText.length).replace(/\:\:before/gi, '')}` else reject('未获取到值,请刷新重试');
); });
} });
}
}
if (sheetsIconList.length > 0) resolve(sheetsIconList);
else reject('未获取到值,请刷新重试');
});
});
}; };
// 初始化获取 css 样式,这里使用 fontawesome 的图标 // 初始化获取 css 样式,这里使用 fontawesome 的图标

View File

@@ -1,57 +1,71 @@
<template> <template>
<div class="icon-selector"> <div class="icon-selector">
<el-popover :placement="placement" :width="650" v-model:visible="fontIconVisible" popper-class="icon-selector-popper"> <el-popover placement="bottom" :width="450" v-model:visible="fontIconVisible" popper-class="icon-selector-popper">
<template #reference> <template #reference>
<el-input <el-input
v-model="modelValue" v-model="fontIconSearch"
placeholder="请点击选择图标" :placeholder="fontIconPlaceholder"
clearable :clearable="clearable"
size="small" :disabled="disabled"
:size="size"
ref="inputWidthRef" ref="inputWidthRef"
:prefix-icon="modelValue"
@clear="onClearFontIcon" @clear="onClearFontIcon"
></el-input> @focus="onIconFocus"
@blur="onIconBlur"
>
<template #prepend>
<SvgIcon :name="prepend" class="font14" />
</template>
</el-input>
</template> </template>
<transition name="el-zoom-in-top"> <transition name="el-zoom-in-top">
<div class="icon-selector-warp" v-show="fontIconVisible"> <div class="icon-selector-warp" v-show="fontIconVisible">
<div class="icon-selector-warp-title">请选择一个图标</div> <div class="icon-selector-warp-title flex">
<div v-if="isAllOn" class="icon-selector-all"> <div class="flex-auto">{{ title }}</div>
<el-input v-model="fontIconSearch" placeholder="请输入内容进行搜索" size="small"></el-input> <div class="icon-selector-warp-title-tab" v-if="type === 'all'">
<div class="icon-selector-all-tabs"> <span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标"
<div >ali</span
class="icon-selector-all-tabs-item" >
v-for="(v, k) in fontIconTabsList" <span
:key="k" :class="{ 'span-active': fontIconType === 'ele' }"
@click="onFontIconTabsClick(v, k)" @click="onIconChange('ele')"
:class="{ 'icon-selector-all-tabs-active': fontIconTabsIndex === k }" class="ml10"
title="elementPlus 图标"
>ele</span
>
<span
:class="{ 'span-active': fontIconType === 'awe' }"
@click="onIconChange('awe')"
class="ml10"
title="fontawesome 图标"
>awe</span
> >
<div class="label">{{ v.label }}</div>
</div>
</div> </div>
</div> </div>
<div class="icon-selector-warp-row"> <div class="icon-selector-warp-row">
<el-row :gutter="10"> <el-scrollbar ref="selectorScrollbarRef">
<el-col <el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
:xs="4" <el-col
:sm="4" :xs="6"
:md="2" :sm="4"
:lg="2" :md="4"
:xl="1" :lg="4"
:class="`${fontIconTabsIcon}-col`" :xl="4"
@click="onColClick(v, k)" @click="onColClick(v)"
v-for="(v, k) in fontIconSheetsFilterList" v-for="(v, k) in fontIconSheetsFilterList"
:key="k" :key="k"
> >
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconIndex === k }"> <div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
<div class="flex-margin"> <div class="flex-margin">
<div class="icon-selector-warp-item-value"> <div class="icon-selector-warp-item-value">
<i :class="[fontIconTabsIcon, v]"></i> <SvgIcon :name="v" />
</div>
</div> </div>
</div> </div>
</div> </el-col>
</el-col> </el-row>
</el-row> <el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
<el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0"></el-empty> </el-scrollbar>
</div> </div>
</div> </div>
</transition> </transition>
@@ -60,43 +74,92 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, toRefs, reactive, onMounted, computed } from 'vue'; import { ref, toRefs, reactive, onMounted, nextTick, computed, watch } from 'vue';
import initIconfont from '@/common/utils/getStyleSheets.ts'; import initIconfont from '@/common/utils/getStyleSheets';
export default { export default {
name: 'iconSelector', name: 'iconSelector',
emits: ['update:modelValue', 'get', 'clear'],
props: { props: {
// 是否开启高级功能 // 输入框前置内容
isAllOn: { prepend: {
type: String,
default: () => 'Pointer',
},
// 输入框占位文本
placeholder: {
type: String,
default: () => '请输入内容搜索图标或者选择图标',
},
// 输入框占位文本
size: {
type: String,
default: () => 'default',
},
// 弹窗标题
title: {
type: String,
default: () => '请选择图标',
},
// icon 图标类型
type: {
type: String,
default: () => 'ele',
},
// 禁用
disabled: {
type: Boolean, type: Boolean,
default: () => false, default: () => false,
}, },
// 出现位置 // 是否可清空
placement: { clearable: {
type: String, type: Boolean,
default: () => 'bottom', default: () => true,
}, },
modelValue: { // 自定义空状态描述文字
emptyDescription: {
type: String, type: String,
default: () => '无相关图标',
}, },
// 双向绑定值,字段名为固定,改了之后将不生效
// 参考https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
modelValue: String,
}, },
setup(props, { emit }) { setup(props, { emit }) {
const inputWidthRef = ref(); const inputWidthRef = ref();
const selectorScrollbarRef = ref();
const state: any = reactive({ const state: any = reactive({
// fontIcon: '', fontIconPrefix: '',
// fontIconPrefix: '',
fontIconVisible: false, fontIconVisible: false,
fontIconWidth: 0, fontIconWidth: 0,
fontIconIndex: '',
fontIconSearch: '', fontIconSearch: '',
fontIconTabsIndex: 0, fontIconTabsIndex: 0,
fontIconTabsIcon: 'iconfont ali',
fontIconTabsList: [{ label: 'element' }, { label: 'iconfont' }],
fontIconSheetsList: [], fontIconSheetsList: [],
fontIconSheetsListAli: [], fontIconPlaceholder: '',
fontIconSheetsListEle: [], fontIconType: 'ali',
fontIconSheetsListAwe: [], fontIconShow: true,
}); });
// 设置无数据时的空状态 // 处理 input 获取焦点时modelValue 有值时,改变 input 的 placeholder 值
const onIconFocus = () => {
state.fontIconVisible = true;
if (!props.modelValue) return false;
state.fontIconSearch = '';
state.fontIconPlaceholder = props.modelValue;
};
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
const onIconBlur = () => {
state.fontIconVisible = false;
setTimeout(() => {
const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
if (icon.length <= 0) state.fontIconSearch = '';
}, 300);
};
// 处理 icon 双向绑定数值回显
const initModeValueEcho = () => {
if (props.modelValue === '') return false;
state.fontIconPlaceholder = props.modelValue;
state.fontIconPrefix = props.modelValue;
};
// 图标搜索及图标数据显示
const fontIconSheetsFilterList = computed(() => { const fontIconSheetsFilterList = computed(() => {
if (!state.fontIconSearch) return state.fontIconSheetsList; if (!state.fontIconSearch) return state.fontIconSheetsList;
let search = state.fontIconSearch.trim().toLowerCase(); let search = state.fontIconSearch.trim().toLowerCase();
@@ -104,67 +167,95 @@ export default {
if (item.toLowerCase().indexOf(search) !== -1) return item; if (item.toLowerCase().indexOf(search) !== -1) return item;
}); });
}); });
// 获取 input 的宽度 // 获取 input 的宽度
// const getInputWidth = () => { const getInputWidth = () => {
// nextTick(() => { nextTick(() => {
// state.fontIconWidth = inputWidthRef.value.$el.offsetWidth; state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
// });
// };
// 监听页面宽度改变
// const initResize = () => {
// window.addEventListener('resize', () => {
// getInputWidth();
// });
// };
// 初始化数据
const initFontIconData = () => {
initIconfont.ele().then((res: any) => {
state.fontIconSheetsListEle = res;
state.fontIconSheetsList = res;
}); });
// initIconfont.ali().then((res: any) => {
// state.fontIconSheetsListAli = res;
// });
}; };
// 当前项点击 // 监听页面宽度改变
const onColClick = (v: any, k: number) => { const initResize = () => {
state.fontIconIndex = k; window.addEventListener('resize', () => {
// state.fontIcon = v; getInputWidth();
});
};
// 初始化数据
const initFontIconData = async (type: string) => {
state.fontIconSheetsList = [];
if (type === 'ali') {
// await initIconfont.ali().then((res: any) => {
// // 阿里字体图标使用 `iconfont xxx`
// state.fontIconSheetsList = res.map((i) => `iconfont ${i}`);
// });
} else if (type === 'ele') {
await initIconfont.ele().then((res: any) => {
state.fontIconSheetsList = res;
});
} else if (type === 'awe') {
// await initIconfont.awe().then((res: any) => {
// // fontawesome字体图标使用 `fa xxx`
// state.fontIconSheetsList = res.map((i) => `fa ${i}`);
// });
}
// 初始化 input 的 placeholder
// 参考单项数据流https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
state.fontIconPlaceholder = props.placeholder;
// 初始化双向绑定回显
initModeValueEcho();
// 切换时,滚动条置顶。感兴趣可以使用 keep-alive <component :is="xxx"/> 进行缓存
selectorScrollbarRef.value.wrap$.scrollTop = 0;
};
// 图标点击切换
const onIconChange = (type: string) => {
state.fontIconType = type;
initFontIconData(type);
};
// 获取当前点击的 icon 图标
const onColClick = (v: any) => {
state.fontIconPlaceholder = v;
state.fontIconVisible = false; state.fontIconVisible = false;
// if (state.fontIconTabsIndex === 0) state.fontIconPrefix = `iconfont ali ${v}`; state.fontIconPrefix = v;
// else if (state.fontIconTabsIndex === 1) state.fontIconPrefix = `${v}`; emit('get', state.fontIconPrefix);
emit('update:modelValue', v);
};
// input 点击清除按钮时
const onClearFontIcon = () => {
state.fontIconIndex = '';
// state.fontIconPrefix = '';
emit('update:modelValue', state.fontIconPrefix); emit('update:modelValue', state.fontIconPrefix);
}; };
// tabs 点击 // 清空当前点击的 icon 图标
const onFontIconTabsClick = (v: any, k: number) => { const onClearFontIcon = () => {
state.fontIconTabsIndex = k; state.fontIconPrefix = '';
if (v.label === 'iconfont') state.fontIconSheetsList = state.fontIconSheetsListAli; emit('clear', state.fontIconPrefix);
else if (v.label === 'element') state.fontIconSheetsList = state.fontIconSheetsListEle; emit('update:modelValue', state.fontIconPrefix);
if (k === 0) state.fontIconTabsIcon = `iconfont ali`;
else if (k === 1) state.fontIconTabsIcon = `ele`;
}; };
// 页面加载时 // 页面加载时
onMounted(() => { onMounted(() => {
initFontIconData(); // 判断默认进来是什么类型图标,进行 tab 回显
// initResize(); if (props.type === 'all') {
// getInputWidth(); // if (props.modelValue?.indexOf('iconfont') > -1) onIconChange('ali');
// else if (props.modelValue?.indexOf('element') > -1) onIconChange('ele');
// else if (props.modelValue?.indexOf('fa') > -1) onIconChange('awe');
// else onIconChange('ali');
} else {
onIconChange(props.type);
}
initResize();
getInputWidth();
}); });
// 监听双向绑定 modelValue 的变化
watch(
() => props.modelValue,
() => {
initModeValueEcho();
}
);
return { return {
inputWidthRef, inputWidthRef,
selectorScrollbarRef,
fontIconSheetsFilterList, fontIconSheetsFilterList,
onColClick, onColClick,
onIconChange,
onClearFontIcon, onClearFontIcon,
onFontIconTabsClick, onIconFocus,
onIconBlur,
...toRefs(state), ...toRefs(state),
}; };
}, },
}; };
</script> </script>

View File

@@ -0,0 +1,24 @@
<script lang="ts">
// 渲染函数https://v3.cn.vuejs.org/guide/render-function.html
import { h, resolveComponent, defineComponent } from 'vue';
export default defineComponent({
name: 'svgIcon',
props: {
// svg 图标组件名字
name: {
type: String,
},
// svg 大小
size: {
type: Number,
},
// svg 颜色
color: {
type: String,
},
},
setup(props: any) {
return () => h('i', { class: 'el-icon', style: `--font-size: ${props.size};--color: ${props.color}` }, [h(resolveComponent(`${props.name}`))]);
},
});
</script>

View File

@@ -7,14 +7,33 @@ import { globalComponentSize } from '@/common/utils/componentSize.ts';
import { dateStrFormat } from '@/common/utils/date.ts' import { dateStrFormat } from '@/common/utils/date.ts'
import ElementPlus from 'element-plus'; import ElementPlus from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css'; import 'element-plus/dist/index.css';
import '@/theme/index.scss'; import '@/theme/index.scss';
import mitt from 'mitt'; import mitt from 'mitt';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import locale from 'element-plus/lib/locale/lang/zh-cn' import locale from 'element-plus/lib/locale/lang/zh-cn'
import * as svg from '@element-plus/icons-vue';
import SvgIcon from '@/components/svgIcon/index.vue';
const app = createApp(App); const app = createApp(App);
/**
* 导出全局注册 element plus svg 图标
* @param app vue 实例
* @description 使用https://element-plus.gitee.io/zh-CN/component/icon.html
*/
function elSvg(app: any) {
const icons = svg as any;
for (const i in icons) {
app.component(`${icons[i].name}`, icons[i]);
}
app.component('SvgIcon', SvgIcon);
}
elSvg(app)
directive(app);
app.use(router) app.use(router)
.use(store, key) .use(store, key)
.use(ElementPlus, { size: globalComponentSize, locale: locale }) .use(ElementPlus, { size: globalComponentSize, locale: locale })
@@ -42,5 +61,3 @@ app.config.errorHandler = function (err: any, vm, info) {
} }
app.config.globalProperties.mittBus = mitt(); app.config.globalProperties.mittBus = mitt();
directive(app);

View File

@@ -119,7 +119,7 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
globalViceTitle: 'mayfly', globalViceTitle: 'mayfly',
// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn // 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
globalI18n: 'zh-cn', globalI18n: 'zh-cn',
// 默认全局组件大小,可选值"<|medium|small|mini>",默认 '' // 默认全局组件大小,可选值"<|large|default|small>",默认 ''
globalComponentSize: '', globalComponentSize: '',
}, },
}, },

View File

@@ -1,12 +1,10 @@
@import 'mixins/function.scss'; @import 'mixins/function.scss';
@import 'mixins/element-mixins.scss'; @import 'mixins/element-mixins.scss';
@import 'mixins/mixins.scss';
/* Button 按钮 /* Button 按钮
------------------------------- */ ------------------------------- */
// text // text
.el-button {
font-weight: 500;
}
.el-button--text { .el-button--text {
color: set-color(primary); color: set-color(primary);
&:focus, &:focus,
@@ -749,9 +747,14 @@
// 默认样式修改 // 默认样式修改
.el-menu { .el-menu {
border-right: none !important; border-right: none !important;
width: 220px;
}
// 修复点击左侧菜单折叠再展开时,宽度不跟随问题
.el-menu--collapse {
width: 64px !important;
} }
.el-menu-item, .el-menu-item,
.el-submenu__title { .el-sub-menu__title {
height: 50px !important; height: 50px !important;
line-height: 50px !important; line-height: 50px !important;
color: var(--bg-menuBarColor); color: var(--bg-menuBarColor);
@@ -759,31 +762,31 @@
} }
// horizontal 水平方向时 // horizontal 水平方向时
.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-menu-item.is-active,
.el-menu--horizontal > .el-submenu.is-active .el-submenu__title { .el-menu--horizontal > .el-sub-menu.is-active .el-sub-menu__title {
border-bottom: 3px solid !important; border-bottom: 3px solid !important;
border-bottom-color: set-color(primary); border-bottom-color: set-color(primary);
color: set-color(primary); color: set-color(primary) !important;
} }
.el-menu--horizontal .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal .el-menu-item:not(.is-disabled):focus,
.el-menu--horizontal .el-menu-item:not(.is-disabled):hover, .el-menu--horizontal .el-menu-item:not(.is-disabled):hover,
.el-menu--horizontal > .el-submenu:focus .el-submenu__title, .el-menu--horizontal > .el-sub-menu:focus .el-sub-menu__title,
.el-menu--horizontal > .el-submenu:hover .el-submenu__title, .el-menu--horizontal > .el-sub-menu:hover .el-sub-menu__title,
.el-menu--horizontal .el-menu .el-menu-item.is-active, .el-menu--horizontal .el-menu .el-menu-item.is-active,
.el-menu--horizontal .el-menu .el-submenu.is-active > .el-submenu__title { .el-menu--horizontal .el-menu .el-sub-menu.is-active > .el-sub-menu__title {
color: set-color(primary); color: set-color(primary) !important;
} }
.el-menu.el-menu--horizontal { .el-menu.el-menu--horizontal {
border-bottom: none !important; border-bottom: none !important;
} }
.el-menu--horizontal > .el-menu-item, .el-menu--horizontal > .el-menu-item,
.el-menu--horizontal > .el-submenu .el-submenu__title { .el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
color: var(--bg-topBarColor); color: var(--bg-topBarColor);
} }
// 外部链接时 // 外部链接时
.el-menu-item a, .el-menu-item a,
.el-menu-item a:hover, .el-menu-item a:hover,
.el-menu-item i, .el-menu-item i,
.el-submenu__title i { .el-sub-menu__title i {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
@@ -793,22 +796,23 @@
} }
// 默认 hover 时 // 默认 hover 时
.el-menu-item:hover, .el-menu-item:hover,
.el-submenu__title:hover { .el-sub-menu__title:hover {
color: set-color(primary) !important; color: set-color(primary) !important;
background-color: transparent !important; background-color: transparent !important;
i { i {
color: set-color(primary); color: set-color(primary);
} }
} }
// 高亮时 // 高亮时/菜单收起时
.el-menu-item.is-active { .el-menu-item.is-active,
.el-menu--collapse .el-sub-menu.is-active i {
color: set-color(primary); color: set-color(primary);
} }
.el-active-extend { .el-active-extend {
color: #ffffff !important; color: var(--color-whites) !important;
background-color: set-color(primary) !important; background-color: set-color(primary) !important;
i { i {
color: #ffffff !important; color: var(--color-whites) !important;
} }
} }
#add-is-active { #add-is-active {
@@ -819,34 +823,37 @@
} }
// 菜单收起时且是a链接 // 菜单收起时且是a链接
.el-popper.is-dark a { .el-popper.is-dark a {
color: #ffffff !important; color: var(--color-whites) !important;
text-decoration: none; text-decoration: none;
} }
// 菜单收起时鼠标经过背景颜色/字体颜色 // 菜单收起时鼠标经过背景颜色/字体颜色
.el-popper.is-light { .el-popper.is-light {
.el-menu--vertical { .el-menu--vertical {
background: var(--bg-menuBar); .el-menu {
background: var(--bg-menuBar);
}
} }
.el-menu--horizontal { .el-menu--horizontal {
background: var(--bg-topBar); background: var(--bg-topBar);
.el-menu,
.el-menu-item, .el-menu-item,
.el-submenu__title { .el-sub-menu__title {
color: var(--bg-topBarColor); color: var(--bg-topBarColor);
background: var(--bg-topBar);
} }
} }
} }
// 第三方图标字体间距/大小设置 // 第三方图标字体间距/大小设置
.el-menu-item .iconfont, // .el-menu-item .iconfont,
.el-submenu .iconfont { // .el-sub-menu .iconfont {
font-size: 14px !important; // @include generalIcon;
display: inline-block; // }
vertical-align: middle; // .el-menu-item .fa,
margin-right: 5px; // .el-sub-menu .fa {
width: 24px; // @include generalIcon;
text-align: center; // }
}
// element plus 本身字体图标 // element plus 本身字体图标
.el-submenu [class^='el-icon-'] { .el-sub-menu .el-icon {
font-size: 14px !important; font-size: 14px !important;
} }
// 去掉离开浏览器时,菜单的默认高亮 // 去掉离开浏览器时,菜单的默认高亮
@@ -888,6 +895,9 @@
color: set-color(primary); color: set-color(primary);
background-color: set-color(primary-light-9); background-color: set-color(primary-light-9);
} }
.el-dropdown-menu .el-dropdown-menu__item {
white-space: nowrap;
}
/* Steps 步骤条 /* Steps 步骤条
------------------------------- */ ------------------------------- */
@@ -931,18 +941,25 @@
color: set-color(primary); color: set-color(primary);
} }
.el-overlay { .el-overlay {
display: flex; overflow: hidden;
align-items: center; .el-overlay-dialog {
justify-content: center; display: flex;
.el-dialog { align-items: center;
margin: 0 auto !important; justify-content: center;
.el-dialog__body { position: unset !important;
padding: 20px !important; width: 100%;
height: 100%;
.el-dialog {
margin: 0 auto !important;
position: absolute;
.el-dialog__body {
padding: 20px !important;
}
} }
} }
} }
.el-dialog__body { .el-dialog__body {
max-height: 70vh !important; max-height: calc(90vh - 111px) !important;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
@@ -998,6 +1015,9 @@
/* scrollbar /* scrollbar
------------------------------- */ ------------------------------- */
.el-scrollbar__bar {
z-index: 4;
}
.el-scrollbar__wrap { .el-scrollbar__wrap {
overflow-x: hidden !important; overflow-x: hidden !important;
max-height: 100%; /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/ max-height: 100%; /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/
@@ -1008,13 +1028,27 @@
.el-select-dropdown__wrap { .el-select-dropdown__wrap {
max-height: 274px !important; /*修复Select 选择器高度问题*/ max-height: 274px !important; /*修复Select 选择器高度问题*/
} }
.el-cascader-menu__wrap.el-scrollbar__wrap {
height: 204px !important; /*修复Cascader 级联选择器高度问题*/
}
/* Drawer 抽屉 /* Drawer 抽屉
------------------------------- */ ------------------------------- */
.el-drawer__body { .el-drawer {
width: 100%; --el-drawer-padding-primary: unset !important;
height: 100%; .el-drawer__header {
overflow: auto; padding: 0 15px !important;
height: 50px;
display: flex;
align-items: center;
margin-bottom: 0 !important;
border-bottom: 1px solid rgb(230, 230, 230);
}
.el-drawer__body {
width: 100%;
height: 100%;
overflow: auto;
}
} }
.el-drawer-fade-enter-active .el-drawer.rtl { .el-drawer-fade-enter-active .el-drawer.rtl {
animation: rtl-drawer-animation 0.3s ease-in reverse !important; animation: rtl-drawer-animation 0.3s ease-in reverse !important;
@@ -1027,4 +1061,4 @@
} }
.el-drawer-fade-leave-active .el-drawer.ltr { .el-drawer-fade-leave-active .el-drawer.ltr {
animation: ltr-drawer-animation 0.3s ease !important; animation: ltr-drawer-animation 0.3s ease !important;
} }

View File

@@ -0,0 +1,15 @@
import Api from '@/common/Api';
export const serviceApi = {
services: Api.create("/gw/services", 'get'),
saveService: Api.create("/gw/services", 'post'),
syncService: Api.create("/gw/services/{id}/sync", 'post'),
// 获取服务下的api信息
serviceApis: Api.create("/gw/services/{serviceId}/apis", 'get'),
saveServiceApi: Api.create("/gw/services/{serviceId}/apis", 'post'),
syncServiceApi: Api.create("/gw/services/{id}/apis/{apiId}/sync", 'post'),
// 获取项目下的成员信息
projectMems: Api.create("/gw/projects/{projectId}/members", 'get'),
saveProjectMem: Api.create("/gw/projects/{projectId}/members", 'post'),
deleteProjectMem: Api.create("/gw/projects/{projectId}/members/{accountId}", 'delete'),
}

View File

@@ -0,0 +1,192 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :show-close="false" :before-close="cancel" width="65%">
<el-form :model="form" ref="apiForm" :rules="rules" label-width="85px" size="small">
<el-form-item prop="serviceId" label="服务:" required>
<el-select style="width: 100%" v-model="form.serviceId" placeholder="请选择服务" @change="changeService" filterable>
<el-option v-for="item in services" :key="item.id" :label="`${item.name}`" :value="item.id"> </el-option>
</el-select>
</el-form-item>
<el-form-item prop="name" label="名称:" required>
<el-input v-model.trim="form.name" placeholder="请输入api名称" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="code" label="code:" required>
<el-input :disabled="form.id" v-model.trim="form.code" placeholder="请输入code" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="method" label="method:" required>
<el-select style="width: 100%" v-model="form.method" placeholder="请选择请求方法" @change="changeService" filterable>
<el-option key="get" label="GET" value="GET"> </el-option>
<el-option key="post" label="POST" value="POST"> </el-option>
<el-option key="put" label="PUT" value="PUT"> </el-option>
<el-option key="delete" label="DELETE" value="DELETE"> </el-option>
</el-select>
</el-form-item>
<el-form-item prop="uri" label="uri:" required>
<el-input v-model.trim="form.uri" placeholder="请输入method:uri格式"></el-input>
</el-form-item>
<el-form-item label="schema:">
<!-- <vue3-json-editor v-model="jsonschema" @json-change="schemaChange" :show-btns="false" :expandedOnStart="true" /> -->
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk" size="mini"> </el-button>
<el-button @click="cancel()" size="mini"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
import { serviceApi } from '../api';
import { ElMessage } from 'element-plus';
// import { Vue3JsonEditor } from 'vue3-json-editor';
export default defineComponent({
name: 'RedisEdit',
components: {
// Vue3JsonEditor,
},
props: {
visible: {
type: Boolean,
},
services: {
type: Array,
},
api: {
type: [Boolean, Object],
},
title: {
type: String,
},
},
setup(props: any, { emit }) {
const apiForm: any = ref(null);
const state = reactive({
dialogVisible: false,
services: [],
form: {
id: null,
name: null,
code: null,
uri: null,
serviceName: null,
serviceId: null,
schema: null,
},
jsonschema: {},
btnLoading: false,
rules: {
serviceId: [
{
required: true,
message: '请选择服务',
trigger: ['change', 'blur'],
},
],
name: [
{
required: true,
message: '请输入api名称',
trigger: ['change', 'blur'],
},
],
method: [
{
required: true,
message: '请输入请求method',
trigger: ['change', 'blur'],
},
],
uri: [
{
required: true,
message: '请输入请求uri',
trigger: ['change', 'blur'],
},
],
code: [
{
required: true,
message: '请输入code',
trigger: ['change', 'blur'],
},
],
},
});
watch(props, async (newValue) => {
state.services = newValue.services;
if (newValue.api) {
state.form = { ...newValue.api };
console.log(state.form)
if (state.form.schema) {
state.jsonschema = JSON.parse(state.form.schema as any);
}
} else {
state.form = { } as any;
}
state.dialogVisible = newValue.visible;
});
const changeService = (serviceId: number) => {
for (let p of state.services as any) {
if (p.id == serviceId) {
state.form.serviceName = p.name;
}
}
};
const schemaChange = (jsonValue: any) => {
state.form.schema = JSON.stringify(jsonValue) as any;
};
const btnOk = async () => {
apiForm.value.validate((valid: boolean) => {
if (valid) {
serviceApi.saveServiceApi.request(state.form).then(() => {
ElMessage.success('保存成功');
emit('val-change', state.form);
state.btnLoading = true;
setTimeout(() => {
state.btnLoading = false;
}, 1000);
cancel();
});
} else {
ElMessage.error('请正确填写信息');
return false;
}
});
};
const cancel = () => {
emit('update:visible', false);
emit('cancel');
setTimeout(() => {
apiForm.value.resetFields();
// 重置对象属性为null
state.form = {} as any;
state.jsonschema = {};
}, 200);
};
return {
...toRefs(state),
schemaChange,
apiForm,
changeService,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,239 @@
<template>
<div>
<div class="toolbar">
<el-button @click="showSaveApiDialog" v-auth="permissions.saveProject" type="primary" icon="el-icon-plus" size="mini">添加</el-button>
<el-button
@click="showSaveApiDialog(chooseData)"
v-auth="permissions.saveProject"
:disabled="chooseId == null"
type="primary"
icon="el-icon-edit"
size="mini"
>编辑</el-button
>
<el-button v-auth="'role:del'" :disabled="chooseId == null" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
<div style="float: right">
<el-select v-model="query.serviceId" @change="changeService" placeholder="请选择服务" filterable size="small">
<el-option v-for="item in services" :key="item.id" :label="`${item.name}`" :value="item.id"> </el-option>
</el-select>
<el-input
class="mr2 ml2"
placeholder="请输入服务名!"
size="small"
style="width: 140px"
v-model="query.name"
@clear="search"
clearable
></el-input>
<el-button @click="search" type="success" icon="el-icon-search" size="mini"></el-button>
</div>
</div>
<el-table :data="apis" @current-change="choose" border ref="table" style="width: 100%">
<el-table-column label="选择" width="50px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="serviceName" label="服务"></el-table-column>
<el-table-column prop="name" label="名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="code" label="code" show-overflow-tooltip> </el-table-column>
<el-table-column prop="method" label="method" min-width="45" show-overflow-tooltip> </el-table-column>
<el-table-column prop="uri" label="uri" show-overflow-tooltip> </el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="creator" label="创建者" min-width="50"> </el-table-column>
<el-table-column label="操作" min-width="50">
<template #default="scope">
<el-button
@click="syncServiceApi(scope.row)"
type="success"
icom="el-icon-tickets"
size="mini"
plain
:disabled="scope.row.canSync == -1"
>同步</el-button
>
</template>
</el-table-column>
<!-- <el-table-column label="查看更多" min-width="80px">
<template #default="scope">
<el-link @click.prevent="showMembers(scope.row)" type="success">成员</el-link>
<el-link class="ml5" @click.prevent="showEnv(scope.row)" type="info">环境</el-link>
</template>
</el-table-column> -->
</el-table>
<el-pagination
@current-change="handlePageChange"
style="text-align: center"
background
layout="prev, pager, next, total, jumper"
:total="total"
v-model:current-page="query.pageNum"
:page-size="query.pageSize"
/>
<api-edit :services="services" v-model:visible="saveApiDialog.visible" :api="saveApiDialog.form" @val-change="valChange"/>
<!-- <el-dialog width="400px" title="服务编辑" :before-close="cancelAddService" v-model="addServiceDialog.visible">
<el-form :model="addServiceDialog.form" size="small" label-width="70px">
<el-form-item label="服务名:" required>
<el-input :disabled="addServiceDialog.form.id ? true : false" v-model="addServiceDialog.form.name" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="描述:" required>
<el-input v-model="addServiceDialog.form.remark" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="地址:">
<el-input v-model="addServiceDialog.form.urls" auto-complete="off" placeholder="不填则注册中心获取"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="saveService" type="primary" size="small"> </el-button>
<el-button @click="cancelAddService()" size="small"> </el-button>
</div>
</template>
</el-dialog> -->
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
import { serviceApi } from '../api';
import { ElMessage } from 'element-plus';
import { notEmpty } from '@/common/assert';
import ApiEdit from './ApiEdit.vue'
export default defineComponent({
name: 'ServiceList',
components: {
ApiEdit
},
setup() {
const state = reactive({
permissions: {
saveProject: 'project:save',
saveMember: 'project:member:add',
delMember: 'project:member:del',
saveEnv: 'project:env:add',
},
query: {
pageNum: 1,
pageSize: 10,
name: null,
},
total: 0,
apis: [],
services: [],
btnLoading: false,
chooseId: null as any,
chooseData: null as any,
saveApiDialog: {
title: '新增api',
visible: false,
form: { name: '', remark: '' },
},
});
onMounted(() => {
getServices();
// search();
});
const search = async () => {
let res = await serviceApi.serviceApis.request(state.query);
state.apis = res.list;
state.total = res.total;
};
const getServices = async () => {
let res = await serviceApi.services.request({ pateNum: 1, pageSize: 100 });
state.services = res.list;
};
const changeService = async () => {
search()
}
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const showSaveApiDialog = (data: any) => {
if (data) {
state.saveApiDialog.form = { ...data };
} else {
state.saveApiDialog.form = {} as any;
}
state.saveApiDialog.visible = true;
};
const cancelAddApi = () => {
state.saveApiDialog.visible = false;
state.saveApiDialog.form = {} as any;
};
const saveService = async () => {
const form = state.saveApiDialog.form as any;
notEmpty(form.name, '服务名不能为空');
notEmpty(form.remark, '服务描述不能为空');
await serviceApi.saveService.request(form);
ElMessage.success('保存成功');
search();
cancelAddApi();
};
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
const syncServiceApi = async (item: any) => {
await serviceApi.syncServiceApi.request({apiId: item.id, id: item.serviceId})
ElMessage.success("同步成功")
item.canSync = -1
}
const valChange = () => {
search();
}
// const addEnv = async () => {
// const envForm = state.showEnvDialog.envForm;
// envForm.projectId = state.chooseData.id;
// await projectApi.saveProjectEnv.request(envForm);
// ElMessage.success('保存成功');
// state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: envForm.projectId });
// cancelAddEnv();
// };
return {
...toRefs(state),
search,
changeService,
handlePageChange,
choose,
showSaveApiDialog,
saveService,
syncServiceApi,
cancelAddApi,
valChange,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,223 @@
<template>
<div>
<div class="toolbar">
<el-button @click="showAddServiceDialog" v-auth="permissions.saveProject" type="primary" icon="el-icon-plus" size="mini">添加</el-button>
<el-button
@click="showAddServiceDialog(chooseData)"
v-auth="permissions.saveProject"
:disabled="chooseId == null"
type="primary"
icon="el-icon-edit"
size="mini"
>编辑</el-button
>
<el-button v-auth="'role:del'" :disabled="chooseId == null" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
<div style="float: right">
<el-input
class="mr2"
placeholder="请输入项目名!"
size="small"
style="width: 140px"
v-model="query.name"
@clear="search"
clearable
></el-input>
<el-button @click="search" type="success" icon="el-icon-search" size="mini"></el-button>
</div>
</div>
<el-table :data="services" @current-change="choose" border ref="table" style="width: 100%">
<el-table-column label="选择" width="50px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="服务名"></el-table-column>
<el-table-column prop="routePath" label="路由路径"></el-table-column>
<el-table-column prop="urls" label="服务地址">
<template #default="scope">
{{ scope.row.urls ? scope.row.urls : '注册中心' }}
</template>
</el-table-column>
<el-table-column prop="remark" label="描述" min-width="80px" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="creator" label="创建者"> </el-table-column>
<!-- <el-table-column label="查看更多" min-width="80px">
<template #default="scope">
<el-link @click.prevent="showMembers(scope.row)" type="success">成员</el-link>
<el-link class="ml5" @click.prevent="showEnv(scope.row)" type="info">环境</el-link>
</template>
</el-table-column> -->
<el-table-column label="操作" min-width="80px">
<template #default="scope">
<el-button
@click="syncService(scope.row)"
type="success"
icom="el-icon-tickets"
size="mini"
plain
:disabled="scope.row.canSync == -1"
>同步</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="handlePageChange"
style="text-align: center"
background
layout="prev, pager, next, total, jumper"
:total="total"
v-model:current-page="query.pageNum"
:page-size="query.pageSize"
/>
<el-dialog width="400px" title="服务编辑" :before-close="cancelAddService" v-model="addServiceDialog.visible">
<el-form :model="addServiceDialog.form" size="small" label-width="85px">
<el-form-item label="服务名:" required>
<el-input :disabled="addServiceDialog.form.id ? true : false" v-model="addServiceDialog.form.name" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="路由路径:" required>
<el-input v-model="addServiceDialog.form.routePath" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="描述:" required>
<el-input v-model="addServiceDialog.form.remark" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="地址:">
<el-input v-model="addServiceDialog.form.urls" auto-complete="off" placeholder="不填则注册中心获取"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="saveService" type="primary" size="small"> </el-button>
<el-button @click="cancelAddService()" size="small"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
import { serviceApi } from '../api';
import { ElMessage } from 'element-plus';
import { notEmpty } from '@/common/assert';
export default defineComponent({
name: 'ServiceList',
components: {},
setup() {
const state = reactive({
permissions: {
saveProject: 'project:save',
saveMember: 'project:member:add',
delMember: 'project:member:del',
saveEnv: 'project:env:add',
},
query: {
pageNum: 1,
pageSize: 10,
name: null,
},
total: 0,
services: [],
btnLoading: false,
chooseId: null as any,
chooseData: null as any,
addServiceDialog: {
title: '新增服务',
visible: false,
form: { name: '', remark: '' },
},
});
onMounted(() => {
search();
});
const search = async () => {
let res = await serviceApi.services.request(state.query);
state.services = res.list;
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const showAddServiceDialog = (data: any) => {
if (data) {
state.addServiceDialog.form = {...data};
} else {
state.addServiceDialog.form = {} as any;
}
state.addServiceDialog.visible = true;
};
const cancelAddService = () => {
state.addServiceDialog.visible = false;
state.addServiceDialog.form = {} as any;
};
const saveService = async () => {
const form = state.addServiceDialog.form as any;
notEmpty(form.name, '服务名不能为空');
notEmpty(form.remark, '服务描述不能为空');
await serviceApi.saveService.request(form);
ElMessage.success('保存成功');
search();
cancelAddService();
};
const syncService = async (item: any) => {
await serviceApi.syncService.request({id: item.id})
ElMessage.success("同步成功")
item.canSync = -1
}
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
// const addEnv = async () => {
// const envForm = state.showEnvDialog.envForm;
// envForm.projectId = state.chooseData.id;
// await projectApi.saveProjectEnv.request(envForm);
// ElMessage.success('保存成功');
// state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: envForm.projectId });
// cancelAddEnv();
// };
// const roleEditChange = (data: any) => {
// ElMessage.success('修改成功!');
// search();
// };
return {
...toRefs(state),
search,
handlePageChange,
choose,
showAddServiceDialog,
saveService,
syncService,
cancelAddService,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -83,7 +83,7 @@ export default defineComponent({
() => route.path, () => route.path,
() => { () => {
initCurrentRouteMeta(route.meta); initCurrentRouteMeta(route.meta);
proxy.$refs.layoutScrollbarRef.wrap.scrollTop = 0; proxy.$refs.layoutScrollbarRef.wrap$.scrollTop = 0;
} }
); );
return { return {

View File

@@ -33,7 +33,7 @@ export default {
watch( watch(
() => route.path, () => route.path,
() => { () => {
proxy.$refs.layoutDefaultsScrollbarRef.wrap.scrollTop = 0; proxy.$refs.layoutDefaultsScrollbarRef.wrap$.scrollTop = 0;
} }
); );
return { return {

View File

@@ -1,20 +1,16 @@
<template> <template>
<div class="layout-navbars-breadcrumb" v-show="getThemeConfig.isBreadcrumb"> <div class="layout-navbars-breadcrumb" v-show="getThemeConfig.isBreadcrumb">
<i <SvgIcon class="layout-navbars-breadcrumb-icon" :name="getThemeConfig.isCollapse ? 'expand' : 'fold'" @click="onThemeConfigChange" />
class="layout-navbars-breadcrumb-icon"
:class="getThemeConfig.isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
@click="onThemeConfigChange"
></i>
<el-breadcrumb class="layout-navbars-breadcrumb-hide"> <el-breadcrumb class="layout-navbars-breadcrumb-hide">
<transition-group name="breadcrumb" mode="out-in"> <transition-group name="breadcrumb" mode="out-in">
<el-breadcrumb-item v-for="(v, k) in breadcrumbList" :key="v.meta.title"> <el-breadcrumb-item v-for="(v, k) in breadcrumbList" :key="v.meta.title">
<span v-if="k === breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span"> <span v-if="k === breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
<i :class="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="getThemeConfig.isBreadcrumbIcon"></i <SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="getThemeConfig.isBreadcrumbIcon" />
>{{ v.meta.title }} {{ v.meta.title }}
</span> </span>
<a v-else @click.prevent="onBreadcrumbClick(v)"> <a v-else @click.prevent="onBreadcrumbClick(v)">
<i :class="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="getThemeConfig.isBreadcrumbIcon"></i <SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="getThemeConfig.isBreadcrumbIcon" />
>{{ v.meta.title }} {{ v.meta.title }}
</a> </a>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>

View File

@@ -10,8 +10,13 @@
@select="onHandleSelect" @select="onHandleSelect"
@blur="onSearchBlur" @blur="onSearchBlur"
> >
<template #prefix>
<el-icon class="el-input__icon">
<search />
</el-icon>
</template>
<template #default="{ item }"> <template #default="{ item }">
<div><i :class="item.meta.icon" class="mr10"></i>{{ item.meta.title }}</div> <div><SvgIcon :name="item.meta.icon" class="mr5" />{{ item.meta.title }}</div>
</template> </template>
</el-autocomplete> </el-autocomplete>
</el-dialog> </el-dialog>

View File

@@ -2,22 +2,27 @@
<div class="layout-navbars-breadcrumb-user" :style="{ flex: layoutUserFlexNum }"> <div class="layout-navbars-breadcrumb-user" :style="{ flex: layoutUserFlexNum }">
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange"> <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
<div class="layout-navbars-breadcrumb-user-icon"> <div class="layout-navbars-breadcrumb-user-icon">
<i class="el-icon-plus" title="组件大小"></i> <el-icon title="组件大小">
<plus />
</el-icon>
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item command="" :disabled="disabledSize === ''">默认</el-dropdown-item> <el-dropdown-item command="" :disabled="disabledSize === ''">默认</el-dropdown-item>
<el-dropdown-item command="medium" :disabled="disabledSize === 'medium'">中等</el-dropdown-item> <el-dropdown-item command="large" :disabled="disabledSize === 'large'">大型</el-dropdown-item>
<el-dropdown-item command="small" :disabled="disabledSize === 'small'">小型</el-dropdown-item> <el-dropdown-item command="small" :disabled="disabledSize === 'small'">小型</el-dropdown-item>
<el-dropdown-item command="mini" :disabled="disabledSize === 'mini'">超小</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick"> <div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
<i class="el-icon-search" title="菜单搜索"></i> <el-icon title="菜单搜索">
<search />
</el-icon>
</div> </div>
<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick"> <div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
<i class="el-icon-setting" title="布局设置"></i> <el-icon title="布局设置">
<setting />
</el-icon>
</div> </div>
<div class="layout-navbars-breadcrumb-user-icon"> <div class="layout-navbars-breadcrumb-user-icon">
<el-popover <el-popover
@@ -29,7 +34,9 @@
> >
<template #reference> <template #reference>
<el-badge :is-dot="true" @click="isShowUserNewsPopover = !isShowUserNewsPopover"> <el-badge :is-dot="true" @click="isShowUserNewsPopover = !isShowUserNewsPopover">
<i class="el-icon-bell" title="消息"></i> <el-icon title="消息">
<bell />
</el-icon>
</el-badge> </el-badge>
</template> </template>
<transition name="el-zoom-in-top"> <transition name="el-zoom-in-top">
@@ -38,7 +45,12 @@
</el-popover> </el-popover>
</div> </div>
<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick"> <div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick">
<i class="iconfont" :title="isScreenfull ? '开全屏' : '关全屏'" :class="!isScreenfull ? 'el-icon-full-screen' : 'el-icon-crop'"></i> <el-icon v-if="!isScreenfull" title="关全屏">
<full-screen />
</el-icon>
<el-icon v-else title="开全屏">
<crop />
</el-icon>
</div> </div>
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick"> <el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
<span class="layout-navbars-breadcrumb-user-link" style="cursor: pointer"> <span class="layout-navbars-breadcrumb-user-link" style="cursor: pointer">
@@ -54,7 +66,7 @@
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<Search ref="searchRef" /> <SearchMenu ref="searchRef" />
</div> </div>
</template> </template>
@@ -70,7 +82,7 @@ import UserNews from '@/views/layout/navBars/breadcrumb/userNews.vue';
import Search from '@/views/layout/navBars/breadcrumb/search.vue'; import Search from '@/views/layout/navBars/breadcrumb/search.vue';
export default { export default {
name: 'layoutBreadcrumbUser', name: 'layoutBreadcrumbUser',
components: { UserNews, Search }, components: { UserNews, SearchMenu: Search },
setup() { setup() {
const { proxy } = getCurrentInstance() as any; const { proxy } = getCurrentInstance() as any;
const router = useRouter(); const router = useRouter();
@@ -159,7 +171,7 @@ export default {
removeLocal('themeConfig'); removeLocal('themeConfig');
getThemeConfig.value.globalComponentSize = size; getThemeConfig.value.globalComponentSize = size;
setLocal('themeConfig', getThemeConfig.value); setLocal('themeConfig', getThemeConfig.value);
proxy.$ELEMENT.size = size; // proxy.$ELEMENT.size = size;
initComponentSize(); initComponentSize();
window.location.reload(); window.location.reload();
}; };
@@ -169,14 +181,14 @@ export default {
case '': case '':
state.disabledSize = ''; state.disabledSize = '';
break; break;
case 'medium': case 'default':
state.disabledSize = 'medium'; state.disabledSize = 'default';
break; break;
case 'small': case 'small':
state.disabledSize = 'small'; state.disabledSize = 'small';
break; break;
case 'mini': case 'large':
state.disabledSize = 'mini'; state.disabledSize = 'large';
break; break;
} }
}; };

View File

@@ -17,21 +17,31 @@
" "
> >
<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont font14" v-if="isActive(v)"></i> <i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont font14" v-if="isActive(v)"></i>
<i class="layout-navbars-tagsview-ul-li-iconfont" :class="v.meta.icon" v-if="!isActive(v) && getThemeConfig.isTagsviewIcon"></i> <SvgIcon
:name="v.meta.icon"
class="layout-navbars-tagsview-ul-li-iconfont"
v-if="!isActive(v) && getThemeConfig.isTagsviewIcon"
/>
<span>{{ v.meta.title }}</span> <span>{{ v.meta.title }}</span>
<template v-if="isActive(v)"> <template v-if="isActive(v)">
<i class="el-icon-refresh-right ml5" @click.stop="refreshCurrentTagsView(v.fullPath)"></i> <SvgIcon
<i name="RefreshRight"
class="el-icon-close layout-navbars-tagsview-ul-li-icon layout-icon-active" class="ml5 layout-navbars-tagsview-ul-li-refresh"
@click.stop="refreshCurrentTagsView($route.fullPath)"
/>
<SvgIcon
name="Close"
class="layout-navbars-tagsview-ul-li-icon layout-icon-active"
v-if="!v.meta.isAffix" v-if="!v.meta.isAffix"
@click.stop="closeCurrentTagsView(v.fullPath)" @click.stop="closeCurrentTagsView(getThemeConfig.isShareTagsView ? v.path : v.url)"
></i> />
</template> </template>
<i <SvgIcon
class="el-icon-close layout-navbars-tagsview-ul-li-icon layout-icon-three" name="Close"
class="layout-navbars-tagsview-ul-li-icon layout-icon-three"
v-if="!v.meta.isAffix" v-if="!v.meta.isAffix"
@click.stop="closeCurrentTagsView(v.fullPath)" @click.stop="closeCurrentTagsView(getThemeConfig.isShareTagsView ? v.path : v.url)"
></i> />
</li> </li>
</ul> </ul>
</el-scrollbar> </el-scrollbar>
@@ -240,7 +250,7 @@ export default {
// 最后 li // 最后 li
let liLast: any = tagsRefs.value[tagsRefs.value.length - 1]; let liLast: any = tagsRefs.value[tagsRefs.value.length - 1];
// 当前滚动条的值 // 当前滚动条的值
let scrollRefs = proxy.$refs.scrollbarRef.$refs.wrap; let scrollRefs = proxy.$refs.scrollbarRef.$refs.wrap$;
// 当前滚动条滚动宽度 // 当前滚动条滚动宽度
let scrollS = scrollRefs.scrollWidth; let scrollS = scrollRefs.scrollWidth;
// 当前滚动条偏移宽度 // 当前滚动条偏移宽度

View File

@@ -1,20 +1,20 @@
<template> <template>
<template v-for="val in chils"> <template v-for="val in chils">
<el-submenu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0"> <el-sub-menu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0">
<template #title> <template #title>
<i :class="val.meta.icon"></i> <SvgIcon :name="val.meta.icon" />
<span>{{ val.meta.title }}</span> <span>{{ val.meta.title }}</span>
</template> </template>
<sub-item :chil="val.children" /> <sub-item :chil="val.children" />
</el-submenu> </el-sub-menu>
<el-menu-item :index="val.path" :key="val.path" v-else> <el-menu-item :index="val.path" :key="val.path" v-else>
<template v-if="!val.meta.link || (val.meta.link && val.meta.isIframe)"> <template v-if="!val.meta.link || (val.meta.link && val.meta.isIframe)">
<i :class="val.meta.icon ? val.meta.icon : ''"></i> <SvgIcon :name="val.meta.icon" />
<span>{{ val.meta.title }}</span> <span>{{ val.meta.title }}</span>
</template> </template>
<template v-else> <template v-else>
<a :href="val.meta.link" target="_blank"> <a :href="val.meta.link" target="_blank">
<i :class="val.meta.icon ? val.meta.icon : ''"></i> <SvgIcon :name="val.meta.icon" />
{{ val.meta.title }} {{ val.meta.title }}
</a> </a>
</template> </template>

View File

@@ -8,15 +8,15 @@
:collapse-transition="false" :collapse-transition="false"
> >
<template v-for="val in menuLists"> <template v-for="val in menuLists">
<el-submenu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path"> <el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
<template #title> <template #title>
<i :class="val.meta.icon ? val.meta.icon : ''"></i> <SvgIcon :name="val.meta.icon" />
<span>{{ val.meta.title }}</span> <span>{{ val.meta.title }}</span>
</template> </template>
<SubItem :chil="val.children" /> <SubItem :chil="val.children" />
</el-submenu> </el-sub-menu>
<el-menu-item :index="val.path" :key="val.path" v-else> <el-menu-item :index="val.path" :key="val.path" v-else>
<i :class="val.meta.icon ? val.meta.icon : ''"></i> <SvgIcon :name="val.meta.icon" />
<template #title v-if="!val.meta.link || (val.meta.link && val.meta.isIframe)"> <template #title v-if="!val.meta.link || (val.meta.link && val.meta.isIframe)">
<span>{{ val.meta.title }}</span> <span>{{ val.meta.title }}</span>
</template> </template>

View File

@@ -1,14 +1,14 @@
<template> <template>
<el-form ref="loginFormRef" :model="loginForm" :rules="rules" class="login-content-form"> <el-form ref="loginFormRef" :model="loginForm" :rules="rules" class="login-content-form" size="large">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input type="text" placeholder="请输入用户名" prefix-icon="el-icon-user" v-model="loginForm.username" clearable autocomplete="off"> <el-input type="text" placeholder="请输入用户名" prefix-icon="user" v-model="loginForm.username" clearable autocomplete="off">
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
type="password" type="password"
placeholder="请输入密码" placeholder="请输入密码"
prefix-icon="el-icon-lock" prefix-icon="lock"
v-model="loginForm.password" v-model="loginForm.password"
autocomplete="off" autocomplete="off"
show-password show-password
@@ -22,7 +22,7 @@
type="text" type="text"
maxlength="6" maxlength="6"
placeholder="请输入验证码" placeholder="请输入验证码"
prefix-icon="el-icon-position" prefix-icon="position"
v-model="loginForm.captcha" v-model="loginForm.captcha"
clearable clearable
autocomplete="off" autocomplete="off"

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<el-form class="search-form" label-position="right" :inline="true" label-width="60px" size="small"> <el-form class="search-form" label-position="right" :inline="true" label-width="60px">
<el-form-item prop="project" label="项目" label-width="40px"> <el-form-item prop="project" label="项目" label-width="40px">
<el-select v-model="projectId" placeholder="请选择项目" @change="changeProject" filterable> <el-select v-model="projectId" placeholder="请选择项目" @change="changeProject" filterable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"></el-option> <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"></el-option>

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<el-dialog :title="title" v-model="dialogVisible" :show-close="false" :before-close="cancel" width="35%"> <el-dialog :title="title" v-model="dialogVisible" :show-close="false" :before-close="cancel" width="35%">
<el-form :model="form" ref="dbForm" :rules="rules" label-width="85px" size="small"> <el-form :model="form" ref="dbForm" :rules="rules" label-width="85px">
<el-form-item prop="projectId" label="项目:" required> <el-form-item prop="projectId" label="项目:" required>
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable> <el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option> <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
@@ -46,8 +46,8 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk" size="mini"> </el-button> <el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()" size="mini"> </el-button> <el-button @click="cancel()"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -198,7 +198,7 @@ export default defineComponent({
const btnOk = async () => { const btnOk = async () => {
dbForm.value.validate((valid: boolean) => { dbForm.value.validate((valid: boolean) => {
if (valid) { if (valid) {
state.form.port = Number.parseInt(state.form.port as any) state.form.port = Number.parseInt(state.form.port as any);
dbApi.saveDb.request(state.form).then(() => { dbApi.saveDb.request(state.form).then(() => {
ElMessage.success('保存成功'); ElMessage.success('保存成功');
emit('val-change', state.form); emit('val-change', state.form);

View File

@@ -1,21 +1,13 @@
<template> <template>
<div class="db-list"> <div class="db-list">
<el-card> <el-card>
<el-button v-auth="permissions.saveDb" type="primary" icon="el-icon-plus" size="mini" @click="editDb(true)">添加</el-button> <el-button v-auth="permissions.saveDb" type="primary" icon="plus" @click="editDb(true)">添加</el-button>
<el-button v-auth="permissions.saveDb" :disabled="chooseId == null" @click="editDb(false)" type="primary" icon="el-icon-edit" size="mini" <el-button v-auth="permissions.saveDb" :disabled="chooseId == null" @click="editDb(false)" type="primary" icon="edit">编辑</el-button>
>编辑</el-button <el-button v-auth="permissions.delDb" :disabled="chooseId == null" @click="deleteDb(chooseId)" type="danger" icon="delete"
>
<el-button
v-auth="permissions.delDb"
:disabled="chooseId == null"
@click="deleteDb(chooseId)"
type="danger"
icon="el-icon-delete"
size="mini"
>删除</el-button >删除</el-button
> >
<div style="float: right"> <div style="float: right">
<el-form class="search-form" label-position="right" :inline="true" label-width="60px" size="small"> <el-form class="search-form" label-position="right" :inline="true" label-width="60px">
<el-form-item prop="project"> <el-form-item prop="project">
<el-select v-model="query.projectId" placeholder="请选择项目" filterable clearable> <el-select v-model="query.projectId" placeholder="请选择项目" filterable clearable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option> <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
@@ -25,11 +17,13 @@
<el-form-item> <el-form-item>
<el-input v-model="query.database" placeholder="请输入数据库" auto-complete="off" clearable></el-input> <el-input v-model="query.database" placeholder="请输入数据库" auto-complete="off" clearable></el-input>
</el-form-item> </el-form-item>
<el-button v-waves type="primary" icon="el-icon-search" size="mini" @click="search()">查询</el-button> <el-form-item>
<el-button v-waves type="primary" icon="search" @click="search()">查询</el-button>
</el-form-item>
</el-form> </el-form>
</div> </div>
<el-table :data="datas" ref="table" @current-change="choose" show-overflow-tooltip> <el-table :data="datas" ref="table" @current-change="choose" show-overflow-tooltip>
<el-table-column label="选择" width="50px"> <el-table-column label="选择" width="60px">
<template #default="scope"> <template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id"> <el-radio v-model="chooseId" :label="scope.row.id">
<i></i> <i></i>
@@ -121,7 +115,7 @@
</el-dialog> </el-dialog>
<el-dialog width="40%" :title="`${chooseTableName} 字段信息`" v-model="columnDialog.visible"> <el-dialog width="40%" :title="`${chooseTableName} 字段信息`" v-model="columnDialog.visible">
<el-table border :data="columnDialog.columns" size="mini"> <el-table border :data="columnDialog.columns">
<el-table-column prop="columnName" label="名称" show-overflow-tooltip> </el-table-column> <el-table-column prop="columnName" label="名称" show-overflow-tooltip> </el-table-column>
<el-table-column prop="columnComment" label="备注" show-overflow-tooltip> </el-table-column> <el-table-column prop="columnComment" label="备注" show-overflow-tooltip> </el-table-column>
<el-table-column width="120" prop="columnType" label="类型" show-overflow-tooltip> </el-table-column> <el-table-column width="120" prop="columnType" label="类型" show-overflow-tooltip> </el-table-column>
@@ -129,7 +123,7 @@
</el-dialog> </el-dialog>
<el-dialog width="40%" :title="`${chooseTableName} 索引信息`" v-model="indexDialog.visible"> <el-dialog width="40%" :title="`${chooseTableName} 索引信息`" v-model="indexDialog.visible">
<el-table border :data="indexDialog.indexs" size="mini"> <el-table border :data="indexDialog.indexs">
<el-table-column prop="indexName" label="索引名" show-overflow-tooltip> </el-table-column> <el-table-column prop="indexName" label="索引名" show-overflow-tooltip> </el-table-column>
<el-table-column prop="columnName" label="列名" show-overflow-tooltip> </el-table-column> <el-table-column prop="columnName" label="列名" show-overflow-tooltip> </el-table-column>
<el-table-column prop="seqInIndex" label="列序列号" show-overflow-tooltip> </el-table-column> <el-table-column prop="seqInIndex" label="列序列号" show-overflow-tooltip> </el-table-column>

View File

@@ -25,9 +25,9 @@
<el-aside id="sqlcontent" width="65%" style="background-color: rgb(238, 241, 246)"> <el-aside id="sqlcontent" width="65%" style="background-color: rgb(238, 241, 246)">
<div class="toolbar"> <div class="toolbar">
<div class="fl"> <div class="fl">
<!-- <el-button v-waves @click="runSql" type="success" icon="el-icon-video-play" size="mini" plain>执行</el-button> <!-- <el-button v-waves @click="runSql" type="success" icon="video-play" plain>执行</el-button>
<el-button v-waves @click="formatSql" type="primary" icon="el-icon-magic-stick" size="mini" plain>格式化</el-button> --> <el-button v-waves @click="formatSql" type="primary" icon="magic-stick" plain>格式化</el-button> -->
<el-upload <el-upload
style="display: inline-block; margin-left: 10px" style="display: inline-block; margin-left: 10px"
@@ -43,7 +43,7 @@
multiple multiple
:limit="100" :limit="100"
> >
<el-button v-waves type="success" icon="el-icon-video-play" size="mini" plain>sql脚本执行</el-button> <el-button v-waves type="success" icon="video-play" plain>sql脚本执行</el-button>
</el-upload> </el-upload>
</div> </div>
@@ -55,7 +55,6 @@
filterable filterable
allow-create allow-create
default-first-option default-first-option
size="mini"
class="mr10" class="mr10"
> >
<el-option v-for="item in sqlNames" :key="item" :label="item.database" :value="item"> <el-option v-for="item in sqlNames" :key="item" :label="item.database" :value="item">
@@ -63,8 +62,8 @@
</el-option> </el-option>
</el-select> </el-select>
<el-button v-waves @click="saveSql" type="primary" icon="el-icon-document-add" size="mini" plain>保存</el-button> <el-button v-waves @click="saveSql" type="primary" icon="document-add" plain>保存</el-button>
<el-button v-waves @click="deleteSql" type="danger" icon="el-icon-delete" size="mini" plain>删除</el-button> <el-button v-waves @click="deleteSql" type="danger" icon="delete" plain>删除</el-button>
</div> </div>
</div> </div>
<codemirror <codemirror
@@ -77,13 +76,13 @@
:options="cmOptions" :options="cmOptions"
/> />
<el-button-group :style="btnStyle"> <el-button-group :style="btnStyle">
<el-button v-waves @click="runSql" type="success" icon="el-icon-video-play" size="small" plain>执行</el-button> <el-button v-waves @click="runSql" type="success" icon="video-play" size="small" plain>执行</el-button>
<el-button v-waves @click="formatSql" type="primary" icon="el-icon-magic-stick" size="small" plain>格式化</el-button> <el-button v-waves @click="formatSql" type="primary" icon="magic-stick" size="small" plain>格式化</el-button>
</el-button-group> </el-button-group>
</el-aside> </el-aside>
<el-container style="margin-left: 2px"> <el-container style="margin-left: 2px">
<el-header style="text-align: left; height: 45px; font-size: 12px; padding: 0px"> <el-header style="text-align: left; height: 35px; font-size: 12px; padding: 0px">
<el-select v-model="tableName" placeholder="请选择表" @change="changeTable" filterable style="width: 99%"> <el-select v-model="tableName" placeholder="请选择表" @change="changeTable" filterable style="width: 99%">
<el-option <el-option
v-for="item in tableMetadata" v-for="item in tableMetadata"
@@ -96,7 +95,7 @@
</el-header> </el-header>
<el-main style="padding: 0px; overflow: hidden"> <el-main style="padding: 0px; overflow: hidden">
<el-table :data="columnMetadata" height="100%" size="mini"> <el-table :data="columnMetadata" height="100%">
<el-table-column prop="columnName" label="名称" show-overflow-tooltip> </el-table-column> <el-table-column prop="columnName" label="名称" show-overflow-tooltip> </el-table-column>
<el-table-column prop="columnComment" label="备注" show-overflow-tooltip> </el-table-column> <el-table-column prop="columnComment" label="备注" show-overflow-tooltip> </el-table-column>
<el-table-column width="120" prop="columnType" label="类型" show-overflow-tooltip> </el-table-column> <el-table-column width="120" prop="columnType" label="类型" show-overflow-tooltip> </el-table-column>
@@ -105,7 +104,7 @@
</el-container> </el-container>
</el-container> </el-container>
<el-table style="margin-top: 1px" :data="execRes.data" size="mini" max-height="300" :empty-text="execRes.emptyResText" stripe border> <el-table style="margin-top: 1px" :data="execRes.data" max-height="300" :empty-text="execRes.emptyResText" stripe border size="small">
<el-table-column <el-table-column
min-width="100" min-width="100"
:width="flexColumnWidth(item, execRes.data)" :width="flexColumnWidth(item, execRes.data)"
@@ -514,8 +513,8 @@ export default defineComponent({
if (temp) { if (temp) {
state.btnStyle.display = 'block'; state.btnStyle.display = 'block';
if (!state.btnStyle.left) { if (!state.btnStyle.left) {
state.btnStyle.left = e.target.getBoundingClientRect().left - 100 + 'px'; state.btnStyle.left = e.target.getBoundingClientRect().left - 150 + 'px';
state.btnStyle.top = e.target.getBoundingClientRect().top - 20 + 'px'; state.btnStyle.top = e.target.getBoundingClientRect().top - 60 + 'px';
} }
} else { } else {
state.btnStyle.display = 'none'; state.btnStyle.display = 'none';
@@ -553,6 +552,7 @@ export default defineComponent({
<style scoped lang="scss"> <style scoped lang="scss">
.codesql { .codesql {
font-size: 10pt; font-size: 10pt;
font-weight: 600;
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif; font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
} }
#sqlcontent { #sqlcontent {

View File

@@ -3,7 +3,7 @@
<el-dialog :title="title" v-model="dialogVisible" :show-close="true" :before-close="handleClose" width="800px"> <el-dialog :title="title" v-model="dialogVisible" :show-close="true" :before-close="handleClose" width="800px">
<div class="toolbar"> <div class="toolbar">
<div style="float: right"> <div style="float: right">
<el-button v-auth="'machine:file:add'" type="primary" @click="add" icon="el-icon-plus" size="mini" plain>添加</el-button> <el-button v-auth="'machine:file:add'" type="primary" @click="add" icon="plus" size="small" plain>添加</el-button>
</div> </div>
</div> </div>
<!-- <div style="float: right;"> <!-- <div style="float: right;">
@@ -12,35 +12,35 @@
<el-table :data="fileTable" stripe style="width: 100%"> <el-table :data="fileTable" stripe style="width: 100%">
<el-table-column prop="name" label="名称" width> <el-table-column prop="name" label="名称" width>
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.name" size="mini" :disabled="scope.row.id != null" clearable></el-input> <el-input v-model="scope.row.name" size="small" :disabled="scope.row.id != null" clearable></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="name" label="类型" min-width="50px"> <el-table-column prop="name" label="类型" min-width="50px">
<template #default="scope"> <template #default="scope">
<el-select :disabled="scope.row.id != null" size="mini" v-model="scope.row.type" style="width: 100px" placeholder="请选择"> <el-select :disabled="scope.row.id != null" size="small" v-model="scope.row.type" style="width: 100px" placeholder="请选择">
<el-option v-for="item in enums.FileTypeEnum" :key="item.value" :label="item.label" :value="item.value"></el-option> <el-option v-for="item in enums.FileTypeEnum" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="path" label="路径" width> <el-table-column prop="path" label="路径" width>
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.path" :disabled="scope.row.id != null" size="mini" clearable></el-input> <el-input v-model="scope.row.path" :disabled="scope.row.id != null" size="small" clearable></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width> <el-table-column label="操作" width>
<template #default="scope"> <template #default="scope">
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="el-icon-success" size="mini" plain <el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="success" size="small" plain
>确定</el-button >确定</el-button
> >
<el-button v-if="scope.row.id != null" @click="getConf(scope.row)" type="primary" icon="el-icon-tickets" size="mini" plain <el-button v-if="scope.row.id != null" @click="getConf(scope.row)" type="primary" icon="tickets" size="small" plain
>查看</el-button >查看</el-button
> >
<el-button <el-button
v-auth="'machine:file:del'" v-auth="'machine:file:del'"
type="danger" type="danger"
@click="deleteRow(scope.$index, scope.row)" @click="deleteRow(scope.$index, scope.row)"
icon="el-icon-delete" icon="delete"
size="mini" size="small"
plain plain
>删除</el-button >删除</el-button
> >
@@ -57,13 +57,13 @@
<el-dropdown size="small" trigger="contextmenu"> <el-dropdown size="small" trigger="contextmenu">
<span class="el-dropdown-link"> <span class="el-dropdown-link">
<span v-if="data.type == 'd' && !node.expanded"> <span v-if="data.type == 'd' && !node.expanded">
<i class="el-icon-folder"></i> <SvgIcon name="folder"/>
</span> </span>
<span v-if="data.type == 'd' && node.expanded"> <span v-if="data.type == 'd' && node.expanded">
<i class="el-icon-folder-opened"></i> <SvgIcon name="folder-opened"/>
</span> </span>
<span v-if="data.type == '-'"> <span v-if="data.type == '-'">
<i class="el-icon-document"></i> <SvgIcon name="document"/>
</span> </span>
<span style="display: inline-block"> <span style="display: inline-block">
@@ -79,7 +79,7 @@
@click.prevent="getFileContent(tree.folder.id, data.path)" @click.prevent="getFileContent(tree.folder.id, data.path)"
v-if="data.type == '-' && data.size < 1 * 1024 * 1024" v-if="data.type == '-' && data.size < 1 * 1024 * 1024"
type="info" type="info"
icon="el-icon-view" icon="view"
:underline="false" :underline="false"
> >
查看 查看
@@ -102,7 +102,7 @@
:limit="100" :limit="100"
style="display: inline-block; margin-left: 2px" style="display: inline-block; margin-left: 2px"
> >
<el-link v-auth="'machine:file:upload'" @click.prevent icon="el-icon-upload" :underline="false"> <el-link v-auth="'machine:file:upload'" @click.prevent icon="upload" :underline="false">
上传 上传
</el-link> </el-link>
</el-upload> </el-upload>
@@ -113,7 +113,7 @@
v-if="data.type == '-'" v-if="data.type == '-'"
@click.prevent="downloadFile(node, data)" @click.prevent="downloadFile(node, data)"
type="primary" type="primary"
icon="el-icon-download" icon="download"
:underline="false" :underline="false"
style="margin-left: 2px" style="margin-left: 2px"
>下载</el-link >下载</el-link
@@ -125,7 +125,7 @@
v-if="!dontOperate(data)" v-if="!dontOperate(data)"
@click.prevent="deleteFile(node, data)" @click.prevent="deleteFile(node, data)"
type="danger" type="danger"
icon="el-icon-delete" icon="delete"
:underline="false" :underline="false"
style="margin-left: 2px" style="margin-left: 2px"
>删除 >删除
@@ -154,8 +154,8 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button v-auth="'machine:file:write'" type="primary" @click="updateContent" size="mini"> </el-button> <el-button v-auth="'machine:file:write'" type="primary" @click="updateContent"> </el-button>
<el-button @click="fileContent.contentVisible = false" size="mini"> </el-button> <el-button @click="fileContent.contentVisible = false"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -171,6 +171,7 @@ import { codemirror } from '@/components/codemirror';
import { getSession } from '@/common/utils/storage'; import { getSession } from '@/common/utils/storage';
import enums from './enums'; import enums from './enums';
import config from '@/common/config'; import config from '@/common/config';
import SvgIcon from '@/components/svgIcon/index.vue';
export default defineComponent({ export default defineComponent({
name: 'FileManage', name: 'FileManage',

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<el-dialog :title="title" v-model="dialogVisible" :show-close="false" :before-close="cancel" width="35%"> <el-dialog :title="title" v-model="dialogVisible" :show-close="false" :before-close="cancel" width="35%">
<el-form :model="form" ref="machineForm" :rules="rules" label-width="85px" size="small"> <el-form :model="form" ref="machineForm" :rules="rules" label-width="85px" >
<el-form-item prop="projectId" label="项目:" required> <el-form-item prop="projectId" label="项目:" required>
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable> <el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option> <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>

View File

@@ -2,51 +2,28 @@
<div> <div>
<el-card> <el-card>
<div> <div>
<el-button v-auth="'machine:add'" type="primary" icon="el-icon-plus" size="mini" @click="openFormDialog(false)" plain>添加</el-button> <el-button v-auth="'machine:add'" type="primary" icon="plus" @click="openFormDialog(false)" plain>添加</el-button>
<el-button <el-button
v-auth="'machine:update'" v-auth="'machine:update'"
type="primary" type="primary"
icon="el-icon-edit" icon="edit"
size="mini"
:disabled="currentId == null" :disabled="currentId == null"
@click="openFormDialog(currentData)" @click="openFormDialog(currentData)"
plain plain
>编辑</el-button >编辑</el-button
> >
<el-button <el-button v-auth="'machine:del'" :disabled="currentId == null" @click="deleteMachine(currentId)" type="danger" icon="delete"
v-auth="'machine:del'"
:disabled="currentId == null"
@click="deleteMachine(currentId)"
type="danger"
icon="el-icon-delete"
size="mini"
>删除</el-button >删除</el-button
> >
<el-button <el-button v-auth="'machine:file'" type="success" icon="files" :disabled="currentId == null" @click="fileManage(currentData)" plain
v-auth="'machine:file'"
type="success"
icon="el-icon-files"
:disabled="currentId == null"
@click="fileManage(currentData)"
size="mini"
plain
>文件</el-button >文件</el-button
> >
<div style="float: right"> <div style="float: right">
<el-select size="small" v-model="params.projectId" placeholder="请选择项目" @clear="search" filterable clearable> <el-select v-model="params.projectId" placeholder="请选择项目" @clear="search" filterable clearable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option> <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
</el-select> </el-select>
<el-input <el-input class="ml5" placeholder="请输入ip" style="width: 200px" v-model="params.ip" @clear="search" plain clearable></el-input>
class="ml5" <el-button class="ml5" @click="search" type="success" icon="search"></el-button>
placeholder="请输入ip"
size="small"
style="width: 300px"
v-model="params.ip"
@clear="search"
plain
clearable
></el-input>
<el-button class="ml5" @click="search" type="success" icon="el-icon-search" size="small"></el-button>
</div> </div>
</div> </div>
@@ -85,9 +62,9 @@
<el-table-column prop="modifier" label="修改者" :min-width="55"></el-table-column> --> <el-table-column prop="modifier" label="修改者" :min-width="55"></el-table-column> -->
<el-table-column label="操作" min-width="260" fixed="right"> <el-table-column label="操作" min-width="260" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button type="success" @click="serviceManager(scope.row)" size="mini" plain>脚本</el-button> <el-button type="success" @click="serviceManager(scope.row)" plain size="small">脚本</el-button>
<el-button v-auth="'machine:terminal'" type="primary" @click="showTerminal(scope.row)" size="mini" plain>终端</el-button> <el-button v-auth="'machine:terminal'" type="primary" @click="showTerminal(scope.row)" plain size="small">终端</el-button>
<el-button :disabled="!scope.row.hasCli" type="danger" @click="closeCli(scope.row)" size="mini" plain>关闭连接</el-button> <el-button :disabled="!scope.row.hasCli" type="danger" @click="closeCli(scope.row)" plain size="small">关闭连接</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

View File

@@ -9,7 +9,7 @@
:destroy-on-close="true" :destroy-on-close="true"
width="800px" width="800px"
> >
<el-form :model="form" ref="mockDataForm" label-width="70px" size="small"> <el-form :model="form" ref="mockDataForm" label-width="70px">
<el-form-item prop="method" label="名称"> <el-form-item prop="method" label="名称">
<el-input v-model.trim="form.name" placeholder="请输入名称"></el-input> <el-input v-model.trim="form.name" placeholder="请输入名称"></el-input>
</el-form-item> </el-form-item>

View File

@@ -3,16 +3,16 @@
<el-dialog :title="title" v-model="dialogVisible" :destroy-on-close="true" :show-close="true" :before-close="handleClose" width="60%"> <el-dialog :title="title" v-model="dialogVisible" :destroy-on-close="true" :show-close="true" :before-close="handleClose" width="60%">
<div class="toolbar"> <div class="toolbar">
<div style="float: left"> <div style="float: left">
<el-select v-model="type" @change="getScripts" size="mini" placeholder="请选择"> <el-select v-model="type" @change="getScripts" size="small" placeholder="请选择">
<el-option :key="0" label="私有" :value="0"> </el-option> <el-option :key="0" label="私有" :value="0"> </el-option>
<el-option :key="1" label="公共" :value="1"> </el-option> <el-option :key="1" label="公共" :value="1"> </el-option>
</el-select> </el-select>
</div> </div>
<div style="float: right"> <div style="float: right">
<el-button @click="editScript(currentData)" :disabled="currentId == null" type="primary" icon="el-icon-tickets" size="mini" plain <el-button @click="editScript(currentData)" :disabled="currentId == null" type="primary" icon="tickets" size="small" plain
>查看</el-button >查看</el-button
> >
<el-button v-auth="'machine:script:save'" type="primary" @click="editScript(null)" icon="el-icon-plus" size="mini" plain <el-button v-auth="'machine:script:save'" type="primary" @click="editScript(null)" icon="plus" size="small" plain
>添加</el-button >添加</el-button
> >
<el-button <el-button
@@ -20,15 +20,15 @@
:disabled="currentId == null" :disabled="currentId == null"
type="danger" type="danger"
@click="deleteRow(currentData)" @click="deleteRow(currentData)"
icon="el-icon-delete" icon="delete"
size="mini" size="small"
plain plain
>删除</el-button >删除</el-button
> >
</div> </div>
</div> </div>
<el-table :data="scriptTable" @current-change="choose" stripe border size="mini" style="width: 100%"> <el-table :data="scriptTable" @current-change="choose" stripe border size="small" style="width: 100%">
<el-table-column label="选择" width="55px"> <el-table-column label="选择" width="55px">
<template #default="scope"> <template #default="scope">
<el-radio v-model="currentId" :label="scope.row.id"> <el-radio v-model="currentId" :label="scope.row.id">
@@ -45,7 +45,7 @@
</el-table-column> </el-table-column>
<el-table-column label="操作"> <el-table-column label="操作">
<template #default="scope"> <template #default="scope">
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="el-icon-success" size="mini" plain <el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="el-icon-success" size="small" plain
>确定</el-button >确定</el-button
> >
@@ -54,8 +54,8 @@
v-if="scope.row.id != null" v-if="scope.row.id != null"
@click="runScript(scope.row)" @click="runScript(scope.row)"
type="primary" type="primary"
icon="el-icon-tickets" icon="video-play"
size="mini" size="small"
plain plain
>执行</el-button >执行</el-button
> >
@@ -65,14 +65,14 @@
</el-dialog> </el-dialog>
<el-dialog title="脚本参数" v-model="scriptParamsDialog.visible" width="400px"> <el-dialog title="脚本参数" v-model="scriptParamsDialog.visible" width="400px">
<el-form ref="paramsForm" :model="scriptParamsDialog.params" label-width="70px" size="mini"> <el-form ref="paramsForm" :model="scriptParamsDialog.params" label-width="70px" size="small">
<el-form-item v-for="item in scriptParamsDialog.paramsFormItem" :key="item.name" :prop="item.model" :label="item.name" required> <el-form-item v-for="item in scriptParamsDialog.paramsFormItem" :key="item.name" :prop="item.model" :label="item.name" required>
<el-input v-model="scriptParamsDialog.params[item.model]" :placeholder="item.placeholder" autocomplete="off"></el-input> <el-input v-model="scriptParamsDialog.params[item.model]" :placeholder="item.placeholder" autocomplete="off"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button type="primary" @click="hasParamsRun(currentData)" size="mini"> </el-button> <el-button type="primary" @click="hasParamsRun(currentData)" size="small"> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -2,47 +2,26 @@
<div class="project-list"> <div class="project-list">
<el-card> <el-card>
<div> <div>
<el-button @click="showAddProjectDialog" v-auth="permissions.saveProject" type="primary" icon="el-icon-plus" size="mini" <el-button @click="showAddProjectDialog" v-auth="permissions.saveProject" type="primary" icon="plus">添加</el-button>
>添加</el-button
>
<el-button <el-button
@click="showAddProjectDialog(chooseData)" @click="showAddProjectDialog(chooseData)"
v-auth="permissions.saveProject" v-auth="permissions.saveProject"
:disabled="chooseId == null" :disabled="chooseId == null"
type="primary" type="primary"
icon="el-icon-edit" icon="edit"
size="mini"
>编辑</el-button >编辑</el-button
> >
<el-button @click="showMembers(chooseData)" :disabled="chooseId == null" type="success" icon="el-icon-setting" size="mini" <el-button @click="showMembers(chooseData)" :disabled="chooseId == null" type="success" icon="setting">成员管理</el-button>
>成员管理</el-button
>
<el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="el-icon-setting" size="mini" <el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="setting">环境管理</el-button>
>环境管理</el-button
>
<el-button <el-button v-auth="permissions.delProject" @click="delProject" :disabled="chooseId == null" type="danger" icon="delete"
v-auth="permissions.delProject"
@click="delProject"
:disabled="chooseId == null"
type="danger"
icon="el-icon-delete"
size="mini"
>删除</el-button >删除</el-button
> >
<div style="float: right"> <div style="float: right">
<el-input <el-input class="mr2" placeholder="请输入项目名!" style="width: 200px" v-model="query.name" @clear="search" clearable></el-input>
class="mr2" <el-button @click="search" type="success" icon="search"></el-button>
placeholder="请输入项目名!"
size="small"
style="width: 300px"
v-model="query.name"
@clear="search"
clearable
></el-input>
<el-button @click="search" type="success" icon="el-icon-search" size="small"></el-button>
</div> </div>
</div> </div>
<el-table :data="projects" @current-change="choose" ref="table" style="width: 100%"> <el-table :data="projects" @current-change="choose" ref="table" style="width: 100%">
@@ -82,7 +61,7 @@
</el-card> </el-card>
<el-dialog width="400px" title="项目编辑" :before-close="cancelAddProject" v-model="addProjectDialog.visible"> <el-dialog width="400px" title="项目编辑" :before-close="cancelAddProject" v-model="addProjectDialog.visible">
<el-form :model="addProjectDialog.form" size="small" label-width="70px"> <el-form :model="addProjectDialog.form" label-width="70px">
<el-form-item label="项目名:" required> <el-form-item label="项目名:" required>
<el-input :disabled="addProjectDialog.form.id ? true : false" v-model="addProjectDialog.form.name" auto-complete="off"></el-input> <el-input :disabled="addProjectDialog.form.id ? true : false" v-model="addProjectDialog.form.name" auto-complete="off"></el-input>
</el-form-item> </el-form-item>
@@ -92,18 +71,18 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="addProject" type="primary" size="small"> </el-button> <el-button @click="addProject" type="primary"> </el-button>
<el-button @click="cancelAddProject()" size="small"> </el-button> <el-button @click="cancelAddProject()"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog width="500px" :title="showEnvDialog.title" v-model="showEnvDialog.visible"> <el-dialog width="500px" :title="showEnvDialog.title" v-model="showEnvDialog.visible">
<div class="toolbar"> <div class="toolbar">
<el-button @click="showAddEnvDialog" v-auth="permissions.saveMember" type="primary" icon="el-icon-plus" size="mini">添加</el-button> <el-button @click="showAddEnvDialog" v-auth="permissions.saveMember" type="primary" icon="plus">添加</el-button>
<!-- <el-button v-auth="'role:update'" :disabled="chooseId == null" type="danger" icon="el-icon-delete" size="mini">删除</el-button> --> <!-- <el-button v-auth="'role:update'" :disabled="chooseId == null" type="danger" icon="delete">删除</el-button> -->
</div> </div>
<el-table border :data="showEnvDialog.envs" size="small"> <el-table border :data="showEnvDialog.envs">
<el-table-column property="name" label="环境名" width="125"></el-table-column> <el-table-column property="name" label="环境名" width="125"></el-table-column>
<el-table-column property="remark" label="描述" width="125"></el-table-column> <el-table-column property="remark" label="描述" width="125"></el-table-column>
<el-table-column property="createTime" label="创建时间"> <el-table-column property="createTime" label="创建时间">
@@ -114,7 +93,7 @@
</el-table> </el-table>
<el-dialog width="400px" title="添加环境" :before-close="cancelAddEnv" v-model="showEnvDialog.addVisible"> <el-dialog width="400px" title="添加环境" :before-close="cancelAddEnv" v-model="showEnvDialog.addVisible">
<el-form :model="showEnvDialog.envForm" size="small" label-width="70px"> <el-form :model="showEnvDialog.envForm" label-width="70px">
<el-form-item label="环境名:" required> <el-form-item label="环境名:" required>
<el-input v-model="showEnvDialog.envForm.name" auto-complete="off"></el-input> <el-input v-model="showEnvDialog.envForm.name" auto-complete="off"></el-input>
</el-form-item> </el-form-item>
@@ -124,8 +103,8 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button v-auth="permissions.saveEnv" @click="addEnv" type="primary" :loading="btnLoading" size="small"> </el-button> <el-button v-auth="permissions.saveEnv" @click="addEnv" type="primary" :loading="btnLoading"> </el-button>
<el-button @click="cancelAddEnv()" size="small"> </el-button> <el-button @click="cancelAddEnv()"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -133,20 +112,12 @@
<el-dialog width="500px" :title="showMemDialog.title" v-model="showMemDialog.visible"> <el-dialog width="500px" :title="showMemDialog.title" v-model="showMemDialog.visible">
<div class="toolbar"> <div class="toolbar">
<el-button v-auth="permissions.saveMember" @click="showAddMemberDialog()" type="primary" icon="el-icon-plus" size="mini" <el-button v-auth="permissions.saveMember" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button>
>添加</el-button <el-button v-auth="permissions.delMember" @click="deleteMember" :disabled="showMemDialog.chooseId == null" type="danger" icon="delete"
>
<el-button
v-auth="permissions.delMember"
@click="deleteMember"
:disabled="showMemDialog.chooseId == null"
type="danger"
icon="el-icon-delete"
size="mini"
>移除</el-button >移除</el-button
> >
</div> </div>
<el-table @current-change="chooseMember" border :data="showMemDialog.members.list" size="small"> <el-table @current-change="chooseMember" border :data="showMemDialog.members.list">
<el-table-column label="选择" width="50px"> <el-table-column label="选择" width="50px">
<template #default="scope"> <template #default="scope">
<el-radio v-model="showMemDialog.chooseId" :label="scope.row.id"> <el-radio v-model="showMemDialog.chooseId" :label="scope.row.id">
@@ -173,7 +144,7 @@
/> />
<el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible"> <el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
<el-form :model="showMemDialog.memForm" size="small" label-width="70px"> <el-form :model="showMemDialog.memForm" label-width="70px">
<el-form-item label="账号:"> <el-form-item label="账号:">
<el-select <el-select
style="width: 100%" style="width: 100%"
@@ -192,10 +163,8 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button v-auth="permissions.saveMember" @click="addMember" type="primary" :loading="btnLoading" size="small" <el-button v-auth="permissions.saveMember" @click="addMember" type="primary" :loading="btnLoading"> </el-button>
> </el-button <el-button @click="cancelAddMember()"> </el-button>
>
<el-button @click="cancelAddMember()" size="small"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -0,0 +1,271 @@
<template>
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="750px" :destroy-on-close="true">
<el-form label-width="85px">
<el-form-item prop="key" label="key:">
<el-input :disabled="operationType == 2" v-model="keyInfo.key"></el-input>
</el-form-item>
<el-form-item prop="timed" label="过期时间:">
<el-input v-model.number="keyInfo.timed" type="number"></el-input>
</el-form-item>
<el-form-item prop="dataType" label="数据类型:">
<el-select :disabled="operationType == 2" style="width: 100%" v-model="keyInfo.type" placeholder="请选择数据类型">
<el-option key="string" label="string" value="string"> </el-option>
<el-option key="hash" label="hash" value="hash"> </el-option>
<el-option key="set" label="set" value="set"> </el-option>
</el-select>
</el-form-item>
<el-form-item v-if="keyInfo.type == 'string'" prop="value" label="内容:">
<el-input class="json-text" v-model="stringValue" type="textarea" :autosize="{ minRows: 10, maxRows: 20 }"></el-input>
</el-form-item>
<span v-if="keyInfo.type == 'hash'">
<el-button @click="onAddHashValue" icon="plus" size="small" plain class="mt10">添加</el-button>
<el-table :data="hashValue" stripe style="width: 100%">
<el-table-column prop="key" label="key" width>
<template #default="scope">
<el-input v-model="scope.row.key" clearable size="small"></el-input>
</template>
</el-table-column>
<el-table-column prop="value" label="value" min-width="200">
<template #default="scope">
<el-input
v-model="scope.row.value"
clearable
type="textarea"
:autosize="{ minRows: 2, maxRows: 10 }"
size="small"
></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="90">
<template #default="scope">
<el-button type="danger" @click="hashValue.splice(scope.$index, 1)" icon="delete" size="small" plain>删除</el-button>
</template>
</el-table-column>
</el-table>
</span>
<span v-if="keyInfo.type == 'set'">
<el-button @click="onAddSetValue" icon="plus" size="small" plain class="mt10">添加</el-button>
<el-table :data="setValue" stripe style="width: 100%">
<el-table-column prop="value" label="value" width>
<template #default="scope" min-width="200">
<el-input
v-model="scope.row.value"
clearable
type="textarea"
:autosize="{ minRows: 2, maxRows: 10 }"
size="small"
></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="90">
<template #default="scope">
<el-button type="danger" @click="setValue.splice(scope.$index, 1)" icon="delete" size="small" plain>删除</el-button>
</template>
</el-table-column>
</el-table>
</span>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="saveValue" type="primary" v-auth="'redis:data:save'"> </el-button>
<el-button @click="cancel()"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, reactive, watch, toRefs } from 'vue';
import { redisApi } from './api';
import { ElMessage } from 'element-plus';
import { isTrue, notEmpty } from '@/common/assert';
export default defineComponent({
name: 'DateEdit',
components: {},
props: {
visible: {
type: Boolean,
},
title: {
type: String,
},
redisId: {
type: [Number],
require: true,
},
keyInfo: {
type: [Object],
},
// 操作类型1新增2修改
operationType: {
type: [Number],
},
stringValue: {
type: [String],
},
setValue: {
type: [Array, Object],
},
hashValue: {
type: [Array, Object],
},
},
emits: ['valChange', 'cancel', 'update:visible'],
setup(props: any, { emit }) {
const state = reactive({
dialogVisible: false,
operationType: 1,
redisId: '',
keyInfo: {
key: '',
type: 'string',
timed: -1,
},
stringValue: '',
hashValue: [
{
key: '',
value: '',
},
],
setValue: [{ value: '' }],
});
const cancel = () => {
emit('update:visible', false);
emit('cancel');
setTimeout(() => {
state.keyInfo = {
key: '',
type: 'string',
timed: -1,
};
state.stringValue = '';
state.hashValue = [
{
key: '',
value: '',
},
];
}, 500);
};
watch(
() => props.visible,
(val) => {
state.dialogVisible = val;
}
);
watch(
() => props.redisId,
(val) => {
state.redisId = val;
}
);
watch(
() => props.operationType,
(val) => {
state.operationType = val;
}
);
watch(
() => props.keyInfo,
(val) => {
if (val) {
state.keyInfo = { ...val };
}
},
{
deep: true, // 深度监听的参数
}
);
watch(
() => props.stringValue,
(val) => {
if (val) {
state.stringValue = val;
}
},
{
deep: true, // 深度监听的参数
}
);
watch(
() => props.setValue,
(val) => {
if (val) {
state.setValue = val;
}
},
{
deep: true, // 深度监听的参数
}
);
watch(
() => props.hashValue,
(val) => {
if (val) {
state.hashValue = val;
}
},
{
deep: true, // 深度监听的参数
}
);
const saveValue = async () => {
notEmpty(state.keyInfo.key, 'key不能为空');
if (state.keyInfo.type == 'string') {
notEmpty(state.stringValue, 'value不能为空');
const sv = { value: state.stringValue, id: state.redisId };
Object.assign(sv, state.keyInfo);
await redisApi.saveStringValue.request(sv);
}
if (state.keyInfo.type == 'hash') {
isTrue(state.hashValue.length > 0, 'hash内容不能为空');
const sv = { value: state.hashValue, id: state.redisId };
Object.assign(sv, state.keyInfo);
await redisApi.saveHashValue.request(sv);
}
if (state.keyInfo.type == 'set') {
isTrue(state.setValue.length > 0, 'set内容不能为空');
const sv = { value: state.setValue.map((x) => x.value), id: state.redisId };
Object.assign(sv, state.keyInfo);
await redisApi.saveSetValue.request(sv);
}
ElMessage.success('数据保存成功');
cancel();
emit('valChange');
};
const onAddHashValue = () => {
state.hashValue.push({ key: '', value: '' });
};
const onAddSetValue = () => {
state.setValue.push({ value: '' });
};
return {
...toRefs(state),
saveValue,
cancel,
onAddHashValue,
onAddSetValue,
};
},
});
</script>

View File

@@ -16,31 +16,36 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="key" label-width="40px">
<el-input
placeholder="支持*模糊key"
style="width: 180px"
v-model="scanParam.match"
size="mini"
@clear="clear()"
clearable
></el-input>
</el-form-item>
<el-form-item label-width="40px">
<el-input placeholder="count" style="width: 62px" v-model="scanParam.count" size="mini"></el-input>
</el-form-item>
<el-button @click="searchKey()" type="success" icon="el-icon-search" size="mini" plain></el-button>
<el-button @click="scan()" icon="el-icon-bottom" size="mini" plain>scan</el-button>
<el-button type="primary" icon="el-icon-plus" size="mini" @click="save(false)" plain></el-button>
</template> </template>
</project-env-select> </project-env-select>
</el-col> </el-col>
<el-col class="mt10">
<el-form class="search-form" label-position="right" :inline="true" label-width="60px">
<el-form-item label="key" label-width="40px">
<el-input
placeholder="支持*模糊key"
style="width: 180px"
v-model="scanParam.match"
@clear="clear()"
clearable
></el-input>
</el-form-item>
<el-form-item label="count" label-width="60px">
<el-input placeholder="count" style="width: 62px" v-model="scanParam.count"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="searchKey()" type="success" icon="search" plain></el-button>
<el-button @click="scan()" icon="bottom" plain>scan</el-button>
<el-button type="primary" icon="plus" @click="onAddData(false)" plain></el-button>
</el-form-item>
<div style="float: right">
<span>keys: {{ dbsize }}</span>
</div>
</el-form>
</el-col>
</el-row> </el-row>
</div> </div>
<div style="float: right">
<!-- <el-button @click="scan()" icon="el-icon-refresh" size="small" plain>刷新</el-button> -->
<span>keys: {{ dbsize }}</span>
</div>
<el-table v-loading="loading" :data="keys" stripe :highlight-current-row="true" style="cursor: pointer"> <el-table v-loading="loading" :data="keys" stripe :highlight-current-row="true" style="cursor: pointer">
<el-table-column show-overflow-tooltip prop="key" label="key"></el-table-column> <el-table-column show-overflow-tooltip prop="key" label="key"></el-table-column>
<el-table-column prop="type" label="type" width="80"> </el-table-column> <el-table-column prop="type" label="type" width="80"> </el-table-column>
@@ -51,8 +56,8 @@
</el-table-column> </el-table-column>
<el-table-column label="操作"> <el-table-column label="操作">
<template #default="scope"> <template #default="scope">
<el-button @click="getValue(scope.row)" type="success" icon="el-icon-search" size="mini" plain>查看</el-button> <el-button @click="getValue(scope.row)" type="success" icon="search" plain size="small">查看</el-button>
<el-button @click="del(scope.row.key)" type="danger" size="mini" icon="el-icon-delete" plain>删除</el-button> <el-button @click="del(scope.row.key)" type="danger" icon="delete" plain size="small">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -60,7 +65,20 @@
<div style="text-align: center; margin-top: 10px"></div> <div style="text-align: center; margin-top: 10px"></div>
<value-dialog v-model:visible="valueDialog.visible" :keyValue="valueDialog.value" /> <!-- <value-dialog v-model:visible="valueDialog.visible" :keyValue="valueDialog.value" /> -->
<data-edit
v-model:visible="dataEdit.visible"
:title="dataEdit.title"
:keyInfo="dataEdit.keyInfo"
:redisId="scanParam.id"
:operationType="dataEdit.operationType"
:stringValue="dataEdit.stringValue"
:setValue="dataEdit.setValue"
:hashValue="dataEdit.hashValue"
@valChange="searchKey"
@cancel="onCancelDataEdit"
/>
</div> </div>
</template> </template>
@@ -70,12 +88,15 @@ import { redisApi } from './api';
import { toRefs, reactive, defineComponent } from 'vue'; import { toRefs, reactive, defineComponent } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue'; import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
import DataEdit from './DataEdit.vue';
import { isTrue, notNull } from '@/common/assert'; import { isTrue, notNull } from '@/common/assert';
import { key } from '../../../store/index';
export default defineComponent({ export default defineComponent({
name: 'DataOperation', name: 'DataOperation',
components: { components: {
ValueDialog, ValueDialog,
DataEdit,
ProjectEnvSelect, ProjectEnvSelect,
}, },
setup() { setup() {
@@ -103,6 +124,19 @@ export default defineComponent({
visible: false, visible: false,
value: {}, value: {},
}, },
dataEdit: {
visible: false,
title: '新增数据',
operationType: 1,
keyInfo: {
type: 'string',
timed: -1,
key: '',
},
stringValue: '',
hashValue: [{ key: '', value: '' }],
setValue: [{ value: '' }],
},
keys: [], keys: [],
dbsize: 0, dbsize: 0,
}); });
@@ -170,40 +204,65 @@ export default defineComponent({
}; };
const getValue = async (row: any) => { const getValue = async (row: any) => {
let api: any; const type = row.type;
switch (row.type) { const key = row.key;
case 'string':
api = redisApi.getStringValue; let res: any;
break;
case 'hash':
api = redisApi.getHashValue;
break;
case 'set':
api = redisApi.getSetValue;
break;
default:
api = redisApi.getStringValue;
break;
}
const id = state.cluster == 0 ? state.scanParam.id : state.cluster; const id = state.cluster == 0 ? state.scanParam.id : state.cluster;
const res = await api.request({ const reqParam = {
cluster: state.cluster, cluster: state.cluster,
key: row.key, key: row.key,
id, id,
}); };
switch (type) {
case 'string':
res = await redisApi.getStringValue.request(reqParam);
break;
case 'hash':
res = await redisApi.getHashValue.request(reqParam);
break;
case 'set':
res = await redisApi.getSetValue.request(reqParam);
break;
default:
res = null;
break;
}
notNull(res, '暂不支持该类型数据查看');
let timed = row.ttl == 18446744073709552000 ? 0 : row.ttl; if (type == 'string') {
state.valueDialog.value = { id: state.scanParam.id, key: row.key, value: res, timed: timed, type: row.type }; state.dataEdit.stringValue = res;
state.valueDialog.visible = true; }
if (type == 'set') {
state.dataEdit.setValue = res.map((x: any) => {
return {
value: x,
};
});
}
if (type == 'hash') {
const hash = [];
//遍历key和value
const keys = Object.keys(res);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = res[key];
hash.push({
key,
value,
});
}
state.dataEdit.hashValue = hash;
}
state.dataEdit.keyInfo.type = type;
state.dataEdit.keyInfo.timed = row.ttl;
state.dataEdit.keyInfo.key = key;
state.dataEdit.operationType = 2;
state.dataEdit.title = '修改数据';
state.dataEdit.visible = true;
}; };
// closeValueDialog() {
// this.valueDialog.visible = false
// this.valueDialog.value = {}
// }
// const update = (key: string) => {};
const del = (key: string) => { const del = (key: string) => {
ElMessageBox.confirm(`此操作将删除对应的key , 是否继续?`, '提示', { ElMessageBox.confirm(`此操作将删除对应的key , 是否继续?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
@@ -227,7 +286,7 @@ export default defineComponent({
}; };
const ttlConveter = (ttl: any) => { const ttlConveter = (ttl: any) => {
if (ttl == 18446744073709552000) { if (ttl == -1) {
return '永久'; return '永久';
} }
if (!ttl) { if (!ttl) {
@@ -262,6 +321,20 @@ export default defineComponent({
return result; return result;
}; };
const onAddData = () => {
notNull(state.scanParam.id, '请先选择redis');
state.dataEdit.operationType = 1;
state.dataEdit.title = '新增数据';
state.dataEdit.visible = true;
};
const onCancelDataEdit = () => {
state.dataEdit.keyInfo = {} as any;
state.dataEdit.stringValue = '';
state.dataEdit.setValue = [];
state.dataEdit.hashValue = [];
};
return { return {
...toRefs(state), ...toRefs(state),
changeProjectEnv, changeProjectEnv,
@@ -273,6 +346,8 @@ export default defineComponent({
getValue, getValue,
del, del,
ttlConveter, ttlConveter,
onAddData,
onCancelDataEdit,
}; };
}, },
}); });

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<el-dialog :title="title" v-model="dialogVisible" :show-close="false" :before-close="cancel" width="35%"> <el-dialog :title="title" v-model="dialogVisible" :show-close="false" :before-close="cancel" width="35%">
<el-form :model="form" ref="redisForm" :rules="rules" label-width="85px" size="small"> <el-form :model="form" ref="redisForm" :rules="rules" label-width="85px">
<el-form-item prop="projectId" label="项目:" required> <el-form-item prop="projectId" label="项目:" required>
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable> <el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option> <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
@@ -32,8 +32,8 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk" size="mini"> </el-button> <el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()" size="mini"> </el-button> <el-button @click="cancel()"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -1,18 +1,18 @@
<template> <template>
<div> <div>
<el-card> <el-card>
<el-button type="primary" icon="el-icon-plus" size="mini" @click="editRedis(true)" plain>添加</el-button> <el-button type="primary" icon="plus" @click="editRedis(true)" plain>添加</el-button>
<el-button type="primary" icon="el-icon-edit" :disabled="currentId == null" size="mini" @click="editRedis(false)" plain>编辑</el-button> <el-button type="primary" icon="edit" :disabled="currentId == null" @click="editRedis(false)" plain>编辑</el-button>
<el-button type="danger" icon="el-icon-delete" :disabled="currentId == null" size="mini" @click="deleteRedis" plain>删除</el-button> <el-button type="danger" icon="delete" :disabled="currentId == null" @click="deleteRedis" plain>删除</el-button>
<div style="float: right"> <div style="float: right">
<!-- <el-input placeholder="host" size="mini" style="width: 140px" v-model="query.host" @clear="search" plain clearable></el-input> <!-- <el-input placeholder="host" style="width: 140px" v-model="query.host" @clear="search" plain clearable></el-input>
<el-select v-model="params.clusterId" size="mini" clearable placeholder="集群选择"> <el-select v-model="params.clusterId" clearable placeholder="集群选择">
<el-option v-for="item in clusters" :key="item.id" :value="item.id" :label="item.name"></el-option> <el-option v-for="item in clusters" :key="item.id" :value="item.id" :label="item.name"></el-option>
</el-select> --> </el-select> -->
<el-select v-model="query.projectId" placeholder="请选择项目" filterable clearable size="small"> <el-select v-model="query.projectId" placeholder="请选择项目" filterable clearable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option> <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
</el-select> </el-select>
<el-button class="ml5" @click="search" type="success" icon="el-icon-search" size="small"></el-button> <el-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div> </div>
<el-table :data="redisTable" style="width: 100%" @current-change="choose"> <el-table :data="redisTable" style="width: 100%" @current-change="choose">
<el-table-column label="选择" width="50px"> <el-table-column label="选择" width="50px">
@@ -33,8 +33,8 @@
<el-table-column prop="creator" label="创建人"></el-table-column> <el-table-column prop="creator" label="创建人"></el-table-column>
<el-table-column label="操作" width> <el-table-column label="操作" width>
<template #default="scope"> <template #default="scope">
<el-button type="primary" @click="info(scope.row)" icon="el-icon-tickets" size="mini" plain>info</el-button> <el-button type="primary" @click="info(scope.row)" icon="tickets" plain size="small">info</el-button>
<!-- <el-button type="success" @click="manage(scope.row)" :ref="scope.row" size="mini" plain>数据管理</el-button> --> <!-- <el-button type="success" @click="manage(scope.row)" :ref="scope.row" plain>数据管理</el-button> -->
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

View File

@@ -8,8 +8,8 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="saveValue" type="primary" size="mini"> </el-button> <el-button @click="saveValue" type="primary"> </el-button>
<el-button @click="cancel()" size="mini"> </el-button> <el-button @click="cancel()"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -10,8 +10,9 @@ export const redisApi = {
getStringValue: Api.create("/redis/{id}/string-value", 'get'), getStringValue: Api.create("/redis/{id}/string-value", 'get'),
saveStringValue: Api.create("/redis/{id}/string-value", 'post'), saveStringValue: Api.create("/redis/{id}/string-value", 'post'),
getHashValue: Api.create("/redis/{id}/hash-value", 'get'), getHashValue: Api.create("/redis/{id}/hash-value", 'get'),
getSetValue: Api.create("/redis/{id}/set-value", 'get'),
saveHashValue: Api.create("/redis/{id}/hash-value", 'post'), saveHashValue: Api.create("/redis/{id}/hash-value", 'post'),
getSetValue: Api.create("/redis/{id}/set-value", 'get'),
saveSetValue: Api.create("/redis/{id}/set-value", 'post'),
del: Api.create("/redis/{id}/scan/{cursor}/{count}", 'delete'), del: Api.create("/redis/{id}/scan/{cursor}/{count}", 'delete'),
delKey: Api.create("/redis/{id}/key", 'delete'), delKey: Api.create("/redis/{id}/key", 'delete'),
} }

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="account-dialog"> <div class="account-dialog">
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="35%"> <el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="35%">
<el-form :model="form" ref="accountForm" :rules="rules" label-width="85px" size="small"> <el-form :model="form" ref="accountForm" :rules="rules" label-width="85px">
<el-form-item prop="username" label="用户名:" required> <el-form-item prop="username" label="用户名:" required>
<el-input :disabled="edit" v-model.trim="form.username" placeholder="请输入账号用户名" auto-complete="off"></el-input> <el-input :disabled="edit" v-model.trim="form.username" placeholder="请输入账号用户名" auto-complete="off"></el-input>
</el-form-item> </el-form-item>
@@ -15,8 +15,8 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="cancel()" size="mini"> </el-button> <el-button @click="cancel()"> </el-button>
<el-button type="primary" :loading="btnLoading" @click="btnOk" size="small"> </el-button> <el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -1,22 +1,12 @@
<template> <template>
<div class="role-list"> <div class="role-list">
<el-card> <el-card>
<el-button v-auth="'account:add'" type="primary" icon="el-icon-plus" size="mini" @click="editAccount(true)">添加</el-button> <el-button v-auth="'account:add'" type="primary" icon="plus" @click="editAccount(true)">添加</el-button>
<el-button <el-button v-auth="'account:update'" :disabled="chooseId == null" @click="editAccount(false)" type="primary" icon="edit">编辑</el-button>
v-auth="'account:update'" <el-button v-auth="'account:saveRoles'" :disabled="chooseId == null" @click="roleEdit()" type="success" icon="setting"
:disabled="chooseId == null"
@click="editAccount(false)"
type="primary"
icon="el-icon-edit"
size="mini"
>编辑</el-button
>
<el-button v-auth="'account:saveRoles'" :disabled="chooseId == null" @click="roleEdit()" type="success" icon="el-icon-setting" size="mini"
>角色分配</el-button >角色分配</el-button
> >
<el-button v-auth="'account:del'" :disabled="chooseId == null" @click="deleteAccount()" type="danger" icon="el-icon-delete" size="mini" <el-button v-auth="'account:del'" :disabled="chooseId == null" @click="deleteAccount()" type="danger" icon="delete">删除</el-button>
>删除</el-button
>
<div style="float: right"> <div style="float: right">
<el-input <el-input
class="mr2" class="mr2"
@@ -27,7 +17,7 @@
@clear="search()" @clear="search()"
clearable clearable
></el-input> ></el-input>
<el-button @click="search()" type="success" icon="el-icon-search" size="small"></el-button> <el-button @click="search()" type="success" icon="search" size="small"></el-button>
</div> </div>
<el-table :data="datas" ref="table" @current-change="choose" show-overflow-tooltip> <el-table :data="datas" ref="table" @current-change="choose" show-overflow-tooltip>
<el-table-column label="选择" width="50px"> <el-table-column label="选择" width="50px">
@@ -41,8 +31,8 @@
<el-table-column align="center" prop="status" label="状态" min-width="63"> <el-table-column align="center" prop="status" label="状态" min-width="63">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.status == 1" type="success" size="mini">正常</el-tag> <el-tag v-if="scope.row.status == 1" type="success">正常</el-tag>
<el-tag v-if="scope.row.status == -1" type="danger" size="mini">禁用</el-tag> <el-tag v-if="scope.row.status == -1" type="danger">禁用</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column min-width="160" prop="lastLoginTime" label="最后登录时间"> <el-table-column min-width="160" prop="lastLoginTime" label="最后登录时间">
@@ -80,8 +70,8 @@
@click="changeStatus(scope.row)" @click="changeStatus(scope.row)"
v-if="scope.row.status == 1" v-if="scope.row.status == 1"
type="danger" type="danger"
icom="el-icon-tickets" icom="tickets"
size="mini" size="small"
plain plain
>禁用</el-button >禁用</el-button
> >
@@ -90,7 +80,7 @@
v-if="scope.row.status == -1" v-if="scope.row.status == -1"
type="success" type="success"
@click="changeStatus(scope.row)" @click="changeStatus(scope.row)"
size="mini" size="small"
plain plain
>启用</el-button >启用</el-button
> >

View File

@@ -8,8 +8,8 @@
> >
<div class="toolbar"> <div class="toolbar">
<div style="float: left"> <div style="float: left">
<el-input placeholder="请输入角色名" size="small" style="width: 150px" v-model="query.name" @clear="clear()" clearable></el-input> <el-input placeholder="请输入角色名" style="width: 150px" v-model="query.name" @clear="clear()" clearable></el-input>
<el-button @click="search" type="success" icon="el-icon-search" size="small"></el-button> <el-button @click="search" type="success" icon="search"></el-button>
</div> </div>
</div> </div>
<el-table :data="allRole" border ref="roleTable" @select="select" style="width: 100%"> <el-table :data="allRole" border ref="roleTable" @select="select" style="width: 100%">
@@ -34,8 +34,8 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="cancel()" size="small"> </el-button> <el-button @click="cancel()"> </el-button>
<el-button type="primary" :loading="btnLoading" @click="btnOk" size="small"> </el-button> <el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -1,9 +1,9 @@
<template> <template>
<div class="menu-dialog"> <div class="menu-dialog">
<el-dialog :title="title" :destroy-on-close="true" v-model="dialogVisible" width="850px"> <el-dialog :title="title" :destroy-on-close="true" v-model="dialogVisible" width="769px">
<el-form :model="form" :inline="true" ref="menuForm" :rules="rules" label-width="95px" size="small"> <el-form :model="form" :inline="true" ref="menuForm" :rules="rules" label-width="95px">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item prop="type" label="类型" required> <el-form-item prop="type" label="类型" required>
<el-select v-model="form.type" :disabled="typeDisabled" placeholder="请选择" width="w100"> <el-select v-model="form.type" :disabled="typeDisabled" placeholder="请选择" width="w100">
<el-option v-for="item in enums.ResourceTypeEnum" :key="item.value" :label="item.label" :value="item.value"> <el-option v-for="item in enums.ResourceTypeEnum" :key="item.value" :label="item.label" :value="item.value">
@@ -11,65 +11,65 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item prop="name" label="名称" required> <el-form-item prop="name" label="名称" required>
<el-input v-model.trim="form.name" placeholder="资源名[菜单名]" auto-complete="off"></el-input> <el-input v-model.trim="form.name" placeholder="资源名[菜单名]" auto-complete="off"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item prop="code" label="path|code"> <el-form-item prop="code" label="path|code">
<el-input v-model.trim="form.code" placeholder="菜单不带/自动拼接父路径"></el-input> <el-input v-model.trim="form.code" placeholder="菜单不带/自动拼接父路径"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item label="序号" prop="weight" required> <el-form-item label="序号" prop="weight" required>
<el-input v-model.trim="form.weight" type="number" placeholder="请输入序号"></el-input> <el-input v-model.trim="form.weight" type="number" placeholder="请输入序号"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" label="图标"> <el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" label="图标">
<icon-selector v-model="form.meta.icon" :isAllOn="true" /> <icon-selector v-model="form.meta.icon" type="ele" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="路由名"> <el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="路由名">
<el-input v-model.trim="form.meta.routeName" placeholder="请输入路由名称"></el-input> <el-input v-model.trim="form.meta.routeName" placeholder="请输入路由名称"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="组件"> <el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="组件">
<el-input v-model.trim="form.meta.component" placeholder="请输入组件名"></el-input> <el-input v-model.trim="form.meta.component" placeholder="请输入组件名"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否缓存"> <el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否缓存">
<el-select v-model="form.meta.isKeepAlive" placeholder="请选择" width="w100"> <el-select v-model="form.meta.isKeepAlive" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option> <el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否隐藏"> <el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否隐藏">
<el-select v-model="form.meta.isHide" placeholder="请选择" width="w100"> <el-select v-model="form.meta.isHide" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option> <el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="tag不可删除"> <el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="tag不可删除">
<el-select v-model="form.meta.isAffix" placeholder="请选择" width="w100"> <el-select v-model="form.meta.isAffix" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option> <el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否iframe"> <el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否iframe">
<el-select @change="changeIsIframe" v-model="form.meta.isIframe" placeholder="请选择" width="w100"> <el-select @change="changeIsIframe" v-model="form.meta.isIframe" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option> <el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item <el-form-item
v-if="form.type === enums.ResourceTypeEnum.MENU.value && form.meta.isIframe" v-if="form.type === enums.ResourceTypeEnum.MENU.value && form.meta.isIframe"
prop="code" prop="code"
@@ -83,8 +83,8 @@
</el-form> </el-form>
<div style="text-align: center" class="dialog-footer mt10"> <div style="text-align: center" class="dialog-footer mt10">
<el-button type="primary" :loading="btnLoading" @click="btnOk" size="mini"> </el-button> <el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()" size="mini"> </el-button> <el-button @click="cancel()"> </el-button>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
@@ -122,7 +122,7 @@ export default defineComponent({
const defaultMeta = { const defaultMeta = {
routeName: '', routeName: '',
icon: 'el-icon-menu', icon: 'Menu',
redirect: '', redirect: '',
component: '', component: '',
isKeepAlive: true, isKeepAlive: true,

View File

@@ -2,9 +2,9 @@
<div class="menu"> <div class="menu">
<div class="toolbar"> <div class="toolbar">
<div> <div>
<span style="font-size: 14px"> <i class="el-icon-info"></i>红色字体表示禁用状态,右击鼠标显示操作 </span> <span style="font-size: 14px"><SvgIcon name="info-filled"/>红色字体表示禁用状态</span>
</div> </div>
<el-button v-auth="'resource:add'" type="primary" icon="el-icon-plus" size="mini" @click="addResource(false)">添加</el-button> <el-button v-auth="'resource:add'" type="primary" icon="plus" @click="addResource(false)">添加</el-button>
</div> </div>
<el-tree <el-tree
class="none-select" class="none-select"
@@ -15,93 +15,74 @@
@node-expand="handleNodeExpand" @node-expand="handleNodeExpand"
@node-collapse="handleNodeCollapse" @node-collapse="handleNodeCollapse"
:default-expanded-keys="defaultExpandedKeys" :default-expanded-keys="defaultExpandedKeys"
:expand-on-click-node="true" :expand-on-click-node="false"
> >
<template #default="{ data }"> <template #default="{ data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<el-dropdown size="mini" trigger="contextmenu"> <span style="font-size: 13px" v-if="data.type === enums.ResourceTypeEnum.MENU.value">
<span class="el-dropdown-link"> <span style="color: #3c8dbc"></span>
<span style="font-size: 13px" v-if="data.type === enums.ResourceTypeEnum.MENU.value"> {{ data.name }}
<span style="color: #3c8dbc"></span> <span style="color: #3c8dbc"></span>
{{ data.name }} <el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag>
<span style="color: #3c8dbc"></span> </span>
<el-tag v-if="data.children !== null" size="mini">{{ data.children.length }}</el-tag> <span style="font-size: 13px" v-if="data.type === enums.ResourceTypeEnum.PERMISSION.value">
</span> <span style="color: #3c8dbc"></span>
<span style="font-size: 13px" v-if="data.type === enums.ResourceTypeEnum.PERMISSION.value"> <span :style="data.status == 1 ? 'color: #67c23a;' : 'color: #f67c6c;'">{{ data.name }}</span>
<span style="color: #3c8dbc"></span> <span style="color: #3c8dbc"></span>
<span :style="data.status == 1 ? 'color: #67c23a;' : 'color: #f67c6c;'">{{ data.name }}</span> </span>
<span style="color: #3c8dbc"></span>
</span> <el-link @click.prevent="info(data)" style="margin-left: 25px" icon="view" type="info" :underline="false" />
</span>
<template #dropdown> <el-link
<el-dropdown-menu> v-auth="'resource:update'"
<el-dropdown-item> @click.prevent="editResource(data)"
<el-link @click.prevent="info(data)" icon="el-icon-view" type="info" :underline="false">查看</el-link> class="ml5"
</el-dropdown-item> type="primary"
<el-dropdown-item> icon="edit"
<el-link :underline="false"
v-auth="'resource:update'" />
@click.prevent="editResource(data)"
type="primary" <el-link
icon="el-icon-edit" v-auth="'resource:add'"
:underline="false" @click.prevent="addResource(data)"
> v-if="data.type === enums.ResourceTypeEnum.MENU.value"
修改 icon="circle-plus"
</el-link> :underline="false"
</el-dropdown-item> type="success"
<el-dropdown-item> class="ml5"
<el-link />
v-auth="'resource:add'"
@click.prevent="addResource(data)" <el-link
v-if="data.type === enums.ResourceTypeEnum.MENU.value" v-auth="'resource:changeStatus'"
icon="el-icon-circle-plus-outline" @click.prevent="changeStatus(data, -1)"
:underline="false" v-if="data.status === 1 && data.type === enums.ResourceTypeEnum.PERMISSION.value"
type="success" icon="circle-close"
> :underline="false"
新增 type="warning"
</el-link></el-dropdown-item class="ml5"
> />
<el-dropdown-item>
<el-link <el-link
v-auth="'resource:changeStatus'" v-auth="'resource:changeStatus'"
@click.prevent="changeStatus(data, -1)" @click.prevent="changeStatus(data, 1)"
v-if="data.status === 1 && data.type === enums.ResourceTypeEnum.PERMISSION.value" v-if="data.status === -1 && data.type === enums.ResourceTypeEnum.PERMISSION.value"
icon="el-icon-circle-close" type="success"
:underline="false" icon="circle-check"
type="warning" :underline="false"
> plain
禁用 class="ml5"
</el-link> />
</el-dropdown-item>
<el-dropdown-item> <el-link
<el-link v-auth="'resource:del'"
v-auth="'resource:changeStatus'" v-if="data.children == null && data.name !== '首页'"
@click.prevent="changeStatus(data, 1)" @click.prevent="deleteMenu(data)"
v-if="data.status === -1 && data.type === enums.ResourceTypeEnum.PERMISSION.value" type="danger"
type="success" icon="delete"
icon="el-icon-circle-check" :underline="false"
:underline="false" plain
plain class="ml5"
> />
启用
</el-link>
</el-dropdown-item>
<el-dropdown-item>
<el-link
v-auth="'resource:del'"
v-if="data.children == null && data.name !== '首页'"
@click.prevent="deleteMenu(data)"
type="danger"
icon="el-icon-remove-outline"
:underline="false"
plain
>
删除
</el-link>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</span> </span>
</template> </template>
</el-tree> </el-tree>
@@ -161,6 +142,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import ResourceEdit from './ResourceEdit.vue'; import ResourceEdit from './ResourceEdit.vue';
import enums from '../enums'; import enums from '../enums';
import { resourceApi } from '../api'; import { resourceApi } from '../api';
import SvgIcon from '@/components/svgIcon/index.vue';
export default defineComponent({ export default defineComponent({
name: 'ResourceList', name: 'ResourceList',

View File

@@ -19,8 +19,8 @@
</el-tree> </el-tree>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="btnOk" size="small"> </el-button> <el-button type="primary" @click="btnOk"> </el-button>
<el-button @click="cancel" size="small"> </el-button> <el-button @click="cancel"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -1,12 +1,12 @@
<template> <template>
<div class="role-dialog"> <div class="role-dialog">
<el-dialog :title="title" v-model="visible" :show-close="false" :before-close="cancel" width="500px"> <el-dialog :title="title" v-model="visible" :show-close="false" :before-close="cancel" width="500px">
<el-form :model="form" size="small" label-width="90px"> <el-form :model="form" label-width="90px">
<el-form-item label="角色名称:" required> <el-form-item label="角色名称:" required>
<el-input v-model="form.name" auto-complete="off"></el-input> <el-input v-model="form.name" auto-complete="off"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="角色code:" required> <el-form-item label="角色code:" required>
<el-input :disabled="form.id" v-model="form.code" placeholder="COMMON开头则为所有账号共有角色" auto-complete="off"></el-input> <el-input :disabled="form.id != null" v-model="form.code" placeholder="COMMON开头则为所有账号共有角色" auto-complete="off"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="角色描述:"> <el-form-item label="角色描述:">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入角色描述"></el-input> <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入角色描述"></el-input>
@@ -14,8 +14,8 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk" size="small"> </el-button> <el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()" size="small"> </el-button> <el-button @click="cancel()"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -1,34 +1,12 @@
<template> <template>
<div class="role-list"> <div class="role-list">
<el-card> <el-card>
<el-button v-auth="'role:add'" type="primary" icon="el-icon-plus" size="mini" @click="editRole(false)">添加</el-button> <el-button v-auth="'role:add'" type="primary" icon="plus" @click="editRole(false)">添加</el-button>
<el-button <el-button v-auth="'role:update'" :disabled="chooseId == null" @click="editRole(chooseData)" type="primary" icon="edit">编辑</el-button>
v-auth="'role:update'" <el-button v-auth="'role:saveResources'" :disabled="chooseId == null" @click="editResource(chooseData)" type="success" icon="setting"
:disabled="chooseId == null"
@click="editRole(chooseData)"
type="primary"
icon="el-icon-edit"
size="mini"
>编辑</el-button
>
<el-button
v-auth="'role:saveResources'"
:disabled="chooseId == null"
@click="editResource(chooseData)"
type="success"
icon="el-icon-setting"
size="mini"
>分配菜单&权限</el-button >分配菜单&权限</el-button
> >
<el-button <el-button v-auth="'role:del'" :disabled="chooseId == null" @click="deleteRole(chooseData)" type="danger" icon="delete">删除</el-button>
v-auth="'role:del'"
:disabled="chooseId == null"
@click="deleteRole(chooseData)"
type="danger"
icon="el-icon-delete"
size="mini"
>删除</el-button
>
<div style="float: right"> <div style="float: right">
<el-input <el-input
@@ -40,7 +18,7 @@
@clear="search" @clear="search"
clearable clearable
></el-input> ></el-input>
<el-button @click="search" type="success" icon="el-icon-search" size="small"></el-button> <el-button @click="search" type="success" icon="search" size="small"></el-button>
</div> </div>
<el-table :data="roles" @current-change="choose" ref="table" style="width: 100%"> <el-table :data="roles" @current-change="choose" ref="table" style="width: 100%">
<el-table-column label="选择" width="50px"> <el-table-column label="选择" width="50px">

View File

@@ -38,12 +38,36 @@ const viteConfig: UserConfig = {
outDir: 'dist', outDir: 'dist',
minify: 'esbuild', minify: 'esbuild',
sourcemap: false, sourcemap: false,
chunkSizeWarningLimit: 1500,
rollupOptions: {
output: {
entryFileNames: `assets/[name].${new Date().getTime()}.js`,
chunkFileNames: `assets/[name].${new Date().getTime()}.js`,
assetFileNames: `assets/[name].${new Date().getTime()}.[ext]`,
},
},
}, },
define: { define: {
__VUE_I18N_LEGACY_API__: JSON.stringify(false), __VUE_I18N_LEGACY_API__: JSON.stringify(false),
__VUE_I18N_FULL_INSTALL__: JSON.stringify(false), __VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
__INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false), __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
}, },
css: {
postcss: {
plugins: [
{
postcssPlugin: 'internal:charset-removal',
AtRule: {
charset: (atRule) => {
if (atRule.name === 'charset') {
atRule.remove();
}
}
}
}
]
}
},
}; };
export default viteConfig; export default viteConfig;

View File

@@ -117,16 +117,20 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
dbId := GetDbId(g) dbId := GetDbId(g)
go func() { go func() {
db := d.DbApp.GetDbInstance(dbId)
dbEntity := d.DbApp.GetById(dbId)
dbInfo := fmt.Sprintf("于%s的%s环境", dbEntity.Name, dbEntity.Env)
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
switch t := err.(type) { switch t := err.(type) {
case *biz.BizError: case *biz.BizError:
d.MsgApp.CreateAndSend(rc.LoginAccount, ws.ErrMsg("sql脚本执行失败", fmt.Sprintf("[%s]执行失败: [%s]", filename, t.Error()))) d.MsgApp.CreateAndSend(rc.LoginAccount, ws.ErrMsg("sql脚本执行失败", fmt.Sprintf("[%s]%s执行失败: [%s]", filename, dbInfo, t.Error())))
} }
} }
}() }()
db := d.DbApp.GetDbInstance(dbId)
for _, sql := range sqls { for _, sql := range sqls {
sql = strings.Trim(sql, " ") sql = strings.Trim(sql, " ")
if sql == "" || sql == "\n" { if sql == "" || sql == "\n" {
@@ -134,11 +138,11 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
} }
_, err := db.Exec(sql) _, err := db.Exec(sql)
if err != nil { if err != nil {
d.MsgApp.CreateAndSend(rc.LoginAccount, ws.ErrMsg("sql脚本执行失败", fmt.Sprintf("[%s]执行失败: [%s]", filename, err.Error()))) d.MsgApp.CreateAndSend(rc.LoginAccount, ws.ErrMsg("sql脚本执行失败", fmt.Sprintf("[%s]%s执行失败: [%s]", filename, dbInfo, err.Error())))
return return
} }
} }
d.MsgApp.CreateAndSend(rc.LoginAccount, ws.SuccessMsg("sql脚本执行成功", fmt.Sprintf("[%s]执行完成", filename))) d.MsgApp.CreateAndSend(rc.LoginAccount, ws.SuccessMsg("sql脚本执行成功", fmt.Sprintf("[%s]%s执行完成", filename, dbInfo)))
}() }()
} }

View File

@@ -11,8 +11,22 @@ type Redis struct {
EnvId uint64 `binding:"required" json:"envId"` EnvId uint64 `binding:"required" json:"envId"`
} }
type KeyValue struct { type KeyInfo struct {
Key string `binding:"required" json:"key"` Key string `binding:"required" json:"key"`
Value interface{} `binding:"required" json:"value"` Timed int64
Timed uint64 }
type StringValue struct {
KeyInfo
Value interface{} `binding:"required" json:"value"`
}
type HashValue struct {
KeyInfo
Value []map[string]interface{} `binding:"required" json:"value"`
}
type SetValue struct {
KeyInfo
Value []interface{} `binding:"required" json:"value"`
} }

View File

@@ -109,7 +109,7 @@ func (r *Redis) Scan(rc *ctx.ReqCtx) {
for i, k := range keys { for i, k := range keys {
ttlType := strings.Split(keyInfoSplit[i], ",") ttlType := strings.Split(keyInfoSplit[i], ",")
ttl, _ := strconv.Atoi(ttlType[0]) ttl, _ := strconv.Atoi(ttlType[0])
ki := &vo.KeyInfo{Key: k, Type: ttlType[1], Ttl: uint64(ttl)} ki := &vo.KeyInfo{Key: k, Type: ttlType[1], Ttl: int64(ttl)}
kis = append(kis, ki) kis = append(kis, ki)
} }
@@ -149,16 +149,9 @@ func (r *Redis) GetHashValue(rc *ctx.ReqCtx) {
rc.ResData = res rc.ResData = res
} }
func (r *Redis) GetSetValue(rc *ctx.ReqCtx) {
ri, key := r.checkKey(rc)
res, err := ri.Cli.SMembers(key).Result()
biz.ErrIsNilAppendErr(err, "获取set值失败: %s")
rc.ResData = res
}
func (r *Redis) SetStringValue(rc *ctx.ReqCtx) { func (r *Redis) SetStringValue(rc *ctx.ReqCtx) {
g := rc.GinCtx g := rc.GinCtx
keyValue := new(form.KeyValue) keyValue := new(form.StringValue)
ginx.BindJsonAndValid(g, keyValue) ginx.BindJsonAndValid(g, keyValue)
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id"))) ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
@@ -166,3 +159,44 @@ func (r *Redis) SetStringValue(rc *ctx.ReqCtx) {
biz.ErrIsNilAppendErr(err, "保存字符串值失败: %s") biz.ErrIsNilAppendErr(err, "保存字符串值失败: %s")
rc.ResData = str rc.ResData = str
} }
func (r *Redis) SetHashValue(rc *ctx.ReqCtx) {
g := rc.GinCtx
hashValue := new(form.HashValue)
ginx.BindJsonAndValid(g, hashValue)
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
key := hashValue.Key
// 简单处理->先删除,后新增
ri.Cli.Del(key)
for _, v := range hashValue.Value {
res := ri.Cli.HSet(key, v["key"].(string), v["value"])
biz.ErrIsNilAppendErr(res.Err(), "保存hash值失败")
}
if hashValue.Timed != -1 {
ri.Cli.Expire(key, time.Second*time.Duration(hashValue.Timed))
}
}
func (r *Redis) GetSetValue(rc *ctx.ReqCtx) {
ri, key := r.checkKey(rc)
res, err := ri.Cli.SMembers(key).Result()
biz.ErrIsNilAppendErr(err, "获取set值失败: %s")
rc.ResData = res
}
func (r *Redis) SetSetValue(rc *ctx.ReqCtx) {
g := rc.GinCtx
keyvalue := new(form.SetValue)
ginx.BindJsonAndValid(g, keyvalue)
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
key := keyvalue.Key
// 简单处理->先删除,后新增
ri.Cli.Del(key)
ri.Cli.SAdd(key, keyvalue.Value...)
if keyvalue.Timed != -1 {
ri.Cli.Expire(key, time.Second*time.Duration(keyvalue.Timed))
}
}

View File

@@ -24,6 +24,6 @@ type Keys struct {
type KeyInfo struct { type KeyInfo struct {
Key string `json:"key"` Key string `json:"key"`
Ttl uint64 `json:"ttl"` Ttl int64 `json:"ttl"`
Type string `json:"type"` Type string `json:"type"`
} }

View File

@@ -44,7 +44,7 @@ func InitDbRouter(router *gin.RouterGroup) {
}) })
db.GET(":dbId/t-create-ddl", func(c *gin.Context) { db.GET(":dbId/t-create-ddl", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithNeedToken(false).Handle(d.GetCreateTableDdl) ctx.NewReqCtxWithGin(c).Handle(d.GetCreateTableDdl)
}) })
// db.GET(":dbId/exec-sql", controllers.SelectData) // db.GET(":dbId/exec-sql", controllers.SelectData)

View File

@@ -61,14 +61,19 @@ func InitRedisRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(rs.GetHashValue) ctx.NewReqCtxWithGin(c).Handle(rs.GetHashValue)
}) })
// 设置hash类型值
redis.POST(":id/hash-value", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).Handle(rs.SetHashValue)
})
// 获取set类型值 // 获取set类型值
redis.GET(":id/set-value", func(c *gin.Context) { redis.GET(":id/set-value", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).Handle(rs.GetSetValue) ctx.NewReqCtxWithGin(c).Handle(rs.GetSetValue)
}) })
// 设置hash类型值 // 设置set类型值
redis.POST(":id/hash-value", func(c *gin.Context) { redis.POST(":id/set-value", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).Handle(rs.SetStringValue) ctx.NewReqCtxWithGin(c).Handle(rs.SetSetValue)
}) })
} }
} }