@mindbase/vue3-app-shell 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/composables/index.ts +3 -0
- package/composables/useBreakpoint.ts +64 -0
- package/composables/useDrawer.ts +66 -0
- package/composables/useTouchGesture.ts +165 -0
- package/createAppShell.ts +82 -0
- package/index.ts +20 -0
- package/layout/AppShellLayout.vue +41 -0
- package/layout/components/AppDrawer.vue +87 -0
- package/layout/components/AppHeader.vue +108 -0
- package/layout/components/AppMain.vue +67 -0
- package/layout/components/PWAInstallPrompt.vue +114 -0
- package/layout/index.ts +5 -0
- package/package.json +42 -0
- package/pwa/index.ts +3 -0
- package/pwa/manifest.ts +92 -0
- package/pwa/types.ts +4 -0
- package/pwa/utils.ts +108 -0
- package/store/index.ts +3 -0
- package/store/layout.ts +69 -0
- package/store/pwa.ts +141 -0
- package/store/slots.ts +68 -0
- package/styles/drawer.scss +62 -0
- package/styles/index.scss +5 -0
- package/styles/mobile.scss +58 -0
- package/styles/responsive.scss +33 -0
- package/styles/variables.scss +44 -0
- package/types/app.ts +41 -0
- package/types/index.ts +4 -0
- package/types/layout.ts +28 -0
- package/types/pwa.ts +53 -0
- package/types/slots.ts +31 -0
package/store/pwa.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
import { getStorage, setStorage } from '@mindbase/vue3-kit/utils/storage'
|
|
4
|
+
import type { PWAInstallState, PWAInstallConfig } from '../types'
|
|
5
|
+
|
|
6
|
+
const STORAGE_KEY = 'pwa_install_dont_show_again'
|
|
7
|
+
const DEFAULT_CONFIG: PWAInstallConfig = {
|
|
8
|
+
title: '安装应用',
|
|
9
|
+
description: '添加到主屏幕,获得更好的体验',
|
|
10
|
+
installButtonText: '安装',
|
|
11
|
+
cancelButtonText: '暂不安装',
|
|
12
|
+
dontShowAgainText: '不再提示',
|
|
13
|
+
autoShow: true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* App Shell PWA 状态管理
|
|
18
|
+
*/
|
|
19
|
+
export const useAppShellPWAStore = defineStore('appShellPWA', () => {
|
|
20
|
+
// 配置
|
|
21
|
+
const config = ref<PWAInstallConfig>(DEFAULT_CONFIG)
|
|
22
|
+
|
|
23
|
+
// 状态
|
|
24
|
+
const canInstall = ref(false)
|
|
25
|
+
const isInstalled = ref(false)
|
|
26
|
+
const promptVisible = ref(false)
|
|
27
|
+
const deferredPrompt = ref<Event | null>(null)
|
|
28
|
+
|
|
29
|
+
// "不再提示"状态(从 storage 读取)
|
|
30
|
+
const dontShowAgain = ref(getStorage<boolean>(STORAGE_KEY) ?? false)
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 设置 PWA 配置
|
|
34
|
+
*/
|
|
35
|
+
function setConfig(newConfig: Partial<PWAInstallConfig>) {
|
|
36
|
+
config.value = { ...DEFAULT_CONFIG, ...newConfig }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 设置可安装状态
|
|
41
|
+
*/
|
|
42
|
+
function setCanInstall(value: boolean) {
|
|
43
|
+
canInstall.value = value
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 设置已安装状态
|
|
48
|
+
*/
|
|
49
|
+
function setIsInstalled(value: boolean) {
|
|
50
|
+
isInstalled.value = value
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 保存延迟的安装事件
|
|
55
|
+
*/
|
|
56
|
+
function setDeferredPrompt(event: Event | null) {
|
|
57
|
+
deferredPrompt.value = event
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 显示安装提示
|
|
62
|
+
*/
|
|
63
|
+
function showPrompt() {
|
|
64
|
+
if (canInstall.value && !isInstalled.value && !dontShowAgain.value) {
|
|
65
|
+
promptVisible.value = true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 隐藏安装提示
|
|
71
|
+
*/
|
|
72
|
+
function hidePrompt() {
|
|
73
|
+
promptVisible.value = false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 设置"不再提示"
|
|
78
|
+
*/
|
|
79
|
+
function setDontShowAgain(value: boolean) {
|
|
80
|
+
dontShowAgain.value = value
|
|
81
|
+
setStorage(STORAGE_KEY, value)
|
|
82
|
+
if (value) {
|
|
83
|
+
hidePrompt()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 触发安装
|
|
89
|
+
*/
|
|
90
|
+
async function promptInstall() {
|
|
91
|
+
const promptEvent = deferredPrompt.value as any
|
|
92
|
+
if (!promptEvent) {
|
|
93
|
+
console.warn('No deferred prompt event available')
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 显示原生安装提示
|
|
98
|
+
promptEvent.prompt()
|
|
99
|
+
|
|
100
|
+
// 等待用户响应
|
|
101
|
+
const { outcome } = await promptEvent.userChoice
|
|
102
|
+
|
|
103
|
+
if (outcome === 'accepted') {
|
|
104
|
+
setIsInstalled(true)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 清除延迟的事件
|
|
108
|
+
setDeferredPrompt(null)
|
|
109
|
+
hidePrompt()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 计算属性:是否应该显示提示
|
|
113
|
+
const shouldShowPrompt = computed(() => {
|
|
114
|
+
return (
|
|
115
|
+
config.value.autoShow &&
|
|
116
|
+
canInstall.value &&
|
|
117
|
+
!isInstalled.value &&
|
|
118
|
+
!dontShowAgain.value
|
|
119
|
+
)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
config,
|
|
124
|
+
canInstall,
|
|
125
|
+
isInstalled,
|
|
126
|
+
dontShowAgain,
|
|
127
|
+
promptVisible,
|
|
128
|
+
deferredPrompt,
|
|
129
|
+
shouldShowPrompt,
|
|
130
|
+
setConfig,
|
|
131
|
+
setCanInstall,
|
|
132
|
+
setIsInstalled,
|
|
133
|
+
setDeferredPrompt,
|
|
134
|
+
showPrompt,
|
|
135
|
+
hidePrompt,
|
|
136
|
+
setDontShowAgain,
|
|
137
|
+
promptInstall
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
export type AppShellPWAStore = ReturnType<typeof useAppShellPWAStore>
|
package/store/slots.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { reactive, computed } from 'vue'
|
|
3
|
+
import type { Component } from 'vue'
|
|
4
|
+
import { AppShellSlotLocation, type AppShellSlotRegistration } from '../types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* App Shell 插槽状态管理
|
|
8
|
+
*/
|
|
9
|
+
export const useAppShellSlotsStore = defineStore('appShellSlots', () => {
|
|
10
|
+
const slots = reactive<Record<string, AppShellSlotRegistration[]>>({})
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 注册插槽
|
|
14
|
+
*/
|
|
15
|
+
function registerSlot(registration: AppShellSlotRegistration) {
|
|
16
|
+
const { location, component } = registration
|
|
17
|
+
|
|
18
|
+
if (!slots[location]) {
|
|
19
|
+
slots[location] = []
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 检查是否已存在相同的组件
|
|
23
|
+
const isDuplicate = slots[location].some((slot) => slot.component === component)
|
|
24
|
+
if (isDuplicate) {
|
|
25
|
+
console.warn(`Component already registered for location: ${location}`)
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
slots[location].push(registration)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 清空插槽
|
|
34
|
+
*/
|
|
35
|
+
function clearSlots(location?: string) {
|
|
36
|
+
if (location) {
|
|
37
|
+
slots[location] = []
|
|
38
|
+
} else {
|
|
39
|
+
Object.keys(slots).forEach((key) => {
|
|
40
|
+
slots[key] = []
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 为每个位置创建计算属性
|
|
46
|
+
const headerLeft = computed(() => slots[AppShellSlotLocation.HEADER_LEFT] || [])
|
|
47
|
+
const headerCenter = computed(() => slots[AppShellSlotLocation.HEADER_CENTER] || [])
|
|
48
|
+
const headerRight = computed(() => slots[AppShellSlotLocation.HEADER_RIGHT] || [])
|
|
49
|
+
const sidebar = computed(() => slots[AppShellSlotLocation.SIDEBAR] || [])
|
|
50
|
+
const contentTop = computed(() => slots[AppShellSlotLocation.CONTENT_TOP] || [])
|
|
51
|
+
const contentBottom = computed(() => slots[AppShellSlotLocation.CONTENT_BOTTOM] || [])
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
slots,
|
|
55
|
+
// 计算属性
|
|
56
|
+
headerLeft,
|
|
57
|
+
headerCenter,
|
|
58
|
+
headerRight,
|
|
59
|
+
sidebar,
|
|
60
|
+
contentTop,
|
|
61
|
+
contentBottom,
|
|
62
|
+
// 方法
|
|
63
|
+
registerSlot,
|
|
64
|
+
clearSlots
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
export type AppShellSlotsStore = ReturnType<typeof useAppShellSlotsStore>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// 抽屉样式
|
|
2
|
+
@import './variables.scss';
|
|
3
|
+
|
|
4
|
+
// PC 端抽屉(固定侧边栏)
|
|
5
|
+
.app-drawer--pc {
|
|
6
|
+
transition: width var(--mb-app-shell-transition-duration, 0.3s) var(--mb-app-shell-transition-function, ease);
|
|
7
|
+
|
|
8
|
+
&.is-collapsed {
|
|
9
|
+
// 折叠状态下的样式
|
|
10
|
+
&:deep(*) {
|
|
11
|
+
// 隐藏文本内容
|
|
12
|
+
.text,
|
|
13
|
+
span:not(.icon) {
|
|
14
|
+
display: none;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 移动端抽屉(全屏覆盖)
|
|
21
|
+
.app-drawer--mobile {
|
|
22
|
+
// 添加阴影
|
|
23
|
+
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
|
24
|
+
|
|
25
|
+
&.visible {
|
|
26
|
+
box-shadow: 2px 0 16px rgba(0, 0, 0, 0.15);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 抽屉内的滚动条样式
|
|
31
|
+
.app-drawer {
|
|
32
|
+
&::-webkit-scrollbar {
|
|
33
|
+
width: 6px;
|
|
34
|
+
height: 6px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&::-webkit-scrollbar-thumb {
|
|
38
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
39
|
+
border-radius: 3px;
|
|
40
|
+
|
|
41
|
+
&:hover {
|
|
42
|
+
background-color: rgba(0, 0, 0, 0.3);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&::-webkit-scrollbar-track {
|
|
47
|
+
background-color: transparent;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 暗色主题下的滚动条
|
|
52
|
+
html.dark {
|
|
53
|
+
.app-drawer {
|
|
54
|
+
&::-webkit-scrollbar-thumb {
|
|
55
|
+
background-color: rgba(255, 255, 255, 0.2);
|
|
56
|
+
|
|
57
|
+
&:hover {
|
|
58
|
+
background-color: rgba(255, 255, 255, 0.3);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// 移动端样式
|
|
2
|
+
@import './variables.scss';
|
|
3
|
+
|
|
4
|
+
// 移动端优化
|
|
5
|
+
@media (max-width: #{($breakpoint-small - 1px)}) {
|
|
6
|
+
// 触摸优化
|
|
7
|
+
* {
|
|
8
|
+
// 增大触摸目标
|
|
9
|
+
-webkit-tap-highlight-color: transparent;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 按钮、链接等触摸元素的最小尺寸
|
|
13
|
+
button,
|
|
14
|
+
a,
|
|
15
|
+
[role="button"] {
|
|
16
|
+
min-height: 44px;
|
|
17
|
+
min-width: 44px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 优化文本选择
|
|
21
|
+
.app-main__content {
|
|
22
|
+
-webkit-user-select: text;
|
|
23
|
+
user-select: text;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 移动端抽屉全屏
|
|
27
|
+
.app-drawer--mobile {
|
|
28
|
+
// 确保全屏显示
|
|
29
|
+
top: 0;
|
|
30
|
+
left: 0;
|
|
31
|
+
right: 0;
|
|
32
|
+
bottom: 0;
|
|
33
|
+
|
|
34
|
+
// 添加状态栏安全区域
|
|
35
|
+
padding-top: env(safe-area-inset-top);
|
|
36
|
+
padding-left: env(safe-area-inset-left);
|
|
37
|
+
padding-right: env(safe-area-inset-right);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 移动端顶栏添加安全区域
|
|
41
|
+
.app-header {
|
|
42
|
+
padding-top: calc(env(safe-area-inset-top) + 16px);
|
|
43
|
+
height: calc(var(--mb-app-shell-header-height, 60px) + env(safe-area-inset-top));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 移动端主内容区添加安全区域
|
|
47
|
+
.app-main__content {
|
|
48
|
+
padding-bottom: calc(var(--mb-app-shell-main-padding, 16px) + env(safe-area-inset-bottom));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 横屏模式优化
|
|
53
|
+
@media (max-width: #{($breakpoint-small - 1px)}) and (orientation: landscape) {
|
|
54
|
+
.app-drawer--mobile {
|
|
55
|
+
// 横屏时抽屉高度减小
|
|
56
|
+
height: 100vh;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// 响应式样式
|
|
2
|
+
@import './variables.scss';
|
|
3
|
+
|
|
4
|
+
// 小屏(< 768px)
|
|
5
|
+
@media (max-width: #{($breakpoint-small - 1px)}) {
|
|
6
|
+
:root {
|
|
7
|
+
--mb-app-shell-drawer-width: 100%;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.app-header {
|
|
11
|
+
padding: 0 12px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.app-main__content {
|
|
15
|
+
padding: 12px;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 中屏(768px - 1199px)
|
|
20
|
+
@media (min-width: $breakpoint-small) and (max-width: #{($breakpoint-medium - 1px)}) {
|
|
21
|
+
.app-drawer--pc {
|
|
22
|
+
width: 200px;
|
|
23
|
+
|
|
24
|
+
&.is-collapsed {
|
|
25
|
+
width: var(--mb-app-shell-drawer-collapsed-width);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 大屏(≥ 1200px)
|
|
31
|
+
@media (min-width: $breakpoint-medium) {
|
|
32
|
+
// 大屏可以添加额外的样式
|
|
33
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// App Shell CSS 变量
|
|
2
|
+
|
|
3
|
+
// 断点
|
|
4
|
+
$breakpoint-small: 768px;
|
|
5
|
+
$breakpoint-medium: 1200px;
|
|
6
|
+
|
|
7
|
+
// 尺寸
|
|
8
|
+
$drawer-width: 240px;
|
|
9
|
+
$drawer-collapsed-width: 64px;
|
|
10
|
+
$header-height: 60px;
|
|
11
|
+
|
|
12
|
+
// 过渡
|
|
13
|
+
$transition-duration: 0.3s;
|
|
14
|
+
$transition-function: ease;
|
|
15
|
+
|
|
16
|
+
// CSS 变量定义
|
|
17
|
+
:root {
|
|
18
|
+
// 布局尺寸
|
|
19
|
+
--mb-app-shell-drawer-width: #{$drawer-width};
|
|
20
|
+
--mb-app-shell-drawer-collapsed-width: #{$drawer-collapsed-width};
|
|
21
|
+
--mb-app-shell-header-height: #{$header-height};
|
|
22
|
+
|
|
23
|
+
// 颜色
|
|
24
|
+
--mb-app-shell-header-bg: #ffffff;
|
|
25
|
+
--mb-app-shell-header-text-color: #606266;
|
|
26
|
+
--mb-app-shell-header-border-bottom: 1px solid #e4e7ed;
|
|
27
|
+
--mb-app-shell-drawer-bg: #ffffff;
|
|
28
|
+
--mb-app-shell-drawer-border-right: 1px solid #e4e7ed;
|
|
29
|
+
--mb-app-shell-main-bg: #f5f5f5;
|
|
30
|
+
|
|
31
|
+
// 间距
|
|
32
|
+
--mb-app-shell-main-padding: 16px;
|
|
33
|
+
--mb-app-shell-header-gap: 12px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 暗色主题
|
|
37
|
+
html.dark {
|
|
38
|
+
--mb-app-shell-header-bg: #1f1f1f;
|
|
39
|
+
--mb-app-shell-header-text-color: #e5e5e5;
|
|
40
|
+
--mb-app-shell-header-border-bottom: 1px solid #333333;
|
|
41
|
+
--mb-app-shell-drawer-bg: #1f1f1f;
|
|
42
|
+
--mb-app-shell-drawer-border-right: 1px solid #333333;
|
|
43
|
+
--mb-app-shell-main-bg: #141414;
|
|
44
|
+
}
|
package/types/app.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Router } from 'vue-router'
|
|
2
|
+
import type { App } from 'vue'
|
|
3
|
+
import type { PWAConfig } from './pwa'
|
|
4
|
+
import type { LayoutConfig } from './layout'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 应用创建选项
|
|
8
|
+
*/
|
|
9
|
+
export interface AppShellOptions {
|
|
10
|
+
/** 挂载元素选择器 */
|
|
11
|
+
el?: string
|
|
12
|
+
/** 路由配置 */
|
|
13
|
+
router?: {
|
|
14
|
+
/** 基础路由 */
|
|
15
|
+
baseRoutes?: any[]
|
|
16
|
+
/** 路由模式 */
|
|
17
|
+
historyMode?: 'hash' | 'html'
|
|
18
|
+
/** 基础路径 */
|
|
19
|
+
base?: string
|
|
20
|
+
}
|
|
21
|
+
/** PWA 配置 */
|
|
22
|
+
pwa?: PWAConfig
|
|
23
|
+
/** 布局配置 */
|
|
24
|
+
layout?: LayoutConfig
|
|
25
|
+
/** 是否设置 Pinia */
|
|
26
|
+
setupPinia?: boolean
|
|
27
|
+
/** 是否设置 Router */
|
|
28
|
+
setupRouter?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 应用创建结果
|
|
33
|
+
*/
|
|
34
|
+
export interface AppShellResult {
|
|
35
|
+
/** Vue 应用实例 */
|
|
36
|
+
app: App
|
|
37
|
+
/** 路由实例 */
|
|
38
|
+
router?: Router
|
|
39
|
+
/** Pinia 实例 */
|
|
40
|
+
pinia?: any
|
|
41
|
+
}
|
package/types/index.ts
ADDED
package/types/layout.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 断点类型
|
|
3
|
+
*/
|
|
4
|
+
export type Breakpoint = 'small' | 'medium' | 'large'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 布局配置
|
|
8
|
+
*/
|
|
9
|
+
export interface LayoutConfig {
|
|
10
|
+
/** 侧边栏宽度(PC端) */
|
|
11
|
+
siderWidth?: string
|
|
12
|
+
/** 侧边栏折叠宽度 */
|
|
13
|
+
siderCollapsedWidth?: string
|
|
14
|
+
/** 顶栏高度 */
|
|
15
|
+
headerHeight?: string
|
|
16
|
+
/** 是否默认折叠 */
|
|
17
|
+
defaultCollapsed?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 抽屉状态
|
|
22
|
+
*/
|
|
23
|
+
export interface DrawerState {
|
|
24
|
+
/** 是否可见(移动端) */
|
|
25
|
+
visible: boolean
|
|
26
|
+
/** 是否折叠(PC端) */
|
|
27
|
+
collapsed: boolean
|
|
28
|
+
}
|
package/types/pwa.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PWA 安装引导配置
|
|
3
|
+
*/
|
|
4
|
+
export interface PWAInstallConfig {
|
|
5
|
+
/** 安装提示标题 */
|
|
6
|
+
title?: string
|
|
7
|
+
/** 安装提示描述 */
|
|
8
|
+
description?: string
|
|
9
|
+
/** 安装按钮文案 */
|
|
10
|
+
installButtonText?: string
|
|
11
|
+
/** 取消按钮文案 */
|
|
12
|
+
cancelButtonText?: string
|
|
13
|
+
/** "不再提示"选项文案 */
|
|
14
|
+
dontShowAgainText?: string
|
|
15
|
+
/** 是否自动显示(默认 true) */
|
|
16
|
+
autoShow?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* PWA 配置
|
|
21
|
+
*/
|
|
22
|
+
export interface PWAConfig {
|
|
23
|
+
/** 应用名称 */
|
|
24
|
+
name?: string
|
|
25
|
+
/** 应用简称 */
|
|
26
|
+
shortName?: string
|
|
27
|
+
/** 应用描述 */
|
|
28
|
+
description?: string
|
|
29
|
+
/** 主题色 */
|
|
30
|
+
themeColor?: string
|
|
31
|
+
/** 背景色 */
|
|
32
|
+
backgroundColor?: string
|
|
33
|
+
/** 应用图标(路径或对象) */
|
|
34
|
+
icon?: string | Record<string, string>
|
|
35
|
+
/** 安装引导配置 */
|
|
36
|
+
installPrompt?: PWAInstallConfig
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* PWA 安装事件状态
|
|
41
|
+
*/
|
|
42
|
+
export interface PWAInstallState {
|
|
43
|
+
/** 是否可安装 */
|
|
44
|
+
canInstall: boolean
|
|
45
|
+
/** 是否已安装 */
|
|
46
|
+
isInstalled: boolean
|
|
47
|
+
/** 是否不再提示 */
|
|
48
|
+
dontShowAgain: boolean
|
|
49
|
+
/** 安装提示是否可见 */
|
|
50
|
+
promptVisible: boolean
|
|
51
|
+
/** 延迟安装的安装事件 */
|
|
52
|
+
deferredPrompt: Event | null
|
|
53
|
+
}
|
package/types/slots.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Component } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* App Shell 插槽位置
|
|
5
|
+
*/
|
|
6
|
+
export enum AppShellSlotLocation {
|
|
7
|
+
/** 顶栏左侧 */
|
|
8
|
+
HEADER_LEFT = 'HEADER_LEFT',
|
|
9
|
+
/** 顶栏中间 */
|
|
10
|
+
HEADER_CENTER = 'HEADER_CENTER',
|
|
11
|
+
/** 顶栏右侧 */
|
|
12
|
+
HEADER_RIGHT = 'HEADER_RIGHT',
|
|
13
|
+
/** 侧边栏(抽屉式) */
|
|
14
|
+
SIDEBAR = 'SIDEBAR',
|
|
15
|
+
/** 内容区域顶部 */
|
|
16
|
+
CONTENT_TOP = 'CONTENT_TOP',
|
|
17
|
+
/** 内容区域底部 */
|
|
18
|
+
CONTENT_BOTTOM = 'CONTENT_BOTTOM'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 插槽注册信息
|
|
23
|
+
*/
|
|
24
|
+
export interface AppShellSlotRegistration {
|
|
25
|
+
/** 插槽位置 */
|
|
26
|
+
location: AppShellSlotLocation
|
|
27
|
+
/** 组件 */
|
|
28
|
+
component: Component
|
|
29
|
+
/** 组件属性 */
|
|
30
|
+
props?: Record<string, any>
|
|
31
|
+
}
|