v2.1.1 更多细节问题修复(快来看看你提的Iss是否更新),增加菜单搜索功能

pull/57/head v2.1.1-release
小诺 2022-11-09 12:29:04 +00:00 committed by Gitee
commit 6634bdf0e6
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
25 changed files with 682 additions and 217 deletions

View File

@ -17,63 +17,66 @@
"prod": "vite --mode production" "prod": "vite --mode production"
}, },
"dependencies": { "dependencies": {
"@ant-design/colors": "4.0.1", "@ant-design/colors": "6.0.0",
"@ant-design/icons-vue": "^6.0.1", "@ant-design/icons-vue": "^6.1.0",
"@antv/g2plot": "^2.4.10", "@antv/g2plot": "^2.4.10",
"@chenfengyuan/vue-qrcode": "2", "@chenfengyuan/vue-qrcode": "2.0.0",
"@highlightjs/vue-plugin": "^2.1.0", "@highlightjs/vue-plugin": "^2.1.0",
"@tinymce/tinymce-vue": "4.0.5", "@tinymce/tinymce-vue": "5.0.0",
"ant-design-vue": "3.2.10", "ant-design-vue": "3.2.13",
"axios": "0.24.0", "axios": "1.1.3",
"core-js": "^3.26.0",
"cropperjs": "1.5.12", "cropperjs": "1.5.12",
"dayjs": "^1.11.5", "dayjs": "^1.11.6",
"echarts": "5.2.2", "echarts": "^5.4.0",
"echarts-stat": "^1.2.0", "echarts-stat": "^1.2.0",
"enquire.js": "^2.1.6", "enquire.js": "^2.1.6",
"fuse.js": "^6.6.2",
"highlight.js": "^11.6.0", "highlight.js": "^11.6.0",
"hotkeys-js": "^3.10.0",
"js-pinyin": "^0.1.9",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"sm-crypto": "^0.3.11", "sm-crypto": "^0.3.11",
"snowflake-id": "^1.1.0", "snowflake-id": "^1.1.0",
"sortablejs": "1.14.0", "sortablejs": "^1.15.0",
"tinymce": "5.10.2", "tinymce": "^6.2.0",
"vue": "3.2.31", "vue": "3.2.41",
"vue-cropper": "^1.0.1", "vue-cropper": "^1.0.1",
"vue-i18n": "^9.1.10", "vue-i18n": "^9.2.2",
"vue-router": "4.0.12", "vue-router": "^4.1.6",
"vue3-colorpicker": "^2.0.4", "vue3-colorpicker": "^2.0.4",
"vue3-tree-org": "^3.1.6", "vue3-tree-org": "^4.1.1",
"vuedraggable-es": "4.1.0", "vuedraggable-es": "^4.1.1",
"vuex": "4.0.2" "vuex": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^0.29.2", "@antfu/eslint-config": "^0.29.3",
"@antfu/utils": "0.6.0", "@babel/eslint-parser": "^7.19.1",
"@vitejs/plugin-legacy": "^1.6.4", "@vitejs/plugin-legacy": "^1.6.4",
"@vitejs/plugin-vue": "^2.1.0", "@vitejs/plugin-vue": "^2.1.0",
"@vitejs/plugin-vue-jsx": "^1.3.8", "@vitejs/plugin-vue-jsx": "^1.3.8",
"@vue/compiler-sfc": "^3.2.31", "@vue/compiler-sfc": "^3.2.41",
"@vue/eslint-config-standard": "^4.0.0", "@vue/eslint-config-standard": "^8.0.1",
"antd-dayjs-vite-plugin": "^1.2.2",
"antd-less-to-css-variable": "^1.0.5", "antd-less-to-css-variable": "^1.0.5",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.13",
"babel-eslint": "10.1.0", "eslint": "^8.26.0",
"eslint": "^7.11.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.1.1", "eslint-plugin-vue": "^9.7.0",
"less": "^4.1.2", "less": "^4.1.3",
"postcss": "^8.4.7", "postcss": "^8.4.18",
"prettier": "^2.4.1", "prettier": "^2.7.1",
"rollup-plugin-visualizer": "^5.7.1", "rollup-plugin-visualizer": "^5.8.3",
"tailwindcss": "^3.0.23", "tailwindcss": "^3.2.1",
"typescript": "^4.5.5", "typescript": "^4.8.4",
"unplugin-auto-import": "^0.11.1", "unplugin-auto-import": "^0.11.4",
"unplugin-vue-components": "^0.17.14", "unplugin-vue-components": "^0.22.9",
"vite": "2.8.6", "vite": "2.8.6",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-setup-extend": "^0.4.0", "vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^9.0.3" "vue-eslint-parser": "^9.1.0"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@ -8,14 +8,12 @@
import i18n from '@/locales' import i18n from '@/locales'
import store from '@/store' import store from '@/store'
import config from '@/config' import config from '@/config'
import configApi from '@/api/dev/configApi' import configApi from '@/api/dev/configApi'
import { message } from 'ant-design-vue'
import tool from '@/utils/tool' import tool from '@/utils/tool'
store.commit('initTheme') store.commit('initTheme')
const locale = i18n.global.messages[i18n.global.locale].lang const locale = i18n.global.messages[i18n.global.locale].lang
if (!tool.data.get('SNOWY_SYS_BASE_CONFIG')) {
let formData = ref(config.SYS_BASE_CONFIG) let formData = ref(config.SYS_BASE_CONFIG)
configApi.configSysBaseList().then((data) => { configApi.configSysBaseList().then((data) => {
if (data) { if (data) {
@ -26,4 +24,5 @@
store.commit('SET_sysBaseConfig', formData.value) store.commit('SET_sysBaseConfig', formData.value)
} }
}) })
}
</script> </script>

View File

@ -13,10 +13,8 @@
data() { data() {
return { return {
needTotalList: [], needTotalList: [],
selectedRows: [], selectedRows: [],
selectedRowKeys: [], selectedRowKeys: [],
localLoading: false, localLoading: false,
localDataSource: [], localDataSource: [],
localPagination: Object.assign({}, this.pagination), localPagination: Object.assign({}, this.pagination),
@ -44,7 +42,7 @@
default: 1 default: 1
}, },
size: { size: {
// type: Number, type: String,
default: '10' default: '10'
}, },
showSizeChanger: { showSizeChanger: {
@ -140,7 +138,7 @@
size: this.size, //this.compSize, size// size: this.size, //this.compSize, size//
showSizeChanger: this.showSizeChanger, showSizeChanger: this.showSizeChanger,
showTotal: (total, range) => { showTotal: (total, range) => {
return `${range[0]}-${range[1]}${total}` return `${range[0]}-${range[1]} ${total} `
} }
})) || })) ||
false false
@ -155,14 +153,9 @@
* @param bool Boolean * @param bool Boolean
*/ */
refresh(bool = false) { refresh(bool = false) {
bool && bool && (this.localPagination = Object.assign({}, {
(this.localPagination = Object.assign( current: 1, size: this.size
{}, }))
{
current: 1,
size: this.size
}
))
this.loadData() this.loadData()
}, },
/** /**
@ -173,30 +166,22 @@
*/ */
loadData(pagination, filters, sorter) { loadData(pagination, filters, sorter) {
this.localLoading = true this.localLoading = true
const parameter = Object.assign( const parameter = Object.assign({
{ current: (pagination && pagination.current) ||
current: this.showPagination && this.localPagination.current || this.pageNum,
(pagination && pagination.current) || size: (pagination && pagination.pageSize) ||
(this.showPagination && this.localPagination.current) || this.showPagination && this.localPagination.pageSize || this.pageSize
this.pageNum,
size: (pagination && pagination.pageSize) || (this.showPagination && this.localPagination.size) || this.size
}, },
(sorter && (sorter && sorter.field && {
sorter.field && {
sortField: sorter.field sortField: sorter.field
}) || }) || {},
{}, (sorter && sorter.order && {
(sorter &&
sorter.order && {
sortOrder: sorter.order sortOrder: sorter.order
}) || }) || {}, {
{},
{
...filters ...filters
} }
) )
const result = this.data(parameter) const result = this.data(parameter)
// r.current, r.totalCount, r.data
// eslint-disable-next-line // eslint-disable-next-line
if ( if (
(typeof result === 'object' || typeof result === 'function') && (typeof result === 'object' || typeof result === 'function') &&
@ -213,6 +198,9 @@
current: r.current, // pageNo, // current: r.current, // pageNo, //
total: r.total, // totalRows, // total: r.total, // totalRows, //
showSizeChanger: this.showSizeChanger, showSizeChanger: this.showSizeChanger,
showTotal: (total, range) => {
return `${range[0]}-${range[1]}${total}`
},
size: (pagination && pagination.size) || this.localPagination.size size: (pagination && pagination.size) || this.localPagination.size
})) || })) ||
false false
@ -226,11 +214,14 @@
this.loadData() this.loadData()
return return
} }
// r.totalCount(total) this.showPagination = true current size totalCount current * size
// table // table
try { try {
if (['auto', true].includes(this.showPagination) && r.total <= r.pages * this.localPagination.size) { /*
if ((['auto', true].includes(this.showPagination) && r.total <= (r.pages * this.localPagination.size))) {
this.localPagination.hideOnSinglePage = true
}
*/
if (!this.showPagination) {
this.localPagination.hideOnSinglePage = true this.localPagination.hideOnSinglePage = true
} }
} catch (e) { } catch (e) {
@ -238,8 +229,11 @@
} }
// //
if (this.showPagination === false) { if (this.showPagination === false) {
// datadata //
this.localDataSource = []
if (r instanceof Array) {
this.localDataSource = r this.localDataSource = r
}
} else { } else {
this.localDataSource = r.records this.localDataSource = r.records
} }
@ -321,7 +315,7 @@
const needTotalItems = this.needTotalList.map((item) => { const needTotalItems = this.needTotalList.map((item) => {
return ( return (
<span className="mr-3"> <span className="mr-3">
{item.title}总计{' '} {item.title} 总计 {' '}
<a className="font-6">{!item.customRender ? item.total : item.customRender(item.total)}</a> <a className="font-6">{!item.customRender ? item.total : item.customRender(item.total)}</a>
</span> </span>
) )
@ -400,24 +394,23 @@
} }
// //
const changeRowClass = (val) => { const changeRowClass = (value) => {
const val = value.target.checked
this.localSettings.rowClassNameSwitch = val this.localSettings.rowClassNameSwitch = val
const evenClass = val ? (_record, index) => (index % 2 === 1 ? 'table-striped' : null) : this.rowClassName const evenClass = val ? (_record, index) => (index % 2 === 1 ? 'table-striped' : null) : this.rowClassName
this.localSettings.rowClassName = evenClass this.localSettings.rowClassName = evenClass
} }
return ( return (
<div className="s-table-tool"> <div className="s-table-tool">
<div className="s-table-tool-left">{this.$slots.operator && this.$slots.operator()}</div> <div className="s-table-tool-left">{this.$slots.operator && this.$slots.operator()}</div>
<div className="layout-items-center s-table-tool-right"> <div className="layout-items-center s-table-tool-right">
{this.toolConfig.striped ? ( {this.toolConfig.striped ? (
<div className="layout-items-center ml-4"> <div className="layout-items-center ml-4">
<span>斑马线</span> <a-checkbox
<a-switch
checked={this.localSettings.rowClassNameSwitch} checked={this.localSettings.rowClassNameSwitch}
onChange={changeRowClass} onChange={changeRowClass}
className="ml-2" >斑马纹
/> </a-checkbox>
</div> </div>
) : null} ) : null}
@ -430,7 +423,6 @@
{tool.icon} {tool.icon}
</a-tooltip> </a-tooltip>
) )
if (tool.isPopover) { if (tool.isPopover) {
return ( return (
<a-popover <a-popover

View File

@ -0,0 +1,60 @@
import { mapState, mapMutations } from 'vuex'
import hotkeys from 'hotkeys-js'
export default {
mounted() {
// 绑定搜索功能快捷键 [ 打开 ]
hotkeys(this.searchHotkey.open, (event) => {
event.preventDefault()
this.searchPanelOpen()
})
// 绑定搜索功能快捷键 [ 关闭 ]
hotkeys(this.searchHotkey.close, (event) => {
event.preventDefault()
this.searchPanelClose()
})
},
beforeDestroy() {
hotkeys.unbind(this.searchHotkey.open)
hotkeys.unbind(this.searchHotkey.close)
},
computed: {
...mapState('search', {
searchActive: (state) => state.active,
searchHotkey: (state) => state.hotkey
})
},
methods: {
...mapMutations({
searchToggle: 'search/toggle',
searchSet: 'search/set'
}),
// 接收点击搜索按钮
handleSearchClick() {
this.searchToggle()
if (this.searchActive) {
setTimeout(() => {
if (this.$refs.panelSearch) {
this.$refs.panelSearch.focus()
}
}, 300)
}
},
searchPanelOpen() {
if (!this.searchActive) {
this.searchSet(true)
setTimeout(() => {
if (this.$refs.panelSearch) {
this.$refs.panelSearch.focus()
}
}, 300)
}
},
// 关闭搜索面板
searchPanelClose() {
if (this.searchActive) {
this.searchSet(false)
}
}
}
}

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-if="moduleUnfoldOpen"> <div v-if="moduleUnfoldOpen">
<a-menu v-model:selectedKeys="selectedKeys" mode="horizontal" v-if="menu && menu.length > 1" class="module-menu" id="moduleMunu"> <a-menu v-model:selectedKeys="selectedKeys" mode="horizontal" v-if="menu && menu.length > 1" class="module-menu" id="moduleMunu">
<a-menu-item v-for="item in menu" :key="item.id" style="padding-right: 5px" @click="moduleClick(item.id)"> <a-menu-item v-for="item in menu" :key="item.id" style="padding-right: 5px;position: relative;" @click="moduleClick(item.id)">
<template #icon> <template #icon>
<component :is="item.meta.icon"/> <component :is="item.meta.icon"/>
</template> </template>
@ -101,7 +101,7 @@
.module-card-icon { .module-card-icon {
color: white; color: white;
font-size: 20px; font-size: 20px;
padding-top: 20px; margin-top: 20px;
} }
.module-card-font { .module-card-font {
color: white; color: white;

View File

@ -0,0 +1,274 @@
<template>
<div @keyup.up="handleKeyUp" @keyup.down="handleKeyDown" @keyup.enter="handleKeyEnter" @click.self="handlePanelClick">
<a-input
ref="input"
v-model="searchText"
class="search-box"
style="width: 100%"
allowClear
placeholder="搜索页面(支持拼音检索)"
@change="querySearch"
>
<template #prefix>
<search-outlined />
</template>
</a-input>
<a-card
:body-style="{ padding: '0 0' }"
hoverable
@mouseenter="onCardIn"
@mouseleave="onCardOut"
@keypress.up="handleKeyUp"
@keypress.down="handleKeyDown"
style="margin: 10px 0"
>
<div ref="cardList" class="search-card beauty-scroll">
<a-list size="small" :data-source="resultsList">
<template #renderItem="{ item, index }">
<a-list-item
@click="handleSelect(item.fullPath)"
@mouseover="onCardItemHover(index)"
:class="{ active: index === cardIndex }"
style="padding-right: 10px"
>
<template #actions>
<a>
<enter-outlined />
</a>
</template>
<a-list-item-meta :description="item.fullName">
<template #title>
<a>{{ item.name }}</a>
</template>
<template #avatar>
<a-avatar style="color: var(--text-color); background-color: transparent" :type="item.icon">
<template #icon>
<component :is="item.icon" />
</template>
</a-avatar>
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</div>
</a-card>
<div class="search-tips">
<span class="key">S</span>
<span class="tips">打开搜索面板</span>
<span class="key">
<arrow-up-outlined />
</span>
<span class="key">
<arrow-down-outlined />
</span>
<span class="tips">选择</span>
<span class="key">
<enter-outlined />
</span>
<span class="tips">确认</span>
<span class="key left">Esc</span>
<span class="tips">关闭</span>
</div>
</div>
</template>
<script>
import Fuse from 'fuse.js'
import { mapState } from 'vuex'
export default {
data() {
return {
searchText: '',
cardIndex: 0,
results: []
}
},
computed: {
...mapState('search', ['pool']),
//
resultsList() {
return this.results.length === 0 || this.searchText === '' ? this.pool : this.results
},
// pool fuse
fuse() {
return new Fuse(this.pool, {
shouldSort: true, //
threshold: 0.6, //
location: 0, //
distance: 100, //
minMatchCharLength: 1, //
keys: ['name', 'namePinyin', 'namePinyinFirst']
})
}
},
methods: {
//
querySearch(e) {
let queryString = e.target.value || ''
const results = queryString && this.fuse.search(queryString).map((e) => e.item)
this.searchText = queryString
this.results = results
},
//
focus() {
this.searchText = ''
setTimeout(() => {
if (this.$refs.input) {
this.$refs.input.focus()
}
//
this.searchText = ''
this.results = []
}, 300)
},
handleKeyEnter() {
let idx = this.cardIndex
if (this.resultsList[idx]) {
this.handleSelect(this.resultsList[idx].fullPath)
}
},
handleKeyUp() {
this.handleKeyUpOrDown(true)
},
handleKeyDown() {
this.handleKeyUpOrDown(false)
},
handleKeyUpOrDown(up) {
let len = this.resultsList.length - 1
let idx = this.cardIndex
if (up) {
//
if (idx > 0) {
idx--
} else {
idx = len
}
} else {
//
if (idx < len) {
idx++
} else {
idx = 0
}
}
this.cardIndex = idx
if (this.$refs.cardList.getElementsByClassName('ant-list-item')[idx]) {
this.$refs.cardList.scrollTop = this.$refs.cardList.getElementsByClassName('ant-list-item')[idx].offsetTop
} else {
this.$refs.cardList.scrollTop = 0
}
},
onCardIn() {
this.$refs.input.activated = false
this.$refs.input.blur()
},
onCardOut() {
this.cardIndex = -1
},
onCardItemHover(index) {
this.cardIndex = index
},
//
handleSelect(path) {
//
if (path === this.$route.path) {
this.handleEsc()
return
}
this.$router.push({ path })
this.handleEsc()
},
//
closeSuggestion() {
if (this.$refs.input.activated) {
this.results = []
this.$refs.input.activated = false
}
},
//
handlePanelClick(e) {
if ('INPUT' !== e.target.tagName) {
this.handleEsc()
}
},
//
async handleEsc() {
this.closeSuggestion()
await this.$nextTick()
this.$emit('close')
}
}
}
</script>
<style lang="less" scoped>
:deep(.ant-input){
height: 35px;
}
:deep(.ant-input:not(:first-child)){
padding-left: 10px;
}
:deep(.ant-input-prefix){
font-size: 20px;
}
:deep(.ant-list-sm .ant-list-item){
padding: 4px 16px;
}
:deep(.ant-list-item-meta){
align-items: center;
}
:deep(.ant-list-item.active){
background-color: var(--primary-1);
}
.search-box {
width: 100%;
}
.beauty-scroll {
scrollbar-color: var(--primary-color) var(--primary-2);
scrollbar-width: thin;
-ms-overflow-style: none;
position: relative;
&::-webkit-scrollbar {
width: 3px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background: var(--primary-color);
}
&::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0);
border-radius: 3px;
background: var(--primary-3);
}
}
.search-card {
height: 220px;
overflow: hidden;
overflow-y: scroll;
}
.search-tips {
display: flex;
border-top: 1px solid var(--component-background);
padding-top: 10px;
.tips {
margin-right: 10px;
}
.key {
width: 30px;
height: 20px;
line-height: 20px;
text-align: center;
padding-bottom: 2px;
margin: 0px 4px;
border-radius: 2px;
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
font-weight: bold;
}
}
</style>

View File

@ -0,0 +1,102 @@
<template>
<div class="d2-panel-search-item" :class="hoverMode ? 'can-hover' : ''" flex>
<div class="d2-panel-search-item__icon" flex-box="0">
<div class="d2-panel-search-item__icon-box" flex="main:center cross:center">
<a-icon v-if="item.icon" :type="item.icon" />
<a-icon v-else type="menu" />
</div>
</div>
<div class="d2-panel-search-item__info" flex-box="1" flex="dir:top">
<div class="d2-panel-search-item__info-title" flex-box="1" flex="cross:center">
<span>{{ item.title }}</span>
</div>
<div class="d2-panel-search-item__info-fullTitle" flex-box="0">
<span>{{ item.fullTitle }}</span>
</div>
<div class="d2-panel-search-item__info-path" flex-box="0">
<span>{{ item.path }}</span>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
item: {
default: () => ({})
},
hoverMode: {
default: false
}
}
}
</script>
<style lang="less" scoped>
.d2-panel-search-item {
height: 64px;
margin: 0px -20px;
&.can-hover {
margin: 0px;
&:hover {
background-color: #f5f7fa;
.d2-panel-search-item__icon {
.d2-panel-search-item__icon-box {
i {
font-size: 24px;
color: var(--primary-color);
}
}
}
.d2-panel-search-item__info {
.d2-panel-search-item__info-title {
//color: $color-text-main;
}
.d2-panel-search-item__info-fullTitle {
//color: $color-text-normal;
}
.d2-panel-search-item__info-path {
//color: $color-text-normal;
}
}
}
}
.d2-panel-search-item__icon {
width: 64px;
.d2-panel-search-item__icon-box {
height: 64px;
width: 64px;
border-right: 1px solid #ccc;
i {
font-size: 20px;
//color: $color-text-sub;
}
svg {
height: 20px;
width: 20px;
}
}
}
.d2-panel-search-item__info {
margin-left: 10px;
.d2-panel-search-item__info-title {
font-size: 16px;
line-height: 16px;
font-weight: bold;
//color: $color-text-normal;
}
.d2-panel-search-item__info-fullTitle {
font-size: 10px;
line-height: 14px;
color: grey;
}
.d2-panel-search-item__info-path {
margin-bottom: 4px;
font-size: 10px;
line-height: 14px;
color: grey;
}
}
}
</style>

View File

@ -1,5 +1,8 @@
<template> <template>
<div class="user-bar"> <div class="user-bar">
<div v-if="!ismobile" class="search panel-item hidden-sm-and-down" @click="handleSearchClick">
<search-outlined />
</div>
<div v-if="!ismobile" class="screen panel-item hidden-sm-and-down" @click="fullscreen"> <div v-if="!ismobile" class="screen panel-item hidden-sm-and-down" @click="fullscreen">
<fullscreen-outlined /> <fullscreen-outlined />
</div> </div>
@ -49,6 +52,20 @@
<a-drawer v-model:visible="settingDialog" :closable="false" width="300"> <a-drawer v-model:visible="settingDialog" :closable="false" width="300">
<setting></setting> <setting></setting>
</a-drawer> </a-drawer>
<!-- 搜索面板 -->
<a-modal
:visible="searchActive"
:closable="false"
:footer="null"
width="600px"
style="overflow: hidden"
destroyOnClose
dialogClass="searchModal"
:bodyStyle="{ maxHeight: '520px', overflow: 'auto', padding: '14px' }"
@cancel="searchPanelClose"
>
<panel-search ref="panelSearch" @close="searchPanelClose" />
</a-modal>
</template> </template>
<script> <script>
@ -61,11 +78,15 @@
import tool from '@/utils/tool' import tool from '@/utils/tool'
import loginApi from '@/api/auth/loginApi' import loginApi from '@/api/auth/loginApi'
import devUserMessage from './message.vue' import devUserMessage from './message.vue'
import panelSearch from './panel-search/index.vue'
import mixinSearch from './mixins/search'
export default { export default {
components: { components: {
setting, setting,
devUserMessage devUserMessage,
panelSearch
}, },
mixins: [mixinSearch],
data() { data() {
return { return {
lang: [], lang: [],
@ -171,12 +192,20 @@
if (screenfull.isEnabled) { if (screenfull.isEnabled) {
screenfull.toggle(element) screenfull.toggle(element)
} }
} },
//
fullSearch() {}
} }
} }
</script> </script>
<style scoped> <style lang="less" scoped>
:deep(.ant-modal) {
top: 20px;
}
:deep(.ant-modal-content) {
border-radius: 10px;
}
.user-bar { .user-bar {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -63,13 +63,6 @@
</div> </div>
</div> </div>
</a-layout-content> </a-layout-content>
<!--
<a-layout-footer style="text-align: center">
<a style="color: #a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a>
</a-layout-footer>
-->
</a-layout> </a-layout>
</a-layout> </a-layout>
</template> </template>
@ -164,13 +157,13 @@
</keep-alive> </keep-alive>
</router-view> </router-view>
<iframe-view></iframe-view> <iframe-view></iframe-view>
</div> <div class="main-bottom-wrapper">
</a-layout-content>
<a-layout-footer style="text-align: center">
<a style="color: #a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{ <a style="color: #a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a> }}</a>
</a-layout-footer> </div>
</div>
</a-layout-content>
</a-layout> </a-layout>
</a-layout> </a-layout>
</template> </template>

View File

@ -76,6 +76,7 @@ const handleGetRouter = (to) => {
menuRouter.forEach((item) => { menuRouter.forEach((item) => {
router.addRoute('layout', item) router.addRoute('layout', item)
}) })
store.commit('search/init', menuRouter)
routes_404_r = router.addRoute(routes_404) routes_404_r = router.addRoute(routes_404)
if (to && to.matched.length === 0) { if (to && to.matched.length === 0) {
router.push(to.fullPath) router.push(to.fullPath)

View File

@ -14,12 +14,14 @@ import global from './modules/global'
import iframe from './modules/iframe' import iframe from './modules/iframe'
import keepAlive from './modules/keepAlive' import keepAlive from './modules/keepAlive'
import viewTags from './modules/viewTags' import viewTags from './modules/viewTags'
import search from './modules/search'
// 自动import导入所有 vuex 模块 // 自动import导入所有 vuex 模块
export default createStore({ export default createStore({
modules: { modules: {
global, global,
iframe, iframe,
keepAlive, keepAlive,
viewTags viewTags,
search
} }
}) })

View File

@ -0,0 +1,61 @@
import '@/utils/objects'
export default {
namespaced: true,
state: {
// 搜索面板激活状态
active: false,
// 快捷键
hotkey: {
open: 's',
close: 'esc'
},
// 所有可以搜索的页面
pool: []
},
mutations: {
// 切换激活状态
toggle(state) {
state.active = !state.active
},
// 设置激活模式
set(state, active) {
state.active = active
},
// 初始化
init(state, menu) {
const pool = []
const getFullName = function (meta) {
if (meta.breadcrumb) {
let list = []
meta.breadcrumb.forEach((item) => {
list.push(item.meta.title)
})
return list.join(' / ')
}
return meta.title
}
const push = function (menu) {
menu.forEach((m) => {
if ('menu' === m.meta.type) {
if (m.children) {
push(m.children)
} else if (m.children === null){
pool.push({
icon: m.meta.icon,
path: m.path,
fullPath: m.path,
name: m.meta.title,
fullName: getFullName(m.meta),
namePinyin: m.meta.title.toPinyin(),
namePinyinFirst: m.meta.title.toPinyin(true)
})
}
}
})
}
push(menu)
state.pool = pool
}
}
}

View File

@ -188,9 +188,10 @@ a, button, input, textarea {
} }
.snowy-header-logo { .snowy-header-logo {
height: 49px; height: 50px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
} }
.snowy-header-logo-primary-color { .snowy-header-logo-primary-color {

View File

@ -1,85 +0,0 @@
/**
* Copyright [2022] [https://www.xiaonuo.vip]
* Snowy采用APACHE LICENSE 2.0开源协议您在使用过程中需要注意以下几点
* 1.请不要删除和修改根目录下的LICENSE文件
* 2.请不要删除和修改Snowy源码头部的版权声明
* 3.本项目代码可免费商业使用商业使用请保留源码和相关描述文件的项目出处作者声明等
* 4.分发源码时候请注明软件出处 https://www.xiaonuo.vip
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { mapState } from 'vuex'
import { DEVICE_TYPE, deviceEnquire } from '@/utils/device'
// const mixinsComputed = Vue.config.optionMergeStrategies.computed
// const mixinsMethods = Vue.config.optionMergeStrategies.methods
const mixin = {
computed: {
...mapState({
layoutMode: (state) => state.app.layout,
navTheme: (state) => state.app.theme,
primaryColor: (state) => state.app.color,
colorWeak: (state) => state.app.weak,
fixedHeader: (state) => state.app.fixedHeader,
fixSiderbar: (state) => state.app.fixSiderbar,
fixSidebar: (state) => state.app.fixSiderbar,
contentWidth: (state) => state.app.contentWidth,
autoHideHeader: (state) => state.app.autoHideHeader,
sidebarOpened: (state) => state.app.sidebar,
multiTab: (state) => state.app.multiTab
})
},
methods: {
isTopMenu() {
return this.layoutMode === 'topmenu'
},
isSideMenu() {
return !this.isTopMenu()
}
}
}
const mixinDevice = {
computed: {
...mapState({
device: (state) => state.app.device
})
},
methods: {
isMobile() {
return this.device === DEVICE_TYPE.MOBILE
},
isDesktop() {
return this.device === DEVICE_TYPE.DESKTOP
},
isTablet() {
return this.device === DEVICE_TYPE.TABLET
}
}
}
const AppDeviceEnquire = {
mounted() {
const { $store } = this
deviceEnquire((deviceType) => {
switch (deviceType) {
case DEVICE_TYPE.DESKTOP:
$store.commit('TOGGLE_DEVICE', 'desktop')
$store.dispatch('setSidebar', true)
break
case DEVICE_TYPE.TABLET:
$store.commit('TOGGLE_DEVICE', 'tablet')
$store.dispatch('setSidebar', false)
break
case DEVICE_TYPE.MOBILE:
default:
$store.commit('TOGGLE_DEVICE', 'mobile')
$store.dispatch('setSidebar', true)
break
}
})
}
}
export { mixin, AppDeviceEnquire, mixinDevice }

View File

@ -0,0 +1,29 @@
import pinyin from 'js-pinyin'
// 中文转拼音 传入仅首字母
Object.defineProperty(String.prototype, 'toPinyin', {
writable: false,
enumerable: false,
configurable: true,
value: function (first) {
let str = this
if (first) {
return pinyin.getCamelChars(str).replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, '')
}
return pinyin.getFullChars(str).replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, '')
}
})
// 字符检索 传入检索值
Object.defineProperty(String.prototype, 'filter', {
writable: false,
enumerable: false,
configurable: true,
value: function (input) {
let str = this
let en = str.toLowerCase().includes(input.toLowerCase())
let zhFull = str.toPinyin().toLowerCase().includes(input.toLowerCase())
let zhFirst = str.toPinyin(true).toLowerCase().includes(input.toLowerCase())
return en || zhFull || zhFirst
}
})

View File

@ -8,7 +8,7 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作 * 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip * 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/ */
import generate from '@ant-design/colors/lib/generate' import { generate } from '@ant-design/colors';
import tool from '../utils/tool' import tool from '../utils/tool'
import config from '../config' import config from '../config'
import { ThemeModeEnum } from './enum' import { ThemeModeEnum } from './enum'

View File

@ -262,7 +262,9 @@
const traverse = (array) => { const traverse = (array) => {
array.forEach((element) => { array.forEach((element) => {
if (element.menuType === 'CATALOG') { if (element.menuType === 'CATALOG') {
if (element.children) {
traverse(element.children) traverse(element.children)
}
} else { } else {
// //
element.disabled = true element.disabled = true

View File

@ -165,7 +165,7 @@
const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*') const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
const $link = document.createElement("a"); const $link = document.createElement("a");
$link.href = URL.createObjectURL(blob); $link.href = URL.createObjectURL(blob);
$link.download = patt.exec(contentDisposition)[1] $link.download = decodeURIComponent(patt.exec(contentDisposition)[1])
$link.click(); $link.click();
document.body.appendChild($link); document.body.appendChild($link);
document.body.removeChild($link); // document.body.removeChild($link); //

View File

@ -135,7 +135,7 @@
const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*') const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
const $link = document.createElement("a"); const $link = document.createElement("a");
$link.href = URL.createObjectURL(blob); $link.href = URL.createObjectURL(blob);
$link.download = patt.exec(contentDisposition)[1] $link.download = decodeURIComponent(patt.exec(contentDisposition)[1])
$link.click(); $link.click();
document.body.appendChild($link); document.body.appendChild($link);
document.body.removeChild($link); // document.body.removeChild($link); //

View File

@ -196,12 +196,10 @@
const param = parameterChanges(formData.value) const param = parameterChanges(formData.value)
submitLoading.value = true submitLoading.value = true
menuApi.submitForm(param, !param.id).then(() => { menuApi.submitForm(param, !param.id).then(() => {
submitLoading.value = false onClose()
visible = false
emit('successful') emit('successful')
}) })
}) }).finally(() => {
.catch(() => {
submitLoading.value = false submitLoading.value = false
}) })
} }

View File

@ -16,8 +16,8 @@ import VueJSX from '@vitejs/plugin-vue-jsx'
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import vueSetupExtend from 'vite-plugin-vue-setup-extend' import vueSetupExtend from 'vite-plugin-vue-setup-extend'
import { visualizer } from 'rollup-plugin-visualizer' import { visualizer } from 'rollup-plugin-visualizer'
import { antdDayjs } from 'antd-dayjs-vite-plugin'
import Less2CssVariablePlugin from 'antd-less-to-css-variable' import Less2CssVariablePlugin from 'antd-less-to-css-variable'
import viteCompression from 'vite-plugin-compression'
export const r = (...args) => resolve(__dirname, '.', ...args) export const r = (...args) => resolve(__dirname, '.', ...args)
@ -90,6 +90,7 @@ export default defineConfig(({ command, mode }) => {
refTransform: true refTransform: true
} }
}), }),
viteCompression(),
vueSetupExtend(), vueSetupExtend(),
VueJSX(), VueJSX(),
AutoImport({ AutoImport({
@ -103,7 +104,6 @@ export default defineConfig(({ command, mode }) => {
dts: false, dts: false,
resolvers: [] resolvers: []
}), }),
antdDayjs(),
visualizer() visualizer()
], ],
css: { css: {

View File

@ -60,7 +60,7 @@ public class AuthClientController {
@ApiOperation("C端获取图片验证码") @ApiOperation("C端获取图片验证码")
@GetMapping("/auth/c/getPicCaptcha") @GetMapping("/auth/c/getPicCaptcha")
public CommonResult<AuthPicValidCodeResult> getPicCaptcha() { public CommonResult<AuthPicValidCodeResult> getPicCaptcha() {
return CommonResult.data(authService.getPicCaptcha(SaClientTypeEnum.B.getValue())); return CommonResult.data(authService.getPicCaptcha(SaClientTypeEnum.C.getValue()));
} }
/** /**

View File

@ -408,6 +408,10 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
FileUtil.del(FileUtil.getTmpDirPath() + File.separator + genBasic.getFunctionName() + ".zip"); FileUtil.del(FileUtil.getTmpDirPath() + File.separator + genBasic.getFunctionName() + ".zip");
// 生成临时目录 // 生成临时目录
File tempFolder = FileUtil.file(FileUtil.getTmpDirPath() + File.separator + genBasic.getFunctionName()); File tempFolder = FileUtil.file(FileUtil.getTmpDirPath() + File.separator + genBasic.getFunctionName());
// 生成SQL代码到临时目录
genBasicPreviewResult.getGenBasicCodeSqlResultList().forEach(genBasicCodeResult ->
FileUtil.writeUtf8String(genBasicCodeResult.getCodeFileContent(), FileUtil.file(tempFolder + File.separator +
genBasicCodeResult.getCodeFileWithPathName())));
// 生成前端代码到临时目录 // 生成前端代码到临时目录
genBasicPreviewResult.getGenBasicCodeFrontendResultList().forEach(genBasicCodeResult -> genBasicPreviewResult.getGenBasicCodeFrontendResultList().forEach(genBasicCodeResult ->
FileUtil.writeUtf8String(genBasicCodeResult.getCodeFileContent(), FileUtil.file(tempFolder + File.separator FileUtil.writeUtf8String(genBasicCodeResult.getCodeFileContent(), FileUtil.file(tempFolder + File.separator
@ -427,19 +431,19 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
try { try {
// SQL基础路径 // SQL基础路径
String genSqlBasicPath = "sql"; String genSqlBasicPath = "sql";
// 前端 // SQL
GroupTemplate groupTemplateSql = new GroupTemplate(new ClasspathResourceLoader("sqlend"), GroupTemplate groupTemplateSql = new GroupTemplate(new ClasspathResourceLoader("sqlend"),
Configuration.defaultConfiguration()); Configuration.defaultConfiguration());
List<GenBasicPreviewResult.GenBasicCodeResult> genBasicCodeSqlResultList = CollectionUtil.newArrayList(); List<GenBasicPreviewResult.GenBasicCodeResult> genBasicCodeSqlResultList = CollectionUtil.newArrayList();
GEN_SQL_FILE_LIST.forEach(fileJsonObject -> { GEN_SQL_FILE_LIST.forEach(fileJsonObject -> {
String fileTemplateName = fileJsonObject.getStr("name"); String fileTemplateName = fileJsonObject.getStr("name");
GenBasicPreviewResult.GenBasicCodeResult genBasicCodeSqlResult = new GenBasicPreviewResult.GenBasicCodeResult(); GenBasicPreviewResult.GenBasicCodeResult genBasicCodeSqlResult = new GenBasicPreviewResult.GenBasicCodeResult();
Template templateFront = groupTemplateSql.getTemplate(fileTemplateName); Template templateSql = groupTemplateSql.getTemplate(fileTemplateName);
templateFront.binding(bindingJsonObject); templateSql.binding(bindingJsonObject);
String resultName = StrUtil.removeSuffix(fileTemplateName, ".btl"); String resultName = StrUtil.removeSuffix(fileTemplateName, ".btl");
genBasicCodeSqlResult.setCodeFileName(resultName); genBasicCodeSqlResult.setCodeFileName(resultName);
genBasicCodeSqlResult.setCodeFileWithPathName(genSqlBasicPath + File.separator + resultName); genBasicCodeSqlResult.setCodeFileWithPathName(genSqlBasicPath + File.separator + resultName);
genBasicCodeSqlResult.setCodeFileContent(templateFront.render()); genBasicCodeSqlResult.setCodeFileContent(templateSql.render());
genBasicCodeSqlResultList.add(genBasicCodeSqlResult); genBasicCodeSqlResultList.add(genBasicCodeSqlResult);
}); });
genBasicPreviewResult.setGenBasicCodeSqlResultList(genBasicCodeSqlResultList); genBasicPreviewResult.setGenBasicCodeSqlResultList(genBasicCodeSqlResultList);

View File

@ -19,7 +19,7 @@
<% } else if (configList[i].effectType == 'textarea') {%> <% } else if (configList[i].effectType == 'textarea') {%>
<a-textarea v-model:value="formData.${configList[i].fieldNameCamelCase}" placeholder="请输入${configList[i].fieldRemark}" :auto-size="{ minRows: 3, maxRows: 5 }" /> <a-textarea v-model:value="formData.${configList[i].fieldNameCamelCase}" placeholder="请输入${configList[i].fieldRemark}" :auto-size="{ minRows: 3, maxRows: 5 }" />
<% } else if (configList[i].effectType == 'select') {%> <% } else if (configList[i].effectType == 'select') {%>
<a-select v-model:value="formData.${configList[i].fieldNameCamelCase}" placeholder="请选择${configList[i].fieldRemark}" :options="categoryOptions" /> <a-select v-model:value="formData.${configList[i].fieldNameCamelCase}" placeholder="请选择${configList[i].fieldRemark}" :options="${configList[i].fieldNameCamelCase}Options" />
<% } else if (configList[i].effectType == 'radio') {%> <% } else if (configList[i].effectType == 'radio') {%>
<a-radio-group v-model:value="formData.${configList[i].fieldNameCamelCase}" placeholder="请选择${configList[i].fieldRemark}" :options="formData.${configList[i].fieldNameCamelCase}Options" /> <a-radio-group v-model:value="formData.${configList[i].fieldNameCamelCase}" placeholder="请选择${configList[i].fieldRemark}" :options="formData.${configList[i].fieldNameCamelCase}Options" />
<% } else if (configList[i].effectType == 'checkbox') {%> <% } else if (configList[i].effectType == 'checkbox') {%>

View File

@ -737,7 +737,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
// 更新指定字段 // 更新指定字段
this.update(new LambdaUpdateWrapper<SysUser>().eq(SysUser::getId, sysUser.getId()) this.update(new LambdaUpdateWrapper<SysUser>().eq(SysUser::getId, sysUser.getId())
.set(SysUser::getName, sysUserUpdateInfoParam.getName()) .set(SysUser::getName, sysUserUpdateInfoParam.getName())
.set(SysUser::getPhone, sysUserUpdateInfoParam.getPhone()) .set(SysUser::getPhone, CommonCryptogramUtil.doSm4CbcEncrypt(sysUserUpdateInfoParam.getPhone()))
.set(SysUser::getNickname, sysUserUpdateInfoParam.getNickname()) .set(SysUser::getNickname, sysUserUpdateInfoParam.getNickname())
.set(SysUser::getGender, sysUserUpdateInfoParam.getGender()) .set(SysUser::getGender, sysUserUpdateInfoParam.getGender())
.set(SysUser::getBirthday, sysUserUpdateInfoParam.getBirthday()) .set(SysUser::getBirthday, sysUserUpdateInfoParam.getBirthday())