n20-common-lib 3.1.1 → 3.1.2
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/package.json +1 -1
- package/src/_qiankun/communicator.js +152 -0
- package/src/_qiankun/eventBus.js +55 -0
- package/src/_qiankun/index.js +7 -5
- package/src/assets/css/element-variables.scss +1 -1
- package/src/components/ProFilterView/index.vue +8 -1
- package/src/components/TablePro/index.vue +1 -1
- package/src/components/TableProOperateColumn/OperateBtns.vue +68 -2
- package/src/components/ViewToggle/index.vue +73 -13
- package/src/components/v3/TablePro/index.vue +11 -6
- package/src/index.js +39 -1
- package/style/index.css +1 -1
- package/theme/blue.css +1 -1
- package/theme/cctcRed.css +1 -1
- package/theme/green.css +1 -1
- package/theme/lightBlue.css +1 -1
- package/theme/orange.css +1 -1
- package/theme/purple.css +1 -1
- package/theme/red.css +1 -1
- package/theme/yellow.css +1 -1
- package/src/components/TableProOperateColumn/OperateBtns_copy.vue +0 -141
package/package.json
CHANGED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import bus from './eventBus'
|
|
2
|
+
|
|
3
|
+
// 事件名常量
|
|
4
|
+
const MAIN_STATE_CHANGE = 'MAIN_STATE_CHANGE'
|
|
5
|
+
const CHILD_MESSAGE = 'CHILD_MESSAGE'
|
|
6
|
+
|
|
7
|
+
// 子应用间通讯的通道标记
|
|
8
|
+
const CHILD_CHANNEL = 'child-to-child'
|
|
9
|
+
|
|
10
|
+
// 模块级状态
|
|
11
|
+
let qiankunProps = null
|
|
12
|
+
let unsubscribe = null
|
|
13
|
+
let latestMainState = null
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 初始化 qiankun 通讯,应在 mount() 中调用
|
|
17
|
+
* @param {Object} props qiankun mount 阶段传入的 props
|
|
18
|
+
*/
|
|
19
|
+
export function initCommunication(props) {
|
|
20
|
+
// 防止重复初始化累积监听器
|
|
21
|
+
destroyCommunication()
|
|
22
|
+
|
|
23
|
+
qiankunProps = props || null
|
|
24
|
+
|
|
25
|
+
if (props && typeof props.onGlobalStateChange === 'function') {
|
|
26
|
+
unsubscribe = props.onGlobalStateChange((state, prev) => {
|
|
27
|
+
if (state && state._channel === CHILD_CHANNEL) {
|
|
28
|
+
// 子应用间消息,走独立通道
|
|
29
|
+
bus.$emit(CHILD_MESSAGE, {
|
|
30
|
+
type: state._type,
|
|
31
|
+
data: state._data,
|
|
32
|
+
from: state._from,
|
|
33
|
+
target: state._target
|
|
34
|
+
})
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
latestMainState = state
|
|
38
|
+
bus.$emit(MAIN_STATE_CHANGE, state, prev)
|
|
39
|
+
}, true)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 销毁通讯,清理监听器和缓存引用,应在 unmount() 中调用
|
|
45
|
+
*/
|
|
46
|
+
export function destroyCommunication() {
|
|
47
|
+
if (typeof unsubscribe === 'function') {
|
|
48
|
+
unsubscribe()
|
|
49
|
+
}
|
|
50
|
+
unsubscribe = null
|
|
51
|
+
qiankunProps = null
|
|
52
|
+
latestMainState = null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 通过 qiankun GlobalState 向主应用发送状态
|
|
57
|
+
* @param {Object} state 要发送的状态对象
|
|
58
|
+
* @returns {boolean} 是否发送成功
|
|
59
|
+
*/
|
|
60
|
+
export function setGlobalState(state) {
|
|
61
|
+
if (qiankunProps && typeof qiankunProps.setGlobalState === 'function') {
|
|
62
|
+
qiankunProps.setGlobalState(state)
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 监听主应用全局状态变化
|
|
70
|
+
* @param {Function} callback 回调函数 (state) => void
|
|
71
|
+
*/
|
|
72
|
+
export function onMainStateChange(callback) {
|
|
73
|
+
bus.$on(MAIN_STATE_CHANGE, callback)
|
|
74
|
+
// 已有缓存状态时立即同步回调
|
|
75
|
+
if (latestMainState !== null) {
|
|
76
|
+
callback(latestMainState)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 注销主应用状态变化监听
|
|
82
|
+
* @param {Function} callback 要注销的回调
|
|
83
|
+
*/
|
|
84
|
+
export function offMainStateChange(callback) {
|
|
85
|
+
bus.$off(MAIN_STATE_CHANGE, callback)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 获取缓存的主应用最新状态
|
|
90
|
+
* @returns {Object|null}
|
|
91
|
+
*/
|
|
92
|
+
export function getMainState() {
|
|
93
|
+
return latestMainState
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 通过 window.postMessage 向主应用发送结构化消息
|
|
98
|
+
* 格式兼容现有 handleTab.js 的 postMessage 协议
|
|
99
|
+
* @param {string} type 消息类型
|
|
100
|
+
* @param {*} data 消息数据
|
|
101
|
+
*/
|
|
102
|
+
export function postToMain(type, data) {
|
|
103
|
+
const originName = process.env.VUE_APP_NAME
|
|
104
|
+
window.postMessage({
|
|
105
|
+
[type]: data,
|
|
106
|
+
targetName: 'main',
|
|
107
|
+
originName
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 通过主应用中转向另一个子应用发送消息
|
|
113
|
+
* @param {string} childAppName 目标子应用名称
|
|
114
|
+
* @param {string} type 消息类型
|
|
115
|
+
* @param {*} data 消息数据
|
|
116
|
+
* @returns {boolean} 是否发送成功
|
|
117
|
+
*/
|
|
118
|
+
export function sendToChild(childAppName, type, data) {
|
|
119
|
+
if (qiankunProps && typeof qiankunProps.setGlobalState === 'function') {
|
|
120
|
+
qiankunProps.setGlobalState({
|
|
121
|
+
_channel: CHILD_CHANNEL,
|
|
122
|
+
_target: childAppName,
|
|
123
|
+
_from: process.env.VUE_APP_NAME,
|
|
124
|
+
_type: type,
|
|
125
|
+
_data: data
|
|
126
|
+
})
|
|
127
|
+
return true
|
|
128
|
+
}
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 监听其他子应用发来的消息
|
|
134
|
+
* @param {Function} callback 回调函数 ({ type, data, from, target }) => void
|
|
135
|
+
*/
|
|
136
|
+
export function onChildMessage(callback) {
|
|
137
|
+
bus.$on(CHILD_MESSAGE, callback)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 检测当前是否在 qiankun 环境中运行
|
|
142
|
+
* @returns {boolean}
|
|
143
|
+
*/
|
|
144
|
+
export function isQiankunEnvironment() {
|
|
145
|
+
return !!window.__POWERED_BY_QIANKUN__
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 透传 EventBus 方法
|
|
149
|
+
export const $on = bus.$on.bind(bus)
|
|
150
|
+
export const $off = bus.$off.bind(bus)
|
|
151
|
+
export const $emit = bus.$emit.bind(bus)
|
|
152
|
+
export const $once = bus.$once.bind(bus)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 轻量级发布/订阅事件总线,供 qiankun 通信模块内部使用,同时对外暴露
|
|
3
|
+
*/
|
|
4
|
+
class EventBus {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._events = Object.create(null)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
$on(event, fn) {
|
|
10
|
+
if (Array.isArray(event)) {
|
|
11
|
+
event.forEach((e) => this.$on(e, fn))
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
;(this._events[event] || (this._events[event] = [])).push(fn)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
$off(event, fn) {
|
|
18
|
+
if (!event) {
|
|
19
|
+
this._events = Object.create(null)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
const cbs = this._events[event]
|
|
23
|
+
if (!cbs) return
|
|
24
|
+
if (!fn) {
|
|
25
|
+
this._events[event] = null
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
for (let i = cbs.length - 1; i >= 0; i--) {
|
|
29
|
+
if (cbs[i] === fn || cbs[i].__onceFn__ === fn) {
|
|
30
|
+
cbs.splice(i, 1)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$emit(event, ...args) {
|
|
36
|
+
const cbs = this._events[event]
|
|
37
|
+
if (!cbs || !cbs.length) return
|
|
38
|
+
// 快照避免边触发边注销导致的遍历问题
|
|
39
|
+
const snapshot = cbs.slice()
|
|
40
|
+
for (let i = 0; i < snapshot.length; i++) {
|
|
41
|
+
snapshot[i](...args)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
$once(event, fn) {
|
|
46
|
+
const wrapper = (...args) => {
|
|
47
|
+
this.$off(event, wrapper)
|
|
48
|
+
fn(...args)
|
|
49
|
+
}
|
|
50
|
+
wrapper.__onceFn__ = fn
|
|
51
|
+
this.$on(event, wrapper)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default new EventBus()
|
package/src/_qiankun/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Vue from 'vue'
|
|
2
|
+
import { initCommunication, onMainStateChange, destroyCommunication } from './communicator'
|
|
2
3
|
|
|
3
4
|
const vuePar = {
|
|
4
5
|
render: undefined,
|
|
@@ -112,16 +113,17 @@ export async function bootstrap() {
|
|
|
112
113
|
// console.log('[vue] vue app bootstraped')
|
|
113
114
|
}
|
|
114
115
|
export async function mount(props) {
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
// state: 变更后的状态; prev 变更前的状态
|
|
118
|
-
// console.log('onGlobalStateChange111', state, prev)
|
|
116
|
+
// 监听主应用状态变化 -> 提交到 Vuex(在 initCommunication 前注册,避免初始状态丢失)
|
|
117
|
+
onMainStateChange((state) => {
|
|
119
118
|
vuePar?.store?.commit('MP/setGlobalState', state)
|
|
120
|
-
}
|
|
119
|
+
})
|
|
120
|
+
// 初始化统一通信层
|
|
121
|
+
initCommunication(props)
|
|
121
122
|
/* 从其他子运用再切回这个运用,是否保持之前的状态 */
|
|
122
123
|
KEEP_ALIVE && !props.router ? _mountKeep(props) : _mount(props)
|
|
123
124
|
}
|
|
124
125
|
export async function unmount(props) {
|
|
126
|
+
destroyCommunication()
|
|
125
127
|
/* 从其他子运用再切回这个运用,是否保持之前的状态 */
|
|
126
128
|
KEEP_ALIVE && !props.router ? _unmountKeep() : _unmount()
|
|
127
129
|
}
|
|
@@ -190,7 +190,7 @@ $--checkbox-disabled-checked-input-border-color: $--border-color-base !default;
|
|
|
190
190
|
$--checkbox-disabled-checked-icon-color: $--color-text-placeholder !default;
|
|
191
191
|
|
|
192
192
|
/// color||Color|0
|
|
193
|
-
$--checkbox-checked-font-color: #
|
|
193
|
+
$--checkbox-checked-font-color: #1d2129;
|
|
194
194
|
$--checkbox-checked-input-border-color: $--color-primary !default;
|
|
195
195
|
/// color||Color|0
|
|
196
196
|
$--checkbox-checked-background-color: $--color-primary !default;
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
</div>
|
|
144
144
|
<cl-advanced-filter
|
|
145
145
|
v-if="form.viewType === VIEW_TYPE.ADVANCED"
|
|
146
|
-
ref="
|
|
146
|
+
ref="filter_box"
|
|
147
147
|
:key="dialogFilterKey"
|
|
148
148
|
class="filter"
|
|
149
149
|
:visible="visible"
|
|
@@ -362,6 +362,13 @@ export default {
|
|
|
362
362
|
this.searchValue = { ...this.getInitialSearchValue, ...this.initialValue }
|
|
363
363
|
},
|
|
364
364
|
methods: {
|
|
365
|
+
/**
|
|
366
|
+
* 供外部调用更新 GroupData
|
|
367
|
+
* @param {Array} data - 筛选条件数据
|
|
368
|
+
*/
|
|
369
|
+
setGroupData(data) {
|
|
370
|
+
this.$refs.filter.mackData(data || [])
|
|
371
|
+
},
|
|
365
372
|
// 处理 slot 类型字段的输入事件,同时更新 searchValue 和 initialValue
|
|
366
373
|
handleSlotInput(fieldName, val) {
|
|
367
374
|
// 防御性检查:如果 fieldName 无效,不执行任何操作
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex-box flex-v flex-c table-operate-btns__wrapper">
|
|
3
3
|
<childrenOperateBtn :btn-list="dfBtns" :row="row" @command="(c) => $emit('command', c)" />
|
|
4
|
-
|
|
4
|
+
<!-- 更多操作下拉菜单:静态 moreBtns + 异步动态按钮 -->
|
|
5
|
+
<el-dropdown
|
|
6
|
+
v-if="showMoreBtn"
|
|
7
|
+
class="m-l-b"
|
|
8
|
+
@command="(c) => $emit('command', c)"
|
|
9
|
+
@visible-change="handleDropdownVisibleChange"
|
|
10
|
+
>
|
|
5
11
|
<el-button class="n20-icon-moren" size="mini" type="text" />
|
|
6
12
|
<el-dropdown-menu slot="dropdown" class="text-c">
|
|
13
|
+
<!-- 静态更多按钮 -->
|
|
7
14
|
<template v-for="(item, i) in moreBtns">
|
|
8
15
|
<el-dropdown-item v-if="item.children && item.children.length" :key="i">
|
|
9
16
|
<template>{{ item.label }}</template>
|
|
@@ -39,12 +46,39 @@
|
|
|
39
46
|
</template>
|
|
40
47
|
</el-dropdown-item>
|
|
41
48
|
</template>
|
|
49
|
+
<!-- 动态按钮加载中 -->
|
|
50
|
+
<el-dropdown-item v-if="dynamicLoading" disabled class="table-operate-loading">
|
|
51
|
+
<i class="el-icon-loading" /> {{ $lc('加载中') }}
|
|
52
|
+
</el-dropdown-item>
|
|
53
|
+
<!-- 动态按钮加载失败 -->
|
|
54
|
+
<el-dropdown-item v-else-if="dynamicError" disabled class="table-operate-error">
|
|
55
|
+
<i class="el-icon-warning" /> {{ $lc('加载失败') }}
|
|
56
|
+
</el-dropdown-item>
|
|
57
|
+
<!-- 动态按钮列表 -->
|
|
58
|
+
<template v-else>
|
|
59
|
+
<el-dropdown-item
|
|
60
|
+
v-for="(item, i) in dynamicBtns"
|
|
61
|
+
:key="'dynamic-' + i"
|
|
62
|
+
:command="item.command"
|
|
63
|
+
:disabled="item.disabled | dbdBtn(row)"
|
|
64
|
+
>
|
|
65
|
+
<el-badge v-if="resolveBadge(item)" :value="resolveBadge(item)" :max="item.badgeMax || 99">
|
|
66
|
+
<el-link v-if="item.type" :type="item.type" :underline="false">{{ item.label }}</el-link>
|
|
67
|
+
<template v-else>{{ item.label }}</template>
|
|
68
|
+
</el-badge>
|
|
69
|
+
<template v-else>
|
|
70
|
+
<el-link v-if="item.type" :type="item.type" :underline="false">{{ item.label }}</el-link>
|
|
71
|
+
<template v-else>{{ item.label }}</template>
|
|
72
|
+
</template>
|
|
73
|
+
</el-dropdown-item>
|
|
74
|
+
</template>
|
|
42
75
|
</el-dropdown-menu>
|
|
43
76
|
</el-dropdown>
|
|
44
77
|
</div>
|
|
45
78
|
</template>
|
|
46
79
|
<script>
|
|
47
80
|
import childrenOperateBtn from './childrenOperateBtn.vue'
|
|
81
|
+
import { $lc } from '../../utils/i18n/index.js'
|
|
48
82
|
|
|
49
83
|
export default {
|
|
50
84
|
name: 'TableOperate',
|
|
@@ -70,10 +104,19 @@ export default {
|
|
|
70
104
|
row: {
|
|
71
105
|
type: Object,
|
|
72
106
|
default: () => ({})
|
|
107
|
+
},
|
|
108
|
+
// 异步获取操作按钮函数,(row) => Promise<Array<BtnItem>>
|
|
109
|
+
getOperateBtns: {
|
|
110
|
+
type: Function,
|
|
111
|
+
default: null
|
|
73
112
|
}
|
|
74
113
|
},
|
|
75
114
|
data() {
|
|
76
|
-
return {
|
|
115
|
+
return {
|
|
116
|
+
dynamicBtns: [], // 异步加载的按钮列表
|
|
117
|
+
dynamicLoading: false, // 加载中状态
|
|
118
|
+
dynamicError: false // 加载失败状态
|
|
119
|
+
}
|
|
77
120
|
},
|
|
78
121
|
computed: {
|
|
79
122
|
hasBtns() {
|
|
@@ -92,9 +135,14 @@ export default {
|
|
|
92
135
|
} else {
|
|
93
136
|
return []
|
|
94
137
|
}
|
|
138
|
+
},
|
|
139
|
+
// 是否显示"更多"下拉按钮(静态 moreBtns 或异步 getOperateBtns 存在时)
|
|
140
|
+
showMoreBtn() {
|
|
141
|
+
return this.moreBtns.length > 0 || !!this.getOperateBtns
|
|
95
142
|
}
|
|
96
143
|
},
|
|
97
144
|
methods: {
|
|
145
|
+
$lc,
|
|
98
146
|
resolveBadge(item) {
|
|
99
147
|
if (!item.badge) return null
|
|
100
148
|
if (typeof item.badge === 'function') {
|
|
@@ -113,6 +161,24 @@ export default {
|
|
|
113
161
|
} else if (typeof isHas === 'function') {
|
|
114
162
|
return isHas(row)
|
|
115
163
|
}
|
|
164
|
+
},
|
|
165
|
+
// 下拉菜单展开/收起时触发异步加载
|
|
166
|
+
async handleDropdownVisibleChange(visible) {
|
|
167
|
+
if (!visible || !this.getOperateBtns) return
|
|
168
|
+
this.dynamicBtns = []
|
|
169
|
+
this.dynamicError = false
|
|
170
|
+
this.dynamicLoading = true
|
|
171
|
+
try {
|
|
172
|
+
const btns = await this.getOperateBtns(this.row)
|
|
173
|
+
// 按 isHas 过滤动态按钮
|
|
174
|
+
this.dynamicBtns = Array.isArray(btns) ? btns.filter((btn) => this.hasBtn(btn.isHas, this.row)) : []
|
|
175
|
+
} catch (e) {
|
|
176
|
+
console.warn('[TablePro] getOperateBtns failed:', e)
|
|
177
|
+
this.dynamicError = true
|
|
178
|
+
this.dynamicBtns = []
|
|
179
|
+
} finally {
|
|
180
|
+
this.dynamicLoading = false
|
|
181
|
+
}
|
|
116
182
|
}
|
|
117
183
|
}
|
|
118
184
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="n20-view-toggle" :class="{ disabled }">
|
|
3
|
-
<div class="n20-toggle-container">
|
|
3
|
+
<div class="n20-toggle-container" ref="container">
|
|
4
4
|
<div
|
|
5
5
|
v-for="(item, index) in options"
|
|
6
6
|
:key="item.value"
|
|
7
|
+
ref="items"
|
|
7
8
|
class="n20-toggle-item"
|
|
8
9
|
:class="{ active: value === item.value, 'is-disabled': item.disabled }"
|
|
9
10
|
:style="itemStyle"
|
|
@@ -33,7 +34,7 @@ export default {
|
|
|
33
34
|
},
|
|
34
35
|
itemWidth: {
|
|
35
36
|
type: [Number, String],
|
|
36
|
-
default:
|
|
37
|
+
default: ''
|
|
37
38
|
},
|
|
38
39
|
itemHeight: {
|
|
39
40
|
type: [Number, String],
|
|
@@ -44,31 +45,90 @@ export default {
|
|
|
44
45
|
default: false
|
|
45
46
|
}
|
|
46
47
|
},
|
|
48
|
+
data() {
|
|
49
|
+
return {
|
|
50
|
+
// 自适应模式下指示器的测量数据
|
|
51
|
+
measuredIndicator: { width: 0, height: 0, translateX: 0 }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
47
54
|
computed: {
|
|
55
|
+
// 是否为固定宽度模式
|
|
56
|
+
isFixedWidth() {
|
|
57
|
+
return this.itemWidth !== '' && this.itemWidth !== undefined && this.itemWidth !== null
|
|
58
|
+
},
|
|
48
59
|
itemStyle() {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
const style = {}
|
|
61
|
+
if (this.isFixedWidth) {
|
|
62
|
+
style.width = this.normalizeSize(this.itemWidth)
|
|
63
|
+
} else {
|
|
64
|
+
// 自适应模式:用 padding 撑开宽度
|
|
65
|
+
style.padding = '0 12px'
|
|
54
66
|
}
|
|
67
|
+
style.height = this.normalizeSize(this.itemHeight)
|
|
68
|
+
return style
|
|
55
69
|
},
|
|
56
70
|
indicatorStyle() {
|
|
57
|
-
const itemW = typeof this.itemWidth === 'number' ? this.itemWidth : parseInt(this.itemWidth)
|
|
58
|
-
const itemH = typeof this.itemHeight === 'number' ? this.itemHeight : parseInt(this.itemHeight)
|
|
59
71
|
const gap = 2
|
|
60
|
-
const
|
|
61
|
-
|
|
72
|
+
const itemH = typeof this.itemHeight === 'number' ? this.itemHeight : parseInt(this.itemHeight) || 30
|
|
73
|
+
if (this.isFixedWidth) {
|
|
74
|
+
const itemW = typeof this.itemWidth === 'number' ? this.itemWidth : parseInt(this.itemWidth) || 80
|
|
75
|
+
const activeIndex = this.options.findIndex((item) => item.value === this.value)
|
|
76
|
+
const translateX = activeIndex >= 0 ? activeIndex * itemW : 0
|
|
77
|
+
return {
|
|
78
|
+
width: `${itemW - 2 * gap}px`,
|
|
79
|
+
height: `${itemH - 2 * gap}px`,
|
|
80
|
+
top: `${gap}px`,
|
|
81
|
+
left: `${gap}px`,
|
|
82
|
+
transform: `translateX(${translateX}px)`
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 自适应模式:基于 DOM 测量定位
|
|
86
|
+
const { width, height, translateX } = this.measuredIndicator
|
|
62
87
|
return {
|
|
63
|
-
width: `${
|
|
64
|
-
height: `${
|
|
88
|
+
width: `${width - 2 * gap}px`,
|
|
89
|
+
height: `${height - 2 * gap}px`,
|
|
65
90
|
top: `${gap}px`,
|
|
66
91
|
left: `${gap}px`,
|
|
67
92
|
transform: `translateX(${translateX}px)`
|
|
68
93
|
}
|
|
69
94
|
}
|
|
70
95
|
},
|
|
96
|
+
watch: {
|
|
97
|
+
value() {
|
|
98
|
+
if (!this.isFixedWidth) this.$nextTick(this.updateIndicator)
|
|
99
|
+
},
|
|
100
|
+
options: {
|
|
101
|
+
deep: true,
|
|
102
|
+
handler() {
|
|
103
|
+
if (!this.isFixedWidth) this.$nextTick(this.updateIndicator)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
mounted() {
|
|
108
|
+
if (!this.isFixedWidth) this.updateIndicator()
|
|
109
|
+
},
|
|
71
110
|
methods: {
|
|
111
|
+
// 统一尺寸格式化
|
|
112
|
+
normalizeSize(val) {
|
|
113
|
+
if (typeof val === 'number') return `${val}px`
|
|
114
|
+
return isNaN(val) ? val : `${val}px`
|
|
115
|
+
},
|
|
116
|
+
// 自适应模式下测量激活项的位置
|
|
117
|
+
updateIndicator() {
|
|
118
|
+
const items = this.$refs.items
|
|
119
|
+
if (!items || !items.length) return
|
|
120
|
+
const activeIndex = this.options.findIndex((item) => item.value === this.value)
|
|
121
|
+
if (activeIndex < 0) {
|
|
122
|
+
this.measuredIndicator = { width: 0, height: 0, translateX: 0 }
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
const activeEl = items[activeIndex]
|
|
126
|
+
this.measuredIndicator = {
|
|
127
|
+
width: activeEl.offsetWidth,
|
|
128
|
+
height: activeEl.offsetHeight,
|
|
129
|
+
translateX: activeEl.offsetLeft
|
|
130
|
+
}
|
|
131
|
+
},
|
|
72
132
|
handleChange(val) {
|
|
73
133
|
if (this.disabled) return
|
|
74
134
|
const item = this.options.find((item) => item.value === val)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="v3-table-pro-wrapper" style="height: 100%; position: relative">
|
|
2
|
+
<div class="v3-table-pro-wrapper" style="height: 100%; position: relative; overflow: hidden">
|
|
3
3
|
<!-- 骨架屏 - 仅覆盖内容区域,不覆盖表头 -->
|
|
4
4
|
<div v-if="loading" class="table-pro-skeleton">
|
|
5
5
|
<div v-for="n in skeletonRows" :key="'skeleton-' + n" class="skeleton-row">
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
...$attrs.columnConfig,
|
|
36
36
|
...$attrs['column-config']
|
|
37
37
|
}"
|
|
38
|
-
:
|
|
38
|
+
:virtual-y-config="scrollY"
|
|
39
39
|
:sort-config="{
|
|
40
40
|
multiple: false,
|
|
41
41
|
remote: true,
|
|
@@ -281,7 +281,7 @@
|
|
|
281
281
|
</div>
|
|
282
282
|
</template>
|
|
283
283
|
<template #default="{ row, $rowIndex }">
|
|
284
|
-
<OperateBtns :btn-list="btnList" :row="row" @command="(c) => handleOperateCommand(c, row, $rowIndex)" />
|
|
284
|
+
<OperateBtns :btn-list="btnList" :row="row" :get-operate-btns="getOperateBtns" @command="(c) => handleOperateCommand(c, row, $rowIndex)" />
|
|
285
285
|
</template>
|
|
286
286
|
</vxe-column>
|
|
287
287
|
<template #empty>
|
|
@@ -420,7 +420,7 @@ export default {
|
|
|
420
420
|
scrollY: {
|
|
421
421
|
type: Object,
|
|
422
422
|
default: () => {
|
|
423
|
-
return { enabled: true,
|
|
423
|
+
return { enabled: true, gt: 0, model: 'wheel' }
|
|
424
424
|
}
|
|
425
425
|
},
|
|
426
426
|
data: {
|
|
@@ -567,6 +567,11 @@ export default {
|
|
|
567
567
|
operateColumnWidth: {
|
|
568
568
|
type: Number,
|
|
569
569
|
default: 180
|
|
570
|
+
},
|
|
571
|
+
// 异步获取操作按钮函数,(row) => Promise<Array<BtnItem>>
|
|
572
|
+
getOperateBtns: {
|
|
573
|
+
type: Function,
|
|
574
|
+
default: null
|
|
570
575
|
}
|
|
571
576
|
},
|
|
572
577
|
data() {
|
|
@@ -728,6 +733,8 @@ export default {
|
|
|
728
733
|
this.checkColumns = columns
|
|
729
734
|
// 将 remote: true 的远程列同步写入 this.columns,保持位置一致
|
|
730
735
|
this.syncRemoteColumns(columns)
|
|
736
|
+
// 强制 vxe-table 重建,确保固定列图标、颜色等表头状态正确回显
|
|
737
|
+
this.colsKey++
|
|
731
738
|
} else {
|
|
732
739
|
this.checkColumns = this.filterDefaultHidden(this.columns)
|
|
733
740
|
}
|
|
@@ -740,8 +747,6 @@ export default {
|
|
|
740
747
|
async refreshColumns() {
|
|
741
748
|
if (this.showColumn && this.pageId) {
|
|
742
749
|
await this.getColumns()
|
|
743
|
-
// 强制重建 vxe-table,确保列变更后视图正确刷新
|
|
744
|
-
this.colsKey++
|
|
745
750
|
}
|
|
746
751
|
},
|
|
747
752
|
// 将 API 返回中标记 remote: true 的列同步到 this.columns,保持对应位置一致
|
package/src/index.js
CHANGED
|
@@ -160,6 +160,26 @@ import fitlers from './fitlers'
|
|
|
160
160
|
/** 修正ElementUI的问题 */
|
|
161
161
|
import monitor from './utils/monitor.js'
|
|
162
162
|
import repairEl from './utils/repairElementUI'
|
|
163
|
+
|
|
164
|
+
/** qiankun 微前端通讯 */
|
|
165
|
+
import {
|
|
166
|
+
initCommunication,
|
|
167
|
+
destroyCommunication,
|
|
168
|
+
setGlobalState,
|
|
169
|
+
postToMain,
|
|
170
|
+
onMainStateChange,
|
|
171
|
+
offMainStateChange,
|
|
172
|
+
getMainState,
|
|
173
|
+
sendToChild,
|
|
174
|
+
onChildMessage,
|
|
175
|
+
isQiankunEnvironment,
|
|
176
|
+
$on,
|
|
177
|
+
$off,
|
|
178
|
+
$emit,
|
|
179
|
+
$once
|
|
180
|
+
} from './_qiankun/communicator'
|
|
181
|
+
import PM from './_qiankun/postMessage.js'
|
|
182
|
+
|
|
163
183
|
const components = [
|
|
164
184
|
ProFilterView,
|
|
165
185
|
DynamicFieldTable,
|
|
@@ -440,5 +460,23 @@ export {
|
|
|
440
460
|
type,
|
|
441
461
|
version,
|
|
442
462
|
watermark,
|
|
443
|
-
accountFormat
|
|
463
|
+
accountFormat,
|
|
464
|
+
// qiankun Vuex module
|
|
465
|
+
PM,
|
|
466
|
+
// qiankun 通讯函数
|
|
467
|
+
initCommunication,
|
|
468
|
+
destroyCommunication,
|
|
469
|
+
setGlobalState,
|
|
470
|
+
postToMain,
|
|
471
|
+
onMainStateChange,
|
|
472
|
+
offMainStateChange,
|
|
473
|
+
getMainState,
|
|
474
|
+
sendToChild,
|
|
475
|
+
onChildMessage,
|
|
476
|
+
isQiankunEnvironment,
|
|
477
|
+
// 事件总线
|
|
478
|
+
$on,
|
|
479
|
+
$off,
|
|
480
|
+
$emit,
|
|
481
|
+
$once
|
|
444
482
|
}
|