mirror of https://gitee.com/xiaonuobase/snowy
commit
6634bdf0e6
|
@ -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%",
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
// 因为按住小诺的套路,不分页的直接是在data中,我们在界面中直接就是返回了data
|
// 既然配置了不分页,那么我们这里接收到肯定是数组
|
||||||
|
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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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 }
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
})
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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); // 下载完成移除元素
|
||||||
|
|
|
@ -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); // 下载完成移除元素
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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') {%>
|
||||||
|
|
|
@ -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())
|
||||||
|
|
Loading…
Reference in New Issue