样式修改
parent
29d808402d
commit
1b0f9368fd
|
|
@ -18,6 +18,7 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
font-size: var(--el-font-size-medium);
|
font-size: var(--el-font-size-medium);
|
||||||
|
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// 优化 Element Plus 组件库默认样式
|
// 优化 Element Plus 组件库默认样式
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--custom-radius: 5px;
|
--custom-radius: 9px;
|
||||||
// 系统主色
|
// 系统主色
|
||||||
--main-color: #1C6EFF;
|
--main-color: #1C6EFF;
|
||||||
--el-color-white: white !important;
|
--el-color-white: white !important;
|
||||||
|
|
@ -11,6 +11,11 @@
|
||||||
--el-button-hover-border-color: #458FFF !important;
|
--el-button-hover-border-color: #458FFF !important;
|
||||||
--el-color-primary-light-3: #458FFF !important;
|
--el-color-primary-light-3: #458FFF !important;
|
||||||
--el-color-danger: #CF171D !important;
|
--el-color-danger: #CF171D !important;
|
||||||
|
--el-menu-text-color: #29343D !important;
|
||||||
|
--el-menu-hover-text-color: #29343D !important;
|
||||||
|
--el-menu-bg-color: #FFFFFF !important;
|
||||||
|
--el-menu-hover-bg-color: rgb(204, 204, 204) !important;
|
||||||
|
--el-menu-level: 0;
|
||||||
// 输入框边框颜色
|
// 输入框边框颜色
|
||||||
// --el-border-color: #E4E4E7 !important; // DCDFE6
|
// --el-border-color: #E4E4E7 !important; // DCDFE6
|
||||||
// 按钮粗度
|
// 按钮粗度
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import Evt from '@/common/utils/evt.ts'
|
import Evt from '@/common/utils/evt.ts'
|
||||||
|
import Nav from '@/common/router/nav.ts'
|
||||||
|
|
||||||
const pageContextCache = new Map<string, AppTypes.PageContext>()
|
const pageContextCache = new Map<string, AppTypes.PageContext>()
|
||||||
|
|
||||||
|
|
@ -12,6 +13,7 @@ initCache()
|
||||||
|
|
||||||
export const useAppPageStore = defineStore('AppPage', () => {
|
export const useAppPageStore = defineStore('AppPage', () => {
|
||||||
const keepAliveInclude = ref<string[]>([])
|
const keepAliveInclude = ref<string[]>([])
|
||||||
|
const pages = ref<AppTypes.PageContext[]>([])
|
||||||
|
|
||||||
const currentPage = ref<string>('')
|
const currentPage = ref<string>('')
|
||||||
|
|
||||||
|
|
@ -27,13 +29,68 @@ export const useAppPageStore = defineStore('AppPage', () => {
|
||||||
if (!keepAliveInclude.value.includes(ctx_.insId)) {
|
if (!keepAliveInclude.value.includes(ctx_.insId)) {
|
||||||
keepAliveInclude.value.push(ctx_.insId)
|
keepAliveInclude.value.push(ctx_.insId)
|
||||||
}
|
}
|
||||||
|
if (!pages.value.find(it => it.insId === ctx_.insId)) {
|
||||||
|
pages.value.push(ctx_)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(insId: string) {
|
function reopen(insId: string) {
|
||||||
|
if (currentPage.value === insId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const page = pageContextCache.get(insId)
|
||||||
|
if (page) {
|
||||||
|
currentPage.value = insId
|
||||||
|
Nav.open(page.routeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePage(insId: string) {
|
||||||
pageContextCache.delete(insId)
|
pageContextCache.delete(insId)
|
||||||
if (keepAliveInclude.value.includes(insId)) {
|
if (keepAliveInclude.value.includes(insId)) {
|
||||||
keepAliveInclude.value = keepAliveInclude.value.splice(keepAliveInclude.value.indexOf(insId), 1)
|
keepAliveInclude.value = keepAliveInclude.value.splice(keepAliveInclude.value.indexOf(insId), 1)
|
||||||
}
|
}
|
||||||
|
const index = pages.value.findIndex(it => it.insId === insId)
|
||||||
|
if (index !== -1) {
|
||||||
|
const oldLen = pages.value.length
|
||||||
|
pages.value = pages.value.filter(it => it.insId !== insId)
|
||||||
|
|
||||||
|
if (currentPage.value !== insId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (index === 0) {
|
||||||
|
if (pages.value.length > 0) {
|
||||||
|
reopen(pages.value[0]?.insId)
|
||||||
|
} else {
|
||||||
|
Nav.open('home')
|
||||||
|
}
|
||||||
|
} else if (index === oldLen - 1) {
|
||||||
|
if (pages.value.length > 0) {
|
||||||
|
reopen(pages.value[pages.value.length - 1]?.insId)
|
||||||
|
} else {
|
||||||
|
Nav.open('home')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pages.value.length > 0) {
|
||||||
|
reopen(pages.value[index]?.insId)
|
||||||
|
} else {
|
||||||
|
Nav.open('home')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeCurrent() {
|
||||||
|
closePage(currentPage.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeOther() {
|
||||||
|
pages.value = pages.value.filter(it => it.insId === currentPage.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAll() {
|
||||||
|
pages.value = []
|
||||||
|
Nav.open('home')
|
||||||
}
|
}
|
||||||
|
|
||||||
function $reset() {
|
function $reset() {
|
||||||
|
|
@ -46,8 +103,14 @@ export const useAppPageStore = defineStore('AppPage', () => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ctx,
|
ctx,
|
||||||
|
reopen,
|
||||||
open,
|
open,
|
||||||
close,
|
closePage,
|
||||||
|
closeCurrent,
|
||||||
|
closeOther,
|
||||||
|
closeAll,
|
||||||
|
pages,
|
||||||
|
currentPage,
|
||||||
keepAliveInclude,
|
keepAliveInclude,
|
||||||
$reset,
|
$reset,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ declare global {
|
||||||
params: Record<string, any>
|
params: Record<string, any>
|
||||||
routeName: string
|
routeName: string
|
||||||
menuId: string
|
menuId: string
|
||||||
|
icon: string
|
||||||
|
breadcrumb: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 菜单
|
// 菜单
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,32 @@ import { useAppSettingStore } from '@/common/app/app-setting-store.ts'
|
||||||
import { useAppUserStore } from '@/common/app/app-user-store.ts'
|
import { useAppUserStore } from '@/common/app/app-user-store.ts'
|
||||||
import Utils from '@/common/utils'
|
import Utils from '@/common/utils'
|
||||||
import type { R } from '@/common/utils/http-util.ts'
|
import type { R } from '@/common/utils/http-util.ts'
|
||||||
|
import { MenuCategory } from '@/common/app/constants.ts'
|
||||||
|
|
||||||
|
const home = {
|
||||||
|
'id': '-1',
|
||||||
|
'sn': 'menus',
|
||||||
|
'pid': '0',
|
||||||
|
'title': '首页',
|
||||||
|
'icon': 'menus',
|
||||||
|
'tier': 1,
|
||||||
|
'breadcrumb': [
|
||||||
|
'首页',
|
||||||
|
],
|
||||||
|
'menuCategory': MenuCategory.Page,
|
||||||
|
'freeze': null,
|
||||||
|
'sort': 0,
|
||||||
|
'routeName': 'home',
|
||||||
|
'path': '/home',
|
||||||
|
}
|
||||||
export const reloadUserInfo = () => {
|
export const reloadUserInfo = () => {
|
||||||
const appSettingStore = useAppSettingStore()
|
const appSettingStore = useAppSettingStore()
|
||||||
const appUserStore = useAppUserStore()
|
const appUserStore = useAppUserStore()
|
||||||
return LoginApi.my()
|
return LoginApi.my()
|
||||||
.then(({data}) => {
|
.then(({data}) => {
|
||||||
const menuTree = Utils.clone(Colls.toTree(data.menus))
|
const menuTree = Utils.clone(Colls.toTree(data.menus))
|
||||||
|
data.menus.unshift(home)
|
||||||
|
menuTree.unshift(home)
|
||||||
appSettingStore.$patch({
|
appSettingStore.$patch({
|
||||||
menus: data.menus, menuTree,
|
menus: data.menus, menuTree,
|
||||||
theme: data.setting?.theme ?? 'light',
|
theme: data.setting?.theme ?? 'light',
|
||||||
|
|
@ -30,30 +49,30 @@ export const reloadUserInfo = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadUserInfo = () => {
|
/* export const loadUserInfo = () => {
|
||||||
const appSettingStore = useAppSettingStore()
|
const appSettingStore = useAppSettingStore()
|
||||||
const appUserStore = useAppUserStore()
|
const appUserStore = useAppUserStore()
|
||||||
return LoginApi.my()
|
return LoginApi.my()
|
||||||
.then(({data}) => {
|
.then(({data}) => {
|
||||||
const menuTree = Utils.clone(Colls.toTree(data.menus))
|
const menuTree = Utils.clone(Colls.toTree(data.menus))
|
||||||
appSettingStore.$patch({
|
appSettingStore.$patch({
|
||||||
menus: data.menus, menuTree,
|
menus: data.menus, menuTree,
|
||||||
theme: data.setting?.theme ?? 'light',
|
theme: data.setting?.theme ?? 'light',
|
||||||
collectedMenus: data.setting?.collectedMenus ?? [],
|
collectedMenus: data.setting?.collectedMenus ?? [],
|
||||||
logo: data.setting?.logo,
|
logo: data.setting?.logo,
|
||||||
language: data.setting?.language ?? 'zh',
|
language: data.setting?.language ?? 'zh',
|
||||||
})
|
})
|
||||||
appUserStore.$patch({
|
appUserStore.$patch({
|
||||||
userId: data.id,
|
userId: data.id,
|
||||||
nickname: data.nickname,
|
nickname: data.nickname,
|
||||||
avatar: data.avatar,
|
avatar: data.avatar,
|
||||||
tenantId: data.tenantId,
|
tenantId: data.tenantId,
|
||||||
tenantName: data.tenantName,
|
tenantName: data.tenantName,
|
||||||
bizObj: data.bizObj,
|
bizObj: data.bizObj,
|
||||||
roles: data.roles,
|
roles: data.roles,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
} */
|
||||||
|
|
||||||
export function hasPermission(resSn?: string) {
|
export function hasPermission(resSn?: string) {
|
||||||
const appSettingStore = useAppSettingStore()
|
const appSettingStore = useAppSettingStore()
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,13 @@ import { useAppUserStore } from '@/common/app/app-user-store.ts'
|
||||||
import { MenuCategory } from '@/common/app/constants.ts'
|
import { MenuCategory } from '@/common/app/constants.ts'
|
||||||
import { appBaseUrl } from '@/common'
|
import { appBaseUrl } from '@/common'
|
||||||
import strings from '@/common/utils/strings.ts'
|
import strings from '@/common/utils/strings.ts'
|
||||||
|
import Strings from '@/common/utils/strings.ts'
|
||||||
import {
|
import {
|
||||||
getRoute,
|
getRoute,
|
||||||
getRoutes,
|
getRoutes,
|
||||||
} from '@/common/router/route-config.ts'
|
} from '@/common/router/route-config.ts'
|
||||||
import { SpecialPage } from '@/common/router/constants.ts'
|
import { SpecialPage } from '@/common/router/constants.ts'
|
||||||
|
import Nav from '@/common/router/nav.ts'
|
||||||
|
|
||||||
function addRoutes(routNames: string[]) {
|
function addRoutes(routNames: string[]) {
|
||||||
if (Colls.isEmpty(routNames)) return
|
if (Colls.isEmpty(routNames)) return
|
||||||
|
|
@ -74,10 +76,17 @@ router.beforeEach((to, from) => {
|
||||||
name: SpecialPage.Home,
|
name: SpecialPage.Home,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
let routeName = router.getRoutes().find((it) => it.path === to.path)?.name as string
|
||||||
replace: true,
|
console.log('reloadRouter11', routeName, Strings.isBlank(routeName))
|
||||||
path: to.fullPath,
|
if (Strings.isBlank(routeName)) {
|
||||||
|
routeName = SpecialPage.Home
|
||||||
|
ElMessage.error('页面不存在222')
|
||||||
}
|
}
|
||||||
|
console.log('reloadRouter', to, router.getRoutes(), routeName)
|
||||||
|
setTimeout(() => {
|
||||||
|
Nav.open(routeName)
|
||||||
|
})
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
@ -113,7 +122,7 @@ export function reloadRouter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Evt.on('login', (_) => {
|
Evt.on('login', (_) => {
|
||||||
router.replace('/')
|
Nav.open(SpecialPage.Home)
|
||||||
})
|
})
|
||||||
|
|
||||||
Evt.on('logout', (_) => {
|
Evt.on('logout', (_) => {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ function open(option: string | Option) {
|
||||||
params: {},
|
params: {},
|
||||||
routeName,
|
routeName,
|
||||||
menuId: menu.id,
|
menuId: menu.id,
|
||||||
|
icon: menu.icon,
|
||||||
|
breadcrumb: menu.breadcrumb,
|
||||||
}
|
}
|
||||||
ctx.insId = ctx.routeName
|
ctx.insId = ctx.routeName
|
||||||
useAppPageStore().open(ctx)
|
useAppPageStore().open(ctx)
|
||||||
|
|
@ -67,6 +69,8 @@ function open(option: string | Option) {
|
||||||
params: {},
|
params: {},
|
||||||
routeName,
|
routeName,
|
||||||
menuId: menu.id,
|
menuId: menu.id,
|
||||||
|
icon: menu.icon,
|
||||||
|
breadcrumb: menu.breadcrumb,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const routeName = option.routeName
|
const routeName = option.routeName
|
||||||
|
|
@ -91,6 +95,8 @@ function open(option: string | Option) {
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
params: option_.params ?? {},
|
params: option_.params ?? {},
|
||||||
menuId: menu.id,
|
menuId: menu.id,
|
||||||
|
icon: menu.icon,
|
||||||
|
breadcrumb: menu.breadcrumb,
|
||||||
}
|
}
|
||||||
ctx.insId = ctx.routeName
|
ctx.insId = ctx.routeName
|
||||||
useAppPageStore().open(ctx)
|
useAppPageStore().open(ctx)
|
||||||
|
|
@ -121,6 +127,8 @@ function open(option: string | Option) {
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
params: option.params ?? {},
|
params: option.params ?? {},
|
||||||
menuId: menu.id,
|
menuId: menu.id,
|
||||||
|
icon: menu.icon,
|
||||||
|
breadcrumb: menu.breadcrumb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.insId = ctx.routeName
|
ctx.insId = ctx.routeName
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<script generic="F extends G.PageParam,TT extends DefaultRow" lang="ts" setup>
|
<script generic="F extends object,TT extends DefaultRow" lang="ts" setup>
|
||||||
import {
|
import {
|
||||||
elIcons,
|
elIcons,
|
||||||
type ElIconType,
|
type ElIconType,
|
||||||
|
|
@ -10,6 +10,7 @@ import Strings from '@/common/utils/strings.ts'
|
||||||
import type { R } from '@/common/utils/http-util.ts'
|
import type { R } from '@/common/utils/http-util.ts'
|
||||||
import type {
|
import type {
|
||||||
TableColumnCtx,
|
TableColumnCtx,
|
||||||
|
TableInstance,
|
||||||
TableProps,
|
TableProps,
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import type { DefaultRow } from 'element-plus/es/components/table/src/table/defaults'
|
import type { DefaultRow } from 'element-plus/es/components/table/src/table/defaults'
|
||||||
|
|
@ -44,17 +45,20 @@ export interface ActionColumnType<T> {
|
||||||
tableActions: TableActionType<T>[]
|
tableActions: TableActionType<T>[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TablePropsType<T extends DefaultRow> = Omit<TableProps<T>, 'data' | 'headerRowClassName' | 'cellClassName' | 'context'>
|
export type TablePropsType<T extends DefaultRow, F extends object> = Omit<TableProps<T>, 'data' | 'headerRowClassName' | 'cellClassName' | 'context'> & {
|
||||||
|
treeLoad?: (param: F, row: T, expanded: any, resolve?: (data: T[]) => void) => void
|
||||||
|
}
|
||||||
export type FormPropsType = Partial<FormProps>
|
export type FormPropsType = Partial<FormProps>
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
defaultSearchForm?: F
|
defaultSearchForm?: F
|
||||||
paging: (param: F) => Promise<R<G.PageResult<TT>>>
|
paging?: (param: F) => Promise<R<G.PageResult<TT>>>
|
||||||
|
list?: (param: F) => Promise<R<TT[]>>
|
||||||
actionColumn?: ActionColumnType<TT>
|
actionColumn?: ActionColumnType<TT>
|
||||||
leftTools?: ToolType[]
|
leftTools?: ToolType[]
|
||||||
rightTools?: Required<Omit<ToolType, 'type' | 'label'>>[]
|
rightTools?: Required<Omit<ToolType, 'type' | 'label'>>[]
|
||||||
tableProps?: TablePropsType<TT>
|
tableProps?: TablePropsType<TT, F>
|
||||||
searchFormProps?: FormPropsType
|
searchFormProps?: FormPropsType
|
||||||
simpleSearchFormProps?: FormPropsType
|
simpleSearchFormProps?: FormPropsType
|
||||||
formStyle?: {
|
formStyle?: {
|
||||||
|
|
@ -100,6 +104,7 @@ const tableData = Utils.resetAble(reactive<TT[]>([])) as ResetAble<TT[]>
|
||||||
const totalCount = ref(0)
|
const totalCount = ref(0)
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
const showSearchForm = ref<boolean>(false)
|
const showSearchForm = ref<boolean>(false)
|
||||||
|
const dataTableIns = useTemplateRef<TableInstance>('dataTable')
|
||||||
|
|
||||||
function doReset() {
|
function doReset() {
|
||||||
searchForm.$reset()
|
searchForm.$reset()
|
||||||
|
|
@ -108,15 +113,30 @@ function doReset() {
|
||||||
|
|
||||||
function doSearch() {
|
function doSearch() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
props.paging(searchForm.$clone() as F)
|
if (props.paging != null) {
|
||||||
.then((res) => {
|
props.paging(searchForm.$clone() as F)
|
||||||
totalCount.value = res.data?.total ?? 0
|
.then((res) => {
|
||||||
const records = res.data?.records ?? ([] as TT[])
|
totalCount.value = res.data?.total ?? 0
|
||||||
tableData.$reset(records)
|
const records = res.data?.records ?? ([] as TT[])
|
||||||
})
|
tableData.$reset(records)
|
||||||
.finally(() => {
|
})
|
||||||
loading.value = false
|
.finally(() => {
|
||||||
})
|
loading.value = false
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (props.list != null) {
|
||||||
|
props.list(searchForm.$clone() as F)
|
||||||
|
.then((res) => {
|
||||||
|
totalCount.value = res.data?.length ?? 0
|
||||||
|
const records = res.data ?? ([] as TT[])
|
||||||
|
tableData.$reset(records)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSearchFormHandle() {
|
function showSearchFormHandle() {
|
||||||
|
|
@ -146,6 +166,7 @@ function rowAction(data: { row: TT, column: TableColumnCtx, $index: number }, ac
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
doSearch,
|
doSearch,
|
||||||
|
dataTableIns,
|
||||||
})
|
})
|
||||||
onMounted(doSearch)
|
onMounted(doSearch)
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -156,7 +177,6 @@ onMounted(doSearch)
|
||||||
<ElScrollbar>
|
<ElScrollbar>
|
||||||
<ElForm :class="{'border-form':formStyle.border}" v-bind="searchFormProps" @submit.prevent="doSearch">
|
<ElForm :class="{'border-form':formStyle.border}" v-bind="searchFormProps" @submit.prevent="doSearch">
|
||||||
<slot :searchForm="searchForm" name="searchFormItem"/>
|
<slot :searchForm="searchForm" name="searchFormItem"/>
|
||||||
|
|
||||||
<ElFormItem class="form-action-btn">
|
<ElFormItem class="form-action-btn">
|
||||||
<ElButton :icon="elIcons.Search" :loading="loading" native-type="submit" type="primary">搜索</ElButton>
|
<ElButton :icon="elIcons.Search" :loading="loading" native-type="submit" type="primary">搜索</ElButton>
|
||||||
<ElButton :icon="elIcons.Refresh" :loading="loading" @click="doReset">重置</ElButton>
|
<ElButton :icon="elIcons.Refresh" :loading="loading" @click="doReset">重置</ElButton>
|
||||||
|
|
@ -165,7 +185,7 @@ onMounted(doSearch)
|
||||||
</ElScrollbar>
|
</ElScrollbar>
|
||||||
</div>
|
</div>
|
||||||
<div class="data-list">
|
<div class="data-list">
|
||||||
<div v-if="!Colls.isEmpty(leftTools) || !Colls.isEmpty(rightTools)" class="tool-bar">
|
<div class="tool-bar">
|
||||||
<div class="tool-bar-left">
|
<div class="tool-bar-left">
|
||||||
<template v-if="!Colls.isEmpty(leftTools)">
|
<template v-if="!Colls.isEmpty(leftTools)">
|
||||||
<ElButton v-for="(tool,i) in leftTools" :key="'tool-bar-left-'+i"
|
<ElButton v-for="(tool,i) in leftTools" :key="'tool-bar-left-'+i"
|
||||||
|
|
@ -209,6 +229,10 @@ onMounted(doSearch)
|
||||||
<ElTable
|
<ElTable
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
|
ref="dataTable"
|
||||||
|
:lazy="tableProps.treeLoad ==null?undefined:true"
|
||||||
|
:load="tableProps.treeLoad?((row, expanded, resolve)=>tableProps.treeLoad!(searchForm as F, row, expanded, resolve)):undefined"
|
||||||
|
@expand-change="tableProps.treeLoad?((row:any, expandedRows:any)=>tableProps.treeLoad!(searchForm as F, row, expandedRows, undefined)):undefined"
|
||||||
cell-class-name="table-cell"
|
cell-class-name="table-cell"
|
||||||
class="table-list"
|
class="table-list"
|
||||||
header-row-class-name="table-header"
|
header-row-class-name="table-header"
|
||||||
|
|
@ -311,9 +335,10 @@ onMounted(doSearch)
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
</ElTable>
|
</ElTable>
|
||||||
<ElPagination
|
<ElPagination
|
||||||
|
v-if="paging!=null"
|
||||||
class="pagination"
|
class="pagination"
|
||||||
v-model:current-page="searchForm.current"
|
v-model:current-page="(searchForm as G.PageParam).current"
|
||||||
v-model:page-size="searchForm.size"
|
v-model:page-size="(searchForm as G.PageParam).size"
|
||||||
:hide-on-single-page="false"
|
:hide-on-single-page="false"
|
||||||
:page-sizes="[10, 20, 50, 100, 500]"
|
:page-sizes="[10, 20, 50, 100, 500]"
|
||||||
:teleported="false"
|
:teleported="false"
|
||||||
|
|
@ -329,7 +354,7 @@ onMounted(doSearch)
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.form-page {
|
.form-page {
|
||||||
.search-form {
|
.search-form {
|
||||||
border: 1px solid #00000014;
|
border: 1px solid #EAEBF1;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
@ -401,7 +426,7 @@ onMounted(doSearch)
|
||||||
flex 1
|
flex 1
|
||||||
display flex
|
display flex
|
||||||
flex-direction column
|
flex-direction column
|
||||||
border: 1px solid #00000014;
|
border: 1px solid #EAEBF1;
|
||||||
padding: 15px 20px 20px 15px;
|
padding: 15px 20px 20px 15px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
width 100%;
|
width 100%;
|
||||||
overflow hidden
|
overflow hidden
|
||||||
padding 5px
|
padding 5px
|
||||||
|
contain: layout paint;
|
||||||
|
transform: translateZ(0);
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
//box-shadow: inset rgba(30, 35, 43, 0.16) 0px 0 10px 1px;
|
//box-shadow: inset rgba(30, 35, 43, 0.16) 0px 0 10px 1px;
|
||||||
//background-color: white;
|
//background-color: white;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
ElAside: typeof import('element-plus/es')['ElAside']
|
ElAside: typeof import('element-plus/es')['ElAside']
|
||||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||||
|
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||||
|
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||||
|
|
@ -32,8 +34,6 @@ declare module 'vue' {
|
||||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
ElIconCircleClose: typeof import('@element-plus/icons-vue')['CircleClose']
|
ElIconCircleClose: typeof import('@element-plus/icons-vue')['CircleClose']
|
||||||
ElIconCirclePlus: typeof import('@element-plus/icons-vue')['CirclePlus']
|
|
||||||
ElIconDelete: typeof import('@element-plus/icons-vue')['Delete']
|
|
||||||
ElIconPicture: typeof import('@element-plus/icons-vue')['Picture']
|
ElIconPicture: typeof import('@element-plus/icons-vue')['Picture']
|
||||||
ElIconPlus: typeof import('@element-plus/icons-vue')['Plus']
|
ElIconPlus: typeof import('@element-plus/icons-vue')['Plus']
|
||||||
ElImage: typeof import('element-plus/es')['ElImage']
|
ElImage: typeof import('element-plus/es')['ElImage']
|
||||||
|
|
@ -71,6 +71,8 @@ declare module 'vue' {
|
||||||
declare global {
|
declare global {
|
||||||
const ElAside: typeof import('element-plus/es')['ElAside']
|
const ElAside: typeof import('element-plus/es')['ElAside']
|
||||||
const ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
const ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||||
|
const ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||||
|
const ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||||
const ElButton: typeof import('element-plus/es')['ElButton']
|
const ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
const ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
const ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||||
const ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
const ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||||
|
|
@ -90,8 +92,6 @@ declare global {
|
||||||
const ElHeader: typeof import('element-plus/es')['ElHeader']
|
const ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||||
const ElIcon: typeof import('element-plus/es')['ElIcon']
|
const ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
const ElIconCircleClose: typeof import('@element-plus/icons-vue')['CircleClose']
|
const ElIconCircleClose: typeof import('@element-plus/icons-vue')['CircleClose']
|
||||||
const ElIconCirclePlus: typeof import('@element-plus/icons-vue')['CirclePlus']
|
|
||||||
const ElIconDelete: typeof import('@element-plus/icons-vue')['Delete']
|
|
||||||
const ElIconPicture: typeof import('@element-plus/icons-vue')['Picture']
|
const ElIconPicture: typeof import('@element-plus/icons-vue')['Picture']
|
||||||
const ElIconPlus: typeof import('@element-plus/icons-vue')['Plus']
|
const ElIconPlus: typeof import('@element-plus/icons-vue')['Plus']
|
||||||
const ElImage: typeof import('element-plus/es')['ElImage']
|
const ElImage: typeof import('element-plus/es')['ElImage']
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import {
|
import {
|
||||||
ElButton,
|
|
||||||
ElIcon,
|
|
||||||
ElMenu,
|
ElMenu,
|
||||||
ElMenuItem,
|
ElMenuItem,
|
||||||
ElMenuItemGroup,
|
ElMenuItemGroup,
|
||||||
|
ElScrollbar,
|
||||||
ElSubMenu,
|
ElSubMenu,
|
||||||
type MenuItemRegistered,
|
type MenuItemRegistered,
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import { elIcons } from '@/common/element/element.ts'
|
|
||||||
import AIcon from '@/components/a-icon/AIcon.vue'
|
import AIcon from '@/components/a-icon/AIcon.vue'
|
||||||
import type { IconName } from '@/components/a-icon/iconfont.ts'
|
import type { IconName } from '@/components/a-icon/iconfont.ts'
|
||||||
import styles from '@/pages/a-frame/aaside.module.styl'
|
import styles from '@/pages/a-frame/aaside.module.styl'
|
||||||
|
|
@ -17,6 +15,7 @@ import { MenuCategory } from '@/common/app/constants.ts'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import Nav from '@/common/router/nav.ts'
|
import Nav from '@/common/router/nav.ts'
|
||||||
|
import logo from '@/assets/images/zsy.png'
|
||||||
|
|
||||||
export interface Menu extends G.TreeNode {
|
export interface Menu extends G.TreeNode {
|
||||||
// Id
|
// Id
|
||||||
|
|
@ -44,11 +43,10 @@ export interface Menu extends G.TreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent(
|
export default defineComponent(
|
||||||
() => {
|
(props) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const appSettingStore = useAppSettingStore()
|
const appSettingStore = useAppSettingStore()
|
||||||
const defaultActive = ref<any>('')
|
const defaultActive = ref<any>('')
|
||||||
const isCollapse = ref(false)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const currentRouteName = router.currentRoute.value.name
|
const currentRouteName = router.currentRoute.value.name
|
||||||
|
|
@ -118,23 +116,27 @@ export default defineComponent(
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
<ElMenu default-active={defaultActive.value} collapse={isCollapse.value} class={[ styles.aMenus ]}>
|
<div class={[ styles.aAsideTop, {[styles.aAsideTopCollapse]: props.isCollapse} ]}>
|
||||||
{{
|
<img alt="" src={logo}/>
|
||||||
default: () => menuTree.value.map(renderMenu),
|
<div>再昇云</div>
|
||||||
}}
|
</div>
|
||||||
</ElMenu>
|
<ElScrollbar class={styles.aScrollbar}>
|
||||||
<ElButton
|
<ElMenu default-active={defaultActive.value} collapse={props.isCollapse} class={[ styles.aMenus ]}>
|
||||||
class={[ styles.aCollapseBtn, {[styles.aCollapseBtnCollapse]: isCollapse.value} ]}
|
{{
|
||||||
onClick={() => {
|
default: () => menuTree.value.map(renderMenu),
|
||||||
isCollapse.value = !isCollapse.value
|
}}
|
||||||
}}
|
</ElMenu>
|
||||||
>
|
</ElScrollbar>
|
||||||
<ElIcon style={{cursor: 'pointer'}}>{isCollapse.value ? <elIcons.Fold/> : <elIcons.Expand/>}</ElIcon>
|
|
||||||
</ElButton>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'AAside',
|
name: 'AAside',
|
||||||
|
props: {
|
||||||
|
isCollapse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import AAside from '@/pages/a-frame/AAside.tsx'
|
import AAside from '@/pages/a-frame/AAside.tsx'
|
||||||
import AAvatar from '@/pages/a-frame/AAvatar.vue'
|
import AAvatar from '@/pages/a-frame/AAvatar.vue'
|
||||||
import { appName } from '@/common'
|
|
||||||
import Evt from '@/common/utils/evt.ts'
|
import Evt from '@/common/utils/evt.ts'
|
||||||
import AIcon from '@/components/a-icon/AIcon.vue'
|
import AIcon from '@/components/a-icon/AIcon.vue'
|
||||||
|
import { useAppPageStore } from '@/common/app/app-page-store.ts'
|
||||||
|
import { elIcons } from '@/common/element/element.ts'
|
||||||
|
import ATabbar from '@/pages/a-frame/ATabbar.vue'
|
||||||
|
|
||||||
|
const appPageStore = useAppPageStore()
|
||||||
|
const isCollapse = ref(false)
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
Evt.emit('connect_ws')
|
Evt.emit('connect_ws')
|
||||||
|
|
@ -16,32 +22,35 @@ onUnmounted(() => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElContainer class="a-frame">
|
<ElContainer class="a-frame">
|
||||||
<ElHeader>
|
<ElAside class="a-frame-aside">
|
||||||
<div>
|
<AAside :is-collapse="isCollapse"/>
|
||||||
<img alt="" src="@/assets/images/zsy.png"/>
|
</ElAside>
|
||||||
<div>{{ appName }}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<ElButton text>
|
|
||||||
<AIcon name="bell"/>
|
|
||||||
</ElButton>
|
|
||||||
<ElButton text>
|
|
||||||
<AIcon name="settings"/>
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
<AAvatar/>
|
|
||||||
</div>
|
|
||||||
</ElHeader>
|
|
||||||
<ElContainer>
|
<ElContainer>
|
||||||
<ElAside>
|
<ElHeader class="a-frame-header">
|
||||||
<ElScrollbar>
|
<div>
|
||||||
<AAside/>
|
<div>
|
||||||
</ElScrollbar>
|
<ElButton :icon="isCollapse?elIcons.Fold:elIcons.Expand" text @click="isCollapse = !isCollapse"/>
|
||||||
</ElAside>
|
<ElBreadcrumb :separator-icon="elIcons.ArrowRight">
|
||||||
<ElMain>
|
<ElBreadcrumbItem v-for="(item, i) in appPageStore?.ctx?.breadcrumb??[]" :key="'a-frame-header-breadcrumb'+i">{{ item }}</ElBreadcrumbItem>
|
||||||
|
</ElBreadcrumb>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<ElButton text>
|
||||||
|
<AIcon name="bell"/>
|
||||||
|
</ElButton>
|
||||||
|
<ElButton text>
|
||||||
|
<AIcon name="settings"/>
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<AAvatar/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ATabbar/>
|
||||||
|
</ElHeader>
|
||||||
|
<ElMain class="a-frame-main">
|
||||||
<RouterView #="{ Component }">
|
<RouterView #="{ Component }">
|
||||||
<Transition name="el-fade-in-linear">
|
<Transition name="slide-fade">
|
||||||
<component :is="Component"/>
|
<component :is="Component"/>
|
||||||
</Transition>
|
</Transition>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
|
|
@ -55,74 +64,135 @@ onUnmounted(() => {
|
||||||
height 100%
|
height 100%
|
||||||
width 100%;
|
width 100%;
|
||||||
overflow hidden
|
overflow hidden
|
||||||
box-shadow: inset rgba(0, 0, 0, 0.12) 0px 0px 12px 0px;
|
box-shadow: inset #0000001F 0 0 12px 0;
|
||||||
background-color #F7F9FC
|
background-color #F7F9FC
|
||||||
|
|
||||||
& > header {
|
.a-frame-aside {
|
||||||
display flex
|
height 100%;
|
||||||
justify-content space-between
|
width auto
|
||||||
border-bottom 1px solid #E5E7EB;
|
position relative
|
||||||
height 60px
|
border-right: solid 1px #EAEBF1;
|
||||||
|
//border-right: solid 1px var(--el-menu-border-color);
|
||||||
background-color white
|
background-color white
|
||||||
|
box-sizing border-box
|
||||||
& > div:first-child {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-sizing border-box
|
|
||||||
|
|
||||||
img {
|
|
||||||
height 50%
|
|
||||||
margin-right 12px
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
color: #165DFF;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div:nth-child(2) {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-sizing border-box
|
|
||||||
gap 20px
|
|
||||||
|
|
||||||
& > div:first-child {
|
|
||||||
display flex
|
|
||||||
gap 10px
|
|
||||||
|
|
||||||
& > button {
|
|
||||||
margin 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& > section {
|
& > section {
|
||||||
width 100%;
|
height 100%
|
||||||
height calc(100% - 60px)
|
|
||||||
|
|
||||||
& > aside {
|
.a-frame-header {
|
||||||
height 100%;
|
height 100px
|
||||||
width auto
|
padding: 0 10px 0 0
|
||||||
position relative
|
|
||||||
border-right: solid 1px var(--el-menu-border-color);
|
|
||||||
|
|
||||||
background-color white
|
background-color white
|
||||||
padding: 10px 0;
|
|
||||||
|
& > div:nth-child(1) {
|
||||||
|
height 60px
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
display flex
|
||||||
|
justify-content space-between
|
||||||
|
box-sizing border-box
|
||||||
|
|
||||||
|
& > div:nth-child(1) {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing border-box
|
||||||
|
|
||||||
|
:deep(.el-breadcrumb) {
|
||||||
|
.el-breadcrumb__inner {
|
||||||
|
color #7987A1
|
||||||
|
cursor default
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-breadcrumb__separator {
|
||||||
|
color #7987A1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div:nth-child(2) {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing border-box
|
||||||
|
gap 20px
|
||||||
|
|
||||||
|
& > div:first-child {
|
||||||
|
display flex
|
||||||
|
gap 10px
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
margin 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& > main {
|
.a-frame-main {
|
||||||
height 100%
|
height calc(100% - 60px)
|
||||||
width calc(100% - 300px)
|
width 100%;
|
||||||
padding 0
|
padding 0
|
||||||
overflow auto
|
overflow hidden
|
||||||
//background-color #F7F9FC
|
|
||||||
background-color rgb(250, 251, 252)
|
background-color rgb(250, 251, 252)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*.slide-fade-enter-active {
|
||||||
|
transition:
|
||||||
|
transform 0.4s cubic-bezier(0.25, 0.1, 0.25, 1),
|
||||||
|
opacity 0.4s ease-out;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-leave-active {
|
||||||
|
transition:
|
||||||
|
transform 0.3s cubic-bezier(0.5, 0, 0.2, 1),
|
||||||
|
opacity 0.3s ease-in;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-from,
|
||||||
|
.slide-fade-leave-to {
|
||||||
|
transform: scaleY(0);
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-to,
|
||||||
|
.slide-fade-leave-from {
|
||||||
|
transform: scaleY(1);
|
||||||
|
opacity: 1;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.slide-fade-enter-active {
|
||||||
|
//transition: transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1),
|
||||||
|
// opacity 0.3s ease-out;
|
||||||
|
transition: transform 0.3s ease-in-out,
|
||||||
|
opacity 0.3s ease-in-out;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-leave-active {
|
||||||
|
//transition: transform 0.3s cubic-bezier(0.5, 0, 0.2, 1),
|
||||||
|
// opacity 0.3s ease-in;
|
||||||
|
transition: transform 0.3s cubic-bezier(1, 0.5, 0.8, 1),
|
||||||
|
opacity 0.3s ease-in-out;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-from,
|
||||||
|
.slide-fade-leave-to {
|
||||||
|
transform: translateX(20px);
|
||||||
|
opacity: 0;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
<template>
|
||||||
|
<div class="a-tabs">
|
||||||
|
<ElScrollbar class="a-tabs-scrollbar">
|
||||||
|
<div class="a-tabs-wrapper">
|
||||||
|
<div v-for="(item,i) in appPageStore.pages"
|
||||||
|
:key="'a-frame-header-tab'+i"
|
||||||
|
:class="{'a-tab-item-active': item.insId === appPageStore.currentPage}"
|
||||||
|
class="a-tab-item"
|
||||||
|
@click="reopen(item.insId)">
|
||||||
|
<div>
|
||||||
|
<AIcon :name="item.icon as IconName"/>
|
||||||
|
<div class="title">{{ item.title }}</div>
|
||||||
|
</div>
|
||||||
|
<ElButton :icon="elIcons.Close" text @click.stop="appPageStore.closePage(item.insId)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ElScrollbar>
|
||||||
|
<ElDropdown placement="bottom" @command="handleCommand">
|
||||||
|
<ElButton :icon="elIcons.More" text/>
|
||||||
|
<template #dropdown>
|
||||||
|
<ElDropdownMenu>
|
||||||
|
<ElDropdownItem command="closeCurrent">关闭当前</ElDropdownItem>
|
||||||
|
<ElDropdownItem command="closeOther">关闭其他</ElDropdownItem>
|
||||||
|
<ElDropdownItem command="closeAll">关闭所有</ElDropdownItem>
|
||||||
|
</ElDropdownMenu>
|
||||||
|
</template>
|
||||||
|
</ElDropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useAppPageStore } from '@/common/app/app-page-store.ts'
|
||||||
|
import { elIcons } from '@/common/element/element.ts'
|
||||||
|
import type { IconName } from '@/components/a-icon/iconfont.ts'
|
||||||
|
import AIcon from '@/components/a-icon/AIcon.vue'
|
||||||
|
|
||||||
|
const appPageStore = useAppPageStore()
|
||||||
|
|
||||||
|
function reopen(insId: string) {
|
||||||
|
appPageStore.reopen(insId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCommand(command: 'closeCurrent' | 'closeOther' | 'closeAll') {
|
||||||
|
appPageStore[command]()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.a-tabs {
|
||||||
|
height 32px
|
||||||
|
width 100%;
|
||||||
|
box-sizing border-box
|
||||||
|
border-top 1px solid #EAEBF1;
|
||||||
|
border-bottom 1px solid #EAEBF1;
|
||||||
|
display flex
|
||||||
|
justify-content space-between
|
||||||
|
align-items center
|
||||||
|
color #303133
|
||||||
|
|
||||||
|
:deep(.el-dropdown) {
|
||||||
|
width: 32px;
|
||||||
|
padding: 0;
|
||||||
|
margin-right 20px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.a-tabs-scrollbar {
|
||||||
|
height 100%
|
||||||
|
width calc(100% - 52px);
|
||||||
|
box-sizing border-box
|
||||||
|
|
||||||
|
:deep(.el-scrollbar__wrap) {
|
||||||
|
height 100%
|
||||||
|
box-sizing border-box
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-scrollbar__view) {
|
||||||
|
height 100%
|
||||||
|
box-sizing border-box
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.a-tabs-wrapper {
|
||||||
|
height 100%
|
||||||
|
width: fit-content;
|
||||||
|
display flex
|
||||||
|
box-sizing border-box
|
||||||
|
align-items center
|
||||||
|
|
||||||
|
.a-tab-item {
|
||||||
|
height 100%
|
||||||
|
max-width 160px
|
||||||
|
padding 5px 10px
|
||||||
|
cursor pointer
|
||||||
|
display flex
|
||||||
|
justify-content space-between
|
||||||
|
gap 20px
|
||||||
|
align-items center
|
||||||
|
box-sizing border-box
|
||||||
|
position relative
|
||||||
|
border-right 1px solid #EAEBF1
|
||||||
|
transition color 0.2s ease-in-out
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
|
& > div:first-child {
|
||||||
|
display flex
|
||||||
|
gap 10px
|
||||||
|
align-items center
|
||||||
|
}
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
padding 0
|
||||||
|
width 14px
|
||||||
|
height 14px !important
|
||||||
|
line-height 14px
|
||||||
|
border-radius 50%
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color var(--el-color-danger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content ''
|
||||||
|
height 2px
|
||||||
|
width 0
|
||||||
|
background-color var(--main-color)
|
||||||
|
position absolute
|
||||||
|
bottom 0
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
transition width 0.2s ease-in-out
|
||||||
|
}
|
||||||
|
|
||||||
|
&.a-tab-item-active {
|
||||||
|
color var(--main-color)
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width 100%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color var(--main-color)
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width 100%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,3 +1,55 @@
|
||||||
|
.a-aside-top {
|
||||||
|
height 60px
|
||||||
|
width 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing border-box
|
||||||
|
//padding 8px 8px 8px 18px;
|
||||||
|
padding-left 18px
|
||||||
|
//border-bottom: solid 1px var(--el-menu-border-color);
|
||||||
|
transition all 0.333s ease-in-out
|
||||||
|
gap 12px
|
||||||
|
overflow hidden
|
||||||
|
max-width 230px
|
||||||
|
//justify-content center
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
height 32px
|
||||||
|
width 32px
|
||||||
|
//margin-left 18px
|
||||||
|
//transition margin-left 0.333s ease-in-out
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
color: #252f4a;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
opacity 1
|
||||||
|
width 168px;
|
||||||
|
overflow hidden
|
||||||
|
transition all 0.333s ease-in-out
|
||||||
|
text-wrap nowrap
|
||||||
|
letter-spacing 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
&.a-aside-top-collapse {
|
||||||
|
width 60px;
|
||||||
|
padding-left 14px
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
opacity 0
|
||||||
|
width 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.a-scrollbar {
|
||||||
|
height calc(100% - 60px)
|
||||||
|
width 100%;
|
||||||
|
padding: 0 0 10px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.a-menus {
|
.a-menus {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
--el-menu-base-level-padding: 10px;
|
--el-menu-base-level-padding: 10px;
|
||||||
|
|
@ -65,7 +117,7 @@
|
||||||
bottom: 6px;
|
bottom: 6px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
transition right 0.3s ease-in-out;
|
transition right 0.333s ease-in-out;
|
||||||
|
|
||||||
&.a-collapse-btn-collapse {
|
&.a-collapse-btn-collapse {
|
||||||
right: calc(50% - 16px);
|
right: calc(50% - 16px);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<div class="home">
|
||||||
|
<div class="home-top">
|
||||||
|
<div class="home-top-title">
|
||||||
|
首页
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import Strings from '@/common/utils/strings.ts'
|
import Strings from '@/common/utils/strings.ts'
|
||||||
import FormUtil from '@/common/utils/formUtil.ts'
|
import FormUtil from '@/common/utils/formUtil.ts'
|
||||||
import { loadUserInfo } from '@/common/app'
|
import { reloadUserInfo } from '@/common/app'
|
||||||
|
|
||||||
const loginFormIns = useTemplateRef<FormInstance>('loginFormRef')
|
const loginFormIns = useTemplateRef<FormInstance>('loginFormRef')
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ function loginSubmitHandler() {
|
||||||
() => LoginApi.login(loginForm))
|
() => LoginApi.login(loginForm))
|
||||||
.then(({data}) => {
|
.then(({data}) => {
|
||||||
appUserStore.$patch({token: data.token})
|
appUserStore.$patch({token: data.token})
|
||||||
return loadUserInfo()
|
return reloadUserInfo()
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Evt.emit('login')
|
Evt.emit('login')
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<FormPage
|
||||||
<ElForm v-show="showSearchForm" inline @submit.prevent="listAll">
|
ref="formPage"
|
||||||
<ElFormItem label-width="90" label="菜单名称">
|
:action-column="actionColumn"
|
||||||
|
:left-tools="leftTools"
|
||||||
|
:list="listAll"
|
||||||
|
:table-props="{
|
||||||
|
treeProps: { children: 'children', hasChildren: 'hasChildren' },
|
||||||
|
treeLoad: treeLoad,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #searchFormItem="{ searchForm }">
|
||||||
|
<ElFormItem label="菜单名称">
|
||||||
<ElInput v-model="searchForm.title" placeholder="菜单名称"/>
|
<ElInput v-model="searchForm.title" placeholder="菜单名称"/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem label-width="90" label="路由名称">
|
<ElFormItem label="路由名称">
|
||||||
<ElInput v-model="searchForm.routeName" placeholder="路由名称"/>
|
<ElInput v-model="searchForm.routeName" placeholder="路由名称"/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem>
|
</template>
|
||||||
<ElButton :icon="elIcons.Search" :loading="searching" native-type="submit" type="primary">搜索</ElButton>
|
<template #columns>
|
||||||
<ElButton :icon="elIcons.Refresh" :loading="searching" @click="reset">重置</ElButton>
|
|
||||||
</ElFormItem>
|
<!--
|
||||||
</ElForm>
|
:data="tableData"
|
||||||
<div class="tool-bar">
|
lazy
|
||||||
<ElButton :icon="elIcons.Plus" type="primary" @click="addHandler">新建</ElButton>
|
:load="treeLoad"
|
||||||
<ElButton :icon="elIcons.Filter" type="default" @click="showSearchForm = !showSearchForm"/>
|
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||||
</div>
|
@expand-change="treeLoad"
|
||||||
<ElTable v-loading="searching"
|
-->
|
||||||
:data="tableData"
|
|
||||||
lazy
|
|
||||||
:load="treeLoad"
|
|
||||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
|
||||||
@expand-change="treeLoad"
|
|
||||||
cell-class-name="table-cell"
|
|
||||||
class="table-list"
|
|
||||||
empty-text="暂无数据"
|
|
||||||
header-row-class-name="table-header"
|
|
||||||
ref="dataTable"
|
|
||||||
row-key="id">
|
|
||||||
<!-- <ElTableColumn type="expand" width="60"/> -->
|
|
||||||
<ElTableColumn label="图标" prop="icon" width="100">
|
<ElTableColumn label="图标" prop="icon" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<AIcon :name="scope.row.icon"/>
|
<AIcon :name="scope.row.icon"/>
|
||||||
|
|
@ -37,7 +34,6 @@
|
||||||
<ElTableColumn label="类型" prop="menuCategoryTxt" width="140"/>
|
<ElTableColumn label="类型" prop="menuCategoryTxt" width="140"/>
|
||||||
<ElTableColumn label="菜单名称" prop="title"/>
|
<ElTableColumn label="菜单名称" prop="title"/>
|
||||||
<ElTableColumn label="编码" prop="sn" width="180"/>
|
<ElTableColumn label="编码" prop="sn" width="180"/>
|
||||||
<!-- <ElTableColumn label="路径" prop="breadcrumb"/> -->
|
|
||||||
<ElTableColumn label="路由名称" prop="routeName">
|
<ElTableColumn label="路由名称" prop="routeName">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -52,102 +48,87 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
<!-- <ElTableColumn label="排序" prop="sort" width="60"/> -->
|
|
||||||
<ElTableColumn label="操作" width="180">
|
|
||||||
<template #default="scope">
|
|
||||||
<ElPopconfirm
|
|
||||||
cancel-button-text="否"
|
|
||||||
cancel-button-type="primary"
|
|
||||||
confirm-button-text="是"
|
|
||||||
confirm-button-type="danger"
|
|
||||||
placement="top"
|
|
||||||
title="是否删除当前数据?"
|
|
||||||
width="180"
|
|
||||||
@confirm="delHandler(scope)">
|
|
||||||
<template #reference>
|
|
||||||
<ElButton :loading="deling" text type="danger">删除</ElButton>
|
|
||||||
</template>
|
|
||||||
</ElPopconfirm>
|
|
||||||
<ElButton text type="primary" @click="modifyHandler(scope)">修改</ElButton>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
</ElTable>
|
|
||||||
|
|
||||||
<MenuForm ref="menuForm" @editSucc="editSuccHandler"/>
|
</template>
|
||||||
|
|
||||||
</Page>
|
<MenuForm ref="menuForm" @editSucc="research"/>
|
||||||
|
|
||||||
|
</FormPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import MenuApi from '@/pages/sys/menus/menu-api.ts'
|
import MenuApi from '@/pages/sys/menus/menu-api.ts'
|
||||||
import Page from '@/components/page/Page.vue'
|
|
||||||
import { onMounted } from 'vue'
|
|
||||||
import { elIcons } from '@/common/element/element.ts'
|
|
||||||
import MenuForm from '@/pages/sys/menus/MenuForm.vue'
|
import MenuForm from '@/pages/sys/menus/MenuForm.vue'
|
||||||
import Strings from '@/common/utils/strings.ts'
|
import Strings from '@/common/utils/strings.ts'
|
||||||
import AIcon from '@/components/a-icon/AIcon.vue'
|
import AIcon from '@/components/a-icon/AIcon.vue'
|
||||||
import type { TableInstance } from 'element-plus'
|
import type { TableInstance } from 'element-plus'
|
||||||
|
import FormPage, {
|
||||||
|
type ActionColumnType,
|
||||||
|
type ToolType,
|
||||||
|
} from '@/components/page/FormPage.vue'
|
||||||
|
import type { ComponentExposed } from 'vue-component-type-helpers'
|
||||||
|
|
||||||
const tableData = ref<MenuTypes.SysMenu[]>([])
|
|
||||||
const searchForm = ref<MenuTypes.SearchForm>({})
|
|
||||||
const searching = ref(false)
|
|
||||||
const showSearchForm = ref(true)
|
|
||||||
const menuFormIns = useTemplateRef<InstanceType<typeof MenuForm>>('menuForm')
|
const menuFormIns = useTemplateRef<InstanceType<typeof MenuForm>>('menuForm')
|
||||||
const dataTableIns = useTemplateRef<TableInstance>('dataTable')
|
const dataTableIns = useTemplateRef<TableInstance>('dataTable')
|
||||||
const deling = ref(false)
|
const formPageIns = useTemplateRef<ComponentExposed<typeof FormPage>>('formPage')
|
||||||
|
|
||||||
function showDialog(data?: MenuTypes.MenuForm) {
|
const actionColumn = reactive<ActionColumnType<MenuTypes.SysMenu>>({
|
||||||
menuFormIns.value?.open(data)
|
tableActions: [
|
||||||
|
{
|
||||||
|
tooltip: '编辑',
|
||||||
|
icon: 'Edit',
|
||||||
|
action({row}) {
|
||||||
|
menuFormIns.value?.open(row as MenuTypes.MenuForm)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'Delete',
|
||||||
|
loading: false,
|
||||||
|
type: 'danger',
|
||||||
|
tooltip: '删除',
|
||||||
|
confirm: {
|
||||||
|
title: '是否删除当前数据',
|
||||||
|
},
|
||||||
|
action({row}) {
|
||||||
|
return MenuApi.del([ row.id! ])
|
||||||
|
.then(() => {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
const leftTools: ToolType[] = [
|
||||||
|
{
|
||||||
|
icon: 'Plus',
|
||||||
|
label: '新建',
|
||||||
|
action() {
|
||||||
|
menuFormIns.value?.open()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function research() {
|
||||||
|
formPageIns.value?.doSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
function delHandler({row}: { row: MenuTypes.MenuForm, }) {
|
function listAll(param: MenuTypes.SearchForm) {
|
||||||
deling.value = true
|
return MenuApi.listAll({...param, pid: '0'})
|
||||||
|
|
||||||
MenuApi.del([ row.id! ])
|
|
||||||
.then(() => {
|
|
||||||
ElMessage.success('删除成功')
|
|
||||||
listAll()
|
|
||||||
})
|
|
||||||
.finally(() => deling.value = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
function modifyHandler({row}: { row: MenuTypes.MenuForm, }) {
|
|
||||||
showDialog(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addHandler() {
|
|
||||||
showDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
searchForm.value = {}
|
|
||||||
listAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
function editSuccHandler() {
|
|
||||||
listAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
function listAll() {
|
|
||||||
searching.value = true
|
|
||||||
MenuApi.listAll({...searchForm.value, pid: '0'})
|
|
||||||
.then(res => {
|
.then(res => {
|
||||||
tableData.value = []
|
const data = res.data ?? []
|
||||||
tableData.value = res.data?.map(it => {
|
for (let it of data) {
|
||||||
it.hasChildren = true
|
it.hasChildren = true
|
||||||
it.children = []
|
it.children = []
|
||||||
return it
|
}
|
||||||
}) ?? []
|
return res
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
searching.value = false
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function treeLoad(row: MenuTypes.SysMenu, expanded: any, resolve: (data: MenuTypes.SysMenu[]) => void) {
|
function treeLoad(param: MenuTypes.SearchForm, row: MenuTypes.SysMenu, expanded: any, resolve: (data: MenuTypes.SysMenu[]) => void) {
|
||||||
if (resolve == null && !expanded) return
|
if (resolve == null && !expanded) return
|
||||||
searching.value = true
|
MenuApi.listAll({...param, pid: row.id})
|
||||||
MenuApi.listAll({...searchForm.value, pid: row.id})
|
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (resolve != null) {
|
if (resolve != null) {
|
||||||
resolve(res.data?.map(it => {
|
resolve(res.data?.map(it => {
|
||||||
|
|
@ -155,64 +136,14 @@ function treeLoad(row: MenuTypes.SysMenu, expanded: any, resolve: (data: MenuTyp
|
||||||
return it
|
return it
|
||||||
}) ?? [])
|
}) ?? [])
|
||||||
} else {
|
} else {
|
||||||
dataTableIns.value?.updateKeyChildren(row.id, res.data?.map(it => {
|
formPageIns.value?.dataTableIns?.updateKeyChildren(row.id, res.data?.map(it => {
|
||||||
it.hasChildren = true
|
it.hasChildren = true
|
||||||
it.children = []
|
it.children = []
|
||||||
return it
|
return it
|
||||||
}) ?? [])
|
}) ?? [])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
|
||||||
searching.value = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
listAll()
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.table-list {
|
|
||||||
flex 1;
|
|
||||||
width 100%
|
|
||||||
|
|
||||||
:deep(.table-header) {
|
|
||||||
color #454C59
|
|
||||||
|
|
||||||
th {
|
|
||||||
background-color #EDF1F7
|
|
||||||
font-weight 500
|
|
||||||
position relative
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
display flex
|
|
||||||
gap 5px
|
|
||||||
align-items center
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:first-child) > div::before {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 1px;
|
|
||||||
width: 1px;
|
|
||||||
background-color: #D3D7DE;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
content: "";
|
|
||||||
height 50%
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.table-cell) {
|
|
||||||
color #2F3540
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-bar {
|
|
||||||
display flex
|
|
||||||
justify-content space-between
|
|
||||||
margin 0 0 20px 0
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue