init
This commit is contained in:
parent
83758dfee9
commit
e350dbe696
|
|
@ -0,0 +1,100 @@
|
||||||
|
<template>
|
||||||
|
<el-sub-menu
|
||||||
|
ref="subMenu"
|
||||||
|
:index="routerInfo.name"
|
||||||
|
>
|
||||||
|
|
||||||
|
<template #title>
|
||||||
|
<div
|
||||||
|
v-if="!isCollapse"
|
||||||
|
class="gva-subMenu"
|
||||||
|
>
|
||||||
|
<el-icon v-if="routerInfo.meta.icon">
|
||||||
|
<component :is="routerInfo.meta.icon" />
|
||||||
|
</el-icon>
|
||||||
|
<span>{{ routerInfo.meta.title }}</span>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<el-icon v-if="routerInfo.meta.icon">
|
||||||
|
<component :is="routerInfo.meta.icon" />
|
||||||
|
</el-icon>
|
||||||
|
<span>{{ routerInfo.meta.title }}</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<slot />
|
||||||
|
</el-sub-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'AsyncSubmenu',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
routerInfo: {
|
||||||
|
default: function() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
|
isCollapse: {
|
||||||
|
default: function() {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
default: function() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeBackground = ref(props.theme.activeBackground)
|
||||||
|
const activeText = ref(props.theme.activeText)
|
||||||
|
const normalText = ref(props.theme.normalText)
|
||||||
|
// const hoverBackground = ref(props.theme.hoverBackground)
|
||||||
|
// const hoverText = ref(props.theme.hoverText)
|
||||||
|
|
||||||
|
watch(() => props.theme, () => {
|
||||||
|
activeBackground.value = props.theme.activeBackground
|
||||||
|
activeText.value = props.theme.activeText
|
||||||
|
normalText.value = props.theme.normalText
|
||||||
|
// hoverBackground.value = props.theme.hoverBackground
|
||||||
|
// hoverText.value = props.theme.hoverText
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-sub-menu{
|
||||||
|
::v-deep(.el-sub-menu__title){
|
||||||
|
padding: 6px;
|
||||||
|
color: v-bind(normalText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-active:not(.is-opened){
|
||||||
|
::v-deep(.el-sub-menu__title) .gva-subMenu{
|
||||||
|
flex:1;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 44px;
|
||||||
|
background: v-bind(activeBackground) !important;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 2px 1px v-bind(activeBackground) !important;
|
||||||
|
i{
|
||||||
|
color: v-bind(activeText);
|
||||||
|
}
|
||||||
|
span{
|
||||||
|
color: v-bind(activeText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gva-subMenu {
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="menuComponent"
|
||||||
|
v-if="!routerInfo.hidden"
|
||||||
|
:is-collapse="isCollapse"
|
||||||
|
:theme="theme"
|
||||||
|
:router-info="routerInfo"
|
||||||
|
>
|
||||||
|
<template v-if="routerInfo.children&&routerInfo.children.length">
|
||||||
|
<AsideComponent
|
||||||
|
v-for="item in routerInfo.children"
|
||||||
|
:key="item.name"
|
||||||
|
:is-collapse="false"
|
||||||
|
:router-info="item"
|
||||||
|
:theme="theme"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import MenuItem from './menuItem.vue'
|
||||||
|
import AsyncSubmenu from './asyncSubmenu.vue'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'AsideComponent',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
routerInfo: {
|
||||||
|
type: Object,
|
||||||
|
default: () => null,
|
||||||
|
},
|
||||||
|
isCollapse: {
|
||||||
|
default: function() {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
default: function() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const menuComponent = computed(() => {
|
||||||
|
if (props.routerInfo.children && props.routerInfo.children.filter(item => !item.hidden).length) {
|
||||||
|
return AsyncSubmenu
|
||||||
|
} else {
|
||||||
|
return MenuItem
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
<template>
|
||||||
|
<el-menu-item :index="routerInfo.name">
|
||||||
|
<template v-if="isCollapse">
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
:content="routerInfo.meta.title"
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<el-icon v-if="routerInfo.meta.icon">
|
||||||
|
<component :is="routerInfo.meta.icon" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="gva-menu-item">
|
||||||
|
<el-icon v-if="routerInfo.meta.icon">
|
||||||
|
<component :is="routerInfo.meta.icon" />
|
||||||
|
</el-icon>
|
||||||
|
<span class="gva-menu-item-title">{{ routerInfo.meta.title }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-menu-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MenuItem',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
routerInfo: {
|
||||||
|
default: function() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
|
isCollapse: {
|
||||||
|
default: function() {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
default: function() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeBackground = ref(props.theme.activeBackground)
|
||||||
|
const activeText = ref(props.theme.activeText)
|
||||||
|
const normalText = ref(props.theme.normalText)
|
||||||
|
const hoverBackground = ref(props.theme.hoverBackground)
|
||||||
|
const hoverText = ref(props.theme.hoverText)
|
||||||
|
|
||||||
|
watch(() => props.theme, () => {
|
||||||
|
activeBackground.value = props.theme.activeBackground
|
||||||
|
activeText.value = props.theme.activeText
|
||||||
|
normalText.value = props.theme.normalText
|
||||||
|
hoverBackground.value = props.theme.hoverBackground
|
||||||
|
hoverText.value = props.theme.hoverText
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.gva-menu-item{
|
||||||
|
width: 100%;
|
||||||
|
flex:1;
|
||||||
|
height: 44px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 4px;
|
||||||
|
|
||||||
|
.gva-menu-item-title {
|
||||||
|
flex:1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu--collapse{
|
||||||
|
.el-menu-item.is-active{
|
||||||
|
color: v-bind(activeBackground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item{
|
||||||
|
color: v-bind(normalText);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item.is-active{
|
||||||
|
.gva-menu-item{
|
||||||
|
background: v-bind(activeBackground) !important;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 2px 1px v-bind(activeBackground) !important;
|
||||||
|
i{
|
||||||
|
color: v-bind(activeText);
|
||||||
|
}
|
||||||
|
span{
|
||||||
|
color: v-bind(activeText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item:hover{
|
||||||
|
.gva-menu-item{
|
||||||
|
background: v-bind(hoverBackground);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 2px 1px v-bind(hoverBackground);
|
||||||
|
color: v-bind(hoverText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,385 @@
|
||||||
|
<template>
|
||||||
|
<div class="router-history">
|
||||||
|
<el-tabs
|
||||||
|
v-model="activeValue"
|
||||||
|
:closable="!(historys.length === 1 && $route.name === defaultRouter)"
|
||||||
|
type="card"
|
||||||
|
@contextmenu.prevent="openContextMenu($event)"
|
||||||
|
@tab-click="changeTab"
|
||||||
|
@tab-remove="removeTab"
|
||||||
|
>
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="item in historys"
|
||||||
|
:key="getFmtString(item)"
|
||||||
|
:label="item.meta.title"
|
||||||
|
:name="getFmtString(item)"
|
||||||
|
:tab="item"
|
||||||
|
class="gva-tab"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span
|
||||||
|
:tab="item"
|
||||||
|
:style="{
|
||||||
|
color: activeValue === getFmtString(item) ? userStore.activeColor : '#333',
|
||||||
|
}"
|
||||||
|
><i
|
||||||
|
class="dot"
|
||||||
|
:style="{
|
||||||
|
backgroundColor:
|
||||||
|
activeValue === getFmtString(item) ? userStore.activeColor : '#ddd',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
{{ fmtTitle(item.meta.title,item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<!--自定义右键菜单html代码-->
|
||||||
|
<ul
|
||||||
|
v-show="contextMenuVisible"
|
||||||
|
:style="{ left: left + 'px', top: top + 'px' }"
|
||||||
|
class="contextmenu"
|
||||||
|
>
|
||||||
|
<li @click="closeAll">关闭所有</li>
|
||||||
|
<li @click="closeLeft">关闭左侧</li>
|
||||||
|
<li @click="closeRight">关闭右侧</li>
|
||||||
|
<li @click="closeOther">关闭其他</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { emitter } from '@/utils/bus.js'
|
||||||
|
import { computed, onUnmounted, ref, watch, nextTick } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
import { fmtTitle } from '@/utils/fmtRouterTitle'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'HistoryComponent',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const getFmtString = (item) => {
|
||||||
|
return item.name + JSON.stringify(item.query) + JSON.stringify(item.params)
|
||||||
|
}
|
||||||
|
|
||||||
|
const historys = ref([])
|
||||||
|
const activeValue = ref('')
|
||||||
|
const contextMenuVisible = ref(false)
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const left = ref(0)
|
||||||
|
const top = ref(0)
|
||||||
|
const isCollapse = ref(false)
|
||||||
|
const isMobile = ref(false)
|
||||||
|
const rightActive = ref('')
|
||||||
|
const defaultRouter = computed(() => userStore.userInfo.authority.defaultRouter)
|
||||||
|
const openContextMenu = (e) => {
|
||||||
|
if (
|
||||||
|
historys.value.length === 1 &&
|
||||||
|
route.name === defaultRouter.value
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let id = ''
|
||||||
|
if (e.srcElement.nodeName === 'SPAN') {
|
||||||
|
id = e.srcElement.offsetParent.id
|
||||||
|
} else {
|
||||||
|
id = e.srcElement.id
|
||||||
|
}
|
||||||
|
if (id) {
|
||||||
|
contextMenuVisible.value = true
|
||||||
|
let width
|
||||||
|
if (isCollapse.value) {
|
||||||
|
width = 54
|
||||||
|
} else {
|
||||||
|
width = 220
|
||||||
|
}
|
||||||
|
if (isMobile.value) {
|
||||||
|
width = 0
|
||||||
|
}
|
||||||
|
left.value = e.clientX - width
|
||||||
|
top.value = e.clientY + 10
|
||||||
|
rightActive.value = id.substring(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const closeAll = () => {
|
||||||
|
historys.value = [
|
||||||
|
{
|
||||||
|
name: defaultRouter.value,
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
router.push({ name: defaultRouter.value })
|
||||||
|
contextMenuVisible.value = false
|
||||||
|
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||||
|
}
|
||||||
|
const closeLeft = () => {
|
||||||
|
let right
|
||||||
|
const rightIndex = historys.value.findIndex((item) => {
|
||||||
|
if (getFmtString(item) === rightActive.value) {
|
||||||
|
right = item
|
||||||
|
}
|
||||||
|
return getFmtString(item) === rightActive.value
|
||||||
|
})
|
||||||
|
const activeIndex = historys.value.findIndex(
|
||||||
|
(item) => getFmtString(item) === activeValue.value
|
||||||
|
)
|
||||||
|
historys.value.splice(0, rightIndex)
|
||||||
|
if (rightIndex > activeIndex) {
|
||||||
|
router.push(right)
|
||||||
|
}
|
||||||
|
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||||
|
}
|
||||||
|
const closeRight = () => {
|
||||||
|
let right
|
||||||
|
const leftIndex = historys.value.findIndex((item) => {
|
||||||
|
if (getFmtString(item) === rightActive.value) {
|
||||||
|
right = item
|
||||||
|
}
|
||||||
|
return getFmtString(item) === rightActive.value
|
||||||
|
})
|
||||||
|
const activeIndex = historys.value.findIndex(
|
||||||
|
(item) => getFmtString(item) === activeValue.value
|
||||||
|
)
|
||||||
|
historys.value.splice(leftIndex + 1, historys.value.length)
|
||||||
|
if (leftIndex < activeIndex) {
|
||||||
|
router.push(right)
|
||||||
|
}
|
||||||
|
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||||
|
}
|
||||||
|
const closeOther = () => {
|
||||||
|
let right
|
||||||
|
historys.value = historys.value.filter((item) => {
|
||||||
|
if (getFmtString(item) === rightActive.value) {
|
||||||
|
right = item
|
||||||
|
}
|
||||||
|
return getFmtString(item) === rightActive.value
|
||||||
|
})
|
||||||
|
router.push(right)
|
||||||
|
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||||
|
}
|
||||||
|
const isSame = (route1, route2) => {
|
||||||
|
if (route1.name !== route2.name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (Object.keys(route1.query).length !== Object.keys(route2.query).length || Object.keys(route1.params).length !== Object.keys(route2.params).length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (const key in route1.query) {
|
||||||
|
if (route1.query[key] !== route2.query[key]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const key in route1.params) {
|
||||||
|
if (route1.params[key] !== route2.params[key]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const setTab = (route) => {
|
||||||
|
if (!historys.value.some((item) => isSame(item, route))) {
|
||||||
|
const obj = {}
|
||||||
|
obj.name = route.name
|
||||||
|
obj.meta = { ...route.meta }
|
||||||
|
delete obj.meta.matched
|
||||||
|
obj.query = route.query
|
||||||
|
obj.params = route.params
|
||||||
|
historys.value.push(obj)
|
||||||
|
}
|
||||||
|
window.sessionStorage.setItem('activeValue', getFmtString(route))
|
||||||
|
}
|
||||||
|
|
||||||
|
const historyMap = ref({})
|
||||||
|
|
||||||
|
const changeTab = (TabsPaneContext) => {
|
||||||
|
const name = TabsPaneContext?.props?.name
|
||||||
|
if (!name) return
|
||||||
|
const tab = historyMap.value[name]
|
||||||
|
router.push({
|
||||||
|
name: tab.name,
|
||||||
|
query: tab.query,
|
||||||
|
params: tab.params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const removeTab = (tab) => {
|
||||||
|
const index = historys.value.findIndex(
|
||||||
|
(item) => getFmtString(item) === tab
|
||||||
|
)
|
||||||
|
if (getFmtString(route) === tab) {
|
||||||
|
if (historys.value.length === 1) {
|
||||||
|
router.push({ name: defaultRouter.value })
|
||||||
|
} else {
|
||||||
|
if (index < historys.value.length - 1) {
|
||||||
|
router.push({
|
||||||
|
name: historys.value[index + 1].name,
|
||||||
|
query: historys.value[index + 1].query,
|
||||||
|
params: historys.value[index + 1].params,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
router.push({
|
||||||
|
name: historys.value[index - 1].name,
|
||||||
|
query: historys.value[index - 1].query,
|
||||||
|
params: historys.value[index - 1].params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
historys.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => contextMenuVisible.value, () => {
|
||||||
|
if (contextMenuVisible.value) {
|
||||||
|
document.body.addEventListener('click', () => {
|
||||||
|
contextMenuVisible.value = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
document.body.removeEventListener('click', () => {
|
||||||
|
contextMenuVisible.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => route, (to, now) => {
|
||||||
|
if (to.name === 'Login' || to.name === 'Reload') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
historys.value = historys.value.filter((item) => !item.meta.closeTab)
|
||||||
|
setTab(to)
|
||||||
|
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||||
|
activeValue.value = window.sessionStorage.getItem('activeValue')
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
|
watch(() => historys.value, () => {
|
||||||
|
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||||
|
historyMap.value = {}
|
||||||
|
historys.value.forEach((item) => {
|
||||||
|
historyMap.value[getFmtString(item)] = item
|
||||||
|
})
|
||||||
|
emitter.emit('setKeepAlive', historys.value)
|
||||||
|
}, {
|
||||||
|
deep: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const initPage = () => {
|
||||||
|
// 全局监听 关闭当前页面函数
|
||||||
|
emitter.on('closeThisPage', () => {
|
||||||
|
removeTab(getFmtString(route))
|
||||||
|
})
|
||||||
|
// 全局监听 关闭所有页面函数
|
||||||
|
emitter.on('closeAllPage', () => {
|
||||||
|
closeAll()
|
||||||
|
})
|
||||||
|
emitter.on('mobile', (data) => {
|
||||||
|
isMobile.value = data
|
||||||
|
})
|
||||||
|
emitter.on('collapse', (data) => {
|
||||||
|
isCollapse.value = data
|
||||||
|
})
|
||||||
|
|
||||||
|
emitter.on('setQuery', (data) => {
|
||||||
|
const index = historys.value.findIndex(
|
||||||
|
(item) => getFmtString(item) === activeValue.value
|
||||||
|
)
|
||||||
|
historys.value[index].query = data
|
||||||
|
activeValue.value = getFmtString(historys.value[index])
|
||||||
|
const currentUrl = window.location.href.split('?')[0]
|
||||||
|
const currentSearchParams = new URLSearchParams(data).toString()
|
||||||
|
window.history.replaceState({}, '', `${currentUrl}?${currentSearchParams}`)
|
||||||
|
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||||
|
})
|
||||||
|
|
||||||
|
emitter.on('switchTab', async(data) => {
|
||||||
|
const index = historys.value.findIndex((item) => item.name === data.name)
|
||||||
|
if (index < 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const key in data.query) {
|
||||||
|
data.query[key] = String(data.query[key])
|
||||||
|
}
|
||||||
|
for (const key in data.params) {
|
||||||
|
data.params[key] = String(data.params[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
historys.value[index].query = data.query || {}
|
||||||
|
historys.value[index].params = data.params || {}
|
||||||
|
await nextTick()
|
||||||
|
router.push(historys.value[index])
|
||||||
|
})
|
||||||
|
const initHistorys = [
|
||||||
|
{
|
||||||
|
name: defaultRouter.value,
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
setTab(route)
|
||||||
|
historys.value =
|
||||||
|
JSON.parse(sessionStorage.getItem('historys')) || initHistorys
|
||||||
|
if (!window.sessionStorage.getItem('activeValue')) {
|
||||||
|
activeValue.value = getFmtString(route)
|
||||||
|
} else {
|
||||||
|
activeValue.value = window.sessionStorage.getItem('activeValue')
|
||||||
|
}
|
||||||
|
if (window.sessionStorage.getItem('needCloseAll') === 'true') {
|
||||||
|
closeAll()
|
||||||
|
window.sessionStorage.removeItem('needCloseAll')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initPage()
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
emitter.off('collapse')
|
||||||
|
emitter.off('mobile')
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.contextmenu {
|
||||||
|
width: 100px;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 3000;
|
||||||
|
position: absolute;
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 5px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
.el-tabs__item .el-icon-close {
|
||||||
|
color: initial !important;
|
||||||
|
}
|
||||||
|
.el-tabs__item .dot {
|
||||||
|
content: "";
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
margin-right: 8px;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contextmenu li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 7px 16px;
|
||||||
|
}
|
||||||
|
.contextmenu li:hover {
|
||||||
|
background: #f2f2f2;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!-- 此文件禁止修改!如果您没有购买授权,请联系wx:shouzi_1994购买授权,未授权状态只需保留此代码 不影响任何正常使用 -->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex gap-4 items-center text-sm text-gray-500 justify-center mb-4">
|
||||||
|
<span>Powered by</span>
|
||||||
|
<span>Web-Admin</span>
|
||||||
|
<slot />
|
||||||
|
<span>Copyright</span>
|
||||||
|
<span>利农天下</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
<template>
|
||||||
|
<div @click="clickFull">
|
||||||
|
<div
|
||||||
|
v-if="isShow"
|
||||||
|
class="gvaIcon gvaIcon-fullscreen-expand"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="gvaIcon gvaIcon-fullscreen-shrink"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import screenfull from 'screenfull' // 引入screenfull
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Screenfull',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 22
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 22
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: String,
|
||||||
|
default: '#48576a'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (screenfull.isEnabled) {
|
||||||
|
screenfull.on('change', changeFullShow)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
screenfull.off('change')
|
||||||
|
})
|
||||||
|
|
||||||
|
const clickFull = () => {
|
||||||
|
if (screenfull.isEnabled) {
|
||||||
|
screenfull.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isShow = ref(true)
|
||||||
|
const changeFullShow = () => {
|
||||||
|
isShow.value = !screenfull.isFullscreen
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.screenfull-svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 32px;
|
||||||
|
fill: rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
<template>
|
||||||
|
<div class="search-component">
|
||||||
|
<div
|
||||||
|
class="gvaIcon gvaIcon-refresh"
|
||||||
|
:class="[reload ? 'reloading' : '']"
|
||||||
|
@click="handleReload"
|
||||||
|
/>
|
||||||
|
<Screenfull class="search-icon" />
|
||||||
|
<div
|
||||||
|
class="gvaIcon gvaIcon-customer-service"
|
||||||
|
@click="toService"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Screenfull from '@/view/layout/screenfull/index.vue'
|
||||||
|
import { emitter } from '@/utils/bus.js'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'BtnBox',
|
||||||
|
})
|
||||||
|
|
||||||
|
const reload = ref(false)
|
||||||
|
const handleReload = () => {
|
||||||
|
reload.value = true
|
||||||
|
emitter.emit('reload')
|
||||||
|
setTimeout(() => {
|
||||||
|
reload.value = false
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
const toService = () => {
|
||||||
|
window.open('https://support.qq.com/product/371961')
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.search-component {
|
||||||
|
@apply inline-flex overflow-hidden text-center gap-5 mr-5;
|
||||||
|
div{
|
||||||
|
@apply cursor-pointer;
|
||||||
|
}
|
||||||
|
.el-input__inner {
|
||||||
|
@apply border-b border-solid border-gray-300;
|
||||||
|
}
|
||||||
|
.el-dropdown-link {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reload {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reloading{
|
||||||
|
animation:turn 0.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes turn {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
class="drawer-container"
|
||||||
|
icon="setting"
|
||||||
|
@click="showSettingDrawer"
|
||||||
|
/>
|
||||||
|
<el-drawer
|
||||||
|
v-model="drawer"
|
||||||
|
title="系统配置"
|
||||||
|
:direction="direction"
|
||||||
|
:before-close="handleClose"
|
||||||
|
>
|
||||||
|
<div class="setting_body">
|
||||||
|
<div class="setting_card">
|
||||||
|
<div class="setting_content">
|
||||||
|
<div class="theme-box">
|
||||||
|
<div
|
||||||
|
class="item"
|
||||||
|
@click="changeMode('light')"
|
||||||
|
>
|
||||||
|
<div class="item-top">
|
||||||
|
<el-icon
|
||||||
|
v-if="userStore.mode === 'light'"
|
||||||
|
class="check"
|
||||||
|
>
|
||||||
|
<check />
|
||||||
|
</el-icon>
|
||||||
|
<img src="https://gw.alipayobjects.com/zos/antfincdn/NQ%24zoisaD2/jpRkZQMyYRryryPNtyIC.svg">
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
简约白
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="item"
|
||||||
|
@click="changeMode('dark')"
|
||||||
|
>
|
||||||
|
<div class="item-top">
|
||||||
|
<el-icon
|
||||||
|
v-if="userStore.mode === 'dark'"
|
||||||
|
class="check"
|
||||||
|
>
|
||||||
|
<check />
|
||||||
|
</el-icon>
|
||||||
|
<img src="https://gw.alipayobjects.com/zos/antfincdn/XwFOFbLkSM/LCkqqYNmvBEbokSDscrm.svg">
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
商务黑
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Setting',
|
||||||
|
})
|
||||||
|
|
||||||
|
const drawer = ref(false)
|
||||||
|
const direction = ref('rtl')
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
drawer.value = false
|
||||||
|
}
|
||||||
|
const showSettingDrawer = () => {
|
||||||
|
drawer.value = true
|
||||||
|
}
|
||||||
|
const changeMode = (e) => {
|
||||||
|
if (e === null) {
|
||||||
|
userStore.changeSideMode('dark')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userStore.changeSideMode(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.drawer-container {
|
||||||
|
transition: all 0.2s;
|
||||||
|
&:hover{
|
||||||
|
right: 0
|
||||||
|
}
|
||||||
|
position: fixed;
|
||||||
|
right: -20px;
|
||||||
|
bottom: 15%;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 999;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0 ,0 ,0, 10%);
|
||||||
|
}
|
||||||
|
.setting_body{
|
||||||
|
padding: 20px;
|
||||||
|
.setting_card{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.setting_content{
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
>.theme-box{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
>.color-box{
|
||||||
|
div{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.item{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 20px;
|
||||||
|
.item-top{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.check{
|
||||||
|
position: absolute;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #00afff;
|
||||||
|
right:10px;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
id="userLayout"
|
||||||
|
class="w-full h-full relative"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="rounded-lg flex items-center justify-evenly w-full h-full bg-white md:w-screen md:h-screen md:bg-[#194bfb]"
|
||||||
|
>
|
||||||
|
<div class="md:w-3/5 w-10/12 h-full flex items-center justify-evenly">
|
||||||
|
<div class="oblique h-[130%] w-3/5 bg-white transform -rotate-12 absolute -ml-52" />
|
||||||
|
<!-- 分割斜块 -->
|
||||||
|
<div class="z-[999] pt-12 pb-10 md:w-96 w-full rounded-lg flex flex-col justify-between box-border">
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
|
||||||
|
<img
|
||||||
|
class="w-24"
|
||||||
|
:src="$GIN_VUE_ADMIN.appLogo"
|
||||||
|
alt
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="mb-9">
|
||||||
|
<p class="text-center text-4xl font-bold">{{ $GIN_VUE_ADMIN.appName }}</p>
|
||||||
|
<!-- <p class="text-center text-sm font-normal text-gray-500 mt-2.5">A management platform using Golang and Vue</p> -->
|
||||||
|
</div>
|
||||||
|
<el-form
|
||||||
|
ref="loginForm"
|
||||||
|
:model="loginFormData"
|
||||||
|
:rules="rules"
|
||||||
|
:validate-on-rule-change="false"
|
||||||
|
@keyup.enter="submitForm"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
prop="username"
|
||||||
|
class="mb-6"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="loginFormData.username"
|
||||||
|
size="large"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
suffix-icon="user"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
prop="password"
|
||||||
|
class="mb-6"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="loginFormData.password"
|
||||||
|
show-password
|
||||||
|
size="large"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="loginFormData.openCaptcha"
|
||||||
|
prop="captcha"
|
||||||
|
class="mb-6"
|
||||||
|
>
|
||||||
|
<div class="flex w-full justify-between">
|
||||||
|
<el-input
|
||||||
|
v-model="loginFormData.captcha"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
size="large"
|
||||||
|
class="flex-1 mr-5"
|
||||||
|
/>
|
||||||
|
<div class="w-1/3 h-11 bg-[#c3d4f2] rounded">
|
||||||
|
<img
|
||||||
|
v-if="picPath"
|
||||||
|
class="w-full h-full"
|
||||||
|
:src="picPath"
|
||||||
|
alt="请输入验证码"
|
||||||
|
@click="loginVerify()"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mb-6">
|
||||||
|
<el-button
|
||||||
|
class="shadow shadow-blue-600 h-11 w-full"
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
@click="submitForm"
|
||||||
|
>登 录</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hidden md:block w-1/2 h-full float-right bg-[#194bfb]">
|
||||||
|
<!-- <img
|
||||||
|
class="h-full"
|
||||||
|
src="@/assets/login_right_banner.jpg"
|
||||||
|
alt="banner" > -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { captcha } from '@/api/user'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Login',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证函数
|
||||||
|
const checkUsername = (rule, value, callback) => {
|
||||||
|
if (value.length < 5) {
|
||||||
|
return callback(new Error('请输入正确的用户名'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const checkPassword = (rule, value, callback) => {
|
||||||
|
if (value.length < 6) {
|
||||||
|
return callback(new Error('请输入正确的密码'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
|
const loginVerify = async() => {
|
||||||
|
const ele = await captcha()
|
||||||
|
rules.captcha.push({
|
||||||
|
max: ele.data.captchaLength,
|
||||||
|
min: ele.data.captchaLength,
|
||||||
|
message: `请输入${ele.data.captchaLength}位验证码`,
|
||||||
|
trigger: 'blur',
|
||||||
|
})
|
||||||
|
picPath.value = ele.data.picPath
|
||||||
|
loginFormData.captchaId = ele.data.captchaId
|
||||||
|
loginFormData.openCaptcha = ele.data.openCaptcha
|
||||||
|
}
|
||||||
|
loginVerify()
|
||||||
|
|
||||||
|
// 登录相关操作
|
||||||
|
const loginForm = ref(null)
|
||||||
|
const picPath = ref('')
|
||||||
|
const loginFormData = reactive({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
captcha: '',
|
||||||
|
captchaId: '',
|
||||||
|
openCaptcha: false,
|
||||||
|
})
|
||||||
|
const rules = reactive({
|
||||||
|
username: [{ validator: checkUsername, trigger: 'blur' }],
|
||||||
|
password: [{ validator: checkPassword, trigger: 'blur' }],
|
||||||
|
captcha: [
|
||||||
|
{
|
||||||
|
message: '验证码格式不正确',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const login = async() => {
|
||||||
|
return await userStore.LoginIn(loginFormData)
|
||||||
|
}
|
||||||
|
const submitForm = () => {
|
||||||
|
loginForm.value.validate(async(v) => {
|
||||||
|
if (v) {
|
||||||
|
const flag = await login()
|
||||||
|
if (!flag) {
|
||||||
|
loginVerify()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '请正确填写登录信息',
|
||||||
|
showClose: true,
|
||||||
|
})
|
||||||
|
loginVerify()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,507 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="grid grid-cols-12 w-full gap-2">
|
||||||
|
<div class="col-span-3 h-full">
|
||||||
|
<div class="w-full h-full bg-white px-4 py-8 rounded-lg shadow-lg box-border">
|
||||||
|
<div class="user-card px-6 text-center bg-white shrink-0">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<SelectImage
|
||||||
|
v-model="userStore.userInfo.headerImg"
|
||||||
|
file-type="image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="py-6 text-center">
|
||||||
|
<p
|
||||||
|
v-if="!editFlag"
|
||||||
|
class="text-3xl flex justify-center items-center gap-4"
|
||||||
|
>
|
||||||
|
{{ userStore.userInfo.nickName }}
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer text-sm"
|
||||||
|
color="#66b1ff"
|
||||||
|
@click="openEdit"
|
||||||
|
>
|
||||||
|
<edit />
|
||||||
|
</el-icon>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
v-if="editFlag"
|
||||||
|
class="flex justify-center items-center gap-4"
|
||||||
|
>
|
||||||
|
<el-input v-model="nickName" />
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="#67c23a"
|
||||||
|
@click="enterEdit"
|
||||||
|
>
|
||||||
|
<check />
|
||||||
|
</el-icon>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="#f23c3c"
|
||||||
|
@click="closeEdit"
|
||||||
|
>
|
||||||
|
<close />
|
||||||
|
</el-icon>
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-500 mt-2 text-md">这个家伙很懒,什么都没有留下</p>
|
||||||
|
</div>
|
||||||
|
<div class="w-full h-full text-left">
|
||||||
|
<ul class="inline-block h-full w-full">
|
||||||
|
<li class="info-list">
|
||||||
|
<el-icon>
|
||||||
|
<user />
|
||||||
|
</el-icon>
|
||||||
|
{{ userStore.userInfo.nickName }}
|
||||||
|
</li>
|
||||||
|
<el-tooltip
|
||||||
|
class="item"
|
||||||
|
effect="light"
|
||||||
|
content="北京反转极光科技有限公司-技术部-前端事业群"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<li class="info-list">
|
||||||
|
<el-icon>
|
||||||
|
<data-analysis />
|
||||||
|
</el-icon>
|
||||||
|
北京反转极光科技有限公司-技术部-前端事业群
|
||||||
|
</li>
|
||||||
|
</el-tooltip>
|
||||||
|
<li class="info-list">
|
||||||
|
<el-icon>
|
||||||
|
<video-camera />
|
||||||
|
</el-icon>
|
||||||
|
中国·北京市·朝阳区
|
||||||
|
</li>
|
||||||
|
<el-tooltip
|
||||||
|
class="item"
|
||||||
|
effect="light"
|
||||||
|
content="GoLang/JavaScript/Vue/Gorm"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<li class="info-list">
|
||||||
|
<el-icon>
|
||||||
|
<medal />
|
||||||
|
</el-icon>
|
||||||
|
GoLang/JavaScript/Vue/Gorm
|
||||||
|
</li>
|
||||||
|
</el-tooltip>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-9 ">
|
||||||
|
<div class="bg-white h-full px-4 py-8 rounded-lg shadow-lg box-border">
|
||||||
|
<el-tabs
|
||||||
|
v-model="activeName"
|
||||||
|
@tab-click="handleClick"
|
||||||
|
>
|
||||||
|
<el-tab-pane
|
||||||
|
label="账号绑定"
|
||||||
|
name="second"
|
||||||
|
>
|
||||||
|
<ul>
|
||||||
|
<li class="borderd">
|
||||||
|
<p class="pb-2.5 text-xl text-gray-600">密保手机</p>
|
||||||
|
<p class="pb-2.5 text-lg text-gray-400">
|
||||||
|
已绑定手机:{{ userStore.userInfo.phone }}
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
class="float-right text-blue-400"
|
||||||
|
@click="changePhoneFlag = true"
|
||||||
|
>立即修改</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li class="borderd pt-2.5">
|
||||||
|
<p class="pb-2.5 text-xl text-gray-600">密保邮箱</p>
|
||||||
|
<p class="pb-2.5 text-lg text-gray-400">
|
||||||
|
已绑定邮箱:{{ userStore.userInfo.email }}
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
class="float-right text-blue-400"
|
||||||
|
@click="changeEmailFlag = true"
|
||||||
|
>立即修改</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li class="borderd pt-2.5">
|
||||||
|
<p class="pb-2.5 text-xl text-gray-600">密保问题</p>
|
||||||
|
<p class="pb-2.5 text-lg text-gray-400">
|
||||||
|
未设置密保问题
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
class="float-right text-blue-400"
|
||||||
|
>去设置</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li class="borderd pt-2.5">
|
||||||
|
<p class="pb-2.5 text-xl text-gray-600">修改密码</p>
|
||||||
|
<p class="pb-2.5 text-lg text-gray-400">
|
||||||
|
修改个人密码
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
class="float-right text-blue-400"
|
||||||
|
@click="showPassword = true"
|
||||||
|
>修改密码</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="showPassword"
|
||||||
|
title="修改密码"
|
||||||
|
width="360px"
|
||||||
|
@close="clearPassword"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="modifyPwdForm"
|
||||||
|
:model="pwdModify"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
:minlength="6"
|
||||||
|
label="原密码"
|
||||||
|
prop="password"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="pwdModify.password"
|
||||||
|
show-password
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:minlength="6"
|
||||||
|
label="新密码"
|
||||||
|
prop="newPassword"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="pwdModify.newPassword"
|
||||||
|
show-password
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:minlength="6"
|
||||||
|
label="确认密码"
|
||||||
|
prop="confirmPassword"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="pwdModify.confirmPassword"
|
||||||
|
show-password
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button
|
||||||
|
|
||||||
|
@click="showPassword = false"
|
||||||
|
>取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
@click="savePassword"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="changePhoneFlag"
|
||||||
|
title="绑定手机"
|
||||||
|
width="600px"
|
||||||
|
>
|
||||||
|
<el-form :model="phoneForm">
|
||||||
|
<el-form-item
|
||||||
|
label="手机号"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="phoneForm.phone"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="验证码"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
|
<div class="flex w-full gap-4">
|
||||||
|
<el-input
|
||||||
|
v-model="phoneForm.code"
|
||||||
|
class="flex-1"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="请自行设计短信服务,此处为模拟随便写"
|
||||||
|
style="width:300px"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:disabled="time>0"
|
||||||
|
@click="getCode"
|
||||||
|
>{{ time>0?`(${time}s)后重新获取`:'获取验证码' }}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button
|
||||||
|
|
||||||
|
@click="closeChangePhone"
|
||||||
|
>取消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
|
||||||
|
@click="changePhone"
|
||||||
|
>更改</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="changeEmailFlag"
|
||||||
|
title="绑定邮箱"
|
||||||
|
width="600px"
|
||||||
|
>
|
||||||
|
<el-form :model="emailForm">
|
||||||
|
<el-form-item
|
||||||
|
label="邮箱"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="emailForm.email"
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="验证码"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
|
<div class="flex w-full gap-4">
|
||||||
|
<el-input
|
||||||
|
v-model="emailForm.code"
|
||||||
|
class="flex-1"
|
||||||
|
placeholder="请自行设计邮件服务,此处为模拟随便写"
|
||||||
|
autocomplete="off"
|
||||||
|
style="width:300px"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:disabled="emailTime>0"
|
||||||
|
@click="getEmailCode"
|
||||||
|
>{{ emailTime>0?`(${emailTime}s)后重新获取`:'获取验证码' }}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button
|
||||||
|
|
||||||
|
@click="closeChangeEmail"
|
||||||
|
>取消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
|
||||||
|
@click="changeEmail"
|
||||||
|
>更改</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { setSelfInfo, changePassword } from '@/api/user.js'
|
||||||
|
import { reactive, ref, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
import SelectImage from '@/components/selectImage/selectImage.vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Person',
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeName = ref('second')
|
||||||
|
const rules = reactive({
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
|
{ min: 6, message: '最少6个字符', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
newPassword: [
|
||||||
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||||
|
{ min: 6, message: '最少6个字符', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{ required: true, message: '请输入确认密码', trigger: 'blur' },
|
||||||
|
{ min: 6, message: '最少6个字符', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value !== pwdModify.value.newPassword) {
|
||||||
|
callback(new Error('两次密码不一致'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const modifyPwdForm = ref(null)
|
||||||
|
const showPassword = ref(false)
|
||||||
|
const pwdModify = ref({})
|
||||||
|
const nickName = ref('')
|
||||||
|
const editFlag = ref(false)
|
||||||
|
const savePassword = async() => {
|
||||||
|
modifyPwdForm.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
changePassword({
|
||||||
|
password: pwdModify.value.password,
|
||||||
|
newPassword: pwdModify.value.newPassword,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('修改密码成功!')
|
||||||
|
}
|
||||||
|
showPassword.value = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearPassword = () => {
|
||||||
|
pwdModify.value = {
|
||||||
|
password: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
}
|
||||||
|
modifyPwdForm.value.clearValidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => userStore.userInfo.headerImg, async(val) => {
|
||||||
|
const res = await setSelfInfo({ headerImg: val })
|
||||||
|
if (res.code === 0) {
|
||||||
|
userStore.ResetUserInfo({ headerImg: val })
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '设置成功',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const openEdit = () => {
|
||||||
|
nickName.value = userStore.userInfo.nickName
|
||||||
|
editFlag.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeEdit = () => {
|
||||||
|
nickName.value = ''
|
||||||
|
editFlag.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const enterEdit = async() => {
|
||||||
|
const res = await setSelfInfo({
|
||||||
|
nickName: nickName.value
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
userStore.ResetUserInfo({ nickName: nickName.value })
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '设置成功',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
nickName.value = ''
|
||||||
|
editFlag.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = (tab, event) => {
|
||||||
|
console.log(tab, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
const changePhoneFlag = ref(false)
|
||||||
|
const time = ref(0)
|
||||||
|
const phoneForm = reactive({
|
||||||
|
phone: '',
|
||||||
|
code: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const getCode = async() => {
|
||||||
|
time.value = 60
|
||||||
|
let timer = setInterval(() => {
|
||||||
|
time.value--
|
||||||
|
if (time.value <= 0) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeChangePhone = () => {
|
||||||
|
changePhoneFlag.value = false
|
||||||
|
phoneForm.phone = ''
|
||||||
|
phoneForm.code = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const changePhone = async() => {
|
||||||
|
const res = await setSelfInfo({ phone: phoneForm.phone })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
userStore.ResetUserInfo({ phone: phoneForm.phone })
|
||||||
|
closeChangePhone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeEmailFlag = ref(false)
|
||||||
|
const emailTime = ref(0)
|
||||||
|
const emailForm = reactive({
|
||||||
|
email: '',
|
||||||
|
code: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const getEmailCode = async() => {
|
||||||
|
emailTime.value = 60
|
||||||
|
let timer = setInterval(() => {
|
||||||
|
emailTime.value--
|
||||||
|
if (emailTime.value <= 0) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeChangeEmail = () => {
|
||||||
|
changeEmailFlag.value = false
|
||||||
|
emailForm.email = ''
|
||||||
|
emailForm.code = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeEmail = async() => {
|
||||||
|
const res = await setSelfInfo({ email: emailForm.email })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
userStore.ResetUserInfo({ email: emailForm.email })
|
||||||
|
closeChangeEmail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.borderd {
|
||||||
|
@apply border-b-2 border-solid border-gray-100 border-t-0 border-r-0 border-l-0;
|
||||||
|
&:last-child{
|
||||||
|
@apply border-b-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-list{
|
||||||
|
@apply w-full whitespace-nowrap overflow-hidden text-ellipsis py-3 text-lg text-gray-700
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,437 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" class="demo-form-inline" :rules="searchRule"
|
||||||
|
@keyup.enter="onSubmit">
|
||||||
|
<el-form-item label="创建日期" prop="createdAt">
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
创建日期
|
||||||
|
<el-tooltip content="搜索范围是开始日期(包含)至结束日期(不包含)">
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<el-date-picker v-model="searchInfo.startCreatedAt" type="datetime" placeholder="开始日期"
|
||||||
|
:disabled-date="time=> searchInfo.endCreatedAt ? time.getTime() > searchInfo.endCreatedAt.getTime() : false"></el-date-picker>
|
||||||
|
—
|
||||||
|
<el-date-picker v-model="searchInfo.endCreatedAt" type="datetime" placeholder="结束日期"
|
||||||
|
:disabled-date="time=> searchInfo.startCreatedAt ? time.getTime() < searchInfo.startCreatedAt.getTime() : false"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="search" @click="onSubmit">查询</el-button>
|
||||||
|
<el-button icon="refresh" @click="onReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
|
||||||
|
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
||||||
|
@click="onDelete">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table ref="multipleTable" style="width: 100%" tooltip-effect="dark" :data="tableData" row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column align="left" label="ID" prop="ID" width="120" />
|
||||||
|
<el-table-column align="left" label="编码" prop="code" width="120" />
|
||||||
|
<el-table-column align="left" label="名称" prop="name" width="120" />
|
||||||
|
<el-table-column align="left" label="分类" prop="categoryId" width="120">
|
||||||
|
<template #default="scope">{{ formatCategory(scope.row.categoryId) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="排序" prop="sortWeight" width="120" />
|
||||||
|
<el-table-column align="left" label="状态" prop="status" width="120">
|
||||||
|
<template #default="scope">{{ formatBoolean(scope.row.status) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="创建时间" width="180">
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.createdAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="操作" fixed="right" min-width="240">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" link class="table-button" @click="getDetails(scope.row)">
|
||||||
|
<el-icon style="margin-right: 5px">
|
||||||
|
<InfoFilled />
|
||||||
|
</el-icon>
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link icon="edit" class="table-button"
|
||||||
|
@click="updateProductFunc(scope.row)">变更</el-button>
|
||||||
|
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination layout="total, sizes, prev, pager, next, jumper" :current-page="page" :page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]" :total="total" @current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer size="800" v-model="dialogFormVisible" :show-close="false" :before-close="closeDialog">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">{{type==='create'?'添加':'修改'}}</span>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="enterDialog">确 定</el-button>
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="编码:" prop="code">
|
||||||
|
<el-input v-model="formData.code" :clearable="true" placeholder="请输入编码" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称:" prop="name">
|
||||||
|
<el-input v-model="formData.name" :clearable="true" placeholder="请输入名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分类:">
|
||||||
|
<el-select v-model="formData.categoryId">
|
||||||
|
<el-option v-for="(item, index) in formData.categoryOptions" :key="index" :label="item.name"
|
||||||
|
:value="item.ID">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:" prop="sortWeight">
|
||||||
|
<el-input v-model.number="formData.sortWeight" :clearable="true" placeholder="请输入排序" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:" prop="status">
|
||||||
|
<el-switch v-model="formData.status" active-color="#13ce66" inactive-color="#ff4949" active-text="开启"
|
||||||
|
inactive-text="关闭" clearable></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-drawer size="800" v-model="detailShow" :before-close="closeDetailShow" destroy-on-close>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">查看详情</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="ID">
|
||||||
|
{{ formData.ID }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="编码">
|
||||||
|
{{ formData.code }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="名称">
|
||||||
|
{{ formData.name }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="分类">
|
||||||
|
{{ formatCategory(formData.categoryId) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="排序">
|
||||||
|
{{ formData.sortWeight }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
{{ formatBoolean(formData.status) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">
|
||||||
|
{{ formatDate(formData.createdAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="更新时间">
|
||||||
|
{{ formatDate(formData.updatedAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createProduct,
|
||||||
|
deleteProduct,
|
||||||
|
deleteProductByIds,
|
||||||
|
updateProduct,
|
||||||
|
findProduct,
|
||||||
|
getProductList
|
||||||
|
} from '@/api/product'
|
||||||
|
import { getProductCategoryList } from '@/api/productCategory'
|
||||||
|
|
||||||
|
// 全量引入格式化工具 请按需保留
|
||||||
|
import { getDictFunc, formatDate, formatBoolean, filterDict, ReturnArrImg, onDownloadFile } from '@/utils/format'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Product'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动化生成的字典(可能为空)以及字段
|
||||||
|
const formData = ref({
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
categoryId: 0,
|
||||||
|
sortWeight: 0,
|
||||||
|
status: false,
|
||||||
|
categoryOptions: []
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchRule = reactive({
|
||||||
|
createdAt: [
|
||||||
|
{ validator: (rule, value, callback) => {
|
||||||
|
if (searchInfo.value.startCreatedAt && !searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写结束日期'))
|
||||||
|
} else if (!searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写开始日期'))
|
||||||
|
} else if (searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt && (searchInfo.value.startCreatedAt.getTime() === searchInfo.value.endCreatedAt.getTime() || searchInfo.value.startCreatedAt.getTime() > searchInfo.value.endCreatedAt.getTime())) {
|
||||||
|
callback(new Error('开始日期应当早于结束日期'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}, trigger: 'change' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
const elSearchFormRef = ref()
|
||||||
|
|
||||||
|
// =========== 表格控制部分 ===========
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
const cateoryList = ref([])
|
||||||
|
|
||||||
|
const formatCategory = (id) => {
|
||||||
|
for (let i = 0; i < cateoryList.value.length; i++) {
|
||||||
|
if (cateoryList.value[i].ID === id) {
|
||||||
|
return cateoryList.value[i].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const onSubmit = () => {
|
||||||
|
elSearchFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
if (searchInfo.value.status === '') {
|
||||||
|
searchInfo.value.status = null
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页面容量
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCategoryData = async() => {
|
||||||
|
const resp = await getProductCategoryList({ page: 1, pageSize: 100 })
|
||||||
|
if (resp.code === 0 && resp.data.list) {
|
||||||
|
cateoryList.value = resp.data.list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategoryData()
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getProductList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// ============== 表格控制部分结束 ===============
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
const setOptions = async() => {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
setOptions()
|
||||||
|
|
||||||
|
// 多选数据
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
// 多选
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteRow = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteProductFunc(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选删除
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const IDs = []
|
||||||
|
if (multipleSelection.value.length === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择要删除的数据'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
multipleSelection.value &&
|
||||||
|
multipleSelection.value.map(item => {
|
||||||
|
IDs.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteProductByIds({ IDs })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === IDs.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行为控制标记(弹窗内部需要增还是改)
|
||||||
|
const type = ref('')
|
||||||
|
|
||||||
|
// 更新行
|
||||||
|
const updateProductFunc = async(row) => {
|
||||||
|
const res = await findProduct({ ID: row.ID })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproduct
|
||||||
|
formData.value.categoryOptions = cateoryList.value
|
||||||
|
console.log(formData)
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteProductFunc = async (row) => {
|
||||||
|
const res = await deleteProduct({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗控制标记
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
|
||||||
|
// 查看详情控制标记
|
||||||
|
const detailShow = ref(false)
|
||||||
|
|
||||||
|
// 打开详情弹窗
|
||||||
|
const openDetailShow = () => {
|
||||||
|
detailShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开详情
|
||||||
|
const getDetails = async (row) => {
|
||||||
|
// 打开弹窗
|
||||||
|
const res = await findProduct({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproduct
|
||||||
|
openDetailShow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情弹窗
|
||||||
|
const closeDetailShow = () => {
|
||||||
|
detailShow.value = false
|
||||||
|
formData.value = {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
categoryId: 0,
|
||||||
|
sortWeight: 0,
|
||||||
|
status: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
categoryId: 0,
|
||||||
|
sortWeight: 0,
|
||||||
|
status: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 弹窗确定
|
||||||
|
const enterDialog = async() => {
|
||||||
|
elFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createProduct(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateProduct(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createProduct(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-form-box">
|
||||||
|
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="编码:" prop="code">
|
||||||
|
<el-input v-model="formData.code" :clearable="true" placeholder="请输入编码" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称:" prop="name">
|
||||||
|
<el-input v-model="formData.name" :clearable="true" placeholder="请输入名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分类:" prop="categoryId">
|
||||||
|
<el-input v-model.number="formData.categoryId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:" prop="sortWeight">
|
||||||
|
<el-input v-model.number="formData.sortWeight" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:" prop="status">
|
||||||
|
<el-switch v-model="formData.status" active-color="#13ce66" inactive-color="#ff4949" active-text="开启" inactive-text="关闭" clearable ></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="save">保存</el-button>
|
||||||
|
<el-button type="primary" @click="back">返回</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createProduct,
|
||||||
|
updateProduct,
|
||||||
|
findProduct
|
||||||
|
} from '@/api/product'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ProductForm'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动获取字典
|
||||||
|
import { getDictFunc } from '@/utils/format'
|
||||||
|
import { useRoute, useRouter } from "vue-router"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const type = ref('')
|
||||||
|
const formData = ref({
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
categoryId: 0,
|
||||||
|
sortWeight: 0,
|
||||||
|
status: false,
|
||||||
|
})
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
|
||||||
|
// 初始化方法
|
||||||
|
const init = async () => {
|
||||||
|
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
|
||||||
|
if (route.query.id) {
|
||||||
|
const res = await findProduct({ ID: route.query.id })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproduct
|
||||||
|
type.value = 'update'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type.value = 'create'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
// 保存按钮
|
||||||
|
const save = async() => {
|
||||||
|
elFormRef.value?.validate( async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createProduct(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateProduct(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createProduct(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
const back = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,341 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
|
||||||
|
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
||||||
|
@click="onDelete">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table ref="multipleTable" style="width: 100%" tooltip-effect="dark" :data="tableData" row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column align="left" label="ID" prop="ID" width="120" />
|
||||||
|
<el-table-column align="left" label="父级ID" prop="parentId" width="120" />
|
||||||
|
<el-table-column align="left" label="分类名称" prop="name" width="120" />
|
||||||
|
<el-table-column align="left" label="操作" fixed="right" min-width="240">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" link class="table-button" @click="getDetails(scope.row)">
|
||||||
|
<el-icon style="margin-right: 5px">
|
||||||
|
<InfoFilled />
|
||||||
|
</el-icon>
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link icon="edit" class="table-button"
|
||||||
|
@click="updateProductCategoryFunc(scope.row)">变更</el-button>
|
||||||
|
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination layout="total, sizes, prev, pager, next, jumper" :current-page="page" :page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]" :total="total" @current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer size="800" v-model="dialogFormVisible" :show-close="false" :before-close="closeDialog">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">{{type==='create'?'添加':'修改'}}</span>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="enterDialog">确 定</el-button>
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="父级ID:" prop="parentId">
|
||||||
|
<el-input v-model.number="formData.parentId" :clearable="true" placeholder="请输入父级ID" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分类名称:" prop="name">
|
||||||
|
<el-input v-model="formData.name" :clearable="true" placeholder="请输入分类名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-drawer size="800" v-model="detailShow" :before-close="closeDetailShow" destroy-on-close>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">查看详情</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="ID">
|
||||||
|
{{ formData.ID }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="父级ID">
|
||||||
|
{{ formData.parentId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="分类名称">
|
||||||
|
{{ formData.name }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">
|
||||||
|
{{ formatDate(formData.createdAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="更新时间">
|
||||||
|
{{ formatDate(formData.updatedAt) }}
|
||||||
|
</el-descriptions-item></el-descriptions>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createProductCategory,
|
||||||
|
deleteProductCategory,
|
||||||
|
deleteProductCategoryByIds,
|
||||||
|
updateProductCategory,
|
||||||
|
findProductCategory,
|
||||||
|
getProductCategoryList
|
||||||
|
} from '@/api/productCategory'
|
||||||
|
|
||||||
|
// 全量引入格式化工具 请按需保留
|
||||||
|
import { getDictFunc, formatDate, formatBoolean, filterDict, ReturnArrImg, onDownloadFile } from '@/utils/format'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ProductCategory'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动化生成的字典(可能为空)以及字段
|
||||||
|
const formData = ref({
|
||||||
|
name: '',
|
||||||
|
parentId: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchRule = reactive({
|
||||||
|
createdAt: [
|
||||||
|
{ validator: (rule, value, callback) => {
|
||||||
|
if (searchInfo.value.startCreatedAt && !searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写结束日期'))
|
||||||
|
} else if (!searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写开始日期'))
|
||||||
|
} else if (searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt && (searchInfo.value.startCreatedAt.getTime() === searchInfo.value.endCreatedAt.getTime() || searchInfo.value.startCreatedAt.getTime() > searchInfo.value.endCreatedAt.getTime())) {
|
||||||
|
callback(new Error('开始日期应当早于结束日期'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}, trigger: 'change' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
const elSearchFormRef = ref()
|
||||||
|
|
||||||
|
// =========== 表格控制部分 ===========
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const onSubmit = () => {
|
||||||
|
elSearchFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页面容量
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getProductCategoryList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// ============== 表格控制部分结束 ===============
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
const setOptions = async() => {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
setOptions()
|
||||||
|
|
||||||
|
// 多选数据
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
// 多选
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteRow = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteProductCategoryFunc(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选删除
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const IDs = []
|
||||||
|
if (multipleSelection.value.length === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择要删除的数据'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
multipleSelection.value &&
|
||||||
|
multipleSelection.value.map(item => {
|
||||||
|
IDs.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteProductCategoryByIds({ IDs })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === IDs.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行为控制标记(弹窗内部需要增还是改)
|
||||||
|
const type = ref('')
|
||||||
|
|
||||||
|
// 更新行
|
||||||
|
const updateProductCategoryFunc = async(row) => {
|
||||||
|
const res = await findProductCategory({ ID: row.ID })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproductCategory
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteProductCategoryFunc = async(row) => {
|
||||||
|
const res = await deleteProductCategory({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗控制标记
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
|
||||||
|
// 查看详情控制标记
|
||||||
|
const detailShow = ref(false)
|
||||||
|
|
||||||
|
// 打开详情弹窗
|
||||||
|
const openDetailShow = () => {
|
||||||
|
detailShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开详情
|
||||||
|
const getDetails = async(row) => {
|
||||||
|
// 打开弹窗
|
||||||
|
const res = await findProductCategory({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproductCategory
|
||||||
|
openDetailShow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情弹窗
|
||||||
|
const closeDetailShow = () => {
|
||||||
|
detailShow.value = false
|
||||||
|
formData.value = {
|
||||||
|
name: '',
|
||||||
|
parentId: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
name: '',
|
||||||
|
parentId: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 弹窗确定
|
||||||
|
const enterDialog = async() => {
|
||||||
|
elFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createProductCategory(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateProductCategory(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createProductCategory(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-form-box">
|
||||||
|
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="父级Id:" prop="parentId">
|
||||||
|
<el-input v-model.number="formData.parentId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分类名称:" prop="name">
|
||||||
|
<el-input v-model="formData.name" :clearable="true" placeholder="请输入name字段" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="save">保存</el-button>
|
||||||
|
<el-button type="primary" @click="back">返回</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createProductCategory,
|
||||||
|
updateProductCategory,
|
||||||
|
findProductCategory
|
||||||
|
} from '@/api/productCategory'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ProductCategoryForm'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动获取字典
|
||||||
|
import { getDictFunc } from '@/utils/format'
|
||||||
|
import { useRoute, useRouter } from "vue-router"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const type = ref('')
|
||||||
|
const formData = ref({
|
||||||
|
name: '',
|
||||||
|
parentId: 0,
|
||||||
|
})
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
|
||||||
|
// 初始化方法
|
||||||
|
const init = async () => {
|
||||||
|
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
|
||||||
|
if (route.query.id) {
|
||||||
|
const res = await findProductCategory({ ID: route.query.id })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproductCategory
|
||||||
|
type.value = 'update'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type.value = 'create'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
// 保存按钮
|
||||||
|
const save = async() => {
|
||||||
|
elFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createProductCategory(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateProductCategory(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createProductCategory(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
const back = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,497 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" class="demo-form-inline" :rules="searchRule"
|
||||||
|
@keyup.enter="onSubmit">
|
||||||
|
<el-form-item label="创建日期" prop="createdAt">
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
创建日期
|
||||||
|
<el-tooltip content="搜索范围是开始日期(包含)至结束日期(不包含)">
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<el-date-picker v-model="searchInfo.startCreatedAt" type="datetime" placeholder="开始日期"
|
||||||
|
:disabled-date="time=> searchInfo.endCreatedAt ? time.getTime() > searchInfo.endCreatedAt.getTime() : false"></el-date-picker>
|
||||||
|
—
|
||||||
|
<el-date-picker v-model="searchInfo.endCreatedAt" type="datetime" placeholder="结束日期"
|
||||||
|
:disabled-date="time=> searchInfo.startCreatedAt ? time.getTime() < searchInfo.startCreatedAt.getTime() : false"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="search" @click="onSubmit">查询</el-button>
|
||||||
|
<el-button icon="refresh" @click="onReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
|
||||||
|
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
||||||
|
@click="onDelete">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table ref="multipleTable" style="width: 100%" tooltip-effect="dark" :data="tableData" row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column align="left" label="ID" prop="ID" width="80" />
|
||||||
|
<el-table-column align="left" label="产品" prop="productId" width="120">
|
||||||
|
<template #default="scope">{{ formatProduct(scope.row.productId) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="发布日期" width="100">
|
||||||
|
<template #default="scope">{{ formatOnlyDate(scope.row.publishDate) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="规格" prop="norm" width="120" />
|
||||||
|
<el-table-column align="left" label="地区" prop="areaFullname" width="120" />
|
||||||
|
<el-table-column align="left" label="价格类型" prop="priceType" width="80" />
|
||||||
|
<el-table-column align="left" label="单位" prop="priceUnit" width="60" />
|
||||||
|
<el-table-column align="left" label="最低价格" prop="priceMin" width="120" />
|
||||||
|
<el-table-column align="left" label="最高价格" prop="priceMax" width="120" />
|
||||||
|
<el-table-column align="left" label="状态" prop="status" width="80">
|
||||||
|
<template #default="scope">{{ formatBoolean(scope.row.status) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="备注" prop="remark" width="120" />
|
||||||
|
|
||||||
|
<el-table-column align="left" label="创建时间" width="180">
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.createdAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column align="left" label="操作" fixed="right" min-width="240">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" link class="table-button" @click="getDetails(scope.row)">
|
||||||
|
<el-icon style="margin-right: 5px">
|
||||||
|
<InfoFilled />
|
||||||
|
</el-icon>
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link icon="edit" class="table-button"
|
||||||
|
@click="updateProductPriceFunc(scope.row)">变更</el-button>
|
||||||
|
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination layout="total, sizes, prev, pager, next, jumper" :current-page="page" :page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]" :total="total" @current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer size="800" v-model="dialogFormVisible" :show-close="false" :before-close="closeDialog">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">{{type==='create'?'添加':'修改'}}</span>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="enterDialog">确 定</el-button>
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="产品Id:" prop="productId">
|
||||||
|
<el-input v-model.number="formData.productId" :clearable="true" placeholder="请输入产品Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="发布日期:" prop="publishDate">
|
||||||
|
<el-date-picker v-model="formData.publishDate" type="date" style="width:100%" placeholder="选择日期"
|
||||||
|
:clearable="true" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格:" prop="norm">
|
||||||
|
<el-input v-model="formData.norm" :clearable="true" placeholder="请输入规格" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区Id:" prop="areaId">
|
||||||
|
<el-input v-model.number="formData.areaId" :clearable="true" placeholder="请输入地区Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区:" prop="areaFullname">
|
||||||
|
<el-input v-model="formData.areaFullname" :clearable="true" placeholder="请输入地区" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="价格类型:" prop="priceType">
|
||||||
|
<el-input v-model="formData.priceType" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="单位:" prop="priceUnit">
|
||||||
|
<el-input v-model="formData.priceUnit" :clearable="true" placeholder="请输入单位" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="最低价格:" prop="priceMin">
|
||||||
|
<el-input-number v-model="formData.priceMin" style="width:100%" :precision="2" :clearable="true" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="最高价格:" prop="priceMax">
|
||||||
|
<el-input-number v-model="formData.priceMax" style="width:100%" :precision="2" :clearable="true" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:" prop="status">
|
||||||
|
<el-switch v-model="formData.status" active-color="#13ce66" inactive-color="#ff4949" active-text="是"
|
||||||
|
inactive-text="否" clearable></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注:" prop="remark">
|
||||||
|
<el-input v-model="formData.remark" :clearable="true" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-drawer size="800" v-model="detailShow" :before-close="closeDetailShow" destroy-on-close>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">查看详情</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="ID">
|
||||||
|
{{ formData.ID }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="产品">
|
||||||
|
{{ formatProduct(formData.productId) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="发布日期">
|
||||||
|
{{ formatOnlyDate(formData.publishDate) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="规格">
|
||||||
|
{{ formData.norm }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区Id">
|
||||||
|
{{ formData.areaId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区">
|
||||||
|
{{ formData.areaFullname }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="价格类型">
|
||||||
|
{{ formData.priceType }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="单位">
|
||||||
|
{{ formData.priceUnit }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="最低价格">
|
||||||
|
{{ formData.priceMin }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="最高价格">
|
||||||
|
{{ formData.priceMax }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
{{ formatBoolean(formData.status) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="备注">
|
||||||
|
{{ formData.remark }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">
|
||||||
|
{{ formatDate(formData.createdAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="更新时间">
|
||||||
|
{{ formatDate(formData.updatedAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getProductList } from '@/api/product'
|
||||||
|
import {
|
||||||
|
createProductPrice,
|
||||||
|
deleteProductPrice,
|
||||||
|
deleteProductPriceByIds,
|
||||||
|
updateProductPrice,
|
||||||
|
findProductPrice,
|
||||||
|
getProductPriceList
|
||||||
|
} from '@/api/productPrice'
|
||||||
|
|
||||||
|
// 全量引入格式化工具 请按需保留
|
||||||
|
import { formatDate, formatOnlyDate, formatBoolean } from '@/utils/format'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ProductPrice'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动化生成的字典(可能为空)以及字段
|
||||||
|
const formData = ref({
|
||||||
|
productId: 0,
|
||||||
|
publishDate: new Date(),
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
priceType: false,
|
||||||
|
priceUnit: '',
|
||||||
|
priceMin: 0,
|
||||||
|
priceMax: 0,
|
||||||
|
status: false,
|
||||||
|
remark: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchRule = reactive({
|
||||||
|
createdAt: [
|
||||||
|
{ validator: (rule, value, callback) => {
|
||||||
|
if (searchInfo.value.startCreatedAt && !searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写结束日期'))
|
||||||
|
} else if (!searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写开始日期'))
|
||||||
|
} else if (searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt && (searchInfo.value.startCreatedAt.getTime() === searchInfo.value.endCreatedAt.getTime() || searchInfo.value.startCreatedAt.getTime() > searchInfo.value.endCreatedAt.getTime())) {
|
||||||
|
callback(new Error('开始日期应当早于结束日期'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}, trigger: 'change' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
const elSearchFormRef = ref()
|
||||||
|
|
||||||
|
// =========== 表格控制部分 ===========
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
const productList = ref([])
|
||||||
|
|
||||||
|
const formatProduct = (id) => {
|
||||||
|
for (let i = 0; i < productList.value.length; i++) {
|
||||||
|
if (productList.value[i].ID === id) {
|
||||||
|
return productList.value[i].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const onSubmit = () => {
|
||||||
|
elSearchFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
if (searchInfo.value.priceType === '') {
|
||||||
|
searchInfo.value.priceType = null
|
||||||
|
}
|
||||||
|
if (searchInfo.value.status === '') {
|
||||||
|
searchInfo.value.status = null
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页面容量
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProductData = async() => {
|
||||||
|
const resp = await getProductList({ page: 1, pageSize: 100 })
|
||||||
|
if (resp.code === 0 && resp.data.list) {
|
||||||
|
productList.value = resp.data.list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getProductData()
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getProductPriceList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// ============== 表格控制部分结束 ===============
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
const setOptions = async() => {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
setOptions()
|
||||||
|
|
||||||
|
// 多选数据
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
// 多选
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteRow = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteProductPriceFunc(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选删除
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const IDs = []
|
||||||
|
if (multipleSelection.value.length === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择要删除的数据'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
multipleSelection.value &&
|
||||||
|
multipleSelection.value.map(item => {
|
||||||
|
IDs.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteProductPriceByIds({ IDs })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === IDs.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行为控制标记(弹窗内部需要增还是改)
|
||||||
|
const type = ref('')
|
||||||
|
|
||||||
|
// 更新行
|
||||||
|
const updateProductPriceFunc = async(row) => {
|
||||||
|
const res = await findProductPrice({ ID: row.ID })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproductPrice
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteProductPriceFunc = async(row) => {
|
||||||
|
const res = await deleteProductPrice({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗控制标记
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
|
||||||
|
// 查看详情控制标记
|
||||||
|
const detailShow = ref(false)
|
||||||
|
|
||||||
|
// 打开详情弹窗
|
||||||
|
const openDetailShow = () => {
|
||||||
|
detailShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开详情
|
||||||
|
const getDetails = async(row) => {
|
||||||
|
// 打开弹窗
|
||||||
|
const res = await findProductPrice({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproductPrice
|
||||||
|
openDetailShow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情弹窗
|
||||||
|
const closeDetailShow = () => {
|
||||||
|
detailShow.value = false
|
||||||
|
formData.value = {
|
||||||
|
productId: 0,
|
||||||
|
publishDate: new Date(),
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
priceType: false,
|
||||||
|
priceUnit: '',
|
||||||
|
priceMin: 0,
|
||||||
|
priceMax: 0,
|
||||||
|
status: false,
|
||||||
|
remark: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
productId: 0,
|
||||||
|
publishDate: new Date(),
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
priceType: false,
|
||||||
|
priceUnit: '',
|
||||||
|
priceMin: 0,
|
||||||
|
priceMax: 0,
|
||||||
|
status: false,
|
||||||
|
remark: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 弹窗确定
|
||||||
|
const enterDialog = async() => {
|
||||||
|
elFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createProductPrice(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateProductPrice(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createProductPrice(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-form-box">
|
||||||
|
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="产品Id:" prop="productId">
|
||||||
|
<el-input v-model.number="formData.productId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="发布日期:" prop="publishDate">
|
||||||
|
<el-date-picker v-model="formData.publishDate" type="date" placeholder="选择日期" :clearable="true"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格:" prop="norm">
|
||||||
|
<el-input v-model="formData.norm" :clearable="true" placeholder="请输入规格" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区Id:" prop="areaId">
|
||||||
|
<el-input v-model.number="formData.areaId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区:" prop="areaFullname">
|
||||||
|
<el-input v-model="formData.areaFullname" :clearable="true" placeholder="请输入地区" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="价格类型:" prop="priceType">
|
||||||
|
<el-switch v-model="formData.priceType" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="单位:" prop="priceUnit">
|
||||||
|
<el-input v-model="formData.priceUnit" :clearable="true" placeholder="请输入单位" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="最低价格:" prop="priceMin">
|
||||||
|
<el-input-number v-model="formData.priceMin" :precision="2" :clearable="true"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="最高价格:" prop="priceMax">
|
||||||
|
<el-input-number v-model="formData.priceMax" :precision="2" :clearable="true"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:" prop="status">
|
||||||
|
<el-switch v-model="formData.status" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注:" prop="remark">
|
||||||
|
<el-input v-model="formData.remark" :clearable="true" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="save">保存</el-button>
|
||||||
|
<el-button type="primary" @click="back">返回</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createProductPrice,
|
||||||
|
updateProductPrice,
|
||||||
|
findProductPrice
|
||||||
|
} from '@/api/productPrice'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ProductPriceForm'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动获取字典
|
||||||
|
import { getDictFunc } from '@/utils/format'
|
||||||
|
import { useRoute, useRouter } from "vue-router"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const type = ref('')
|
||||||
|
const formData = ref({
|
||||||
|
productId: 0,
|
||||||
|
publishDate: new Date(),
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
priceType: false,
|
||||||
|
priceUnit: '',
|
||||||
|
priceMin: 0,
|
||||||
|
priceMax: 0,
|
||||||
|
status: false,
|
||||||
|
remark: '',
|
||||||
|
})
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
|
||||||
|
// 初始化方法
|
||||||
|
const init = async () => {
|
||||||
|
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
|
||||||
|
if (route.query.id) {
|
||||||
|
const res = await findProductPrice({ ID: route.query.id })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reproductPrice
|
||||||
|
type.value = 'update'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type.value = 'create'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
// 保存按钮
|
||||||
|
const save = async() => {
|
||||||
|
elFormRef.value?.validate( async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createProductPrice(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateProductPrice(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createProductPrice(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
const back = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,641 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" class="demo-form-inline" :rules="searchRule"
|
||||||
|
@keyup.enter="onSubmit">
|
||||||
|
<el-form-item label="创建日期" prop="createdAt">
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
创建日期
|
||||||
|
<el-tooltip content="搜索范围是开始日期(包含)至结束日期(不包含)">
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<el-date-picker v-model="searchInfo.startCreatedAt" type="datetime" placeholder="开始日期"
|
||||||
|
:disabled-date="time=> searchInfo.endCreatedAt ? time.getTime() > searchInfo.endCreatedAt.getTime() : false"></el-date-picker>
|
||||||
|
—
|
||||||
|
<el-date-picker v-model="searchInfo.endCreatedAt" type="datetime" placeholder="结束日期"
|
||||||
|
:disabled-date="time=> searchInfo.startCreatedAt ? time.getTime() < searchInfo.startCreatedAt.getTime() : false"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="search" @click="onSubmit">查询</el-button>
|
||||||
|
<el-button icon="refresh" @click="onReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
|
||||||
|
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
||||||
|
@click="onDelete">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table ref="multipleTable" style="width: 100%" tooltip-effect="dark" :data="tableData" row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column align="left" label="ID" prop="ID" width="60" />
|
||||||
|
<el-table-column align="left" label="产品" prop="productId" width="80">
|
||||||
|
<template #default="scope">{{ formatProduct(scope.row.productId) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="产品价格Id" prop="productPriceId" width="120" />
|
||||||
|
<el-table-column align="left" label="公司价格Id" prop="companyPriceId" width="120" />
|
||||||
|
<el-table-column align="left" label="规格" prop="norm" width="120" />
|
||||||
|
<el-table-column align="left" label="地区Id" prop="areaId" width="80" />
|
||||||
|
<el-table-column align="left" label="地区" prop="areaFullname" width="120" />
|
||||||
|
<el-table-column align="left" label="品牌" prop="producter" width="120" />
|
||||||
|
<el-table-column align="left" label="价格类型" prop="priceType" width="80">
|
||||||
|
<template #default="scope">{{ formatPriceType(scope.row.priceType) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="价格" prop="price" width="120" />
|
||||||
|
<el-table-column align="left" label="单位" prop="priceUnit" width="60" />
|
||||||
|
<el-table-column align="left" label="公司" prop="companyName" width="220" />
|
||||||
|
<el-table-column align="left" label="发布日期" width="120">
|
||||||
|
<template #default="scope">{{ formatOnlyDate(scope.row.publishDate) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column align="left" label="创建时间" width="180">
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.createdAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="更新时间" width="180">
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.updatedAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="爬虫表.Id" prop="crawTableId" width="220">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" link @click="getCrawlData(scope.row)">
|
||||||
|
{{ scope.row.crawTable }}.{{ scope.row.crawTableId }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column align="left" label="操作" fixed="right" min-width="240">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" link class="table-button" @click="getDetails(scope.row)">
|
||||||
|
<el-icon style="margin-right: 5px">
|
||||||
|
<InfoFilled />
|
||||||
|
</el-icon>
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link icon="edit" class="table-button"
|
||||||
|
@click="updateSourcePriceFunc(scope.row)">变更</el-button>
|
||||||
|
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination layout="total, sizes, prev, pager, next, jumper" :current-page="page" :page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]" :total="total" @current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer size="800" v-model="dialogFormVisible" :show-close="false" :before-close="closeDialog">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">{{type==='create'?'添加':'修改'}}</span>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="enterDialog">确 定</el-button>
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="爬虫表:" prop="crawTable">
|
||||||
|
<el-input v-model="formData.crawTable" :clearable="true" placeholder="请输入爬虫表" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="爬虫表Id:" prop="crawTableId">
|
||||||
|
<el-input v-model.number="formData.crawTableId" :clearable="true" placeholder="请输入爬虫表Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品Id:" prop="productId">
|
||||||
|
<el-input v-model.number="formData.productId" :clearable="true" placeholder="请输入产品Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品价格Id:" prop="productPriceId">
|
||||||
|
<el-input v-model.number="formData.productPriceId" :clearable="true" placeholder="请输入产品价格Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="公司价格Id:" prop="companyPriceId">
|
||||||
|
<el-input v-model.number="formData.companyPriceId" :clearable="true" placeholder="请输入公司价格Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格:" prop="norm">
|
||||||
|
<el-input v-model="formData.norm" :clearable="true" placeholder="请输入规格" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区Id:" prop="areaId">
|
||||||
|
<el-input v-model.number="formData.areaId" :clearable="true" placeholder="请输入地区Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区:" prop="areaFullname">
|
||||||
|
<el-input v-model="formData.areaFullname" :clearable="true" placeholder="请输入地区" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="品牌:" prop="producter">
|
||||||
|
<el-input v-model="formData.producter" :clearable="true" placeholder="请输入品牌" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="价格类型:" prop="priceType">
|
||||||
|
<el-switch v-model="formData.priceType" active-color="#13ce66" inactive-color="#ff4949" active-text="是"
|
||||||
|
inactive-text="否" clearable></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="价格:" prop="price">
|
||||||
|
<el-input-number v-model="formData.price" style="width:100%" :precision="2" :clearable="true" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="单位:" prop="priceUnit">
|
||||||
|
<el-input v-model="formData.priceUnit" :clearable="true" placeholder="请输入单位" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="公司:" prop="companyName">
|
||||||
|
<el-input v-model="formData.companyName" :clearable="true" placeholder="请输入公司" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="发布日期:" prop="publishDate">
|
||||||
|
<el-date-picker v-model="formData.publishDate" type="date" style="width:100%" placeholder="选择日期"
|
||||||
|
:clearable="true" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-drawer size="800" v-model="detailShow" :before-close="closeDetailShow" destroy-on-close>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">查看详情</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="ID">
|
||||||
|
{{ formData.ID }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="爬虫表">
|
||||||
|
{{ formData.crawTable }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="爬虫表Id">
|
||||||
|
{{ formData.crawTableId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="产品Id">
|
||||||
|
{{ formatProduct(formData.productId) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="产品价格Id">
|
||||||
|
{{ formData.productPriceId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="公司价格Id">
|
||||||
|
{{ formData.companyPriceId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="规格">
|
||||||
|
{{ formData.norm }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区Id">
|
||||||
|
{{ formData.areaId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区">
|
||||||
|
{{ formData.areaFullname }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="品牌">
|
||||||
|
{{ formData.producter }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="价格类型">
|
||||||
|
{{ formatPriceType(formData.priceType) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="价格">
|
||||||
|
{{ formData.price }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="单位">
|
||||||
|
{{ formData.priceUnit }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="公司">
|
||||||
|
{{ formData.companyName }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="发布日期">
|
||||||
|
{{ formatOnlyDate(formData.publishDate) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">
|
||||||
|
{{ formatDate(formData.createdAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="更新时间">
|
||||||
|
{{ formatDate(formData.updatedAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-drawer size="800" v-model="crawl100ppiDetail" :before-close="closeCrawl100ppiShow" destroy-on-close>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">查看爬虫数据详情</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="ID">
|
||||||
|
{{ formData.ID }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="产品Code">
|
||||||
|
{{ formData.code }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="产品名称">
|
||||||
|
{{ formData.name }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="规格">
|
||||||
|
{{ formData.norm }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="品牌">
|
||||||
|
{{ formData.producer }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="价格类型">
|
||||||
|
{{ formData.priceType }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="单位">
|
||||||
|
{{ formData.priceUnit }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="价格">
|
||||||
|
{{ formData.price }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区">
|
||||||
|
{{ formData.areaDetail }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="公司">
|
||||||
|
{{ formData.dealer }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="发布日期">
|
||||||
|
{{ formData.publishDate }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="数据唯一值">
|
||||||
|
{{ formData.uniqueCode }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="抓取时间">
|
||||||
|
{{ formData.crawlTime }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
{{ formData.status }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="同步失败原因">
|
||||||
|
{{ formData.syncError }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">
|
||||||
|
{{ formatDate(formData.createdAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="更新时间">
|
||||||
|
{{ formatDate(formData.updatedAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { findCrawl100ppi } from '@/api/crawl100ppi'
|
||||||
|
import { getProductList } from '@/api/product'
|
||||||
|
import {
|
||||||
|
createSourcePrice,
|
||||||
|
deleteSourcePrice,
|
||||||
|
deleteSourcePriceByIds,
|
||||||
|
updateSourcePrice,
|
||||||
|
findSourcePrice,
|
||||||
|
getSourcePriceList
|
||||||
|
} from '@/api/sourcePrice'
|
||||||
|
|
||||||
|
// 全量引入格式化工具 请按需保留
|
||||||
|
import { formatDate, formatOnlyDate, formatPriceType } from '@/utils/format'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SourcePrice'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动化生成的字典(可能为空)以及字段
|
||||||
|
const formData = ref({
|
||||||
|
crawTable: '',
|
||||||
|
crawTableId: 0,
|
||||||
|
productId: 0,
|
||||||
|
productPriceId: 0,
|
||||||
|
companyPriceId: 0,
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
producter: '',
|
||||||
|
priceType: false,
|
||||||
|
price: 0,
|
||||||
|
priceUnit: '',
|
||||||
|
companyName: '',
|
||||||
|
publishDate: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchRule = reactive({
|
||||||
|
createdAt: [
|
||||||
|
{ validator: (rule, value, callback) => {
|
||||||
|
if (searchInfo.value.startCreatedAt && !searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写结束日期'))
|
||||||
|
} else if (!searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写开始日期'))
|
||||||
|
} else if (searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt && (searchInfo.value.startCreatedAt.getTime() === searchInfo.value.endCreatedAt.getTime() || searchInfo.value.startCreatedAt.getTime() > searchInfo.value.endCreatedAt.getTime())) {
|
||||||
|
callback(new Error('开始日期应当早于结束日期'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}, trigger: 'change' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
const elSearchFormRef = ref()
|
||||||
|
|
||||||
|
// =========== 表格控制部分 ===========
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
const productList = ref([])
|
||||||
|
|
||||||
|
const formatProduct = (id) => {
|
||||||
|
for (let i = 0; i < productList.value.length; i++) {
|
||||||
|
if (productList.value[i].ID === id) {
|
||||||
|
return productList.value[i].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
const formmatStatus = (stauts) => {
|
||||||
|
switch (status) {
|
||||||
|
case '1': return '待同步'
|
||||||
|
case '2': return '已同步'
|
||||||
|
case '3': return '同步失败'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const onSubmit = () => {
|
||||||
|
elSearchFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
if (searchInfo.value.priceType === '') {
|
||||||
|
searchInfo.value.priceType = null
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页面容量
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProductData = async() => {
|
||||||
|
const resp = await getProductList({ page: 1, pageSize: 100 })
|
||||||
|
if (resp.code === 0 && resp.data.list) {
|
||||||
|
productList.value = resp.data.list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getProductData()
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getSourcePriceList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// ============== 表格控制部分结束 ===============
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
const setOptions = async() => {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
setOptions()
|
||||||
|
|
||||||
|
// 多选数据
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
// 多选
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteRow = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteSourcePriceFunc(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选删除
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const IDs = []
|
||||||
|
if (multipleSelection.value.length === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择要删除的数据'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
multipleSelection.value &&
|
||||||
|
multipleSelection.value.map(item => {
|
||||||
|
IDs.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteSourcePriceByIds({ IDs })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === IDs.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行为控制标记(弹窗内部需要增还是改)
|
||||||
|
const type = ref('')
|
||||||
|
|
||||||
|
// 更新行
|
||||||
|
const updateSourcePriceFunc = async(row) => {
|
||||||
|
const res = await findSourcePrice({ ID: row.ID })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resourcePrice
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteSourcePriceFunc = async(row) => {
|
||||||
|
const res = await deleteSourcePrice({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗控制标记
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
|
||||||
|
// 查看详情控制标记
|
||||||
|
const detailShow = ref(false)
|
||||||
|
|
||||||
|
const crawl100ppiDetail = ref(false)
|
||||||
|
|
||||||
|
// 打开详情弹窗
|
||||||
|
const openDetailShow = () => {
|
||||||
|
detailShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开详情
|
||||||
|
const getDetails = async(row) => {
|
||||||
|
// 打开弹窗
|
||||||
|
const res = await findSourcePrice({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resourcePrice
|
||||||
|
openDetailShow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情弹窗
|
||||||
|
const closeDetailShow = () => {
|
||||||
|
detailShow.value = false
|
||||||
|
formData.value = {
|
||||||
|
crawTable: '',
|
||||||
|
crawTableId: 0,
|
||||||
|
productId: 0,
|
||||||
|
productPriceId: 0,
|
||||||
|
companyPriceId: 0,
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
producter: '',
|
||||||
|
priceType: false,
|
||||||
|
price: 0,
|
||||||
|
priceUnit: '',
|
||||||
|
companyName: '',
|
||||||
|
publishDate: new Date(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openCrawl100ppiShow = () => {
|
||||||
|
crawl100ppiDetail.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情弹窗
|
||||||
|
const closeCrawl100ppiShow = () => {
|
||||||
|
crawl100ppiDetail.value = false
|
||||||
|
formData.value = {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
norm: '',
|
||||||
|
producer: '',
|
||||||
|
priceType: '',
|
||||||
|
priceUnit: '',
|
||||||
|
price: '',
|
||||||
|
areaDetail: '',
|
||||||
|
dealer: '',
|
||||||
|
publishDate: '',
|
||||||
|
uniqueCode: '',
|
||||||
|
crawlTime: '',
|
||||||
|
status: 0,
|
||||||
|
syncError: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 打开爬虫详情
|
||||||
|
const getCrawlData = async(row) => {
|
||||||
|
// 打开弹窗
|
||||||
|
if (row.crawTable === 'nzhq_crawl_100ppi') {
|
||||||
|
const res = await findCrawl100ppi({ ID: row.crawTableId })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.recrawl100ppi
|
||||||
|
openCrawl100ppiShow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
crawTable: '',
|
||||||
|
crawTableId: 0,
|
||||||
|
productId: 0,
|
||||||
|
productPriceId: 0,
|
||||||
|
companyPriceId: 0,
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
producter: '',
|
||||||
|
priceType: false,
|
||||||
|
price: 0,
|
||||||
|
priceUnit: '',
|
||||||
|
companyName: '',
|
||||||
|
publishDate: new Date(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 弹窗确定
|
||||||
|
const enterDialog = async() => {
|
||||||
|
elFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createSourcePrice(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateSourcePrice(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createSourcePrice(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-form-box">
|
||||||
|
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="爬虫表:" prop="crawTable">
|
||||||
|
<el-input v-model="formData.crawTable" :clearable="true" placeholder="请输入爬虫表" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="爬虫表Id:" prop="crawTableId">
|
||||||
|
<el-input v-model.number="formData.crawTableId" :clearable="true" placeholder="请输入爬虫表Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品Id:" prop="productId">
|
||||||
|
<el-input v-model.number="formData.productId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品价格Id:" prop="productPriceId">
|
||||||
|
<el-input v-model.number="formData.productPriceId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="公司价格Id:" prop="companyPriceId">
|
||||||
|
<el-input v-model.number="formData.companyPriceId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格:" prop="norm">
|
||||||
|
<el-input v-model="formData.norm" :clearable="true" placeholder="请输入规格" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区Id:" prop="areaId">
|
||||||
|
<el-input v-model.number="formData.areaId" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区:" prop="areaFullname">
|
||||||
|
<el-input v-model="formData.areaFullname" :clearable="true" placeholder="请输入地区" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="品牌:" prop="producter">
|
||||||
|
<el-input v-model="formData.producter" :clearable="true" placeholder="请输入品牌" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="价格类型:" prop="priceType">
|
||||||
|
<el-switch v-model="formData.priceType" active-color="#13ce66" inactive-color="#ff4949" active-text="是"
|
||||||
|
inactive-text="否" clearable></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="价格:" prop="price">
|
||||||
|
<el-input-number v-model="formData.price" :precision="2" :clearable="true"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="单位:" prop="priceUnit">
|
||||||
|
<el-input v-model="formData.priceUnit" :clearable="true" placeholder="请输入单位" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="公司:" prop="companyName">
|
||||||
|
<el-input v-model="formData.companyName" :clearable="true" placeholder="请输入公司" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="发布日期:" prop="publishDate">
|
||||||
|
<el-date-picker v-model="formData.publishDate" type="date" placeholder="选择日期"
|
||||||
|
:clearable="true"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="save">保存</el-button>
|
||||||
|
<el-button type="primary" @click="back">返回</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createSourcePrice,
|
||||||
|
updateSourcePrice,
|
||||||
|
findSourcePrice
|
||||||
|
} from '@/api/sourcePrice'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SourcePriceForm'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动获取字典
|
||||||
|
import { getDictFunc } from '@/utils/format'
|
||||||
|
import { useRoute, useRouter } from "vue-router"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const type = ref('')
|
||||||
|
const formData = ref({
|
||||||
|
crawTable: '',
|
||||||
|
crawTableId: 0,
|
||||||
|
productId: 0,
|
||||||
|
productPriceId: 0,
|
||||||
|
companyPriceId: 0,
|
||||||
|
norm: '',
|
||||||
|
areaId: 0,
|
||||||
|
areaFullname: '',
|
||||||
|
producter: '',
|
||||||
|
priceType: false,
|
||||||
|
price: 0,
|
||||||
|
priceUnit: '',
|
||||||
|
companyName: '',
|
||||||
|
publishDate: new Date(),
|
||||||
|
})
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
|
||||||
|
// 初始化方法
|
||||||
|
const init = async () => {
|
||||||
|
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
|
||||||
|
if (route.query.id) {
|
||||||
|
const res = await findSourcePrice({ ID: route.query.id })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resourcePrice
|
||||||
|
type.value = 'update'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type.value = 'create'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
// 保存按钮
|
||||||
|
const save = async() => {
|
||||||
|
elFormRef.value?.validate( async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createSourcePrice(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateSourcePrice(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createSourcePrice(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
const back = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,512 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form
|
||||||
|
ref="searchForm"
|
||||||
|
:inline="true"
|
||||||
|
:model="searchInfo"
|
||||||
|
>
|
||||||
|
<el-form-item label="路径">
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.path"
|
||||||
|
placeholder="路径"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述">
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.description"
|
||||||
|
placeholder="描述"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API组">
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.apiGroup"
|
||||||
|
placeholder="api组"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="请求">
|
||||||
|
<el-select
|
||||||
|
v-model="searchInfo.method"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in methodOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="`${item.label}(${item.value})`"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="search"
|
||||||
|
@click="onSubmit"
|
||||||
|
>查询</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="refresh"
|
||||||
|
@click="onReset"
|
||||||
|
>重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="openDialog('addApi')"
|
||||||
|
>新增</el-button>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=7&vd_source=f2640257c21e3b547a790461ed94875e')"
|
||||||
|
><VideoCameraFilled /></el-icon>
|
||||||
|
<el-button
|
||||||
|
icon="delete"
|
||||||
|
:disabled="!apis.length"
|
||||||
|
@click="onDelete"
|
||||||
|
>删除</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Refresh"
|
||||||
|
@click="onFresh"
|
||||||
|
>刷新缓存</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
@sort-change="sortChange"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="id"
|
||||||
|
min-width="60"
|
||||||
|
prop="ID"
|
||||||
|
sortable="custom"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="API路径"
|
||||||
|
min-width="150"
|
||||||
|
prop="path"
|
||||||
|
sortable="custom"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="API分组"
|
||||||
|
min-width="150"
|
||||||
|
prop="apiGroup"
|
||||||
|
sortable="custom"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="API简介"
|
||||||
|
min-width="150"
|
||||||
|
prop="description"
|
||||||
|
sortable="custom"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="请求"
|
||||||
|
min-width="150"
|
||||||
|
prop="method"
|
||||||
|
sortable="custom"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
{{ scope.row.method }} / {{ methodFilter(scope.row.method) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
fixed="right"
|
||||||
|
label="操作"
|
||||||
|
width="200"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="edit"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="editApiFunc(scope.row)"
|
||||||
|
>编辑</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="delete"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="deleteApiFunc(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:before-close="closeDialog"
|
||||||
|
:title="dialogTitle"
|
||||||
|
>
|
||||||
|
<warning-bar title="新增API,需要在角色管理内配置权限才可使用" />
|
||||||
|
<el-form
|
||||||
|
ref="apiForm"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="路径"
|
||||||
|
prop="path"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.path"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="请求"
|
||||||
|
prop="method"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="form.method"
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in methodOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="`${item.label}(${item.value})`"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="api分组"
|
||||||
|
prop="apiGroup"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.apiGroup"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="api简介"
|
||||||
|
prop="description"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.description"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
getApiById,
|
||||||
|
getApiList,
|
||||||
|
createApi,
|
||||||
|
updateApi,
|
||||||
|
deleteApi,
|
||||||
|
deleteApisByIds,
|
||||||
|
freshCasbin
|
||||||
|
} from '@/api/api'
|
||||||
|
import { toSQLLine } from '@/utils/stringFun'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { VideoCameraFilled } from '@element-plus/icons-vue'
|
||||||
|
import { toDoc } from '@/utils/doc'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Api',
|
||||||
|
})
|
||||||
|
|
||||||
|
const methodFilter = (value) => {
|
||||||
|
const target = methodOptions.value.filter(item => item.value === value)[0]
|
||||||
|
return target && `${target.label}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const apis = ref([])
|
||||||
|
const form = ref({
|
||||||
|
path: '',
|
||||||
|
apiGroup: '',
|
||||||
|
method: '',
|
||||||
|
description: ''
|
||||||
|
})
|
||||||
|
const methodOptions = ref([
|
||||||
|
{
|
||||||
|
value: 'POST',
|
||||||
|
label: '创建',
|
||||||
|
type: 'success'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'GET',
|
||||||
|
label: '查看',
|
||||||
|
type: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'PUT',
|
||||||
|
label: '更新',
|
||||||
|
type: 'warning'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'DELETE',
|
||||||
|
label: '删除',
|
||||||
|
type: 'danger'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const type = ref('')
|
||||||
|
const rules = ref({
|
||||||
|
path: [{ required: true, message: '请输入api路径', trigger: 'blur' }],
|
||||||
|
apiGroup: [
|
||||||
|
{ required: true, message: '请输入组名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
method: [
|
||||||
|
{ required: true, message: '请选择请求方式', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
description: [
|
||||||
|
{ required: true, message: '请输入api介绍', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
}
|
||||||
|
// 搜索
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
const sortChange = ({ prop, order }) => {
|
||||||
|
if (prop) {
|
||||||
|
if (prop === 'ID') {
|
||||||
|
prop = 'id'
|
||||||
|
}
|
||||||
|
searchInfo.value.orderKey = toSQLLine(prop)
|
||||||
|
searchInfo.value.desc = order === 'descending'
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getApiList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// 批量操作
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
apis.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const ids = apis.value.map(item => item.ID)
|
||||||
|
const res = await deleteApisByIds({ ids })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: res.msg
|
||||||
|
})
|
||||||
|
if (tableData.value.length === ids.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onFresh = async() => {
|
||||||
|
ElMessageBox.confirm('确定要刷新缓存吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const res = await freshCasbin()
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: res.msg
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const apiForm = ref(null)
|
||||||
|
const initForm = () => {
|
||||||
|
apiForm.value.resetFields()
|
||||||
|
form.value = {
|
||||||
|
path: '',
|
||||||
|
apiGroup: '',
|
||||||
|
method: '',
|
||||||
|
description: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogTitle = ref('新增Api')
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
const openDialog = (key) => {
|
||||||
|
switch (key) {
|
||||||
|
case 'addApi':
|
||||||
|
dialogTitle.value = '新增Api'
|
||||||
|
break
|
||||||
|
case 'edit':
|
||||||
|
dialogTitle.value = '编辑Api'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
type.value = key
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
const closeDialog = () => {
|
||||||
|
initForm()
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const editApiFunc = async(row) => {
|
||||||
|
const res = await getApiById({ id: row.ID })
|
||||||
|
form.value = res.data.api
|
||||||
|
openDialog('edit')
|
||||||
|
}
|
||||||
|
|
||||||
|
const enterDialog = async() => {
|
||||||
|
apiForm.value.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
switch (type.value) {
|
||||||
|
case 'addApi':
|
||||||
|
{
|
||||||
|
const res = await createApi(form.value)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '添加成功',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case 'edit':
|
||||||
|
{
|
||||||
|
const res = await updateApi(form.value)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '编辑成功',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
// eslint-disable-next-line no-lone-blocks
|
||||||
|
{
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '未知操作',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteApiFunc = async(row) => {
|
||||||
|
ElMessageBox.confirm('此操作将永久删除所有角色下该api, 是否继续?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async() => {
|
||||||
|
const res = await deleteApi(row)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功!'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.warning {
|
||||||
|
color: #dc143c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,468 @@
|
||||||
|
<template>
|
||||||
|
<div class="authority">
|
||||||
|
<warning-bar title="注:右上角头像下拉可切换角色" />
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="addAuthority(0)"
|
||||||
|
>新增角色</el-button>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=8&vd_source=f2640257c21e3b547a790461ed94875e')"
|
||||||
|
><VideoCameraFilled /></el-icon>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||||
|
row-key="authorityId"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
label="角色ID"
|
||||||
|
min-width="180"
|
||||||
|
prop="authorityId"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="角色名称"
|
||||||
|
min-width="180"
|
||||||
|
prop="authorityName"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="操作"
|
||||||
|
width="460"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="setting"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="openDrawer(scope.row)"
|
||||||
|
>设置权限</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="plus"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="addAuthority(scope.row.authorityId)"
|
||||||
|
>新增子角色</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="copy-document"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="copyAuthorityFunc(scope.row)"
|
||||||
|
>拷贝</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="edit"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="editAuthority(scope.row)"
|
||||||
|
>编辑</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="delete"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="deleteAuth(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<!-- 新增角色弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:title="dialogTitle"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="authorityForm"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="父级角色"
|
||||||
|
prop="parentId"
|
||||||
|
>
|
||||||
|
<el-cascader
|
||||||
|
v-model="form.parentId"
|
||||||
|
style="width:100%"
|
||||||
|
:disabled="dialogType==='add'"
|
||||||
|
:options="AuthorityOption"
|
||||||
|
:props="{ checkStrictly: true,label:'authorityName',value:'authorityId',disabled:'disabled',emitPath:false}"
|
||||||
|
:show-all-levels="false"
|
||||||
|
filterable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="角色ID"
|
||||||
|
prop="authorityId"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.authorityId"
|
||||||
|
:disabled="dialogType==='edit'"
|
||||||
|
autocomplete="off"
|
||||||
|
maxlength="15"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="角色姓名"
|
||||||
|
prop="authorityName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.authorityName"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-drawer
|
||||||
|
v-if="drawer"
|
||||||
|
v-model="drawer"
|
||||||
|
:with-header="false"
|
||||||
|
size="40%"
|
||||||
|
title="角色配置"
|
||||||
|
>
|
||||||
|
<el-tabs
|
||||||
|
:before-leave="autoEnter"
|
||||||
|
type="border-card"
|
||||||
|
>
|
||||||
|
<el-tab-pane label="角色菜单">
|
||||||
|
<Menus
|
||||||
|
ref="menus"
|
||||||
|
:row="activeRow"
|
||||||
|
@changeRow="changeRow"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="角色api">
|
||||||
|
<Apis
|
||||||
|
ref="apis"
|
||||||
|
:row="activeRow"
|
||||||
|
@changeRow="changeRow"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="资源权限">
|
||||||
|
<Datas
|
||||||
|
ref="datas"
|
||||||
|
:authority="tableData"
|
||||||
|
:row="activeRow"
|
||||||
|
@changeRow="changeRow"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
getAuthorityList,
|
||||||
|
deleteAuthority,
|
||||||
|
createAuthority,
|
||||||
|
updateAuthority,
|
||||||
|
copyAuthority
|
||||||
|
} from '@/api/authority'
|
||||||
|
|
||||||
|
import Menus from '@/view/superAdmin/authority/components/menus.vue'
|
||||||
|
import Apis from '@/view/superAdmin/authority/components/apis.vue'
|
||||||
|
import Datas from '@/view/superAdmin/authority/components/datas.vue'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { toDoc } from '@/utils/doc'
|
||||||
|
import { VideoCameraFilled } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Authority'
|
||||||
|
})
|
||||||
|
|
||||||
|
const mustUint = (rule, value, callback) => {
|
||||||
|
if (!/^[0-9]*[1-9][0-9]*$/.test(value)) {
|
||||||
|
return callback(new Error('请输入正整数'))
|
||||||
|
}
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthorityOption = ref([
|
||||||
|
{
|
||||||
|
authorityId: 0,
|
||||||
|
authorityName: '根角色'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const drawer = ref(false)
|
||||||
|
const dialogType = ref('add')
|
||||||
|
const activeRow = ref({})
|
||||||
|
|
||||||
|
const dialogTitle = ref('新增角色')
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
const apiDialogFlag = ref(false)
|
||||||
|
const copyForm = ref({})
|
||||||
|
|
||||||
|
const form = ref({
|
||||||
|
authorityId: 0,
|
||||||
|
authorityName: '',
|
||||||
|
parentId: 0
|
||||||
|
})
|
||||||
|
const rules = ref({
|
||||||
|
authorityId: [
|
||||||
|
{ required: true, message: '请输入角色ID', trigger: 'blur' },
|
||||||
|
{ validator: mustUint, trigger: 'blur', message: '必须为正整数' }
|
||||||
|
],
|
||||||
|
authorityName: [
|
||||||
|
{ required: true, message: '请输入角色名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
parentId: [
|
||||||
|
{ required: true, message: '请选择父角色', trigger: 'blur' },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(999)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getAuthorityList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
const changeRow = (key, value) => {
|
||||||
|
activeRow.value[key] = value
|
||||||
|
}
|
||||||
|
const menus = ref(null)
|
||||||
|
const apis = ref(null)
|
||||||
|
const datas = ref(null)
|
||||||
|
const autoEnter = (activeName, oldActiveName) => {
|
||||||
|
const paneArr = [menus, apis, datas]
|
||||||
|
if (oldActiveName) {
|
||||||
|
if (paneArr[oldActiveName].value.needConfirm) {
|
||||||
|
paneArr[oldActiveName].value.enterAndNext()
|
||||||
|
paneArr[oldActiveName].value.needConfirm = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 拷贝角色
|
||||||
|
const copyAuthorityFunc = (row) => {
|
||||||
|
setOptions()
|
||||||
|
dialogTitle.value = '拷贝角色'
|
||||||
|
dialogType.value = 'copy'
|
||||||
|
for (const k in form.value) {
|
||||||
|
form.value[k] = row[k]
|
||||||
|
}
|
||||||
|
copyForm.value = row
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
const openDrawer = (row) => {
|
||||||
|
drawer.value = true
|
||||||
|
activeRow.value = row
|
||||||
|
}
|
||||||
|
// 删除角色
|
||||||
|
const deleteAuth = (row) => {
|
||||||
|
ElMessageBox.confirm('此操作将永久删除该角色, 是否继续?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async() => {
|
||||||
|
const res = await deleteAuthority({ authorityId: row.authorityId })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功!'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: '已取消删除'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 初始化表单
|
||||||
|
const authorityForm = ref(null)
|
||||||
|
const initForm = () => {
|
||||||
|
if (authorityForm.value) {
|
||||||
|
authorityForm.value.resetFields()
|
||||||
|
}
|
||||||
|
form.value = {
|
||||||
|
authorityId: 0,
|
||||||
|
authorityName: '',
|
||||||
|
parentId: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关闭窗口
|
||||||
|
const closeDialog = () => {
|
||||||
|
initForm()
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
apiDialogFlag.value = false
|
||||||
|
}
|
||||||
|
// 确定弹窗
|
||||||
|
|
||||||
|
const enterDialog = () => {
|
||||||
|
authorityForm.value.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
form.value.authorityId = Number(form.value.authorityId)
|
||||||
|
switch (dialogType.value) {
|
||||||
|
case 'add':
|
||||||
|
{
|
||||||
|
const res = await createAuthority(form.value)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '添加成功!'
|
||||||
|
})
|
||||||
|
getTableData()
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'edit':
|
||||||
|
{
|
||||||
|
const res = await updateAuthority(form.value)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '添加成功!'
|
||||||
|
})
|
||||||
|
getTableData()
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'copy': {
|
||||||
|
const data = {
|
||||||
|
authority: {
|
||||||
|
authorityId: 0,
|
||||||
|
authorityName: '',
|
||||||
|
datauthorityId: [],
|
||||||
|
parentId: 0
|
||||||
|
},
|
||||||
|
oldAuthorityId: 0
|
||||||
|
}
|
||||||
|
data.authority.authorityId = form.value.authorityId
|
||||||
|
data.authority.authorityName = form.value.authorityName
|
||||||
|
data.authority.parentId = form.value.parentId
|
||||||
|
data.authority.dataAuthorityId = copyForm.value.dataAuthorityId
|
||||||
|
data.oldAuthorityId = copyForm.value.authorityId
|
||||||
|
const res = await copyAuthority(data)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '复制成功!'
|
||||||
|
})
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initForm()
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const setOptions = () => {
|
||||||
|
AuthorityOption.value = [
|
||||||
|
{
|
||||||
|
authorityId: 0,
|
||||||
|
authorityName: '根角色'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
setAuthorityOptions(tableData.value, AuthorityOption.value, false)
|
||||||
|
}
|
||||||
|
const setAuthorityOptions = (AuthorityData, optionsData, disabled) => {
|
||||||
|
form.value.authorityId = String(form.value.authorityId)
|
||||||
|
AuthorityData &&
|
||||||
|
AuthorityData.forEach(item => {
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
const option = {
|
||||||
|
authorityId: item.authorityId,
|
||||||
|
authorityName: item.authorityName,
|
||||||
|
disabled: disabled || item.authorityId === form.value.authorityId,
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
setAuthorityOptions(
|
||||||
|
item.children,
|
||||||
|
option.children,
|
||||||
|
disabled || item.authorityId === form.value.authorityId
|
||||||
|
)
|
||||||
|
optionsData.push(option)
|
||||||
|
} else {
|
||||||
|
const option = {
|
||||||
|
authorityId: item.authorityId,
|
||||||
|
authorityName: item.authorityName,
|
||||||
|
disabled: disabled || item.authorityId === form.value.authorityId
|
||||||
|
}
|
||||||
|
optionsData.push(option)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 增加角色
|
||||||
|
const addAuthority = (parentId) => {
|
||||||
|
initForm()
|
||||||
|
dialogTitle.value = '新增角色'
|
||||||
|
dialogType.value = 'add'
|
||||||
|
form.value.parentId = parentId
|
||||||
|
setOptions()
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
// 编辑角色
|
||||||
|
const editAuthority = (row) => {
|
||||||
|
setOptions()
|
||||||
|
dialogTitle.value = '编辑角色'
|
||||||
|
dialogType.value = 'edit'
|
||||||
|
for (const key in form.value) {
|
||||||
|
form.value[key] = row[key]
|
||||||
|
}
|
||||||
|
setOptions()
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.authority {
|
||||||
|
.el-input-number {
|
||||||
|
margin-left: 15px;
|
||||||
|
span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tree-content{
|
||||||
|
margin-top: 10px;
|
||||||
|
height: calc(100vh - 158px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="sticky top-0.5 z-10 bg-white">
|
||||||
|
<el-input
|
||||||
|
v-model="filterText"
|
||||||
|
class="w-3/5"
|
||||||
|
placeholder="筛选"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
class="float-right"
|
||||||
|
type="primary"
|
||||||
|
@click="authApiEnter"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="tree-content">
|
||||||
|
<el-scrollbar>
|
||||||
|
<el-tree
|
||||||
|
ref="apiTree"
|
||||||
|
:data="apiTreeData"
|
||||||
|
:default-checked-keys="apiTreeIds"
|
||||||
|
:props="apiDefaultProps"
|
||||||
|
default-expand-all
|
||||||
|
highlight-current
|
||||||
|
node-key="onlyId"
|
||||||
|
show-checkbox
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
@check="nodeChange"
|
||||||
|
/>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getAllApis } from '@/api/api'
|
||||||
|
import { UpdateCasbin, getPolicyPathByAuthorityId } from '@/api/casbin'
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Apis',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
row: {
|
||||||
|
default: function() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const apiDefaultProps = ref({
|
||||||
|
children: 'children',
|
||||||
|
label: 'description'
|
||||||
|
})
|
||||||
|
const filterText = ref('')
|
||||||
|
const apiTreeData = ref([])
|
||||||
|
const apiTreeIds = ref([])
|
||||||
|
const activeUserId = ref('')
|
||||||
|
const init = async() => {
|
||||||
|
const res2 = await getAllApis()
|
||||||
|
const apis = res2.data.apis
|
||||||
|
|
||||||
|
apiTreeData.value = buildApiTree(apis)
|
||||||
|
const res = await getPolicyPathByAuthorityId({
|
||||||
|
authorityId: props.row.authorityId
|
||||||
|
})
|
||||||
|
activeUserId.value = props.row.authorityId
|
||||||
|
apiTreeIds.value = []
|
||||||
|
res.data.paths && res.data.paths.forEach(item => {
|
||||||
|
apiTreeIds.value.push('p:' + item.path + 'm:' + item.method)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
|
||||||
|
const needConfirm = ref(false)
|
||||||
|
const nodeChange = () => {
|
||||||
|
needConfirm.value = true
|
||||||
|
}
|
||||||
|
// 暴露给外层使用的切换拦截统一方法
|
||||||
|
const enterAndNext = () => {
|
||||||
|
authApiEnter()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建api树方法
|
||||||
|
const buildApiTree = (apis) => {
|
||||||
|
const apiObj = {}
|
||||||
|
apis &&
|
||||||
|
apis.forEach(item => {
|
||||||
|
item.onlyId = 'p:' + item.path + 'm:' + item.method
|
||||||
|
if (Object.prototype.hasOwnProperty.call(apiObj, item.apiGroup)) {
|
||||||
|
apiObj[item.apiGroup].push(item)
|
||||||
|
} else {
|
||||||
|
Object.assign(apiObj, { [item.apiGroup]: [item] })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const apiTree = []
|
||||||
|
for (const key in apiObj) {
|
||||||
|
const treeNode = {
|
||||||
|
ID: key,
|
||||||
|
description: key + '组',
|
||||||
|
children: apiObj[key]
|
||||||
|
}
|
||||||
|
apiTree.push(treeNode)
|
||||||
|
}
|
||||||
|
return apiTree
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关联关系确定
|
||||||
|
const apiTree = ref(null)
|
||||||
|
const authApiEnter = async() => {
|
||||||
|
const checkArr = apiTree.value.getCheckedNodes(true)
|
||||||
|
var casbinInfos = []
|
||||||
|
checkArr && checkArr.forEach(item => {
|
||||||
|
var casbinInfo = {
|
||||||
|
path: item.path,
|
||||||
|
method: item.method
|
||||||
|
}
|
||||||
|
casbinInfos.push(casbinInfo)
|
||||||
|
})
|
||||||
|
const res = await UpdateCasbin({
|
||||||
|
authorityId: activeUserId.value,
|
||||||
|
casbinInfos
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: 'api设置成功' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
needConfirm,
|
||||||
|
enterAndNext
|
||||||
|
})
|
||||||
|
|
||||||
|
const filterNode = (value, data) => {
|
||||||
|
if (!value) return true
|
||||||
|
return data.description.indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
watch(filterText, (val) => {
|
||||||
|
apiTree.value.filter(val)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<warning-bar
|
||||||
|
title="此功能仅用于创建角色和角色的many2many关系表,具体使用还须自己结合表实现业务,详情参考示例代码(客户示例)。此功能不建议使用,建议使用插件市场【组织管理功能(点击前往)】来管理资源权限。"
|
||||||
|
/>
|
||||||
|
<div class="sticky top-0.5 z-10 bg-white my-4">
|
||||||
|
<el-button
|
||||||
|
class="float-left"
|
||||||
|
type="primary"
|
||||||
|
@click="all"
|
||||||
|
>全选</el-button>
|
||||||
|
<el-button
|
||||||
|
class="float-left"
|
||||||
|
type="primary"
|
||||||
|
@click="self"
|
||||||
|
>本角色</el-button>
|
||||||
|
<el-button
|
||||||
|
class="float-left"
|
||||||
|
type="primary"
|
||||||
|
@click="selfAndChildren"
|
||||||
|
>本角色及子角色</el-button>
|
||||||
|
<el-button
|
||||||
|
class="float-right"
|
||||||
|
type="primary"
|
||||||
|
@click="authDataEnter"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="clear-both pt-4">
|
||||||
|
<el-checkbox-group
|
||||||
|
v-model="dataAuthorityId"
|
||||||
|
@change="selectAuthority"
|
||||||
|
>
|
||||||
|
<el-checkbox
|
||||||
|
v-for="(item,key) in authoritys"
|
||||||
|
:key="key"
|
||||||
|
:label="item"
|
||||||
|
>{{ item.authorityName }}</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { setDataAuthority } from '@/api/authority'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Datas'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
row: {
|
||||||
|
default: function() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
|
authority: {
|
||||||
|
default: function() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
type: Array
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const authoritys = ref([])
|
||||||
|
const needConfirm = ref(false)
|
||||||
|
// 平铺角色
|
||||||
|
const roundAuthority = (authoritysData) => {
|
||||||
|
authoritysData && authoritysData.forEach(item => {
|
||||||
|
const obj = {}
|
||||||
|
obj.authorityId = item.authorityId
|
||||||
|
obj.authorityName = item.authorityName
|
||||||
|
authoritys.value.push(obj)
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
roundAuthority(item.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataAuthorityId = ref([])
|
||||||
|
const init = () => {
|
||||||
|
roundAuthority(props.authority)
|
||||||
|
props.row.dataAuthorityId && props.row.dataAuthorityId.forEach(item => {
|
||||||
|
const obj = authoritys.value && authoritys.value.filter(au => au.authorityId === item.authorityId) && authoritys.value.filter(au => au.authorityId === item.authorityId)[0]
|
||||||
|
dataAuthorityId.value.push(obj)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
|
||||||
|
// 暴露给外层使用的切换拦截统一方法
|
||||||
|
const enterAndNext = () => {
|
||||||
|
authDataEnter()
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['changeRow'])
|
||||||
|
const all = () => {
|
||||||
|
dataAuthorityId.value = [...authoritys.value]
|
||||||
|
emit('changeRow', 'dataAuthorityId', dataAuthorityId.value)
|
||||||
|
needConfirm.value = true
|
||||||
|
}
|
||||||
|
const self = () => {
|
||||||
|
dataAuthorityId.value = authoritys.value.filter(item => item.authorityId === props.row.authorityId)
|
||||||
|
emit('changeRow', 'dataAuthorityId', dataAuthorityId.value)
|
||||||
|
needConfirm.value = true
|
||||||
|
}
|
||||||
|
const selfAndChildren = () => {
|
||||||
|
const arrBox = []
|
||||||
|
getChildrenId(props.row, arrBox)
|
||||||
|
dataAuthorityId.value = authoritys.value.filter(item => arrBox.indexOf(item.authorityId) > -1)
|
||||||
|
emit('changeRow', 'dataAuthorityId', dataAuthorityId.value)
|
||||||
|
needConfirm.value = true
|
||||||
|
}
|
||||||
|
const getChildrenId = (row, arrBox) => {
|
||||||
|
arrBox.push(row.authorityId)
|
||||||
|
row.children && row.children.forEach(item => {
|
||||||
|
getChildrenId(item, arrBox)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 提交
|
||||||
|
const authDataEnter = async() => {
|
||||||
|
const res = await setDataAuthority(props.row)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: '资源设置成功' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择
|
||||||
|
const selectAuthority = () => {
|
||||||
|
emit('changeRow', 'dataAuthorityId', dataAuthorityId.value)
|
||||||
|
needConfirm.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
enterAndNext,
|
||||||
|
needConfirm
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,247 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="sticky top-0.5 z-10 bg-white">
|
||||||
|
<el-input
|
||||||
|
v-model="filterText"
|
||||||
|
class="w-3/5"
|
||||||
|
placeholder="筛选"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
class="float-right"
|
||||||
|
type="primary"
|
||||||
|
@click="relation"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="tree-content clear-both">
|
||||||
|
<el-scrollbar>
|
||||||
|
<el-tree
|
||||||
|
ref="menuTree"
|
||||||
|
:data="menuTreeData"
|
||||||
|
:default-checked-keys="menuTreeIds"
|
||||||
|
:props="menuDefaultProps"
|
||||||
|
default-expand-all
|
||||||
|
highlight-current
|
||||||
|
node-key="ID"
|
||||||
|
show-checkbox
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
@check="nodeChange"
|
||||||
|
>
|
||||||
|
<template #default="{ node , data }">
|
||||||
|
<span class="custom-tree-node">
|
||||||
|
<span>{{ node.label }}</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
|
||||||
|
:style="{color:row.defaultRouter === data.name?'#E6A23C':'#85ce61'}"
|
||||||
|
:disabled="!node.checked"
|
||||||
|
@click="() => setDefault(data)"
|
||||||
|
>
|
||||||
|
{{ row.defaultRouter === data.name?"首页":"设为首页" }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
<span v-if="data.menuBtn.length">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
|
||||||
|
@click="() => OpenBtn(data)"
|
||||||
|
>
|
||||||
|
分配按钮
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<el-dialog
|
||||||
|
v-model="btnVisible"
|
||||||
|
title="分配按钮"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-table
|
||||||
|
ref="btnTableRef"
|
||||||
|
:data="btnData"
|
||||||
|
row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="按钮名称"
|
||||||
|
prop="name"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="按钮备注"
|
||||||
|
prop="desc"
|
||||||
|
/>
|
||||||
|
</el-table>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getBaseMenuTree, getMenuAuthority, addMenuAuthority } from '@/api/menu'
|
||||||
|
import {
|
||||||
|
updateAuthority
|
||||||
|
} from '@/api/authority'
|
||||||
|
import { getAuthorityBtnApi, setAuthorityBtnApi } from '@/api/authorityBtn'
|
||||||
|
import { nextTick, ref, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Menus'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
row: {
|
||||||
|
default: function() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['changeRow'])
|
||||||
|
const filterText = ref('')
|
||||||
|
const menuTreeData = ref([])
|
||||||
|
const menuTreeIds = ref([])
|
||||||
|
const needConfirm = ref(false)
|
||||||
|
const menuDefaultProps = ref({
|
||||||
|
children: 'children',
|
||||||
|
label: function(data) {
|
||||||
|
return data.meta.title
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const init = async() => {
|
||||||
|
// 获取所有菜单树
|
||||||
|
const res = await getBaseMenuTree()
|
||||||
|
menuTreeData.value = res.data.menus
|
||||||
|
const res1 = await getMenuAuthority({ authorityId: props.row.authorityId })
|
||||||
|
const menus = res1.data.menus
|
||||||
|
const arr = []
|
||||||
|
menus.forEach(item => {
|
||||||
|
// 防止直接选中父级造成全选
|
||||||
|
if (!menus.some(same => same.parentId === item.menuId)) {
|
||||||
|
arr.push(Number(item.menuId))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
menuTreeIds.value = arr
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
|
||||||
|
const setDefault = async(data) => {
|
||||||
|
const res = await updateAuthority({ authorityId: props.row.authorityId, AuthorityName: props.row.authorityName, parentId: props.row.parentId, defaultRouter: data.name })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: '设置成功' })
|
||||||
|
emit('changeRow', 'defaultRouter', res.data.authority.defaultRouter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const nodeChange = () => {
|
||||||
|
needConfirm.value = true
|
||||||
|
}
|
||||||
|
// 暴露给外层使用的切换拦截统一方法
|
||||||
|
const enterAndNext = () => {
|
||||||
|
relation()
|
||||||
|
}
|
||||||
|
// 关联树 确认方法
|
||||||
|
const menuTree = ref(null)
|
||||||
|
const relation = async() => {
|
||||||
|
const checkArr = menuTree.value.getCheckedNodes(false, true)
|
||||||
|
const res = await addMenuAuthority({
|
||||||
|
menus: checkArr,
|
||||||
|
authorityId: props.row.authorityId
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '菜单设置成功!'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ enterAndNext, needConfirm })
|
||||||
|
|
||||||
|
const btnVisible = ref(false)
|
||||||
|
|
||||||
|
const btnData = ref([])
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
const btnTableRef = ref()
|
||||||
|
let menuID = ''
|
||||||
|
const OpenBtn = async(data) => {
|
||||||
|
menuID = data.ID
|
||||||
|
const res = await getAuthorityBtnApi({ menuID: menuID, authorityId: props.row.authorityId })
|
||||||
|
if (res.code === 0) {
|
||||||
|
openDialog(data)
|
||||||
|
await nextTick()
|
||||||
|
if (res.data.selected) {
|
||||||
|
res.data.selected.forEach(id => {
|
||||||
|
btnData.value.some(item => {
|
||||||
|
if (item.ID === id) {
|
||||||
|
btnTableRef.value.toggleRowSelection(item, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDialog = (data) => {
|
||||||
|
btnVisible.value = true
|
||||||
|
btnData.value = data.menuBtn
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
btnVisible.value = false
|
||||||
|
}
|
||||||
|
const enterDialog = async() => {
|
||||||
|
const selected = multipleSelection.value.map(item => item.ID)
|
||||||
|
const res = await setAuthorityBtnApi({
|
||||||
|
menuID,
|
||||||
|
selected,
|
||||||
|
authorityId: props.row.authorityId
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: '设置成功' })
|
||||||
|
btnVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterNode = (value, data) => {
|
||||||
|
if (!value) return true
|
||||||
|
// console.log(data.mate.title)
|
||||||
|
return data.meta.title.indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(filterText, (val) => {
|
||||||
|
menuTree.value.filter(val)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.custom-tree-node{
|
||||||
|
span+span{
|
||||||
|
@apply ml-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,262 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<warning-bar
|
||||||
|
title="获取字典且缓存方法已在前端utils/dictionary 已经封装完成 不必自己书写 使用方法查看文件内注释"
|
||||||
|
/>
|
||||||
|
<div class="dict-box flex gap-4">
|
||||||
|
<div class="w-64 bg-white p-4">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text font-bold">字典列表</span>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="openDialog"
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-scrollbar
|
||||||
|
class="mt-4"
|
||||||
|
style="height: calc(100vh - 300px)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="dictionary in dictionaryData"
|
||||||
|
:key="dictionary.ID"
|
||||||
|
class="rounded flex justify-between items-center px-2 py-4 cursor-pointer mt-2 hover:bg-blue-50 hover:text-gray-800 group bg-gray-50"
|
||||||
|
:class="selectID === dictionary.ID && 'active'"
|
||||||
|
@click="toDetail(dictionary)"
|
||||||
|
>
|
||||||
|
<span class="max-w-[160px] truncate">{{ dictionary.name }}</span>
|
||||||
|
<div>
|
||||||
|
<el-icon
|
||||||
|
class="group-hover:text-blue-500"
|
||||||
|
:class="selectID === dictionary.ID ? 'text-white-800':'text-blue-500'"
|
||||||
|
@click.stop="updateSysDictionaryFunc(dictionary)"
|
||||||
|
>
|
||||||
|
<Edit />
|
||||||
|
</el-icon>
|
||||||
|
<el-icon
|
||||||
|
class="ml-2 group-hover:text-red-500"
|
||||||
|
:class="selectID === dictionary.ID ? 'text-white-800':'text-red-500'"
|
||||||
|
@click="deleteSysDictionaryFunc(dictionary)"
|
||||||
|
>
|
||||||
|
<Delete />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 bg-white">
|
||||||
|
<sysDictionaryDetail :sys-dictionary-i-d="selectID" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:before-close="closeDialog"
|
||||||
|
:title="type==='create'?'添加字典':'修改字典'"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="dialogForm"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="110px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="字典名(中)"
|
||||||
|
prop="name"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.name"
|
||||||
|
placeholder="请输入字典名(中)"
|
||||||
|
clearable
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="字典名(英)"
|
||||||
|
prop="type"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.type"
|
||||||
|
placeholder="请输入字典名(英)"
|
||||||
|
clearable
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="状态"
|
||||||
|
prop="status"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="formData.status"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="停用"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="描述"
|
||||||
|
prop="desc"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.desc"
|
||||||
|
placeholder="请输入描述"
|
||||||
|
clearable
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createSysDictionary,
|
||||||
|
deleteSysDictionary,
|
||||||
|
updateSysDictionary,
|
||||||
|
findSysDictionary,
|
||||||
|
getSysDictionaryList,
|
||||||
|
} from '@/api/sysDictionary' // 此处请自行替换地址
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
import sysDictionaryDetail from './sysDictionaryDetail.vue'
|
||||||
|
import { Edit } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SysDictionary',
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectID = ref(1)
|
||||||
|
|
||||||
|
const formData = ref({
|
||||||
|
name: null,
|
||||||
|
type: null,
|
||||||
|
status: true,
|
||||||
|
desc: null,
|
||||||
|
})
|
||||||
|
const rules = ref({
|
||||||
|
name: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入字典名(中)',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入字典名(英)',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
desc: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入描述',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const dictionaryData = ref([])
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const res = await getSysDictionaryList()
|
||||||
|
if (res.code === 0) {
|
||||||
|
dictionaryData.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
const toDetail = (row) => {
|
||||||
|
selectID.value = row.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
const type = ref('')
|
||||||
|
const updateSysDictionaryFunc = async(row) => {
|
||||||
|
const res = await findSysDictionary({ ID: row.ID, status: row.status })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resysDictionary
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
name: null,
|
||||||
|
type: null,
|
||||||
|
status: true,
|
||||||
|
desc: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const deleteSysDictionaryFunc = async(row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const res = await deleteSysDictionary({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功',
|
||||||
|
})
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogForm = ref(null)
|
||||||
|
const enterDialog = async() => {
|
||||||
|
dialogForm.value.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createSysDictionary(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateSysDictionary(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createSysDictionary(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dict-box{
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
background-color: var(--el-color-primary) !important;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,344 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list justify-between">
|
||||||
|
<span class="text font-bold">字典详细内容</span>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="openDialog"
|
||||||
|
>新增字典项</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
ref="multipleTable"
|
||||||
|
:data="tableData"
|
||||||
|
style="width: 100%"
|
||||||
|
tooltip-effect="dark"
|
||||||
|
row-key="ID"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="日期"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="展示值"
|
||||||
|
prop="label"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="字典值"
|
||||||
|
prop="value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="扩展值"
|
||||||
|
prop="extend"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="启用状态"
|
||||||
|
prop="status"
|
||||||
|
width="120"
|
||||||
|
>
|
||||||
|
<template #default="scope">{{ formatBoolean(scope.row.status) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="排序标记"
|
||||||
|
prop="sort"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="操作"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="edit"
|
||||||
|
@click="updateSysDictionaryDetailFunc(scope.row)"
|
||||||
|
>变更</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="delete"
|
||||||
|
@click="deleteSysDictionaryDetailFunc(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:before-close="closeDialog"
|
||||||
|
:title="type==='create'?'添加字典项':'修改字典项'"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="dialogForm"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="110px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="展示值"
|
||||||
|
prop="label"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.label"
|
||||||
|
placeholder="请输入展示值"
|
||||||
|
clearable
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="字典值"
|
||||||
|
prop="value"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.value"
|
||||||
|
placeholder="请输入字典值"
|
||||||
|
clearable
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="扩展值"
|
||||||
|
prop="extend"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.extend"
|
||||||
|
placeholder="请输入扩展值"
|
||||||
|
clearable
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="启用状态"
|
||||||
|
prop="status"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="formData.status"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="停用"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="排序标记"
|
||||||
|
prop="sort"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
v-model.number="formData.sort"
|
||||||
|
placeholder="排序标记"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createSysDictionaryDetail,
|
||||||
|
deleteSysDictionaryDetail,
|
||||||
|
updateSysDictionaryDetail,
|
||||||
|
findSysDictionaryDetail,
|
||||||
|
getSysDictionaryDetailList
|
||||||
|
} from '@/api/sysDictionaryDetail' // 此处请自行替换地址
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { formatBoolean, formatDate } from '@/utils/format'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SysDictionaryDetail'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
sysDictionaryID: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formData = ref({
|
||||||
|
label: null,
|
||||||
|
value: null,
|
||||||
|
status: true,
|
||||||
|
sort: null
|
||||||
|
})
|
||||||
|
const rules = ref({
|
||||||
|
label: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入展示值',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入字典值',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
sort: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '排序标记',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getSysDictionaryDetailList({
|
||||||
|
page: page.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
sysDictionaryID: props.sysDictionaryID
|
||||||
|
})
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
const type = ref('')
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
const updateSysDictionaryDetailFunc = async(row) => {
|
||||||
|
const res = await findSysDictionaryDetail({ ID: row.ID })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.reSysDictionaryDetail
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
label: null,
|
||||||
|
value: null,
|
||||||
|
status: true,
|
||||||
|
sort: null,
|
||||||
|
sysDictionaryID: props.sysDictionaryID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const deleteSysDictionaryDetailFunc = async(row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const res = await deleteSysDictionaryDetail({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogForm = ref(null)
|
||||||
|
const enterDialog = async() => {
|
||||||
|
dialogForm.value.validate(async valid => {
|
||||||
|
formData.value.sysDictionaryID = props.sysDictionaryID
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createSysDictionaryDetail(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateSysDictionaryDetail(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createSysDictionaryDetail(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.sysDictionaryID, () => {
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition
|
||||||
|
mode="out-in"
|
||||||
|
name="el-fade-in-linear"
|
||||||
|
>
|
||||||
|
<keep-alive :include="routerStore.keepAliveRouters">
|
||||||
|
<component :is="Component" />
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouterStore } from '@/pinia/modules/router'
|
||||||
|
const routerStore = useRouterStore()
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SuperAdmin'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,763 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="addMenu('0')"
|
||||||
|
>新增根菜单</el-button>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT/?p=4&vd_source=f2640257c21e3b547a790461ed94875e')"
|
||||||
|
><VideoCameraFilled /></el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 由于此处菜单跟左侧列表一一对应所以不需要分页 pageSize默认999 -->
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
row-key="ID"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="ID"
|
||||||
|
min-width="100"
|
||||||
|
prop="ID"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="展示名称"
|
||||||
|
min-width="120"
|
||||||
|
prop="authorityName"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.meta.title }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="图标"
|
||||||
|
min-width="140"
|
||||||
|
prop="authorityName"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div
|
||||||
|
v-if="scope.row.meta.icon"
|
||||||
|
class="icon-column"
|
||||||
|
>
|
||||||
|
<el-icon>
|
||||||
|
<component :is="scope.row.meta.icon" />
|
||||||
|
</el-icon>
|
||||||
|
<span>{{ scope.row.meta.icon }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="路由Name"
|
||||||
|
show-overflow-tooltip
|
||||||
|
min-width="160"
|
||||||
|
prop="name"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="路由Path"
|
||||||
|
show-overflow-tooltip
|
||||||
|
min-width="160"
|
||||||
|
prop="path"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="是否隐藏"
|
||||||
|
min-width="100"
|
||||||
|
prop="hidden"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.hidden?"隐藏":"显示" }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="父节点"
|
||||||
|
min-width="90"
|
||||||
|
prop="parentId"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="排序"
|
||||||
|
min-width="70"
|
||||||
|
prop="sort"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="文件路径"
|
||||||
|
min-width="360"
|
||||||
|
prop="component"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
fixed="right"
|
||||||
|
label="操作"
|
||||||
|
width="300"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="plus"
|
||||||
|
@click="addMenu(scope.row.ID)"
|
||||||
|
>添加子菜单</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="edit"
|
||||||
|
@click="editMenu(scope.row.ID)"
|
||||||
|
>编辑</el-button>
|
||||||
|
<el-button
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="delete"
|
||||||
|
@click="deleteMenu(scope.row.ID)"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:before-close="handleClose"
|
||||||
|
:title="dialogTitle"
|
||||||
|
>
|
||||||
|
<warning-bar title="新增菜单,需要在角色管理内配置权限才可使用" />
|
||||||
|
<el-form
|
||||||
|
v-if="dialogFormVisible"
|
||||||
|
ref="menuForm"
|
||||||
|
:inline="true"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-position="top"
|
||||||
|
label-width="85px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="路由Name"
|
||||||
|
prop="path"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.name"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="唯一英文字符串"
|
||||||
|
@change="changeName"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
prop="path"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span style="display: inline-flex;align-items: center;">
|
||||||
|
<span>路由Path</span>
|
||||||
|
<el-checkbox
|
||||||
|
v-model="checkFlag"
|
||||||
|
style="margin-left:12px;height: auto"
|
||||||
|
>添加参数</el-checkbox>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-input
|
||||||
|
v-model="form.path"
|
||||||
|
:disabled="!checkFlag"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="建议只在后方拼接参数"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="是否隐藏"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="form.hidden"
|
||||||
|
placeholder="是否在列表隐藏"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
:value="false"
|
||||||
|
label="否"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
:value="true"
|
||||||
|
label="是"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="父节点ID"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<el-cascader
|
||||||
|
v-model="form.parentId"
|
||||||
|
style="width:100%"
|
||||||
|
:disabled="!isEdit"
|
||||||
|
:options="menuOption"
|
||||||
|
:props="{ checkStrictly: true,label:'title',value:'ID',disabled:'disabled',emitPath:false}"
|
||||||
|
:show-all-levels="false"
|
||||||
|
filterable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="文件路径"
|
||||||
|
prop="component"
|
||||||
|
style="width:60%"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.component"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="页面:view/xxx/xx.vue 插件:plugin/xx/xx.vue"
|
||||||
|
@blur="fmtComponent"
|
||||||
|
/>
|
||||||
|
<span style="font-size:12px;margin-right:12px;">如果菜单包含子菜单,请创建router-view二级路由页面或者</span><el-button
|
||||||
|
style="margin-top:4px"
|
||||||
|
@click="form.component = 'view/routerHolder.vue'"
|
||||||
|
>点我设置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="展示名称"
|
||||||
|
prop="meta.title"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.meta.title"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="图标"
|
||||||
|
prop="meta.icon"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<icon
|
||||||
|
:meta="form.meta"
|
||||||
|
style="width:100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="排序标记"
|
||||||
|
prop="sort"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model.number="form.sort"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
prop="meta.activeName"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<div>
|
||||||
|
<span> 高亮菜单 </span>
|
||||||
|
<el-tooltip
|
||||||
|
content="注:当到达此路由时候,指定左侧菜单指定name会处于活跃状态(亮起),可为空,为空则为本路由Name。"
|
||||||
|
placement="top"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<el-icon><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-input
|
||||||
|
v-model="form.meta.activeName"
|
||||||
|
:placeholder="form.name"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="KeepAlive"
|
||||||
|
prop="meta.keepAlive"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="form.meta.keepAlive"
|
||||||
|
style="width:100%"
|
||||||
|
placeholder="是否keepAlive缓存页面"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
:value="false"
|
||||||
|
label="否"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
:value="true"
|
||||||
|
label="是"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="CloseTab"
|
||||||
|
prop="meta.closeTab"
|
||||||
|
style="width:30%"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="form.meta.closeTab"
|
||||||
|
style="width:100%"
|
||||||
|
placeholder="是否自动关闭tab"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
:value="false"
|
||||||
|
label="否"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
:value="true"
|
||||||
|
label="是"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item style="width:30%">
|
||||||
|
<template #label>
|
||||||
|
<div>
|
||||||
|
<span> 是否为基础页面 </span>
|
||||||
|
<el-tooltip
|
||||||
|
content="此项选择为是,则不会展示左侧菜单以及顶部信息。"
|
||||||
|
placement="top"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<el-icon><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-select
|
||||||
|
v-model="form.meta.defaultMenu"
|
||||||
|
style="width:100%"
|
||||||
|
placeholder="是否为基础页面"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
:value="false"
|
||||||
|
label="否"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
:value="true"
|
||||||
|
label="是"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="edit"
|
||||||
|
@click="addParameter(form)"
|
||||||
|
>新增菜单参数</el-button>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=9&vd_source=f2640257c21e3b547a790461ed94875e')"
|
||||||
|
><VideoCameraFilled /></el-icon>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="form.parameters"
|
||||||
|
style="width: 100%;margin-top: 12px;"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
prop="type"
|
||||||
|
label="参数类型"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-select
|
||||||
|
v-model="scope.row.type"
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
key="query"
|
||||||
|
value="query"
|
||||||
|
label="query"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
key="params"
|
||||||
|
value="params"
|
||||||
|
label="params"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
prop="key"
|
||||||
|
label="参数key"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-input v-model="scope.row.key" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
prop="value"
|
||||||
|
label="参数值"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-input v-model="scope.row.value" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left">
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
|
||||||
|
icon="delete"
|
||||||
|
@click="deleteParameter(form.parameters,scope.$index)"
|
||||||
|
>删除</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2 mt-3">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="edit"
|
||||||
|
@click="addBtn(form)"
|
||||||
|
>新增可控按钮
|
||||||
|
</el-button>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=11&vd_source=f2640257c21e3b547a790461ed94875e')"
|
||||||
|
><VideoCameraFilled /></el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="form.menuBtn"
|
||||||
|
style="width: 100%;margin-top: 12px;"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
prop="name"
|
||||||
|
label="按钮名称"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-input v-model="scope.row.name" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
prop="name"
|
||||||
|
label="备注"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-input v-model="scope.row.desc" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left">
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
|
||||||
|
icon="delete"
|
||||||
|
@click="deleteBtn(form.menuBtn,scope.$index)"
|
||||||
|
>删除</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
updateBaseMenu,
|
||||||
|
getMenuList,
|
||||||
|
addBaseMenu,
|
||||||
|
deleteBaseMenu,
|
||||||
|
getBaseMenuById
|
||||||
|
} from '@/api/menu'
|
||||||
|
import icon from '@/view/superAdmin/menu/icon.vue'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { canRemoveAuthorityBtnApi } from '@/api/authorityBtn'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { QuestionFilled, VideoCameraFilled } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
import { toDoc } from '@/utils/doc'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Menus',
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
path: [{ required: true, message: '请输入菜单name', trigger: 'blur' }],
|
||||||
|
component: [
|
||||||
|
{ required: true, message: '请输入文件路径', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
'meta.title': [
|
||||||
|
{ required: true, message: '请输入菜单展示名称', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(999)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getMenuList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// 新增参数
|
||||||
|
const addParameter = (form) => {
|
||||||
|
if (!form.parameters) {
|
||||||
|
form.parameters = []
|
||||||
|
}
|
||||||
|
form.parameters.push({
|
||||||
|
type: 'query',
|
||||||
|
key: '',
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fmtComponent = () => {
|
||||||
|
form.value.component = form.value.component.replace(/\\/g, '/')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除参数
|
||||||
|
const deleteParameter = (parameters, index) => {
|
||||||
|
parameters.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增可控按钮
|
||||||
|
const addBtn = (form) => {
|
||||||
|
if (!form.menuBtn) {
|
||||||
|
form.menuBtn = []
|
||||||
|
}
|
||||||
|
form.menuBtn.push({
|
||||||
|
name: '',
|
||||||
|
desc: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 删除可控按钮
|
||||||
|
const deleteBtn = async(btns, index) => {
|
||||||
|
const btn = btns[index]
|
||||||
|
if (btn.ID === 0) {
|
||||||
|
btns.splice(index, 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await canRemoveAuthorityBtnApi({ id: btn.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
btns.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = ref({
|
||||||
|
ID: 0,
|
||||||
|
path: '',
|
||||||
|
name: '',
|
||||||
|
hidden: false,
|
||||||
|
parentId: '',
|
||||||
|
component: '',
|
||||||
|
meta: {
|
||||||
|
activeName: '',
|
||||||
|
title: '',
|
||||||
|
icon: '',
|
||||||
|
defaultMenu: false,
|
||||||
|
closeTab: false,
|
||||||
|
keepAlive: false
|
||||||
|
},
|
||||||
|
parameters: [],
|
||||||
|
menuBtn: []
|
||||||
|
})
|
||||||
|
const changeName = () => {
|
||||||
|
form.value.path = form.value.name
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = (done) => {
|
||||||
|
initForm()
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
// 删除菜单
|
||||||
|
const deleteMenu = (ID) => {
|
||||||
|
ElMessageBox.confirm('此操作将永久删除所有角色下该菜单, 是否继续?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async() => {
|
||||||
|
const res = await deleteBaseMenu({ ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功!'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: '已取消删除'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 初始化弹窗内表格方法
|
||||||
|
const menuForm = ref(null)
|
||||||
|
const checkFlag = ref(false)
|
||||||
|
const initForm = () => {
|
||||||
|
checkFlag.value = false
|
||||||
|
menuForm.value.resetFields()
|
||||||
|
form.value = {
|
||||||
|
ID: 0,
|
||||||
|
path: '',
|
||||||
|
name: '',
|
||||||
|
hidden: false,
|
||||||
|
parentId: '',
|
||||||
|
component: '',
|
||||||
|
meta: {
|
||||||
|
title: '',
|
||||||
|
icon: '',
|
||||||
|
defaultMenu: false,
|
||||||
|
closeTab: false,
|
||||||
|
keepAlive: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关闭弹窗
|
||||||
|
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
const closeDialog = () => {
|
||||||
|
initForm()
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
}
|
||||||
|
// 添加menu
|
||||||
|
const enterDialog = async() => {
|
||||||
|
menuForm.value.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
let res
|
||||||
|
if (isEdit.value) {
|
||||||
|
res = await updateBaseMenu(form.value)
|
||||||
|
} else {
|
||||||
|
res = await addBaseMenu(form.value)
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: isEdit.value ? '编辑成功' : '添加成功!'
|
||||||
|
})
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
initForm()
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuOption = ref([
|
||||||
|
{
|
||||||
|
ID: '0',
|
||||||
|
title: '根菜单'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const setOptions = () => {
|
||||||
|
menuOption.value = [
|
||||||
|
{
|
||||||
|
ID: '0',
|
||||||
|
title: '根目录'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
setMenuOptions(tableData.value, menuOption.value, false)
|
||||||
|
}
|
||||||
|
const setMenuOptions = (menuData, optionsData, disabled) => {
|
||||||
|
menuData &&
|
||||||
|
menuData.forEach(item => {
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
const option = {
|
||||||
|
title: item.meta.title,
|
||||||
|
ID: String(item.ID),
|
||||||
|
disabled: disabled || item.ID === form.value.ID,
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
setMenuOptions(
|
||||||
|
item.children,
|
||||||
|
option.children,
|
||||||
|
disabled || item.ID === form.value.ID
|
||||||
|
)
|
||||||
|
optionsData.push(option)
|
||||||
|
} else {
|
||||||
|
const option = {
|
||||||
|
title: item.meta.title,
|
||||||
|
ID: String(item.ID),
|
||||||
|
disabled: disabled || item.ID === form.value.ID
|
||||||
|
}
|
||||||
|
optionsData.push(option)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加菜单方法,id为 0则为添加根菜单
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const dialogTitle = ref('新增菜单')
|
||||||
|
const addMenu = (id) => {
|
||||||
|
dialogTitle.value = '新增菜单'
|
||||||
|
form.value.parentId = String(id)
|
||||||
|
isEdit.value = false
|
||||||
|
setOptions()
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
// 修改菜单方法
|
||||||
|
const editMenu = async(id) => {
|
||||||
|
dialogTitle.value = '编辑菜单'
|
||||||
|
const res = await getBaseMenuById({ id })
|
||||||
|
form.value = res.data.menu
|
||||||
|
isEdit.value = true
|
||||||
|
setOptions()
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.warning {
|
||||||
|
color: #dc143c;
|
||||||
|
}
|
||||||
|
.icon-column{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.el-icon{
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,323 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form
|
||||||
|
:inline="true"
|
||||||
|
:model="searchInfo"
|
||||||
|
>
|
||||||
|
<el-form-item label="请求方法">
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.method"
|
||||||
|
placeholder="搜索条件"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="请求路径">
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.path"
|
||||||
|
placeholder="搜索条件"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="结果状态码">
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.status"
|
||||||
|
placeholder="搜索条件"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="search"
|
||||||
|
@click="onSubmit"
|
||||||
|
>查询</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="refresh"
|
||||||
|
@click="onReset"
|
||||||
|
>重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button
|
||||||
|
icon="delete"
|
||||||
|
:disabled="!multipleSelection.length"
|
||||||
|
@click="onDelete"
|
||||||
|
>删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
ref="multipleTable"
|
||||||
|
:data="tableData"
|
||||||
|
style="width: 100%"
|
||||||
|
tooltip-effect="dark"
|
||||||
|
row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="操作人"
|
||||||
|
width="140"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>{{ scope.row.user.userName }}({{ scope.row.user.nickName }})</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="日期"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="状态码"
|
||||||
|
prop="status"
|
||||||
|
width="120"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-tag type="success">{{ scope.row.status }}</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="请求IP"
|
||||||
|
prop="ip"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="请求方法"
|
||||||
|
prop="method"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="请求路径"
|
||||||
|
prop="path"
|
||||||
|
width="240"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="请求"
|
||||||
|
prop="path"
|
||||||
|
width="80"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-popover
|
||||||
|
v-if="scope.row.body"
|
||||||
|
placement="left-start"
|
||||||
|
>
|
||||||
|
<div class="popover-box">
|
||||||
|
<pre>{{ fmtBody(scope.row.body) }}</pre>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<el-icon style="cursor: pointer;"><warning /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<span v-else>无</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="响应"
|
||||||
|
prop="path"
|
||||||
|
width="80"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-popover
|
||||||
|
v-if="scope.row.resp"
|
||||||
|
placement="left-start"
|
||||||
|
>
|
||||||
|
<div class="popover-box">
|
||||||
|
<pre>{{ fmtBody(scope.row.resp) }}</pre>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<el-icon style="cursor: pointer;"><warning /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else>无</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="操作"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="delete"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="deleteSysOperationRecordFunc(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
deleteSysOperationRecord,
|
||||||
|
getSysOperationRecordList,
|
||||||
|
deleteSysOperationRecordByIds
|
||||||
|
} from '@/api/sysOperationRecord' // 此处请自行替换地址
|
||||||
|
import { formatDate } from '@/utils/format'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SysOperationRecord'
|
||||||
|
})
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
}
|
||||||
|
// 条件搜索前端看此方法
|
||||||
|
const onSubmit = () => {
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
if (searchInfo.value.status === '') {
|
||||||
|
searchInfo.value.status = null
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getSysOperationRecordList({
|
||||||
|
page: page.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
...searchInfo.value,
|
||||||
|
})
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const ids = []
|
||||||
|
multipleSelection.value &&
|
||||||
|
multipleSelection.value.forEach(item => {
|
||||||
|
ids.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteSysOperationRecordByIds({ ids })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === ids.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const deleteSysOperationRecordFunc = async(row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const res = await deleteSysOperationRecord({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const fmtBody = (value) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (err) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.table-expand {
|
||||||
|
padding-left: 60px;
|
||||||
|
font-size: 0;
|
||||||
|
label {
|
||||||
|
width: 90px;
|
||||||
|
color: #99a9bf;
|
||||||
|
.el-form-item {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.popover-box {
|
||||||
|
background: #112435;
|
||||||
|
color: #f08047;
|
||||||
|
height: 600px;
|
||||||
|
width: 420px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.popover-box::-webkit-scrollbar {
|
||||||
|
display: none; /* Chrome Safari */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,515 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<warning-bar title="注:右上角头像下拉可切换角色" />
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="addUser"
|
||||||
|
>新增用户</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
row-key="ID"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="头像"
|
||||||
|
min-width="75"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<CustomPic
|
||||||
|
style="margin-top:8px"
|
||||||
|
:pic-src="scope.row.headerImg"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="ID"
|
||||||
|
min-width="50"
|
||||||
|
prop="ID"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="用户名"
|
||||||
|
min-width="150"
|
||||||
|
prop="userName"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="昵称"
|
||||||
|
min-width="150"
|
||||||
|
prop="nickName"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="手机号"
|
||||||
|
min-width="180"
|
||||||
|
prop="phone"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="邮箱"
|
||||||
|
min-width="180"
|
||||||
|
prop="email"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="用户角色"
|
||||||
|
min-width="200"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-cascader
|
||||||
|
v-model="scope.row.authorityIds"
|
||||||
|
:options="authOptions"
|
||||||
|
:show-all-levels="false"
|
||||||
|
collapse-tags
|
||||||
|
:props="{ multiple:true,checkStrictly: true,label:'authorityName',value:'authorityId',disabled:'disabled',emitPath:false}"
|
||||||
|
:clearable="false"
|
||||||
|
@visible-change="(flag)=>{changeAuthority(scope.row,flag,0)}"
|
||||||
|
@remove-tag="(removeAuth)=>{changeAuthority(scope.row,false,removeAuth)}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="启用"
|
||||||
|
min-width="150"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.enable"
|
||||||
|
inline-prompt
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="2"
|
||||||
|
@change="()=>{switchEnable(scope.row)}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="操作"
|
||||||
|
min-width="250"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="delete"
|
||||||
|
@click="deleteUserFunc(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="edit"
|
||||||
|
@click="openEdit(scope.row)"
|
||||||
|
>编辑</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="magic-stick"
|
||||||
|
@click="resetPasswordFunc(scope.row)"
|
||||||
|
>重置密码</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-dialog
|
||||||
|
v-model="addUserDialog"
|
||||||
|
title="用户"
|
||||||
|
:show-close="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<div style="height:60vh;overflow:auto;padding:0 12px;">
|
||||||
|
<el-form
|
||||||
|
ref="userForm"
|
||||||
|
:rules="rules"
|
||||||
|
:model="userInfo"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dialogFlag === 'add'"
|
||||||
|
label="用户名"
|
||||||
|
prop="userName"
|
||||||
|
>
|
||||||
|
<el-input v-model="userInfo.userName" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dialogFlag === 'add'"
|
||||||
|
label="密码"
|
||||||
|
prop="password"
|
||||||
|
>
|
||||||
|
<el-input v-model="userInfo.password" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="昵称"
|
||||||
|
prop="nickName"
|
||||||
|
>
|
||||||
|
<el-input v-model="userInfo.nickName" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="手机号"
|
||||||
|
prop="phone"
|
||||||
|
>
|
||||||
|
<el-input v-model="userInfo.phone" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="邮箱"
|
||||||
|
prop="email"
|
||||||
|
>
|
||||||
|
<el-input v-model="userInfo.email" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="用户角色"
|
||||||
|
prop="authorityId"
|
||||||
|
>
|
||||||
|
<el-cascader
|
||||||
|
v-model="userInfo.authorityIds"
|
||||||
|
style="width:100%"
|
||||||
|
:options="authOptions"
|
||||||
|
:show-all-levels="false"
|
||||||
|
:props="{ multiple:true,checkStrictly: true,label:'authorityName',value:'authorityId',disabled:'disabled',emitPath:false}"
|
||||||
|
:clearable="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="启用"
|
||||||
|
prop="disabled"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="userInfo.enable"
|
||||||
|
inline-prompt
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="2"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="头像"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="display:inline-block"
|
||||||
|
@click="openHeaderChange"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="userInfo.headerImg"
|
||||||
|
alt="头像"
|
||||||
|
class="header-img-box"
|
||||||
|
:src="(userInfo.headerImg && userInfo.headerImg.slice(0, 4) !== 'http')?path+userInfo.headerImg:userInfo.headerImg"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="header-img-box"
|
||||||
|
>从媒体库选择</div>
|
||||||
|
<ChooseImg
|
||||||
|
ref="chooseImg"
|
||||||
|
:target="userInfo"
|
||||||
|
:target-key="`headerImg`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeAddUserDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterAddUserDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import {
|
||||||
|
getUserList,
|
||||||
|
setUserAuthorities,
|
||||||
|
register,
|
||||||
|
deleteUser
|
||||||
|
} from '@/api/user'
|
||||||
|
|
||||||
|
import { getAuthorityList } from '@/api/authority'
|
||||||
|
import CustomPic from '@/components/customPic/index.vue'
|
||||||
|
import ChooseImg from '@/components/chooseImg/index.vue'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { setUserInfo, resetPassword } from '@/api/user.js'
|
||||||
|
|
||||||
|
import { nextTick, ref, watch } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'User',
|
||||||
|
})
|
||||||
|
|
||||||
|
const path = ref(import.meta.env.VITE_BASE_API + '/')
|
||||||
|
// 初始化相关
|
||||||
|
const setAuthorityOptions = (AuthorityData, optionsData) => {
|
||||||
|
AuthorityData &&
|
||||||
|
AuthorityData.forEach(item => {
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
const option = {
|
||||||
|
authorityId: item.authorityId,
|
||||||
|
authorityName: item.authorityName,
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
setAuthorityOptions(item.children, option.children)
|
||||||
|
optionsData.push(option)
|
||||||
|
} else {
|
||||||
|
const option = {
|
||||||
|
authorityId: item.authorityId,
|
||||||
|
authorityName: item.authorityName
|
||||||
|
}
|
||||||
|
optionsData.push(option)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getUserList({ page: page.value, pageSize: pageSize.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => tableData.value, () => {
|
||||||
|
setAuthorityIds()
|
||||||
|
})
|
||||||
|
|
||||||
|
const initPage = async() => {
|
||||||
|
getTableData()
|
||||||
|
const res = await getAuthorityList({ page: 1, pageSize: 999 })
|
||||||
|
setOptions(res.data.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
initPage()
|
||||||
|
|
||||||
|
const resetPasswordFunc = (row) => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'是否将此用户密码重置为123456?',
|
||||||
|
'警告',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
).then(async() => {
|
||||||
|
const res = await resetPassword({
|
||||||
|
ID: row.ID,
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: res.msg,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: res.msg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const setAuthorityIds = () => {
|
||||||
|
tableData.value && tableData.value.forEach((user) => {
|
||||||
|
user.authorityIds = user.authorities && user.authorities.map(i => {
|
||||||
|
return i.authorityId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const chooseImg = ref(null)
|
||||||
|
const openHeaderChange = () => {
|
||||||
|
chooseImg.value.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
const authOptions = ref([])
|
||||||
|
const setOptions = (authData) => {
|
||||||
|
authOptions.value = []
|
||||||
|
setAuthorityOptions(authData, authOptions.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteUserFunc = async(row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async () => {
|
||||||
|
const res = await deleteUser({ id: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
await getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const userInfo = ref({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
nickName: '',
|
||||||
|
headerImg: '',
|
||||||
|
authorityId: '',
|
||||||
|
authorityIds: [],
|
||||||
|
enable: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = ref({
|
||||||
|
userName: [
|
||||||
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
{ min: 5, message: '最低5位字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入用户密码', trigger: 'blur' },
|
||||||
|
{ min: 6, message: '最低6位字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
nickName: [
|
||||||
|
{ required: true, message: '请输入用户昵称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
phone: [
|
||||||
|
{ pattern: /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/, message: '请输入合法手机号', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{ pattern: /^([0-9A-Za-z\-_.]+)@([0-9a-z]+\.[a-z]{2,3}(\.[a-z]{2})?)$/g, message: '请输入正确的邮箱', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
authorityId: [
|
||||||
|
{ required: true, message: '请选择用户角色', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const userForm = ref(null)
|
||||||
|
const enterAddUserDialog = async() => {
|
||||||
|
userInfo.value.authorityId = userInfo.value.authorityIds[0]
|
||||||
|
userForm.value.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
const req = {
|
||||||
|
...userInfo.value
|
||||||
|
}
|
||||||
|
if (dialogFlag.value === 'add') {
|
||||||
|
const res = await register(req)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: '创建成功' })
|
||||||
|
await getTableData()
|
||||||
|
closeAddUserDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dialogFlag.value === 'edit') {
|
||||||
|
const res = await setUserInfo(req)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
await getTableData()
|
||||||
|
closeAddUserDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addUserDialog = ref(false)
|
||||||
|
const closeAddUserDialog = () => {
|
||||||
|
userForm.value.resetFields()
|
||||||
|
userInfo.value.headerImg = ''
|
||||||
|
userInfo.value.authorityIds = []
|
||||||
|
addUserDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogFlag = ref('add')
|
||||||
|
|
||||||
|
const addUser = () => {
|
||||||
|
dialogFlag.value = 'add'
|
||||||
|
addUserDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempAuth = {}
|
||||||
|
const changeAuthority = async(row, flag, removeAuth) => {
|
||||||
|
if (flag) {
|
||||||
|
if (!removeAuth) {
|
||||||
|
tempAuth[row.ID] = [...row.authorityIds]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await nextTick()
|
||||||
|
const res = await setUserAuthorities({
|
||||||
|
ID: row.ID,
|
||||||
|
authorityIds: row.authorityIds
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: '角色设置成功' })
|
||||||
|
} else {
|
||||||
|
if (!removeAuth) {
|
||||||
|
row.authorityIds = [...tempAuth[row.ID]]
|
||||||
|
delete tempAuth[row.ID]
|
||||||
|
} else {
|
||||||
|
row.authorityIds = [removeAuth, ...row.authorityIds]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openEdit = (row) => {
|
||||||
|
dialogFlag.value = 'edit'
|
||||||
|
userInfo.value = JSON.parse(JSON.stringify(row))
|
||||||
|
addUserDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const switchEnable = async(row) => {
|
||||||
|
userInfo.value = JSON.parse(JSON.stringify(row))
|
||||||
|
await nextTick()
|
||||||
|
const req = {
|
||||||
|
...userInfo.value
|
||||||
|
}
|
||||||
|
const res = await setUserInfo(req)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: `${req.enable === 2 ? '禁用' : '启用'}成功` })
|
||||||
|
await getTableData()
|
||||||
|
userInfo.value.headerImg = ''
|
||||||
|
userInfo.value.authorityIds = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.header-img-box {
|
||||||
|
@apply w-52 h-52 border border-solid border-gray-300 rounded-xl flex justify-center items-center cursor-pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,237 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-row
|
||||||
|
:gutter="15"
|
||||||
|
class="py-1"
|
||||||
|
>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card
|
||||||
|
v-if="state.os"
|
||||||
|
class="card_item"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div>Runtime</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">os:</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.os.goos"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">cpu nums:</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.os.numCpu"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">compiler:</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.os.compiler"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">go version:</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.os.goVersion"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">goroutine nums:</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.os.numGoroutine"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card
|
||||||
|
v-if="state.disk"
|
||||||
|
class="card_item"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div>Disk</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">total (MB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.disk.totalMb"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">used (MB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.disk.usedMb"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">total (GB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.disk.totalGb"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">used (GB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.disk.usedGb"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-progress
|
||||||
|
type="dashboard"
|
||||||
|
:percentage="state.disk.usedPercent"
|
||||||
|
:color="colors"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row
|
||||||
|
:gutter="15"
|
||||||
|
class="py-1"
|
||||||
|
>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card
|
||||||
|
v-if="state.cpu"
|
||||||
|
class="card_item"
|
||||||
|
:body-style="{ height: '180px', 'overflow-y': 'scroll' }"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div>CPU</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">physical number of cores:</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.cpu.cores"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row
|
||||||
|
v-for="(item, index) in state.cpu.cpus"
|
||||||
|
:key="index"
|
||||||
|
:gutter="10"
|
||||||
|
>
|
||||||
|
<el-col :span="12">core {{ index }}:</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
><el-progress
|
||||||
|
type="line"
|
||||||
|
:percentage="+item.toFixed(0)"
|
||||||
|
:color="colors"
|
||||||
|
/></el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card
|
||||||
|
v-if="state.ram"
|
||||||
|
class="card_item"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div>Ram</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">total (MB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.ram.totalMb"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">used (MB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.ram.usedMb"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">total (GB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="state.ram.totalMb / 1024"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">used (GB)</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
v-text="(state.ram.usedMb / 1024).toFixed(2)"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-progress
|
||||||
|
type="dashboard"
|
||||||
|
:percentage="state.ram.usedPercent"
|
||||||
|
:color="colors"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getSystemState } from '@/api/system'
|
||||||
|
import { onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'State',
|
||||||
|
})
|
||||||
|
|
||||||
|
const timer = ref(null)
|
||||||
|
const state = ref({})
|
||||||
|
const colors = ref([
|
||||||
|
{ color: '#5cb87a', percentage: 20 },
|
||||||
|
{ color: '#e6a23c', percentage: 40 },
|
||||||
|
{ color: '#f56c6c', percentage: 80 }
|
||||||
|
])
|
||||||
|
|
||||||
|
const reload = async() => {
|
||||||
|
const { data } = await getSystemState()
|
||||||
|
state.value = data.server
|
||||||
|
}
|
||||||
|
|
||||||
|
reload()
|
||||||
|
timer.value = setInterval(() => {
|
||||||
|
reload()
|
||||||
|
}, 1000 * 10)
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
timer.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.card_item {
|
||||||
|
@apply h-80 text-xl;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,387 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" class="demo-form-inline"
|
||||||
|
@keyup.enter="onSubmit">
|
||||||
|
<el-form-item label="ID" prop="id">
|
||||||
|
<template #label>
|
||||||
|
<span>ID</span>
|
||||||
|
</template>
|
||||||
|
<el-input v-model="searchInfo.id" type="input" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上级Id" prop="pid">
|
||||||
|
<template #label>
|
||||||
|
<span>上级Id</span>
|
||||||
|
</template>
|
||||||
|
<el-input v-model="searchInfo.pid" type="input" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区级别" prop="level">
|
||||||
|
<template #label>
|
||||||
|
<span>地区级别</span>
|
||||||
|
</template>
|
||||||
|
<el-input v-model="searchInfo.level" type="input" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="search" @click="onSubmit">查询</el-button>
|
||||||
|
<el-button icon="refresh" @click="onReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
|
||||||
|
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
||||||
|
@click="onDelete">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table ref="multipleTable" style="width: 100%" tooltip-effect="dark" :data="tableData" row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
|
||||||
|
<el-table-column align="left" label="ID" prop="ID" width="120" />
|
||||||
|
<el-table-column align="left" label="上级Id" prop="pid" width="120" />
|
||||||
|
<el-table-column align="left" label="地区名称" prop="name" width="300" />
|
||||||
|
<el-table-column align="left" label="是否显示" prop="visible" width="120">
|
||||||
|
<template #default="scope">{{ formatBoolean(scope.row.visible) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="排序值" prop="weight" width="120" />
|
||||||
|
<el-table-column align="left" label="地区级别" prop="level" width="120" />
|
||||||
|
<el-table-column align="left" label="操作" fixed="right" min-width="240">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" link class="table-button" @click="getDetails(scope.row)">
|
||||||
|
<el-icon style="margin-right: 5px">
|
||||||
|
<InfoFilled />
|
||||||
|
</el-icon>
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link icon="edit" class="table-button"
|
||||||
|
@click="updateSystemAreaFunc(scope.row)">变更</el-button>
|
||||||
|
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination layout="total, sizes, prev, pager, next, jumper" :current-page="page" :page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]" :total="total" @current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer v-model="dialogFormVisible" size="800" :show-close="false" :before-close="closeDialog">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">{{ type==='create'?'添加':'修改' }}</span>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="enterDialog">确 定</el-button>
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form ref="elFormRef" :model="formData" label-position="top" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="上级Id:" prop="pid">
|
||||||
|
<el-input v-model.number="formData.pid" :clearable="true" placeholder="请输入上级Id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区名称:" prop="name">
|
||||||
|
<el-input v-model="formData.name" :clearable="true" placeholder="请输入地区名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否显示:" prop="visible">
|
||||||
|
<el-switch v-model="formData.visible" active-color="#13ce66" inactive-color="#ff4949" active-text="是"
|
||||||
|
inactive-text="否" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序值:" prop="weight">
|
||||||
|
<el-input v-model.number="formData.weight" :clearable="true" placeholder="请输入排序值" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区级别:" prop="level">
|
||||||
|
<el-input v-model.number="formData.level" :clearable="true" placeholder="请输入地区级别" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-drawer v-model="detailShow" size="800" :before-close="closeDetailShow" destroy-on-close>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">查看详情</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="ID">
|
||||||
|
{{ formData.ID }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="上级Id">
|
||||||
|
{{ formData.pid }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区名称">
|
||||||
|
{{ formData.name }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="是否显示">
|
||||||
|
{{ formatBoolean(formData.visible) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="排序值">
|
||||||
|
{{ formData.weight }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区级别">
|
||||||
|
{{ formData.level }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createSystemArea,
|
||||||
|
deleteSystemArea,
|
||||||
|
deleteSystemAreaByIds,
|
||||||
|
updateSystemArea,
|
||||||
|
findSystemArea,
|
||||||
|
getSystemAreaList
|
||||||
|
} from '@/api/systemArea'
|
||||||
|
|
||||||
|
// 全量引入格式化工具 请按需保留
|
||||||
|
import { formatBoolean } from '@/utils/format'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SystemArea'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动化生成的字典(可能为空)以及字段
|
||||||
|
const formData = ref({
|
||||||
|
id: 0,
|
||||||
|
pid: 0,
|
||||||
|
name: '',
|
||||||
|
visible: false,
|
||||||
|
weight: 0,
|
||||||
|
level: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
const elSearchFormRef = ref()
|
||||||
|
|
||||||
|
// =========== 表格控制部分 ===========
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const onSubmit = () => {
|
||||||
|
elSearchFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
if (searchInfo.value.visible === '') {
|
||||||
|
searchInfo.value.visible = null
|
||||||
|
}
|
||||||
|
if (searchInfo.value.pid === '') {
|
||||||
|
searchInfo.value.pid = null
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页面容量
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getSystemAreaList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// ============== 表格控制部分结束 ===============
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
const setOptions = async() => {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
setOptions()
|
||||||
|
|
||||||
|
// 多选数据
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
// 多选
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteRow = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteSystemAreaFunc(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选删除
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const IDs = []
|
||||||
|
if (multipleSelection.value.length === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择要删除的数据'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
multipleSelection.value &&
|
||||||
|
multipleSelection.value.map(item => {
|
||||||
|
IDs.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteSystemAreaByIds({ IDs })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === IDs.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行为控制标记(弹窗内部需要增还是改)
|
||||||
|
const type = ref('')
|
||||||
|
|
||||||
|
// 更新行
|
||||||
|
const updateSystemAreaFunc = async(row) => {
|
||||||
|
const res = await findSystemArea({ ID: row.ID })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resystemArea
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteSystemAreaFunc = async(row) => {
|
||||||
|
const res = await deleteSystemArea({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗控制标记
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
|
||||||
|
// 查看详情控制标记
|
||||||
|
const detailShow = ref(false)
|
||||||
|
|
||||||
|
// 打开详情弹窗
|
||||||
|
const openDetailShow = () => {
|
||||||
|
detailShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开详情
|
||||||
|
const getDetails = async(row) => {
|
||||||
|
// 打开弹窗
|
||||||
|
const res = await findSystemArea({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resystemArea
|
||||||
|
openDetailShow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情弹窗
|
||||||
|
const closeDetailShow = () => {
|
||||||
|
detailShow.value = false
|
||||||
|
formData.value = {
|
||||||
|
pid: 0,
|
||||||
|
name: '',
|
||||||
|
visible: false,
|
||||||
|
weight: 0,
|
||||||
|
level: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
pid: 0,
|
||||||
|
name: '',
|
||||||
|
visible: false,
|
||||||
|
weight: 0,
|
||||||
|
level: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 弹窗确定
|
||||||
|
const enterDialog = async() => {
|
||||||
|
elFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createSystemArea(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateSystemArea(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createSystemArea(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-form-box">
|
||||||
|
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
|
||||||
|
<el-form-item label="上级Id:" prop="pid">
|
||||||
|
<el-input v-model.number="formData.pid" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区名称:" prop="name">
|
||||||
|
<el-input v-model="formData.name" :clearable="true" placeholder="请输入地区名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否显示:" prop="visible">
|
||||||
|
<el-switch v-model="formData.visible" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序值:" prop="weight">
|
||||||
|
<el-input v-model.number="formData.weight" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区级别:" prop="level">
|
||||||
|
<el-input v-model.number="formData.level" :clearable="true" placeholder="请输入" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="save">保存</el-button>
|
||||||
|
<el-button type="primary" @click="back">返回</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createSystemArea,
|
||||||
|
updateSystemArea,
|
||||||
|
findSystemArea
|
||||||
|
} from '@/api/systemArea'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SystemAreaForm'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动获取字典
|
||||||
|
import { getDictFunc } from '@/utils/format'
|
||||||
|
import { useRoute, useRouter } from "vue-router"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const type = ref('')
|
||||||
|
const formData = ref({
|
||||||
|
pid: 0,
|
||||||
|
name: '',
|
||||||
|
visible: false,
|
||||||
|
weight: 0,
|
||||||
|
level: 0,
|
||||||
|
})
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
|
||||||
|
// 初始化方法
|
||||||
|
const init = async () => {
|
||||||
|
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
|
||||||
|
if (route.query.id) {
|
||||||
|
const res = await findSystemArea({ ID: route.query.id })
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resystemArea
|
||||||
|
type.value = 'update'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type.value = 'create'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
// 保存按钮
|
||||||
|
const save = async() => {
|
||||||
|
elFormRef.value?.validate( async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createSystemArea(formData.value)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateSystemArea(formData.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createSystemArea(formData.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
const back = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,239 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<warning-bar title="id , created_at , updated_at , deleted_at 会自动生成请勿重复创建。搜索时如果条件为LIKE只支持字符串" />
|
||||||
|
<el-form
|
||||||
|
ref="fieldDialogFrom"
|
||||||
|
:model="middleDate"
|
||||||
|
label-width="120px"
|
||||||
|
label-position="right"
|
||||||
|
:rules="rules"
|
||||||
|
class="grid grid-cols-2"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="字段名称"
|
||||||
|
prop="fieldName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="middleDate.fieldName"
|
||||||
|
autocomplete="off"
|
||||||
|
style="width:80%"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
style="width:18%;margin-left:2%"
|
||||||
|
@click="autoFill"
|
||||||
|
>
|
||||||
|
<span style="font-size: 12px">自动填充</span>
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="字段中文名"
|
||||||
|
prop="fieldDesc"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="middleDate.fieldDesc"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="字段JSON"
|
||||||
|
prop="fieldJson"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="middleDate.fieldJson"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="数据库字段名"
|
||||||
|
prop="columnName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="middleDate.columnName"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="数据库字段描述"
|
||||||
|
prop="comment"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="middleDate.comment"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="字段类型"
|
||||||
|
prop="fieldType"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="middleDate.fieldType"
|
||||||
|
style="width:100%"
|
||||||
|
placeholder="请选择字段类型"
|
||||||
|
clearable
|
||||||
|
@change="clearOther"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in typeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="middleDate.fieldType === 'enum' ? '枚举值' : '类型长度'"
|
||||||
|
prop="dataTypeLong"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="middleDate.dataTypeLong"
|
||||||
|
:placeholder="middleDate.fieldType === 'enum'?`例:'北京','天津'`:'数据库类型长度'"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="字段查询条件"
|
||||||
|
prop="fieldSearchType"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="middleDate.fieldSearchType"
|
||||||
|
:disabled="middleDate.fieldType === 'json'"
|
||||||
|
style="width:100%"
|
||||||
|
placeholder="请选择字段查询条件"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in typeSearchOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
:disabled="canSelect(item.value)"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="关联字典"
|
||||||
|
prop="dictType"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="middleDate.dictType"
|
||||||
|
style="width:100%"
|
||||||
|
:disabled="middleDate.fieldType!=='int'&&middleDate.fieldType!=='string'"
|
||||||
|
placeholder="请选择字典"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in dictOptions"
|
||||||
|
:key="item.type"
|
||||||
|
:label="`${item.type}(${item.name})`"
|
||||||
|
:value="item.type"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="主键">
|
||||||
|
<el-checkbox v-model="middleDate.primaryKey" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否排序">
|
||||||
|
<el-switch v-model="middleDate.sort" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否必填">
|
||||||
|
<el-switch v-model="middleDate.require" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否可清空">
|
||||||
|
<el-switch v-model="middleDate.clearable" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="校验失败文案">
|
||||||
|
<el-input v-model="middleDate.errorText" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { toLowerCase, toSQLLine } from '@/utils/stringFun'
|
||||||
|
import { getSysDictionaryList } from '@/api/sysDictionary'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'FieldDialog'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dialogMiddle: {
|
||||||
|
type: Object,
|
||||||
|
default: function() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: function() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
typeSearchOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: function() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const middleDate = ref({})
|
||||||
|
const dictOptions = ref([])
|
||||||
|
|
||||||
|
const rules = ref({
|
||||||
|
fieldName: [
|
||||||
|
{ required: true, message: '请输入字段英文名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
fieldDesc: [
|
||||||
|
{ required: true, message: '请输入字段中文名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
fieldJson: [
|
||||||
|
{ required: true, message: '请输入字段格式化json', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
columnName: [
|
||||||
|
{ required: true, message: '请输入数据库字段', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
fieldType: [
|
||||||
|
{ required: true, message: '请选择字段类型', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const init = async() => {
|
||||||
|
middleDate.value = props.dialogMiddle
|
||||||
|
const dictRes = await getSysDictionaryList({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999999
|
||||||
|
})
|
||||||
|
|
||||||
|
dictOptions.value = dictRes.data
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
|
||||||
|
const autoFill = () => {
|
||||||
|
middleDate.value.fieldJson = toLowerCase(middleDate.value.fieldName)
|
||||||
|
middleDate.value.columnName = toSQLLine(middleDate.value.fieldJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
const canSelect = (item) => {
|
||||||
|
const fieldType = middleDate.value.fieldType
|
||||||
|
if (fieldType !== 'string' && item === 'LIKE') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fieldType !== 'int' && fieldType !== 'time.Time' && fieldType !== 'float64') && (item === 'BETWEEN' || item === 'NOT BETWEEN')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearOther = () => {
|
||||||
|
middleDate.value.fieldSearchType = ''
|
||||||
|
middleDate.value.dictType = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldDialogFrom = ref(null)
|
||||||
|
defineExpose({ fieldDialogFrom })
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<el-tabs v-model="activeName">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="(item, key) in previewCode"
|
||||||
|
:key="key"
|
||||||
|
:label="key"
|
||||||
|
:name="key"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:id="key"
|
||||||
|
class="h-[50vh] bg-white px-5 overflow-y-scroll"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { marked } from 'marked'
|
||||||
|
import hljs from 'highlight.js'
|
||||||
|
import 'highlight.js/styles/atom-one-dark.css'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
previewCode: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeName = ref('')
|
||||||
|
onMounted(() => {
|
||||||
|
marked.setOptions({
|
||||||
|
renderer: new marked.Renderer(),
|
||||||
|
highlight: function(code) {
|
||||||
|
return hljs.highlightAuto(code).value
|
||||||
|
},
|
||||||
|
pedantic: false,
|
||||||
|
gfm: true,
|
||||||
|
tables: true,
|
||||||
|
breaks: false,
|
||||||
|
sanitize: false,
|
||||||
|
smartLists: true,
|
||||||
|
smartypants: false,
|
||||||
|
xhtml: false,
|
||||||
|
langPrefix: 'hljs language-'
|
||||||
|
})
|
||||||
|
for (const key in props.previewCode) {
|
||||||
|
if (activeName.value === '') {
|
||||||
|
activeName.value = key
|
||||||
|
}
|
||||||
|
document.getElementById(key).innerHTML = marked(props.previewCode[key])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectText = () => {
|
||||||
|
const element = document.getElementById(activeName.value)
|
||||||
|
if (document.body.createTextRange) {
|
||||||
|
const range = document.body.createTextRange()
|
||||||
|
range.moveToElementText(element)
|
||||||
|
range.select()
|
||||||
|
} else if (window.getSelection) {
|
||||||
|
const selection = window.getSelection()
|
||||||
|
const range = document.createRange()
|
||||||
|
range.selectNodeContents(element)
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(range)
|
||||||
|
} else {
|
||||||
|
alert('none')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const copy = () => {
|
||||||
|
selectText()
|
||||||
|
document.execCommand('copy')
|
||||||
|
ElMessage.success('复制成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ copy })
|
||||||
|
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,227 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="goAutoCode(null)"
|
||||||
|
>新增</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="tableData">
|
||||||
|
<el-table-column
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="id"
|
||||||
|
width="60"
|
||||||
|
prop="ID"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="日期"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="结构体名"
|
||||||
|
min-width="150"
|
||||||
|
prop="structName"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="结构体描述"
|
||||||
|
min-width="150"
|
||||||
|
prop="structCNName"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="表名称"
|
||||||
|
min-width="150"
|
||||||
|
prop="tableName"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="回滚标记"
|
||||||
|
min-width="150"
|
||||||
|
prop="flag"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
v-if="scope.row.flag"
|
||||||
|
type="danger"
|
||||||
|
|
||||||
|
effect="dark"
|
||||||
|
>
|
||||||
|
已回滚
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-else
|
||||||
|
|
||||||
|
type="success"
|
||||||
|
effect="dark"
|
||||||
|
>
|
||||||
|
未回滚
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="操作"
|
||||||
|
min-width="240"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
:disabled="scope.row.flag === 1"
|
||||||
|
@click="rollbackFunc(scope.row,true)"
|
||||||
|
>回滚(删表)</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
:disabled="scope.row.flag === 1"
|
||||||
|
@click="rollbackFunc(scope.row,false)"
|
||||||
|
>回滚(不删表)</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="goAutoCode(scope.row)"
|
||||||
|
>复用</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="deleteRow(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getSysHistory, rollback, delSysHistory } from '@/api/autoCode.js'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { formatDate } from '@/utils/format'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'AutoCodeAdmin'
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getSysHistory({
|
||||||
|
page: page.value,
|
||||||
|
pageSize: pageSize.value
|
||||||
|
})
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
const deleteRow = async(row) => {
|
||||||
|
ElMessageBox.confirm('此操作将删除本历史, 是否继续?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const res = await delSysHistory({ id: Number(row.ID) })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const rollbackFunc = async(row, flag) => {
|
||||||
|
if (flag) {
|
||||||
|
ElMessageBox.confirm(`此操作将删除自动创建的文件和api(会删除表!!!), 是否继续?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
ElMessageBox.confirm(`此操作将删除自动创建的文件和api(会删除表!!!), 请继续确认!!!`, '会删除表', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
ElMessageBox.confirm(`此操作将删除自动创建的文件和api(会删除表!!!), 请继续确认!!!`, '会删除表', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const res = await rollback({ id: Number(row.ID), deleteTable: flag })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('回滚成功')
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessageBox.confirm(`此操作将删除自动创建的文件和api, 是否继续?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const res = await rollback({ id: Number(row.ID), deleteTable: flag })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('回滚成功')
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const goAutoCode = (row) => {
|
||||||
|
if (row) {
|
||||||
|
router.push({ name: 'autoCodeEdit', params: {
|
||||||
|
id: row.ID
|
||||||
|
}})
|
||||||
|
} else {
|
||||||
|
router.push({ name: 'autoCode' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<warning-bar
|
||||||
|
href="https://www.bilibili.com/video/BV1kv4y1g7nT?p=3"
|
||||||
|
title="此功能为开发环境使用,不建议发布到生产,具体使用效果请看视频https://www.bilibili.com/video/BV1kv4y1g7nT?p=3"
|
||||||
|
/>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list gap-3 flex items-center">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="openDialog('addApi')"
|
||||||
|
>新增</el-button>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=3&vd_source=f2640257c21e3b547a790461ed94875e')"
|
||||||
|
><VideoCameraFilled /></el-icon>
|
||||||
|
</div>
|
||||||
|
<el-table :data="tableData">
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="id"
|
||||||
|
width="60"
|
||||||
|
prop="ID"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="包名"
|
||||||
|
width="150"
|
||||||
|
prop="packageName"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="展示名"
|
||||||
|
width="150"
|
||||||
|
prop="label"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="描述"
|
||||||
|
min-width="150"
|
||||||
|
prop="desc"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="操作"
|
||||||
|
width="200"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="delete"
|
||||||
|
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="deleteApiFunc(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:before-close="closeDialog"
|
||||||
|
title="创建Package"
|
||||||
|
>
|
||||||
|
<warning-bar title="新增Pkg用于自动化代码使用" />
|
||||||
|
<el-form
|
||||||
|
ref="pkgForm"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="包名"
|
||||||
|
prop="packageName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.packageName"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="展示名"
|
||||||
|
prop="label"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.label"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="描述"
|
||||||
|
prop="desc"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.desc"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createPackageApi,
|
||||||
|
getPackageApi,
|
||||||
|
deletePackageApi,
|
||||||
|
} from '@/api/autoCode'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { toDoc } from '@/utils/doc'
|
||||||
|
import { VideoCameraFilled } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'AutoPkg',
|
||||||
|
})
|
||||||
|
|
||||||
|
const form = ref({
|
||||||
|
packageName: '',
|
||||||
|
label: '',
|
||||||
|
desc: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const validateNum = (rule, value, callback) => {
|
||||||
|
if ((/^\d+$/.test(value[0]))) {
|
||||||
|
callback(new Error('不能够以数字开头'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = ref({
|
||||||
|
packageName: [
|
||||||
|
{ required: true, message: '请输入包名', trigger: 'blur' },
|
||||||
|
{ validator: validateNum, trigger: 'blur' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
const openDialog = () => {
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
form.value = {
|
||||||
|
packageName: '',
|
||||||
|
label: '',
|
||||||
|
desc: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkgForm = ref(null)
|
||||||
|
const enterDialog = async() => {
|
||||||
|
pkgForm.value.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
const res = await createPackageApi(form.value)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '添加成功',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableData = ref([])
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getPackageApi()
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.pkgs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteApiFunc = async(row) => {
|
||||||
|
ElMessageBox.confirm('此操作仅删除数据库中的pkg存储,后端相应目录结构请自行删除与数据库保持一致!', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async() => {
|
||||||
|
const res = await deletePackageApi(row)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功!'
|
||||||
|
})
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,350 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<el-form
|
||||||
|
label-width="140px"
|
||||||
|
class="w-[680px]"
|
||||||
|
>
|
||||||
|
<el-form-item label="插件名">
|
||||||
|
<el-input
|
||||||
|
v-model="form.plugName"
|
||||||
|
placeholder="必填(英文大写字母开头)"
|
||||||
|
@blur="titleCase"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="路由组">
|
||||||
|
<el-input
|
||||||
|
v-model="form.routerGroup"
|
||||||
|
placeholder="将会作为插件路由组使用"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="使用全局属性">
|
||||||
|
<el-checkbox v-model="form.hasGlobal" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="form.hasGlobal"
|
||||||
|
label="全局属性"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(i,k) in form.global"
|
||||||
|
:key="k"
|
||||||
|
class="plug-row"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<el-input
|
||||||
|
v-model="i.key"
|
||||||
|
placeholder="key 必填"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-select
|
||||||
|
v-model="i.type"
|
||||||
|
placeholder="type 必填"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
label="string"
|
||||||
|
value="string"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="int"
|
||||||
|
value="int"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="float32"
|
||||||
|
value="float32"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="float64"
|
||||||
|
value="float64"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="bool"
|
||||||
|
value="bool"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-input
|
||||||
|
v-model="i.desc"
|
||||||
|
placeholder="备注 必填"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
:icon="Plus"
|
||||||
|
circle
|
||||||
|
@click="addkv(form.global)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
:icon="Minus"
|
||||||
|
circle
|
||||||
|
@click="minkv(form.global,k)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="使用Request">
|
||||||
|
<el-checkbox v-model="form.hasRequest" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="form.hasRequest"
|
||||||
|
label="Request"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(i,k) in form.request"
|
||||||
|
:key="k"
|
||||||
|
class="plug-row"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<el-input
|
||||||
|
v-model="i.key"
|
||||||
|
placeholder="key 必填"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-select
|
||||||
|
v-model="i.type"
|
||||||
|
placeholder="type 必填"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
label="string"
|
||||||
|
value="string"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="int"
|
||||||
|
value="int"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="float32"
|
||||||
|
value="float32"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="float64"
|
||||||
|
value="float64"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="bool"
|
||||||
|
value="bool"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-input
|
||||||
|
v-model="i.desc"
|
||||||
|
placeholder="备注 必填"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
:icon="Plus"
|
||||||
|
circle
|
||||||
|
@click="addkv(form.request)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
:icon="Minus"
|
||||||
|
circle
|
||||||
|
@click="minkv(form.request,k)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="使用Response">
|
||||||
|
<el-checkbox v-model="form.hasResponse" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="form.hasResponse"
|
||||||
|
label="Response"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(i,k) in form.response"
|
||||||
|
:key="k"
|
||||||
|
class="plug-row"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<el-input
|
||||||
|
v-model="i.key"
|
||||||
|
placeholder="key 必填"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-select
|
||||||
|
v-model="i.type"
|
||||||
|
placeholder="type 必填"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
label="string"
|
||||||
|
value="string"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="int"
|
||||||
|
value="int"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="float32"
|
||||||
|
value="float32"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="float64"
|
||||||
|
value="float64"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="bool"
|
||||||
|
value="bool"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-input
|
||||||
|
v-model="i.desc"
|
||||||
|
placeholder="备注 必填"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
:icon="Plus"
|
||||||
|
circle
|
||||||
|
@click="addkv(form.response)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
:icon="Minus"
|
||||||
|
circle
|
||||||
|
@click="minkv(form.response,k)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="createPlug"
|
||||||
|
>创建</el-button>
|
||||||
|
<el-icon
|
||||||
|
class="cursor-pointer ml-3"
|
||||||
|
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=13&vd_source=f2640257c21e3b547a790461ed94875e')"
|
||||||
|
><VideoCameraFilled /></el-icon>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { toUpperCase } from '@/utils/stringFun'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Plus,
|
||||||
|
Minus, VideoCameraFilled
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
import { createPlugApi } from '@/api/autoCode.js'
|
||||||
|
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import { toDoc } from '@/utils/doc'
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
plugName: '',
|
||||||
|
routerGroup: '',
|
||||||
|
hasGlobal: true,
|
||||||
|
hasRequest: true,
|
||||||
|
hasResponse: true,
|
||||||
|
global: [{
|
||||||
|
key: '',
|
||||||
|
type: '',
|
||||||
|
desc: '',
|
||||||
|
}],
|
||||||
|
request: [{
|
||||||
|
key: '',
|
||||||
|
type: '',
|
||||||
|
desc: '',
|
||||||
|
}],
|
||||||
|
response: [{
|
||||||
|
key: '',
|
||||||
|
type: '',
|
||||||
|
desc: '',
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
const titleCase = () => {
|
||||||
|
form.plugName = toUpperCase(form.plugName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createPlug = async() => {
|
||||||
|
if (!form.plugName || !form.routerGroup) {
|
||||||
|
ElMessage.error('插件名称和插件路由组为必填项')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (form.hasGlobal) {
|
||||||
|
const intercept = form.global.some(i => {
|
||||||
|
if (!i.key || !i.type) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (intercept) {
|
||||||
|
ElMessage.error('全局属性的key和type为必填项')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (form.hasRequest) {
|
||||||
|
const intercept = form.request.some(i => {
|
||||||
|
if (!i.key || !i.type) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (intercept) {
|
||||||
|
ElMessage.error('请求属性的key和type为必填项')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (form.hasResponse) {
|
||||||
|
const intercept = form.response.some(i => {
|
||||||
|
if (!i.key || !i.type) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (intercept) {
|
||||||
|
ElMessage.error('响应属性的key和type为必填项')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const res = await createPlugApi(form)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessageBox('创建成功,插件已自动写入后端plugin目录下,请按照自己的逻辑进行创造')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addkv = (arr) => {
|
||||||
|
arr.push({
|
||||||
|
key: '',
|
||||||
|
value: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const minkv = (arr, key) => {
|
||||||
|
if (arr.length === 1) {
|
||||||
|
ElMessage.warning('至少有一个全局属性')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arr.splice(key, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.plug-row{
|
||||||
|
@apply flex items-center w-full;
|
||||||
|
&+&{
|
||||||
|
@apply mt-3;
|
||||||
|
}
|
||||||
|
&>span{
|
||||||
|
@apply ml-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,723 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<WarningBar
|
||||||
|
title="本功能提供同步的表格导出功能,大数据量的异步表格导出功能,可以选择点我定制"
|
||||||
|
href="https://flipped-aurora.feishu.cn/docx/KwjxdnvatozgwIxGV0rcpkZSn4d"
|
||||||
|
/>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form
|
||||||
|
ref="elSearchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
:model="searchInfo"
|
||||||
|
class="demo-form-inline"
|
||||||
|
:rules="searchRule"
|
||||||
|
@keyup.enter="onSubmit"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="创建日期"
|
||||||
|
prop="createdAt"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
创建日期
|
||||||
|
<el-tooltip content="搜索范围是开始日期(包含)至结束日期(不包含)">
|
||||||
|
<el-icon><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="searchInfo.startCreatedAt"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="开始日期"
|
||||||
|
:disabled-date="time=> searchInfo.endCreatedAt ? time.getTime() > searchInfo.endCreatedAt.getTime() : false"
|
||||||
|
/>
|
||||||
|
—
|
||||||
|
<el-date-picker
|
||||||
|
v-model="searchInfo.endCreatedAt"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="结束日期"
|
||||||
|
:disabled-date="time=> searchInfo.startCreatedAt ? time.getTime() < searchInfo.startCreatedAt.getTime() : false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="模板名称"
|
||||||
|
prop="name"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.name"
|
||||||
|
placeholder="搜索条件"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="表名称"
|
||||||
|
prop="tableName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.tableName"
|
||||||
|
placeholder="搜索条件"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="模板标识"
|
||||||
|
prop="templateID"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="searchInfo.templateID"
|
||||||
|
placeholder="搜索条件"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="search"
|
||||||
|
@click="onSubmit"
|
||||||
|
>查询</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="refresh"
|
||||||
|
@click="onReset"
|
||||||
|
>重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="openDialog"
|
||||||
|
>新增</el-button>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
icon="delete"
|
||||||
|
style="margin-left: 10px;"
|
||||||
|
:disabled="!multipleSelection.length"
|
||||||
|
@click="onDelete"
|
||||||
|
>删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
ref="multipleTable"
|
||||||
|
style="width: 100%"
|
||||||
|
tooltip-effect="dark"
|
||||||
|
:data="tableData"
|
||||||
|
row-key="ID"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="日期"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="数据库"
|
||||||
|
prop="name"
|
||||||
|
width="120"
|
||||||
|
>
|
||||||
|
<template #defalut="scope">
|
||||||
|
<span>{{ scope.row.dbNname || "GVA库" }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="模板名称"
|
||||||
|
prop="name"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="表名称"
|
||||||
|
prop="tableName"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="模板标识"
|
||||||
|
prop="templateID"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="模板信息"
|
||||||
|
prop="templateInfo"
|
||||||
|
min-width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
align="left"
|
||||||
|
label="操作"
|
||||||
|
min-width="120"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="edit"
|
||||||
|
class="table-button"
|
||||||
|
@click="updateSysExportTemplateFunc(scope.row)"
|
||||||
|
>变更</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
icon="delete"
|
||||||
|
@click="deleteRow(scope.row)"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:before-close="closeDialog"
|
||||||
|
:title="type==='create'?'添加':'修改'"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-scrollbar height="500px">
|
||||||
|
<el-form
|
||||||
|
ref="elFormRef"
|
||||||
|
:model="formData"
|
||||||
|
label-position="right"
|
||||||
|
:rules="rule"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="业务库"
|
||||||
|
prop="dbName"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<el-tooltip
|
||||||
|
content="注:需要提前到db-list自行配置多数据库,如未配置需配置后重启服务方可使用。若无法选择,请到config.yaml中设置disabled:false,选择导入导出的目标库。"
|
||||||
|
placement="bottom"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<div> 业务库 <el-icon><QuestionFilled /></el-icon> </div>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<el-select
|
||||||
|
v-model="formData.dbName"
|
||||||
|
clearable
|
||||||
|
placeholder="选择业务库"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in dbList"
|
||||||
|
:key="item.aliasName"
|
||||||
|
:value="item.aliasName"
|
||||||
|
:label="item.aliasName"
|
||||||
|
:disabled="item.disable"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<span>{{ item.aliasName }}</span>
|
||||||
|
<span style="float:right;color:#8492a6;font-size:13px">{{ item.dbName }}</span>
|
||||||
|
</div>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="模板名称:"
|
||||||
|
prop="name"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.name"
|
||||||
|
:clearable="true"
|
||||||
|
placeholder="请输入模板名称"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="表名称:"
|
||||||
|
prop="tableName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.tableName"
|
||||||
|
:clearable="true"
|
||||||
|
placeholder="请输入要导出的表名称"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="模板标识:"
|
||||||
|
prop="templateID"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.templateID"
|
||||||
|
:clearable="true"
|
||||||
|
placeholder="模板标识为前端组件需要挂在的标识属性"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="模板信息:"
|
||||||
|
prop="templateInfo"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.templateInfo"
|
||||||
|
type="textarea"
|
||||||
|
:rows="12"
|
||||||
|
:clearable="true"
|
||||||
|
:placeholder="templatePlaceholder"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="默认导出条数:"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.limit"
|
||||||
|
:step="1"
|
||||||
|
:step-strictly="true"
|
||||||
|
:precision="0"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="默认排序条件:"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.order"
|
||||||
|
placeholder="例:id desc"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="导出条件:"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(condition,key) in formData.conditions"
|
||||||
|
:key="key"
|
||||||
|
class="flex gap-4 w-full mb-2"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="condition.from"
|
||||||
|
placeholder="需要从查询条件取的json key"
|
||||||
|
/>
|
||||||
|
<el-input
|
||||||
|
v-model="condition.column"
|
||||||
|
placeholder="表对应的column"
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
v-model="condition.operator"
|
||||||
|
placeholder="请选择查询条件"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in typeSearchOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
icon="delete"
|
||||||
|
@click="() => formData.conditions.splice(key, 1)"
|
||||||
|
>删除</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end w-full">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="plus"
|
||||||
|
@click="addCondition"
|
||||||
|
>添加条件</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-scrollbar>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取 消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="enterDialog"
|
||||||
|
>确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
createSysExportTemplate,
|
||||||
|
deleteSysExportTemplate,
|
||||||
|
deleteSysExportTemplateByIds,
|
||||||
|
updateSysExportTemplate,
|
||||||
|
findSysExportTemplate,
|
||||||
|
getSysExportTemplateList
|
||||||
|
} from '@/api/exportTemplate.js'
|
||||||
|
|
||||||
|
// 全量引入格式化工具 请按需保留
|
||||||
|
import { formatDate } from '@/utils/format'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { getDB } from '@/api/autoCode'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ExportTemplate'
|
||||||
|
})
|
||||||
|
|
||||||
|
const templatePlaceholder = `模板信息格式:key标识数据库column列名称,value标识导出excel列名称,如下:
|
||||||
|
{
|
||||||
|
"table_name1":"第一列",
|
||||||
|
"table_name2":"第二列",
|
||||||
|
"table_name3":"第三列",
|
||||||
|
"table_name4":"第四列",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// 自动化生成的字典(可能为空)以及字段
|
||||||
|
const formData = ref({
|
||||||
|
name: '',
|
||||||
|
tableName: '',
|
||||||
|
templateID: '',
|
||||||
|
templateInfo: '',
|
||||||
|
limit: 0,
|
||||||
|
order: '',
|
||||||
|
conditions: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const typeSearchOptions = ref([
|
||||||
|
{
|
||||||
|
label: '=',
|
||||||
|
value: '='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '<>',
|
||||||
|
value: '<>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '>',
|
||||||
|
value: '>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '<',
|
||||||
|
value: '<'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'LIKE',
|
||||||
|
value: 'LIKE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'BETWEEN',
|
||||||
|
value: 'BETWEEN'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'NOT BETWEEN',
|
||||||
|
value: 'NOT BETWEEN'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const addCondition = () => {
|
||||||
|
formData.value.conditions.push({
|
||||||
|
from: '',
|
||||||
|
column: '',
|
||||||
|
operator: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const rule = reactive({
|
||||||
|
name: [{
|
||||||
|
required: true,
|
||||||
|
message: '',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
whitespace: true,
|
||||||
|
message: '不能只输入空格',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tableName: [{
|
||||||
|
required: true,
|
||||||
|
message: '',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
whitespace: true,
|
||||||
|
message: '不能只输入空格',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
templateID: [{
|
||||||
|
required: true,
|
||||||
|
message: '',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
whitespace: true,
|
||||||
|
message: '不能只输入空格',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
templateInfo: [{
|
||||||
|
required: true,
|
||||||
|
message: '',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
whitespace: true,
|
||||||
|
message: '不能只输入空格',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchRule = reactive({
|
||||||
|
createdAt: [
|
||||||
|
{ validator: (rule, value, callback) => {
|
||||||
|
if (searchInfo.value.startCreatedAt && !searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写结束日期'))
|
||||||
|
} else if (!searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt) {
|
||||||
|
callback(new Error('请填写开始日期'))
|
||||||
|
} else if (searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt && (searchInfo.value.startCreatedAt.getTime() === searchInfo.value.endCreatedAt.getTime() || searchInfo.value.startCreatedAt.getTime() > searchInfo.value.endCreatedAt.getTime())) {
|
||||||
|
callback(new Error('开始日期应当早于结束日期'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}, trigger: 'change' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const elFormRef = ref()
|
||||||
|
const elSearchFormRef = ref()
|
||||||
|
|
||||||
|
// =========== 表格控制部分 ===========
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
const searchInfo = ref({})
|
||||||
|
const dbList = ref([])
|
||||||
|
|
||||||
|
const getDbFunc = async() => {
|
||||||
|
const res = await getDB()
|
||||||
|
if (res.code === 0) {
|
||||||
|
dbList.value = res.data.dbList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDbFunc()
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const onSubmit = () => {
|
||||||
|
elSearchFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
page.value = 1
|
||||||
|
pageSize.value = 10
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页面容量
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const getTableData = async() => {
|
||||||
|
const table = await getSysExportTemplateList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableData()
|
||||||
|
|
||||||
|
// ============== 表格控制部分结束 ===============
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
const setOptions = async() => {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要的字典 可能为空 按需保留
|
||||||
|
setOptions()
|
||||||
|
|
||||||
|
// 多选数据
|
||||||
|
const multipleSelection = ref([])
|
||||||
|
// 多选
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteRow = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteSysExportTemplateFunc(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选删除
|
||||||
|
const onDelete = async() => {
|
||||||
|
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async() => {
|
||||||
|
const ids = []
|
||||||
|
if (multipleSelection.value.length === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择要删除的数据'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
multipleSelection.value &&
|
||||||
|
multipleSelection.value.map(item => {
|
||||||
|
ids.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteSysExportTemplateByIds({ ids })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === ids.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行为控制标记(弹窗内部需要增还是改)
|
||||||
|
const type = ref('')
|
||||||
|
|
||||||
|
// 更新行
|
||||||
|
const updateSysExportTemplateFunc = async(row) => {
|
||||||
|
const res = await findSysExportTemplate({ ID: row.ID })
|
||||||
|
type.value = 'update'
|
||||||
|
if (res.code === 0) {
|
||||||
|
formData.value = res.data.resysExportTemplate
|
||||||
|
if (!formData.value.conditions) {
|
||||||
|
formData.value.conditions = []
|
||||||
|
}
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteSysExportTemplateFunc = async(row) => {
|
||||||
|
const res = await deleteSysExportTemplate({ ID: row.ID })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === 1 && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗控制标记
|
||||||
|
const dialogFormVisible = ref(false)
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = () => {
|
||||||
|
type.value = 'create'
|
||||||
|
dialogFormVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogFormVisible.value = false
|
||||||
|
formData.value = {
|
||||||
|
name: '',
|
||||||
|
tableName: '',
|
||||||
|
templateID: '',
|
||||||
|
templateInfo: '',
|
||||||
|
limit: 0,
|
||||||
|
order: '',
|
||||||
|
conditions: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 弹窗确定
|
||||||
|
const enterDialog = async() => {
|
||||||
|
// 判断 formData.templateInfo 是否为标准json格式 如果不是标准json 则辅助调整
|
||||||
|
try {
|
||||||
|
JSON.parse(formData.value.templateInfo)
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '模板信息格式不正确,请检查'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const reqData = JSON.parse(JSON.stringify(formData.value))
|
||||||
|
for (let i = 0; i < reqData.conditions.length; i++) {
|
||||||
|
if (!reqData.conditions[i].from || !reqData.conditions[i].column || !reqData.conditions[i].operator) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '请填写完整的导出条件'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reqData.conditions[i].templateID = reqData.templateID
|
||||||
|
}
|
||||||
|
|
||||||
|
elFormRef.value?.validate(async(valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let res
|
||||||
|
switch (type.value) {
|
||||||
|
case 'create':
|
||||||
|
res = await createSysExportTemplate(reqData)
|
||||||
|
break
|
||||||
|
case 'update':
|
||||||
|
res = await updateSysExportTemplate(reqData)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
res = await createSysExportTemplate(reqData)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '创建/更改成功'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<div style="height:80vh">
|
||||||
|
<iframe
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
:src="`${basePath}:${basePort}/form-generator/#/`"
|
||||||
|
frameborder="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
const basePath = ref(import.meta.env.VITE_BASE_PATH)
|
||||||
|
const basePort = ref(import.meta.env.VITE_SERVER_PORT)
|
||||||
|
defineOptions({
|
||||||
|
name: 'FormGenerator'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition
|
||||||
|
mode="out-in"
|
||||||
|
name="el-fade-in-linear"
|
||||||
|
>
|
||||||
|
<keep-alive :include="routerStore.keepAliveRouters">
|
||||||
|
<component :is="Component" />
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouterStore } from '@/pinia/modules/router'
|
||||||
|
const routerStore = useRouterStore()
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'System'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-upload
|
||||||
|
drag
|
||||||
|
:action="`${path}/autoCode/installPlugin`"
|
||||||
|
:show-file-list="false"
|
||||||
|
:on-success="handleSuccess"
|
||||||
|
:on-error="handleSuccess"
|
||||||
|
name="plug"
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
拖拽或<em>点击上传</em>
|
||||||
|
</div>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">
|
||||||
|
请把安装包的zip拖拽至此处上传
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const path = ref(import.meta.env.VITE_BASE_API)
|
||||||
|
|
||||||
|
const handleSuccess = (res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
let msg = ``
|
||||||
|
res.data && res.data.forEach((item, index) => {
|
||||||
|
msg += `${index + 1}.${item.msg}\n`
|
||||||
|
})
|
||||||
|
alert(msg)
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<template>
|
||||||
|
<div class="p-5 bg-white">
|
||||||
|
<WarningBar title="目前只支持标准插件(通过插件模板生成的标准目录插件),非标准插件请自行打包" />
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<el-input
|
||||||
|
v-model="plugName"
|
||||||
|
placeholder="插件模板处填写的【插件名】"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="pubPlugin"
|
||||||
|
>打包插件</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { pubPlug } from '@/api/autoCode.js'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const plugName = ref('')
|
||||||
|
|
||||||
|
const pubPlugin = async() => {
|
||||||
|
const res = await pubPlug({ plugName: plugName.value })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,557 @@
|
||||||
|
<template>
|
||||||
|
<div class="system">
|
||||||
|
<el-form
|
||||||
|
ref="form"
|
||||||
|
:model="config"
|
||||||
|
label-width="240px"
|
||||||
|
>
|
||||||
|
<!-- System start -->
|
||||||
|
<el-collapse v-model="activeNames">
|
||||||
|
<el-collapse-item
|
||||||
|
title="系统配置"
|
||||||
|
name="1"
|
||||||
|
>
|
||||||
|
<el-form-item label="端口值">
|
||||||
|
<el-input v-model.number="config.system.addr" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据库类型">
|
||||||
|
<el-select
|
||||||
|
v-model="config.system['db-type']"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<el-option value="mysql" />
|
||||||
|
<el-option value="pgsql" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Oss类型">
|
||||||
|
<el-select
|
||||||
|
v-model="config.system['oss-type']"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<el-option value="local" />
|
||||||
|
<el-option value="qiniu" />
|
||||||
|
<el-option value="tencent-cos" />
|
||||||
|
<el-option value="aliyun-oss" />
|
||||||
|
<el-option value="huawei-obs" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="多点登录拦截">
|
||||||
|
<el-checkbox v-model="config.system['use-multipoint']">开启</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开启redis">
|
||||||
|
<el-checkbox v-model="config.system['use-redis']">开启</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="限流次数">
|
||||||
|
<el-input-number v-model.number="config.system['iplimit-count']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="限流时间">
|
||||||
|
<el-input-number v-model.number="config.system['iplimit-time']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-tooltip
|
||||||
|
content="请修改完成后,注意一并修改前端env环境下的VITE_BASE_PATH"
|
||||||
|
placement="top-start"
|
||||||
|
>
|
||||||
|
<el-form-item label="全局路由前缀">
|
||||||
|
<el-input v-model="config.system['router-prefix']" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
title="jwt签名"
|
||||||
|
name="2"
|
||||||
|
>
|
||||||
|
<el-form-item label="jwt签名">
|
||||||
|
<el-input v-model="config.jwt['signing-key']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="有效期">
|
||||||
|
<el-input v-model="config.jwt['expires-time']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="缓冲期">
|
||||||
|
<el-input v-model="config.jwt['buffer-time']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="签发者">
|
||||||
|
<el-input v-model="config.jwt.issuer" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
title="Zap日志配置"
|
||||||
|
name="3"
|
||||||
|
>
|
||||||
|
<el-form-item label="级别">
|
||||||
|
<el-input v-model.number="config.zap.level" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="输出">
|
||||||
|
<el-input v-model="config.zap.format" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="日志前缀">
|
||||||
|
<el-input v-model="config.zap.prefix" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="日志文件夹">
|
||||||
|
<el-input v-model="config.zap.director" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="编码级">
|
||||||
|
<el-input v-model="config.zap['encode-level']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="栈名">
|
||||||
|
<el-input v-model="config.zap['stacktrace-key']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="日志留存时间(默认以天为单位)">
|
||||||
|
<el-input v-model.number="config.zap['max-age']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="显示行">
|
||||||
|
<el-checkbox v-model="config.zap['show-line']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="输出控制台">
|
||||||
|
<el-checkbox v-model="config.zap['log-in-console']" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
title="Redis admin数据库配置"
|
||||||
|
name="4"
|
||||||
|
>
|
||||||
|
<el-form-item label="库">
|
||||||
|
<el-input v-model.number="config.redis.db" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地址">
|
||||||
|
<el-input v-model="config.redis.addr" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码">
|
||||||
|
<el-input v-model="config.redis.password" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
|
<el-collapse-item
|
||||||
|
title="Mongo 数据库配置"
|
||||||
|
name="14"
|
||||||
|
>
|
||||||
|
<el-form-item label="collection name(表名,一般不写)">
|
||||||
|
<el-input v-model="config.mongo.coll" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="mongodb options">
|
||||||
|
<el-input v-model="config.mongo.options" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="database name(数据库名)">
|
||||||
|
<el-input v-model="config.mongo.database" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="用户名">
|
||||||
|
<el-input v-model="config.mongo.username" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码">
|
||||||
|
<el-input v-model="config.mongo.password" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="最小连接池">
|
||||||
|
<el-input v-model="config.mongo['min-pool-size']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="最大连接池">
|
||||||
|
<el-input v-model="config.mongo['max-pool-size']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="socket超时时间">
|
||||||
|
<el-input v-model="config.mongo['socket-timeout-ms']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="连接超时时间">
|
||||||
|
<el-input v-model="config.mongo['socket-timeout-ms']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否开启zap日志">
|
||||||
|
<el-checkbox v-model="config.mongo['is-zap']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="hosts">
|
||||||
|
<template v-for="(item,k) in config.mongo.hosts">
|
||||||
|
<div
|
||||||
|
v-for="(_,k2) in item"
|
||||||
|
:key="k2"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
:key="k+k2"
|
||||||
|
:label="k2"
|
||||||
|
>
|
||||||
|
<el-input v-model="item[k2]" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
|
<el-collapse-item
|
||||||
|
title="邮箱配置"
|
||||||
|
name="5"
|
||||||
|
>
|
||||||
|
<el-form-item label="接收者邮箱">
|
||||||
|
<el-input
|
||||||
|
v-model="config.email.to"
|
||||||
|
placeholder="可多个,以逗号分隔"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="端口">
|
||||||
|
<el-input v-model.number="config.email.port" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="发送者邮箱">
|
||||||
|
<el-input v-model="config.email.from" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="host">
|
||||||
|
<el-input v-model="config.email.host" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否为ssl">
|
||||||
|
<el-checkbox v-model="config.email['is-ssl']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="secret">
|
||||||
|
<el-input v-model="config.email.secret" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="测试邮件">
|
||||||
|
<el-button @click="email">测试邮件</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
title="验证码配置"
|
||||||
|
name="7"
|
||||||
|
>
|
||||||
|
<el-form-item label="字符长度">
|
||||||
|
<el-input v-model.number="config.captcha['key-long']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="图片宽度">
|
||||||
|
<el-input v-model.number="config.captcha['img-width']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="图片高度">
|
||||||
|
<el-input v-model.number="config.captcha['img-height']" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
title="数据库配置"
|
||||||
|
name="9"
|
||||||
|
>
|
||||||
|
<template v-if="config.system['db-type'] === 'mysql'">
|
||||||
|
<el-form-item label="用户名">
|
||||||
|
<el-input v-model="config.mysql.username" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码">
|
||||||
|
<el-input v-model="config.mysql.password" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地址">
|
||||||
|
<el-input v-model="config.mysql.path" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据库">
|
||||||
|
<el-input v-model="config.mysql['db-name']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="前缀">
|
||||||
|
<el-input v-model="config.mysql['refix']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="复数表">
|
||||||
|
<el-switch v-model="config.mysql['singular']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="引擎">
|
||||||
|
<el-input v-model="config.mysql['engine']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="maxIdleConns">
|
||||||
|
<el-input v-model.number="config.mysql['max-idle-conns']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="maxOpenConns">
|
||||||
|
<el-input v-model.number="config.mysql['max-open-conns']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="写入日志">
|
||||||
|
<el-checkbox v-model="config.mysql['log-zap']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="日志模式">
|
||||||
|
<el-input v-model="config.mysql['log-mode']" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="config.system['db-type'] === 'pgsql'">
|
||||||
|
<el-form-item label="用户名">
|
||||||
|
<el-input v-model="config.pgsql.username" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码">
|
||||||
|
<el-input v-model="config.pgsql.password" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地址">
|
||||||
|
<el-input v-model="config.pgsql.path" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据库">
|
||||||
|
<el-input v-model="config.pgsql.dbname" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="前缀">
|
||||||
|
<el-input v-model="config.pgsql['refix']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="复数表">
|
||||||
|
<el-switch v-model="config.pgsql['singular']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="引擎">
|
||||||
|
<el-input v-model="config.pgsql['engine']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="maxIdleConns">
|
||||||
|
<el-input v-model.number="config.pgsql['max-idle-conns']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="maxOpenConns">
|
||||||
|
<el-input v-model.number="config.pgsql['max-open-conns']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="写入日志">
|
||||||
|
<el-checkbox v-model="config.pgsql['log-zap']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="日志模式">
|
||||||
|
<el-input v-model="config.pgsql['log-mode']" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
|
<el-collapse-item
|
||||||
|
title="oss配置"
|
||||||
|
name="10"
|
||||||
|
>
|
||||||
|
<template v-if="config.system['oss-type'] === 'local'">
|
||||||
|
<h2>本地文件配置</h2>
|
||||||
|
<el-form-item label="本地文件访问路径">
|
||||||
|
<el-input v-model="config.local.path" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="本地文件存储路径">
|
||||||
|
<el-input v-model="config.local['store-path']" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="config.system['oss-type'] === 'qiniu'">
|
||||||
|
<h2>qiniu上传配置</h2>
|
||||||
|
<el-form-item label="存储区域">
|
||||||
|
<el-input v-model="config.qiniu.zone" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="空间名称">
|
||||||
|
<el-input v-model="config.qiniu.bucket" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="CDN加速域名">
|
||||||
|
<el-input v-model="config.qiniu['img-path']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否使用https">
|
||||||
|
<el-checkbox v-model="config.qiniu['use-https']">开启</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="accessKey">
|
||||||
|
<el-input v-model="config.qiniu['access-key']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="secretKey">
|
||||||
|
<el-input v-model="config.qiniu['secret-key']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上传是否使用CDN上传加速">
|
||||||
|
<el-checkbox v-model="config.qiniu['use-cdn-domains']">开启</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="config.system['oss-type'] === 'tencent-cos'">
|
||||||
|
<h2>腾讯云COS上传配置</h2>
|
||||||
|
<el-form-item label="存储桶名称">
|
||||||
|
<el-input v-model="config['tencent-cos']['bucket']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属地域">
|
||||||
|
<el-input v-model="config['tencent-cos'].region" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="secretID">
|
||||||
|
<el-input v-model="config['tencent-cos']['secret-id']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="secretKey">
|
||||||
|
<el-input v-model="config['tencent-cos']['secret-key']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="路径前缀">
|
||||||
|
<el-input v-model="config['tencent-cos']['path-prefix']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="访问域名">
|
||||||
|
<el-input v-model="config['tencent-cos']['base-url']" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="config.system['oss-type'] === 'aliyun-oss'">
|
||||||
|
<h2>阿里云OSS上传配置</h2>
|
||||||
|
<el-form-item label="区域">
|
||||||
|
<el-input v-model="config['aliyun-oss'].endpoint" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="accessKeyId">
|
||||||
|
<el-input v-model="config['aliyun-oss']['access-key-id']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="accessKeySecret">
|
||||||
|
<el-input v-model="config['aliyun-oss']['access-key-secret']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="存储桶名称">
|
||||||
|
<el-input v-model="config['aliyun-oss']['bucket-name']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="访问域名">
|
||||||
|
<el-input v-model="config['aliyun-oss']['bucket-url']" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="config.system['oss-type'] === 'huawei-obs'">
|
||||||
|
<h2>华为云Obs上传配置</h2>
|
||||||
|
<el-form-item label="路径">
|
||||||
|
<el-input v-model="config['hua-wei-obs'].path" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="存储桶名称">
|
||||||
|
<el-input v-model="config['hua-wei-obs'].bucket" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="区域">
|
||||||
|
<el-input v-model="config['hua-wei-obs'].endpoint" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="accessKey">
|
||||||
|
<el-input v-model="config['hua-wei-obs']['access-key']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="secretKey">
|
||||||
|
<el-input v-model="config['hua-wei-obs']['secret-key']" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
|
<el-collapse-item
|
||||||
|
title="Excel上传配置"
|
||||||
|
name="11"
|
||||||
|
>
|
||||||
|
<el-form-item label="合成目标地址">
|
||||||
|
<el-input v-model="config.excel.dir" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
|
<el-collapse-item
|
||||||
|
title="自动化代码配置"
|
||||||
|
name="12"
|
||||||
|
>
|
||||||
|
<el-form-item label="是否自动重启(linux)">
|
||||||
|
<el-checkbox v-model="config.autocode['transfer-restart']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="root(项目根路径)">
|
||||||
|
<el-input
|
||||||
|
v-model="config.autocode.root"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Server(后端代码地址)">
|
||||||
|
<el-input v-model="config.autocode['transfer-restart']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SApi(后端api文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode['server-api']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SInitialize(后端Initialize文件夹)">
|
||||||
|
<el-input v-model="config.autocode['server-initialize']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SModel(后端Model文件地址)">
|
||||||
|
<el-input v-model="config.autocode['server-model']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SRequest(后端Request文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode['server-request']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SRouter(后端Router文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode['server-router']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SService(后端Service文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode['server-service']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Web(前端文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode.web" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="WApi(后端WApi文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode['web-api']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="WForm(后端WForm文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode['web-form']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="WTable(后端WTable文件夹地址)">
|
||||||
|
<el-input v-model="config.autocode['web-table']" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</el-form>
|
||||||
|
<div class="mt-4">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="update"
|
||||||
|
>立即更新</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="reload"
|
||||||
|
>重启服务(开发中)</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getSystemConfig, setSystemConfig } from '@/api/system'
|
||||||
|
import { emailTest } from '@/api/email'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Config'
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeNames = reactive([])
|
||||||
|
const config = ref({
|
||||||
|
system: {
|
||||||
|
'iplimit-count': 0,
|
||||||
|
'iplimit-time': 0
|
||||||
|
},
|
||||||
|
jwt: {},
|
||||||
|
mysql: {},
|
||||||
|
pgsql: {},
|
||||||
|
excel: {},
|
||||||
|
autocode: {},
|
||||||
|
redis: {},
|
||||||
|
mongo: {
|
||||||
|
coll: '',
|
||||||
|
options: '',
|
||||||
|
database: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
'min-pool-size': '',
|
||||||
|
'max-pool-size': '',
|
||||||
|
'socket-timeout-ms': '',
|
||||||
|
'connect-timeout-ms': '',
|
||||||
|
'is-zap': '',
|
||||||
|
hosts: [
|
||||||
|
{
|
||||||
|
host: '',
|
||||||
|
port: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
qiniu: {},
|
||||||
|
'tencent-cos': {},
|
||||||
|
'aliyun-oss': {},
|
||||||
|
'hua-wei-obs': {},
|
||||||
|
captcha: {},
|
||||||
|
zap: {},
|
||||||
|
local: {},
|
||||||
|
email: {},
|
||||||
|
timer: {
|
||||||
|
detail: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const initForm = async() => {
|
||||||
|
const res = await getSystemConfig()
|
||||||
|
if (res.code === 0) {
|
||||||
|
config.value = res.data.config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initForm()
|
||||||
|
const reload = () => {}
|
||||||
|
const update = async() => {
|
||||||
|
const res = await setSystemConfig({ config: config.value })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '配置文件设置成功'
|
||||||
|
})
|
||||||
|
await initForm()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const email = async() => {
|
||||||
|
const res = await emailTest()
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '邮件发送成功'
|
||||||
|
})
|
||||||
|
await initForm()
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '邮件发送失败'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.system {
|
||||||
|
@apply bg-white p-9 rounded;
|
||||||
|
h2 {
|
||||||
|
@apply p-2.5 my-2.5 text-lg shadow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
const child_process = require('child_process')
|
||||||
|
|
||||||
|
export default function GvaPositionServer() {
|
||||||
|
return {
|
||||||
|
name: 'gva-position-server',
|
||||||
|
apply: 'serve',
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use((req, _, next) => {
|
||||||
|
if (req._parsedUrl.pathname === '/gvaPositionCode') {
|
||||||
|
const path =
|
||||||
|
req._parsedUrl.query && req._parsedUrl.query.split('=')[1]
|
||||||
|
if (path && path !== 'null') {
|
||||||
|
if (process.env.VITE_EDITOR === 'webstorm') {
|
||||||
|
const linePath = path.split(':')[1]
|
||||||
|
const filePath = path.split(':')[0]
|
||||||
|
const platform = os()
|
||||||
|
if (platform === 'win32') {
|
||||||
|
child_process.exec(
|
||||||
|
`webstorm64.exe --line ${linePath} ${filePath}`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
child_process.exec(
|
||||||
|
`webstorm --line ${linePath} ${filePath}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
child_process.exec('code -r -g ' + path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function os() {
|
||||||
|
'use strict'
|
||||||
|
const os = require('os')
|
||||||
|
const platform = os.platform()
|
||||||
|
return platform
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import * as path from 'path'
|
||||||
|
export default function fullImportPlugin() {
|
||||||
|
let config
|
||||||
|
return {
|
||||||
|
name: 'fullImportElementPlus',
|
||||||
|
async configResolved(conf) {
|
||||||
|
config = conf
|
||||||
|
},
|
||||||
|
transform(code, id) {
|
||||||
|
const sourcePath = path.join(config.root, 'src/main.js').split(path.sep).join('/')
|
||||||
|
const targetPath = id.split(path.sep).join('/')
|
||||||
|
if (sourcePath === targetPath) {
|
||||||
|
const name = 'ElementPlus'
|
||||||
|
// 引入 ElementPlus 和 样式
|
||||||
|
code = code.replace(`import { createApp } from 'vue'`, ($1) => $1 + `\nimport ${name} from 'element-plus'`)
|
||||||
|
code = code.replace(`import './style/element_visiable.scss'`, ($1) => $1 + `\nimport 'element-plus/theme-chalk/src/index.scss'`)
|
||||||
|
code = code.replace('.mount(', ($1) => `.use(${name})` + $1)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
export default function GvaPosition() {
|
||||||
|
return {
|
||||||
|
name: 'gva-position',
|
||||||
|
apply: 'serve',
|
||||||
|
transform(code, id) {
|
||||||
|
const index = id.lastIndexOf('.')
|
||||||
|
const ext = id.substr(index + 1)
|
||||||
|
if (ext.toLowerCase() === 'vue') {
|
||||||
|
return codeLineTrack(code, id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeLineTrack = (code, id) => {
|
||||||
|
const lineList = code.split('\n')
|
||||||
|
const newList = []
|
||||||
|
lineList.forEach((item, index) => {
|
||||||
|
newList.push(addLineAttr(item, index + 1, id)) // 添加位置属性,index+1为具体的代码行号
|
||||||
|
})
|
||||||
|
return newList.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
const addLineAttr = (lineStr, line, id) => {
|
||||||
|
if (!/^\s+</.test(lineStr)) {
|
||||||
|
return lineStr
|
||||||
|
}
|
||||||
|
|
||||||
|
const reg = /((((^(\s)+\<))|(^\<))[\w-]+)|(<\/template)/g
|
||||||
|
let leftTagList = lineStr.match(reg)
|
||||||
|
if (leftTagList) {
|
||||||
|
leftTagList = Array.from(new Set(leftTagList))
|
||||||
|
leftTagList.forEach((item) => {
|
||||||
|
const skip = [
|
||||||
|
'KeepAlive',
|
||||||
|
'template',
|
||||||
|
'keep-alive',
|
||||||
|
'transition',
|
||||||
|
'el-',
|
||||||
|
'El',
|
||||||
|
'router-view',
|
||||||
|
]
|
||||||
|
if (item && !skip.some((i) => item.indexOf(i) > -1)) {
|
||||||
|
const reg = new RegExp(`${item}`)
|
||||||
|
const location = `${item} code-location="${id}:${line}"`
|
||||||
|
lineStr = lineStr.replace(reg, location)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return lineStr
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { readFileSync, readdirSync } from 'fs'
|
||||||
|
const svgTitle = /<svg([^>+].*?)>/
|
||||||
|
const clearHeightWidth = /(width|height)="([^>+].*?)"/g
|
||||||
|
const hasViewBox = /(viewBox="[^>+].*?")/g
|
||||||
|
const clearReturn = /(\r)|(\n)/g
|
||||||
|
function findSvgFile(dir) {
|
||||||
|
const svgRes = []
|
||||||
|
const dirents = readdirSync(dir, {
|
||||||
|
withFileTypes: true
|
||||||
|
})
|
||||||
|
for (const dirent of dirents) {
|
||||||
|
if (dirent.isDirectory()) {
|
||||||
|
svgRes.push(...findSvgFile(dir + dirent.name + '/'))
|
||||||
|
} else {
|
||||||
|
const svg = readFileSync(dir + dirent.name)
|
||||||
|
.toString()
|
||||||
|
.replace(clearReturn, '')
|
||||||
|
.replace(svgTitle, ($1, $2) => {
|
||||||
|
let width = 0
|
||||||
|
let height = 0
|
||||||
|
let content = $2.replace(clearHeightWidth, (s1, s2, s3) => {
|
||||||
|
if (s2 === 'width') {
|
||||||
|
width = s3
|
||||||
|
} else if (s2 === 'height') {
|
||||||
|
height = s3
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
if (!hasViewBox.test($2)) {
|
||||||
|
content += `viewBox="0 0 ${width} ${height}"`
|
||||||
|
}
|
||||||
|
return `<symbol id="${dirent.name.replace('.svg', '')}" ${content}>`
|
||||||
|
})
|
||||||
|
.replace('</svg>', '</symbol>')
|
||||||
|
svgRes.push(svg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return svgRes
|
||||||
|
}
|
||||||
|
export const svgBuilder = (path) => {
|
||||||
|
if (path === '') return
|
||||||
|
const res = findSvgFile(path)
|
||||||
|
return {
|
||||||
|
name: 'svg-transform',
|
||||||
|
transformIndexHtml(html) {
|
||||||
|
return html.replace(
|
||||||
|
'<body>',
|
||||||
|
`
|
||||||
|
<body>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
|
||||||
|
${res.join('')}
|
||||||
|
</svg>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue