create-jnrs-template-vue 1.1.12 → 1.1.14

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.
Files changed (41) hide show
  1. package/jnrs-template-vue/README.md +5 -1
  2. package/jnrs-template-vue/components.d.ts +3 -4
  3. package/jnrs-template-vue/package.json +5 -4
  4. package/jnrs-template-vue/src/App.vue +3 -1
  5. package/jnrs-template-vue/src/api/common/index.ts +20 -5
  6. package/jnrs-template-vue/src/api/demos/index.ts +86 -0
  7. package/jnrs-template-vue/src/api/system/index.ts +1 -1
  8. package/jnrs-template-vue/src/api/user/index.ts +1 -1
  9. package/jnrs-template-vue/src/components/base/ImageView.vue +58 -0
  10. package/jnrs-template-vue/src/components/common/JnPagination.vue +83 -0
  11. package/jnrs-template-vue/src/components/common/JnTable.vue +133 -0
  12. package/jnrs-template-vue/src/composables/{common → base}/useAvatar.ts +1 -1
  13. package/jnrs-template-vue/src/composables/{useForm.ts → common/useForm.ts} +1 -1
  14. package/jnrs-template-vue/src/composables/{useModal.ts → common/useModal.ts} +1 -1
  15. package/jnrs-template-vue/src/composables/{useTable.ts → common/usePagination.ts} +1 -1
  16. package/jnrs-template-vue/src/composables/common/useTable.ts +35 -0
  17. package/jnrs-template-vue/src/composables/{useUser.ts → common/useUser.ts} +1 -1
  18. package/jnrs-template-vue/src/composables/tools/useReactivityTableHeight.ts +63 -0
  19. package/jnrs-template-vue/src/layout/SideMenu.vue +1 -1
  20. package/jnrs-template-vue/src/layout/SideMenuItem.vue +1 -1
  21. package/jnrs-template-vue/src/layout/TopHeader.vue +3 -3
  22. package/jnrs-template-vue/src/layout/index.vue +1 -1
  23. package/jnrs-template-vue/src/router/index.ts +1 -1
  24. package/jnrs-template-vue/src/utils/file.ts +15 -1
  25. package/jnrs-template-vue/src/utils/{common.ts → packages.ts} +33 -1
  26. package/jnrs-template-vue/src/utils/permissions.ts +1 -1
  27. package/jnrs-template-vue/src/views/demos/crud/index.vue +104 -2
  28. package/jnrs-template-vue/src/views/demos/unitTest/RequestPage.vue +141 -0
  29. package/jnrs-template-vue/src/views/demos/unitTest/index.vue +5 -99
  30. package/jnrs-template-vue/src/views/login/index.vue +12 -12
  31. package/jnrs-template-vue/src/views/system/menu/index.vue +1 -1
  32. package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +3 -3
  33. package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +1 -1
  34. package/jnrs-template-vue/viteMockServe/index.ts +9 -0
  35. package/jnrs-template-vue/viteMockServe/tableRes.json +275 -0
  36. package/package.json +1 -1
  37. package/jnrs-template-vue/src/api/mock/index.ts +0 -34
  38. package/jnrs-template-vue/src/api/request.ts +0 -40
  39. package/jnrs-template-vue/src/composables/index.ts +0 -11
  40. package/jnrs-template-vue/src/stores/index.ts +0 -2
  41. package/jnrs-template-vue/src/stores/mock.ts +0 -21
@@ -20,6 +20,7 @@ jnrs-template-vue/
20
20
  ├── node_modules/ # 项目依赖
21
21
  ├── viteMockServe/ # Mock 服务配置(用于开发环境模拟 API)
22
22
  ├── public/ # 静态资源
23
+ ├── layout/ # 布局
23
24
  ├── src/ # 源码目录
24
25
  │ ├── components/ # Vue 组件
25
26
  │ ├── composables/ # 组合式函数
@@ -41,4 +42,7 @@ jnrs-template-vue/
41
42
  ├── .env.production # 生产环境变量(全局)
42
43
  ├── .gitignore # Git 忽略规则
43
44
  ├── .prettierrc.json # Prettier 格式化配置
44
- ```
45
+ ```
46
+
47
+ ## ⚠️ 注意事项
48
+ - layout 中使用了 KeepAlive 内置组件进行组件实例缓存,使用生命周期进行如“副作用清理“时注意用 onActivated / onDeactivated 替代 onMounted / onUnmounted
@@ -12,12 +12,12 @@ export {}
12
12
  declare module 'vue' {
13
13
  export interface GlobalComponents {
14
14
  ElAside: typeof import('element-plus/es')['ElAside']
15
+ ElAvatar: typeof import('element-plus/es')['ElAvatar']
15
16
  ElBadge: typeof import('element-plus/es')['ElBadge']
16
17
  ElButton: typeof import('element-plus/es')['ElButton']
17
18
  ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
18
19
  ElCard: typeof import('element-plus/es')['ElCard']
19
20
  ElCascader: typeof import('element-plus/es')['ElCascader']
20
- ElCol: typeof import('element-plus/es')['ElCol']
21
21
  ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
22
22
  ElContainer: typeof import('element-plus/es')['ElContainer']
23
23
  ElDatePickerPanel: typeof import('element-plus/es')['ElDatePickerPanel']
@@ -32,10 +32,9 @@ declare module 'vue' {
32
32
  ElMenu: typeof import('element-plus/es')['ElMenu']
33
33
  ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
34
34
  ElOption: typeof import('element-plus/es')['ElOption']
35
+ ElPagination: typeof import('element-plus/es')['ElPagination']
35
36
  ElPopover: typeof import('element-plus/es')['ElPopover']
36
- ElRadio: typeof import('element-plus/es')['ElRadio']
37
- ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
38
- ElRow: typeof import('element-plus/es')['ElRow']
37
+ ElProgress: typeof import('element-plus/es')['ElProgress']
39
38
  ElSelect: typeof import('element-plus/es')['ElSelect']
40
39
  ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
41
40
  ElSwitch: typeof import('element-plus/es')['ElSwitch']
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jnrs-template-vue",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "JNRS 信息化管理系统模板",
5
5
  "author": "Talia-Tan",
6
6
  "private": true,
@@ -19,9 +19,10 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@element-plus/icons-vue": "^2.3.2",
22
- "@jnrs/core": "1.1.5",
23
- "@jnrs/shared": "1.1.5",
24
- "@jnrs/vue-core": "1.1.5",
22
+ "@jnrs/core": "1.1.6",
23
+ "@jnrs/shared": "1.1.6",
24
+ "@jnrs/vue-core": "1.1.6",
25
+ "@vueuse/core": "^14.1.0",
25
26
  "element-plus": "^2.11.9",
26
27
  "pinia": "^3.0.4",
27
28
  "pinia-plugin-persistedstate": "^4.7.1",
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { onMounted, watch } from 'vue'
3
- import { useSystemStore } from '@/stores'
3
+ import { useSystemStore } from '@jnrs/vue-core/pinia'
4
4
  import { ElConfigProvider } from 'element-plus'
5
5
  import zhCn from 'element-plus/es/locale/lang/zh-CN'
6
6
  import en from 'element-plus/es/locale/lang/en'
@@ -9,6 +9,7 @@ import { changeLocales as changeLocalesForShared } from '@jnrs/shared/locales'
9
9
  import { changeLocales as changeLocalesForCore } from '@jnrs/core/locales'
10
10
 
11
11
  const { locale } = useI18n()
12
+
12
13
  const { theme } = useSystemStore()
13
14
  watch(
14
15
  () => theme.locale,
@@ -21,6 +22,7 @@ watch(
21
22
  immediate: true
22
23
  }
23
24
  )
25
+
24
26
  const localeMap = {
25
27
  zhCn: zhCn,
26
28
  en: en
@@ -1,9 +1,24 @@
1
- import { request } from '../request'
1
+ import { request } from '@jnrs/vue-core'
2
+ // import { useFetch } from '@vueuse/core'
2
3
 
3
- //附件获取
4
- export const FileApi = (name: string) => {
4
+ /**
5
+ * 文件下载
6
+ * @param uniqueFileName 文件唯一名称(列表中一般是在 item.attachmentDocument/imageDocument.attachments[].uniqueFileName)
7
+ * @returns Blob
8
+ */
9
+ export const FileApi = (uniqueFileName: string): Promise<Blob> => {
5
10
  return request({
6
- url: '/api/files/' + name,
7
- method: 'get'
11
+ url: '/api/files/' + uniqueFileName,
12
+ method: 'get',
13
+ responseType: 'blob'
8
14
  })
9
15
  }
16
+
17
+ // export const FileApi = (uniqueFileName: string) => {
18
+ // return useFetch(`/api/files/${uniqueFileName}`, {
19
+ // method: 'get',
20
+ // headers: {
21
+ // responseType: 'blob'
22
+ // }
23
+ // }).blob()
24
+ // }
@@ -0,0 +1,86 @@
1
+ import { request } from '@jnrs/vue-core'
2
+ import type { ApiResponse } from '@/types'
3
+
4
+ /**
5
+ * 404 错误
6
+ */
7
+ export const NotFoundApi = () => {
8
+ return request({
9
+ url: '/notFound',
10
+ method: 'get'
11
+ })
12
+ }
13
+
14
+ /**
15
+ * 无权限
16
+ * @param showErrorMsg 是否显示错误信息
17
+ */
18
+ export const NoAuth = (showErrorMsg: boolean) => {
19
+ return request({
20
+ url: '/auth/no',
21
+ method: 'post',
22
+ showErrorMsg
23
+ })
24
+ }
25
+
26
+ /**
27
+ * 数据详情
28
+ */
29
+ export const DetailsApi = (): Promise<ApiResponse> => {
30
+ return request({
31
+ url: '/details',
32
+ method: 'get'
33
+ })
34
+ }
35
+
36
+ /**
37
+ * 附件(文件)信息
38
+ */
39
+ export interface Attachment {
40
+ id: number
41
+ documentId: number
42
+ fileName: string
43
+ uniqueFileName: string
44
+ fileType: string // MIME 类型
45
+ fileSize: number // 单位:字节(Bytes)
46
+ }
47
+
48
+ /**
49
+ * 文档容器(用于图片或附件)
50
+ */
51
+ export interface Document {
52
+ id: number
53
+ description: string | null
54
+ attachments: Attachment[]
55
+ }
56
+
57
+ /**
58
+ * 项目项
59
+ */
60
+ export interface ProjectItem {
61
+ id: string
62
+ programCode: string
63
+ program: string
64
+ code: string
65
+ name: string
66
+ description: string
67
+ type: '敏捷型' | '瀑布型' | '混合型'
68
+ manager: string
69
+ budget: string
70
+ plannedStartDate: string
71
+ plannedFinishDate: string
72
+ progress: string
73
+ status: '进行中' | '已完成' | '未开始' | '已暂停'
74
+ imageDocument?: Document // 图片类附件
75
+ attachmentDocument?: Document // 普通文件类附件
76
+ }
77
+
78
+ /**
79
+ * 数据详情
80
+ */
81
+ export const DemosTableApi = (): Promise<ProjectItem[]> => {
82
+ return request({
83
+ url: '/mock/demos/table',
84
+ method: 'get'
85
+ })
86
+ }
@@ -1,4 +1,4 @@
1
- import { request } from '../request'
1
+ import { request } from '@jnrs/vue-core'
2
2
  import type { Dict, DictItem, User, Role } from '@jnrs/shared'
3
3
  import type { MenuItem } from '@jnrs/vue-core'
4
4
 
@@ -1,4 +1,4 @@
1
- import { request } from '../request'
1
+ import { request } from '@jnrs/vue-core'
2
2
 
3
3
  // 获取用户信息
4
4
  export const UserApi = (id: string) => {
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts">
2
+ import { ref, onBeforeUnmount } from 'vue'
3
+ import { FileApi } from '@/api/common'
4
+
5
+ // 响应式数据
6
+ const squareUrl = ref<string>('')
7
+
8
+ let currentObjectUrl: string | null = null
9
+
10
+ // 加载图片
11
+ async function loadImage() {
12
+ try {
13
+ const blob = await FileApi('20251216134412632-1376c8a7-1f31-451c-9eb4-944aaa03497b.png')
14
+
15
+ // 创建 URL 并保存引用
16
+ currentObjectUrl = URL.createObjectURL(blob)
17
+ squareUrl.value = currentObjectUrl
18
+ } catch (error) {
19
+ console.error('加载头像失败:', error)
20
+ }
21
+ }
22
+
23
+ // 图片加载成功 → 立即释放内存(浏览器已缓存图像)
24
+ function handleImageLoad() {
25
+ if (currentObjectUrl) {
26
+ URL.revokeObjectURL(currentObjectUrl)
27
+ currentObjectUrl = null
28
+ console.log('✅ 头像加载完成,已释放 Blob URL')
29
+ }
30
+ }
31
+
32
+ // 图片加载失败 → 也释放内存
33
+ function handleImageError() {
34
+ if (currentObjectUrl) {
35
+ URL.revokeObjectURL(currentObjectUrl)
36
+ currentObjectUrl = null
37
+ console.error('❌ 头像加载失败')
38
+ }
39
+ }
40
+
41
+ // 组件卸载前 → 确保清理(防止用户快速跳转导致未加载完)
42
+ onBeforeUnmount(() => {
43
+ if (currentObjectUrl) {
44
+ URL.revokeObjectURL(currentObjectUrl)
45
+ currentObjectUrl = null
46
+ console.log('🧹 组件卸载,强制释放 Blob URL')
47
+ }
48
+ })
49
+
50
+ // 启动加载
51
+ loadImage()
52
+ </script>
53
+
54
+ <template>
55
+ <el-avatar v-if="squareUrl" shape="square" :src="squareUrl" @load="handleImageLoad" @error="handleImageError" />
56
+ <!-- 可选:加载中状态 -->
57
+ <div v-else>加载中...</div>
58
+ </template>
@@ -0,0 +1,83 @@
1
+ <!--
2
+ @Author : TanRui
3
+ @WeChat : Tan578853789
4
+ @File : JnPagination.vue
5
+ @Date : 2025/12/18
6
+ @Desc. : 分页组件
7
+ -->
8
+
9
+ <script setup lang="ts">
10
+ import { ref, watch } from 'vue'
11
+ import type { ComponentSize } from 'element-plus'
12
+
13
+ type PaginationModelValue = {
14
+ currentPage: number
15
+ pageSize: number
16
+ }
17
+
18
+ interface PaginationProps {
19
+ total: number
20
+ modelValue?: PaginationModelValue
21
+ size?: ComponentSize
22
+ background?: boolean
23
+ marginTop?: string
24
+ }
25
+
26
+ const props = withDefaults(defineProps<PaginationProps>(), {
27
+ modelValue: () => ({ currentPage: 1, pageSize: 10 }),
28
+ size: 'small',
29
+ background: true,
30
+ marginTop: '10px'
31
+ })
32
+
33
+ const emit = defineEmits<{
34
+ (e: 'update:modelValue', value: { currentPage: number; pageSize: number }): void
35
+ (e: 'change'): void
36
+ }>()
37
+
38
+ const localPage = ref(props.modelValue.currentPage)
39
+ const localSize = ref(props.modelValue.pageSize)
40
+
41
+ watch(
42
+ () => props.modelValue,
43
+ (newVal) => {
44
+ localPage.value = newVal.currentPage
45
+ localSize.value = newVal.pageSize
46
+ },
47
+ { deep: true }
48
+ )
49
+
50
+ // 页码变化
51
+ const handleCurrentChange = (page: number) => {
52
+ localPage.value = page
53
+ const newValue = { currentPage: page, pageSize: localSize.value }
54
+ emit('update:modelValue', newValue)
55
+ emit('change')
56
+ }
57
+
58
+ // 每页条数变化时重置到第一页
59
+ const handleSizeChange = (size: number) => {
60
+ localSize.value = size
61
+ localPage.value = 1
62
+ const newValue = { currentPage: 1, pageSize: size }
63
+ emit('update:modelValue', newValue)
64
+ emit('change')
65
+ }
66
+ </script>
67
+
68
+ <template>
69
+ <el-pagination
70
+ v-model:current-page="localPage"
71
+ v-model:page-size="localSize"
72
+ :total="total"
73
+ :page-sizes="[10, 20, 50, 100]"
74
+ :size="size"
75
+ :background="background"
76
+ layout="->, total, sizes, prev, pager, next, jumper"
77
+ :style="{
78
+ marginTop: marginTop
79
+ }"
80
+ @current-change="handleCurrentChange"
81
+ @size-change="handleSizeChange"
82
+ />
83
+ </template>
@@ -0,0 +1,133 @@
1
+ <!--
2
+ @Author : TanRui
3
+ @WeChat : Tan578853789
4
+ @File : JnTable.vue
5
+ @Date : 2025/12/18
6
+ @Desc. : 表格组件
7
+ -->
8
+
9
+ <script setup lang="ts" generic="T extends Record<string, any>">
10
+ import { computed } from 'vue'
11
+ import type { UseReactivityTableHeightOptions } from '@/composables/tools/useReactivityTableHeight'
12
+ import { useReactivityTableHeight } from '@/composables/tools/useReactivityTableHeight'
13
+
14
+ type PaginationModelValue = {
15
+ currentPage: number
16
+ pageSize: number
17
+ }
18
+
19
+ interface Props {
20
+ /**
21
+ * 表数据
22
+ */
23
+ data?: T[]
24
+ /**
25
+ * 行数据的 Key,在使用 reserve-selection 功能与显示树形数据时,该属性是必填的。
26
+ * 支持多层访问:user.info.id
27
+ */
28
+ rowKey?: string
29
+ /**
30
+ * 总是显示横向滚动条
31
+ */
32
+ scrollbar?: boolean
33
+ /**
34
+ * 表格高度,默认为 100%
35
+ * @default 100%
36
+ */
37
+ height?: string | number
38
+ /**
39
+ * 表格高度是否自动计算,默认为 false
40
+ * @default false
41
+ */
42
+ autoHeight?: boolean
43
+ /**
44
+ * 表格高度自动计算配置项
45
+ */
46
+ autoHeightOptions?: UseReactivityTableHeightOptions
47
+ /**
48
+ * 是否显示序号列
49
+ */
50
+ showIndexColumn?: boolean
51
+ /**
52
+ * 是否显示选择列
53
+ */
54
+ showSelectionColumn?: boolean
55
+ /**
56
+ * 当前页码和每页大小(用于序号计算)
57
+ */
58
+ pagination?: PaginationModelValue
59
+ }
60
+
61
+ const {
62
+ rowKey = 'id',
63
+ scrollbar = true,
64
+ height = null,
65
+ autoHeight = true,
66
+ autoHeightOptions = {},
67
+ showIndexColumn = true,
68
+ showSelectionColumn = true,
69
+ pagination = { currentPage: 0, pageSize: 0 }
70
+ } = defineProps<Props>()
71
+
72
+ const { maxHeight: reactiveMaxHeight } = useReactivityTableHeight(autoHeightOptions ?? {})
73
+
74
+ // 表格最终高度
75
+ const finalHeight = computed(() => {
76
+ if (height != null) {
77
+ return height
78
+ }
79
+ if (autoHeight) {
80
+ return reactiveMaxHeight.value
81
+ }
82
+ return '100%'
83
+ })
84
+
85
+ // 索引列计算
86
+ const getIndex = (index: number) => {
87
+ return index + (pagination.currentPage - 1) * pagination.pageSize + 1
88
+ }
89
+ </script>
90
+
91
+ <template>
92
+ <el-table
93
+ class="jn_table"
94
+ header-cell-class-name="jn_table_header"
95
+ ref="jnTableRef"
96
+ :row-key="rowKey"
97
+ :data="data"
98
+ border
99
+ :scrollbar-always-on="scrollbar"
100
+ :height="finalHeight"
101
+ >
102
+ <el-table-column v-if="showIndexColumn" label="序号" type="index" align="center" width="60">
103
+ <template #default="{ $index }">
104
+ <span>{{ getIndex($index) }}</span>
105
+ </template>
106
+ </el-table-column>
107
+ <slot></slot>
108
+ <el-table-column
109
+ v-if="showSelectionColumn"
110
+ type="selection"
111
+ width="60"
112
+ align="center"
113
+ fixed="right"
114
+ :reserve-selection="true"
115
+ />
116
+ </el-table>
117
+ </template>
118
+
119
+ <style lang="scss" scoped>
120
+ .jn_table {
121
+ width: 100%;
122
+
123
+ :deep(.jn_table_header) {
124
+ color: var(--jnrs-font-primary) !important;
125
+ background: var(--jnrs-background-head) !important;
126
+
127
+ .cell {
128
+ padding: 0 8px;
129
+ line-height: 1.2em;
130
+ }
131
+ }
132
+ }
133
+ </style>
@@ -1,6 +1,6 @@
1
1
  import { ref, toRefs, watch } from 'vue'
2
2
  // import { getImgSrc } from '@/utils/common'
3
- // import { useCommonStore } from '@/stores'
3
+ // import { useCommonStore } from '@jnrs/vue-core/pinia'
4
4
  // import imgFailed from '@/assets/img/common/404.png'
5
5
  import defaultAvatar from '@/assets/images/common/avatar.png'
6
6
 
@@ -2,7 +2,7 @@
2
2
  * @Author : TanRui
3
3
  * @WeChat : Tan578853789
4
4
  * @File : useForm.ts
5
- * @Date : 2025/09/30
5
+ * @Date : 2025/11/30
6
6
  * @Desc. : 表单通用逻辑
7
7
  */
8
8
 
@@ -2,7 +2,7 @@
2
2
  * @Author : TanRui
3
3
  * @WeChat : Tan578853789
4
4
  * @File : useModal.ts
5
- * @Date : 2025/09/30
5
+ * @Date : 2025/11/30
6
6
  * @Desc. : 弹窗控制逻辑
7
7
  */
8
8
 
@@ -2,7 +2,7 @@
2
2
  * @Author : TanRui
3
3
  * @WeChat : Tan578853789
4
4
  * @File : useTable.ts
5
- * @Date : 2025/09/30
5
+ * @Date : 2025/11/30
6
6
  * @Desc. : 表格、分页、筛选等通用逻辑
7
7
  */
8
8
 
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : useTable.ts
5
+ * @Date : 2025/11/30
6
+ * @Desc. : 表格
7
+ */
8
+
9
+ import { ref, computed } from 'vue'
10
+
11
+ export function useTable<T>(initialData: T[] = []) {
12
+ const data = ref<T[]>(initialData)
13
+ const pageSize = ref(10)
14
+ const currentPage = ref(1)
15
+
16
+ // 分页数据
17
+ const paginatedData = computed(() => {
18
+ const start = (currentPage.value - 1) * pageSize.value
19
+ return data.value?.slice(start, start + pageSize.value)
20
+ })
21
+
22
+ // 设置新的数据
23
+ const setNewData = (newData: T[]) => {
24
+ data.value = newData
25
+ currentPage.value = 1
26
+ }
27
+
28
+ return {
29
+ data,
30
+ pageSize,
31
+ currentPage,
32
+ paginatedData,
33
+ setNewData
34
+ }
35
+ }
@@ -2,7 +2,7 @@
2
2
  * @Author : TanRui
3
3
  * @WeChat : Tan578853789
4
4
  * @File : useUser.ts
5
- * @Date : 2025/09/30
5
+ * @Date : 2025/11/30
6
6
  * @Desc. : 用户相关逻辑
7
7
  */
8
8
 
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : useReactivityTableHeight.ts
5
+ * @Date : 2025/12/10
6
+ * @Desc. : 响应式根据视口动态计算表格高度
7
+ */
8
+
9
+ import { ref, onActivated, onDeactivated } from 'vue'
10
+ import { debounce } from '@jnrs/shared/lodash'
11
+
12
+ // 总底部高度
13
+ const PADDING_HEIGHT = 40
14
+
15
+ // 总边距
16
+ const FOOT_HEIGHT = 40
17
+
18
+ export interface UseReactivityTableHeightOptions {
19
+ // 表格类名,默认值 el-table
20
+ className?: string
21
+ // 默认高度,默认值 300
22
+ bottomGap?: number
23
+ }
24
+
25
+ /**
26
+ * @param className 表格类名,默认值 el-table,如果页面存在多个表格,则必须分别传入自定义类名
27
+ * @param bottomGap 底部间隔,默认值 77
28
+ * @return maxHeight 表格高度
29
+ */
30
+ export function useReactivityTableHeight({
31
+ className = 'el-table',
32
+ bottomGap = PADDING_HEIGHT + FOOT_HEIGHT
33
+ }: UseReactivityTableHeightOptions = {}) {
34
+ const maxHeight = ref(500)
35
+
36
+ const calculateTableHeight = debounce(
37
+ () => {
38
+ const element = document.querySelector(`.${className}`)
39
+ if (element) {
40
+ const rect = element.getBoundingClientRect()
41
+ const innerHeight = window.innerHeight
42
+ const gap = rect.top + bottomGap
43
+ const mh = innerHeight - gap
44
+ maxHeight.value = mh > 100 ? mh : 100
45
+ }
46
+ },
47
+ 300,
48
+ { leading: false }
49
+ )
50
+
51
+ // 初始化高度计算
52
+ onActivated(() => {
53
+ calculateTableHeight()
54
+ window.addEventListener('resize', calculateTableHeight)
55
+ })
56
+
57
+ // 清理事件监听器
58
+ onDeactivated(() => {
59
+ window.removeEventListener('resize', calculateTableHeight)
60
+ })
61
+
62
+ return { maxHeight }
63
+ }
@@ -2,7 +2,7 @@
2
2
  import SideMenuItem from './SideMenuItem.vue'
3
3
  import { storeToRefs } from 'pinia'
4
4
  import { useRoute } from '@jnrs/vue-core/router'
5
- import { useSystemStore, useMenuStore } from '@/stores'
5
+ import { useSystemStore, useMenuStore } from '@jnrs/vue-core/pinia'
6
6
 
7
7
  const systemStore = useSystemStore()
8
8
  const { menuCollapse } = storeToRefs(systemStore)
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { useSystemStore } from '@/stores'
2
+ import { useSystemStore } from '@jnrs/vue-core/pinia'
3
3
  import type { MenuItem } from '@jnrs/vue-core'
4
4
 
5
5
  defineProps<{
@@ -4,10 +4,10 @@ import { ref, computed } from 'vue'
4
4
  import { storeToRefs } from 'pinia'
5
5
  import { ElMessageBox } from 'element-plus'
6
6
  import { handleRouter } from '@jnrs/vue-core/router'
7
- import { useSystemStore, useAuthStore, useMenuStore } from '@/stores'
8
- import { useAvatar } from '@/composables/common/useAvatar'
7
+ import { useSystemStore, useAuthStore, useMenuStore } from '@jnrs/vue-core/pinia'
8
+ import { useAvatar } from '@/composables/base/useAvatar'
9
9
  import { LogoutApi } from '@/api/system'
10
- import { getDictLabel, getDictColor } from '@/utils/common'
10
+ import { getDictLabel, getDictColor } from '@/utils/packages'
11
11
 
12
12
  const { avatar } = useAvatar()
13
13
  const { userInfo, clearAuth } = useAuthStore()