sh-view 2.8.13 → 2.9.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/package.json +4 -3
- package/packages/components/sh-calendar/index.vue +1 -1
- package/packages/components/sh-form/js/useForm.js +9 -3
- package/packages/components/sh-table/css/index.scss +13 -10
- package/packages/components/sh-table/grid.vue +26 -45
- package/packages/components/sh-table/js/props.js +11 -7
- package/packages/components/sh-table/js/tableMethods.js +19 -0
- package/packages/components/sh-table/js/useTable.js +116 -52
- package/packages/components/sh-table/table.vue +12 -18
- package/packages/components/sh-tabs/index.vue +6 -3
- package/packages/components/sh-tree/css/index.scss +1 -1
- package/packages/components/sh-tree/index.vue +1 -1
- package/packages/vxeTable/css/index.scss +0 -3
- package/packages/vxeTable/index.js +14 -2
- package/packages/vxeTable/plugins/export.js +36 -23
- package/packages/vxeTable/render/cell/vxe-render-input.vue +1 -1
- package/packages/vxeTable/render/cell/vxe-render-money.vue +1 -1
- package/packages/vxeTable/render/cell/vxe-render-tree.vue +1 -1
- package/packages/vxeTable/render/globalRenders.jsx +3 -2
- package/packages/vxeTable/render/mixin/cell-hooks.js +16 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sh-view",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "基于vxe-table二次封装,更包含Alert,Badge,Card,CodeEditor,Col,Corner,CountTo,Drawer,Empty,Form,Header,Icon,List,Loading,Modal,Noticebar,Poptip,Progress,PullRefresh,Query,Result,Row,Split,Grid,Table,Tabs,Tag,Toolbar,Tree,Upload,WaterFall,WaterMark等丰富组件库",
|
|
5
5
|
"main": "packages/index.js",
|
|
6
6
|
"typings": "types/index.d.ts",
|
|
@@ -32,14 +32,15 @@
|
|
|
32
32
|
"jszip": "^3.10.1",
|
|
33
33
|
"lunar-typescript": "^1.6.10",
|
|
34
34
|
"popper.js": "^1.16.1",
|
|
35
|
-
"sh-tools": "^2.2.
|
|
35
|
+
"sh-tools": "^2.2.12",
|
|
36
36
|
"tinymce": "^5.10.5",
|
|
37
37
|
"vue": "^3.3.4",
|
|
38
38
|
"vue-masonry": "^0.16.0",
|
|
39
39
|
"vue-router": "^4.2.4",
|
|
40
|
-
"vxe-table": "^4.5.
|
|
40
|
+
"vxe-table": "^4.5.21",
|
|
41
41
|
"vxe-table-plugin-export-pdf": "^4.0.1",
|
|
42
42
|
"vxe-table-plugin-export-xlsx": "^4.0.1",
|
|
43
|
+
"vxe-table-plugin-menus": "^4.0.0",
|
|
43
44
|
"xe-clipboard": "^1.10.2"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { computed, onBeforeMount, ref, watch } from 'vue'
|
|
2
|
-
import { getFieldRules } from '../../sh-table/js/tableMethods'
|
|
2
|
+
import { formulaReplaceAll, getFieldRules } from '../../sh-table/js/tableMethods'
|
|
3
3
|
|
|
4
4
|
// 表单默认配置
|
|
5
5
|
const formConfigDefault = {
|
|
@@ -35,11 +35,17 @@ export default function (props, context, proxy, isForm) {
|
|
|
35
35
|
const formRules = ref({})
|
|
36
36
|
|
|
37
37
|
const formBindConfig = computed(() => {
|
|
38
|
+
let formulaMap = {}
|
|
39
|
+
$vUtils.eachTree(props.items, column => {
|
|
40
|
+
let rformula = $vUtils.get(column, 'renderProps.formula')
|
|
41
|
+
if (rformula) formulaMap[column.field] = rformula
|
|
42
|
+
})
|
|
38
43
|
return Object.assign({ size: isForm ? 'medium' : 'small' }, formConfigDefault, props.formConfig, {
|
|
39
44
|
items: formItems.value,
|
|
40
45
|
data: props.data,
|
|
41
46
|
rules: props.valid ? formRules.value : {},
|
|
42
|
-
validConfig: props.validConfig
|
|
47
|
+
validConfig: props.validConfig,
|
|
48
|
+
formulaMap: formulaReplaceAll(formulaMap)
|
|
43
49
|
})
|
|
44
50
|
})
|
|
45
51
|
|
|
@@ -63,8 +69,8 @@ export default function (props, context, proxy, isForm) {
|
|
|
63
69
|
}
|
|
64
70
|
// 表单项编辑回调
|
|
65
71
|
const onFormEditClosed = async params => {
|
|
66
|
-
emit('edit-closed', params)
|
|
67
72
|
Object.assign(props.data, params.data)
|
|
73
|
+
emit('edit-closed', params)
|
|
68
74
|
}
|
|
69
75
|
// 表单校验不通过
|
|
70
76
|
const onFormSubmitInvalid = params => {
|
|
@@ -49,23 +49,26 @@
|
|
|
49
49
|
margin-right: 10px;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
.sh-table-toolbar
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
.sh-table-toolbar{
|
|
53
|
+
& > .vxe-toolbar{
|
|
54
|
+
padding: 0.6em;
|
|
55
|
+
.vxe-button{
|
|
56
|
+
& + .vxe-custom--wrapper{
|
|
57
|
+
margin-left: 0.8em;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
.vxe-custom--wrapper{
|
|
61
|
+
margin-left: 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
55
64
|
.sh-table-toolbar-item{
|
|
56
65
|
display: inline-block;
|
|
57
66
|
vertical-align: baseline;
|
|
58
|
-
|
|
59
|
-
margin: 5px;
|
|
67
|
+
margin: 0 0.8em;
|
|
60
68
|
& + .sh-table-toolbar-item{
|
|
61
69
|
margin-left: 0;
|
|
62
70
|
}
|
|
63
71
|
}
|
|
64
|
-
.sh-table-toolbar-left{
|
|
65
|
-
flex: 1;
|
|
66
|
-
}
|
|
67
|
-
.sh-table-toolbar-right{
|
|
68
|
-
}
|
|
69
72
|
}
|
|
70
73
|
// 关键词搜索高亮样式
|
|
71
74
|
.sh-keyword-lighten{
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="sh-vxe-table" :class="{ 'sh-table-ghost': tableGlobalConfig.ghost }" :style="{ height: wrapHeight }">
|
|
3
3
|
<vxe-grid
|
|
4
|
-
:id="id"
|
|
5
4
|
ref="tableRef"
|
|
6
5
|
:key="renderKey"
|
|
7
6
|
:class="{ 'is--hasfooter': showFooter }"
|
|
@@ -36,38 +35,34 @@
|
|
|
36
35
|
</sh-query>
|
|
37
36
|
</slot>
|
|
38
37
|
</template>
|
|
39
|
-
<template #toolbar>
|
|
40
|
-
<div class="sh-table-toolbar
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<div class="sh-table-toolbar-item">
|
|
38
|
+
<template v-if="isToolbarSlot" #toolbar>
|
|
39
|
+
<div class="sh-table-toolbar">
|
|
40
|
+
<vxe-toolbar ref="toolbarRef" v-bind="tableToolbarConfig">
|
|
41
|
+
<template #buttons>
|
|
42
|
+
<div v-if="tableGlobalConfig.title" class="sh-table-toolbar-item">
|
|
43
|
+
<h3 class="tableName">{{ tableGlobalConfig.tableName }}</h3>
|
|
44
|
+
</div>
|
|
45
|
+
<!--ps:全表搜索不支持反转-->
|
|
46
|
+
<div v-if="tableGlobalConfig.globalFilter" class="sh-table-toolbar-item">
|
|
47
|
+
<vxe-input v-model="tableFilterText" v-bind="tableFilterConfig" @blur="handleTableFilter" @clear="handleTableFilter" @search-click="handleTableFilter" />
|
|
48
|
+
<span v-if="tableFilterData">
|
|
49
|
+
共搜索到 <strong>{{ tableFilterData.length }}</strong> 条数据
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div v-if="slots.toolbarLeft" class="sh-table-toolbar-item">
|
|
53
|
+
<slot name="toolbarLeft"></slot>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
<template #tools>
|
|
57
|
+
<div v-if="slots.toolbarRight" class="sh-table-toolbar-item">
|
|
58
|
+
<slot name="toolbarRight"></slot>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="sh-table-toolbar-item" v-for="(tool, toolIndex) in tableTools" :key="toolIndex">
|
|
62
61
|
<vxe-button v-ripple :size="size" v-bind="tool" @click="handleTableTool(tool)"></vxe-button>
|
|
63
62
|
</div>
|
|
63
|
+
<div v-if="tableMoneyConfig.enabled" @click.stop class="sh-table-toolbar-item">单位:<sh-select v-model="tableMoneyUnit" v-bind="tableMoneyConfig" /></div>
|
|
64
64
|
</template>
|
|
65
|
-
|
|
66
|
-
<div v-if="tableGlobalConfig.zoom" class="sh-table-toolbar-item">
|
|
67
|
-
<vxe-button v-if="!tableIsFullscreen" v-ripple :size="size" icon="vxe-icon-zoom-out" @click="handleTableZoomBtn(true)">全屏</vxe-button>
|
|
68
|
-
<vxe-button v-else v-ripple :size="size" icon="vxe-icon-zoom-in" @click="handleTableZoomBtn(false)">退出全屏</vxe-button>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
65
|
+
</vxe-toolbar>
|
|
71
66
|
</div>
|
|
72
67
|
</template>
|
|
73
68
|
<template #pagerLeft>
|
|
@@ -135,22 +130,8 @@ export default defineComponent({
|
|
|
135
130
|
|
|
136
131
|
const useTableHooks = useTable(props, context, proxy, true)
|
|
137
132
|
|
|
138
|
-
const tableIsFullscreen = ref(false) // 表格是否全屏显示状态
|
|
139
|
-
|
|
140
|
-
// 表格切换全屏缩放按钮
|
|
141
|
-
const handleTableZoomBtn = val => {
|
|
142
|
-
tableIsFullscreen.value = val
|
|
143
|
-
if (val) {
|
|
144
|
-
useTableHooks.tableRef.value.zoom()
|
|
145
|
-
} else {
|
|
146
|
-
useTableHooks.tableRef.value.revert()
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
133
|
return {
|
|
151
|
-
...useTableHooks
|
|
152
|
-
tableIsFullscreen,
|
|
153
|
-
handleTableZoomBtn
|
|
134
|
+
...useTableHooks
|
|
154
135
|
}
|
|
155
136
|
}
|
|
156
137
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
id: {
|
|
3
3
|
type: String,
|
|
4
|
-
default: '
|
|
4
|
+
default: ''
|
|
5
5
|
},
|
|
6
6
|
dataSourse: {
|
|
7
7
|
type: Array,
|
|
@@ -240,12 +240,10 @@ export default {
|
|
|
240
240
|
return {}
|
|
241
241
|
}
|
|
242
242
|
},
|
|
243
|
-
scrollX:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
type: Object
|
|
248
|
-
},
|
|
243
|
+
scrollX: Object,
|
|
244
|
+
scrollY: Object,
|
|
245
|
+
params: Object,
|
|
246
|
+
footerData: Array,
|
|
249
247
|
|
|
250
248
|
// 扩展配置
|
|
251
249
|
disabled: {
|
|
@@ -270,6 +268,12 @@ export default {
|
|
|
270
268
|
return {}
|
|
271
269
|
}
|
|
272
270
|
},
|
|
271
|
+
menuConfig: {
|
|
272
|
+
type: Object,
|
|
273
|
+
default() {
|
|
274
|
+
return {}
|
|
275
|
+
}
|
|
276
|
+
},
|
|
273
277
|
footerCalculate: {
|
|
274
278
|
type: Object,
|
|
275
279
|
default() {
|
|
@@ -171,3 +171,22 @@ export const getTransfarFields = (oriArr = [], { slots, columnObj, isSearch }) =
|
|
|
171
171
|
})
|
|
172
172
|
return { columnsArr, columnsFlatArr, formItemsArr, rules }
|
|
173
173
|
}
|
|
174
|
+
// 递归替换嵌套依赖公式
|
|
175
|
+
export const formulaReplaceAll = formulaMap => {
|
|
176
|
+
Object.keys(formulaMap).forEach(key => {
|
|
177
|
+
let dowhile = true
|
|
178
|
+
while (dowhile) {
|
|
179
|
+
let regUKey = formulaMap[key].match(/\$?[a-zA-Z0-9_.]{0,}\{[A-Za-z0-9_.]{1,}\}/gi) || []
|
|
180
|
+
regUKey = regUKey.filter(item => item.startsWith('${') || item.startsWith('{')).map((key, keyIndex) => key.replace(/\$?{|}/gi, ''))
|
|
181
|
+
dowhile = !regUKey.every((keyc, indexkey) => {
|
|
182
|
+
if (formulaMap[keyc]) {
|
|
183
|
+
formulaMap[key] = formulaMap[key].replace('{' + keyc + '}', '(' + formulaMap[keyc] + ')')
|
|
184
|
+
return false
|
|
185
|
+
} else {
|
|
186
|
+
return true
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
return formulaMap
|
|
192
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
|
2
|
-
import { columnDefaultFilterMethod, tableFooterCompute, getTransfarFields } from './tableMethods'
|
|
1
|
+
import { computed, onMounted, reactive, ref, watch, nextTick } from 'vue'
|
|
2
|
+
import { columnDefaultFilterMethod, tableFooterCompute, getTransfarFields, formulaReplaceAll } from './tableMethods'
|
|
3
3
|
|
|
4
4
|
// 记录自定义参数,传给vxe要过滤掉
|
|
5
5
|
let omitProps = [
|
|
@@ -39,14 +39,37 @@ const exportAndPrintDefault = {
|
|
|
39
39
|
isAllExpand: true,
|
|
40
40
|
download: true,
|
|
41
41
|
useStyle: false,
|
|
42
|
+
isUnit: false,
|
|
42
43
|
columnFilterMethod: columnDefaultFilterMethod
|
|
43
44
|
}
|
|
45
|
+
const menuConfigDefault = {
|
|
46
|
+
header: {
|
|
47
|
+
options: [
|
|
48
|
+
[
|
|
49
|
+
{ code: 'HIDDEN_COLUMN', name: '隐藏' },
|
|
50
|
+
{ code: 'RESET_COLUMN', name: '取消隐藏' },
|
|
51
|
+
{ code: 'FIXED_LEFT_COLUMN', name: '固定到左侧' },
|
|
52
|
+
{ code: 'FIXED_RIGHT_COLUMN', name: '固定到右侧' },
|
|
53
|
+
{ code: 'CLEAR_FIXED_COLUMN', name: '取消固定' }
|
|
54
|
+
]
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
body: {
|
|
58
|
+
options: [
|
|
59
|
+
[
|
|
60
|
+
{ code: 'SORT_ASC', name: '升序', prefixIcon: 'fa fa-sort-alpha-desc' },
|
|
61
|
+
{ code: 'SORT_DESC', name: '倒序', prefixIcon: 'fa fa-sort-alpha-desc' },
|
|
62
|
+
{ code: 'CLEAR_SORT', name: '清除排序' }
|
|
63
|
+
]
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
44
67
|
const toolsList = [
|
|
45
|
-
{ status: 'primary', code: 'addRow',
|
|
46
|
-
{ status: 'danger', code: 'deleteRow',
|
|
47
|
-
{ status: 'primary', code: 'import', icon: 'vxe-icon-upload',
|
|
48
|
-
{ status: 'success', code: 'export', icon: 'vxe-icon-download',
|
|
49
|
-
{ status: 'warning', code: 'print', icon: 'vxe-icon-print',
|
|
68
|
+
{ status: 'primary', code: 'addRow', icon: 'vxe-icon-square-plus', title: '新增行', circle: true },
|
|
69
|
+
{ status: 'danger', code: 'deleteRow', icon: 'vxe-icon-square-minus', title: '删除行', circle: true },
|
|
70
|
+
{ status: 'primary', code: 'import', icon: 'vxe-icon-upload', title: '导入', circle: true },
|
|
71
|
+
{ status: 'success', code: 'export', icon: 'vxe-icon-download', title: '导出', circle: true },
|
|
72
|
+
{ status: 'warning', code: 'print', icon: 'vxe-icon-print', title: '打印', circle: true }
|
|
50
73
|
]
|
|
51
74
|
// 表头默认值
|
|
52
75
|
const columnObjDefault = { minWidth: 120, sortable: true, filter: true }
|
|
@@ -58,10 +81,11 @@ const columnsMapDefault = {
|
|
|
58
81
|
}
|
|
59
82
|
|
|
60
83
|
export default function (props, context, proxy, isGrid) {
|
|
61
|
-
const { $vUtils, $vTableSetup, $vxePluginNames } = proxy
|
|
84
|
+
const { $vUtils, $vTableSetup, $vxePluginNames, $route } = proxy
|
|
62
85
|
const { emit, slots } = context
|
|
63
86
|
|
|
64
87
|
const tableRef = ref()
|
|
88
|
+
const toolbarRef = ref()
|
|
65
89
|
const renderKey = ref(1) // 渲染key值
|
|
66
90
|
const selectedRowKeys = ref([]) // table选中keys
|
|
67
91
|
const selectionRows = ref([]) // table选中records
|
|
@@ -106,18 +130,19 @@ export default function (props, context, proxy, isGrid) {
|
|
|
106
130
|
return hasTree.value ? Object.assign({}, props.treeConfig) : null
|
|
107
131
|
})
|
|
108
132
|
const tableSeqConfig = computed(() => {
|
|
133
|
+
const pagerConfig = tablePagerConfig.value
|
|
134
|
+
const pageStart = (+pagerConfig.pageSize || 50) * ((+pagerConfig.currentPage || 1) - 1)
|
|
109
135
|
return {
|
|
110
|
-
seqMethod: ({ $table, seq, $
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return $seq ? $seq + '.' + seq : seq
|
|
136
|
+
seqMethod: ({ $table, seq, $rowIndex }) => {
|
|
137
|
+
if (pagerConfig && pagerConfig.enabled) {
|
|
138
|
+
if (String(seq).includes('.')) {
|
|
139
|
+
const seqIndex = seq.indexOf('.')
|
|
140
|
+
const seqStart = seq.substr(0, seqIndex)
|
|
141
|
+
return $vUtils.add(pageStart, seqStart) + seq.substr(seqIndex)
|
|
142
|
+
}
|
|
143
|
+
return $vUtils.add(pageStart, seq)
|
|
119
144
|
}
|
|
120
|
-
return seq
|
|
145
|
+
return seq
|
|
121
146
|
}
|
|
122
147
|
}
|
|
123
148
|
})
|
|
@@ -166,14 +191,23 @@ export default function (props, context, proxy, isGrid) {
|
|
|
166
191
|
tableVmConfig.value
|
|
167
192
|
)
|
|
168
193
|
})
|
|
194
|
+
const tableToolbarConfig = computed(() => {
|
|
195
|
+
return Object.assign({ perfect: true, zoom: tableGlobalConfig.value.zoom, custom: tableGlobalConfig.value.custom }, props.toolbarConfig, tableVmConfig.value)
|
|
196
|
+
})
|
|
197
|
+
const tableMenuConfig = computed(() => Object.assign({ enabled: false }, menuConfigDefault, props.menuConfig))
|
|
169
198
|
const tableFilterConfig = computed(() => Object.assign({ clearable: true, type: 'search', placeholder: '全局关键字搜索' }, tableVmConfig.value))
|
|
170
199
|
const tableSlots = computed(() => {
|
|
171
200
|
let disSlots = ['top', 'head', 'foot', 'form', 'toolbar', 'formLeft', 'formRight', 'toolbarLeft', 'toolbarRight', 'pagerLeft', 'pagerRight']
|
|
172
201
|
return Object.keys(slots).filter(key => !disSlots.includes(key))
|
|
173
202
|
})
|
|
174
203
|
const tableBindConfig = computed(() => {
|
|
175
|
-
let defaultProps = { footerMethod: tableFooterMethod, footerSpanMethod: tableFooterSpanMethod }
|
|
204
|
+
let defaultProps = { id: `sh-table-${$route.fullPath}`, footerMethod: tableFooterMethod, footerSpanMethod: tableFooterSpanMethod }
|
|
176
205
|
let tableProps = $vUtils.omit(props, (val, key) => omitProps.includes(key) || $vUtils.isNone(val))
|
|
206
|
+
let formulaMap = {}
|
|
207
|
+
$vUtils.eachTree(props.columns, column => {
|
|
208
|
+
let rformula = $vUtils.get(column, 'renderProps.formula')
|
|
209
|
+
if (rformula) formulaMap[column.field] = rformula
|
|
210
|
+
})
|
|
177
211
|
let shProps = {
|
|
178
212
|
data: tableViewData.value,
|
|
179
213
|
stripe: props.stripe && !tableTreeConfig.value,
|
|
@@ -186,8 +220,11 @@ export default function (props, context, proxy, isGrid) {
|
|
|
186
220
|
exportConfig: tableExportConfig.value,
|
|
187
221
|
printConfig: tablePrintConfig.value,
|
|
188
222
|
moneyConfig: tableMoneyConfig.value,
|
|
189
|
-
moneyUnit: tableMoneyUnit.value
|
|
223
|
+
moneyUnit: tableMoneyUnit.value,
|
|
224
|
+
menuConfig: tableMenuConfig.value,
|
|
225
|
+
formulaMap: formulaReplaceAll(formulaMap)
|
|
190
226
|
}
|
|
227
|
+
if (tableMenuConfig.value.enabled) shProps.menuConfig = tableMenuConfig.value
|
|
191
228
|
return Object.assign(defaultProps, tableProps, shProps)
|
|
192
229
|
})
|
|
193
230
|
const importBindConfig = reactive({
|
|
@@ -197,6 +234,15 @@ export default function (props, context, proxy, isGrid) {
|
|
|
197
234
|
importConfig: props.importConfig,
|
|
198
235
|
importRules: tableEditRules.value
|
|
199
236
|
})
|
|
237
|
+
const isToolbarSlot = computed(() => {
|
|
238
|
+
const globalFg = tableGlobalConfig.value
|
|
239
|
+
const toolbarFg = tableToolbarConfig.value
|
|
240
|
+
const toolsFg = tableTools.value
|
|
241
|
+
const globalTool = globalFg.title || globalFg.globalFilter
|
|
242
|
+
const toolbarTool = toolbarFg.import || toolbarFg.export || toolbarFg.print || toolbarFg.refresh || toolbarFg.custom
|
|
243
|
+
const slotsTool = slots.toolbar || slots.toolbarLeft || slots.toolbarRight
|
|
244
|
+
return globalTool || toolbarTool || slotsTool || toolsFg.length > 0
|
|
245
|
+
})
|
|
200
246
|
|
|
201
247
|
// 获取选中数据
|
|
202
248
|
const getSelectionData = () => {
|
|
@@ -205,16 +251,23 @@ export default function (props, context, proxy, isGrid) {
|
|
|
205
251
|
// 获取全部数据
|
|
206
252
|
// visible为true获取视图数据
|
|
207
253
|
const getFullData = params => {
|
|
208
|
-
const { visible = false, deleteXid = false } = params || {}
|
|
209
|
-
let
|
|
254
|
+
const { visible = false, deleteXid = false, datas = null } = params || {}
|
|
255
|
+
let fullData = datas || tableRef.value.internalData.afterFullData
|
|
256
|
+
let data = $vUtils.clone(fullData, true)
|
|
210
257
|
const columns = tableRef.value.getColumns()
|
|
211
258
|
let rnameColumns = columns.filter(col => col.rname)
|
|
212
259
|
rnameColumns.forEach(col => {
|
|
213
260
|
let { property, rname, rprops } = col
|
|
214
261
|
data.forEach(row => {
|
|
215
262
|
let cellValue = $vUtils.get(row, property)
|
|
216
|
-
let { rvalue, rtext } = $vUtils.formatRender(cellValue, property, row, rname, rprops, proxy)
|
|
217
|
-
|
|
263
|
+
let { rvalue, rtext } = $vUtils.formatRender(cellValue, property, row, rname, { ...rprops, commafy: false }, proxy)
|
|
264
|
+
if (rprops.bill && rprops.bill !== '0') {
|
|
265
|
+
$vUtils.set(row, property, rvalue)
|
|
266
|
+
} else if (visible) {
|
|
267
|
+
$vUtils.set(row, property, rtext)
|
|
268
|
+
} else {
|
|
269
|
+
$vUtils.set(row, property, rvalue)
|
|
270
|
+
}
|
|
218
271
|
if (deleteXid) delete row._XID
|
|
219
272
|
})
|
|
220
273
|
})
|
|
@@ -223,10 +276,18 @@ export default function (props, context, proxy, isGrid) {
|
|
|
223
276
|
// 表格打印、导出统一导出前渲染数据
|
|
224
277
|
const getExportPrintDataByOption = (obj = {}, type) => {
|
|
225
278
|
let { options, content } = obj
|
|
226
|
-
let { data, columns, mode, original } = options
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
279
|
+
let { data, columns, mode, original, isUnit } = options
|
|
280
|
+
let exportDatas = data
|
|
281
|
+
$vUtils.eachTree(columns, col => {
|
|
282
|
+
const { rname, rprops } = col
|
|
283
|
+
if (rname === '$vMoney' && isUnit && rprops.moneyOption?.label && !col.headerExportMethod && !original) {
|
|
284
|
+
col.headerExportMethod = () => {
|
|
285
|
+
return `${col.title}(${rprops.moneyOption.label})`
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
if (mode === 'selected') exportDatas = getSelectionData()
|
|
290
|
+
options.data = getFullData({ deleteXid: true, visible: !original, datas: exportDatas })
|
|
230
291
|
return type === 'print' ? content : obj
|
|
231
292
|
}
|
|
232
293
|
|
|
@@ -235,8 +296,9 @@ export default function (props, context, proxy, isGrid) {
|
|
|
235
296
|
const { footerCalculate } = props
|
|
236
297
|
let footerData = []
|
|
237
298
|
let footerCalculateList = footerCalculate.calculate || []
|
|
299
|
+
let fullData = getFullData({ deleteXid: true, datas: data })
|
|
238
300
|
footerCalculateList.forEach(computeType => {
|
|
239
|
-
footerData.push(tableFooterCompute(columns,
|
|
301
|
+
footerData.push(tableFooterCompute(columns, fullData, computeType, footerCalculate[computeType]))
|
|
240
302
|
})
|
|
241
303
|
return footerData
|
|
242
304
|
}
|
|
@@ -330,19 +392,6 @@ export default function (props, context, proxy, isGrid) {
|
|
|
330
392
|
}
|
|
331
393
|
// 只对 edit-config 配置时有效,单元格编辑状态下被关闭时会触发该事件
|
|
332
394
|
const onEditClosed = params => {
|
|
333
|
-
// 此操作火狐浏览器(bug(vxe):编辑后不触发渲染器的blur问题)
|
|
334
|
-
const userAgent = navigator.userAgent
|
|
335
|
-
if (userAgent.indexOf('Firefox') !== -1) {
|
|
336
|
-
let { row, column } = params
|
|
337
|
-
let editModel = $vUtils.get(column, 'params.__RowEditModelValue')
|
|
338
|
-
if (editModel !== undefined) {
|
|
339
|
-
if (['$vMoney'].includes(column.rname) && !$vUtils.isNone(editModel)) editModel = $vUtils.multiply(editModel, column.rprops.moneyUnit || 1)
|
|
340
|
-
let { rvalue, rtext } = $vUtils.formatRender(editModel, column.property, row, column.rname, column.rprops, proxy, true)
|
|
341
|
-
editModel = rvalue
|
|
342
|
-
$vUtils.set(row, column.property, editModel)
|
|
343
|
-
delete column.params.__RowEditModelValue
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
395
|
emit('edit-closed', params)
|
|
347
396
|
}
|
|
348
397
|
// 只对 edit-config 配置时有效,单元格被激活编辑时会触发该事件
|
|
@@ -479,6 +528,7 @@ export default function (props, context, proxy, isGrid) {
|
|
|
479
528
|
handleTablePrintBtn()
|
|
480
529
|
break
|
|
481
530
|
}
|
|
531
|
+
emit('toolbar-btn-click', code)
|
|
482
532
|
}
|
|
483
533
|
// 表格导出按钮
|
|
484
534
|
const handleTableExportBtn = options => {
|
|
@@ -498,6 +548,17 @@ export default function (props, context, proxy, isGrid) {
|
|
|
498
548
|
}
|
|
499
549
|
return tableRef.value.openPrint(options)
|
|
500
550
|
}
|
|
551
|
+
// 关联toolbar
|
|
552
|
+
const connectToolbar = () => {
|
|
553
|
+
nextTick(() => {
|
|
554
|
+
// 将表格和工具栏进行关联
|
|
555
|
+
const $table = tableRef.value
|
|
556
|
+
const $toolbar = toolbarRef.value
|
|
557
|
+
if ($table && $toolbar) {
|
|
558
|
+
$table.connect($toolbar)
|
|
559
|
+
}
|
|
560
|
+
})
|
|
561
|
+
}
|
|
501
562
|
|
|
502
563
|
// 自定义操作列点击事件
|
|
503
564
|
const handleGoptionClick = async (btnObj, dataObj) => {
|
|
@@ -554,9 +615,7 @@ export default function (props, context, proxy, isGrid) {
|
|
|
554
615
|
value => {
|
|
555
616
|
initTableColumns()
|
|
556
617
|
},
|
|
557
|
-
{
|
|
558
|
-
deep: true
|
|
559
|
-
}
|
|
618
|
+
{ deep: true }
|
|
560
619
|
)
|
|
561
620
|
watch(
|
|
562
621
|
() => props.dataSourse,
|
|
@@ -564,24 +623,27 @@ export default function (props, context, proxy, isGrid) {
|
|
|
564
623
|
updateSelection([])
|
|
565
624
|
updateExpended()
|
|
566
625
|
},
|
|
567
|
-
{
|
|
568
|
-
immediate: true
|
|
569
|
-
}
|
|
626
|
+
{ immediate: true }
|
|
570
627
|
)
|
|
571
628
|
watch(
|
|
572
629
|
() => props.editRules,
|
|
573
630
|
value => {
|
|
574
631
|
initEditRules(value)
|
|
575
632
|
},
|
|
576
|
-
{
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
633
|
+
{ deep: true, immediate: true }
|
|
634
|
+
)
|
|
635
|
+
watch(
|
|
636
|
+
() => isToolbarSlot.value,
|
|
637
|
+
() => {
|
|
638
|
+
connectToolbar()
|
|
639
|
+
},
|
|
640
|
+
{ deep: true, immediate: true }
|
|
580
641
|
)
|
|
581
642
|
|
|
582
643
|
return {
|
|
583
644
|
slots,
|
|
584
645
|
tableRef,
|
|
646
|
+
toolbarRef,
|
|
585
647
|
renderKey,
|
|
586
648
|
wrapHeight,
|
|
587
649
|
tableGlobalConfig,
|
|
@@ -590,11 +652,13 @@ export default function (props, context, proxy, isGrid) {
|
|
|
590
652
|
tableColumnObjConfig,
|
|
591
653
|
tablePagerConfig,
|
|
592
654
|
tableQueryConfig,
|
|
655
|
+
tableToolbarConfig,
|
|
593
656
|
tableFilterText,
|
|
594
657
|
tableFilterConfig,
|
|
595
658
|
tableFilterData,
|
|
596
659
|
tableTools,
|
|
597
660
|
tableSlots,
|
|
661
|
+
isToolbarSlot,
|
|
598
662
|
selectionRows,
|
|
599
663
|
tableBindConfig,
|
|
600
664
|
importBindConfig,
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
<div v-if="isToolbarSlot" ref="toolbarSlotRef" v-resize="handleResize" class="sh-table-toolbar">
|
|
13
13
|
<slot name="toolbar">
|
|
14
|
-
<
|
|
15
|
-
<
|
|
14
|
+
<vxe-toolbar ref="toolbarRef" v-bind="tableToolbarConfig">
|
|
15
|
+
<template #buttons>
|
|
16
16
|
<div v-if="tableGlobalConfig.title" class="sh-table-toolbar-item">
|
|
17
17
|
<h3 class="tableName">{{ tableGlobalConfig.tableName }}</h3>
|
|
18
18
|
</div>
|
|
@@ -26,24 +26,21 @@
|
|
|
26
26
|
<div v-if="slots.toolbarLeft" class="sh-table-toolbar-item">
|
|
27
27
|
<slot name="toolbarLeft"></slot>
|
|
28
28
|
</div>
|
|
29
|
-
</
|
|
30
|
-
<
|
|
29
|
+
</template>
|
|
30
|
+
<template #tools>
|
|
31
31
|
<div v-if="slots.toolbarRight" class="sh-table-toolbar-item">
|
|
32
32
|
<slot name="toolbarRight"></slot>
|
|
33
33
|
</div>
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
</div>
|
|
38
|
-
</template>
|
|
34
|
+
<div class="sh-table-toolbar-item" v-for="(tool, toolIndex) in tableTools" :key="toolIndex">
|
|
35
|
+
<vxe-button v-ripple :size="size" v-bind="tool" @click="handleTableTool(tool)"></vxe-button>
|
|
36
|
+
</div>
|
|
39
37
|
<div v-if="tableMoneyConfig.enabled" @click.stop class="sh-table-toolbar-item">单位:<sh-select v-model="tableMoneyUnit" v-bind="tableMoneyConfig" /></div>
|
|
40
|
-
</
|
|
41
|
-
</
|
|
38
|
+
</template>
|
|
39
|
+
</vxe-toolbar>
|
|
42
40
|
</slot>
|
|
43
41
|
</div>
|
|
44
42
|
<div v-if="isHeadSlot" ref="headSlotRef" v-resize="handleResize" class="sh-table-head"><slot name="head"></slot></div>
|
|
45
43
|
<vxe-table
|
|
46
|
-
:id="id"
|
|
47
44
|
ref="tableRef"
|
|
48
45
|
:key="renderKey"
|
|
49
46
|
:class="{ 'is--hasfooter': showFooter }"
|
|
@@ -93,7 +90,7 @@
|
|
|
93
90
|
</template>
|
|
94
91
|
|
|
95
92
|
<script>
|
|
96
|
-
import { computed, defineComponent, getCurrentInstance,
|
|
93
|
+
import { computed, defineComponent, getCurrentInstance, ref, watch, nextTick } from 'vue'
|
|
97
94
|
import './css/index.scss'
|
|
98
95
|
|
|
99
96
|
import props from './js/props'
|
|
@@ -125,6 +122,7 @@ export default defineComponent({
|
|
|
125
122
|
'page-change',
|
|
126
123
|
'toolbar-button-click',
|
|
127
124
|
'toolbar-tool-click',
|
|
125
|
+
'toolbar-btn-click',
|
|
128
126
|
|
|
129
127
|
'form-submit',
|
|
130
128
|
'form-reset',
|
|
@@ -145,16 +143,13 @@ export default defineComponent({
|
|
|
145
143
|
const footSlotRef = ref()
|
|
146
144
|
const pagerSlotRef = ref()
|
|
147
145
|
const bottomSlotRef = ref()
|
|
146
|
+
|
|
148
147
|
const tableHeight = ref(props.height)
|
|
149
148
|
|
|
150
149
|
const useTableHooks = useTable(props, context, proxy)
|
|
151
150
|
|
|
152
151
|
const isTopSlot = computed(() => Boolean(slots.top))
|
|
153
152
|
const isFormSlot = computed(() => useTableHooks.tableGlobalConfig.value.search || Boolean(slots.form))
|
|
154
|
-
const isToolbarSlot = computed(() => {
|
|
155
|
-
const { title, globalFilter } = useTableHooks.tableGlobalConfig.value
|
|
156
|
-
return title || globalFilter || slots.toolbar || slots.toolbarLeft || slots.toolbarRight || useTableHooks.tableTools.value.length
|
|
157
|
-
})
|
|
158
153
|
const isHeadSlot = computed(() => Boolean(slots.head))
|
|
159
154
|
const isFootSlot = computed(() => Boolean(slots.foot))
|
|
160
155
|
const isPagerSlot = computed(() => useTableHooks.tablePagerConfig.value.enabled)
|
|
@@ -184,7 +179,6 @@ export default defineComponent({
|
|
|
184
179
|
tableHeight,
|
|
185
180
|
isTopSlot,
|
|
186
181
|
isFormSlot,
|
|
187
|
-
isToolbarSlot,
|
|
188
182
|
isHeadSlot,
|
|
189
183
|
isFootSlot,
|
|
190
184
|
isPagerSlot,
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
<div ref="navScrollRef" class="sh-tabs-nav-scroll" @DOMMouseScroll="handleScroll" @mousewheel="handleScroll">
|
|
10
10
|
<div ref="navRef" v-resize="handleResize" class="sh-tabs-nav-inner" :style="navStyle">
|
|
11
11
|
<template v-for="(tab, tabIndex) in tabList" :key="tabIndex">
|
|
12
|
-
<div v-
|
|
12
|
+
<div v-bind="getTabItemBind(tab, tabIndex)" @click="handleChange(tab)">
|
|
13
13
|
<slot name="tabItem" v-bind="{ ...tab, isActive: tab[labelKey] === activeKey }">
|
|
14
14
|
<div v-if="tab.icon" class="sh-tab-icon"><sh-icon :type="tab.icon"></sh-icon></div>
|
|
15
|
-
<div class="sh-tab-label">{{ tab[labelField] }}</div>
|
|
15
|
+
<div v-if="tab[labelField]" class="sh-tab-label">{{ tab[labelField] }}</div>
|
|
16
16
|
<div v-if="getTabIsClosable(tab)" class="sh-tab-close" @click.stop="handleClose(tab)"><sh-icon type="ios-close"></sh-icon></div>
|
|
17
17
|
</slot>
|
|
18
18
|
</div>
|
|
@@ -445,7 +445,10 @@ export default defineComponent({
|
|
|
445
445
|
.sh-tab-icon {
|
|
446
446
|
display: inline-flex;
|
|
447
447
|
align-items: center;
|
|
448
|
-
padding: 0 0.
|
|
448
|
+
padding: 0 0.5em;
|
|
449
|
+
& + .sh-tab-label {
|
|
450
|
+
padding-left: 0;
|
|
451
|
+
}
|
|
449
452
|
}
|
|
450
453
|
.sh-tab-label {
|
|
451
454
|
flex: 1;
|
|
@@ -94,7 +94,7 @@ export default defineComponent({
|
|
|
94
94
|
return ''
|
|
95
95
|
})
|
|
96
96
|
const selectConfigIn = computed(() => {
|
|
97
|
-
return Object.assign({ transfer: true
|
|
97
|
+
return Object.assign({ transfer: true }, props.globalConfig?.selectConfig, checkConfig.value)
|
|
98
98
|
})
|
|
99
99
|
const selectInputConfigIn = computed(() => {
|
|
100
100
|
return Object.assign({ clearable: true, controls: false, transfer: false, suffixIcon: 'vxe-icon-caret-down', placeholder: props.placeholder }, props.globalConfig?.inputConfig)
|
|
@@ -2,7 +2,9 @@ import VXETable from 'vxe-table'
|
|
|
2
2
|
import './css/index.scss'
|
|
3
3
|
import VXETablePluginExportXLSX from './plugins/export'
|
|
4
4
|
import VXETablePluginExportPDF from 'vxe-table-plugin-export-pdf'
|
|
5
|
+
import VXETablePluginMenus from 'vxe-table-plugin-menus'
|
|
5
6
|
import { jsPDF } from 'jspdf'
|
|
7
|
+
import ExcelJS from 'exceljs'
|
|
6
8
|
import XEClipboard from 'xe-clipboard'
|
|
7
9
|
import ShValidators from 'sh-tools/packages/utils/validate'
|
|
8
10
|
import { utils } from 'sh-tools'
|
|
@@ -143,6 +145,11 @@ let vxeOptions = {
|
|
|
143
145
|
autoHidden: false,
|
|
144
146
|
enabled: true
|
|
145
147
|
},
|
|
148
|
+
toolbar: {
|
|
149
|
+
custom: {
|
|
150
|
+
immediate: false
|
|
151
|
+
}
|
|
152
|
+
},
|
|
146
153
|
form: {},
|
|
147
154
|
modal: {
|
|
148
155
|
lockView: true,
|
|
@@ -190,7 +197,10 @@ let vxePdfOptions = {
|
|
|
190
197
|
jsPDF,
|
|
191
198
|
fonts: [{ fontName: 'SourceHanSansNormal' }]
|
|
192
199
|
}
|
|
193
|
-
let vxeXlsxOptions = {
|
|
200
|
+
let vxeXlsxOptions = {
|
|
201
|
+
ExcelJS
|
|
202
|
+
}
|
|
203
|
+
let vxeMenuOptions = {}
|
|
194
204
|
Object.keys(ShValidators).forEach(key => {
|
|
195
205
|
VXETable.validators.add(key, {
|
|
196
206
|
itemValidatorMethod({ itemValue }) {
|
|
@@ -207,13 +217,15 @@ VXETable.renderer.mixin(extraRenders)
|
|
|
207
217
|
VXETable.renderer.mixin(filterRenders)
|
|
208
218
|
|
|
209
219
|
const index = {
|
|
210
|
-
install(Vue, { vxeOption
|
|
220
|
+
install(Vue, { vxeOption, vxePdfOption, vxeXlsxOption, vxeMenuOption }) {
|
|
211
221
|
let setupOption = utils.merge(vxeOptions, vxeOption)
|
|
212
222
|
let xlsxSetUpOption = utils.merge(vxeXlsxOptions, vxeXlsxOption)
|
|
213
223
|
let pdfSetUpOption = utils.merge(vxePdfOptions, vxePdfOption)
|
|
224
|
+
let menuSetUpOption = utils.merge(vxeMenuOptions, vxeMenuOption)
|
|
214
225
|
VXETable.setup(setupOption)
|
|
215
226
|
VXETable.use(VXETablePluginExportXLSX, xlsxSetUpOption)
|
|
216
227
|
VXETable.use(VXETablePluginExportPDF, pdfSetUpOption)
|
|
228
|
+
VXETable.use(VXETablePluginMenus, menuSetUpOption)
|
|
217
229
|
Vue.use(VXETable)
|
|
218
230
|
Vue.config.globalProperties.$vTable = VXETable
|
|
219
231
|
Vue.config.globalProperties.$vTableSetup = setupOption
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import XEUtils from 'xe-utils'
|
|
2
|
-
import ExcelJS from 'exceljs'
|
|
3
2
|
|
|
4
|
-
let
|
|
3
|
+
let globalVxetable = null
|
|
4
|
+
let globalExcelJS = null
|
|
5
5
|
|
|
6
6
|
const defaultHeaderBackgroundColor = '80c1ff'
|
|
7
|
-
const defaultCellFontColor = '
|
|
7
|
+
const defaultCellFontColor = '333333'
|
|
8
8
|
const defaultCellBorderStyle = 'thin'
|
|
9
9
|
const defaultCellBorderColor = 'e7f1ff'
|
|
10
10
|
|
|
@@ -96,12 +96,15 @@ function getDefaultBorderStyle() {
|
|
|
96
96
|
|
|
97
97
|
function exportXLSX(params) {
|
|
98
98
|
const msgKey = 'xlsx'
|
|
99
|
-
const { modal, t } =
|
|
99
|
+
const { modal, t } = globalVxetable
|
|
100
100
|
const { $table, options, columns, colgroups, datas } = params
|
|
101
101
|
const { props, reactData } = $table
|
|
102
|
+
const { computeColumnOpts } = $table.getComputeMaps()
|
|
102
103
|
const { headerAlign: allHeaderAlign, align: allAlign, footerAlign: allFooterAlign } = props
|
|
103
104
|
const { rowHeight } = reactData
|
|
104
105
|
const { message, sheetName, isHeader, isFooter, isMerge, isColgroup, original, useStyle, sheetMethod } = options
|
|
106
|
+
const columnOpts = computeColumnOpts.value
|
|
107
|
+
const _isCustomColumn = options._isCustomColumn
|
|
105
108
|
const showMsg = message !== false
|
|
106
109
|
const mergeCells = $table.getMergeCells()
|
|
107
110
|
const colList = []
|
|
@@ -110,10 +113,8 @@ function exportXLSX(params) {
|
|
|
110
113
|
const sheetMerges = []
|
|
111
114
|
let beforeRowCount = 0
|
|
112
115
|
let headerRowCount = 0
|
|
113
|
-
const colHead = {}
|
|
114
116
|
columns.forEach(column => {
|
|
115
|
-
const { id,
|
|
116
|
-
colHead[id] = headerExportMethod ? headerExportMethod({ column, $table }) : original ? field : column.getTitle()
|
|
117
|
+
const { id, renderWidth } = column
|
|
117
118
|
sheetCols.push({
|
|
118
119
|
key: id,
|
|
119
120
|
width: XEUtils.ceil(renderWidth / 8, 1)
|
|
@@ -129,10 +130,11 @@ function exportXLSX(params) {
|
|
|
129
130
|
groupHead[column.id] = null
|
|
130
131
|
})
|
|
131
132
|
cols.forEach(column => {
|
|
132
|
-
const { _colSpan, _rowSpan
|
|
133
|
+
const { _colSpan, _rowSpan } = column
|
|
133
134
|
const validColumn = getValidColumn(column)
|
|
134
135
|
const columnIndex = columns.indexOf(validColumn)
|
|
135
|
-
|
|
136
|
+
const headExportMethod = column.headerExportMethod || columnOpts.headerExportMethod
|
|
137
|
+
groupHead[validColumn.id] = headExportMethod ? headExportMethod({ column, options, $table }) : original ? validColumn.field : column.getTitle()
|
|
136
138
|
if (_colSpan > 1 || _rowSpan > 1) {
|
|
137
139
|
sheetMerges.push({
|
|
138
140
|
s: { r: rIndex, c: columnIndex },
|
|
@@ -143,13 +145,19 @@ function exportXLSX(params) {
|
|
|
143
145
|
colList.push(groupHead)
|
|
144
146
|
})
|
|
145
147
|
} else {
|
|
148
|
+
const colHead = {}
|
|
149
|
+
columns.forEach(column => {
|
|
150
|
+
const { id, field } = column
|
|
151
|
+
const headExportMethod = column.headerExportMethod || columnOpts.headerExportMethod
|
|
152
|
+
colHead[id] = headExportMethod ? headExportMethod({ column, options, $table }) : original ? field : column.getTitle()
|
|
153
|
+
})
|
|
146
154
|
colList.push(colHead)
|
|
147
155
|
}
|
|
148
156
|
headerRowCount = colList.length
|
|
149
157
|
beforeRowCount += headerRowCount
|
|
150
158
|
}
|
|
151
159
|
// 处理合并
|
|
152
|
-
if (isMerge) {
|
|
160
|
+
if (isMerge && !_isCustomColumn) {
|
|
153
161
|
mergeCells.forEach(mergeItem => {
|
|
154
162
|
const { row: mergeRowIndex, rowspan: mergeRowspan, col: mergeColIndex, colspan: mergeColspan } = mergeItem
|
|
155
163
|
sheetMerges.push({
|
|
@@ -172,7 +180,7 @@ function exportXLSX(params) {
|
|
|
172
180
|
const footers = getFooterData(options, footerData)
|
|
173
181
|
const mergeFooterItems = $table.getMergeFooterItems()
|
|
174
182
|
// 处理合并
|
|
175
|
-
if (isMerge) {
|
|
183
|
+
if (isMerge && !_isCustomColumn) {
|
|
176
184
|
mergeFooterItems.forEach(mergeItem => {
|
|
177
185
|
const { row: mergeRowIndex, rowspan: mergeRowspan, col: mergeColIndex, colspan: mergeColspan } = mergeItem
|
|
178
186
|
sheetMerges.push({
|
|
@@ -190,7 +198,7 @@ function exportXLSX(params) {
|
|
|
190
198
|
})
|
|
191
199
|
}
|
|
192
200
|
const exportMethod = () => {
|
|
193
|
-
const workbook = new ExcelJS.Workbook()
|
|
201
|
+
const workbook = new (globalExcelJS || window.ExcelJS).Workbook()
|
|
194
202
|
const sheet = workbook.addWorksheet(sheetName, { views: [{ state: 'frozen', xSplit: 0, ySplit: headerRowCount }] })
|
|
195
203
|
workbook.creator = 'vxe-table'
|
|
196
204
|
sheet.columns = sheetCols
|
|
@@ -271,7 +279,8 @@ function exportXLSX(params) {
|
|
|
271
279
|
})
|
|
272
280
|
})
|
|
273
281
|
}
|
|
274
|
-
|
|
282
|
+
// 自定义处理
|
|
283
|
+
if (sheetMethod) {
|
|
275
284
|
sheetMethod({ options: options, workbook, worksheet: sheet, columns, colgroups, datas, $table })
|
|
276
285
|
}
|
|
277
286
|
sheetMerges.forEach(({ s, e }) => {
|
|
@@ -296,7 +305,7 @@ function exportXLSX(params) {
|
|
|
296
305
|
}
|
|
297
306
|
|
|
298
307
|
function downloadFile(params, blob, options) {
|
|
299
|
-
const { modal, t } =
|
|
308
|
+
const { modal, t } = globalVxetable
|
|
300
309
|
const { message, filename, type } = options
|
|
301
310
|
const showMsg = message !== false
|
|
302
311
|
if (window.Blob) {
|
|
@@ -323,7 +332,7 @@ function checkImportData(tableFields, fields) {
|
|
|
323
332
|
}
|
|
324
333
|
|
|
325
334
|
function importError(params) {
|
|
326
|
-
const { modal, t } =
|
|
335
|
+
const { modal, t } = globalVxetable
|
|
327
336
|
const { $table, options } = params
|
|
328
337
|
const { internalData } = $table
|
|
329
338
|
const { _importReject } = internalData
|
|
@@ -337,7 +346,7 @@ function importError(params) {
|
|
|
337
346
|
}
|
|
338
347
|
|
|
339
348
|
function importXLSX(params) {
|
|
340
|
-
const { modal, t } =
|
|
349
|
+
const { modal, t } = globalVxetable
|
|
341
350
|
const { $table, columns, options, file } = params
|
|
342
351
|
const { internalData } = $table
|
|
343
352
|
const { _importResolve } = internalData
|
|
@@ -354,7 +363,7 @@ function importXLSX(params) {
|
|
|
354
363
|
tableFields.push(field)
|
|
355
364
|
}
|
|
356
365
|
})
|
|
357
|
-
const workbook = new ExcelJS.Workbook()
|
|
366
|
+
const workbook = new (globalExcelJS || window.ExcelJS).Workbook()
|
|
358
367
|
const readerTarget = evnt.target
|
|
359
368
|
if (readerTarget) {
|
|
360
369
|
workbook.xlsx.load(readerTarget.result).then(wb => {
|
|
@@ -421,22 +430,26 @@ function handleExportEvent(params) {
|
|
|
421
430
|
}
|
|
422
431
|
|
|
423
432
|
/**
|
|
424
|
-
* 基于 vxe-table
|
|
433
|
+
* 基于 vxe-table 表格的扩展插件,支持导出 xlsx 格式
|
|
425
434
|
*/
|
|
426
435
|
export const VXETablePluginExportXLSX = {
|
|
427
|
-
install(
|
|
428
|
-
|
|
436
|
+
install(vxetable, options) {
|
|
437
|
+
// 检查版本
|
|
438
|
+
if (!/^(4)\./.test(vxetable.version)) {
|
|
439
|
+
console.error('[vxe-table-plugin-export-pdf] Version vxe-table 4.x is required')
|
|
440
|
+
}
|
|
429
441
|
|
|
430
|
-
|
|
442
|
+
globalVxetable = vxetable
|
|
443
|
+
globalExcelJS = options ? options.ExcelJS : null
|
|
431
444
|
|
|
432
|
-
|
|
445
|
+
vxetable.config({
|
|
433
446
|
export: {
|
|
434
447
|
types: {
|
|
435
448
|
xlsx: 0
|
|
436
449
|
}
|
|
437
450
|
}
|
|
438
451
|
})
|
|
439
|
-
interceptor.mixin({
|
|
452
|
+
vxetable.interceptor.mixin({
|
|
440
453
|
'event.import': handleImportEvent,
|
|
441
454
|
'event.export': handleExportEvent
|
|
442
455
|
})
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<span v-else @click="vxeInputPrefixClick">{{ rprops.prefixText }}</span>
|
|
7
7
|
</span>
|
|
8
8
|
<span v-else-if="controlButton" class="control-btn before" @click="vxeControlClick(false)">-</span>
|
|
9
|
-
<vxe-input v-model="renderValue" v-bind="rprops" :size="rsize" @change="vxeInputChange" @blur="vxeBlurCallback" @clear="vxeBlurCallback"></vxe-input>
|
|
9
|
+
<vxe-input v-model="renderValue" v-bind="rprops" :size="rsize" :immediate="false" @change="vxeInputChange" @blur="vxeBlurCallback" @clear="vxeBlurCallback"></vxe-input>
|
|
10
10
|
<span v-if="rprops.suffixText && rform" class="suffix">
|
|
11
11
|
<vxe-button v-if="suffixButton" v-bind="psButtonConfig" @click="vxeInputSuffixClick">{{ rprops.suffixText }}</vxe-button>
|
|
12
12
|
<span v-else @click="vxeInputSuffixClick">{{ rprops.suffixText }}</span>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<span class="vxe-render--inner" :class="{ 'form-render': rform, 'td-render': !rform, 'td-all': rprops.bill }">
|
|
3
3
|
<template v-if="redit || isEditAll">
|
|
4
|
-
<vxe-input v-model="renderValue" type="number" v-bind="rprops" :size="rsize" @change="vxeInputChange" @blur="vxeBlurCallback" @clear="vxeBlurCallback" />
|
|
4
|
+
<vxe-input v-model="renderValue" type="number" v-bind="rprops" :size="rsize" :immediate="false" @change="vxeInputChange" @blur="vxeBlurCallback" @clear="vxeBlurCallback" />
|
|
5
5
|
</template>
|
|
6
6
|
<template v-else-if="rprops.bill">
|
|
7
7
|
<template v-for="(bil, bilindex) in billGroups" :key="bilindex">
|
|
@@ -39,7 +39,7 @@ let defaultPublicProps = {
|
|
|
39
39
|
|
|
40
40
|
// 渲染器个性化默认配置
|
|
41
41
|
let defaultProps = {
|
|
42
|
-
$vInput: { type: 'text', placeholder: '请输入', transfer: true, digits: 2, prefixType: 'text', suffixType: 'text'
|
|
42
|
+
$vInput: { type: 'text', placeholder: '请输入', transfer: true, digits: 2, prefixType: 'text', suffixType: 'text' },
|
|
43
43
|
$vTextArea: { placeholder: '请输入', rows: 2, transfer: true, resize: 'none', showWordCount: true },
|
|
44
44
|
$vSelect: { placeholder: '请选择', filterable: true, transfer: true, showType: 'text', tagColor: 'default', split: ',', options: [] },
|
|
45
45
|
$vTree: { placeholder: '请选择', transfer: true, split: ',', nodeKey: 'id', labelField: 'label' },
|
|
@@ -73,12 +73,13 @@ const getFixedProp = (renderOpts, params) => {
|
|
|
73
73
|
}
|
|
74
74
|
})
|
|
75
75
|
const contextAttrs = $grid?.context?.attrs || $table?.context?.attrs || {}
|
|
76
|
-
if (['$vMoney'].includes(name) && contextAttrs.moneyConfig?.enabled && contextAttrs.moneyUnit
|
|
76
|
+
if (['$vMoney'].includes(name) && contextAttrs.moneyConfig?.enabled && contextAttrs.moneyUnit) {
|
|
77
77
|
let moneyOption = contextAttrs.moneyConfig?.options.find(item => item.value === contextAttrs.moneyUnit)
|
|
78
78
|
rturnProps.moneyUnit = contextAttrs.moneyUnit
|
|
79
79
|
rturnProps.moneyOption = moneyOption
|
|
80
80
|
if (moneyOption && moneyOption.digits) rturnProps.digits = moneyOption.digits
|
|
81
81
|
}
|
|
82
|
+
rturnProps.formulaMap = contextAttrs.formulaMap
|
|
82
83
|
if (column) {
|
|
83
84
|
column.rname = name
|
|
84
85
|
column.rprops = rturnProps
|
|
@@ -27,7 +27,6 @@ export default function (props, context, proxy) {
|
|
|
27
27
|
// 初始化数据
|
|
28
28
|
const initData = () => {
|
|
29
29
|
let keyValue = $vUtils.get(props.rdata, props.rkey)
|
|
30
|
-
if (keyValue && $vUtils.isEqual(keyValue, renderValue.value)) return
|
|
31
30
|
formatValueFun(keyValue)
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -48,12 +47,7 @@ export default function (props, context, proxy) {
|
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
// 输入框变化
|
|
51
|
-
const vxeInputChange = async ({ value, $event }) => {
|
|
52
|
-
// 此操作火狐浏览器(bug(vxe):编辑后不触发渲染器的blur问题)触发edit-closed,故在edit-close重新赋值
|
|
53
|
-
if (!rform.value) {
|
|
54
|
-
$vUtils.set(props.rparams.column, 'params.__RowEditModelValue', value)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
50
|
+
const vxeInputChange = async ({ value, $event }) => {}
|
|
57
51
|
// 输入框变化回调
|
|
58
52
|
const vxeChangeCallBack = async ({ value, $event }) => {
|
|
59
53
|
setRenderValue(value)
|
|
@@ -81,26 +75,28 @@ export default function (props, context, proxy) {
|
|
|
81
75
|
let { rvalue, rtext } = formatValueFun(cellValue, true)
|
|
82
76
|
cellValue = rvalue
|
|
83
77
|
}
|
|
78
|
+
$vUtils.set(props.rdata, props.rkey, cellValue)
|
|
84
79
|
if (rform.value) {
|
|
85
80
|
let { $form } = props.rparams
|
|
86
81
|
$form.context.emit('edit-closed', props.rparams)
|
|
87
82
|
}
|
|
88
|
-
$vUtils.set(props.rdata, props.rkey, cellValue)
|
|
89
83
|
}
|
|
90
84
|
// 格式化值formatValue
|
|
91
85
|
const formatValueFun = (value, editable) => {
|
|
92
86
|
if (props.rparams.type === 'footer') {
|
|
93
87
|
const { items, itemIndex } = props.rparams
|
|
94
|
-
const { moneyUnit, digits } = props.rprops
|
|
88
|
+
const { moneyUnit, digits, type, commafy } = props.rprops
|
|
95
89
|
let footerText = items[itemIndex]
|
|
96
90
|
if (props.rname === '$vMoney') footerText = $vUtils.truncate($vUtils.divide(items[itemIndex], moneyUnit), digits)
|
|
97
|
-
if (
|
|
91
|
+
else if (['integer'].includes(type)) footerText = $vUtils.toInteger(footerText)
|
|
92
|
+
else if (['number', 'float'].includes(type)) footerText = $vUtils.truncate(footerText, digits)
|
|
93
|
+
if (commafy) footerText = $vUtils.commafy(footerText, { digits })
|
|
98
94
|
renderValue.value = items[itemIndex]
|
|
99
95
|
renderText.value = footerText
|
|
100
96
|
return
|
|
101
97
|
}
|
|
102
98
|
const { rvalue, rtext } = $vUtils.formatRender(value, props.rkey, props.rdata, props.rname, props.rprops, proxy, editable)
|
|
103
|
-
if (
|
|
99
|
+
if (props.rname === '$vMoney' && !props.rprops.bill && props.rprops.bill !== '0') {
|
|
104
100
|
renderValue.value = rtext
|
|
105
101
|
} else {
|
|
106
102
|
renderValue.value = rvalue
|
|
@@ -125,12 +121,19 @@ export default function (props, context, proxy) {
|
|
|
125
121
|
|
|
126
122
|
watch(
|
|
127
123
|
() => props.rdata,
|
|
128
|
-
() =>
|
|
124
|
+
() => {
|
|
125
|
+
let keyValue = $vUtils.get(props.rdata, props.rkey)
|
|
126
|
+
let keyFormula = $vUtils.get(props.rprops, 'formula')
|
|
127
|
+
if (keyValue && $vUtils.isEqual(keyValue, renderValue.value) && !keyFormula) return
|
|
128
|
+
initData()
|
|
129
|
+
},
|
|
129
130
|
{ deep: true, immediate: true }
|
|
130
131
|
)
|
|
131
132
|
watch(
|
|
132
133
|
() => props.rprops,
|
|
133
|
-
() =>
|
|
134
|
+
() => {
|
|
135
|
+
initData()
|
|
136
|
+
}
|
|
134
137
|
)
|
|
135
138
|
|
|
136
139
|
return {
|