create-jnrs-template-vue 1.2.3 → 1.2.4
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/jnrs-template-vue/.env.development +1 -1
- package/jnrs-template-vue/index.html +1 -1
- package/jnrs-template-vue/package.json +3 -3
- package/jnrs-template-vue/public/system/menu.json +11 -2
- package/jnrs-template-vue/src/api/demos/index.ts +17 -10
- package/jnrs-template-vue/src/api/system/index.ts +11 -1
- package/jnrs-template-vue/src/assets/styles/index.scss +0 -24
- package/jnrs-template-vue/src/assets/styles/init.scss +24 -0
- package/jnrs-template-vue/src/assets/styles/root.scss +4 -0
- package/jnrs-template-vue/src/components/common/CardTable.vue +89 -0
- package/jnrs-template-vue/src/components/common/DictTag.vue +8 -4
- package/jnrs-template-vue/src/components/common/ImageView.vue +16 -7
- package/jnrs-template-vue/src/components/common/PdfView.vue +14 -5
- package/jnrs-template-vue/src/components/select/SelectManager.vue +2 -2
- package/jnrs-template-vue/src/layout/SideMenu.vue +0 -1
- package/jnrs-template-vue/src/layout/TopHeader.vue +7 -14
- package/jnrs-template-vue/src/types/webSocket.ts +19 -0
- package/jnrs-template-vue/src/utils/file.ts +36 -1
- package/jnrs-template-vue/src/views/demos/crud/index.vue +24 -36
- package/jnrs-template-vue/src/views/demos/simpleTable/index.vue +41 -0
- package/jnrs-template-vue/src/views/demos/unitTest/RequestPage.vue +2 -2
- package/jnrs-template-vue/src/views/login/index.vue +18 -15
- package/jnrs-template-vue/src/views/system/dict/index.vue +63 -76
- package/jnrs-template-vue/src/views/system/menu/index.vue +42 -54
- package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +26 -59
- package/jnrs-template-vue/src/views/system/role/index.vue +20 -29
- package/jnrs-template-vue/src/views/visual/index.vue +130 -15
- package/package.json +1 -1
- package/jnrs-template-vue/src/composables/base/useAvatar.ts +0 -36
- package/jnrs-template-vue/src/composables/tools/useMouseSelection.ts +0 -150
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import type { FormInstance } from 'element-plus'
|
|
2
3
|
import { ref, onMounted } from 'vue'
|
|
3
|
-
import { storeToRefs } from 'pinia'
|
|
4
|
-
import { ElMessage } from 'element-plus'
|
|
5
|
-
import type { UploadProps } from 'element-plus'
|
|
6
4
|
import { useAuthStore } from '@jnrs/vue-core/pinia'
|
|
7
|
-
import { useAvatar } from '@/composables/base/useAvatar'
|
|
8
5
|
import { getDictList } from '@/utils/packages'
|
|
9
|
-
import
|
|
6
|
+
import { JnFileUpload } from '@jnrs/vue-core/components'
|
|
7
|
+
import { AvatarChangeApi } from '@/api/system'
|
|
8
|
+
import { objectMatchAssign } from '@jnrs/shared'
|
|
10
9
|
|
|
11
|
-
const { userInfo
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const ruleForm = ref<User>({
|
|
10
|
+
const { userInfo } = useAuthStore()
|
|
11
|
+
// const loading = ref(false)
|
|
12
|
+
const ruleFormRef = ref<FormInstance>()
|
|
13
|
+
const ruleForm = ref({
|
|
16
14
|
id: 0,
|
|
17
15
|
account: '',
|
|
18
16
|
name: '',
|
|
@@ -20,7 +18,8 @@ const ruleForm = ref<User>({
|
|
|
20
18
|
jobTitle: 0,
|
|
21
19
|
workgroup: '',
|
|
22
20
|
role: 0,
|
|
23
|
-
avatarFileName: ''
|
|
21
|
+
avatarFileName: '',
|
|
22
|
+
file: []
|
|
24
23
|
})
|
|
25
24
|
const rules = ref({
|
|
26
25
|
// account: { required: true, message: '请输入', trigger: 'change' },
|
|
@@ -29,44 +28,14 @@ const rules = ref({
|
|
|
29
28
|
// workNo: { required: true, message: '请输入', trigger: 'change' },
|
|
30
29
|
})
|
|
31
30
|
|
|
32
|
-
interface ApiResponse {
|
|
33
|
-
code: number
|
|
34
|
-
message: string
|
|
35
|
-
data: string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
31
|
onMounted(() => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
32
|
+
const matched = objectMatchAssign(ruleForm.value, userInfo)
|
|
33
|
+
ruleForm.value = matched
|
|
42
34
|
})
|
|
43
|
-
|
|
44
|
-
const handleAvatarSuccess: UploadProps['onSuccess'] = (response: ApiResponse) => {
|
|
45
|
-
loading.value = false
|
|
46
|
-
if (userInfo.value && response.code === 0) {
|
|
47
|
-
userInfo.value.avatarFileName = response.data
|
|
48
|
-
ElMessage.success({ message: '头像上传成功' })
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const handleProgress = () => {
|
|
53
|
-
loading.value = true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
|
57
|
-
if (rawFile.type.indexOf('image/') < 0) {
|
|
58
|
-
ElMessage.error('上传头像只能是图片格式!')
|
|
59
|
-
return false
|
|
60
|
-
} else if (rawFile.size / 1024 / 1024 > 10) {
|
|
61
|
-
ElMessage.error('上传头像图片大小不能超过10MB!')
|
|
62
|
-
return false
|
|
63
|
-
}
|
|
64
|
-
return true
|
|
65
|
-
}
|
|
66
35
|
</script>
|
|
67
36
|
|
|
68
37
|
<template>
|
|
69
|
-
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto" disabled>
|
|
38
|
+
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto" :disabled="true">
|
|
70
39
|
<el-form-item label="登录账号" prop="account">
|
|
71
40
|
<el-input v-model.trim="ruleForm.account" style="width: 200px" />
|
|
72
41
|
</el-form-item>
|
|
@@ -94,23 +63,21 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
|
|
94
63
|
<el-option :label="item.label" :value="item.value" v-for="(item, index) in getDictList('role')" :key="index" />
|
|
95
64
|
</el-select>
|
|
96
65
|
</el-form-item>
|
|
97
|
-
<el-form-item label="头像" prop="
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
:
|
|
104
|
-
:
|
|
105
|
-
:
|
|
106
|
-
:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<el-icon v-else class="uploader_icon"><Plus /></el-icon>
|
|
110
|
-
</el-upload>
|
|
66
|
+
<el-form-item label="头像" prop="file" class="uploader_item">
|
|
67
|
+
<JnFileUpload
|
|
68
|
+
v-model="ruleForm.file"
|
|
69
|
+
:formRef="ruleFormRef"
|
|
70
|
+
validateFieldName="newImageFiles"
|
|
71
|
+
accept=".png,.jpg,.bmp,.gif"
|
|
72
|
+
:fileSizeMb="50"
|
|
73
|
+
:limit="1"
|
|
74
|
+
:showFileList="false"
|
|
75
|
+
:autoUploadApi="AvatarChangeApi"
|
|
76
|
+
style="width: 500px"
|
|
77
|
+
/>
|
|
111
78
|
</el-form-item>
|
|
112
79
|
<!-- <el-form-item>
|
|
113
|
-
<el-button class="form_submit" :loading="loading" type="primary" @click="submitForm
|
|
80
|
+
<el-button class="form_submit" :loading="loading" type="primary" @click="submitForm">
|
|
114
81
|
保存修改
|
|
115
82
|
</el-button>
|
|
116
83
|
</el-form-item> -->
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, onMounted } from 'vue'
|
|
3
3
|
import { RoleApi } from '@/api/system'
|
|
4
|
+
import { JnTable } from '@jnrs/vue-core/components'
|
|
4
5
|
|
|
5
6
|
const loading = ref(false)
|
|
6
7
|
const tableData = ref()
|
|
@@ -18,33 +19,23 @@ onMounted(() => {
|
|
|
18
19
|
</script>
|
|
19
20
|
|
|
20
21
|
<template>
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<el-table
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</template>
|
|
41
|
-
</el-table-column>
|
|
42
|
-
<el-table-column prop="permissions" label="可用权限" header-align="center">
|
|
43
|
-
<template #default="{ row }">
|
|
44
|
-
{{ row.permissions }}
|
|
45
|
-
</template>
|
|
46
|
-
</el-table-column>
|
|
47
|
-
</el-table>
|
|
48
|
-
</el-card>
|
|
49
|
-
</div>
|
|
22
|
+
<el-card v-loading="loading">
|
|
23
|
+
<template #header>
|
|
24
|
+
<span>角色管理</span>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<JnTable :data="tableData">
|
|
28
|
+
<el-table-column prop="label" label="角色名称" width="200" align="center" />
|
|
29
|
+
<el-table-column prop="value" label="值" width="200" align="center">
|
|
30
|
+
<template #default="{ row }">
|
|
31
|
+
{{ row.value + ' [' + typeof row.value + ']' }}
|
|
32
|
+
</template>
|
|
33
|
+
</el-table-column>
|
|
34
|
+
<el-table-column prop="permissions" label="可用权限" header-align="center">
|
|
35
|
+
<template #default="{ row }">
|
|
36
|
+
{{ row.permissions }}
|
|
37
|
+
</template>
|
|
38
|
+
</el-table-column>
|
|
39
|
+
</JnTable>
|
|
40
|
+
</el-card>
|
|
50
41
|
</template>
|
|
@@ -1,22 +1,59 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
}
|
|
2
|
+
import type { MsgIdMessage, TypeDataMessage } from '@/types/webSocket'
|
|
3
|
+
import { isMsgIdMessage, isTypeDataMessage } from '@/types/webSocket'
|
|
4
|
+
import { storeToRefs } from 'pinia'
|
|
5
|
+
import { useWebSocket } from '@jnrs/vue-core/composables'
|
|
6
|
+
import { useRouter } from '@jnrs/vue-core/router'
|
|
7
|
+
import { useSystemStore } from '@jnrs/vue-core/pinia'
|
|
8
|
+
|
|
9
|
+
const router = useRouter()
|
|
10
|
+
const systemStore = useSystemStore()
|
|
11
|
+
const { documentFullscreen } = storeToRefs(systemStore)
|
|
12
|
+
const { toggleFullScreen } = systemStore
|
|
13
|
+
|
|
14
|
+
const { isLoading, wsStateInfo } = useWebSocket({
|
|
15
|
+
url: '/ws/monitor',
|
|
16
|
+
connectionTimeoutDuration: 3_000, // 修改仅为测试
|
|
17
|
+
onMessage: (rawMessage: unknown) => {
|
|
18
|
+
if (isMsgIdMessage(rawMessage)) {
|
|
19
|
+
const msg = rawMessage as MsgIdMessage
|
|
20
|
+
if (msg.msgId && msg.d && typeof msg.d === 'object') {
|
|
21
|
+
// console.log('接收 msgId 数据:', msg)
|
|
22
|
+
}
|
|
23
|
+
} else if (isTypeDataMessage(rawMessage)) {
|
|
24
|
+
const msg = rawMessage as TypeDataMessage
|
|
25
|
+
if (msg.type && msg.data && typeof msg.data === 'object') {
|
|
26
|
+
// console.log('接收 type 数据:', msg)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
12
31
|
</script>
|
|
13
32
|
|
|
14
33
|
<template>
|
|
15
|
-
<div
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
34
|
+
<div
|
|
35
|
+
class="visual"
|
|
36
|
+
v-loading="isLoading"
|
|
37
|
+
element-loading-background="rgba(0,0,0,.5)"
|
|
38
|
+
element-loading-text="网络加载中..."
|
|
39
|
+
>
|
|
40
|
+
<div class="visual_head">
|
|
41
|
+
<div class="visual_head_left">
|
|
42
|
+
<div class="visual_title">
|
|
43
|
+
<b>数字孪生看板</b>
|
|
44
|
+
<span>{{ wsStateInfo }}</span>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="visual_head_right">
|
|
48
|
+
<span class="btn" @click="toggleFullScreen">
|
|
49
|
+
{{ !documentFullscreen ? '启用全屏' : '退出全屏' }}
|
|
50
|
+
</span>
|
|
51
|
+
<span class="btn" @click="router.push('/')">返回首页</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="visual_canvas">
|
|
55
|
+
<i>canvas</i>
|
|
56
|
+
</div>
|
|
20
57
|
</div>
|
|
21
58
|
</template>
|
|
22
59
|
|
|
@@ -25,4 +62,82 @@ const goBack = () => {
|
|
|
25
62
|
@function px2vw($px) {
|
|
26
63
|
@return math.div($px, 1920) * 100vw;
|
|
27
64
|
}
|
|
65
|
+
|
|
66
|
+
.visual {
|
|
67
|
+
position: relative;
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: 100%;
|
|
70
|
+
font-size: px2vw(18);
|
|
71
|
+
overflow: hidden;
|
|
72
|
+
user-select: none;
|
|
73
|
+
|
|
74
|
+
.visual_head {
|
|
75
|
+
position: absolute;
|
|
76
|
+
z-index: 10;
|
|
77
|
+
display: flex;
|
|
78
|
+
justify-content: space-between;
|
|
79
|
+
align-items: top;
|
|
80
|
+
width: 100%;
|
|
81
|
+
padding: px2vw(10);
|
|
82
|
+
color: #fff;
|
|
83
|
+
|
|
84
|
+
.visual_title {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
font-size: px2vw(24);
|
|
88
|
+
height: px2vw(30);
|
|
89
|
+
background: #000;
|
|
90
|
+
border: px2vw(2) solid #f2f2f2;
|
|
91
|
+
margin: 0 0 px2vw(5) 0;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
|
|
94
|
+
b {
|
|
95
|
+
display: inline-block;
|
|
96
|
+
padding: 0 px2vw(5);
|
|
97
|
+
font-size: px2vw(24);
|
|
98
|
+
background: #f2f2f2;
|
|
99
|
+
color: #000;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
span {
|
|
103
|
+
padding: 0 px2vw(8);
|
|
104
|
+
color: #f2f2f2;
|
|
105
|
+
box-sizing: border-box;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.visual_head_right {
|
|
110
|
+
.btn {
|
|
111
|
+
align-items: center;
|
|
112
|
+
padding: 0 px2vw(8);
|
|
113
|
+
color: #f2f2f2;
|
|
114
|
+
background: var(--jnrs-color-primary);
|
|
115
|
+
border-radius: px2vw(2);
|
|
116
|
+
margin: 0 px2vw(2);
|
|
117
|
+
transition: all 0.25s ease;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
|
|
120
|
+
&:hover {
|
|
121
|
+
filter: brightness(1.3);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.visual_canvas {
|
|
128
|
+
position: relative;
|
|
129
|
+
width: 100%;
|
|
130
|
+
height: 100%;
|
|
131
|
+
|
|
132
|
+
i {
|
|
133
|
+
position: absolute;
|
|
134
|
+
top: 50%;
|
|
135
|
+
left: 50%;
|
|
136
|
+
transform: translate(-50%, -50%);
|
|
137
|
+
font-size: px2vw(100);
|
|
138
|
+
color: #fff;
|
|
139
|
+
opacity: 0.5;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
28
143
|
</style>
|
package/package.json
CHANGED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { ref, toRefs, watch } from 'vue'
|
|
2
|
-
// import { getImgSrc } from '@/utils/common'
|
|
3
|
-
// import { useCommonStore } from '@jnrs/vue-core/pinia'
|
|
4
|
-
// import imgFailed from '@/assets/img/common/404.png'
|
|
5
|
-
import defaultAvatar from '@/assets/images/common/avatar.png'
|
|
6
|
-
|
|
7
|
-
export const useAvatar = () => {
|
|
8
|
-
// const { userInfo } = toRefs(useCommonStore())
|
|
9
|
-
const avatar = ref(defaultAvatar)
|
|
10
|
-
|
|
11
|
-
// watch(
|
|
12
|
-
// () => userInfo.value.avatar,
|
|
13
|
-
// nv => {
|
|
14
|
-
// if (nv) {
|
|
15
|
-
// getImgSrc(nv).then(res => {
|
|
16
|
-
// avatar.value = URL.createObjectURL(res.data)
|
|
17
|
-
// })
|
|
18
|
-
// }
|
|
19
|
-
// }
|
|
20
|
-
// )
|
|
21
|
-
|
|
22
|
-
// const at = userInfo.value.avatar
|
|
23
|
-
// if (at) {
|
|
24
|
-
// getImgSrc(at)
|
|
25
|
-
// .then(res => {
|
|
26
|
-
// avatar.value = URL.createObjectURL(res.data)
|
|
27
|
-
// })
|
|
28
|
-
// .catch(() => {
|
|
29
|
-
// avatar.value = imgFailed
|
|
30
|
-
// })
|
|
31
|
-
// }
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
avatar
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @Author : TanRui
|
|
3
|
-
* @WeChat : Tan578853789
|
|
4
|
-
* @File : useMouseSelection.ts
|
|
5
|
-
* @Date : 2025/04/14
|
|
6
|
-
* @Desc. : 鼠标框选表格进行多选、回车执行回调
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { onMounted, onUnmounted, onActivated, onDeactivated } from 'vue'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @param {*} containerClass 表格或容器类名,默认值 el-table,如果页面存在多个表格,则必须分别传入自定义类名
|
|
13
|
-
* @param {*} rowClass 表格或容器类名,默认值 el-table__row
|
|
14
|
-
* @param {*} callback 框选后的回调,默认处理的是 input[type='checkbox'] 元素
|
|
15
|
-
* @returns handleMouseDown
|
|
16
|
-
* @example <el-table @mousedown="handleMouseDown" @selection-change="handleSelectionChange">
|
|
17
|
-
*/
|
|
18
|
-
export function useMouseSelection({
|
|
19
|
-
containerClass = 'jn_table',
|
|
20
|
-
rowClass = 'el-table__row',
|
|
21
|
-
callback = handleSelection
|
|
22
|
-
} = {}) {
|
|
23
|
-
let isDragging = false
|
|
24
|
-
let startPoint = { x: 0, y: 0 }
|
|
25
|
-
let endPoint = { x: 0, y: 0 }
|
|
26
|
-
|
|
27
|
-
onMounted(() => {
|
|
28
|
-
window.addEventListener('mouseup', handleMouseUp)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
onUnmounted(() => {
|
|
32
|
-
window.removeEventListener('mouseup', handleMouseUp)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
onActivated(() => {
|
|
36
|
-
window.addEventListener('mouseup', handleMouseUp)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
onDeactivated(() => {
|
|
40
|
-
window.removeEventListener('mouseup', handleMouseUp)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
// 鼠标按下事件
|
|
44
|
-
const handleMouseDown = (event: MouseEvent) => {
|
|
45
|
-
window.addEventListener('mousemove', handleMouseMove)
|
|
46
|
-
isDragging = false
|
|
47
|
-
startPoint = { x: event.clientX, y: event.clientY }
|
|
48
|
-
endPoint = { ...startPoint }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 鼠标移动事件
|
|
52
|
-
const handleMouseMove = (event: MouseEvent) => {
|
|
53
|
-
endPoint = { x: event.clientX, y: event.clientY }
|
|
54
|
-
// 判断是否为拖动
|
|
55
|
-
if (Math.abs(endPoint.x - startPoint.x) > 5 && Math.abs(endPoint.y - startPoint.y) > 25) {
|
|
56
|
-
isDragging = true
|
|
57
|
-
}
|
|
58
|
-
// 如果没有拖动,则认为是单击
|
|
59
|
-
if (isDragging) {
|
|
60
|
-
selectRows()
|
|
61
|
-
}
|
|
62
|
-
setFixedRectBoxStyle()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// 鼠标抬起事件
|
|
66
|
-
const handleMouseUp = () => {
|
|
67
|
-
window.removeEventListener('mousemove', handleMouseMove)
|
|
68
|
-
// 如果没有拖动,则认为是单击
|
|
69
|
-
if (isDragging) {
|
|
70
|
-
isDragging = false
|
|
71
|
-
}
|
|
72
|
-
setFixedRectBoxStyle()
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 判断选中的行
|
|
76
|
-
const selectRows = () => {
|
|
77
|
-
const container = document.querySelector(`.${containerClass}`) as HTMLElement
|
|
78
|
-
const rows = container?.querySelectorAll(`.${rowClass}`) as NodeListOf<HTMLElement>
|
|
79
|
-
if (!rows) {
|
|
80
|
-
return false
|
|
81
|
-
}
|
|
82
|
-
rows.forEach((row) => {
|
|
83
|
-
const rect = row.getBoundingClientRect()
|
|
84
|
-
const isIntersecting =
|
|
85
|
-
rect.right > Math.min(startPoint.x, endPoint.x) &&
|
|
86
|
-
rect.left < Math.max(startPoint.x, endPoint.x) &&
|
|
87
|
-
rect.bottom > Math.min(startPoint.y, endPoint.y) &&
|
|
88
|
-
rect.top < Math.max(startPoint.y, endPoint.y)
|
|
89
|
-
if (isIntersecting && callback) {
|
|
90
|
-
callback(row)
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 设置框选框的位置
|
|
96
|
-
const setFixedRectBoxStyle = () => {
|
|
97
|
-
const selectionBox = document.querySelector('.useMouseSelection_fixedRectBox') as HTMLElement
|
|
98
|
-
if (selectionBox) {
|
|
99
|
-
const left = Math.min(startPoint.x, endPoint.x) + 'px'
|
|
100
|
-
const top = Math.min(startPoint.y, endPoint.y) + 'px'
|
|
101
|
-
const width = Math.abs(endPoint.x - startPoint.x) + 'px'
|
|
102
|
-
const height = Math.abs(endPoint.y - startPoint.y) + 'px'
|
|
103
|
-
Object.assign(selectionBox.style, {
|
|
104
|
-
display: isDragging ? 'block' : 'none',
|
|
105
|
-
left: left,
|
|
106
|
-
top: top,
|
|
107
|
-
width: width,
|
|
108
|
-
height: height
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
const container = document.querySelector(`.${containerClass}`) as HTMLElement
|
|
112
|
-
Object.assign(container.style, {
|
|
113
|
-
'user-select': isDragging ? 'none' : 'auto'
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
checkAndAddElement()
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
handleMouseDown
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 回调选中逻辑
|
|
126
|
-
const handleSelection = (row: HTMLElement) => {
|
|
127
|
-
const checkbox = row.querySelector("input[type='checkbox']") as HTMLInputElement
|
|
128
|
-
if (checkbox && !checkbox.checked) {
|
|
129
|
-
checkbox.click()
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// 往页面上添加框选框元素
|
|
134
|
-
const checkAndAddElement = () => {
|
|
135
|
-
const fixedRectBox = document.querySelector('.useMouseSelection_fixedRectBox')
|
|
136
|
-
if (!fixedRectBox) {
|
|
137
|
-
const newDiv = document.createElement('div')
|
|
138
|
-
newDiv.className = 'useMouseSelection_fixedRectBox'
|
|
139
|
-
Object.assign(newDiv.style, {
|
|
140
|
-
display: 'none',
|
|
141
|
-
position: 'fixed',
|
|
142
|
-
'z-index': 100,
|
|
143
|
-
border: '2px dashed #409eff',
|
|
144
|
-
'background-color': 'rgba(64, 158, 255, 0.2)',
|
|
145
|
-
'pointer-events': 'none',
|
|
146
|
-
'will-change': 'transform'
|
|
147
|
-
})
|
|
148
|
-
document.body.appendChild(newDiv)
|
|
149
|
-
}
|
|
150
|
-
}
|