update 调整代码格式
This commit is contained in:
parent
a7a31b011a
commit
97187b246b
@ -1,17 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['toggleClick'])
|
||||
const toggleClick = () => {
|
||||
emit('toggleClick');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<svg :class="{'is-active':isActive}" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
|
||||
@ -22,6 +8,20 @@ const toggleClick = () => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['toggleClick'])
|
||||
const toggleClick = () => {
|
||||
emit('toggleClick');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
|
@ -1,126 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import Fuse from 'fuse.js'
|
||||
import { getNormalPath } from '@/utils/ruoyi'
|
||||
import { isHttp } from '@/utils/validate'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { RouteOption } from 'vue-router'
|
||||
|
||||
type Router = Array<{
|
||||
path: string;
|
||||
title: string[];
|
||||
}>
|
||||
|
||||
const search = ref('');
|
||||
const options = ref<any>([]);
|
||||
const searchPool = ref<Router>([]);
|
||||
const show = ref(false);
|
||||
const fuse = ref();
|
||||
const headerSearchSelectRef = ref(ElSelect);
|
||||
const router = useRouter();
|
||||
const routes = computed(() => usePermissionStore().routes);
|
||||
|
||||
const click = () => {
|
||||
show.value = !show.value
|
||||
if (show.value) {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
||||
}
|
||||
};
|
||||
const close = () => {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
||||
options.value = []
|
||||
show.value = false
|
||||
}
|
||||
const change = (val: any) => {
|
||||
const path = val.path;
|
||||
if (isHttp(path)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
const pindex = path.indexOf("http");
|
||||
window.open(path.substr(pindex, path.length), "_blank");
|
||||
} else {
|
||||
router.push(path)
|
||||
}
|
||||
search.value = ''
|
||||
options.value = []
|
||||
nextTick(() => {
|
||||
show.value = false
|
||||
})
|
||||
}
|
||||
const initFuse = (list: Router) => {
|
||||
fuse.value = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
}
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
|
||||
let res: Router = []
|
||||
routes.forEach(r => {
|
||||
// skip hidden router
|
||||
if (!r.hidden) {
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||
const data = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
title: [...prefixTitle]
|
||||
}
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title];
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data);
|
||||
}
|
||||
}
|
||||
// recursive child routes
|
||||
if (r.children) {
|
||||
const tempRoutes = generateRoutes(r.children, data.path, data.title);
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes];
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return res;
|
||||
}
|
||||
const querySearch = (query: string) => {
|
||||
if (query !== '') {
|
||||
options.value = fuse.value.search(query)
|
||||
} else {
|
||||
options.value = []
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
searchPool.value = generateRoutes(routes.value);
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
searchPool.value = generateRoutes(routes.value)
|
||||
})
|
||||
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', close)
|
||||
}
|
||||
})
|
||||
|
||||
watch(searchPool, (list) => {
|
||||
initFuse(list)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="{ 'show': show }" class="header-search">
|
||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||
@ -140,6 +17,129 @@ watch(searchPool, (list) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Fuse from 'fuse.js'
|
||||
import { getNormalPath } from '@/utils/ruoyi'
|
||||
import { isHttp } from '@/utils/validate'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { RouteOption } from 'vue-router'
|
||||
|
||||
type Router = Array<{
|
||||
path: string;
|
||||
title: string[];
|
||||
}>
|
||||
|
||||
const search = ref('');
|
||||
const options = ref<any>([]);
|
||||
const searchPool = ref<Router>([]);
|
||||
const show = ref(false);
|
||||
const fuse = ref();
|
||||
const headerSearchSelectRef = ref(ElSelect);
|
||||
const router = useRouter();
|
||||
const routes = computed(() => usePermissionStore().routes);
|
||||
|
||||
const click = () => {
|
||||
show.value = !show.value
|
||||
if (show.value) {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
||||
}
|
||||
};
|
||||
const close = () => {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
||||
options.value = []
|
||||
show.value = false
|
||||
}
|
||||
const change = (val: any) => {
|
||||
const path = val.path;
|
||||
if (isHttp(path)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
const pindex = path.indexOf("http");
|
||||
window.open(path.substr(pindex, path.length), "_blank");
|
||||
} else {
|
||||
router.push(path)
|
||||
}
|
||||
search.value = ''
|
||||
options.value = []
|
||||
nextTick(() => {
|
||||
show.value = false
|
||||
})
|
||||
}
|
||||
const initFuse = (list: Router) => {
|
||||
fuse.value = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
}
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
|
||||
let res: Router = []
|
||||
routes.forEach(r => {
|
||||
// skip hidden router
|
||||
if (!r.hidden) {
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||
const data = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
title: [...prefixTitle]
|
||||
}
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title];
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data);
|
||||
}
|
||||
}
|
||||
// recursive child routes
|
||||
if (r.children) {
|
||||
const tempRoutes = generateRoutes(r.children, data.path, data.title);
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes];
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return res;
|
||||
}
|
||||
const querySearch = (query: string) => {
|
||||
if (query !== '') {
|
||||
options.value = fuse.value.search(query)
|
||||
} else {
|
||||
options.value = []
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
searchPool.value = generateRoutes(routes.value);
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
searchPool.value = generateRoutes(routes.value)
|
||||
})
|
||||
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', close)
|
||||
}
|
||||
})
|
||||
|
||||
watch(searchPool, (list) => {
|
||||
initFuse(list)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
|
@ -1,48 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import icons from '@/components/IconSelect/requireIcons';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
require: true
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
require: false,
|
||||
default: '400px'
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const visible = ref(false);
|
||||
const { modelValue, width } = toRefs(props);
|
||||
const iconNames = ref<string[]>(icons);
|
||||
|
||||
const filterValue = ref('');
|
||||
|
||||
/**
|
||||
* 筛选图标
|
||||
*/
|
||||
const filterIcons = () => {
|
||||
if (filterValue.value) {
|
||||
iconNames.value = icons.filter(iconName =>
|
||||
iconName.includes(filterValue.value)
|
||||
);
|
||||
} else {
|
||||
iconNames.value = icons;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择图标
|
||||
* @param iconName 选择的图标名称
|
||||
*/
|
||||
const selectedIcon = (iconName: string) => {
|
||||
emit('update:modelValue', iconName);
|
||||
visible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative" :style="{ width: width }">
|
||||
<el-input v-model="modelValue" readonly @click="visible = !visible" placeholder="点击选择图标">
|
||||
@ -74,6 +29,51 @@ const selectedIcon = (iconName: string) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import icons from '@/components/IconSelect/requireIcons';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
require: true
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
require: false,
|
||||
default: '400px'
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const visible = ref(false);
|
||||
const { modelValue, width } = toRefs(props);
|
||||
const iconNames = ref<string[]>(icons);
|
||||
|
||||
const filterValue = ref('');
|
||||
|
||||
/**
|
||||
* 筛选图标
|
||||
*/
|
||||
const filterIcons = () => {
|
||||
if (filterValue.value) {
|
||||
iconNames.value = icons.filter(iconName =>
|
||||
iconName.includes(filterValue.value)
|
||||
);
|
||||
} else {
|
||||
iconNames.value = icons;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择图标
|
||||
* @param iconName 选择的图标名称
|
||||
*/
|
||||
const selectedIcon = (iconName: string) => {
|
||||
emit('update:modelValue', iconName);
|
||||
visible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-divider--horizontal {
|
||||
margin: 10px auto !important;
|
||||
|
@ -1,48 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
src: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
|
||||
const realSrc = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
let real_src = props.src.split(",")[0];
|
||||
return real_src;
|
||||
});
|
||||
|
||||
const realSrcList = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
let real_src_list = props.src.split(",");
|
||||
let srcList:string[] = [];
|
||||
real_src_list.forEach(item => {
|
||||
return srcList.push(item);
|
||||
});
|
||||
return srcList;
|
||||
});
|
||||
|
||||
const realWidth = computed(() =>
|
||||
typeof props.width == "string" ? props.width : `${props.width}px`
|
||||
);
|
||||
|
||||
const realHeight = computed(() =>
|
||||
typeof props.height == "string" ? props.height : `${props.height}px`
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-image :src="`${realSrc}`" fit="cover" :style="`width:${realWidth};height:${realHeight};`" :preview-src-list="realSrcList" preview-teleported>
|
||||
<template #error>
|
||||
@ -53,6 +8,51 @@ const realHeight = computed(() =>
|
||||
</el-image>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
src: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
|
||||
const realSrc = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
let real_src = props.src.split(",")[0];
|
||||
return real_src;
|
||||
});
|
||||
|
||||
const realSrcList = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
let real_src_list = props.src.split(",");
|
||||
let srcList:string[] = [];
|
||||
real_src_list.forEach(item => {
|
||||
return srcList.push(item);
|
||||
});
|
||||
return srcList;
|
||||
});
|
||||
|
||||
const realWidth = computed(() =>
|
||||
typeof props.width == "string" ? props.width : `${props.width}px`
|
||||
);
|
||||
|
||||
const realHeight = computed(() =>
|
||||
typeof props.height == "string" ? props.height : `${props.height}px`
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-image {
|
||||
border-radius: 5px;
|
||||
|
@ -1,20 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import useAppStore from "@/store/modules/app";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const size = computed(() => appStore.size);
|
||||
|
||||
const sizeOptions = ref([
|
||||
{ label: "较大", value: "large" },
|
||||
{ label: "默认", value: "default" },
|
||||
{ label: "稍小", value: "small" },
|
||||
]);
|
||||
|
||||
const handleSetSize = (size: string) => {
|
||||
appStore.setSize(size);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-dropdown trigger="click" @command="handleSetSize">
|
||||
@ -32,6 +15,23 @@ const handleSetSize = (size: string) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useAppStore from "@/store/modules/app";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const size = computed(() => appStore.size);
|
||||
|
||||
const sizeOptions = ref([
|
||||
{ label: "较大", value: "large" },
|
||||
{ label: "默认", value: "default" },
|
||||
{ label: "稍小", value: "small" },
|
||||
]);
|
||||
|
||||
const handleSetSize = (size: string) => {
|
||||
appStore.setSize(size);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.size-icon--style {
|
||||
font-size: 18px;
|
||||
|
@ -1,33 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
})
|
||||
const iconName = computed(() => `#icon-${props.iconClass}`);
|
||||
const svgClass = computed(() => {
|
||||
if (props.className) {
|
||||
return `svg-icon ${props.className}`
|
||||
}
|
||||
return 'svg-icon'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg :class="svgClass" aria-hidden="true">
|
||||
<use :xlink:href="iconName" :fill="color" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
})
|
||||
const iconName = computed(() => `#icon-${props.iconClass}`);
|
||||
const svgClass = computed(() => {
|
||||
if (props.className) {
|
||||
return `svg-icon ${props.className}`
|
||||
}
|
||||
return 'svg-icon'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scope lang="scss">
|
||||
.sub-el-icon,
|
||||
.nav-icon {
|
||||
|
@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
|
||||
><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
|
||||
>
|
||||
</template>
|
||||
<el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
|
||||
><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
|
||||
>
|
||||
</template>
|
||||
|
||||
<!-- 顶部菜单超出数量折叠 -->
|
||||
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
|
||||
<template #title>更多菜单</template>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber"
|
||||
><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
|
||||
>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
<!-- 顶部菜单超出数量折叠 -->
|
||||
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
|
||||
<template #title>更多菜单</template>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber"
|
||||
><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
|
||||
>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -1,39 +1,69 @@
|
||||
<template>
|
||||
<div class="el-tree-select">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
v-model="valueId"
|
||||
ref="treeSelect"
|
||||
:filterable="true"
|
||||
:clearable="true"
|
||||
@clear="clearHandle"
|
||||
:filter-method="selectFilterData"
|
||||
:placeholder="placeholder"
|
||||
>
|
||||
<el-option :value="valueId" :label="valueTitle">
|
||||
<el-tree
|
||||
id="tree-option"
|
||||
ref="selectTree"
|
||||
:accordion="accordion"
|
||||
:data="options"
|
||||
:props="objMap"
|
||||
:node-key="objMap.value"
|
||||
:expand-on-click-node="false"
|
||||
:default-expanded-keys="defaultExpandedKey"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="handleNodeClick"
|
||||
></el-tree>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElTreeSelect } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
/* 配置项 */
|
||||
objMap: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
value: 'id', // ID字段名
|
||||
label: 'label', // 显示名称
|
||||
children: 'children' // 子级字段名
|
||||
}
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
value: 'id', // ID字段名
|
||||
label: 'label', // 显示名称
|
||||
children: 'children' // 子级字段名
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 自动收起 */
|
||||
accordion: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false
|
||||
}
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**当前双向数据绑定的值 */
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
/**当前的数据 */
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/**输入框内部的文字 */
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
@ -45,7 +75,7 @@ const emit = defineEmits(['update:value']);
|
||||
const valueId = computed({
|
||||
get: () => props.value,
|
||||
set: (val) => {
|
||||
emit('update:value', val)
|
||||
emit('update:value', val)
|
||||
}
|
||||
});
|
||||
const valueTitle = ref('');
|
||||
@ -53,17 +83,17 @@ const defaultExpandedKey = ref<any[]>([]);
|
||||
|
||||
function initHandle() {
|
||||
nextTick(() => {
|
||||
const selectedValue = valueId.value;
|
||||
if(selectedValue !== null && typeof (selectedValue) !== 'undefined') {
|
||||
const node = selectTree.value.getNode(selectedValue)
|
||||
if (node) {
|
||||
valueTitle.value = node.data[props.objMap.label]
|
||||
selectTree.value.setCurrentKey(selectedValue) // 设置默认选中
|
||||
defaultExpandedKey.value = [selectedValue] // 设置默认展开
|
||||
}
|
||||
} else {
|
||||
clearHandle()
|
||||
const selectedValue = valueId.value;
|
||||
if(selectedValue !== null && typeof (selectedValue) !== 'undefined') {
|
||||
const node = selectTree.value.getNode(selectedValue)
|
||||
if (node) {
|
||||
valueTitle.value = node.data[props.objMap.label]
|
||||
selectTree.value.setCurrentKey(selectedValue) // 设置默认选中
|
||||
defaultExpandedKey.value = [selectedValue] // 设置默认展开
|
||||
}
|
||||
} else {
|
||||
clearHandle()
|
||||
}
|
||||
})
|
||||
}
|
||||
function handleNodeClick(node: any) {
|
||||
@ -126,33 +156,3 @@ ul li .el-tree .el-tree-node__content {
|
||||
color: $--color-primary;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div class="el-tree-select">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
v-model="valueId"
|
||||
ref="treeSelect"
|
||||
:filterable="true"
|
||||
:clearable="true"
|
||||
@clear="clearHandle"
|
||||
:filter-method="selectFilterData"
|
||||
:placeholder="placeholder"
|
||||
>
|
||||
<el-option :value="valueId" :label="valueTitle">
|
||||
<el-tree
|
||||
id="tree-option"
|
||||
ref="selectTree"
|
||||
:accordion="accordion"
|
||||
:data="options"
|
||||
:props="objMap"
|
||||
:node-key="objMap.value"
|
||||
:expand-on-click-node="false"
|
||||
:default-expanded-keys="defaultExpandedKey"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="handleNodeClick"
|
||||
></el-tree>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,3 +1,9 @@
|
||||
<template>
|
||||
<div v-loading="loading" :style="'height:' + height">
|
||||
<iframe :src="url" frameborder="no" style="width: 100%; height: 100%" scrolling="auto" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
src: {
|
||||
@ -19,9 +25,3 @@ onMounted(() => {
|
||||
};
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-loading="loading" :style="'height:' + height">
|
||||
<iframe :src="url" frameborder="no" style="width: 100%; height: 100%" scrolling="auto" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,11 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import InnerLink from "../InnerLink/index.vue";
|
||||
import useTagsViewStore from '@/store/modules/tagsView';
|
||||
|
||||
const route = useRoute();
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<transition-group name="fade-transform" mode="out-in">
|
||||
<inner-link
|
||||
@ -17,3 +9,11 @@ const tagsViewStore = useTagsViewStore()
|
||||
></inner-link>
|
||||
</transition-group>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import InnerLink from "../InnerLink/index.vue";
|
||||
import useTagsViewStore from '@/store/modules/tagsView';
|
||||
|
||||
const route = useRoute();
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
</script>
|
@ -1,105 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { handleThemeStyle } from '@/utils/theme'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { SettingTypeEnum } from "@/enums/SettingTypeEnum";
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
|
||||
const showSettings = ref(false);
|
||||
const theme = ref(settingsStore.theme);
|
||||
const sideTheme = ref(settingsStore.sideTheme);
|
||||
const storeSettings = computed(() => settingsStore);
|
||||
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
|
||||
|
||||
/** 是否需要topnav */
|
||||
const topNav = computed({
|
||||
get: () => storeSettings.value.topNav,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val })
|
||||
if (!val) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
|
||||
}
|
||||
}
|
||||
})
|
||||
/** 是否需要tagview */
|
||||
const tagsView = computed({
|
||||
get: () => storeSettings.value.tagsView,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val })
|
||||
}
|
||||
})
|
||||
/**是否需要固定头部 */
|
||||
const fixedHeader = computed({
|
||||
get: () => storeSettings.value.fixedHeader,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val })
|
||||
}
|
||||
})
|
||||
/**是否需要侧边栏的logo */
|
||||
const sidebarLogo = computed({
|
||||
get: () => storeSettings.value.sidebarLogo,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val })
|
||||
}
|
||||
})
|
||||
/**是否需要侧边栏的动态网页的title */
|
||||
const dynamicTitle = computed({
|
||||
get: () => storeSettings.value.dynamicTitle,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val })
|
||||
// 动态设置网页标题
|
||||
useDynamicTitle()
|
||||
}
|
||||
})
|
||||
|
||||
const themeChange = (val: string | null) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val })
|
||||
theme.value = val;
|
||||
if (val) {
|
||||
handleThemeStyle(val);
|
||||
}
|
||||
}
|
||||
const handleTheme = (val: string) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
|
||||
sideTheme.value = val;
|
||||
}
|
||||
const saveSetting = () => {
|
||||
proxy?.$modal.loading("正在保存到本地,请稍候...");
|
||||
let layoutSetting = {
|
||||
"topNav": storeSettings.value.topNav,
|
||||
"tagsView": storeSettings.value.tagsView,
|
||||
"fixedHeader": storeSettings.value.fixedHeader,
|
||||
"sidebarLogo": storeSettings.value.sidebarLogo,
|
||||
"dynamicTitle": storeSettings.value.dynamicTitle,
|
||||
"sideTheme": storeSettings.value.sideTheme,
|
||||
"theme": storeSettings.value.theme
|
||||
};
|
||||
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
|
||||
setTimeout(() => {proxy?.$modal.closeLoading()}, 1000)
|
||||
}
|
||||
const resetSetting = () => {
|
||||
proxy?.$modal.loading("正在清除设置缓存并刷新,请稍候...");
|
||||
localStorage.removeItem("layout-setting")
|
||||
setTimeout("window.location.reload()", 1000)
|
||||
}
|
||||
const openSetting = () => {
|
||||
showSettings.value = true;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openSetting,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px" close-on-click-modal>
|
||||
<div class="setting-drawer-title">
|
||||
@ -183,6 +81,108 @@ defineExpose({
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { handleThemeStyle } from '@/utils/theme'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { SettingTypeEnum } from "@/enums/SettingTypeEnum";
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
|
||||
const showSettings = ref(false);
|
||||
const theme = ref(settingsStore.theme);
|
||||
const sideTheme = ref(settingsStore.sideTheme);
|
||||
const storeSettings = computed(() => settingsStore);
|
||||
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
|
||||
|
||||
/** 是否需要topnav */
|
||||
const topNav = computed({
|
||||
get: () => storeSettings.value.topNav,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val })
|
||||
if (!val) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
|
||||
}
|
||||
}
|
||||
})
|
||||
/** 是否需要tagview */
|
||||
const tagsView = computed({
|
||||
get: () => storeSettings.value.tagsView,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val })
|
||||
}
|
||||
})
|
||||
/**是否需要固定头部 */
|
||||
const fixedHeader = computed({
|
||||
get: () => storeSettings.value.fixedHeader,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val })
|
||||
}
|
||||
})
|
||||
/**是否需要侧边栏的logo */
|
||||
const sidebarLogo = computed({
|
||||
get: () => storeSettings.value.sidebarLogo,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val })
|
||||
}
|
||||
})
|
||||
/**是否需要侧边栏的动态网页的title */
|
||||
const dynamicTitle = computed({
|
||||
get: () => storeSettings.value.dynamicTitle,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val })
|
||||
// 动态设置网页标题
|
||||
useDynamicTitle()
|
||||
}
|
||||
})
|
||||
|
||||
const themeChange = (val: string | null) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val })
|
||||
theme.value = val;
|
||||
if (val) {
|
||||
handleThemeStyle(val);
|
||||
}
|
||||
}
|
||||
const handleTheme = (val: string) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
|
||||
sideTheme.value = val;
|
||||
}
|
||||
const saveSetting = () => {
|
||||
proxy?.$modal.loading("正在保存到本地,请稍候...");
|
||||
let layoutSetting = {
|
||||
"topNav": storeSettings.value.topNav,
|
||||
"tagsView": storeSettings.value.tagsView,
|
||||
"fixedHeader": storeSettings.value.fixedHeader,
|
||||
"sidebarLogo": storeSettings.value.sidebarLogo,
|
||||
"dynamicTitle": storeSettings.value.dynamicTitle,
|
||||
"sideTheme": storeSettings.value.sideTheme,
|
||||
"theme": storeSettings.value.theme
|
||||
};
|
||||
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
|
||||
setTimeout(() => {proxy?.$modal.closeLoading()}, 1000)
|
||||
}
|
||||
const resetSetting = () => {
|
||||
proxy?.$modal.loading("正在清除设置缓存并刷新,请稍候...");
|
||||
localStorage.removeItem("layout-setting")
|
||||
setTimeout("window.location.reload()", 1000)
|
||||
}
|
||||
const openSetting = () => {
|
||||
showSettings.value = true;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openSetting,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-drawer-title {
|
||||
margin-bottom: 12px;
|
||||
|
@ -1,40 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
const props = defineProps({
|
||||
to: {
|
||||
type: [String, Object],
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const isExt = computed(() => {
|
||||
return isExternal(props.to as string)
|
||||
})
|
||||
|
||||
const type = computed(() => {
|
||||
if (isExt.value) {
|
||||
return 'a'
|
||||
}
|
||||
return 'router-link'
|
||||
})
|
||||
|
||||
function linkProps() {
|
||||
if (isExt.value) {
|
||||
return {
|
||||
href: props.to,
|
||||
target: '_blank',
|
||||
rel: 'noopener'
|
||||
}
|
||||
}
|
||||
return {
|
||||
to: props.to
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="type" v-bind="linkProps()">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
const props = defineProps({
|
||||
to: {
|
||||
type: [String, Object],
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const isExt = computed(() => {
|
||||
return isExternal(props.to as string)
|
||||
})
|
||||
|
||||
const type = computed(() => {
|
||||
if (isExt.value) {
|
||||
return 'a'
|
||||
}
|
||||
return 'router-link'
|
||||
})
|
||||
|
||||
function linkProps() {
|
||||
if (isExt.value) {
|
||||
return {
|
||||
href: props.to,
|
||||
target: '_blank',
|
||||
rel: 'noopener'
|
||||
}
|
||||
}
|
||||
return {
|
||||
to: props.to
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,22 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
import logo from '@/assets/logo/logo.png'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
defineProps({
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const title = ref('RuoYi-Vue-Plus');
|
||||
const settingsStore = useSettingsStore();
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="sidebar-logo-container"
|
||||
@ -40,6 +21,25 @@ const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
import logo from '@/assets/logo/logo.png'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
defineProps({
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const title = ref('RuoYi-Vue-Plus');
|
||||
const settingsStore = useSettingsStore();
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sidebarLogoFade-enter-active {
|
||||
transition: opacity 1.5s;
|
||||
|
@ -1,3 +1,25 @@
|
||||
<template>
|
||||
<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: bgColor }">
|
||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
|
||||
<transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in">
|
||||
<el-menu
|
||||
:default-active="activeMenu as string"
|
||||
:collapse="isCollapse"
|
||||
:background-color="bgColor"
|
||||
:text-color="textColor"
|
||||
:unique-opened="true"
|
||||
:active-text-color="theme"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
>
|
||||
<sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route" :base-path="route.path" />
|
||||
</el-menu>
|
||||
</transition>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Logo from './Logo.vue'
|
||||
import SidebarItem from './SidebarItem.vue'
|
||||
@ -20,36 +42,14 @@ const theme = computed(() => settingsStore.theme);
|
||||
const isCollapse = computed(() => !appStore.sidebar.opened);
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
})
|
||||
|
||||
const bgColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground);
|
||||
const textColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: bgColor }">
|
||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
|
||||
<transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in">
|
||||
<el-menu
|
||||
:default-active="activeMenu as string"
|
||||
:collapse="isCollapse"
|
||||
:background-color="bgColor"
|
||||
:text-color="textColor"
|
||||
:unique-opened="true"
|
||||
:active-text-color="theme"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
>
|
||||
<sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route" :base-path="route.path" />
|
||||
</el-menu>
|
||||
</transition>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,3 +1,9 @@
|
||||
<template>
|
||||
<el-scrollbar ref="scrollContainerRef" :vertical="false" class="scroll-container" @wheel.prevent="handleScroll">
|
||||
<slot />
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useTagsViewStore from '@/store/modules/tagsView'
|
||||
import { ElScrollbar } from 'element-plus';
|
||||
@ -8,84 +14,78 @@ const scrollContainerRef = ref(ElScrollbar)
|
||||
const scrollWrapper = computed(() => scrollContainerRef.value.$refs.wrapRef);
|
||||
|
||||
onMounted(() => {
|
||||
scrollWrapper.value.addEventListener('scroll', emitScroll, true)
|
||||
scrollWrapper.value.addEventListener('scroll', emitScroll, true)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
scrollWrapper.value.removeEventListener('scroll', emitScroll)
|
||||
scrollWrapper.value.removeEventListener('scroll', emitScroll)
|
||||
})
|
||||
|
||||
const handleScroll = (e: WheelEvent) => {
|
||||
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
}
|
||||
const emits = defineEmits(['scroll'])
|
||||
const emitScroll = () => {
|
||||
emits('scroll')
|
||||
emits('scroll')
|
||||
}
|
||||
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
const visitedViews = computed(() => tagsViewStore.visitedViews);
|
||||
|
||||
const moveToTarget = (currentTag: TagView) => {
|
||||
const $container = scrollContainerRef.value.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
const $container = scrollContainerRef.value.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
|
||||
// find first tag and last tag
|
||||
if (visitedViews.value.length > 0) {
|
||||
firstTag = visitedViews.value[0]
|
||||
lastTag = visitedViews.value[visitedViews.value.length - 1]
|
||||
}
|
||||
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||
} else {
|
||||
const tagListDom: any = document.getElementsByClassName('tags-view-item');
|
||||
const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
|
||||
let prevTag = null
|
||||
let nextTag = null
|
||||
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
|
||||
prevTag = tagListDom[k];
|
||||
}
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
|
||||
nextTag = tagListDom[k];
|
||||
}
|
||||
}
|
||||
// find first tag and last tag
|
||||
if (visitedViews.value.length > 0) {
|
||||
firstTag = visitedViews.value[0]
|
||||
lastTag = visitedViews.value[visitedViews.value.length - 1]
|
||||
}
|
||||
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||
} else {
|
||||
const tagListDom: any = document.getElementsByClassName('tags-view-item');
|
||||
const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
|
||||
let prevTag = null
|
||||
let nextTag = null
|
||||
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
|
||||
prevTag = tagListDom[k];
|
||||
}
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
|
||||
nextTag = tagListDom[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value
|
||||
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
moveToTarget,
|
||||
moveToTarget,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-scrollbar ref="scrollContainerRef" :vertical="false" class="scroll-container" @wheel.prevent="handleScroll">
|
||||
<slot />
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
|
@ -1,56 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import SideBar from './components/Sidebar/index.vue'
|
||||
import { AppMain, Navbar, Settings, TagsView } from './components'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
const sidebar = computed(() => useAppStore().sidebar);
|
||||
const device = computed(() => useAppStore().device);
|
||||
const needTagsView = computed(() => settingsStore.tagsView);
|
||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||
|
||||
const classObj = computed(() => ({
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
openSidebar: sidebar.value.opened,
|
||||
withoutAnimation: sidebar.value.withoutAnimation,
|
||||
mobile: device.value === 'mobile'
|
||||
}))
|
||||
|
||||
const { width } = useWindowSize();
|
||||
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||
|
||||
watchEffect(() => {
|
||||
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
if (width.value - 1 < WIDTH) {
|
||||
useAppStore().toggleDevice('mobile')
|
||||
useAppStore().closeSideBar({ withoutAnimation: true })
|
||||
} else {
|
||||
useAppStore().toggleDevice('desktop')
|
||||
}
|
||||
})
|
||||
|
||||
const navbarRef = ref(Navbar);
|
||||
const settingRef = ref(Settings);
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
navbarRef.value.initTenantList();
|
||||
})
|
||||
})
|
||||
|
||||
const handleClickOutside = () => {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
|
||||
const setLayout = () => {
|
||||
settingRef.value.openSetting();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
@ -66,6 +13,59 @@ const setLayout = () => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SideBar from './components/Sidebar/index.vue'
|
||||
import { AppMain, Navbar, Settings, TagsView } from './components'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
const sidebar = computed(() => useAppStore().sidebar);
|
||||
const device = computed(() => useAppStore().device);
|
||||
const needTagsView = computed(() => settingsStore.tagsView);
|
||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||
|
||||
const classObj = computed(() => ({
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
openSidebar: sidebar.value.opened,
|
||||
withoutAnimation: sidebar.value.withoutAnimation,
|
||||
mobile: device.value === 'mobile'
|
||||
}))
|
||||
|
||||
const { width } = useWindowSize();
|
||||
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||
|
||||
watchEffect(() => {
|
||||
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
if (width.value - 1 < WIDTH) {
|
||||
useAppStore().toggleDevice('mobile')
|
||||
useAppStore().closeSideBar({ withoutAnimation: true })
|
||||
} else {
|
||||
useAppStore().toggleDevice('desktop')
|
||||
}
|
||||
})
|
||||
|
||||
const navbarRef = ref(Navbar);
|
||||
const settingRef = ref(Settings);
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
navbarRef.value.initTenantList();
|
||||
})
|
||||
})
|
||||
|
||||
const handleClickOutside = () => {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
|
||||
const setLayout = () => {
|
||||
settingRef.value.openSetting();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/assets/styles/mixin.scss";
|
||||
@import "@/assets/styles/variables.module.scss";
|
||||
|
129
src/views/monitor/cache/index.vue
vendored
129
src/views/monitor/cache/index.vue
vendored
@ -1,67 +1,3 @@
|
||||
<script setup name="Cache" lang="ts">
|
||||
import { getCache } from '@/api/monitor/cache';
|
||||
import * as echarts from 'echarts';
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
|
||||
const cache = ref<any>({});
|
||||
const commandstats = ref();
|
||||
const usedmemory = ref();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const getList = async () => {
|
||||
proxy?.$modal.loading("正在加载缓存监控数据,请稍候!");
|
||||
const res = await getCache();
|
||||
proxy?.$modal.closeLoading();
|
||||
cache.value = res.data;
|
||||
const commandstatsIntance = echarts.init(commandstats.value, "macarons");
|
||||
commandstatsIntance.setOption({
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: "{a} <br/>{b} : {c} ({d}%)"
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "命令",
|
||||
type: "pie",
|
||||
roseType: "radius",
|
||||
radius: [15, 95],
|
||||
center: ["50%", "38%"],
|
||||
data: res.data.commandStats,
|
||||
animationEasing: "cubicInOut",
|
||||
animationDuration: 1000
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
|
||||
usedmemoryInstance.setOption({
|
||||
tooltip: {
|
||||
formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "峰值",
|
||||
type: "gauge",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
detail: {
|
||||
formatter: cache.value.info.used_memory_human
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: parseFloat(cache.value.info.used_memory_human),
|
||||
name: "内存消耗"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row>
|
||||
@ -186,3 +122,68 @@ onMounted(() => {
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Cache" lang="ts">
|
||||
import { getCache } from '@/api/monitor/cache';
|
||||
import * as echarts from 'echarts';
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
|
||||
const cache = ref<any>({});
|
||||
const commandstats = ref();
|
||||
const usedmemory = ref();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const getList = async () => {
|
||||
proxy?.$modal.loading("正在加载缓存监控数据,请稍候!");
|
||||
const res = await getCache();
|
||||
proxy?.$modal.closeLoading();
|
||||
cache.value = res.data;
|
||||
const commandstatsIntance = echarts.init(commandstats.value, "macarons");
|
||||
commandstatsIntance.setOption({
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: "{a} <br/>{b} : {c} ({d}%)"
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "命令",
|
||||
type: "pie",
|
||||
roseType: "radius",
|
||||
radius: [15, 95],
|
||||
center: ["50%", "38%"],
|
||||
data: res.data.commandStats,
|
||||
animationEasing: "cubicInOut",
|
||||
animationDuration: 1000
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
|
||||
usedmemoryInstance.setOption({
|
||||
tooltip: {
|
||||
formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "峰值",
|
||||
type: "gauge",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
detail: {
|
||||
formatter: cache.value.info.used_memory_human
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: parseFloat(cache.value.info.used_memory_human),
|
||||
name: "内存消耗"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
})
|
||||
</script>
|
@ -1,143 +1,3 @@
|
||||
<script setup name="Config" lang="ts">
|
||||
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
|
||||
import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { DateModelType } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
|
||||
|
||||
const configList = ref<ConfigVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<number | string>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
const configFormRef = ref(ElForm);
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
const initFormData: ConfigForm = {
|
||||
configId: undefined,
|
||||
configName: '',
|
||||
configKey: '',
|
||||
configValue: '',
|
||||
configType: "Y",
|
||||
remark: ''
|
||||
}
|
||||
const data = reactive<PageData<ConfigForm, ConfigQuery>>({
|
||||
form: {...initFormData},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
configName: '',
|
||||
configKey: '',
|
||||
configType: '',
|
||||
},
|
||||
rules: {
|
||||
configName: [{ required: true, message: "参数名称不能为空", trigger: "blur" }],
|
||||
configKey: [{ required: true, message: "参数键名不能为空", trigger: "blur" }],
|
||||
configValue: [{ required: true, message: "参数键值不能为空", trigger: "blur" }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询参数列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listConfig(proxy?.addDateRange(queryParams.value, dateRange.value));
|
||||
configList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
}
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
}
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = {...initFormData};
|
||||
configFormRef.value.resetFields();
|
||||
}
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: ConfigVO[]) => {
|
||||
ids.value = selection.map(item => item.configId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
dialog.visible = true;
|
||||
dialog.title = "添加参数";
|
||||
nextTick(() => {
|
||||
reset();
|
||||
})
|
||||
}
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = (row?: ConfigVO) => {
|
||||
dialog.visible = true;
|
||||
dialog.title = "修改参数";
|
||||
const configId = row?.configId || ids.value[0];
|
||||
nextTick(async () => {
|
||||
reset();
|
||||
const res = await getConfig(configId);
|
||||
form.value = res.data;
|
||||
})
|
||||
}
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
configFormRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
|
||||
proxy?.$modal.msgSuccess("操作成功");
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: ConfigVO) => {
|
||||
const configIds = row?.configId || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?');
|
||||
await delConfig(configIds);
|
||||
getList();
|
||||
proxy?.$modal.msgSuccess("删除成功");
|
||||
}
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download("system/config/export", {
|
||||
...queryParams.value
|
||||
}, `config_${new Date().getTime()}.xlsx`);
|
||||
}
|
||||
/** 刷新缓存按钮操作 */
|
||||
const handleRefreshCache = async () => {
|
||||
await refreshCache();
|
||||
proxy?.$modal.msgSuccess("刷新缓存成功");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
@ -259,3 +119,143 @@ onMounted(() => {
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Config" lang="ts">
|
||||
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
|
||||
import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { DateModelType } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
|
||||
|
||||
const configList = ref<ConfigVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<number | string>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
const configFormRef = ref(ElForm);
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
const initFormData: ConfigForm = {
|
||||
configId: undefined,
|
||||
configName: '',
|
||||
configKey: '',
|
||||
configValue: '',
|
||||
configType: "Y",
|
||||
remark: ''
|
||||
}
|
||||
const data = reactive<PageData<ConfigForm, ConfigQuery>>({
|
||||
form: {...initFormData},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
configName: '',
|
||||
configKey: '',
|
||||
configType: '',
|
||||
},
|
||||
rules: {
|
||||
configName: [{ required: true, message: "参数名称不能为空", trigger: "blur" }],
|
||||
configKey: [{ required: true, message: "参数键名不能为空", trigger: "blur" }],
|
||||
configValue: [{ required: true, message: "参数键值不能为空", trigger: "blur" }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询参数列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listConfig(proxy?.addDateRange(queryParams.value, dateRange.value));
|
||||
configList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
}
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
}
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = {...initFormData};
|
||||
configFormRef.value.resetFields();
|
||||
}
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: ConfigVO[]) => {
|
||||
ids.value = selection.map(item => item.configId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
dialog.visible = true;
|
||||
dialog.title = "添加参数";
|
||||
nextTick(() => {
|
||||
reset();
|
||||
})
|
||||
}
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = (row?: ConfigVO) => {
|
||||
dialog.visible = true;
|
||||
dialog.title = "修改参数";
|
||||
const configId = row?.configId || ids.value[0];
|
||||
nextTick(async () => {
|
||||
reset();
|
||||
const res = await getConfig(configId);
|
||||
form.value = res.data;
|
||||
})
|
||||
}
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
configFormRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
|
||||
proxy?.$modal.msgSuccess("操作成功");
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: ConfigVO) => {
|
||||
const configIds = row?.configId || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?');
|
||||
await delConfig(configIds);
|
||||
getList();
|
||||
proxy?.$modal.msgSuccess("删除成功");
|
||||
}
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download("system/config/export", {
|
||||
...queryParams.value
|
||||
}, `config_${new Date().getTime()}.xlsx`);
|
||||
}
|
||||
/** 刷新缓存按钮操作 */
|
||||
const handleRefreshCache = async () => {
|
||||
await refreshCache();
|
||||
proxy?.$modal.msgSuccess("刷新缓存成功");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
})
|
||||
</script>
|
||||
|
@ -1,31 +1,3 @@
|
||||
<script setup name="Profile" lang="ts">
|
||||
import userAvatar from "./userAvatar.vue";
|
||||
import userInfo from "./userInfo.vue";
|
||||
import resetPwd from "./resetPwd.vue";
|
||||
import { getUserProfile } from "@/api/system/user";
|
||||
|
||||
const activeTab = ref("userinfo");
|
||||
const state = ref<{ user: any; roleGroup: string; postGroup: string}>({
|
||||
user: {},
|
||||
roleGroup: '',
|
||||
postGroup: ''
|
||||
});
|
||||
|
||||
const userForm = ref({});
|
||||
|
||||
const getUser = async () => {
|
||||
const res = await getUserProfile();
|
||||
state.value.user = res.data.user;
|
||||
userForm.value = { ...res.data.user }
|
||||
state.value.roleGroup = res.data.roleGroup;
|
||||
state.value.postGroup = res.data.postGroup;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getUser();
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
@ -89,3 +61,31 @@ onMounted(() => {
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Profile" lang="ts">
|
||||
import userAvatar from "./userAvatar.vue";
|
||||
import userInfo from "./userInfo.vue";
|
||||
import resetPwd from "./resetPwd.vue";
|
||||
import { getUserProfile } from "@/api/system/user";
|
||||
|
||||
const activeTab = ref("userinfo");
|
||||
const state = ref<{ user: any; roleGroup: string; postGroup: string}>({
|
||||
user: {},
|
||||
roleGroup: '',
|
||||
postGroup: ''
|
||||
});
|
||||
|
||||
const userForm = ref({});
|
||||
|
||||
const getUser = async () => {
|
||||
const res = await getUserProfile();
|
||||
state.value.user = res.data.user;
|
||||
userForm.value = { ...res.data.user }
|
||||
state.value.roleGroup = res.data.roleGroup;
|
||||
state.value.postGroup = res.data.postGroup;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getUser();
|
||||
})
|
||||
</script>
|
||||
|
@ -1,48 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { updateUserPwd } from '@/api/system/user';
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
import { ResetPwdForm } from '@/api/system/user/types'
|
||||
import { ElForm } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
|
||||
const pwdRef = ref(ElForm);
|
||||
|
||||
const user = ref<ResetPwdForm>({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
const equalToPassword = (rule: any, value: string, callback: any) => {
|
||||
if (user.value.newPassword !== value) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const rules = ref({
|
||||
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
|
||||
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }],
|
||||
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
const submit = () => {
|
||||
pwdRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
await updateUserPwd(user.value.oldPassword, user.value.newPassword)
|
||||
proxy?.$modal.msgSuccess("修改成功");
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
const close = () => {
|
||||
proxy?.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="旧密码" prop="oldPassword">
|
||||
@ -60,3 +15,48 @@ const close = () => {
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { updateUserPwd } from '@/api/system/user';
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
import { ResetPwdForm } from '@/api/system/user/types'
|
||||
import { ElForm } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
|
||||
const pwdRef = ref(ElForm);
|
||||
|
||||
const user = ref<ResetPwdForm>({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
const equalToPassword = (rule: any, value: string, callback: any) => {
|
||||
if (user.value.newPassword !== value) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const rules = ref({
|
||||
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
|
||||
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }],
|
||||
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
const submit = () => {
|
||||
pwdRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
await updateUserPwd(user.value.oldPassword, user.value.newPassword)
|
||||
proxy?.$modal.msgSuccess("修改成功");
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
const close = () => {
|
||||
proxy?.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
|
@ -1,104 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import "vue-cropper/dist/index.css";
|
||||
import { VueCropper } from "vue-cropper";
|
||||
import { uploadAvatar } from "@/api/system/user";
|
||||
import useUserStore from "@/store/modules/user";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
|
||||
interface Options {
|
||||
img: string | ArrayBuffer | null // 裁剪图片的地址
|
||||
autoCrop: boolean // 是否默认生成截图框
|
||||
autoCropWidth: number // 默认生成截图框宽度
|
||||
autoCropHeight: number // 默认生成截图框高度
|
||||
fixedBox: boolean // 固定截图框大小 不允许改变
|
||||
fileName: string
|
||||
previews: any // 预览数据
|
||||
outputType: string
|
||||
visible: boolean
|
||||
}
|
||||
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref("修改头像");
|
||||
|
||||
const cropper = ref<any>({});
|
||||
//图片裁剪数据
|
||||
const options = reactive<Options>({
|
||||
img: userStore.avatar,
|
||||
autoCrop: true,
|
||||
autoCropWidth: 200,
|
||||
autoCropHeight: 200,
|
||||
fixedBox: true,
|
||||
outputType: "png",
|
||||
fileName: '',
|
||||
previews: {},
|
||||
visible: false
|
||||
});
|
||||
|
||||
/** 编辑头像 */
|
||||
const editCropper = () => {
|
||||
open.value = true;
|
||||
}
|
||||
/** 打开弹出层结束时的回调 */
|
||||
const modalOpened = () => {
|
||||
visible.value = true;
|
||||
}
|
||||
/** 覆盖默认上传行为 */
|
||||
const requestUpload = (): any => {}
|
||||
/** 向左旋转 */
|
||||
const rotateLeft = () => {
|
||||
cropper.value.rotateLeft();
|
||||
}
|
||||
/** 向右旋转 */
|
||||
const rotateRight = () => {
|
||||
cropper.value.rotateRight();
|
||||
}
|
||||
/** 图片缩放 */
|
||||
const changeScale = (num: number) => {
|
||||
num = num || 1;
|
||||
cropper.value.changeScale(num);
|
||||
}
|
||||
/** 上传预处理 */
|
||||
const beforeUpload = (file: any) => {
|
||||
if (file.type.indexOf("image/") == -1) {
|
||||
proxy?.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
options.fileName = file.name;
|
||||
};
|
||||
}
|
||||
}
|
||||
/** 上传图片 */
|
||||
const uploadImg = async () => {
|
||||
cropper.value.getCropBlob(async (data: any) => {
|
||||
let formData = new FormData();
|
||||
formData.append("avatarfile", data, options.fileName);
|
||||
const res = await uploadAvatar(formData);
|
||||
open.value = false;
|
||||
options.img = res.data.imgUrl;
|
||||
userStore.avatar = options.img as string;
|
||||
proxy?.$modal.msgSuccess("修改成功");
|
||||
visible.value = false;
|
||||
});
|
||||
}
|
||||
/** 实时预览 */
|
||||
const realTime = (data: any) => {
|
||||
options.previews = data;
|
||||
}
|
||||
/** 关闭窗口 */
|
||||
const closeDialog = () => {
|
||||
options.img = userStore.avatar;
|
||||
options.visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="user-info-head" @click="editCropper()">
|
||||
<img :src="options.img as string" title="点击上传头像" class="img-circle img-lg" />
|
||||
@ -154,6 +53,107 @@ const closeDialog = () => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import "vue-cropper/dist/index.css";
|
||||
import { VueCropper } from "vue-cropper";
|
||||
import { uploadAvatar } from "@/api/system/user";
|
||||
import useUserStore from "@/store/modules/user";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
|
||||
interface Options {
|
||||
img: string | ArrayBuffer | null // 裁剪图片的地址
|
||||
autoCrop: boolean // 是否默认生成截图框
|
||||
autoCropWidth: number // 默认生成截图框宽度
|
||||
autoCropHeight: number // 默认生成截图框高度
|
||||
fixedBox: boolean // 固定截图框大小 不允许改变
|
||||
fileName: string
|
||||
previews: any // 预览数据
|
||||
outputType: string
|
||||
visible: boolean
|
||||
}
|
||||
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref("修改头像");
|
||||
|
||||
const cropper = ref<any>({});
|
||||
//图片裁剪数据
|
||||
const options = reactive<Options>({
|
||||
img: userStore.avatar,
|
||||
autoCrop: true,
|
||||
autoCropWidth: 200,
|
||||
autoCropHeight: 200,
|
||||
fixedBox: true,
|
||||
outputType: "png",
|
||||
fileName: '',
|
||||
previews: {},
|
||||
visible: false
|
||||
});
|
||||
|
||||
/** 编辑头像 */
|
||||
const editCropper = () => {
|
||||
open.value = true;
|
||||
}
|
||||
/** 打开弹出层结束时的回调 */
|
||||
const modalOpened = () => {
|
||||
visible.value = true;
|
||||
}
|
||||
/** 覆盖默认上传行为 */
|
||||
const requestUpload = (): any => {}
|
||||
/** 向左旋转 */
|
||||
const rotateLeft = () => {
|
||||
cropper.value.rotateLeft();
|
||||
}
|
||||
/** 向右旋转 */
|
||||
const rotateRight = () => {
|
||||
cropper.value.rotateRight();
|
||||
}
|
||||
/** 图片缩放 */
|
||||
const changeScale = (num: number) => {
|
||||
num = num || 1;
|
||||
cropper.value.changeScale(num);
|
||||
}
|
||||
/** 上传预处理 */
|
||||
const beforeUpload = (file: any) => {
|
||||
if (file.type.indexOf("image/") == -1) {
|
||||
proxy?.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
options.fileName = file.name;
|
||||
};
|
||||
}
|
||||
}
|
||||
/** 上传图片 */
|
||||
const uploadImg = async () => {
|
||||
cropper.value.getCropBlob(async (data: any) => {
|
||||
let formData = new FormData();
|
||||
formData.append("avatarfile", data, options.fileName);
|
||||
const res = await uploadAvatar(formData);
|
||||
open.value = false;
|
||||
options.img = res.data.imgUrl;
|
||||
userStore.avatar = options.img as string;
|
||||
proxy?.$modal.msgSuccess("修改成功");
|
||||
visible.value = false;
|
||||
});
|
||||
}
|
||||
/** 实时预览 */
|
||||
const realTime = (data: any) => {
|
||||
options.previews = data;
|
||||
}
|
||||
/** 关闭窗口 */
|
||||
const closeDialog = () => {
|
||||
options.img = userStore.avatar;
|
||||
options.visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-info-head {
|
||||
position: relative;
|
||||
|
@ -1,43 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { updateUserProfile } from "@/api/system/user";
|
||||
import { FormRules } from "element-plus";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { PropType } from "vue";
|
||||
import { ElForm } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object as PropType<any>,
|
||||
}
|
||||
});
|
||||
const userForm = computed(() => props.user);
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const userRef = ref(ElForm);
|
||||
|
||||
const rules = ref<FormRules>({
|
||||
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
|
||||
/** 提交按钮 */
|
||||
const submit = () => {
|
||||
userRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
await updateUserProfile(props.user)
|
||||
proxy?.$modal.msgSuccess("修改成功");
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
const close = () => {
|
||||
proxy?.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="userRef" :model="userForm" :rules="rules" label-width="80px">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
@ -61,3 +21,43 @@ const close = () => {
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { updateUserProfile } from "@/api/system/user";
|
||||
import { FormRules } from "element-plus";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { PropType } from "vue";
|
||||
import { ElForm } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object as PropType<any>,
|
||||
}
|
||||
});
|
||||
const userForm = computed(() => props.user);
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const userRef = ref(ElForm);
|
||||
|
||||
const rules = ref<FormRules>({
|
||||
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
|
||||
/** 提交按钮 */
|
||||
const submit = () => {
|
||||
userRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
await updateUserProfile(props.user)
|
||||
proxy?.$modal.msgSuccess("修改成功");
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
const close = () => {
|
||||
proxy?.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
|
@ -1,26 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const prop = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const infoForm = computed(() => prop.info)
|
||||
|
||||
// 表单校验
|
||||
const rules = ref({
|
||||
tableName: [{ required: true, message: "请输入表名称", trigger: "blur" }],
|
||||
tableComment: [{ required: true, message: "请输入表描述", trigger: "blur" }],
|
||||
className: [{ required: true, message: "请输入实体类名称", trigger: "blur" }],
|
||||
functionAuthor: [{ required: true, message: "请输入作者", trigger: "blur" }]
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="basicInfoForm" :model="infoForm" :rules="rules" label-width="150px">
|
||||
<el-row>
|
||||
@ -52,3 +29,26 @@ const rules = ref({
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const prop = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const infoForm = computed(() => prop.info)
|
||||
|
||||
// 表单校验
|
||||
const rules = ref({
|
||||
tableName: [{ required: true, message: "请输入表名称", trigger: "blur" }],
|
||||
tableComment: [{ required: true, message: "请输入表描述", trigger: "blur" }],
|
||||
className: [{ required: true, message: "请输入实体类名称", trigger: "blur" }],
|
||||
functionAuthor: [{ required: true, message: "请输入作者", trigger: "blur" }]
|
||||
});
|
||||
</script>
|
||||
|
@ -1,76 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { listMenu } from '@/api/system/menu';
|
||||
import { ComponentInternalInstance, PropType } from 'vue';
|
||||
|
||||
interface MenuOptionsType {
|
||||
menuId: number;
|
||||
menuName: string;
|
||||
children: MenuOptionsType[] | undefined;
|
||||
}
|
||||
|
||||
const subColumns = ref<any>([]);
|
||||
const menuOptions = ref<Array<MenuOptionsType>>([]);
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const props = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<any>,
|
||||
default: null
|
||||
},
|
||||
tables: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const infoForm = computed(() => props.info);
|
||||
|
||||
const table = computed(() => props.tables);
|
||||
|
||||
// 表单校验
|
||||
const rules = ref({
|
||||
tplCategory: [{ required: true, message: "请选择生成模板", trigger: "blur" }],
|
||||
packageName: [{ required: true, message: "请输入生成包路径", trigger: "blur" }],
|
||||
moduleName: [{ required: true, message: "请输入生成模块名", trigger: "blur" }],
|
||||
businessName: [{ required: true, message: "请输入生成业务名", trigger: "blur" }],
|
||||
functionName: [{ required: true, message: "请输入生成功能名", trigger: "blur" }]
|
||||
});
|
||||
const subSelectChange = () => {
|
||||
infoForm.value.subTableFkName = "";
|
||||
}
|
||||
const tplSelectChange = (value: string) => {
|
||||
if (value !== "sub") {
|
||||
infoForm.value.subTableName = "";
|
||||
infoForm.value.subTableFkName = "";
|
||||
}
|
||||
}
|
||||
const setSubTableColumns = (value: string) => {
|
||||
table.value.forEach(item => {
|
||||
const name = item.tableName;
|
||||
if (value === name) {
|
||||
subColumns.value = item.columns;
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
/** 查询菜单下拉树结构 */
|
||||
const getMenuTreeselect = async () => {
|
||||
const res = await listMenu();
|
||||
const data = proxy?.handleTree<MenuOptionsType>(res.data, "menuId");
|
||||
if (data) {
|
||||
menuOptions.value = data
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.info.subTableName, val => {
|
||||
setSubTableColumns(val);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getMenuTreeselect();
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="genInfoForm" :model="infoForm" :rules="rules" label-width="150px">
|
||||
<el-row>
|
||||
@ -287,3 +214,76 @@ onMounted(() => {
|
||||
</template>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { listMenu } from '@/api/system/menu';
|
||||
import { ComponentInternalInstance, PropType } from 'vue';
|
||||
|
||||
interface MenuOptionsType {
|
||||
menuId: number;
|
||||
menuName: string;
|
||||
children: MenuOptionsType[] | undefined;
|
||||
}
|
||||
|
||||
const subColumns = ref<any>([]);
|
||||
const menuOptions = ref<Array<MenuOptionsType>>([]);
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const props = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<any>,
|
||||
default: null
|
||||
},
|
||||
tables: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const infoForm = computed(() => props.info);
|
||||
|
||||
const table = computed(() => props.tables);
|
||||
|
||||
// 表单校验
|
||||
const rules = ref({
|
||||
tplCategory: [{ required: true, message: "请选择生成模板", trigger: "blur" }],
|
||||
packageName: [{ required: true, message: "请输入生成包路径", trigger: "blur" }],
|
||||
moduleName: [{ required: true, message: "请输入生成模块名", trigger: "blur" }],
|
||||
businessName: [{ required: true, message: "请输入生成业务名", trigger: "blur" }],
|
||||
functionName: [{ required: true, message: "请输入生成功能名", trigger: "blur" }]
|
||||
});
|
||||
const subSelectChange = () => {
|
||||
infoForm.value.subTableFkName = "";
|
||||
}
|
||||
const tplSelectChange = (value: string) => {
|
||||
if (value !== "sub") {
|
||||
infoForm.value.subTableName = "";
|
||||
infoForm.value.subTableFkName = "";
|
||||
}
|
||||
}
|
||||
const setSubTableColumns = (value: string) => {
|
||||
table.value.forEach(item => {
|
||||
const name = item.tableName;
|
||||
if (value === name) {
|
||||
subColumns.value = item.columns;
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
/** 查询菜单下拉树结构 */
|
||||
const getMenuTreeselect = async () => {
|
||||
const res = await listMenu();
|
||||
const data = proxy?.handleTree<MenuOptionsType>(res.data, "menuId");
|
||||
if (data) {
|
||||
menuOptions.value = data
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.info.subTableName, val => {
|
||||
setSubTableColumns(val);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getMenuTreeselect();
|
||||
})
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user