【升级】v3.2版本升级

This commit is contained in:
俞宝山
2024-08-08 23:29:07 +08:00
parent 8f7424b5e7
commit 4a6eeb0c0f
56 changed files with 1041 additions and 1284 deletions

View File

@@ -53,6 +53,8 @@ gitee下载地址[https://gitee.com/xiaonuobase/snowy](https://gitee.com/xiao
github下载地址镜像[https://github.com/xiaonuobase/Snowy](https://github.com/xiaonuobase/Snowy) github下载地址镜像[https://github.com/xiaonuobase/Snowy](https://github.com/xiaonuobase/Snowy)
gitcode下载地址[https://gitcode.com/xiaonuobase/Snowy](https://gitcode.com/xiaonuobase/Snowy)
演示地址:[https://snowy.xiaonuo.vip](https://snowy.xiaonuo.vip) 演示地址:[https://snowy.xiaonuo.vip](https://snowy.xiaonuo.vip)
文档地址:[https://xiaonuo.vip/doc](https://xiaonuo.vip/doc) 文档地址:[https://xiaonuo.vip/doc](https://xiaonuo.vip/doc)

View File

@@ -1,9 +1,3 @@
# 本地环境
NODE_ENV = development
# 标题
VITE_TITLE = Snowy
# 接口地址 # 接口地址
VITE_API_BASEURL = http://127.0.0.1:82 VITE_API_BASEURL = http://127.0.0.1:82
@@ -12,3 +6,6 @@ VITE_PORT = 81
# 开启设置抽屉 # 开启设置抽屉
VITE_SET_DRAWER = true VITE_SET_DRAWER = true
# 本地环境
NODE_ENV = development

View File

@@ -1,9 +1,3 @@
# 生产环境
NODE_ENV = production
# 标题
VITE_TITLE = Snowy
# 接口地址 # 接口地址
VITE_API_BASEURL = http://127.0.0.1:82 VITE_API_BASEURL = http://127.0.0.1:82

View File

@@ -8,10 +8,10 @@
<title>Snowy</title> <title>Snowy</title>
<style> <style>
.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1677FF;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}} .dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1677FF;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}
.app-loading {position: absolute;top:0px;left:0px;right:0px;bottom:0px;display: flex;justify-content: center;align-items: center;flex-direction: column;background: #fff;} .app-loading {position: absolute;top:0;left:0;right:0;bottom:0;display: flex;justify-content: center;align-items: center;flex-direction: column;background: #fff;}
.app-loading__logo {margin-bottom: 30px;} .app-loading-logo {margin-bottom: 30px;}
.app-loading__logo img {width: 90px;vertical-align: bottom;} .app-loading-logo img {width: 90px;vertical-align: bottom;}
.app-loading__title {font-size: 24px;color: #333;margin-top: 30px;} .app-loading-title {font-size: 24px;color: #333;margin-top: 30px;}
.app-main { height: 100% } .app-main { height: 100% }
@keyframes loader { @keyframes loader {
0% { 0% {
@@ -26,7 +26,7 @@
</head> </head>
<body> <body>
<noscript> <noscript>
<strong>We're sorry but Snowy2.0 doesn't work properly without JavaScript <strong>We're sorry but Snowy doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong> enabled. Please enable it to continue.</strong>
</noscript> </noscript>
<div id="app" class="app-main"></div> <div id="app" class="app-main"></div>

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card title="站内信" :bordered="false"> <a-card :title="title" :bordered="false" :loading="miniMessageLoading">
<template #extra><a @click="leaveFor('/usercenter')">更多</a></template> <template #extra><a @click="leaveFor('/usercenter')">更多</a></template>
<div class="index-message-list"> <div class="index-message-list">
<a-list :data-source="messageList" size="small" :loading="miniMessageLoading"> <a-list :data-source="messageList" size="small" :loading="miniMessageLoading">
@@ -54,9 +54,7 @@
import router from '@/router' import router from '@/router'
const miniMessageLoading = ref(false) const miniMessageLoading = ref(false)
const messageList = ref([]) const messageList = ref([])
const miniMessageBodyStyle = ref({ const title = ref('站内信')
'padding-top': '10px'
})
onMounted(() => { onMounted(() => {
// 进来后执行查询 // 进来后执行查询
getMessageList() getMessageList()
@@ -69,6 +67,7 @@
.then((data) => { .then((data) => {
messageList.value = data messageList.value = data
}) })
.catch(() => {})
.finally(() => { .finally(() => {
miniMessageLoading.value = false miniMessageLoading.value = false
}) })

View File

@@ -1,11 +1,5 @@
<template> <template>
<xn-form-container <xn-form-container title="详情" :width="1000" v-model:open="open" :destroy-on-close="true" @close="onClose">
title="详情"
:width="1000"
v-model:open="open"
:destroy-on-close="true"
@close="onClose"
>
<a-descriptions bordered> <a-descriptions bordered>
<a-descriptions-item label="标题">{{formData.title}}</a-descriptions-item> <a-descriptions-item label="标题">{{formData.title}}</a-descriptions-item>
<a-descriptions-item label="类型"> <a-descriptions-item label="类型">

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card :bordered="false" :title="title"> <a-card :bordered="false" :title="title" :loading="apiLoading">
<template #extra><a @click="leaveFor('/biz/notice')">更多</a></template> <template #extra><a @click="leaveFor('/biz/notice')">更多</a></template>
<a-table <a-table
:columns="columns" :columns="columns"
@@ -63,9 +63,19 @@
] ]
const title = ref('通知公告') const title = ref('通知公告')
const dataSource = ref([]) const dataSource = ref([])
bizIndexApi.bizIndexNoticeList().then((data) => { const apiLoading = ref(false)
onMounted(() => {
apiLoading.value = true
bizIndexApi
.bizIndexNoticeList()
.then((data) => {
dataSource.value = data dataSource.value = data
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
})
const leaveFor = (url = '/') => { const leaveFor = (url = '/') => {
router.replace({ router.replace({
path: url path: url

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card :title="title" :bordered="false" class="mt-2"> <a-card :title="title" :bordered="false" class="mt-2" :loading="apiLoading">
<a-calendar v-model:value="calendarValue" :fullscreen="false" @select="onPanelSelect" /> <a-calendar v-model:value="calendarValue" :fullscreen="false" @select="onPanelSelect" />
<a-card :bordered="false"> <a-card :bordered="false">
<a-timeline> <a-timeline>
@@ -45,7 +45,7 @@
const title = ref('我的日程') const title = ref('我的日程')
const scheduleList = ref([]) const scheduleList = ref([])
const calendarValue = ref(dayjs()) const calendarValue = ref(dayjs())
const apiLoading = ref(false)
onMounted(() => { onMounted(() => {
// 进来后执行查询 // 进来后执行查询
seleScheduleList() seleScheduleList()
@@ -55,9 +55,16 @@
const param = { const param = {
scheduleDate: calendarValue.value.format('YYYY-MM-DD') scheduleDate: calendarValue.value.format('YYYY-MM-DD')
} }
indexApi.indexScheduleList(param).then((data) => { apiLoading.value = true
indexApi
.indexScheduleList(param)
.then((data) => {
scheduleList.value = data scheduleList.value = data
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
} }
// 点击某一天 // 点击某一天

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card :title="title" :bordered="false"> <a-card :title="title" :bordered="false" :loading="apiLoading">
<div class="card-div"> <div class="card-div">
<a-row :gutter="10"> <a-row :gutter="10">
<a-col :span="6" :key="shortcut.id" v-for="shortcut in shortcutList" :xs="12" :sm="8" :md="6" :lg="8" :xl="6"> <a-col :span="6" :key="shortcut.id" v-for="shortcut in shortcutList" :xs="12" :sm="8" :md="6" :lg="8" :xl="6">
@@ -21,16 +21,24 @@
import { onMounted } from 'vue' import { onMounted } from 'vue'
const shortcutList = ref([]) const shortcutList = ref([])
const title = ref('快捷方式') const title = ref('快捷方式')
const apiLoading = ref(false)
onMounted(() => { onMounted(() => {
// 进来后执行查询 // 进来后执行查询
getUserLoginWorkbench() getUserLoginWorkbench()
}) })
const getUserLoginWorkbench = () => { const getUserLoginWorkbench = () => {
userCenterApi.userLoginWorkbench().then((data) => { apiLoading.value = true
userCenterApi
.userLoginWorkbench()
.then((data) => {
if (data) { if (data) {
shortcutList.value = JSON.parse(data).shortcut shortcutList.value = JSON.parse(data).shortcut
} }
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
} }
const leaveFor = (url = '/') => { const leaveFor = (url = '/') => {
router.replace({ router.replace({

View File

@@ -9,6 +9,7 @@
<template #nextArrow> <template #nextArrow>
<div class="custom-slick-arrow" style="right: 10px"><RightOutlined /></div> <div class="custom-slick-arrow" style="right: 10px"><RightOutlined /></div>
</template> </template>
<div v-if="!isEmpty(slideshowList)">
<img <img
v-for="item in slideshowList" v-for="item in slideshowList"
:key="item.id" :key="item.id"
@@ -16,12 +17,15 @@
class="carousel-images" class="carousel-images"
@click="leaveForOpen(item.pathDetails)" @click="leaveForOpen(item.pathDetails)"
/> />
</div>
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
</a-carousel> </a-carousel>
</a-card> </a-card>
</template> </template>
<script setup name="carousel"> <script setup name="carousel">
import bizIndexApi from '@/api/biz/bizIndexApi' import bizIndexApi from '@/api/biz/bizIndexApi'
import { Empty } from 'ant-design-vue'
import { isEmpty, cloneDeep } from 'lodash-es' import { isEmpty, cloneDeep } from 'lodash-es'
import router from '@/router' import router from '@/router'
const slideshowList = ref([]) const slideshowList = ref([])
@@ -40,9 +44,12 @@
// 这是字典内维护的该位置 // 这是字典内维护的该位置
place: props.config.options.place ? props.config.options.place : 'BACK_SYS_INDEX' place: props.config.options.place ? props.config.options.place : 'BACK_SYS_INDEX'
} }
bizIndexApi.bizIndexSlideshowList(param).then((data) => { bizIndexApi
.bizIndexSlideshowList(param)
.then((data) => {
slideshowList.value = data slideshowList.value = data
}) })
.catch(() => {})
}) })
// URL跟路由的跳转 // URL跟路由的跳转
const leaveForOpen = (value) => { const leaveForOpen = (value) => {
@@ -81,7 +88,7 @@
text-align: center; text-align: center;
height: 180px; height: 180px;
line-height: 150px; line-height: 150px;
background: #364d79; background: #1890ff;
overflow: hidden; overflow: hidden;
} }
.ant-carousel :deep(.slick-arrow.custom-slick-arrow) { .ant-carousel :deep(.slick-arrow.custom-slick-arrow) {

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card :title="title" :bordered="false"> <a-card :title="title" :bordered="false" :loading="apiLoading">
<a-row> <a-row>
<a-col :span="6"> <a-col :span="6">
<a-statistic :value="dataSource.userCount"> <a-statistic :value="dataSource.userCount">
@@ -40,6 +40,7 @@
<script setup name="sysBizDataCard"> <script setup name="sysBizDataCard">
import indexApi from '@/api/sys/indexApi' import indexApi from '@/api/sys/indexApi'
const title = ref('业务数据') const title = ref('业务数据')
const apiLoading = ref(false)
const dataSource = ref({ const dataSource = ref({
userCount: 0, userCount: 0,
roleCount: 0, roleCount: 0,
@@ -47,9 +48,16 @@
positionCount: 0 positionCount: 0
}) })
onMounted(() => { onMounted(() => {
indexApi.indexBizDataCount().then((data) => { apiLoading.value = true
indexApi
.indexBizDataCount()
.then((data) => {
dataSource.value = data dataSource.value = data
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
}) })
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card :title="title" :bordered="false"> <a-card :title="title" :bordered="false" :loading="apiLoading">
<a-row> <a-row>
<a-col :span="4"> <a-col :span="4">
<a-statistic :value="dataSource.jobCount"> <a-statistic :value="dataSource.jobCount">
@@ -56,6 +56,7 @@
<script setup name="sysBizDataCard"> <script setup name="sysBizDataCard">
import indexApi from '@/api/sys/indexApi' import indexApi from '@/api/sys/indexApi'
const title = ref('运维一览') const title = ref('运维一览')
const apiLoading = ref(false)
const dataSource = ref({ const dataSource = ref({
jobCount: 0, jobCount: 0,
sysDictCount: 0, sysDictCount: 0,
@@ -65,9 +66,16 @@
thirdUserCount: 0 thirdUserCount: 0
}) })
onMounted(() => { onMounted(() => {
indexApi.indexOpDataCount().then((data) => { apiLoading.value = true
indexApi
.indexOpDataCount()
.then((data) => {
dataSource.value = data dataSource.value = data
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
}) })
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card title="操作记录" :bordered="false"> <a-card :title="title" :bordered="false" :loading="apiLoading">
<template #extra v-if="displayMore()"><a @click="leaveFor('/dev/oplog')">更多</a></template> <template #extra v-if="displayMore()"><a @click="leaveFor('/dev/oplog')">更多</a></template>
<div class="timeline-div"> <div class="timeline-div">
<a-timeline> <a-timeline>
@@ -18,6 +18,8 @@
import tool from '@/utils/tool' import tool from '@/utils/tool'
const userInfo = tool.data.get('USER_INFO') const userInfo = tool.data.get('USER_INFO')
const opLogList = ref([]) const opLogList = ref([])
const title = ref('操作记录')
const apiLoading = ref(false)
onMounted(() => { onMounted(() => {
// 进来后执行查询 // 进来后执行查询
getOpLogList() getOpLogList()
@@ -28,9 +30,16 @@
return userInfo.roleCodeList && userInfo.roleCodeList.toString().indexOf('superAdmin') !== -1 return userInfo.roleCodeList && userInfo.roleCodeList.toString().indexOf('superAdmin') !== -1
} }
const getOpLogList = () => { const getOpLogList = () => {
indexApi.indexOpLogList().then((data) => { apiLoading.value = true
indexApi
.indexOpLogList()
.then((data) => {
opLogList.value = data opLogList.value = data
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
} }
const leaveFor = (url = '/') => { const leaveFor = (url = '/') => {
router.replace({ router.replace({

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card :title="title" :bordered="false"> <a-card :title="title" :bordered="false" :loading="apiLoading">
<a-row> <a-row>
<a-col :span="12"> <a-col :span="12">
<a-statistic :value="dataSource.fileCount"> <a-statistic :value="dataSource.fileCount">
@@ -40,6 +40,7 @@
<script setup name="sysToolDataCard"> <script setup name="sysToolDataCard">
import indexApi from '@/api/sys/indexApi' import indexApi from '@/api/sys/indexApi'
const title = ref('基础工具') const title = ref('基础工具')
const apiLoading = ref(false)
const dataSource = ref({ const dataSource = ref({
fileCount: 0, fileCount: 0,
smsCount: 0, smsCount: 0,
@@ -47,9 +48,16 @@
messageCount: 0 messageCount: 0
}) })
onMounted(() => { onMounted(() => {
indexApi.indexToolDataCount().then((data) => { apiLoading.value = true
indexApi
.indexToolDataCount()
.then((data) => {
dataSource.value = data dataSource.value = data
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
}) })
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card :bordered="false" title="周访问量"> <a-card :bordered="false" :title="title">
<div id="visLogChartLine" class="xn-ht200"></div> <div id="visLogChartLine" class="xn-ht200"></div>
</a-card> </a-card>
</template> </template>
@@ -11,6 +11,7 @@
const seriesKey = 'series' const seriesKey = 'series'
const valueKey = 'value' const valueKey = 'value'
const title = ref('周访问量')
const processData = (data, yFields, meta) => { const processData = (data, yFields, meta) => {
const result = [] const result = []
data.forEach((d) => { data.forEach((d) => {
@@ -34,7 +35,9 @@
alias: '登出' alias: '登出'
} }
} }
logApi.logVisLineChartData().then((data) => { logApi
.logVisLineChartData()
.then((data) => {
const line = new Line('visLogChartLine', { const line = new Line('visLogChartLine', {
data: processData(data, ['loginCount', 'logoutCount'], lineMeta), data: processData(data, ['loginCount', 'logoutCount'], lineMeta),
padding: 'auto', padding: 'auto',
@@ -46,6 +49,7 @@
}) })
line.render() line.render()
}) })
.catch(() => {})
}) })
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,5 +1,5 @@
<template> <template>
<a-card title="访问记录" :bordered="false"> <a-card :title="title" :bordered="false" :loading="apiLoading">
<template #extra v-if="displayMore()"><a @click="leaveFor('/dev/vislog')">更多</a></template> <template #extra v-if="displayMore()"><a @click="leaveFor('/dev/vislog')">更多</a></template>
<div class="timeline-div"> <div class="timeline-div">
<a-timeline> <a-timeline>
@@ -19,6 +19,8 @@
import tool from '@/utils/tool' import tool from '@/utils/tool'
const userInfo = tool.data.get('USER_INFO') const userInfo = tool.data.get('USER_INFO')
const visLogList = ref([]) const visLogList = ref([])
const title = ref('访问记录')
const apiLoading = ref(false)
onMounted(() => { onMounted(() => {
// 进来后执行查询 // 进来后执行查询
getVisLogList() getVisLogList()
@@ -29,9 +31,16 @@
} }
// 查询数据 // 查询数据
const getVisLogList = () => { const getVisLogList = () => {
indexApi.indexVisLogList().then((data) => { apiLoading.value = true
indexApi
.indexVisLogList()
.then((data) => {
visLogList.value = data visLogList.value = data
}) })
.catch(() => {})
.finally(() => {
apiLoading.value = false
})
} }
// 跳转 // 跳转
const leaveFor = (url = '/') => { const leaveFor = (url = '/') => {
@@ -55,7 +64,7 @@
padding-bottom: 10px !important; padding-bottom: 10px !important;
} }
.timeline-item-p { .timeline-item-p {
margin-bottom: 0px; margin-bottom: 0;
color: rgb(188, 189, 190); color: rgb(188, 189, 190);
} }
.timeline-div { .timeline-div {

View File

@@ -54,7 +54,7 @@
</div> </div>
<!-- 统计列数据 --> <!-- 统计列数据 -->
<a-alert showIcon class="mb-4" v-if="props.alert"> <a-alert showIcon class="s-table-alert mb-4" v-if="props.alert">
<template #message> <template #message>
<div> <div>
<span className="mr-3"> <span className="mr-3">
@@ -98,7 +98,7 @@
@change="loadData" @change="loadData"
@expand=" @expand="
(expanded, record) => { (expanded, record) => {
emit('expand', expanded, record) emit('onExpand', expanded, record)
} }
" "
:rowClassName=" :rowClassName="
@@ -124,7 +124,7 @@
import { get } from 'lodash-es' import { get } from 'lodash-es'
const slots = useSlots() const slots = useSlots()
const route = useRoute() const route = useRoute()
const emit = defineEmits(['expand']) const emit = defineEmits(['onExpand'])
const renderSlots = Object.keys(slots) const renderSlots = Object.keys(slots)
const props = defineProps( const props = defineProps(
@@ -367,7 +367,8 @@
// 用请求数据请求该列表的返回数据 // 用请求数据请求该列表的返回数据
const result = props.data(parameter) const result = props.data(parameter)
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') { if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then((r) => { result
.then((r) => {
if (r == null) { if (r == null) {
data.localLoading = false data.localLoading = false
return return
@@ -588,4 +589,12 @@
} }
} }
} }
.s-table-alert {
background-color: var(--primary-1) !important;
border-color: var(--primary-color) !important;
:deep(.ant-alert-icon),
a {
color: var(--primary-color) !important;
}
}
</style> </style>

View File

@@ -15,6 +15,10 @@
const emit = defineEmits({ batchCallBack: null }) const emit = defineEmits({ batchCallBack: null })
const buttonLoading = ref(false) const buttonLoading = ref(false)
const props = defineProps({ const props = defineProps({
idKey: {
type: String,
default: () => 'id'
},
buttonName: { buttonName: {
type: String, type: String,
default: () => '批量操作' default: () => '批量操作'
@@ -62,7 +66,7 @@
const deleteBatch = () => { const deleteBatch = () => {
const params = props.selectedRowKeys.map((m) => { const params = props.selectedRowKeys.map((m) => {
return { return {
id: m [props.idKey]: m
} }
}) })
// 发起方法调用,谁的谁来实现 // 发起方法调用,谁的谁来实现

View File

@@ -1,4 +1,5 @@
<template> <template>
<div style="position: relative">
<a-space class="go-back-button"> <a-space class="go-back-button">
<a-button :href="props.src" size="small" target="_blank"> <a-button :href="props.src" size="small" target="_blank">
<template #icon><download-outlined /></template> <template #icon><download-outlined /></template>
@@ -45,6 +46,7 @@
<a-result v-else status="warning" title="不支持预览的文件类型" /> <a-result v-else status="warning" title="不支持预览的文件类型" />
</a-spin> </a-spin>
</a-card> </a-card>
</div>
</template> </template>
<script setup> <script setup>
@@ -122,7 +124,7 @@
.go-back-button { .go-back-button {
position: absolute; position: absolute;
float: right; float: right;
right: 10px; right: 0;
z-index: 999; z-index: 999;
} }
</style> </style>

View File

@@ -1,5 +1,11 @@
<template> <template>
<a-modal v-if="isModal" :open="visible" @cancel="cancel" v-bind="$attrs"> <a-modal
v-if="isModal"
:open="visible"
@cancel="cancel"
v-bind="$attrs"
:footer="slotKeys.includes('footer') ? undefined : null"
>
<template v-for="slotKey in slotKeys" #[slotKey]> <template v-for="slotKey in slotKeys" #[slotKey]>
<slot :name="slotKey" /> <slot :name="slotKey" />
</template> </template>

View File

@@ -40,6 +40,9 @@
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.hljs-container {
position: relative;
}
/** 滚动条 */ /** 滚动条 */
:deep(.hljs, .hljs-container) { :deep(.hljs, .hljs-container) {
max-height: 300px !important; max-height: 300px !important;
@@ -78,8 +81,8 @@
/** 复制样式 */ /** 复制样式 */
.hljs-copy { .hljs-copy {
float: right; float: right;
top: 10px; top: 6px;
right: 10px; right: 6px;
position: absolute; position: absolute;
z-index: 9; z-index: 9;
} }

View File

@@ -11,7 +11,7 @@
:showSearch="props.showSearch" :showSearch="props.showSearch"
:filterOption="!props.showSearch" :filterOption="!props.showSearch"
@change="handleChange" @change="handleChange"
@search="handleSearch" @onSearch="handleSearch"
@popupScroll="handlePopupScroll" @popupScroll="handlePopupScroll"
/> />
</a-spin> </a-spin>
@@ -24,7 +24,7 @@
const initParams = ref({}) const initParams = ref({})
const options = ref([]) const options = ref([])
const spinning = ref(false) const spinning = ref(false)
const emit = defineEmits({ change: null, 'update:value': null, search:null }) const emit = defineEmits({ change: null, 'update:value': null, search: null })
const props = defineProps({ const props = defineProps({
value: { value: {
type: String, type: String,
@@ -110,9 +110,9 @@
// change // change
const handleChange = (value, array) => { const handleChange = (value, array) => {
modelValue.value = value modelValue.value = value
if (value == null && props.showSearch){ if (value == null && props.showSearch) {
//被清空,重置查询条件 //被清空,重置查询条件
initParams.value[props.searchKeyName] = value; initParams.value[props.searchKeyName] = value
} }
// 更新数据 // 更新数据
emit('update:value', value) emit('update:value', value)
@@ -121,10 +121,10 @@
} }
// search // search
const handleSearch = (searchValue) => { const handleSearch = (searchValue) => {
let _params = {current: 1}; let _params = { current: 1 }
if (props.searchKeyName && props.searchKeyName !== ''){ if (props.searchKeyName && props.searchKeyName !== '') {
_params[props.searchKeyName] = searchValue; _params[props.searchKeyName] = searchValue
onPage({ ...initParams.value, ..._params}); onPage({ ...initParams.value, ..._params })
} }
// 触发search事件 // 触发search事件
emit('search', searchValue) emit('search', searchValue)

View File

@@ -1,503 +0,0 @@
@import './index.less';
body {
}
#app {
height: 100%;
&.colorWeak {
filter: invert(80%);
}
&.userLayout {
overflow: auto;
}
}
.layout.ant-layout {
height: auto;
overflow-x: hidden;
&.mobile,
&.tablet {
.ant-layout-content {
.content {
margin: 24px 0 0;
}
}
.topmenu {
/* 必须为 topmenu 才能启用流式布局 */
&.content-width-Fluid {
.header-index-wide {
margin-left: 0;
}
}
}
}
&.mobile {
.sidemenu {
.ant-header-fixedHeader {
&.ant-header-side-opened,
&.ant-header-side-closed {
width: 100%;
}
}
}
}
&.ant-layout-has-sider {
flex-direction: row;
}
.trigger {
font-size: 20px;
line-height: 55px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
.topmenu {
.ant-header-fixedHeader {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: 100%;
transition: width 0.2s;
&.ant-header-side-opened {
width: 100%;
}
&.ant-header-side-closed {
width: 100%;
}
}
/* 必须为 topmenu 才能启用流式布局 */
&.content-width-Fluid {
.header-index-wide {
max-width: unset;
.header-index-left {
flex: 1 1 1000px;
.logo{
margin-left: 25px;
}
.ant-menu.ant-menu-horizontal{
max-width: calc(100vw - 190px - 238px - 25px);
flex: 1 1 calc(100vw - 190px - 238px - 25px);
}
}
.header-index-right{
margin-right:25px;
}
}
.page-header-index-wide {
max-width: unset;
}
}
}
.sidemenu {
.ant-header-fixedHeader {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: 100%;
transition: width 0.2s;
&.ant-header-side-opened {
width: calc(100% - 230px);
}
&.ant-header-side-closed {
width: calc(100% - 80px);
}
}
}
.header {
height: 55px;
// padding: 0 12px 0 0;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
position: relative;
}
.header,
.top-nav-header-index {
.user-wrapper {
float: right;
height: 100%;
.action {
line-height: 55px;
cursor: pointer;
padding: 0 12px;
display: inline-block;
transition: all 0.3s;
height: 100%;
color: rgba(0, 0, 0, 0.65);
&:hover {
background: rgba(0, 0, 0, 0.025);
}
.avatar {
margin: 15px 8px 15px 0;
color: #1890ff;
background: hsla(0, 0%, 100%, 0.85);
vertical-align: middle;
}
.icon {
font-size: 16px;
padding: 4px;
}
}
}
&.dark {
.user-wrapper {
.action {
color: rgba(255, 255, 255, 0.85);
a {
color: rgba(255, 255, 255, 0.85);
}
&:hover {
background: rgba(255, 255, 255, 0.16);
}
}
}
}
}
&.mobile,
&.tablet {
.top-nav-header-index {
.header-index-wide {
.header-index-left {
.trigger {
color: rgba(255, 255, 255, 0.85);
padding: 0 12px;
}
.logo.top-nav-header {
flex: 0 0 56px;
text-align: center;
line-height: 58px;
h1 {
display: none;
}
}
}
}
&.light {
.header-index-wide {
.header-index-left {
.trigger {
color: rgba(0, 0, 0, 0.65);
}
}
}
}
}
}
&.tablet {
// overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
.top-nav-header-index {
.header-index-wide {
.header-index-left {
.logo > a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.ant-menu.ant-menu-horizontal {
flex: 1 1 auto;
white-space: normal;
}
}
}
}
.top-nav-header-index {
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
position: relative;
transition: background 0.3s, width 0.2s;
.header-index-wide {
max-width: 1200px;
margin: auto;
padding-left: 0;
display: flex;
height: 55px;
.ant-menu.ant-menu-horizontal {
max-width: 835px;
flex: 0 1 835px;
border: none;
height: 55px;
line-height: 55px;
}
.header-index-left {
flex: 0 1 1000px;
display: flex;
.logo.top-nav-header {
flex: 0 0 165px;
width: 165px;
height: 55px;
position: relative;
line-height: 55px;
transition: all 0.3s;
overflow: hidden;
img,
svg {
display: inline-block;
vertical-align: middle;
height: 32px;
width: 32px;
}
h1 {
color: #fff;
display: inline-block;
vertical-align: top;
font-size: 16px;
margin: 0 0 0 12px;
font-weight: 400;
}
}
}
.header-index-right {
flex: 0 0 238px;
align-self: flex-end;
height: 55px;
overflow: hidden;
.content-box {
float: right;
.action {
max-width: 140px;
overflow: hidden;
text-overflow:ellipsis;
white-space:nowrap;
}
}
}
}
&.light {
background-color: #fff;
.header-index-wide {
.header-index-left {
.logo {
h1 {
color: #002140;
}
}
}
}
}
}
// 内容区
.layout-content {
margin: 24px 24px 0px;
//height: 100%;
//height: 64px;
padding: 0 12px 0 0;
}
// footer
.ant-layout-footer {
padding: 0;
}
}
.topmenu {
.page-header-index-wide {
max-width: 1200px;
margin: 0 auto;
}
}
// drawer-sider 自定义
.ant-drawer.drawer-sider {
.sider {
box-shadow: none;
}
&.dark {
.ant-drawer-content {
background-color: rgb(0, 21, 41);
}
}
&.light {
box-shadow: none;
.ant-drawer-content {
background-color: #fff;
}
}
.ant-drawer-body {
padding: 0;
}
}
// 菜单样式
.sider {
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
position: relative;
z-index: @ant-global-sider-zindex;
min-height: 100vh;
.ant-layout-sider-children {
overflow-y: hidden;
&:hover {
overflow-y: auto;
}
}
&.ant-fixed-sidemenu {
position: fixed;
height: 100%;
}
// logo区域样式
.logo {
position: relative;
height: 55px;
padding-left: 24px;
overflow: hidden;
line-height: 55px;
background: #002140;
transition: all .3s;
img,
svg,
h1 {
display: inline-block;
vertical-align: middle;
}
img,
svg {
height: 32px;
width: 32px;
}
h1 {
color: #fff;
font-size: 20px;
margin: 0 0 0 12px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
font-weight: 600;
vertical-align: middle;
}
}
&.light {
background-color: #fff;
box-shadow: 2px 0px 8px 0px rgba(29, 35, 41, 0.05);
.logo {
background: #fff;
box-shadow: 1px 1px 0px 0px #e8e8e8;
h1 {
color: unset;
}
}
.ant-menu-light {
border-right-color: transparent;
}
}
}
// 外置的样式控制
.user-dropdown-menu {
span {
user-select: none;
}
}
.user-dropdown-menu-wrapper.ant-dropdown-menu {
padding: 4px 0;
.ant-dropdown-menu-item {
width: 160px;
}
.ant-dropdown-menu-item > .anticon:first-child,
.ant-dropdown-menu-item > a > .anticon:first-child,
.ant-dropdown-menu-submenu-title > .anticon:first-child .ant-dropdown-menu-submenu-title > a > .anticon:first-child {
min-width: 12px;
margin-right: 8px;
}
}
// 数据列表 样式
.table-alert {
margin-bottom: 16px;
}
.table-page-search-wrapper {
.ant-form-inline {
.ant-form-item {
display: flex;
margin-bottom: 24px;
margin-right: 0;
.ant-form-item-control-wrapper {
flex: 1 1;
display: inline-block;
vertical-align: middle;
}
> .ant-form-item-label {
line-height: 32px;
padding-right: 8px;
width: auto;
}
.ant-form-item-control {
height: 32px;
line-height: 32px;
}
}
}
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
}
.content {
.table-operator {
margin-bottom: 18px;
button {
margin-right: 8px;
}
}
}

View File

@@ -1,6 +0,0 @@
@import "ant-design-vue/lib/style/index";
// The prefix to use on all css classes from ant-pro.
@ant-pro-prefix : ant-pro;
@ant-global-sider-zindex : 106;
@ant-global-header-zindex : 105;

View File

@@ -18,6 +18,9 @@ const DEFAULT_CONFIG = {
// 请求超时 // 请求超时
TIMEOUT: 60000, TIMEOUT: 60000,
// 版本更新时间 默认10s
UPDATE_VERSION_TIME: 10000,
// TokenName // Authorization // TokenName // Authorization
TOKEN_NAME: 'token', TOKEN_NAME: 'token',

View File

@@ -77,14 +77,18 @@
import { layoutEnum } from '@/layout/enum/layoutEnum' import { layoutEnum } from '@/layout/enum/layoutEnum'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import tool from '@/utils/tool' import tool from '@/utils/tool'
import { message } from 'ant-design-vue' import { notification, Button } from 'ant-design-vue'
import ClassicalMenu from '@/layout/menu/classicalMenu.vue' import ClassicalMenu from '@/layout/menu/classicalMenu.vue'
import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue' import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue'
import TopMenu from '@/layout/menu/topMenu.vue' import TopMenu from '@/layout/menu/topMenu.vue'
import { NextLoading } from '@/utils/loading' import { NextLoading } from '@/utils/loading'
import { useMenuStore } from '@/store/menu' import { useMenuStore } from '@/store/menu'
import { userStore } from '@/store/user' import { userStore } from '@/store/user'
import { getLocalHash, checkHash } from '@/utils/version'
import sysConfig from '@/config/index'
import dictApi from '@/api/dev/dictApi'
let timer = null
const store = globalStore() const store = globalStore()
const kStore = keepAliveStore() const kStore = keepAliveStore()
const route = useRoute() const route = useRoute()
@@ -246,15 +250,71 @@
switchoverTopHeaderThemeColor() switchoverTopHeaderThemeColor()
settingTopHeaderThemeOrColor(theme.value, layout.value) settingTopHeaderThemeOrColor(theme.value, layout.value)
settingFixedWidth() settingFixedWidth()
updateVersion()
nextTick(() => { nextTick(() => {
getNav(menu.value) getNav()
// 刷新登录信息,不影响其他 // 刷新登录信息,不影响其他
userStore().refreshUserLoginUserInfo() userStore().refreshUserLoginUserInfo()
// 刷新菜单信息,不影响其他
useMenuStore().refreshApiMenu()
// 刷新字典信息,不影响其他
dictApi.dictTree().then((data) => {
// 设置字典到store中
tool.data.set('DICT_TYPE_TREE_DATA', data)
}) })
}) })
})
onBeforeUnmount(() => {
clearUpdateVersion()
window.removeEventListener('resize', onLayoutResize)
window.removeEventListener('resize', getNav)
})
// 新版检测
const updateVersion = () => {
timer = setInterval(async () => {
// 本地
let localVersion = getLocalHash()
// 线上
let onlineVersion = await checkHash()
// 如果不一样,提示更新
if (localVersion !== onlineVersion) {
if (document.querySelector('.notification-update-version')) {
return
}
const key = `open${Date.now()}`
notification.open({
type: 'info',
message: '发现新版本',
description: '检测到新版本,请刷新后使用',
duration: 0,
class: 'notification-update-version',
btn: () =>
h(
Button,
{
type: 'primary',
size: 'small',
onClick: () => {
notification.close(key)
window.location.reload()
}
},
{ default: () => '立即更新' }
),
key
})
}
}, sysConfig.UPDATE_VERSION_TIME)
}
// 销毁定时器
const clearUpdateVersion = () => {
if (timer) {
clearInterval(timer)
timer = null
}
}
// 动态获取横向导航栏隐藏数量 // 动态获取横向导航栏隐藏数量
const getNav = (items) => { const getNav = () => {
const item = menu.value
// 判断一下是不是顶部导航栏的模式 // 判断一下是不是顶部导航栏的模式
if (layout.value !== 'top') return if (layout.value !== 'top') return
const menuNavList = menu.value const menuNavList = menu.value
@@ -337,7 +397,7 @@
switchoverTopHeaderThemeColor() switchoverTopHeaderThemeColor()
// top下的顶栏 // top下的顶栏
settingTopHeaderThemeOrColor(theme.value, newValue) settingTopHeaderThemeOrColor(theme.value, newValue)
getNav(menu.value) getNav()
settingFixedWidth() settingFixedWidth()
let element = document.querySelector('#xn-line-nav') let element = document.querySelector('#xn-line-nav')
if (element) { if (element) {
@@ -572,7 +632,7 @@
message.warning('该模块下无任何菜单') message.warning('该模块下无任何菜单')
} }
} }
getNav(menu.value) getNav()
} }
// 通过标签切换应用 // 通过标签切换应用
const tagSwitchModule = (id) => { const tagSwitchModule = (id) => {

View File

@@ -9,7 +9,6 @@
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip * 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/ */
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import { notification } from 'ant-design-vue'
import NProgress from 'nprogress' import NProgress from 'nprogress'
import 'nprogress/nprogress.css' import 'nprogress/nprogress.css'
import systemRouter from './systemRouter' import systemRouter from './systemRouter'
@@ -128,7 +127,7 @@ router.afterEach((to, from) => {
router.onError((error) => { router.onError((error) => {
NProgress.done() NProgress.done()
window.nextLoading && NextLoading.done() window.nextLoading && NextLoading.done()
notification.error({ console.error({
message: '路由错误', message: '路由错误',
description: error.message description: error.message
}) })

View File

@@ -18,7 +18,12 @@ import userCenterApi from '@/api/sys/userCenterApi'
import whiteList from '@/router/whiteList' import whiteList from '@/router/whiteList'
import routesData from '@/router/systemRouter' import routesData from '@/router/systemRouter'
const modules = import.meta.glob('/src/views/**/**.vue') // findPwd和login路由组件已静态加载此处不在进行异步加载
const modules = import.meta.glob([
'/src/views/**/**.vue',
'!/src/views/auth/findPwd/**.vue',
'!/src/views/auth/login/**.vue'
])
export const useMenuStore = defineStore('menuStore', () => { export const useMenuStore = defineStore('menuStore', () => {
const menuData = ref([]) const menuData = ref([])
const refreshFlag = ref(false) const refreshFlag = ref(false)
@@ -119,24 +124,33 @@ export const useMenuStore = defineStore('menuStore', () => {
} }
}) })
} }
// 获取用户菜单 // 获取用户菜单通过API重新初始化菜单用于界面实时响应
const fetchMenu = async () => { const fetchMenu = async () => {
const menu = await userCenterApi.userLoginMenu() const menu = await userCenterApi.userLoginMenu()
tool.data.set('MENU', menu) tool.data.set('MENU', menu)
refreshMenu() refreshMenu()
} }
// 刷新菜单 // 刷新菜单非API刷新用于路由守卫内使用
const refreshMenu = () => { const refreshMenu = () => {
loadMenu() loadMenu()
removeFromRouter() removeFromRouter()
addToRouter() addToRouter()
changeRefreshFlag(true) changeRefreshFlag(true)
} }
// 通过API刷新菜单仅在layout的onMounted内使用浏览器刷新只刷新一次
const refreshApiMenu = () => {
userCenterApi.userLoginMenu().then((data) => {
tool.data.set('MENU', data)
nextTick(() => {
refreshMenu()
})
})
}
// 将菜单添加到路由 // 将菜单添加到路由
const addToRouter = () => { const addToRouter = () => {
menuData.value.forEach((item) => { menuData.value.forEach((item) => {
router.addRoute('layout', item) router.addRoute('layout', item)
}) })
} }
return { menuData, loadMenu, addToRouter, refreshMenu, changeRefreshFlag, refreshFlag, fetchMenu } return { menuData, loadMenu, addToRouter, refreshMenu, changeRefreshFlag, refreshFlag, fetchMenu, refreshApiMenu }
}) })

View File

@@ -367,6 +367,7 @@ body,
.selector-table, .selector-table,
.card-div, .card-div,
.ant-table-body, .ant-table-body,
.dict-tree-div,
.admin-ui-main{ .admin-ui-main{
&::-webkit-scrollbar { &::-webkit-scrollbar {

View File

@@ -15,7 +15,7 @@ export const required = (message, trigger = ['blur', 'change']) => ({
}) })
// 常用正则规则大全https://any86.github.io/any-rule/ // 常用正则规则大全https://any86.github.io/any-rule/
// 表单上面使用参照菜单管理的 title 字段,例如:-> title: [required('请输入菜单名称'), rules.horizontalChart]
export const rules = { export const rules = {
phone: { phone: {
pattern: /^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/, pattern: /^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/,
@@ -47,5 +47,17 @@ export const rules = {
pattern: /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/, pattern: /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/,
message: '只支持正数金额', message: '只支持正数金额',
trigger: 'blur' trigger: 'blur'
},
horizontalChart: {
pattern: /^[^-]*$/,
message: '不可包含横杠 “-”'
},
initialNotBackslashChart: {
pattern: /^(?!\/)[\s\S]*$/,
message: '首字母不可出现反斜杠 “/”'
},
initialYesBackslashChart: {
pattern: /^\/[^/].*$/,
message: '首字母必须是反斜杠 “/”'
} }
} }

View File

@@ -1,5 +1,3 @@
import { nextTick } from 'vue'
/** /**
* 页面全局 Loading * 页面全局 Loading
* @method start 创建 loading * @method start 创建 loading
@@ -10,19 +8,18 @@ export const NextLoading = {
start: () => { start: () => {
const el = document.querySelector('.admin-ui') const el = document.querySelector('.admin-ui')
if (el) return if (el) return
const bodys = document.body const body = document.body
const div = document.createElement('div') const div = document.createElement('div')
div.setAttribute('class', 'admin-ui') div.setAttribute('class', 'admin-ui')
const htmls = ` div.innerHTML = `
<div class="app-loading"> <div class="app-loading">
<div class="app-loading__logo"> <div class="app-loading-logo">
<img src="/img/logo.png"/> <img src="/img/logo.png"/>
</div> </div>
<div><span class="dot dot-spin"><i></i><i></i><i></i><i></i></span></div> <div><span class="dot dot-spin"><i></i><i></i><i></i><i></i></span></div>
<div class="app-loading__title">Snowy</div> <div class="app-loading-title">Snowy</div>
</div>` </div>`
div.innerHTML = htmls body.insertBefore(div, body.childNodes[0])
bodys.insertBefore(div, bodys.childNodes[0])
window.nextLoading = true window.nextLoading = true
}, },
// 移除 loading // 移除 loading

View File

@@ -11,7 +11,7 @@
// 统一的请求发送 // 统一的请求发送
import axios from 'axios' import axios from 'axios'
import qs from 'qs' import qs from 'qs'
import { Modal, message, notification } from 'ant-design-vue' import { Modal, message } from 'ant-design-vue'
import sysConfig from '@/config/index' import sysConfig from '@/config/index'
import tool from '@/utils/tool' import tool from '@/utils/tool'
@@ -140,7 +140,7 @@ service.interceptors.response.use(
if (error) { if (error) {
const status = 503 const status = 503
const description = errorCodeMap[status] const description = errorCodeMap[status]
notification.error({ console.error({
message: '请求错误', message: '请求错误',
description description
}) })

View File

@@ -0,0 +1,14 @@
// 递归选中或取消子节点a-tree专属方法
export const checkOrUnCheckChildren = (checked, node, checkedKeys) => {
if (node.children) {
node.children.forEach((item) => {
if (checked) {
checkedKeys.checked.push(item.id)
} else {
checkedKeys.checked = checkedKeys.checked.filter((k) => k !== item.id)
}
checkOrUnCheckChildren(checked, item, checkedKeys)
})
}
return checkedKeys.checked
}

View File

@@ -0,0 +1,35 @@
// 获取app.js 的哈希值
const getAppHash = (scripts) => {
let localVersion = ''
for (let i = 0; i < scripts.length; i++) {
let src = scripts[i].getAttribute('src')
if (src && src.indexOf('main.') !== -1) {
// 返回时间戳
localVersion = src.split('t=')[1] || ''
}
}
return localVersion
}
// 获取本地的app.js版本号
export const getLocalHash = () => {
return getAppHash(document.getElementsByTagName('script'))
}
// 获取线上的app.js版本号
export const checkHash = () => {
return new Promise((resolve, reject) => {
// 加上时间戳,防止缓存
fetch('/?t=' + Date.now())
.then(async (res) => {
let html = await res.text() //转成字符串判断
let doc = new DOMParser().parseFromString(html, 'text/html')
let newVersion = getAppHash(doc.getElementsByTagName('script'))
resolve(newVersion)
})
.catch((err) => {
console.log('获取版本号失败', err)
reject(err)
})
})
}

View File

@@ -139,8 +139,7 @@
} }
.login-form { .login-form {
width: 450px; width: 450px;
position: absolute; margin-top: 110px;
top: 21.8%;
} }
.login-header { .login-header {
margin-bottom: 20px; margin-bottom: 20px;
@@ -175,7 +174,7 @@
.logo_background { .logo_background {
position: absolute; position: absolute;
left: 0; left: 0;
top: 56px; top: 50px;
height: 60px; height: 60px;
padding-left: 56px; padding-left: 56px;
width: 100%; width: 100%;
@@ -240,11 +239,8 @@
left: 0; left: 0;
right: 0; right: 0;
} }
.login_background_front { .logo_background {
display: none; padding-left: 40px;
}
.logo_background{
padding-left:40px;
} }
.login-form { .login-form {
width: 100%; width: 100%;

View File

@@ -23,50 +23,32 @@
background-image: url(/img/login_background.png); background-image: url(/img/login_background.png);
position: relative; position: relative;
} }
.login_background_front {
width: 450px;
height: 450px;
margin-left: 100px;
margin-top: 15%;
overflow: hidden;
/*position: relative;*/
background-size: cover;
background-position: center;
background-image: url(/img/login_background_front.png);
animation-name: myfirst;
animation-duration: 5s;
animation-timing-function: linear;
animation-delay: 1s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-play-state: running;
}
@keyframes myfirst { @keyframes myfirst {
0% { 0% {
left: 0px; left: 0;
top: 0px; top: 0;
} }
50% { 50% {
left: 50px; left: 50px;
top: 0px; top: 0;
} }
100% { 100% {
left: 0px; left: 0;
top: 0px; top: 0;
} }
} }
@-webkit-keyframes myfirst /* Safari and Chrome */ { @-webkit-keyframes myfirst {
0% { 0% {
left: 0px; left: 0;
top: 0px; top: 0;
} }
50% { 50% {
left: 50px; left: 50px;
top: 0px; top: 0;
} }
100% { 100% {
left: 0px; left: 0;
top: 0px; top: 0;
} }
} }
.login_adv__title h2 { .login_adv__title h2 {
@@ -107,8 +89,7 @@
} }
.login-form { .login-form {
width: 450px; width: 450px;
position: absolute; margin-top: 110px;
top:21.8%
} }
.login-header { .login-header {
margin-bottom: 20px; margin-bottom: 20px;
@@ -126,7 +107,7 @@
.logo_background{ .logo_background{
position: absolute; position: absolute;
left: 0; left: 0;
top: 56px; top: 50px;
height: 60px; height: 60px;
padding-left: 56px; padding-left: 56px;
width: 100%; width: 100%;
@@ -180,9 +161,6 @@
left:0; left:0;
right:0; right:0;
} }
.login_background_front {
display: none;
}
.login-form { .login-form {
width: 100%; width: 100%;
padding: 20px 40px; padding: 20px 40px;

View File

@@ -170,7 +170,9 @@
onMounted(() => { onMounted(() => {
let formData = ref(configData.SYS_BASE_CONFIG) let formData = ref(configData.SYS_BASE_CONFIG)
configApi.configSysBaseList().then((data) => { configApi
.configSysBaseList()
.then((data) => {
if (data) { if (data) {
data.forEach((item) => { data.forEach((item) => {
formData.value[item.configKey] = item.configValue formData.value[item.configKey] = item.configValue
@@ -181,6 +183,7 @@
refreshSwitch() refreshSwitch()
} }
}) })
.catch(() => {})
}) })
onBeforeMount(() => { onBeforeMount(() => {
@@ -238,7 +241,7 @@
// 获取token // 获取token
try { try {
const loginToken = await loginApi.login(loginData) const loginToken = await loginApi.login(loginData)
const loginAfter = afterLogin(loginToken) await afterLogin(loginToken)
} catch (err) { } catch (err) {
loading.value = false loading.value = false
if (captchaOpen.value === 'true') { if (captchaOpen.value === 'true') {

View File

@@ -1,11 +1,5 @@
<template> <template>
<xn-form-container <xn-form-container title="详情" :width="1000" v-model:open="open" :destroy-on-close="true" @close="onClose">
title="详情"
:width="1000"
v-model:open="open"
:destroy-on-close="true"
@close="onClose"
>
<a-descriptions bordered> <a-descriptions bordered>
<a-descriptions-item label="标题">{{formData.title}}</a-descriptions-item> <a-descriptions-item label="标题">{{formData.title}}</a-descriptions-item>
<a-descriptions-item label="类型"> <a-descriptions-item label="类型">

View File

@@ -118,17 +118,18 @@
</a-row> </a-row>
<a-form-item label="任职信息" name="positionJson"> <a-form-item label="任职信息" name="positionJson">
<a-button type="primary" class="childAddButton" @click="addDomains()">
<PlusOutlined />
增加任职
</a-button>
<a-row :gutter="10" class="form-row"> <a-row :gutter="10" class="form-row">
<a-col :span="7" class="form-row-con"> 机构 </a-col> <a-col :span="7" class="form-row-con"> 机构 </a-col>
<a-col :span="7" class="form-row-con"> 职位 </a-col> <a-col :span="7" class="form-row-con"> 职位 </a-col>
<a-col :span="7" class="form-row-con"> 主管 </a-col> <a-col :span="7" class="form-row-con"> 主管 </a-col>
<a-col :span="3" class="form-row-con"> 操作 </a-col> <a-col :span="3" class="form-row-con">
<a-button type="primary" @click="addDomains()" size="small">
<PlusOutlined />
增加
</a-button>
</a-col>
</a-row> </a-row>
<div v-for="(positionInfo, index) in formData.positionJson" class="form-div"> <div :key="positionInfo" v-for="(positionInfo, index) in formData.positionJson">
<a-row :gutter="10"> <a-row :gutter="10">
<a-col :span="7"> <a-col :span="7">
<a-form-item <a-form-item
@@ -545,24 +546,15 @@
}) })
</script> </script>
<style scoped type="less"> <style scoped lang="less">
.childAddButton {
margin-bottom: 10px;
}
.form-row { .form-row {
background-color: var(--item-hover-bg); background-color: var(--item-hover-bg);
margin-left: 0px !important; margin-left: 0 !important;
margin-bottom: 10px;
} }
.form-row-con { .form-row-con {
padding-bottom: 5px; padding-bottom: 5px;
padding-top: 5px; padding-top: 5px;
padding-left: 15px; padding-left: 15px;
} }
.dashedButton {
margin-top: 10px;
width: 100%;
}
.form-div {
padding-top: 10px;
}
</style> </style>

View File

@@ -1,184 +0,0 @@
<template>
<a-row :gutter="10">
<a-col :xs="24" :sm="24" :md="24" :lg="5" :xl="5">
<a-tree
v-if="treeData.length > 0"
v-model:expandedKeys="defaultExpandedKeys"
:tree-data="treeData"
:field-names="treeFieldNames"
@select="treeSelect"
>
</a-tree>
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
</a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="19" :xl="19">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item name="searchKey" label="字典名称">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入字典名称" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="tableRef.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
<a-button class="snowy-button-left" @click="reset">
<template #icon><redo-outlined /></template>
重置
</a-button>
</a-col>
</a-row>
</a-form>
<a-divider class="m-3 mx-0" />
<s-table
ref="tableRef"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
bordered
:tool-config="toolConfig"
:row-key="(record) => record.id"
>
<template #operator class="table-operator">
<a-button type="primary" @click="formRef.onOpen(undefined, 'FRM', searchFormState.parentId)">
<template #icon><plus-outlined /></template>
新增
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'level'">
<a-tag color="blue" v-if="record.level">{{ record.level }}</a-tag>
<a-tag color="green" v-else>子级</a-tag>
</template>
<template v-if="column.dataIndex === 'action'">
<a @click="formRef.onOpen(record, 'FRM')">编辑</a>
</template>
</template>
</s-table>
</a-col>
</a-row>
<Form ref="formRef" @successful="formSuccessful()" />
</template>
<script setup>
import { Empty } from 'ant-design-vue'
import dictApi from '@/api/dev/dictApi'
import Form from './form.vue'
import tool from '@/utils/tool'
const searchFormState = ref({})
const columns = [
{
title: '字典名称',
dataIndex: 'dictLabel',
width: 350
},
{
title: '字典值',
dataIndex: 'dictValue',
width: 350
},
{
title: '排序',
dataIndex: 'sortCode'
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
width: '150px'
}
]
// 定义tableDOM
const tableRef = ref(null)
const formRef = ref()
const searchFormRef = ref()
// 默认展开的节点
const defaultExpandedKeys = ref([])
const treeData = ref([])
// 替换treeNode 中 title,key,children
const treeFieldNames = { children: 'children', title: 'dictLabel', key: 'id' }
const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
// 表格查询 返回 Promise 对象
const loadData = (parameter) => {
loadTreeData()
parameter.category = 'FRM'
return dictApi.dictPage(Object.assign(parameter, searchFormState.value)).then((data) => {
if (data.records) {
if (searchFormState.value.parentId) {
let dataArray = []
data.records.forEach((item) => {
const obj = data.records.find((f) => f.id === item.parentId)
if (!obj) {
dataArray.push(item)
}
})
if (dataArray.length === 1) {
data.records.forEach((item) => {
if (item.id === dataArray[0].id) {
item.level = '上级'
}
})
}
dataArray = []
}
}
return data
})
}
// 重置
const reset = () => {
searchFormRef.value.resetFields()
tableRef.value.refresh(true)
}
// 加载左侧的树
const loadTreeData = () => {
const param = {
category: 'FRM'
}
dictApi.dictTree(param).then((res) => {
if (res) {
treeData.value = res
}
})
}
// 点击树查询
const treeSelect = (selectedKeys) => {
if (selectedKeys && selectedKeys.length > 0) {
searchFormState.value.parentId = selectedKeys.toString()
if (!columns.find((f) => f.title === '层级')) {
columns.splice(2, 0, {
title: '层级',
dataIndex: 'level',
width: 100
})
}
} else {
delete searchFormState.value.parentId
columns.splice(2, 1)
}
tableRef.value.refresh(true)
}
// 表单界面回调
const formSuccessful = () => {
tableRef.value.refresh()
refreshStoreDict()
}
// 刷新store中的字典
const refreshStoreDict = () => {
dictApi.dictTree().then((res) => {
tool.data.set('DICT_TYPE_TREE_DATA', res)
})
}
</script>
<style scoped>
.ant-form-item {
margin-bottom: 0 !important;
}
.snowy-button-left {
margin-left: 8px;
}
</style>

View File

@@ -1,6 +1,7 @@
<template> <template>
<a-row :gutter="10"> <a-row :gutter="10">
<a-col :xs="24" :sm="24" :md="24" :lg="5" :xl="5"> <a-col :xs="24" :sm="24" :md="24" :lg="5" :xl="5">
<div class="dict-tree-div">
<a-tree <a-tree
v-if="treeData.length > 0" v-if="treeData.length > 0"
v-model:expandedKeys="defaultExpandedKeys" v-model:expandedKeys="defaultExpandedKeys"
@@ -10,6 +11,7 @@
> >
</a-tree> </a-tree>
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" /> <a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
</div>
</a-col> </a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="19" :xl="19"> <a-col :xs="24" :sm="24" :md="24" :lg="19" :xl="19">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form mb-3" :model="searchFormState"> <a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form mb-3" :model="searchFormState">
@@ -42,7 +44,7 @@
:row-key="(record) => record.id" :row-key="(record) => record.id"
> >
<template #operator class="table-operator"> <template #operator class="table-operator">
<a-button type="primary" @click="formRef.onOpen(undefined, 'BIZ', searchFormState.parentId)"> <a-button type="primary" @click="formRef.onOpen(undefined, categoryType, searchFormState.parentId)">
<template #icon><plus-outlined /></template> <template #icon><plus-outlined /></template>
新增 新增
</a-button> </a-button>
@@ -53,7 +55,7 @@
<a-tag color="green" v-else>子级</a-tag> <a-tag color="green" v-else>子级</a-tag>
</template> </template>
<template v-if="column.dataIndex === 'action'"> <template v-if="column.dataIndex === 'action'">
<a @click="formRef.onOpen(record, 'BIZ')">编辑</a> <a @click="formRef.onOpen(record, categoryType)">编辑</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm title="删除此字典与下级字典吗?" @confirm="remove(record)"> <a-popconfirm title="删除此字典与下级字典吗?" @confirm="remove(record)">
<a-button type="link" danger size="small">删除</a-button> <a-button type="link" danger size="small">删除</a-button>
@@ -66,11 +68,17 @@
<Form ref="formRef" @successful="formSuccessful()" /> <Form ref="formRef" @successful="formSuccessful()" />
</template> </template>
<script setup> <script setup name="dictCategoryIndex">
import { Empty } from 'ant-design-vue' import { Empty } from 'ant-design-vue'
import dictApi from '@/api/dev/dictApi' import dictApi from '@/api/dev/dictApi'
import Form from './form.vue' import Form from './form.vue'
import tool from '@/utils/tool' import tool from '@/utils/tool'
const props = defineProps({
type: {
type: String,
default: 'FRM'
}
})
const columns = [ const columns = [
{ {
title: '字典名称', title: '字典名称',
@@ -93,6 +101,9 @@
width: '150px' width: '150px'
} }
] ]
const categoryType = computed(() => {
return props.type
})
// tableDOM // tableDOM
const tableRef = ref(null) const tableRef = ref(null)
const formRef = ref() const formRef = ref()
@@ -108,7 +119,7 @@
// Promise // Promise
const loadData = (parameter) => { const loadData = (parameter) => {
loadTreeData() loadTreeData()
parameter.category = 'BIZ' parameter.category = categoryType.value
return dictApi.dictPage(Object.assign(parameter, searchFormState.value)).then((data) => { return dictApi.dictPage(Object.assign(parameter, searchFormState.value)).then((data) => {
if (data.records) { if (data.records) {
if (searchFormState.value.parentId) { if (searchFormState.value.parentId) {
@@ -140,7 +151,7 @@
// //
const loadTreeData = () => { const loadTreeData = () => {
const param = { const param = {
category: 'BIZ' category: categoryType.value
} }
dictApi.dictTree(param).then((res) => { dictApi.dictTree(param).then((res) => {
if (res) { if (res) {
@@ -172,8 +183,12 @@
id: record.id id: record.id
} }
] ]
dictApi.dictDelete(params).then(() => { dictApi.dictDelete(params).then((res) => {
if (res.code === 200) {
tableRef.value.refresh(true) tableRef.value.refresh(true)
} else {
res.message && tool.error(res.message)
}
}) })
refreshStoreDict() refreshStoreDict()
} }
@@ -190,11 +205,15 @@
} }
</script> </script>
<style scoped> <style scoped lang="less">
.ant-form-item { .ant-form-item {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.snowy-button-left { .snowy-button-left {
margin-left: 8px; margin-left: 8px;
} }
.dict-tree-div {
height: 700px;
overflow: auto;
}
</style> </style>

View File

@@ -1,32 +1,23 @@
<template> <template>
<a-card <a-card>
:bordered="false" <a-tabs size="large" v-model:activeKey="activeKey">
:active-tab-key="activeKey" <a-tab-pane v-for="item in tabListNoTitle" :key="item.key" :tab="item.tab">
:tab-list="tabListNoTitle" <category :type="item.key" />
@tabChange="(key) => onTabChange(key, 'frmIndex')" </a-tab-pane>
> </a-tabs>
<p v-if="activeKey === 'frmIndex'">
<frm-index />
</p>
<p v-if="activeKey === 'bizIndex'">
<biz-index />
</p>
</a-card> </a-card>
</template> </template>
<script setup name="devDict"> <script setup name="devDict">
import frmIndex from './category/frmIndex.vue' import Category from './category/index.vue'
import bizIndex from './category/bizIndex.vue' const activeKey = ref('FRM')
const activeKey = ref('frmIndex')
const tabListNoTitle = ref([ const tabListNoTitle = ref([
{ key: 'frmIndex', tab: '系统字典' }, { key: 'FRM', tab: '系统字典' },
{ key: 'bizIndex', tab: '业务字典' } { key: 'BIZ', tab: '业务字典' }
]) ])
const onTabChange = (value, type) => {
if (type === 'key') {
key.value = value
} else if (type === 'frmIndex') {
activeKey.value = value
}
}
</script> </script>
<style lang="less" scoped>
:deep(.ant-card-body) {
padding-top: 0 !important;
}
</style>

View File

@@ -1,11 +1,5 @@
<template> <template>
<xn-form-container <xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
title="详情"
:width="700"
:visible="visible"
:destroy-on-close="true"
@close="onClose"
>
<a-descriptions :column="1" size="middle" bordered class="mb-2"> <a-descriptions :column="1" size="middle" bordered class="mb-2">
<a-descriptions-item label="名称">{{ formData.name }}</a-descriptions-item> <a-descriptions-item label="名称">{{ formData.name }}</a-descriptions-item>
<a-descriptions-item label="请求IP">{{ formData.opIp }}</a-descriptions-item> <a-descriptions-item label="请求IP">{{ formData.opIp }}</a-descriptions-item>

View File

@@ -1,11 +1,5 @@
<template> <template>
<xn-form-container <xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
title="详情"
:width="700"
:visible="visible"
:destroy-on-close="true"
@close="onClose"
>
<a-descriptions :column="1" size="middle" bordered class="mb-2"> <a-descriptions :column="1" size="middle" bordered class="mb-2">
<a-descriptions-item label="名称">{{ formData.name }}</a-descriptions-item> <a-descriptions-item label="名称">{{ formData.name }}</a-descriptions-item>
<a-descriptions-item label="IP地址">{{ formData.opIp }}</a-descriptions-item> <a-descriptions-item label="IP地址">{{ formData.opIp }}</a-descriptions-item>

View File

@@ -51,7 +51,7 @@
<template #label> <template #label>
<a-tooltip> <a-tooltip>
<template #title> <template #title>
类型为内外链输入https开头的链接即可https://xiaonuo.vip,正常路由前面必须有反斜杠! 类型为内外链时输入https开头的链接即可https://xiaonuo.vip,正常路由前面必须有反斜杠!
</template> </template>
<question-circle-outlined /> <question-circle-outlined />
</a-tooltip> </a-tooltip>
@@ -122,7 +122,7 @@
</template> </template>
<script setup name="sysResourceMenuForm"> <script setup name="sysResourceMenuForm">
import { required } from '@/utils/formRules' import { required, rules } from '@/utils/formRules'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import SnowflakeId from 'snowflake-id' import SnowflakeId from 'snowflake-id'
import tool from '@/utils/tool' import tool from '@/utils/tool'
@@ -197,20 +197,20 @@
// 图标选择器回调 // 图标选择器回调
const iconCallBack = (value) => { const iconCallBack = (value) => {
if (value) { if (value) {
formRef.value.clearValidate("icon") formRef.value.clearValidate('icon')
} }
formData.value.icon = value formData.value.icon = value
} }
// 默认要校验的 // 默认要校验的
const formRules = { const formRules = {
title: [required('请输入菜单名称')], title: [required('请输入菜单名称'), rules.horizontalChart],
parentId: [required('请选择上级菜单')], parentId: [required('请选择上级菜单')],
menuType: [required('请选择菜单类型')], menuType: [required('请选择菜单类型')],
path: [required('请输入路由地址')], path: [required('请输入路由地址')],
name: [required('请输入组件中name属性')], name: [required('请输入组件中name属性')],
module: [required('请选择模块')], module: [required('请选择模块')],
component: [required('请输入组件地址')], component: [required('请输入组件地址'), rules.initialNotBackslashChart],
visible: [required('请选择是否可见')] visible: [required('请选择是否可见')]
} }

View File

@@ -101,8 +101,7 @@
// firstShowMap = {} // 重置单元格合并映射 // firstShowMap = {} // 重置单元格合并映射
// 如果有数据,我们再不去反复的查询 // 如果有数据,我们再不去反复的查询
if (echoDatalist.value.length > 0) { if (echoDatalist.value.length > 0) {
let data = echoDatalist.value.find((f) => f.id === moduleId.value).menu loadDatas.value = echoDatalist.value.find((f) => f.id === moduleId.value).menu
loadDatas.value = data
} else { } else {
// 获取表格数据 // 获取表格数据
spinningLoading.value = true spinningLoading.value = true
@@ -144,11 +143,11 @@
if (module.menu) { if (module.menu) {
// 加入回显内容 // 加入回显内容
module.menu.forEach((item) => { module.menu.forEach((item) => {
const menueCheck = ref(0) const menusCheck = ref(0)
if (resEcho.grantInfoList.length > 0) { if (resEcho.grantInfoList.length > 0) {
resEcho.grantInfoList.forEach((grant) => { resEcho.grantInfoList.forEach((grant) => {
if (item.id === grant.menuId) { if (item.id === grant.menuId) {
menueCheck.value++ menusCheck.value++
// 处理按钮 // 处理按钮
if (grant.buttonInfo) { if (grant.buttonInfo) {
grant.buttonInfo.forEach((button) => { grant.buttonInfo.forEach((button) => {
@@ -163,15 +162,23 @@
}) })
} }
// 回显前面的2个 // 回显前面的2个
if (menueCheck.value > 0) { if (menusCheck.value > 0) {
item.parentCheck = true item.parentCheck = true
item.nameCheck = true item.nameCheck = true
} }
}) })
// 排序 // 排序
module.menu = module.menu.sort((a, b) => { module.menu.sort((a, b) => {
return a.parentId - b.parentId // 首先比较parentName属性
let nameComparison = b.parentName.localeCompare(a.parentName)
if (nameComparison !== 0) {
// 如果parentName不同直接返回parentName的比较结果
return nameComparison
} else {
// 如果name相同则比较parentId属性直接返回parentId的差值
return Number(a.parentId) - Number(b.parentId)
}
}) })
// 缓存加入索引 // 缓存加入索引
module.menu.forEach((item, index) => { module.menu.forEach((item, index) => {
@@ -200,12 +207,11 @@
}) })
} }
const checkAllChildNotChecked = (record) => { const checkAllChildNotChecked = (record) => {
const allChecked = checkFieldKeys.every((key) => { return checkFieldKeys.every((key) => {
// 遍历所有的字段 // 遍历所有的字段
const child = record[key] const child = record[key]
return child.every((field) => !field.check) return child.every((field) => !field.check)
}) })
return allChecked
} }
const changeChildCheckBox = (record, evt) => { const changeChildCheckBox = (record, evt) => {
let checked = evt.target.checked let checked = evt.target.checked
@@ -298,7 +304,7 @@
<style scoped> <style scoped>
/* 重写复选框的样式 */ /* 重写复选框的样式 */
.ant-checkbox-wrapper { .ant-checkbox-wrapper {
margin-left: 0px !important; margin-left: 0 !important;
padding-top: 2px !important; padding-top: 2px !important;
padding-bottom: 2px !important; padding-bottom: 2px !important;
} }

View File

@@ -17,13 +17,20 @@
class="mt-4" class="mt-4"
size="middle" size="middle"
:columns="columns" :columns="columns"
:data-source="loadDatas" :data-source="tableLoadData"
:row-key="(record) => record.api" :row-key="(record) => record.api"
:pagination="pagination"
@change="handleTableChange"
bordered bordered
> >
<template #headerCell="{ column }"> <template #headerCell="{ column }">
<template v-if="column.key === 'api'"> <template v-if="column.key === 'prefix'">
<a-checkbox @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox> <a-checkbox :checked="allChecked" @update:checked="(val) => onCheckAllChange(val)">
{{ column.title }}
</a-checkbox>
</template>
<template v-if="column.key === 'suffix'">
<a-checkbox :checked="allChecked" @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox>
</template> </template>
<template v-if="column.key === 'dataScope'"> <template v-if="column.key === 'dataScope'">
<span>{{ column.title }}</span> <span>{{ column.title }}</span>
@@ -54,16 +61,21 @@
<template #icon><SearchOutlined /></template> <template #icon><SearchOutlined /></template>
搜索 搜索
</a-button> </a-button>
<a-button size="small" class="xn-wd90" @click="handleReset(clearFilters)"> 重置 </a-button> <a-button size="small" class="xn-wd90" @click="handleReset(clearFilters, confirm)"> 重置 </a-button>
</div> </div>
</template> </template>
<template #customFilterIcon="{ filtered }"> <template #customFilterIcon="{ filtered }">
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" /> <search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'api'"> <template v-if="column.dataIndex === 'prefix'">
<a-checkbox :checked="record.parentCheck" @update:checked="(val) => changeParentApi(record, val)">
{{ record.prefix }}
</a-checkbox>
</template>
<template v-if="column.dataIndex === 'suffix'">
<a-checkbox :checked="record.check" @update:checked="(val) => changeApi(record, val)"> <a-checkbox :checked="record.check" @update:checked="(val) => changeApi(record, val)">
{{ record.api }} {{ record.suffix }}
</a-checkbox> </a-checkbox>
</template> </template>
<template v-if="column.dataIndex === 'dataScope'"> <template v-if="column.dataIndex === 'dataScope'">
@@ -109,6 +121,7 @@
import roleApi from '@/api/sys/roleApi' import roleApi from '@/api/sys/roleApi'
import ScopeDefineOrg from './scopeDefineOrg.vue' import ScopeDefineOrg from './scopeDefineOrg.vue'
import { userStore } from '@/store/user' import { userStore } from '@/store/user'
import { cloneDeep } from 'lodash-es'
const visible = ref(false) const visible = ref(false)
const spinningLoading = ref(false) const spinningLoading = ref(false)
@@ -117,7 +130,7 @@
const submitLoading = ref(false) const submitLoading = ref(false)
const CustomValue = 'SCOPE_ORG_DEFINE' const CustomValue = 'SCOPE_ORG_DEFINE'
// 抽屉的宽度 // 抽屉的宽度
const drawerWidth = 1000 const drawerWidth = 1050
// 自动获取宽度默认获取浏览器的宽度的90% // 自动获取宽度默认获取浏览器的宽度的90%
//(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9 //(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9
let loadDatas = ref([]) let loadDatas = ref([])
@@ -128,13 +141,39 @@
searchedColumn: '' searchedColumn: ''
}) })
const searchInput = ref() const searchInput = ref()
// 分页
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
defaultPageSize: 10,
pageSizeOptions: ['10', '20', '50', '100']
})
const firstShowMap = ref({})
// 全选
const allChecked = ref(false)
const tableLoadData = ref([])
const columns = [ const columns = [
{ {
key: 'api', key: 'prefix',
title: '接口前缀',
dataIndex: 'prefix',
width: 140,
customCell: (row, index) => {
const indexArr = firstShowMap.value[row.prefix]
if (index === indexArr[0]) {
return { rowSpan: indexArr.length }
}
return { rowSpan: 0 }
}
},
{
key: 'suffix',
title: '接口', title: '接口',
dataIndex: 'api', dataIndex: 'suffix',
width: 380, width: 290,
customFilterDropdown: true, customFilterDropdown: true,
onFilter: (value, record) => record.api.includes(value), onFilter: (value, record) => record.api.includes(value),
onFilterDropdownOpenChange: (visible) => { onFilterDropdownOpenChange: (visible) => {
@@ -161,16 +200,27 @@
} }
const resOwn = await roleApi.roleOwnPermission(param) const resOwn = await roleApi.roleOwnPermission(param)
// 数据转换 // 数据转换
echoModuleData(res, resOwn) loadDatas.value = echoModuleData(res, resOwn)
pagination.value.total = loadDatas.value.length
allChecked.value = loadDatas.value.every((item) => item.parentCheck)
spinningLoading.value = false spinningLoading.value = false
} }
// table事件触发
const handleTableChange = (pageInfo) => {
Object.assign(pagination.value, pageInfo)
}
// 数据转换 // 数据转换
const echoModuleData = (res, resOwn) => { const echoModuleData = (res, resOwn) => {
let list = []
res.forEach((api) => { res.forEach((api) => {
const apiArr = splitByThirdSlash(api)
const obj = { const obj = {
api: api, api: api,
prefix: apiArr[0],
suffix: apiArr[1],
dataScope: datascope(api), dataScope: datascope(api),
check: false check: false,
parentCheck: false
} }
if (resOwn.grantInfoList.length > 0) { if (resOwn.grantInfoList.length > 0) {
resOwn.grantInfoList.forEach((item) => { resOwn.grantInfoList.forEach((item) => {
@@ -189,8 +239,10 @@
} }
}) })
} }
loadDatas.value.push(obj) list.push(obj)
}) })
// 设置父节点check状态
return setParentDataCheckedStatus(list)
} }
const datascope = (id) => { const datascope = (id) => {
return [ return [
@@ -277,6 +329,11 @@
const onOpen = (record) => { const onOpen = (record) => {
grantPermissionParam.id = record.id grantPermissionParam.id = record.id
visible.value = true visible.value = true
firstShowMap.value = {}
pagination.value.current = 1
pagination.value.pageSize = 10
pagination.value.total = 0
allChecked.value = false
loadData() loadData()
} }
// 关闭抽屉 // 关闭抽屉
@@ -288,15 +345,41 @@
} }
// 全选 // 全选
const onCheckAllChange = (value) => { const onCheckAllChange = (value) => {
allChecked.value = value
spinningLoading.value = true spinningLoading.value = true
loadDatas.value.forEach((data) => { loadDatas.value.forEach((data) => {
changeApi(data, value) changeApi(data, value, false)
data.parentCheck = value
spinningLoading.value = false spinningLoading.value = false
}) })
} }
// 选中接口前缀
const changeParentApi = (record, val) => {
loadDatas.value.forEach((data) => {
if (data.prefix === record.prefix) {
data.check = val
data.parentCheck = val
if (val) {
let isChecked = data.dataScope.some((item) => item.check)
if (!isChecked) {
data.dataScope[0].check = true
}
} else {
data.dataScope.forEach((item) => {
item.check = false
})
}
data.dataScope.forEach((item) => {
if (item.value === 'SCOPE_ORG_DEFINE') {
item.scopeDefineOrgIdList = []
}
})
}
})
allChecked.value = loadDatas.value.every((item) => item.parentCheck)
}
// 选中接口 // 选中接口
const changeApi = (record, val) => { const changeApi = (record, val, isLoadData = true) => {
record.check = val record.check = val
if (val) { if (val) {
let checkStatus = 0 let checkStatus = 0
@@ -317,6 +400,7 @@
} }
}) })
} }
isLoadData && (loadDatas.value = setParentDataCheckedStatus(loadDatas.value))
} }
// 设置选中状态 // 设置选中状态
const changeChildCheckBox = (record, evt) => { const changeChildCheckBox = (record, evt) => {
@@ -382,8 +466,9 @@
state.searchedColumn = dataIndex state.searchedColumn = dataIndex
} }
// 标题接口列搜索重置 // 标题接口列搜索重置
const handleReset = (clearFilters) => { const handleReset = (clearFilters, confirm) => {
clearFilters() clearFilters()
confirm()
state.searchText = '' state.searchText = ''
} }
// 标题数据范围列radio-group事件 // 标题数据范围列radio-group事件
@@ -398,6 +483,42 @@
}) })
}) })
} }
// 设置父节点check状态
const setParentDataCheckedStatus = (records) => {
const cloneRecords = cloneDeep(records)
cloneRecords.forEach((item) => {
let childrenList = records.filter((f) => f.prefix === item.prefix)
item.parentCheck = childrenList.every((e) => e.check)
})
return cloneRecords
}
// 字符串分割
function splitByThirdSlash(str) {
const arr = str.split('/').filter(Boolean)
const leftPart = '/' + arr.slice(0, 2).join('/')
const rightPart = '/' + arr.slice(2).join('/')
return [leftPart, rightPart]
}
// 监听分页及数据变化
watch(
() => [pagination.value, loadDatas.value],
(val) => {
const start = (pagination.value.current - 1) * pagination.value.pageSize
const end = start + pagination.value.pageSize
const tableData = loadDatas.value.slice(start, end)
firstShowMap.value = {}
// 生成map
tableData?.forEach((item, index) => {
if (firstShowMap.value[item.prefix]) {
firstShowMap.value[item.prefix].push(index)
} else {
firstShowMap.value[item.prefix] = [index]
}
})
tableLoadData.value = tableData
},
{ deep: true }
)
// 调用这个函数将子组件的一些数据和方法暴露出去 // 调用这个函数将子组件的一些数据和方法暴露出去
defineExpose({ defineExpose({
onOpen onOpen

View File

@@ -104,8 +104,8 @@
// firstShowMap = {} // 重置单元格合并映射 // firstShowMap = {} // 重置单元格合并映射
// 如果有数据,我们再不去反复的查询 // 如果有数据,我们再不去反复的查询
if (echoDatalist.value.length > 0) { if (echoDatalist.value.length > 0) {
let data = echoDatalist.value.find((f) => f.id === moduleId.value).menu // 这里必须保持联动,不可克隆
loadDatas.value = data loadDatas.value = echoDatalist.value.find((f) => f.id === moduleId.value).menu
} else { } else {
// 获取表格数据 // 获取表格数据
spinningLoading.value = true spinningLoading.value = true
@@ -142,11 +142,11 @@
if (module.menu) { if (module.menu) {
// 加入回显内容 // 加入回显内容
module.menu.forEach((item) => { module.menu.forEach((item) => {
const menueCheck = ref(0) const menusCheck = ref(0)
if (resEcho.grantInfoList.length > 0) { if (resEcho.grantInfoList.length > 0) {
resEcho.grantInfoList.forEach((grant) => { resEcho.grantInfoList.forEach((grant) => {
if (item.id === grant.menuId) { if (item.id === grant.menuId) {
menueCheck.value++ menusCheck.value++
// 处理按钮 // 处理按钮
if (grant.buttonInfo.length > 0) { if (grant.buttonInfo.length > 0) {
grant.buttonInfo.forEach((button) => { grant.buttonInfo.forEach((button) => {
@@ -161,15 +161,23 @@
}) })
} }
// 回显前面的2个 // 回显前面的2个
if (menueCheck.value > 0) { if (menusCheck.value > 0) {
item.parentCheck = true item.parentCheck = true
item.nameCheck = true item.nameCheck = true
} }
}) })
// 排序 // 排序
module.menu = module.menu.sort((a, b) => { module.menu.sort((a, b) => {
return a.parentId - b.parentId // 首先比较parentName属性
let nameComparison = b.parentName.localeCompare(a.parentName)
if (nameComparison !== 0) {
// 如果parentName不同直接返回parentName的比较结果
return nameComparison
} else {
// 如果name相同则比较parentId属性直接返回parentId的差值
return Number(a.parentId) - Number(b.parentId)
}
}) })
// 缓存加入索引 // 缓存加入索引
module.menu.forEach((item, index) => { module.menu.forEach((item, index) => {
@@ -198,12 +206,11 @@
}) })
} }
const checkAllChildNotChecked = (record) => { const checkAllChildNotChecked = (record) => {
const allChecked = checkFieldKeys.every((key) => { return checkFieldKeys.every((key) => {
// 遍历所有的字段 // 遍历所有的字段
const child = record[key] const child = record[key]
return child.every((field) => !field.check) return child.every((field) => !field.check)
}) })
return allChecked
} }
const changeChildCheckBox = (record, evt) => { const changeChildCheckBox = (record, evt) => {
let checked = evt.target.checked let checked = evt.target.checked
@@ -303,7 +310,7 @@
<style scoped> <style scoped>
/* 重写复选框的样式 */ /* 重写复选框的样式 */
.ant-checkbox-wrapper { .ant-checkbox-wrapper {
margin-left: 0px !important; margin-left: 0 !important;
padding-top: 2px !important; padding-top: 2px !important;
padding-bottom: 2px !important; padding-bottom: 2px !important;
} }

View File

@@ -15,6 +15,7 @@
:tree-data="treeData" :tree-data="treeData"
:field-names="treeFieldNames" :field-names="treeFieldNames"
checkable checkable
check-strictly
:selectable="false" :selectable="false"
@check="treeCheck" @check="treeCheck"
> >
@@ -25,6 +26,7 @@
<script setup="props, context" name="scopeDefineOrg"> <script setup="props, context" name="scopeDefineOrg">
import roleApi from '@/api/sys/roleApi' import roleApi from '@/api/sys/roleApi'
import { checkOrUnCheckChildren } from '@/utils/treeHandler'
const visible = ref(false) const visible = ref(false)
let defaultExpandedKeys = ref([]) let defaultExpandedKeys = ref([])
let checkedKeys = ref([]) let checkedKeys = ref([])
@@ -83,8 +85,8 @@
const treeFieldNames = { children: 'children', title: 'name', key: 'id' } const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
// 选中触发 // 选中触发
const treeCheck = (checkedKeys) => { const treeCheck = (checkedKeys, { checked, node }) => {
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkedKeys resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkOrUnCheckChildren(checked, node, checkedKeys)
} }
// 定义emit事件 // 定义emit事件
const emit = defineEmits({ const emit = defineEmits({

View File

@@ -118,17 +118,18 @@
</a-row> </a-row>
<a-form-item label="任职信息" name="positionJson"> <a-form-item label="任职信息" name="positionJson">
<a-button type="primary" class="childAddButton" @click="addDomains()">
<PlusOutlined />
增加任职
</a-button>
<a-row :gutter="10" class="form-row"> <a-row :gutter="10" class="form-row">
<a-col :span="7" class="form-row-con"> 机构 </a-col> <a-col :span="7" class="form-row-con"> 机构 </a-col>
<a-col :span="7" class="form-row-con"> 职位 </a-col> <a-col :span="7" class="form-row-con"> 职位 </a-col>
<a-col :span="7" class="form-row-con"> 主管 </a-col> <a-col :span="7" class="form-row-con"> 主管 </a-col>
<a-col :span="3" class="form-row-con"> 操作 </a-col> <a-col :span="3" class="form-row-con">
<a-button type="primary" @click="addDomains()" size="small">
<PlusOutlined />
增加
</a-button>
</a-col>
</a-row> </a-row>
<div v-for="(positionInfo, index) in formData.positionJson" class="form-div"> <div :key="positionInfo" v-for="(positionInfo, index) in formData.positionJson">
<a-row :gutter="10"> <a-row :gutter="10">
<a-col :span="7"> <a-col :span="7">
<a-form-item <a-form-item
@@ -549,19 +550,14 @@
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.childAddButton {
margin-bottom: 10px;
}
.form-row { .form-row {
background-color: var(--item-hover-bg); background-color: var(--item-hover-bg);
margin-left: 0px !important; margin-left: 0 !important;
margin-bottom: 10px;
} }
.form-row-con { .form-row-con {
padding-bottom: 5px; padding-bottom: 5px;
padding-top: 5px; padding-top: 5px;
padding-left: 15px; padding-left: 15px;
} }
.form-div {
padding-top: 10px;
}
</style> </style>

View File

@@ -17,13 +17,20 @@
class="mt-4" class="mt-4"
size="middle" size="middle"
:columns="columns" :columns="columns"
:data-source="loadDatas" :data-source="tableLoadData"
bordered bordered
:row-key="(record) => record.api" :row-key="(record) => record.api"
:pagination="pagination"
@change="handleTableChange"
> >
<template #headerCell="{ column }"> <template #headerCell="{ column }">
<template v-if="column.key === 'api'"> <template v-if="column.key === 'prefix'">
<a-checkbox @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox> <a-checkbox :checked="allChecked" @update:checked="(val) => onCheckAllChange(val)">
{{ column.title }}
</a-checkbox>
</template>
<template v-if="column.key === 'suffix'">
<a-checkbox :checked="allChecked" @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox>
</template> </template>
<template v-if="column.key === 'dataScope'"> <template v-if="column.key === 'dataScope'">
<span>{{ column.title }}</span> <span>{{ column.title }}</span>
@@ -54,16 +61,21 @@
<template #icon><SearchOutlined /></template> <template #icon><SearchOutlined /></template>
搜索 搜索
</a-button> </a-button>
<a-button size="small" class="xn-wd90" @click="handleReset(clearFilters)"> 重置 </a-button> <a-button size="small" class="xn-wd90" @click="handleReset(clearFilters, confirm)"> 重置 </a-button>
</div> </div>
</template> </template>
<template #customFilterIcon="{ filtered }"> <template #customFilterIcon="{ filtered }">
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" /> <search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'api'"> <template v-if="column.dataIndex === 'prefix'">
<a-checkbox :checked="record.parentCheck" @update:checked="(val) => changeParentApi(record, val)">
{{ record.prefix }}
</a-checkbox>
</template>
<template v-if="column.dataIndex === 'suffix'">
<a-checkbox :checked="record.check" @update:checked="(val) => changeApi(record, val)"> <a-checkbox :checked="record.check" @update:checked="(val) => changeApi(record, val)">
{{ record.api }} {{ record.suffix }}
</a-checkbox> </a-checkbox>
</template> </template>
<template v-if="column.dataIndex === 'dataScope'"> <template v-if="column.dataIndex === 'dataScope'">
@@ -110,6 +122,7 @@
import roleApi from '@/api/sys/roleApi' import roleApi from '@/api/sys/roleApi'
import ScopeDefineOrg from './scopeDefineOrg.vue' import ScopeDefineOrg from './scopeDefineOrg.vue'
import { userStore } from '@/store/user' import { userStore } from '@/store/user'
import { cloneDeep } from 'lodash-es'
const visible = ref(false) const visible = ref(false)
const spinningLoading = ref(false) const spinningLoading = ref(false)
@@ -118,7 +131,7 @@
const submitLoading = ref(false) const submitLoading = ref(false)
const CustomValue = 'SCOPE_ORG_DEFINE' const CustomValue = 'SCOPE_ORG_DEFINE'
// 抽屉的宽度 // 抽屉的宽度
const drawerWidth = 1000 const drawerWidth = 1050
// 自动获取宽度默认获取浏览器的宽度的90% // 自动获取宽度默认获取浏览器的宽度的90%
//(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9 //(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9
const loadDatas = ref([]) const loadDatas = ref([])
@@ -129,13 +142,39 @@
searchedColumn: '' searchedColumn: ''
}) })
const searchInput = ref() const searchInput = ref()
// 分页
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
defaultPageSize: 10,
pageSizeOptions: ['10', '20', '50', '100']
})
const firstShowMap = ref({})
// 全选
const allChecked = ref(false)
const tableLoadData = ref([])
const columns = [ const columns = [
{ {
key: 'api', key: 'prefix',
title: '接口前缀',
dataIndex: 'prefix',
width: 140,
customCell: (row, index) => {
const indexArr = firstShowMap.value[row.prefix]
if (index === indexArr[0]) {
return { rowSpan: indexArr.length }
}
return { rowSpan: 0 }
}
},
{
key: 'suffix',
title: '接口', title: '接口',
dataIndex: 'api', dataIndex: 'suffix',
width: 380, width: 290,
customFilterDropdown: true, customFilterDropdown: true,
onFilter: (value, record) => record.api.includes(value), onFilter: (value, record) => record.api.includes(value),
onFilterDropdownOpenChange: (visible) => { onFilterDropdownOpenChange: (visible) => {
@@ -162,17 +201,29 @@
} }
const resOwn = await userApi.userOwnPermission(param) const resOwn = await userApi.userOwnPermission(param)
// 数据转换 // 数据转换
echoModuleData(res, resOwn) loadDatas.value = echoModuleData(res, resOwn)
pagination.value.total = loadDatas.value.length
allChecked.value = loadDatas.value.every((item) => item.parentCheck)
spinningLoading.value = false spinningLoading.value = false
} }
// table事件触发
const handleTableChange = (pageInfo) => {
Object.assign(pagination.value, pageInfo)
}
// 数据转换 // 数据转换
const echoModuleData = (res, resOwn) => { const echoModuleData = (res, resOwn) => {
let list = []
res.forEach((api) => { res.forEach((api) => {
const apiArr = splitByThirdSlash(api)
const obj = { const obj = {
api: api, api: api,
prefix: apiArr[0],
suffix: apiArr[1],
dataScope: datascope(api), dataScope: datascope(api),
check: false check: false,
parentCheck: false
} }
if (resOwn.grantInfoList.length > 0) { if (resOwn.grantInfoList.length > 0) {
resOwn.grantInfoList.forEach((item) => { resOwn.grantInfoList.forEach((item) => {
if (item.apiUrl === subStrApi(api)) { if (item.apiUrl === subStrApi(api)) {
@@ -190,8 +241,10 @@
} }
}) })
} }
loadDatas.value.push(obj) list.push(obj)
}) })
// 设置父节点check状态
return setParentDataCheckedStatus(list)
} }
const datascope = (id) => { const datascope = (id) => {
return [ return [
@@ -278,6 +331,11 @@
const onOpen = (record) => { const onOpen = (record) => {
grantPermissionParam.id = record.id grantPermissionParam.id = record.id
visible.value = true visible.value = true
firstShowMap.value = {}
pagination.value.current = 1
pagination.value.pageSize = 10
pagination.value.total = 0
allChecked.value = false
loadData() loadData()
} }
// 关闭抽屉 // 关闭抽屉
@@ -289,15 +347,41 @@
} }
// 全选 // 全选
const onCheckAllChange = (value) => { const onCheckAllChange = (value) => {
allChecked.value = value
spinningLoading.value = true spinningLoading.value = true
loadDatas.value.forEach((data) => { loadDatas.value.forEach((data) => {
changeApi(data, value) changeApi(data, value, false)
data.parentCheck = value
spinningLoading.value = false spinningLoading.value = false
}) })
} }
// 选中接口前缀
const changeParentApi = (record, val) => {
loadDatas.value.forEach((data) => {
if (data.prefix === record.prefix) {
data.check = val
data.parentCheck = val
if (val) {
let isChecked = data.dataScope.some((item) => item.check)
if (!isChecked) {
data.dataScope[0].check = true
}
} else {
data.dataScope.forEach((item) => {
item.check = false
})
}
data.dataScope.forEach((item) => {
if (item.value === 'SCOPE_ORG_DEFINE') {
item.scopeDefineOrgIdList = []
}
})
}
})
allChecked.value = loadDatas.value.every((item) => item.parentCheck)
}
// 选中接口 // 选中接口
const changeApi = (record, val) => { const changeApi = (record, val, isLoadData = true) => {
record.check = val record.check = val
if (val) { if (val) {
let checkStatus = 0 let checkStatus = 0
@@ -318,6 +402,7 @@
} }
}) })
} }
isLoadData && (loadDatas.value = setParentDataCheckedStatus(loadDatas.value))
} }
// 设置选中状态 // 设置选中状态
const changeChildCheckBox = (record, evt) => { const changeChildCheckBox = (record, evt) => {
@@ -383,8 +468,9 @@
state.searchedColumn = dataIndex state.searchedColumn = dataIndex
} }
// 标题接口列搜索重置 // 标题接口列搜索重置
const handleReset = (clearFilters) => { const handleReset = (clearFilters, confirm) => {
clearFilters() clearFilters()
confirm()
state.searchText = '' state.searchText = ''
} }
// 标题数据范围列radio-group事件 // 标题数据范围列radio-group事件
@@ -399,6 +485,42 @@
}) })
}) })
} }
// 设置父节点check状态
const setParentDataCheckedStatus = (records) => {
const cloneRecords = cloneDeep(records)
cloneRecords.forEach((item) => {
let childrenList = records.filter((f) => f.prefix === item.prefix)
item.parentCheck = childrenList.every((e) => e.check)
})
return cloneRecords
}
// 字符串分割
function splitByThirdSlash(str) {
const arr = str.split('/').filter(Boolean)
const leftPart = '/' + arr.slice(0, 2).join('/')
const rightPart = '/' + arr.slice(2).join('/')
return [leftPart, rightPart]
}
// 监听分页及数据变化
watch(
() => [pagination.value, loadDatas.value],
(val) => {
const start = (pagination.value.current - 1) * pagination.value.pageSize
const end = start + pagination.value.pageSize
const tableData = loadDatas.value.slice(start, end)
firstShowMap.value = {}
// 生成map
tableData?.forEach((item, index) => {
if (firstShowMap.value[item.prefix]) {
firstShowMap.value[item.prefix].push(index)
} else {
firstShowMap.value[item.prefix] = [index]
}
})
tableLoadData.value = tableData
},
{ deep: true }
)
// 调用这个函数将子组件的一些数据和方法暴露出去 // 调用这个函数将子组件的一些数据和方法暴露出去
defineExpose({ defineExpose({
onOpen onOpen

View File

@@ -105,8 +105,7 @@
// firstShowMap = {} // 重置单元格合并映射 // firstShowMap = {} // 重置单元格合并映射
// 如果有数据,我们再不去反复的查询 // 如果有数据,我们再不去反复的查询
if (echoDatalist.value.length > 0) { if (echoDatalist.value.length > 0) {
let data = echoDatalist.value.find((f) => f.id === moduleId.value).menu loadDatas.value = echoDatalist.value.find((f) => f.id === moduleId.value).menu
loadDatas.value = data
} else { } else {
// 获取表格数据 // 获取表格数据
spinningLoading.value = true spinningLoading.value = true
@@ -143,11 +142,11 @@
if (module.menu) { if (module.menu) {
// 加入回显内容 // 加入回显内容
module.menu.forEach((item) => { module.menu.forEach((item) => {
const menueCheck = ref(0) const menusCheck = ref(0)
if (resEcho.grantInfoList.length > 0) { if (resEcho.grantInfoList.length > 0) {
resEcho.grantInfoList.forEach((grant) => { resEcho.grantInfoList.forEach((grant) => {
if (item.id === grant.menuId) { if (item.id === grant.menuId) {
menueCheck.value++ menusCheck.value++
// 处理按钮 // 处理按钮
if (grant.buttonInfo.length > 0) { if (grant.buttonInfo.length > 0) {
grant.buttonInfo.forEach((button) => { grant.buttonInfo.forEach((button) => {
@@ -162,15 +161,23 @@
}) })
} }
// 回显前面的2个 // 回显前面的2个
if (menueCheck.value > 0) { if (menusCheck.value > 0) {
item.parentCheck = true item.parentCheck = true
item.nameCheck = true item.nameCheck = true
} }
}) })
// 排序 // 排序
module.menu = module.menu.sort((a, b) => { module.menu.sort((a, b) => {
return a.parentId - b.parentId // 首先比较parentName属性
let nameComparison = b.parentName.localeCompare(a.parentName)
if (nameComparison !== 0) {
// 如果parentName不同直接返回parentName的比较结果
return nameComparison
} else {
// 如果name相同则比较parentId属性直接返回parentId的差值
return Number(a.parentId) - Number(b.parentId)
}
}) })
// 缓存加入索引 // 缓存加入索引
module.menu.forEach((item, index) => { module.menu.forEach((item, index) => {
@@ -199,12 +206,11 @@
}) })
} }
const checkAllChildNotChecked = (record) => { const checkAllChildNotChecked = (record) => {
const allChecked = checkFieldKeys.every((key) => { return checkFieldKeys.every((key) => {
// 遍历所有的字段 // 遍历所有的字段
const child = record[key] const child = record[key]
return child.every((field) => !field.check) return child.every((field) => !field.check)
}) })
return allChecked
} }
const changeChildCheckBox = (record, evt) => { const changeChildCheckBox = (record, evt) => {
let checked = evt.target.checked let checked = evt.target.checked
@@ -305,7 +311,7 @@
<style scoped> <style scoped>
/* 重写复选框的样式 */ /* 重写复选框的样式 */
.ant-checkbox-wrapper { .ant-checkbox-wrapper {
margin-left: 0px !important; margin-left: 0 !important;
padding-top: 2px !important; padding-top: 2px !important;
padding-bottom: 2px !important; padding-bottom: 2px !important;
} }

View File

@@ -15,6 +15,7 @@
:tree-data="treeData" :tree-data="treeData"
:field-names="treeFieldNames" :field-names="treeFieldNames"
checkable checkable
check-strictly
:selectable="false" :selectable="false"
@check="treeCheck" @check="treeCheck"
> >
@@ -25,6 +26,7 @@
<script setup="props, context" name="userScopeDefineOrg"> <script setup="props, context" name="userScopeDefineOrg">
import userApi from '@/api/sys/userApi' import userApi from '@/api/sys/userApi'
import { checkOrUnCheckChildren } from '@/utils/treeHandler'
const visible = ref(false) const visible = ref(false)
let defaultExpandedKeys = ref([]) let defaultExpandedKeys = ref([])
let checkedKeys = ref([]) let checkedKeys = ref([])
@@ -83,8 +85,8 @@
const treeFieldNames = { children: 'children', title: 'name', key: 'id' } const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
// 选中触发 // 选中触发
const treeCheck = (checkedKeys) => { const treeCheck = (checkedKeys, { checked, node }) => {
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkedKeys resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkOrUnCheckChildren(checked, node, checkedKeys)
} }
// 定义emit事件 // 定义emit事件
const emit = defineEmits({ const emit = defineEmits({

View File

@@ -421,7 +421,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
queryWrapper.lambda().eq(BizUser::getUserStatus, bizUserExportParam.getUserStatus()); queryWrapper.lambda().eq(BizUser::getUserStatus, bizUserExportParam.getUserStatus());
} }
} }
String fileName = "SNOWY2.0系统B端人员信息清单.xlsx"; String fileName = "SNOWY系统B端人员信息清单.xlsx";
List<BizUser> bizUserList = this.list(queryWrapper); List<BizUser> bizUserList = this.list(queryWrapper);
if(ObjectUtil.isEmpty(bizUserList)) { if(ObjectUtil.isEmpty(bizUserList)) {
throw new CommonException("无数据可导出"); throw new CommonException("无数据可导出");
@@ -575,7 +575,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
// 生成doc // 生成doc
XWPFDocument doc = WordExportUtil.exportWord07(destTemplateFile.getAbsolutePath(), map); XWPFDocument doc = WordExportUtil.exportWord07(destTemplateFile.getAbsolutePath(), map);
// 生成临时导出文件 // 生成临时导出文件
resultFile = FileUtil.file(FileUtil.getTmpDir() + File.separator + "SNOWY2.0系统B端人员信息_" + bizUser.getName() + ".docx"); resultFile = FileUtil.file(FileUtil.getTmpDir() + File.separator + "SNOWY系统B端人员信息_" + bizUser.getName() + ".docx");
// 写入 // 写入
BufferedOutputStream outputStream = FileUtil.getOutputStream(resultFile); BufferedOutputStream outputStream = FileUtil.getOutputStream(resultFile);
doc.write(outputStream); doc.write(outputStream);

View File

@@ -996,7 +996,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
try { try {
InputStream inputStream = POICacheManager.getFile("userImportTemplate.xlsx"); InputStream inputStream = POICacheManager.getFile("userImportTemplate.xlsx");
byte[] bytes = IoUtil.readBytes(inputStream); byte[] bytes = IoUtil.readBytes(inputStream);
CommonDownloadUtil.download("SNOWY2.0系统B端用户导入模板.xlsx", bytes, response); CommonDownloadUtil.download("SNOWY系统B端用户导入模板.xlsx", bytes, response);
} catch (Exception e) { } catch (Exception e) {
log.error(">>> 下载用户导入模板失败:", e); log.error(">>> 下载用户导入模板失败:", e);
CommonResponseUtil.renderError(response, "下载用户导入模板失败"); CommonResponseUtil.renderError(response, "下载用户导入模板失败");
@@ -1181,7 +1181,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
queryWrapper.lambda().eq(SysUser::getUserStatus, sysUserExportParam.getUserStatus()); queryWrapper.lambda().eq(SysUser::getUserStatus, sysUserExportParam.getUserStatus());
} }
} }
String fileName = "SNOWY2.0系统B端用户信息清单.xlsx"; String fileName = "SNOWY系统B端用户信息清单.xlsx";
List<SysUser> sysUserList = this.list(queryWrapper); List<SysUser> sysUserList = this.list(queryWrapper);
if(ObjectUtil.isEmpty(sysUserList)) { if(ObjectUtil.isEmpty(sysUserList)) {
throw new CommonException("无数据可导出"); throw new CommonException("无数据可导出");
@@ -1335,7 +1335,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
// 生成doc // 生成doc
XWPFDocument doc = WordExportUtil.exportWord07(destTemplateFile.getAbsolutePath(), map); XWPFDocument doc = WordExportUtil.exportWord07(destTemplateFile.getAbsolutePath(), map);
// 生成临时导出文件 // 生成临时导出文件
resultFile = FileUtil.file(FileUtil.getTmpDir() + File.separator + "SNOWY2.0系统B端用户信息_" + sysUser.getName() + ".docx"); resultFile = FileUtil.file(FileUtil.getTmpDir() + File.separator + "SNOWY系统B端用户信息_" + sysUser.getName() + ".docx");
// 写入 // 写入
BufferedOutputStream outputStream = FileUtil.getOutputStream(resultFile); BufferedOutputStream outputStream = FileUtil.getOutputStream(resultFile);
doc.write(outputStream); doc.write(outputStream);

View File

@@ -19,12 +19,11 @@ import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.*;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.ContentType; import cn.hutool.http.ContentType;
import cn.hutool.http.Header; import cn.hutool.http.Header;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
@@ -35,13 +34,14 @@ import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerIntercept
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.ibatis.mapping.DatabaseIdProvider; import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectionException; import org.apache.ibatis.reflection.ReflectionException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
@@ -54,12 +54,8 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.jdbc.support.JdbcUtils; import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import vip.xiaonuo.auth.core.util.StpClientUtil; import vip.xiaonuo.auth.core.util.StpClientUtil;
@@ -72,6 +68,9 @@ import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
import vip.xiaonuo.common.listener.CommonDataChangeListener; import vip.xiaonuo.common.listener.CommonDataChangeListener;
import vip.xiaonuo.common.pojo.CommonResult; import vip.xiaonuo.common.pojo.CommonResult;
import vip.xiaonuo.common.pojo.CommonWrapperInterface; import vip.xiaonuo.common.pojo.CommonWrapperInterface;
import vip.xiaonuo.common.util.CommonIpAddressUtil;
import vip.xiaonuo.common.util.CommonJoinPointUtil;
import vip.xiaonuo.common.util.CommonServletUtil;
import vip.xiaonuo.common.util.CommonTimeFormatUtil; import vip.xiaonuo.common.util.CommonTimeFormatUtil;
import vip.xiaonuo.core.handler.GlobalExceptionUtil; import vip.xiaonuo.core.handler.GlobalExceptionUtil;
import vip.xiaonuo.sys.core.enums.SysBuildInEnum; import vip.xiaonuo.sys.core.enums.SysBuildInEnum;
@@ -98,9 +97,6 @@ public class GlobalConfigure implements WebMvcConfigurer {
private static final String COMMON_REPEAT_SUBMIT_CACHE_KEY = "common-repeatSubmit:"; private static final String COMMON_REPEAT_SUBMIT_CACHE_KEY = "common-repeatSubmit:";
@Resource
private CommonCacheOperator commonCacheOperator;
/** /**
* 无需登录的接口地址集合 * 无需登录的接口地址集合
*/ */
@@ -283,6 +279,7 @@ public class GlobalConfigure implements WebMvcConfigurer {
* @author xuyuxiang * @author xuyuxiang
* @date 2022/6/21 17:01 * @date 2022/6/21 17:01
**/ **/
@SuppressWarnings("ALL")
@Primary @Primary
@Bean @Bean
public RedisTemplate<String, Object> redisTemplate(@Autowired(required = false) RedisConnectionFactory redisConnectionFactory) { public RedisTemplate<String, Object> redisTemplate(@Autowired(required = false) RedisConnectionFactory redisConnectionFactory) {
@@ -311,41 +308,52 @@ public class GlobalConfigure implements WebMvcConfigurer {
} }
/** /**
* 添加节流防抖拦截器 * 节流防抖的AOP
* *
* @author xuyuxiang * @author xuyuxiang
* @date 2022/6/20 15:18 * @date 2022/9/15 21:24
**/ */
@Override @Component
public void addInterceptors(InterceptorRegistry registry) { @Aspect
registry.addInterceptor(new HandlerInterceptor() { public static class CommonNoRepeatAop {
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, /**
@NonNull Object handler) throws Exception { * 切入点
if (handler instanceof HandlerMethod) { *
HandlerMethod handlerMethod = (HandlerMethod) handler; * @author xuyuxiang
Method method = handlerMethod.getMethod(); * @date 2022/9/15 21:27
CommonNoRepeat annotation = method.getAnnotation(CommonNoRepeat.class); */
if (ObjectUtil.isNotEmpty(annotation)) { @Pointcut("@annotation(vip.xiaonuo.common.annotation.CommonNoRepeat)")
JSONObject repeatSubmitJsonObject = this.isRepeatSubmit(request, annotation); private void noRepeatPointcut() {
if (repeatSubmitJsonObject.getBool("repeat")) {
response.setCharacterEncoding(CharsetUtil.UTF_8);
response.setContentType(ContentType.JSON.toString());
response.getWriter().write(JSONUtil.toJsonStr(CommonResult.error("请求过于频繁,请" + repeatSubmitJsonObject.getStr("time") + "后再试")));
return false;
}
}
}
return true;
} }
public JSONObject isRepeatSubmit(HttpServletRequest request, CommonNoRepeat annotation) { /**
JSONObject jsonObject = JSONUtil.createObj(); * 执行校验
jsonObject.set("repeatParam", JSONUtil.toJsonStr(request.getParameterMap())); *
jsonObject.set("repeatTime", DateUtil.current()); * @author xuyuxiang
* @date 2022/9/15 21:27
*/
@Before("noRepeatPointcut()")
public void doBefore(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
CommonNoRepeat commonNoRepeat = method.getAnnotation(CommonNoRepeat.class);
HttpServletRequest request = CommonServletUtil.getRequest();
String url = request.getRequestURI(); String url = request.getRequestURI();
// 获取该接口缓存的限流数据 CommonCacheOperator commonCacheOperator = SpringUtil.getBean(CommonCacheOperator.class);
Object cacheObj = commonCacheOperator.get(COMMON_REPEAT_SUBMIT_CACHE_KEY + url); JSONObject jsonObject = JSONUtil.createObj();
jsonObject.set("repeatParam", CommonJoinPointUtil.getArgsJsonString(joinPoint));
jsonObject.set("repeatTime", DateUtil.current());
// 获取该接口缓存的限流数据跟当前ip以及登录用户有关
String cacheKey = COMMON_REPEAT_SUBMIT_CACHE_KEY + CommonIpAddressUtil.getIp(request) + StrUtil.COLON;
Object loginId = StpUtil.getLoginIdDefaultNull();
if(ObjectUtil.isNotEmpty(loginId)) {
cacheKey = cacheKey + Convert.toStr(loginId) + StrUtil.COLON + url;
} else {
cacheKey = cacheKey + url;
}
Object cacheObj = commonCacheOperator.get(cacheKey);
if (ObjectUtil.isNotEmpty(cacheObj)) { if (ObjectUtil.isNotEmpty(cacheObj)) {
JSONObject cacheJsonObject = JSONUtil.parseObj(cacheObj); JSONObject cacheJsonObject = JSONUtil.parseObj(cacheObj);
if(cacheJsonObject.containsKey(url)) { if(cacheJsonObject.containsKey(url)) {
@@ -353,22 +361,18 @@ public class GlobalConfigure implements WebMvcConfigurer {
// 如果与上次参数一致,且时间间隔小于要求的限流时长,则判定为重复提交 // 如果与上次参数一致,且时间间隔小于要求的限流时长,则判定为重复提交
if (jsonObject.getStr("repeatParam").equals(existRepeatJsonObject.getStr("repeatParam"))) { if (jsonObject.getStr("repeatParam").equals(existRepeatJsonObject.getStr("repeatParam"))) {
long interval = jsonObject.getLong("repeatTime") - existRepeatJsonObject.getLong("repeatTime"); long interval = jsonObject.getLong("repeatTime") - existRepeatJsonObject.getLong("repeatTime");
if(interval < annotation.interval()) { if(interval < commonNoRepeat.interval()) {
long secondsParam = (annotation.interval() - interval) / 1000; long secondsParam = (commonNoRepeat.interval() - interval) / 1000;
if(secondsParam == 0) { if(secondsParam > 0) {
return JSONUtil.createObj().set("repeat", false); throw new CommonException("请求过于频繁,请" + CommonTimeFormatUtil.formatSeconds(secondsParam) + "后再试");
} else {
return JSONUtil.createObj().set("repeat", true).set("time", CommonTimeFormatUtil.formatSeconds(secondsParam));
} }
} }
} }
} }
} }
// 缓存最新的该接口的限流数据为防止缓存的数据过多缓存时效为1小时 // 缓存最新的该接口的限流数据,跟当前ip以及登录用户有关为防止缓存的数据过多缓存时效为1小时
commonCacheOperator.put(COMMON_REPEAT_SUBMIT_CACHE_KEY + url, JSONUtil.createObj().set(url, jsonObject), 60 * 60); commonCacheOperator.put(cacheKey, JSONUtil.createObj().set(url, jsonObject), 60 * 60);
return JSONUtil.createObj().set("repeat", false);
} }
}).addPathPatterns("/**");
} }
/** /**
@@ -411,7 +415,7 @@ public class GlobalConfigure implements WebMvcConfigurer {
* @author xuyuxiang * @author xuyuxiang
* @date 2022/9/15 21:27 * @date 2022/9/15 21:27
*/ */
@SuppressWarnings("all") @SuppressWarnings("ALL")
private Object processWrapping(ProceedingJoinPoint proceedingJoinPoint, Object originResult) throws IllegalAccessException, InstantiationException { private Object processWrapping(ProceedingJoinPoint proceedingJoinPoint, Object originResult) throws IllegalAccessException, InstantiationException {
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = methodSignature.getMethod(); Method method = methodSignature.getMethod();
@@ -462,7 +466,7 @@ public class GlobalConfigure implements WebMvcConfigurer {
* @author xuyuxiang * @author xuyuxiang
* @date 2022/9/15 21:36 * @date 2022/9/15 21:36
*/ */
@SuppressWarnings("all") @SuppressWarnings("ALL")
private JSONObject wrapPureObject(Object originModel, Class<? extends CommonWrapperInterface<?>>[] baseWrapperClasses) { private JSONObject wrapPureObject(Object originModel, Class<? extends CommonWrapperInterface<?>>[] baseWrapperClasses) {
JSONObject jsonObject = JSONUtil.parseObj(originModel); JSONObject jsonObject = JSONUtil.parseObj(originModel);
try { try {