mirror of https://gitee.com/xiaonuobase/snowy
【升级】v3.2版本升级
parent
8f7424b5e7
commit
4a6eeb0c0f
|
@ -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)
|
||||
|
||||
gitcode下载地址:[https://gitcode.com/xiaonuobase/Snowy](https://gitcode.com/xiaonuobase/Snowy)
|
||||
|
||||
演示地址:[https://snowy.xiaonuo.vip](https://snowy.xiaonuo.vip)
|
||||
|
||||
文档地址:[https://xiaonuo.vip/doc](https://xiaonuo.vip/doc)
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
# 本地环境
|
||||
NODE_ENV = development
|
||||
|
||||
# 标题
|
||||
VITE_TITLE = Snowy
|
||||
|
||||
# 接口地址
|
||||
VITE_API_BASEURL = http://127.0.0.1:82
|
||||
|
||||
|
@ -12,3 +6,6 @@ VITE_PORT = 81
|
|||
|
||||
# 开启设置抽屉
|
||||
VITE_SET_DRAWER = true
|
||||
|
||||
# 本地环境
|
||||
NODE_ENV = development
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
# 生产环境
|
||||
NODE_ENV = production
|
||||
|
||||
# 标题
|
||||
VITE_TITLE = Snowy
|
||||
|
||||
# 接口地址
|
||||
VITE_API_BASEURL = http://127.0.0.1:82
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
<title>Snowy</title>
|
||||
<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}}
|
||||
.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__logo {margin-bottom: 30px;}
|
||||
.app-loading__logo img {width: 90px;vertical-align: bottom;}
|
||||
.app-loading__title {font-size: 24px;color: #333;margin-top: 30px;}
|
||||
.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 img {width: 90px;vertical-align: bottom;}
|
||||
.app-loading-title {font-size: 24px;color: #333;margin-top: 30px;}
|
||||
.app-main { height: 100% }
|
||||
@keyframes loader {
|
||||
0% {
|
||||
|
@ -26,7 +26,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<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>
|
||||
</noscript>
|
||||
<div id="app" class="app-main"></div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card title="站内信" :bordered="false">
|
||||
<a-card :title="title" :bordered="false" :loading="miniMessageLoading">
|
||||
<template #extra><a @click="leaveFor('/usercenter')">更多</a></template>
|
||||
<div class="index-message-list">
|
||||
<a-list :data-source="messageList" size="small" :loading="miniMessageLoading">
|
||||
|
@ -54,9 +54,7 @@
|
|||
import router from '@/router'
|
||||
const miniMessageLoading = ref(false)
|
||||
const messageList = ref([])
|
||||
const miniMessageBodyStyle = ref({
|
||||
'padding-top': '10px'
|
||||
})
|
||||
const title = ref('站内信')
|
||||
onMounted(() => {
|
||||
// 进来后执行查询
|
||||
getMessageList()
|
||||
|
@ -69,6 +67,7 @@
|
|||
.then((data) => {
|
||||
messageList.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
miniMessageLoading.value = false
|
||||
})
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<template>
|
||||
<xn-form-container
|
||||
title="详情"
|
||||
:width="1000"
|
||||
v-model:open="open"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<xn-form-container title="详情" :width="1000" v-model:open="open" :destroy-on-close="true" @close="onClose">
|
||||
<a-descriptions bordered>
|
||||
<a-descriptions-item label="标题">{{formData.title}}</a-descriptions-item>
|
||||
<a-descriptions-item label="类型">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card :bordered="false" :title="title">
|
||||
<a-card :bordered="false" :title="title" :loading="apiLoading">
|
||||
<template #extra><a @click="leaveFor('/biz/notice')">更多</a></template>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
|
@ -63,8 +63,18 @@
|
|||
]
|
||||
const title = ref('通知公告')
|
||||
const dataSource = ref([])
|
||||
bizIndexApi.bizIndexNoticeList().then((data) => {
|
||||
dataSource.value = data
|
||||
const apiLoading = ref(false)
|
||||
onMounted(() => {
|
||||
apiLoading.value = true
|
||||
bizIndexApi
|
||||
.bizIndexNoticeList()
|
||||
.then((data) => {
|
||||
dataSource.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
})
|
||||
const leaveFor = (url = '/') => {
|
||||
router.replace({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<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-card :bordered="false">
|
||||
<a-timeline>
|
||||
|
@ -45,7 +45,7 @@
|
|||
const title = ref('我的日程')
|
||||
const scheduleList = ref([])
|
||||
const calendarValue = ref(dayjs())
|
||||
|
||||
const apiLoading = ref(false)
|
||||
onMounted(() => {
|
||||
// 进来后执行查询
|
||||
seleScheduleList()
|
||||
|
@ -55,9 +55,16 @@
|
|||
const param = {
|
||||
scheduleDate: calendarValue.value.format('YYYY-MM-DD')
|
||||
}
|
||||
indexApi.indexScheduleList(param).then((data) => {
|
||||
scheduleList.value = data
|
||||
})
|
||||
apiLoading.value = true
|
||||
indexApi
|
||||
.indexScheduleList(param)
|
||||
.then((data) => {
|
||||
scheduleList.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 点击某一天
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card :title="title" :bordered="false">
|
||||
<a-card :title="title" :bordered="false" :loading="apiLoading">
|
||||
<div class="card-div">
|
||||
<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">
|
||||
|
@ -21,16 +21,24 @@
|
|||
import { onMounted } from 'vue'
|
||||
const shortcutList = ref([])
|
||||
const title = ref('快捷方式')
|
||||
const apiLoading = ref(false)
|
||||
onMounted(() => {
|
||||
// 进来后执行查询
|
||||
getUserLoginWorkbench()
|
||||
})
|
||||
const getUserLoginWorkbench = () => {
|
||||
userCenterApi.userLoginWorkbench().then((data) => {
|
||||
if (data) {
|
||||
shortcutList.value = JSON.parse(data).shortcut
|
||||
}
|
||||
})
|
||||
apiLoading.value = true
|
||||
userCenterApi
|
||||
.userLoginWorkbench()
|
||||
.then((data) => {
|
||||
if (data) {
|
||||
shortcutList.value = JSON.parse(data).shortcut
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
}
|
||||
const leaveFor = (url = '/') => {
|
||||
router.replace({
|
||||
|
|
|
@ -9,19 +9,23 @@
|
|||
<template #nextArrow>
|
||||
<div class="custom-slick-arrow" style="right: 10px"><RightOutlined /></div>
|
||||
</template>
|
||||
<img
|
||||
v-for="item in slideshowList"
|
||||
:key="item.id"
|
||||
:src="item.image"
|
||||
class="carousel-images"
|
||||
@click="leaveForOpen(item.pathDetails)"
|
||||
/>
|
||||
<div v-if="!isEmpty(slideshowList)">
|
||||
<img
|
||||
v-for="item in slideshowList"
|
||||
:key="item.id"
|
||||
:src="item.image"
|
||||
class="carousel-images"
|
||||
@click="leaveForOpen(item.pathDetails)"
|
||||
/>
|
||||
</div>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</a-carousel>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup name="carousel">
|
||||
import bizIndexApi from '@/api/biz/bizIndexApi'
|
||||
import { Empty } from 'ant-design-vue'
|
||||
import { isEmpty, cloneDeep } from 'lodash-es'
|
||||
import router from '@/router'
|
||||
const slideshowList = ref([])
|
||||
|
@ -40,9 +44,12 @@
|
|||
// 这是字典内维护的该位置
|
||||
place: props.config.options.place ? props.config.options.place : 'BACK_SYS_INDEX'
|
||||
}
|
||||
bizIndexApi.bizIndexSlideshowList(param).then((data) => {
|
||||
slideshowList.value = data
|
||||
})
|
||||
bizIndexApi
|
||||
.bizIndexSlideshowList(param)
|
||||
.then((data) => {
|
||||
slideshowList.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
})
|
||||
// URL跟路由的跳转
|
||||
const leaveForOpen = (value) => {
|
||||
|
@ -81,7 +88,7 @@
|
|||
text-align: center;
|
||||
height: 180px;
|
||||
line-height: 150px;
|
||||
background: #364d79;
|
||||
background: #1890ff;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ant-carousel :deep(.slick-arrow.custom-slick-arrow) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card :title="title" :bordered="false">
|
||||
<a-card :title="title" :bordered="false" :loading="apiLoading">
|
||||
<a-row>
|
||||
<a-col :span="6">
|
||||
<a-statistic :value="dataSource.userCount">
|
||||
|
@ -40,6 +40,7 @@
|
|||
<script setup name="sysBizDataCard">
|
||||
import indexApi from '@/api/sys/indexApi'
|
||||
const title = ref('业务数据')
|
||||
const apiLoading = ref(false)
|
||||
const dataSource = ref({
|
||||
userCount: 0,
|
||||
roleCount: 0,
|
||||
|
@ -47,9 +48,16 @@
|
|||
positionCount: 0
|
||||
})
|
||||
onMounted(() => {
|
||||
indexApi.indexBizDataCount().then((data) => {
|
||||
dataSource.value = data
|
||||
})
|
||||
apiLoading.value = true
|
||||
indexApi
|
||||
.indexBizDataCount()
|
||||
.then((data) => {
|
||||
dataSource.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card :title="title" :bordered="false">
|
||||
<a-card :title="title" :bordered="false" :loading="apiLoading">
|
||||
<a-row>
|
||||
<a-col :span="4">
|
||||
<a-statistic :value="dataSource.jobCount">
|
||||
|
@ -56,6 +56,7 @@
|
|||
<script setup name="sysBizDataCard">
|
||||
import indexApi from '@/api/sys/indexApi'
|
||||
const title = ref('运维一览')
|
||||
const apiLoading = ref(false)
|
||||
const dataSource = ref({
|
||||
jobCount: 0,
|
||||
sysDictCount: 0,
|
||||
|
@ -65,9 +66,16 @@
|
|||
thirdUserCount: 0
|
||||
})
|
||||
onMounted(() => {
|
||||
indexApi.indexOpDataCount().then((data) => {
|
||||
dataSource.value = data
|
||||
})
|
||||
apiLoading.value = true
|
||||
indexApi
|
||||
.indexOpDataCount()
|
||||
.then((data) => {
|
||||
dataSource.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<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>
|
||||
<div class="timeline-div">
|
||||
<a-timeline>
|
||||
|
@ -18,6 +18,8 @@
|
|||
import tool from '@/utils/tool'
|
||||
const userInfo = tool.data.get('USER_INFO')
|
||||
const opLogList = ref([])
|
||||
const title = ref('操作记录')
|
||||
const apiLoading = ref(false)
|
||||
onMounted(() => {
|
||||
// 进来后执行查询
|
||||
getOpLogList()
|
||||
|
@ -28,9 +30,16 @@
|
|||
return userInfo.roleCodeList && userInfo.roleCodeList.toString().indexOf('superAdmin') !== -1
|
||||
}
|
||||
const getOpLogList = () => {
|
||||
indexApi.indexOpLogList().then((data) => {
|
||||
opLogList.value = data
|
||||
})
|
||||
apiLoading.value = true
|
||||
indexApi
|
||||
.indexOpLogList()
|
||||
.then((data) => {
|
||||
opLogList.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
}
|
||||
const leaveFor = (url = '/') => {
|
||||
router.replace({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card :title="title" :bordered="false">
|
||||
<a-card :title="title" :bordered="false" :loading="apiLoading">
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-statistic :value="dataSource.fileCount">
|
||||
|
@ -40,6 +40,7 @@
|
|||
<script setup name="sysToolDataCard">
|
||||
import indexApi from '@/api/sys/indexApi'
|
||||
const title = ref('基础工具')
|
||||
const apiLoading = ref(false)
|
||||
const dataSource = ref({
|
||||
fileCount: 0,
|
||||
smsCount: 0,
|
||||
|
@ -47,9 +48,16 @@
|
|||
messageCount: 0
|
||||
})
|
||||
onMounted(() => {
|
||||
indexApi.indexToolDataCount().then((data) => {
|
||||
dataSource.value = data
|
||||
})
|
||||
apiLoading.value = true
|
||||
indexApi
|
||||
.indexToolDataCount()
|
||||
.then((data) => {
|
||||
dataSource.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card :bordered="false" title="周访问量">
|
||||
<a-card :bordered="false" :title="title">
|
||||
<div id="visLogChartLine" class="xn-ht200"></div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
@ -11,6 +11,7 @@
|
|||
|
||||
const seriesKey = 'series'
|
||||
const valueKey = 'value'
|
||||
const title = ref('周访问量')
|
||||
const processData = (data, yFields, meta) => {
|
||||
const result = []
|
||||
data.forEach((d) => {
|
||||
|
@ -34,18 +35,21 @@
|
|||
alias: '登出'
|
||||
}
|
||||
}
|
||||
logApi.logVisLineChartData().then((data) => {
|
||||
const line = new Line('visLogChartLine', {
|
||||
data: processData(data, ['loginCount', 'logoutCount'], lineMeta),
|
||||
padding: 'auto',
|
||||
xField: 'date',
|
||||
yField: valueKey,
|
||||
seriesField: seriesKey,
|
||||
color: ['#1677FF', 'rgb(188, 189, 190)'],
|
||||
appendPadding: [0, 8, 0, 0]
|
||||
logApi
|
||||
.logVisLineChartData()
|
||||
.then((data) => {
|
||||
const line = new Line('visLogChartLine', {
|
||||
data: processData(data, ['loginCount', 'logoutCount'], lineMeta),
|
||||
padding: 'auto',
|
||||
xField: 'date',
|
||||
yField: valueKey,
|
||||
seriesField: seriesKey,
|
||||
color: ['#1677FF', 'rgb(188, 189, 190)'],
|
||||
appendPadding: [0, 8, 0, 0]
|
||||
})
|
||||
line.render()
|
||||
})
|
||||
line.render()
|
||||
})
|
||||
.catch(() => {})
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<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>
|
||||
<div class="timeline-div">
|
||||
<a-timeline>
|
||||
|
@ -19,6 +19,8 @@
|
|||
import tool from '@/utils/tool'
|
||||
const userInfo = tool.data.get('USER_INFO')
|
||||
const visLogList = ref([])
|
||||
const title = ref('访问记录')
|
||||
const apiLoading = ref(false)
|
||||
onMounted(() => {
|
||||
// 进来后执行查询
|
||||
getVisLogList()
|
||||
|
@ -29,9 +31,16 @@
|
|||
}
|
||||
// 查询数据
|
||||
const getVisLogList = () => {
|
||||
indexApi.indexVisLogList().then((data) => {
|
||||
visLogList.value = data
|
||||
})
|
||||
apiLoading.value = true
|
||||
indexApi
|
||||
.indexVisLogList()
|
||||
.then((data) => {
|
||||
visLogList.value = data
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
apiLoading.value = false
|
||||
})
|
||||
}
|
||||
// 跳转
|
||||
const leaveFor = (url = '/') => {
|
||||
|
@ -55,7 +64,7 @@
|
|||
padding-bottom: 10px !important;
|
||||
}
|
||||
.timeline-item-p {
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0;
|
||||
color: rgb(188, 189, 190);
|
||||
}
|
||||
.timeline-div {
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
</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>
|
||||
<div>
|
||||
<span className="mr-3">
|
||||
|
@ -98,7 +98,7 @@
|
|||
@change="loadData"
|
||||
@expand="
|
||||
(expanded, record) => {
|
||||
emit('expand', expanded, record)
|
||||
emit('onExpand', expanded, record)
|
||||
}
|
||||
"
|
||||
:rowClassName="
|
||||
|
@ -124,7 +124,7 @@
|
|||
import { get } from 'lodash-es'
|
||||
const slots = useSlots()
|
||||
const route = useRoute()
|
||||
const emit = defineEmits(['expand'])
|
||||
const emit = defineEmits(['onExpand'])
|
||||
const renderSlots = Object.keys(slots)
|
||||
|
||||
const props = defineProps(
|
||||
|
@ -367,56 +367,57 @@
|
|||
// 用请求数据请求该列表的返回数据
|
||||
const result = props.data(parameter)
|
||||
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
|
||||
result.then((r) => {
|
||||
if (r == null) {
|
||||
data.localLoading = false
|
||||
return
|
||||
}
|
||||
// 获取分页数据及分页的显示内容
|
||||
data.localPagination =
|
||||
(props.showPagination &&
|
||||
Object.assign({}, data.localPagination, {
|
||||
current: r.current, // pageNo, // 返回结果中的当前分页数
|
||||
total: r.total, // totalRows, // 返回结果中的总记录数
|
||||
showSizeChanger: props.showSizeChanger,
|
||||
pageSizeOptions: props.pageSizeOptions,
|
||||
showTotal: (total, range) => {
|
||||
return `${range[0]}-${range[1]} 共 ${total} 条 `
|
||||
},
|
||||
pageSize: (pagination && pagination.pageSize) || data.localPagination.pageSize
|
||||
})) ||
|
||||
false
|
||||
// 后端数据records为null保存修复
|
||||
if (r.records == null) {
|
||||
r.records = []
|
||||
}
|
||||
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
|
||||
if (r.records.length === 0 && props.showPagination && data.localPagination.current > 1) {
|
||||
data.localPagination.current--
|
||||
loadData()
|
||||
return
|
||||
}
|
||||
try {
|
||||
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
|
||||
// 没有数据或只有一页数据时隐藏分页栏
|
||||
// if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.pageSize))) {
|
||||
// data.localPagination.hideOnSinglePage = true
|
||||
// }
|
||||
if (!props.showPagination) {
|
||||
data.localPagination.hideOnSinglePage = true
|
||||
result
|
||||
.then((r) => {
|
||||
if (r == null) {
|
||||
data.localLoading = false
|
||||
return
|
||||
}
|
||||
// 获取分页数据及分页的显示内容
|
||||
data.localPagination =
|
||||
(props.showPagination &&
|
||||
Object.assign({}, data.localPagination, {
|
||||
current: r.current, // pageNo, // 返回结果中的当前分页数
|
||||
total: r.total, // totalRows, // 返回结果中的总记录数
|
||||
showSizeChanger: props.showSizeChanger,
|
||||
pageSizeOptions: props.pageSizeOptions,
|
||||
showTotal: (total, range) => {
|
||||
return `${range[0]}-${range[1]} 共 ${total} 条 `
|
||||
},
|
||||
pageSize: (pagination && pagination.pageSize) || data.localPagination.pageSize
|
||||
})) ||
|
||||
false
|
||||
// 后端数据records为null保存修复
|
||||
if (r.records == null) {
|
||||
r.records = []
|
||||
}
|
||||
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
|
||||
if (r.records.length === 0 && props.showPagination && data.localPagination.current > 1) {
|
||||
data.localPagination.current--
|
||||
loadData()
|
||||
return
|
||||
}
|
||||
try {
|
||||
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
|
||||
// 没有数据或只有一页数据时隐藏分页栏
|
||||
// if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.pageSize))) {
|
||||
// data.localPagination.hideOnSinglePage = true
|
||||
// }
|
||||
if (!props.showPagination) {
|
||||
data.localPagination.hideOnSinglePage = true
|
||||
}
|
||||
} catch (e) {
|
||||
data.localPagination = false
|
||||
}
|
||||
} catch (e) {
|
||||
data.localPagination = false
|
||||
}
|
||||
|
||||
// 返回结果中的数组数据
|
||||
if (props.showPagination === false) {
|
||||
data.localDataSource = r instanceof Array ? r : r.records
|
||||
} else {
|
||||
data.localDataSource = r.records
|
||||
}
|
||||
getTableProps()
|
||||
})
|
||||
// 返回结果中的数组数据
|
||||
if (props.showPagination === false) {
|
||||
data.localDataSource = r instanceof Array ? r : r.records
|
||||
} else {
|
||||
data.localDataSource = r.records
|
||||
}
|
||||
getTableProps()
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
data.localLoading = false
|
||||
|
@ -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>
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
const emit = defineEmits({ batchCallBack: null })
|
||||
const buttonLoading = ref(false)
|
||||
const props = defineProps({
|
||||
idKey: {
|
||||
type: String,
|
||||
default: () => 'id'
|
||||
},
|
||||
buttonName: {
|
||||
type: String,
|
||||
default: () => '批量操作'
|
||||
|
@ -62,7 +66,7 @@
|
|||
const deleteBatch = () => {
|
||||
const params = props.selectedRowKeys.map((m) => {
|
||||
return {
|
||||
id: m
|
||||
[props.idKey]: m
|
||||
}
|
||||
})
|
||||
// 发起方法调用,谁的谁来实现
|
||||
|
|
|
@ -1,50 +1,52 @@
|
|||
<template>
|
||||
<a-space class="go-back-button">
|
||||
<a-button :href="props.src" size="small" target="_blank">
|
||||
<template #icon><download-outlined /></template>
|
||||
</a-button>
|
||||
<a-button type="primary" size="small" @click="emit('goBack')">
|
||||
<template #icon><rollback-outlined /></template>
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||
<a-spin :spinning="loading">
|
||||
<vue-office-docx
|
||||
v-if="fileType === 'doc' || fileType === 'docx'"
|
||||
:src="props.src"
|
||||
class="xn-ht82"
|
||||
@rendered="renderedHandler"
|
||||
/>
|
||||
<vue-office-excel
|
||||
v-else-if="fileType === 'xls' || fileType === 'xlsx'"
|
||||
:src="props.src"
|
||||
class="xn-ht82"
|
||||
@rendered="renderedHandler"
|
||||
@error="errorHandler"
|
||||
/>
|
||||
<vue-office-pdf
|
||||
v-else-if="fileType === 'pdf'"
|
||||
:src="props.src"
|
||||
@rendered="renderedHandler"
|
||||
@error="errorHandler"
|
||||
/>
|
||||
<img
|
||||
v-else-if="
|
||||
fileType === 'png' ||
|
||||
fileType === 'jpg' ||
|
||||
fileType === 'gif' ||
|
||||
fileType === 'bmp' ||
|
||||
fileType === 'jpeg' ||
|
||||
fileType === 'ico' ||
|
||||
fileType === 'svg'
|
||||
"
|
||||
:src="props.src"
|
||||
class="xn-mwh"
|
||||
/>
|
||||
<a-result v-else status="warning" title="不支持预览的文件类型" />
|
||||
</a-spin>
|
||||
</a-card>
|
||||
<div style="position: relative">
|
||||
<a-space class="go-back-button">
|
||||
<a-button :href="props.src" size="small" target="_blank">
|
||||
<template #icon><download-outlined /></template>
|
||||
</a-button>
|
||||
<a-button type="primary" size="small" @click="emit('goBack')">
|
||||
<template #icon><rollback-outlined /></template>
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||
<a-spin :spinning="loading">
|
||||
<vue-office-docx
|
||||
v-if="fileType === 'doc' || fileType === 'docx'"
|
||||
:src="props.src"
|
||||
class="xn-ht82"
|
||||
@rendered="renderedHandler"
|
||||
/>
|
||||
<vue-office-excel
|
||||
v-else-if="fileType === 'xls' || fileType === 'xlsx'"
|
||||
:src="props.src"
|
||||
class="xn-ht82"
|
||||
@rendered="renderedHandler"
|
||||
@error="errorHandler"
|
||||
/>
|
||||
<vue-office-pdf
|
||||
v-else-if="fileType === 'pdf'"
|
||||
:src="props.src"
|
||||
@rendered="renderedHandler"
|
||||
@error="errorHandler"
|
||||
/>
|
||||
<img
|
||||
v-else-if="
|
||||
fileType === 'png' ||
|
||||
fileType === 'jpg' ||
|
||||
fileType === 'gif' ||
|
||||
fileType === 'bmp' ||
|
||||
fileType === 'jpeg' ||
|
||||
fileType === 'ico' ||
|
||||
fileType === 'svg'
|
||||
"
|
||||
:src="props.src"
|
||||
class="xn-mwh"
|
||||
/>
|
||||
<a-result v-else status="warning" title="不支持预览的文件类型" />
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
@ -122,7 +124,7 @@
|
|||
.go-back-button {
|
||||
position: absolute;
|
||||
float: right;
|
||||
right: 10px;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
<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]>
|
||||
<slot :name="slotKey" />
|
||||
</template>
|
||||
|
|
|
@ -40,6 +40,9 @@
|
|||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.hljs-container {
|
||||
position: relative;
|
||||
}
|
||||
/** 滚动条 */
|
||||
:deep(.hljs, .hljs-container) {
|
||||
max-height: 300px !important;
|
||||
|
@ -78,8 +81,8 @@
|
|||
/** 复制样式 */
|
||||
.hljs-copy {
|
||||
float: right;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
:showSearch="props.showSearch"
|
||||
:filterOption="!props.showSearch"
|
||||
@change="handleChange"
|
||||
@search="handleSearch"
|
||||
@onSearch="handleSearch"
|
||||
@popupScroll="handlePopupScroll"
|
||||
/>
|
||||
</a-spin>
|
||||
|
@ -24,7 +24,7 @@
|
|||
const initParams = ref({})
|
||||
const options = ref([])
|
||||
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({
|
||||
value: {
|
||||
type: String,
|
||||
|
@ -110,9 +110,9 @@
|
|||
// change
|
||||
const handleChange = (value, array) => {
|
||||
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)
|
||||
|
@ -121,10 +121,10 @@
|
|||
}
|
||||
// search
|
||||
const handleSearch = (searchValue) => {
|
||||
let _params = {current: 1};
|
||||
if (props.searchKeyName && props.searchKeyName !== ''){
|
||||
_params[props.searchKeyName] = searchValue;
|
||||
onPage({ ...initParams.value, ..._params});
|
||||
let _params = { current: 1 }
|
||||
if (props.searchKeyName && props.searchKeyName !== '') {
|
||||
_params[props.searchKeyName] = searchValue
|
||||
onPage({ ...initParams.value, ..._params })
|
||||
}
|
||||
// 触发search事件
|
||||
emit('search', searchValue)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -18,6 +18,9 @@ const DEFAULT_CONFIG = {
|
|||
// 请求超时
|
||||
TIMEOUT: 60000,
|
||||
|
||||
// 版本更新时间 默认10s
|
||||
UPDATE_VERSION_TIME: 10000,
|
||||
|
||||
// TokenName // Authorization
|
||||
TOKEN_NAME: 'token',
|
||||
|
||||
|
|
|
@ -77,14 +77,18 @@
|
|||
import { layoutEnum } from '@/layout/enum/layoutEnum'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
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 DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue'
|
||||
import TopMenu from '@/layout/menu/topMenu.vue'
|
||||
import { NextLoading } from '@/utils/loading'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
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 kStore = keepAliveStore()
|
||||
const route = useRoute()
|
||||
|
@ -246,15 +250,71 @@
|
|||
switchoverTopHeaderThemeColor()
|
||||
settingTopHeaderThemeOrColor(theme.value, layout.value)
|
||||
settingFixedWidth()
|
||||
updateVersion()
|
||||
nextTick(() => {
|
||||
getNav(menu.value)
|
||||
getNav()
|
||||
// 刷新登录信息,不影响其他
|
||||
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 item = menu.value
|
||||
const getNav = () => {
|
||||
// 判断一下是不是顶部导航栏的模式
|
||||
if (layout.value !== 'top') return
|
||||
const menuNavList = menu.value
|
||||
|
@ -337,7 +397,7 @@
|
|||
switchoverTopHeaderThemeColor()
|
||||
// top下的顶栏
|
||||
settingTopHeaderThemeOrColor(theme.value, newValue)
|
||||
getNav(menu.value)
|
||||
getNav()
|
||||
settingFixedWidth()
|
||||
let element = document.querySelector('#xn-line-nav')
|
||||
if (element) {
|
||||
|
@ -572,7 +632,7 @@
|
|||
message.warning('该模块下无任何菜单')
|
||||
}
|
||||
}
|
||||
getNav(menu.value)
|
||||
getNav()
|
||||
}
|
||||
// 通过标签切换应用
|
||||
const tagSwitchModule = (id) => {
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { notification } from 'ant-design-vue'
|
||||
import NProgress from 'nprogress'
|
||||
import 'nprogress/nprogress.css'
|
||||
import systemRouter from './systemRouter'
|
||||
|
@ -128,7 +127,7 @@ router.afterEach((to, from) => {
|
|||
router.onError((error) => {
|
||||
NProgress.done()
|
||||
window.nextLoading && NextLoading.done()
|
||||
notification.error({
|
||||
console.error({
|
||||
message: '路由错误',
|
||||
description: error.message
|
||||
})
|
||||
|
|
|
@ -18,7 +18,12 @@ import userCenterApi from '@/api/sys/userCenterApi'
|
|||
import whiteList from '@/router/whiteList'
|
||||
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', () => {
|
||||
const menuData = ref([])
|
||||
const refreshFlag = ref(false)
|
||||
|
@ -119,24 +124,33 @@ export const useMenuStore = defineStore('menuStore', () => {
|
|||
}
|
||||
})
|
||||
}
|
||||
// 获取用户菜单
|
||||
// 获取用户菜单(通过API重新初始化菜单,用于界面实时响应)
|
||||
const fetchMenu = async () => {
|
||||
const menu = await userCenterApi.userLoginMenu()
|
||||
tool.data.set('MENU', menu)
|
||||
refreshMenu()
|
||||
}
|
||||
// 刷新菜单
|
||||
// 刷新菜单(非API刷新,用于路由守卫内使用)
|
||||
const refreshMenu = () => {
|
||||
loadMenu()
|
||||
removeFromRouter()
|
||||
addToRouter()
|
||||
changeRefreshFlag(true)
|
||||
}
|
||||
// 通过API刷新菜单(仅在layout的onMounted内使用,浏览器刷新只刷新一次)
|
||||
const refreshApiMenu = () => {
|
||||
userCenterApi.userLoginMenu().then((data) => {
|
||||
tool.data.set('MENU', data)
|
||||
nextTick(() => {
|
||||
refreshMenu()
|
||||
})
|
||||
})
|
||||
}
|
||||
// 将菜单添加到路由
|
||||
const addToRouter = () => {
|
||||
menuData.value.forEach((item) => {
|
||||
router.addRoute('layout', item)
|
||||
})
|
||||
}
|
||||
return { menuData, loadMenu, addToRouter, refreshMenu, changeRefreshFlag, refreshFlag, fetchMenu }
|
||||
return { menuData, loadMenu, addToRouter, refreshMenu, changeRefreshFlag, refreshFlag, fetchMenu, refreshApiMenu }
|
||||
})
|
||||
|
|
|
@ -367,6 +367,7 @@ body,
|
|||
.selector-table,
|
||||
.card-div,
|
||||
.ant-table-body,
|
||||
.dict-tree-div,
|
||||
|
||||
.admin-ui-main{
|
||||
&::-webkit-scrollbar {
|
||||
|
|
|
@ -15,7 +15,7 @@ export const required = (message, trigger = ['blur', 'change']) => ({
|
|||
})
|
||||
|
||||
// 常用正则规则大全:https://any86.github.io/any-rule/
|
||||
|
||||
// 表单上面使用参照菜单管理的 title 字段,例如:-> title: [required('请输入菜单名称'), rules.horizontalChart]
|
||||
export const rules = {
|
||||
phone: {
|
||||
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])?$)/,
|
||||
message: '只支持正数金额',
|
||||
trigger: 'blur'
|
||||
},
|
||||
horizontalChart: {
|
||||
pattern: /^[^-]*$/,
|
||||
message: '不可包含横杠 “-”'
|
||||
},
|
||||
initialNotBackslashChart: {
|
||||
pattern: /^(?!\/)[\s\S]*$/,
|
||||
message: '首字母不可出现反斜杠 “/”'
|
||||
},
|
||||
initialYesBackslashChart: {
|
||||
pattern: /^\/[^/].*$/,
|
||||
message: '首字母必须是反斜杠 “/”'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { nextTick } from 'vue'
|
||||
|
||||
/**
|
||||
* 页面全局 Loading
|
||||
* @method start 创建 loading
|
||||
|
@ -10,19 +8,18 @@ export const NextLoading = {
|
|||
start: () => {
|
||||
const el = document.querySelector('.admin-ui')
|
||||
if (el) return
|
||||
const bodys = document.body
|
||||
const body = document.body
|
||||
const div = document.createElement('div')
|
||||
div.setAttribute('class', 'admin-ui')
|
||||
const htmls = `
|
||||
div.innerHTML = `
|
||||
<div class="app-loading">
|
||||
<div class="app-loading__logo">
|
||||
<div class="app-loading-logo">
|
||||
<img src="/img/logo.png"/>
|
||||
</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.innerHTML = htmls
|
||||
bodys.insertBefore(div, bodys.childNodes[0])
|
||||
body.insertBefore(div, body.childNodes[0])
|
||||
window.nextLoading = true
|
||||
},
|
||||
// 移除 loading
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// 统一的请求发送
|
||||
import axios from 'axios'
|
||||
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 tool from '@/utils/tool'
|
||||
|
||||
|
@ -140,7 +140,7 @@ service.interceptors.response.use(
|
|||
if (error) {
|
||||
const status = 503
|
||||
const description = errorCodeMap[status]
|
||||
notification.error({
|
||||
console.error({
|
||||
message: '请求错误',
|
||||
description
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -139,8 +139,7 @@
|
|||
}
|
||||
.login-form {
|
||||
width: 450px;
|
||||
position: absolute;
|
||||
top: 21.8%;
|
||||
margin-top: 110px;
|
||||
}
|
||||
.login-header {
|
||||
margin-bottom: 20px;
|
||||
|
@ -175,7 +174,7 @@
|
|||
.logo_background {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 56px;
|
||||
top: 50px;
|
||||
height: 60px;
|
||||
padding-left: 56px;
|
||||
width: 100%;
|
||||
|
@ -240,11 +239,8 @@
|
|||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.login_background_front {
|
||||
display: none;
|
||||
}
|
||||
.logo_background{
|
||||
padding-left:40px;
|
||||
.logo_background {
|
||||
padding-left: 40px;
|
||||
}
|
||||
.login-form {
|
||||
width: 100%;
|
||||
|
|
|
@ -1,132 +1,113 @@
|
|||
.login-icon-gray {
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.login-validCode-img {
|
||||
border: 1px solid var(--border-color-split);
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
border: 1px solid var(--border-color-split);
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
.login-wrapper{
|
||||
width: 100vw;
|
||||
height:100vh;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
width: 100vw;
|
||||
height:100vh;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
}
|
||||
.login_background {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-image: url(/img/login_background.png);
|
||||
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;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-image: url(/img/login_background.png);
|
||||
position: relative;
|
||||
}
|
||||
@keyframes myfirst {
|
||||
0% {
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
50% {
|
||||
left: 50px;
|
||||
top: 0px;
|
||||
}
|
||||
100% {
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
0% {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
50% {
|
||||
left: 50px;
|
||||
top: 0;
|
||||
}
|
||||
100% {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes myfirst /* Safari and Chrome */ {
|
||||
0% {
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
50% {
|
||||
left: 50px;
|
||||
top: 0px;
|
||||
}
|
||||
100% {
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
@-webkit-keyframes myfirst {
|
||||
0% {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
50% {
|
||||
left: 50px;
|
||||
top: 0;
|
||||
}
|
||||
100% {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
.login_adv__title h2 {
|
||||
font-size: 40px;
|
||||
font-size: 40px;
|
||||
}
|
||||
.login_adv__title h4 {
|
||||
font-size: 18px;
|
||||
margin-top: 10px;
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
margin-top: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.login_adv__title p {
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
line-height: 1.8;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
line-height: 1.8;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.login_adv__title div {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.login_adv__title div span {
|
||||
margin-right: 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.login_adv__title div i {
|
||||
font-size: 40px;
|
||||
font-size: 40px;
|
||||
}
|
||||
.login_adv__title div i.add {
|
||||
font-size: 20px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 20px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
/*background-image:linear-gradient(transparent, #000);*/
|
||||
.login_main {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.login-form {
|
||||
width: 450px;
|
||||
position: absolute;
|
||||
top:21.8%
|
||||
width: 450px;
|
||||
margin-top: 110px;
|
||||
}
|
||||
.login-header {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.login-header h2 {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.login_config {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
.logo_background{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 56px;
|
||||
top: 50px;
|
||||
height: 60px;
|
||||
padding-left: 56px;
|
||||
width: 100%;
|
||||
|
@ -149,7 +130,7 @@
|
|||
margin-right: 10px;
|
||||
}
|
||||
.logo_background a label{
|
||||
font-size:24px;
|
||||
font-size:24px;
|
||||
color:#fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -169,32 +150,29 @@
|
|||
margin-bottom:6px;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
.login-form {
|
||||
width: 340px;
|
||||
}
|
||||
.login-form {
|
||||
width: 340px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1000px) {
|
||||
.login_main {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left:0;
|
||||
right:0;
|
||||
}
|
||||
.login_background_front {
|
||||
display: none;
|
||||
}
|
||||
.login-form {
|
||||
width: 100%;
|
||||
padding: 20px 40px;
|
||||
top:15%
|
||||
}
|
||||
.logo_background{
|
||||
padding-left:40px;
|
||||
}
|
||||
.login_background .version{
|
||||
padding:0 20px;
|
||||
}
|
||||
.login_background .version p:first-child{
|
||||
display: none;
|
||||
}
|
||||
.login_main {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left:0;
|
||||
right:0;
|
||||
}
|
||||
.login-form {
|
||||
width: 100%;
|
||||
padding: 20px 40px;
|
||||
top:15%
|
||||
}
|
||||
.logo_background{
|
||||
padding-left:40px;
|
||||
}
|
||||
.login_background .version{
|
||||
padding:0 20px;
|
||||
}
|
||||
.login_background .version p:first-child{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,17 +170,20 @@
|
|||
|
||||
onMounted(() => {
|
||||
let formData = ref(configData.SYS_BASE_CONFIG)
|
||||
configApi.configSysBaseList().then((data) => {
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = item.configValue
|
||||
})
|
||||
captchaOpen.value = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
|
||||
tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
|
||||
setSysBaseConfig(formData.value)
|
||||
refreshSwitch()
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configSysBaseList()
|
||||
.then((data) => {
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = item.configValue
|
||||
})
|
||||
captchaOpen.value = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
|
||||
tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
|
||||
setSysBaseConfig(formData.value)
|
||||
refreshSwitch()
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
|
@ -238,7 +241,7 @@
|
|||
// 获取token
|
||||
try {
|
||||
const loginToken = await loginApi.login(loginData)
|
||||
const loginAfter = afterLogin(loginToken)
|
||||
await afterLogin(loginToken)
|
||||
} catch (err) {
|
||||
loading.value = false
|
||||
if (captchaOpen.value === 'true') {
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<template>
|
||||
<xn-form-container
|
||||
title="详情"
|
||||
:width="1000"
|
||||
v-model:open="open"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<xn-form-container title="详情" :width="1000" v-model:open="open" :destroy-on-close="true" @close="onClose">
|
||||
<a-descriptions bordered>
|
||||
<a-descriptions-item label="标题">{{formData.title}}</a-descriptions-item>
|
||||
<a-descriptions-item label="类型">
|
||||
|
|
|
@ -118,17 +118,18 @@
|
|||
</a-row>
|
||||
|
||||
<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-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>
|
||||
<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-col :span="7">
|
||||
<a-form-item
|
||||
|
@ -545,24 +546,15 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<style scoped type="less">
|
||||
.childAddButton {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
<style scoped lang="less">
|
||||
.form-row {
|
||||
background-color: var(--item-hover-bg);
|
||||
margin-left: 0px !important;
|
||||
margin-left: 0 !important;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-row-con {
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.dashedButton {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
.form-div {
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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>
|
|
@ -1,15 +1,17 @@
|
|||
<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" />
|
||||
<div class="dict-tree-div">
|
||||
<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" />
|
||||
</div>
|
||||
</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 mb-3" :model="searchFormState">
|
||||
|
@ -42,7 +44,7 @@
|
|||
:row-key="(record) => record.id"
|
||||
>
|
||||
<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>
|
||||
新增
|
||||
</a-button>
|
||||
|
@ -53,7 +55,7 @@
|
|||
<a-tag color="green" v-else>子级</a-tag>
|
||||
</template>
|
||||
<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-popconfirm title="删除此字典与下级字典吗?" @confirm="remove(record)">
|
||||
<a-button type="link" danger size="small">删除</a-button>
|
||||
|
@ -66,11 +68,17 @@
|
|||
<Form ref="formRef" @successful="formSuccessful()" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup name="dictCategoryIndex">
|
||||
import { Empty } from 'ant-design-vue'
|
||||
import dictApi from '@/api/dev/dictApi'
|
||||
import Form from './form.vue'
|
||||
import tool from '@/utils/tool'
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'FRM'
|
||||
}
|
||||
})
|
||||
const columns = [
|
||||
{
|
||||
title: '字典名称',
|
||||
|
@ -93,6 +101,9 @@
|
|||
width: '150px'
|
||||
}
|
||||
]
|
||||
const categoryType = computed(() => {
|
||||
return props.type
|
||||
})
|
||||
// 定义tableDOM
|
||||
const tableRef = ref(null)
|
||||
const formRef = ref()
|
||||
|
@ -108,7 +119,7 @@
|
|||
// 表格查询 返回 Promise 对象
|
||||
const loadData = (parameter) => {
|
||||
loadTreeData()
|
||||
parameter.category = 'BIZ'
|
||||
parameter.category = categoryType.value
|
||||
return dictApi.dictPage(Object.assign(parameter, searchFormState.value)).then((data) => {
|
||||
if (data.records) {
|
||||
if (searchFormState.value.parentId) {
|
||||
|
@ -140,7 +151,7 @@
|
|||
// 加载左侧的树
|
||||
const loadTreeData = () => {
|
||||
const param = {
|
||||
category: 'BIZ'
|
||||
category: categoryType.value
|
||||
}
|
||||
dictApi.dictTree(param).then((res) => {
|
||||
if (res) {
|
||||
|
@ -172,8 +183,12 @@
|
|||
id: record.id
|
||||
}
|
||||
]
|
||||
dictApi.dictDelete(params).then(() => {
|
||||
tableRef.value.refresh(true)
|
||||
dictApi.dictDelete(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
tableRef.value.refresh(true)
|
||||
} else {
|
||||
res.message && tool.error(res.message)
|
||||
}
|
||||
})
|
||||
refreshStoreDict()
|
||||
}
|
||||
|
@ -190,11 +205,15 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="less">
|
||||
.ant-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.snowy-button-left {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.dict-tree-div {
|
||||
height: 700px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
|
@ -1,32 +1,23 @@
|
|||
<template>
|
||||
<a-card
|
||||
:bordered="false"
|
||||
:active-tab-key="activeKey"
|
||||
:tab-list="tabListNoTitle"
|
||||
@tabChange="(key) => onTabChange(key, 'frmIndex')"
|
||||
>
|
||||
<p v-if="activeKey === 'frmIndex'">
|
||||
<frm-index />
|
||||
</p>
|
||||
<p v-if="activeKey === 'bizIndex'">
|
||||
<biz-index />
|
||||
</p>
|
||||
<a-card>
|
||||
<a-tabs size="large" v-model:activeKey="activeKey">
|
||||
<a-tab-pane v-for="item in tabListNoTitle" :key="item.key" :tab="item.tab">
|
||||
<category :type="item.key" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup name="devDict">
|
||||
import frmIndex from './category/frmIndex.vue'
|
||||
import bizIndex from './category/bizIndex.vue'
|
||||
const activeKey = ref('frmIndex')
|
||||
import Category from './category/index.vue'
|
||||
const activeKey = ref('FRM')
|
||||
const tabListNoTitle = ref([
|
||||
{ key: 'frmIndex', tab: '系统字典' },
|
||||
{ key: 'bizIndex', tab: '业务字典' }
|
||||
{ key: 'FRM', tab: '系统字典' },
|
||||
{ key: 'BIZ', tab: '业务字典' }
|
||||
])
|
||||
const onTabChange = (value, type) => {
|
||||
if (type === 'key') {
|
||||
key.value = value
|
||||
} else if (type === 'frmIndex') {
|
||||
activeKey.value = value
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-card-body) {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<template>
|
||||
<xn-form-container
|
||||
title="详情"
|
||||
:width="700"
|
||||
:visible="visible"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
|
||||
<a-descriptions :column="1" size="middle" bordered class="mb-2">
|
||||
<a-descriptions-item label="名称">{{ formData.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="请求IP">{{ formData.opIp }}</a-descriptions-item>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<template>
|
||||
<xn-form-container
|
||||
title="详情"
|
||||
:width="700"
|
||||
:visible="visible"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
|
||||
<a-descriptions :column="1" size="middle" bordered class="mb-2">
|
||||
<a-descriptions-item label="名称">{{ formData.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="IP地址">{{ formData.opIp }}</a-descriptions-item>
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title>
|
||||
类型为内外链条时,输入https开头的链接即可(例:https://xiaonuo.vip),正常路由前面必须有反斜杠!
|
||||
类型为内外链时,输入https开头的链接即可(例:https://xiaonuo.vip),正常路由前面必须有反斜杠!
|
||||
</template>
|
||||
<question-circle-outlined />
|
||||
</a-tooltip>
|
||||
|
@ -122,7 +122,7 @@
|
|||
</template>
|
||||
|
||||
<script setup name="sysResourceMenuForm">
|
||||
import { required } from '@/utils/formRules'
|
||||
import { required, rules } from '@/utils/formRules'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import SnowflakeId from 'snowflake-id'
|
||||
import tool from '@/utils/tool'
|
||||
|
@ -197,20 +197,20 @@
|
|||
// 图标选择器回调
|
||||
const iconCallBack = (value) => {
|
||||
if (value) {
|
||||
formRef.value.clearValidate("icon")
|
||||
formRef.value.clearValidate('icon')
|
||||
}
|
||||
formData.value.icon = value
|
||||
}
|
||||
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
title: [required('请输入菜单名称')],
|
||||
title: [required('请输入菜单名称'), rules.horizontalChart],
|
||||
parentId: [required('请选择上级菜单')],
|
||||
menuType: [required('请选择菜单类型')],
|
||||
path: [required('请输入路由地址')],
|
||||
name: [required('请输入组件中name属性')],
|
||||
module: [required('请选择模块')],
|
||||
component: [required('请输入组件地址')],
|
||||
component: [required('请输入组件地址'), rules.initialNotBackslashChart],
|
||||
visible: [required('请选择是否可见')]
|
||||
}
|
||||
|
||||
|
|
|
@ -101,8 +101,7 @@
|
|||
// firstShowMap = {} // 重置单元格合并映射
|
||||
// 如果有数据,我们再不去反复的查询
|
||||
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 {
|
||||
// 获取表格数据
|
||||
spinningLoading.value = true
|
||||
|
@ -144,11 +143,11 @@
|
|||
if (module.menu) {
|
||||
// 加入回显内容
|
||||
module.menu.forEach((item) => {
|
||||
const menueCheck = ref(0)
|
||||
const menusCheck = ref(0)
|
||||
if (resEcho.grantInfoList.length > 0) {
|
||||
resEcho.grantInfoList.forEach((grant) => {
|
||||
if (item.id === grant.menuId) {
|
||||
menueCheck.value++
|
||||
menusCheck.value++
|
||||
// 处理按钮
|
||||
if (grant.buttonInfo) {
|
||||
grant.buttonInfo.forEach((button) => {
|
||||
|
@ -163,15 +162,23 @@
|
|||
})
|
||||
}
|
||||
// 回显前面的2个
|
||||
if (menueCheck.value > 0) {
|
||||
if (menusCheck.value > 0) {
|
||||
item.parentCheck = true
|
||||
item.nameCheck = true
|
||||
}
|
||||
})
|
||||
|
||||
// 排序
|
||||
module.menu = module.menu.sort((a, b) => {
|
||||
return a.parentId - b.parentId
|
||||
module.menu.sort((a, b) => {
|
||||
// 首先比较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) => {
|
||||
|
@ -200,12 +207,11 @@
|
|||
})
|
||||
}
|
||||
const checkAllChildNotChecked = (record) => {
|
||||
const allChecked = checkFieldKeys.every((key) => {
|
||||
return checkFieldKeys.every((key) => {
|
||||
// 遍历所有的字段
|
||||
const child = record[key]
|
||||
return child.every((field) => !field.check)
|
||||
})
|
||||
return allChecked
|
||||
}
|
||||
const changeChildCheckBox = (record, evt) => {
|
||||
let checked = evt.target.checked
|
||||
|
@ -298,7 +304,7 @@
|
|||
<style scoped>
|
||||
/* 重写复选框的样式 */
|
||||
.ant-checkbox-wrapper {
|
||||
margin-left: 0px !important;
|
||||
margin-left: 0 !important;
|
||||
padding-top: 2px !important;
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
|
|
|
@ -17,13 +17,20 @@
|
|||
class="mt-4"
|
||||
size="middle"
|
||||
:columns="columns"
|
||||
:data-source="loadDatas"
|
||||
:data-source="tableLoadData"
|
||||
:row-key="(record) => record.api"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
bordered
|
||||
>
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'api'">
|
||||
<a-checkbox @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox>
|
||||
<template v-if="column.key === 'prefix'">
|
||||
<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 v-if="column.key === 'dataScope'">
|
||||
<span>{{ column.title }}</span>
|
||||
|
@ -54,16 +61,21 @@
|
|||
<template #icon><SearchOutlined /></template>
|
||||
搜索
|
||||
</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>
|
||||
</template>
|
||||
<template #customFilterIcon="{ filtered }">
|
||||
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
|
||||
</template>
|
||||
<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)">
|
||||
{{ record.api }}
|
||||
{{ record.suffix }}
|
||||
</a-checkbox>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'dataScope'">
|
||||
|
@ -109,6 +121,7 @@
|
|||
import roleApi from '@/api/sys/roleApi'
|
||||
import ScopeDefineOrg from './scopeDefineOrg.vue'
|
||||
import { userStore } from '@/store/user'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const visible = ref(false)
|
||||
const spinningLoading = ref(false)
|
||||
|
@ -117,7 +130,7 @@
|
|||
const submitLoading = ref(false)
|
||||
const CustomValue = 'SCOPE_ORG_DEFINE'
|
||||
// 抽屉的宽度
|
||||
const drawerWidth = 1000
|
||||
const drawerWidth = 1050
|
||||
// 自动获取宽度,默认获取浏览器的宽度的90%
|
||||
//(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9
|
||||
let loadDatas = ref([])
|
||||
|
@ -128,13 +141,39 @@
|
|||
searchedColumn: ''
|
||||
})
|
||||
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 = [
|
||||
{
|
||||
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: '接口',
|
||||
dataIndex: 'api',
|
||||
width: 380,
|
||||
dataIndex: 'suffix',
|
||||
width: 290,
|
||||
customFilterDropdown: true,
|
||||
onFilter: (value, record) => record.api.includes(value),
|
||||
onFilterDropdownOpenChange: (visible) => {
|
||||
|
@ -161,16 +200,27 @@
|
|||
}
|
||||
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
|
||||
}
|
||||
// table事件触发
|
||||
const handleTableChange = (pageInfo) => {
|
||||
Object.assign(pagination.value, pageInfo)
|
||||
}
|
||||
// 数据转换
|
||||
const echoModuleData = (res, resOwn) => {
|
||||
let list = []
|
||||
res.forEach((api) => {
|
||||
const apiArr = splitByThirdSlash(api)
|
||||
const obj = {
|
||||
api: api,
|
||||
prefix: apiArr[0],
|
||||
suffix: apiArr[1],
|
||||
dataScope: datascope(api),
|
||||
check: false
|
||||
check: false,
|
||||
parentCheck: false
|
||||
}
|
||||
if (resOwn.grantInfoList.length > 0) {
|
||||
resOwn.grantInfoList.forEach((item) => {
|
||||
|
@ -189,8 +239,10 @@
|
|||
}
|
||||
})
|
||||
}
|
||||
loadDatas.value.push(obj)
|
||||
list.push(obj)
|
||||
})
|
||||
// 设置父节点check状态
|
||||
return setParentDataCheckedStatus(list)
|
||||
}
|
||||
const datascope = (id) => {
|
||||
return [
|
||||
|
@ -277,6 +329,11 @@
|
|||
const onOpen = (record) => {
|
||||
grantPermissionParam.id = record.id
|
||||
visible.value = true
|
||||
firstShowMap.value = {}
|
||||
pagination.value.current = 1
|
||||
pagination.value.pageSize = 10
|
||||
pagination.value.total = 0
|
||||
allChecked.value = false
|
||||
loadData()
|
||||
}
|
||||
// 关闭抽屉
|
||||
|
@ -288,15 +345,41 @@
|
|||
}
|
||||
// 全选
|
||||
const onCheckAllChange = (value) => {
|
||||
allChecked.value = value
|
||||
spinningLoading.value = true
|
||||
loadDatas.value.forEach((data) => {
|
||||
changeApi(data, value)
|
||||
changeApi(data, value, false)
|
||||
data.parentCheck = value
|
||||
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
|
||||
if (val) {
|
||||
let checkStatus = 0
|
||||
|
@ -317,6 +400,7 @@
|
|||
}
|
||||
})
|
||||
}
|
||||
isLoadData && (loadDatas.value = setParentDataCheckedStatus(loadDatas.value))
|
||||
}
|
||||
// 设置选中状态
|
||||
const changeChildCheckBox = (record, evt) => {
|
||||
|
@ -382,8 +466,9 @@
|
|||
state.searchedColumn = dataIndex
|
||||
}
|
||||
// 标题接口列搜索重置
|
||||
const handleReset = (clearFilters) => {
|
||||
const handleReset = (clearFilters, confirm) => {
|
||||
clearFilters()
|
||||
confirm()
|
||||
state.searchText = ''
|
||||
}
|
||||
// 标题数据范围列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({
|
||||
onOpen
|
||||
|
|
|
@ -104,8 +104,8 @@
|
|||
// firstShowMap = {} // 重置单元格合并映射
|
||||
// 如果有数据,我们再不去反复的查询
|
||||
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 {
|
||||
// 获取表格数据
|
||||
spinningLoading.value = true
|
||||
|
@ -142,11 +142,11 @@
|
|||
if (module.menu) {
|
||||
// 加入回显内容
|
||||
module.menu.forEach((item) => {
|
||||
const menueCheck = ref(0)
|
||||
const menusCheck = ref(0)
|
||||
if (resEcho.grantInfoList.length > 0) {
|
||||
resEcho.grantInfoList.forEach((grant) => {
|
||||
if (item.id === grant.menuId) {
|
||||
menueCheck.value++
|
||||
menusCheck.value++
|
||||
// 处理按钮
|
||||
if (grant.buttonInfo.length > 0) {
|
||||
grant.buttonInfo.forEach((button) => {
|
||||
|
@ -161,15 +161,23 @@
|
|||
})
|
||||
}
|
||||
// 回显前面的2个
|
||||
if (menueCheck.value > 0) {
|
||||
if (menusCheck.value > 0) {
|
||||
item.parentCheck = true
|
||||
item.nameCheck = true
|
||||
}
|
||||
})
|
||||
|
||||
// 排序
|
||||
module.menu = module.menu.sort((a, b) => {
|
||||
return a.parentId - b.parentId
|
||||
module.menu.sort((a, b) => {
|
||||
// 首先比较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) => {
|
||||
|
@ -198,12 +206,11 @@
|
|||
})
|
||||
}
|
||||
const checkAllChildNotChecked = (record) => {
|
||||
const allChecked = checkFieldKeys.every((key) => {
|
||||
return checkFieldKeys.every((key) => {
|
||||
// 遍历所有的字段
|
||||
const child = record[key]
|
||||
return child.every((field) => !field.check)
|
||||
})
|
||||
return allChecked
|
||||
}
|
||||
const changeChildCheckBox = (record, evt) => {
|
||||
let checked = evt.target.checked
|
||||
|
@ -303,7 +310,7 @@
|
|||
<style scoped>
|
||||
/* 重写复选框的样式 */
|
||||
.ant-checkbox-wrapper {
|
||||
margin-left: 0px !important;
|
||||
margin-left: 0 !important;
|
||||
padding-top: 2px !important;
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
checkable
|
||||
check-strictly
|
||||
:selectable="false"
|
||||
@check="treeCheck"
|
||||
>
|
||||
|
@ -25,6 +26,7 @@
|
|||
|
||||
<script setup="props, context" name="scopeDefineOrg">
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import { checkOrUnCheckChildren } from '@/utils/treeHandler'
|
||||
const visible = ref(false)
|
||||
let defaultExpandedKeys = ref([])
|
||||
let checkedKeys = ref([])
|
||||
|
@ -83,8 +85,8 @@
|
|||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
|
||||
// 选中触发
|
||||
const treeCheck = (checkedKeys) => {
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkedKeys
|
||||
const treeCheck = (checkedKeys, { checked, node }) => {
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkOrUnCheckChildren(checked, node, checkedKeys)
|
||||
}
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({
|
||||
|
|
|
@ -118,17 +118,18 @@
|
|||
</a-row>
|
||||
|
||||
<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-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>
|
||||
<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-col :span="7">
|
||||
<a-form-item
|
||||
|
@ -549,19 +550,14 @@
|
|||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.childAddButton {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-row {
|
||||
background-color: var(--item-hover-bg);
|
||||
margin-left: 0px !important;
|
||||
margin-left: 0 !important;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-row-con {
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.form-div {
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -17,13 +17,20 @@
|
|||
class="mt-4"
|
||||
size="middle"
|
||||
:columns="columns"
|
||||
:data-source="loadDatas"
|
||||
:data-source="tableLoadData"
|
||||
bordered
|
||||
:row-key="(record) => record.api"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'api'">
|
||||
<a-checkbox @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox>
|
||||
<template v-if="column.key === 'prefix'">
|
||||
<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 v-if="column.key === 'dataScope'">
|
||||
<span>{{ column.title }}</span>
|
||||
|
@ -54,16 +61,21 @@
|
|||
<template #icon><SearchOutlined /></template>
|
||||
搜索
|
||||
</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>
|
||||
</template>
|
||||
<template #customFilterIcon="{ filtered }">
|
||||
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
|
||||
</template>
|
||||
<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)">
|
||||
{{ record.api }}
|
||||
{{ record.suffix }}
|
||||
</a-checkbox>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'dataScope'">
|
||||
|
@ -110,6 +122,7 @@
|
|||
import roleApi from '@/api/sys/roleApi'
|
||||
import ScopeDefineOrg from './scopeDefineOrg.vue'
|
||||
import { userStore } from '@/store/user'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const visible = ref(false)
|
||||
const spinningLoading = ref(false)
|
||||
|
@ -118,7 +131,7 @@
|
|||
const submitLoading = ref(false)
|
||||
const CustomValue = 'SCOPE_ORG_DEFINE'
|
||||
// 抽屉的宽度
|
||||
const drawerWidth = 1000
|
||||
const drawerWidth = 1050
|
||||
// 自动获取宽度,默认获取浏览器的宽度的90%
|
||||
//(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9
|
||||
const loadDatas = ref([])
|
||||
|
@ -129,13 +142,39 @@
|
|||
searchedColumn: ''
|
||||
})
|
||||
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 = [
|
||||
{
|
||||
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: '接口',
|
||||
dataIndex: 'api',
|
||||
width: 380,
|
||||
dataIndex: 'suffix',
|
||||
width: 290,
|
||||
customFilterDropdown: true,
|
||||
onFilter: (value, record) => record.api.includes(value),
|
||||
onFilterDropdownOpenChange: (visible) => {
|
||||
|
@ -162,17 +201,29 @@
|
|||
}
|
||||
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
|
||||
}
|
||||
// table事件触发
|
||||
const handleTableChange = (pageInfo) => {
|
||||
Object.assign(pagination.value, pageInfo)
|
||||
}
|
||||
// 数据转换
|
||||
const echoModuleData = (res, resOwn) => {
|
||||
let list = []
|
||||
res.forEach((api) => {
|
||||
const apiArr = splitByThirdSlash(api)
|
||||
const obj = {
|
||||
api: api,
|
||||
prefix: apiArr[0],
|
||||
suffix: apiArr[1],
|
||||
dataScope: datascope(api),
|
||||
check: false
|
||||
check: false,
|
||||
parentCheck: false
|
||||
}
|
||||
|
||||
if (resOwn.grantInfoList.length > 0) {
|
||||
resOwn.grantInfoList.forEach((item) => {
|
||||
if (item.apiUrl === subStrApi(api)) {
|
||||
|
@ -190,8 +241,10 @@
|
|||
}
|
||||
})
|
||||
}
|
||||
loadDatas.value.push(obj)
|
||||
list.push(obj)
|
||||
})
|
||||
// 设置父节点check状态
|
||||
return setParentDataCheckedStatus(list)
|
||||
}
|
||||
const datascope = (id) => {
|
||||
return [
|
||||
|
@ -278,6 +331,11 @@
|
|||
const onOpen = (record) => {
|
||||
grantPermissionParam.id = record.id
|
||||
visible.value = true
|
||||
firstShowMap.value = {}
|
||||
pagination.value.current = 1
|
||||
pagination.value.pageSize = 10
|
||||
pagination.value.total = 0
|
||||
allChecked.value = false
|
||||
loadData()
|
||||
}
|
||||
// 关闭抽屉
|
||||
|
@ -289,15 +347,41 @@
|
|||
}
|
||||
// 全选
|
||||
const onCheckAllChange = (value) => {
|
||||
allChecked.value = value
|
||||
spinningLoading.value = true
|
||||
loadDatas.value.forEach((data) => {
|
||||
changeApi(data, value)
|
||||
changeApi(data, value, false)
|
||||
data.parentCheck = value
|
||||
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
|
||||
if (val) {
|
||||
let checkStatus = 0
|
||||
|
@ -318,6 +402,7 @@
|
|||
}
|
||||
})
|
||||
}
|
||||
isLoadData && (loadDatas.value = setParentDataCheckedStatus(loadDatas.value))
|
||||
}
|
||||
// 设置选中状态
|
||||
const changeChildCheckBox = (record, evt) => {
|
||||
|
@ -383,8 +468,9 @@
|
|||
state.searchedColumn = dataIndex
|
||||
}
|
||||
// 标题接口列搜索重置
|
||||
const handleReset = (clearFilters) => {
|
||||
const handleReset = (clearFilters, confirm) => {
|
||||
clearFilters()
|
||||
confirm()
|
||||
state.searchText = ''
|
||||
}
|
||||
// 标题数据范围列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({
|
||||
onOpen
|
||||
|
|
|
@ -105,8 +105,7 @@
|
|||
// firstShowMap = {} // 重置单元格合并映射
|
||||
// 如果有数据,我们再不去反复的查询
|
||||
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 {
|
||||
// 获取表格数据
|
||||
spinningLoading.value = true
|
||||
|
@ -143,11 +142,11 @@
|
|||
if (module.menu) {
|
||||
// 加入回显内容
|
||||
module.menu.forEach((item) => {
|
||||
const menueCheck = ref(0)
|
||||
const menusCheck = ref(0)
|
||||
if (resEcho.grantInfoList.length > 0) {
|
||||
resEcho.grantInfoList.forEach((grant) => {
|
||||
if (item.id === grant.menuId) {
|
||||
menueCheck.value++
|
||||
menusCheck.value++
|
||||
// 处理按钮
|
||||
if (grant.buttonInfo.length > 0) {
|
||||
grant.buttonInfo.forEach((button) => {
|
||||
|
@ -162,15 +161,23 @@
|
|||
})
|
||||
}
|
||||
// 回显前面的2个
|
||||
if (menueCheck.value > 0) {
|
||||
if (menusCheck.value > 0) {
|
||||
item.parentCheck = true
|
||||
item.nameCheck = true
|
||||
}
|
||||
})
|
||||
|
||||
// 排序
|
||||
module.menu = module.menu.sort((a, b) => {
|
||||
return a.parentId - b.parentId
|
||||
module.menu.sort((a, b) => {
|
||||
// 首先比较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) => {
|
||||
|
@ -199,12 +206,11 @@
|
|||
})
|
||||
}
|
||||
const checkAllChildNotChecked = (record) => {
|
||||
const allChecked = checkFieldKeys.every((key) => {
|
||||
return checkFieldKeys.every((key) => {
|
||||
// 遍历所有的字段
|
||||
const child = record[key]
|
||||
return child.every((field) => !field.check)
|
||||
})
|
||||
return allChecked
|
||||
}
|
||||
const changeChildCheckBox = (record, evt) => {
|
||||
let checked = evt.target.checked
|
||||
|
@ -305,7 +311,7 @@
|
|||
<style scoped>
|
||||
/* 重写复选框的样式 */
|
||||
.ant-checkbox-wrapper {
|
||||
margin-left: 0px !important;
|
||||
margin-left: 0 !important;
|
||||
padding-top: 2px !important;
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
checkable
|
||||
check-strictly
|
||||
:selectable="false"
|
||||
@check="treeCheck"
|
||||
>
|
||||
|
@ -25,6 +26,7 @@
|
|||
|
||||
<script setup="props, context" name="userScopeDefineOrg">
|
||||
import userApi from '@/api/sys/userApi'
|
||||
import { checkOrUnCheckChildren } from '@/utils/treeHandler'
|
||||
const visible = ref(false)
|
||||
let defaultExpandedKeys = ref([])
|
||||
let checkedKeys = ref([])
|
||||
|
@ -83,8 +85,8 @@
|
|||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
|
||||
// 选中触发
|
||||
const treeCheck = (checkedKeys) => {
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkedKeys
|
||||
const treeCheck = (checkedKeys, { checked, node }) => {
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkOrUnCheckChildren(checked, node, checkedKeys)
|
||||
}
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({
|
||||
|
|
|
@ -421,7 +421,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
|||
queryWrapper.lambda().eq(BizUser::getUserStatus, bizUserExportParam.getUserStatus());
|
||||
}
|
||||
}
|
||||
String fileName = "SNOWY2.0系统B端人员信息清单.xlsx";
|
||||
String fileName = "SNOWY系统B端人员信息清单.xlsx";
|
||||
List<BizUser> bizUserList = this.list(queryWrapper);
|
||||
if(ObjectUtil.isEmpty(bizUserList)) {
|
||||
throw new CommonException("无数据可导出");
|
||||
|
@ -575,7 +575,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
|||
// 生成doc
|
||||
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);
|
||||
doc.write(outputStream);
|
||||
|
|
|
@ -996,7 +996,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||
try {
|
||||
InputStream inputStream = POICacheManager.getFile("userImportTemplate.xlsx");
|
||||
byte[] bytes = IoUtil.readBytes(inputStream);
|
||||
CommonDownloadUtil.download("SNOWY2.0系统B端用户导入模板.xlsx", bytes, response);
|
||||
CommonDownloadUtil.download("SNOWY系统B端用户导入模板.xlsx", bytes, response);
|
||||
} catch (Exception e) {
|
||||
log.error(">>> 下载用户导入模板失败:", e);
|
||||
CommonResponseUtil.renderError(response, "下载用户导入模板失败");
|
||||
|
@ -1181,7 +1181,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||
queryWrapper.lambda().eq(SysUser::getUserStatus, sysUserExportParam.getUserStatus());
|
||||
}
|
||||
}
|
||||
String fileName = "SNOWY2.0系统B端用户信息清单.xlsx";
|
||||
String fileName = "SNOWY系统B端用户信息清单.xlsx";
|
||||
List<SysUser> sysUserList = this.list(queryWrapper);
|
||||
if(ObjectUtil.isEmpty(sysUserList)) {
|
||||
throw new CommonException("无数据可导出");
|
||||
|
@ -1335,7 +1335,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||
// 生成doc
|
||||
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);
|
||||
doc.write(outputStream);
|
||||
|
|
|
@ -19,12 +19,11 @@ import cn.dev33.satoken.router.SaHttpMethod;
|
|||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.Header;
|
||||
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 jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.ibatis.mapping.DatabaseIdProvider;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.ReflectionException;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
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.StringRedisSerializer;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
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.WebMvcConfigurer;
|
||||
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.pojo.CommonResult;
|
||||
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.core.handler.GlobalExceptionUtil;
|
||||
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:";
|
||||
|
||||
@Resource
|
||||
private CommonCacheOperator commonCacheOperator;
|
||||
|
||||
/**
|
||||
* 无需登录的接口地址集合
|
||||
*/
|
||||
|
@ -283,6 +279,7 @@ public class GlobalConfigure implements WebMvcConfigurer {
|
|||
* @author xuyuxiang
|
||||
* @date 2022/6/21 17:01
|
||||
**/
|
||||
@SuppressWarnings("ALL")
|
||||
@Primary
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(@Autowired(required = false) RedisConnectionFactory redisConnectionFactory) {
|
||||
|
@ -311,64 +308,71 @@ public class GlobalConfigure implements WebMvcConfigurer {
|
|||
}
|
||||
|
||||
/**
|
||||
* 添加节流防抖拦截器
|
||||
* 节流防抖的AOP
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/6/20 15:18
|
||||
**/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new HandlerInterceptor() {
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
||||
@NonNull Object handler) throws Exception {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
Method method = handlerMethod.getMethod();
|
||||
CommonNoRepeat annotation = method.getAnnotation(CommonNoRepeat.class);
|
||||
if (ObjectUtil.isNotEmpty(annotation)) {
|
||||
JSONObject repeatSubmitJsonObject = this.isRepeatSubmit(request, annotation);
|
||||
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;
|
||||
}
|
||||
* @date 2022/9/15 21:24
|
||||
*/
|
||||
@Component
|
||||
@Aspect
|
||||
public static class CommonNoRepeatAop {
|
||||
|
||||
public JSONObject isRepeatSubmit(HttpServletRequest request, CommonNoRepeat annotation) {
|
||||
JSONObject jsonObject = JSONUtil.createObj();
|
||||
jsonObject.set("repeatParam", JSONUtil.toJsonStr(request.getParameterMap()));
|
||||
jsonObject.set("repeatTime", DateUtil.current());
|
||||
String url = request.getRequestURI();
|
||||
// 获取该接口缓存的限流数据
|
||||
Object cacheObj = commonCacheOperator.get(COMMON_REPEAT_SUBMIT_CACHE_KEY + url);
|
||||
if (ObjectUtil.isNotEmpty(cacheObj)) {
|
||||
JSONObject cacheJsonObject = JSONUtil.parseObj(cacheObj);
|
||||
if(cacheJsonObject.containsKey(url)) {
|
||||
JSONObject existRepeatJsonObject = cacheJsonObject.getJSONObject(url);
|
||||
// 如果与上次参数一致,且时间间隔小于要求的限流时长,则判定为重复提交
|
||||
if (jsonObject.getStr("repeatParam").equals(existRepeatJsonObject.getStr("repeatParam"))) {
|
||||
long interval = jsonObject.getLong("repeatTime") - existRepeatJsonObject.getLong("repeatTime");
|
||||
if(interval < annotation.interval()) {
|
||||
long secondsParam = (annotation.interval() - interval) / 1000;
|
||||
if(secondsParam == 0) {
|
||||
return JSONUtil.createObj().set("repeat", false);
|
||||
} else {
|
||||
return JSONUtil.createObj().set("repeat", true).set("time", CommonTimeFormatUtil.formatSeconds(secondsParam));
|
||||
}
|
||||
/**
|
||||
* 切入点
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/9/15 21:27
|
||||
*/
|
||||
@Pointcut("@annotation(vip.xiaonuo.common.annotation.CommonNoRepeat)")
|
||||
private void noRepeatPointcut() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行校验
|
||||
*
|
||||
* @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();
|
||||
CommonCacheOperator commonCacheOperator = SpringUtil.getBean(CommonCacheOperator.class);
|
||||
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)) {
|
||||
JSONObject cacheJsonObject = JSONUtil.parseObj(cacheObj);
|
||||
if(cacheJsonObject.containsKey(url)) {
|
||||
JSONObject existRepeatJsonObject = cacheJsonObject.getJSONObject(url);
|
||||
// 如果与上次参数一致,且时间间隔小于要求的限流时长,则判定为重复提交
|
||||
if (jsonObject.getStr("repeatParam").equals(existRepeatJsonObject.getStr("repeatParam"))) {
|
||||
long interval = jsonObject.getLong("repeatTime") - existRepeatJsonObject.getLong("repeatTime");
|
||||
if(interval < commonNoRepeat.interval()) {
|
||||
long secondsParam = (commonNoRepeat.interval() - interval) / 1000;
|
||||
if(secondsParam > 0) {
|
||||
throw new CommonException("请求过于频繁,请" + CommonTimeFormatUtil.formatSeconds(secondsParam) + "后再试");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 缓存最新的该接口的限流数据,为防止缓存的数据过多,缓存时效为1小时
|
||||
commonCacheOperator.put(COMMON_REPEAT_SUBMIT_CACHE_KEY + url, JSONUtil.createObj().set(url, jsonObject), 60 * 60);
|
||||
return JSONUtil.createObj().set("repeat", false);
|
||||
}
|
||||
}).addPathPatterns("/**");
|
||||
// 缓存最新的该接口的限流数据,跟当前ip以及登录用户有关,为防止缓存的数据过多,缓存时效为1小时
|
||||
commonCacheOperator.put(cacheKey, JSONUtil.createObj().set(url, jsonObject), 60 * 60);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -411,7 +415,7 @@ public class GlobalConfigure implements WebMvcConfigurer {
|
|||
* @author xuyuxiang
|
||||
* @date 2022/9/15 21:27
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
@SuppressWarnings("ALL")
|
||||
private Object processWrapping(ProceedingJoinPoint proceedingJoinPoint, Object originResult) throws IllegalAccessException, InstantiationException {
|
||||
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
|
||||
Method method = methodSignature.getMethod();
|
||||
|
@ -462,7 +466,7 @@ public class GlobalConfigure implements WebMvcConfigurer {
|
|||
* @author xuyuxiang
|
||||
* @date 2022/9/15 21:36
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
@SuppressWarnings("ALL")
|
||||
private JSONObject wrapPureObject(Object originModel, Class<? extends CommonWrapperInterface<?>>[] baseWrapperClasses) {
|
||||
JSONObject jsonObject = JSONUtil.parseObj(originModel);
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue