t20-common-lib 0.15.38 → 0.15.40

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "t20-common-lib",
3
- "version": "0.15.38",
3
+ "version": "0.15.40",
4
4
  "description": "T20",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
@@ -492,6 +492,25 @@ export default {
492
492
  this.isApplyingLinkage = false;
493
493
  });
494
494
  },
495
+ /**
496
+ * 校验失败后滚动到第一个展示校验错误的表单项(ElementUI 会为失败项加上 .is-error)。
497
+ */
498
+ scrollToFirstFormError() {
499
+ const formRef = this.$refs.dynamicForms;
500
+ const root = formRef && formRef.$el;
501
+ if (!root || typeof root.querySelectorAll !== "function") return;
502
+ const nodes = root.querySelectorAll(".el-form-item.is-error");
503
+ for (let i = 0; i < nodes.length; i++) {
504
+ const el = nodes[i];
505
+ const cs = window.getComputedStyle(el);
506
+ if (cs.display === "none" || cs.visibility === "hidden") continue;
507
+ if (el.offsetParent === null && cs.position !== "fixed") continue;
508
+ if (typeof el.scrollIntoView === "function") {
509
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
510
+ }
511
+ break;
512
+ }
513
+ },
495
514
  /**
496
515
  * 触发表单校验;通过后由返回值携带表单数据与附件(与 needFile 一致时含已落档行)。
497
516
  * @returns {Promise<{ valid: false, formData: object } | { valid: true, formData: object, operate?: string, fileUploadTableData?: array }>}
@@ -46,33 +46,65 @@
46
46
  {{ $lc('条。') }}
47
47
  </span>
48
48
  </div>
49
- <template v-if="validateResult.errorList?.length">
50
- <div class="bd-a">
51
- <div class="flex-box flex-lr flex-v m-t m-b p-l p-r">
52
- <span>{{ $lc('无效数据详情') }}</span>
53
- <el-button v-if="showErrorExport" type="text" @click="importError">{{ $lc('导出错误数据') }}</el-button>
54
- </div>
55
- <N20-table-pro
56
- ref="vTable"
57
- :data="errorListC"
58
- :columns="columnsList"
59
- auto-resize
60
- :height="'300px'"
61
- :clearSelect="false"
62
- :rowConfig="{ keyField: 'id' }"
63
- :checkboxConfig="{ reserve: true }"
64
- :resizable-config="{ isAllColumnDrag: true, minWidth: '0px' }"
65
- headerAlign="center"
66
- />
67
- <div v-if="pagination" class="flex-box flex-r m-t-ss m-b-ss">
68
- <Pagination
69
- :page-obj="page"
70
- :page-key="{ no: 'current', size: 'pageSize', total: 'total' }"
71
- @change="getList"
49
+
50
+ <!-- 未传 tabList:保持原有错误数据展示 -->
51
+ <template v-if="!useTabMode">
52
+ <template v-if="validateResult.errorList?.length">
53
+ <div class="bd-a">
54
+ <div class="flex-box flex-lr flex-v m-t m-b p-l p-r">
55
+ <span>{{ $lc('无效数据详情') }}</span>
56
+ <el-button v-if="showErrorExport" type="text" @click="importError">{{ $lc('导出错误数据') }}</el-button>
57
+ </div>
58
+ <component
59
+ :is="tableProTag"
60
+ ref="vTable"
61
+ :data="errorListC"
62
+ :columns="columnsList"
63
+ auto-resize
64
+ :height="'300px'"
65
+ :clearSelect="false"
66
+ :rowConfig="{ keyField: 'id' }"
67
+ :checkboxConfig="{ reserve: true }"
68
+ :resizable-config="{ isAllColumnDrag: true, minWidth: '0px' }"
69
+ headerAlign="center"
72
70
  />
71
+ <div v-if="pagination" class="flex-box flex-r m-t-ss m-b-ss">
72
+ <Pagination
73
+ :page-obj="page"
74
+ :page-key="{ no: 'current', size: 'pageSize', total: 'total' }"
75
+ @change="getList"
76
+ />
77
+ </div>
78
+ </div>
79
+ <div class="color-warning m-t-s">{{ '上述数据输入有误,请修改导入文件中相关信息!' | $lc }}</div>
80
+ </template>
81
+ </template>
82
+
83
+ <!-- 传入 tabList:分页签展示 -->
84
+ <template v-else>
85
+ <TabPane :data="tabList" :init.sync="activeTabName" @click="handleTabClick" />
86
+ <!-- 页签内容:错误页签仍走 errorListC / columnsList,其他页签 table 参数由 tabList 传入 -->
87
+ <template v-if="activeTab">
88
+ <div class="bd-a">
89
+ <div class="flex-box flex-lr flex-v m-t m-b p-l p-r">
90
+ <span>{{ tabPanelTitle }}</span>
91
+ <el-button v-if="showErrorExport && showErrorPanel" type="text" @click="importError">
92
+ {{ $lc('导出错误数据') }}
93
+ </el-button>
94
+ </div>
95
+ <component :is="tableProTag" ref="vTable" v-bind="tabModeTableBind" />
96
+ <div v-if="tabModePagination" class="flex-box flex-r m-t-ss m-b-ss">
97
+ <Pagination
98
+ :page-obj="page"
99
+ :page-key="{ no: 'current', size: 'pageSize', total: 'total' }"
100
+ @change="getList"
101
+ />
102
+ </div>
73
103
  </div>
104
+ </template>
105
+ <div v-if="showImportTip" class="color-warning m-t-s">
106
+ {{ importTipText | $lc }}
74
107
  </div>
75
- <div class="color-warning m-t-s">{{ '上述数据输入有误,请修改导入文件中相关信息!' | $lc }}</div>
76
108
  </template>
77
109
  </template>
78
110
  </div>
@@ -109,9 +141,24 @@
109
141
 
110
142
  <script>
111
143
  import cloneDeep from 'lodash-es/cloneDeep'
144
+ import TabPane from '../../../tab-pane/src/main.vue'
145
+ import { resolveTableProComponent } from '../../../../src/utils/resolvePrefixedComponent'
146
+
147
+ /** 内置错误数据页签标识 */
148
+ const ERROR_TAB_TYPE = 'error'
149
+
150
+ const DEFAULT_TABLE_BIND = {
151
+ autoResize: true,
152
+ clearSelect: false,
153
+ rowConfig: { keyField: 'id' },
154
+ checkboxConfig: { reserve: true },
155
+ resizableConfig: { isAllColumnDrag: true, minWidth: '0px' },
156
+ headerAlign: 'center'
157
+ }
112
158
 
113
159
  export default {
114
160
  name: 'UploadMsg',
161
+ components: { TabPane },
115
162
  props: {
116
163
  title: { type: String, default: '提示' },
117
164
  footerBtn: { type: Object, default: () => ({}) },
@@ -124,10 +171,17 @@ export default {
124
171
  validateResult: { type: Object, default: undefined },
125
172
  validateConfirm: { type: Function, default: undefined },
126
173
  hidePercent: { type: Boolean, default: false },
127
- showErrorExport: { type: Boolean, default: false }
174
+ showErrorExport: { type: Boolean, default: false },
175
+ /**
176
+ * 校验弹窗页签配置。未传或空数组时保持仅展示错误数据(原逻辑)。
177
+ * 项示例:{ name: '无效数据', type: 'error' } | { name: '校验信息', key: 'info', data, columns, tableProps }
178
+ * 非 error 页签:data/columns(或 tableData/columnsList)及 tableProps 等同 N20-table-pro 配置
179
+ */
180
+ tabList: { type: Array, default: undefined }
128
181
  },
129
182
  data() {
130
183
  return {
184
+ activeTabName: '',
131
185
  tableData: [],
132
186
  errorListC: [],
133
187
  page: {
@@ -138,6 +192,12 @@ export default {
138
192
  }
139
193
  },
140
194
  watch: {
195
+ tabList: {
196
+ handler() {
197
+ this.initActiveTab()
198
+ },
199
+ immediate: true
200
+ },
141
201
  errorList: {
142
202
  handler() {
143
203
  this.errorListC = this.errorList
@@ -149,9 +209,23 @@ export default {
149
209
  this.pageSize = v
150
210
  },
151
211
  deep: true
212
+ },
213
+ activeTabName() {
214
+ this.$nextTick(() => {
215
+ if (this.$refs.vTable && this.$refs.vTable.doLayout) {
216
+ this.$refs.vTable.doLayout()
217
+ }
218
+ })
152
219
  }
153
220
  },
154
221
  computed: {
222
+ useTabMode() {
223
+ return Array.isArray(this.tabList) && this.tabList.length > 0
224
+ },
225
+ tableProTag() {
226
+ const Vue = this.$root.$options._base
227
+ return resolveTableProComponent(Vue)
228
+ },
155
229
  dialogTitle() {
156
230
  return this.title || this.$lc('提示')
157
231
  },
@@ -181,9 +255,74 @@ export default {
181
255
  set(value) {
182
256
  return value
183
257
  }
258
+ },
259
+ activeTab() {
260
+ if (!this.useTabMode) return null
261
+ return this.tabList.find(item => item.name === this.activeTabName) || this.tabList[0]
262
+ },
263
+ showErrorPanel() {
264
+ return this.useTabMode && this.activeTab && this.isErrorTab(this.activeTab)
265
+ },
266
+ tabPanelTitle() {
267
+ if (!this.activeTab) return ''
268
+ if (this.showErrorPanel) {
269
+ return this.activeTab.panelTitle || this.activeTab.name || this.$lc('无效数据详情')
270
+ }
271
+ return this.activeTab.panelTitle || this.activeTab.name || ''
272
+ },
273
+ tabModePagination() {
274
+ if (this.showErrorPanel) return this.pagination
275
+ return !!(this.activeTab && this.activeTab.pagination)
276
+ },
277
+ tabModeTableBind() {
278
+ if (this.showErrorPanel) {
279
+ const height =
280
+ (this.activeTab && (this.activeTab.tableHeight || this.activeTab.height)) || '300px'
281
+ return {
282
+ ...DEFAULT_TABLE_BIND,
283
+ data: this.errorListC,
284
+ columns: this.columnsList,
285
+ height
286
+ }
287
+ }
288
+ const tab = this.activeTab || {}
289
+ const tableProps = tab.tableProps || tab.tableOpt || {}
290
+ return {
291
+ ...DEFAULT_TABLE_BIND,
292
+ height: tab.height || tab.tableHeight || '300px',
293
+ data: tab.data || tab.tableData || [],
294
+ columns: tab.columns || tab.columnsList || [],
295
+ ...tableProps
296
+ }
297
+ },
298
+ showImportTip() {
299
+ if (!this.useTabMode || !this.validateResult) return false
300
+ if (this.validateResult.importTip === false) return false
301
+ return !!(this.validateResult.errorList && this.validateResult.errorList.length)
302
+ },
303
+ importTipText() {
304
+ return this.validateResult?.importTip || '上述数据输入有误,请修改导入文件中相关信息!'
184
305
  }
185
306
  },
186
307
  methods: {
308
+ isErrorTab(tab) {
309
+ return tab && (tab.type === ERROR_TAB_TYPE || tab.key === ERROR_TAB_TYPE)
310
+ },
311
+ initActiveTab() {
312
+ if (!this.useTabMode) {
313
+ this.activeTabName = ''
314
+ return
315
+ }
316
+ const exists = this.tabList.some(item => item.name === this.activeTabName)
317
+ if (!exists) {
318
+ const defaultTab = this.tabList.find(item => item.default) || this.tabList[0]
319
+ this.activeTabName = defaultTab.name
320
+ }
321
+ },
322
+ handleTabClick(item) {
323
+ this.activeTabName = item.name
324
+ this.$emit('tab-click', item)
325
+ },
187
326
  importError() {
188
327
  this.$emit('importError')
189
328
  },
@@ -15,12 +15,14 @@
15
15
  :width="width"
16
16
  :show-error-export="showErrorExport"
17
17
  :validate-result="validateResult"
18
+ :tab-list="tabList"
18
19
  :hide-percent="hidePercent"
19
20
  :percentType="percentType"
20
21
  :percent="percent"
21
22
  :footer-btn="footer"
22
23
  :validate-confirm="validateConfirm"
23
24
  @importError="importError"
25
+ @tab-click="$emit('tab-click', $event)"
24
26
  >
25
27
  <template #top>
26
28
  <slot name="top"></slot>
@@ -61,7 +63,9 @@ export default {
61
63
  percentType: { type: String, default: 'loading' },
62
64
  percent: { type: Number, default: 0 },
63
65
  errorExportMode: { type: String, default: 'frontend' },
64
- errorExportOptions: { type: Object, default: () => ({}) }
66
+ errorExportOptions: { type: Object, default: () => ({}) },
67
+ /** 校验弹窗页签,见 UploadMsg.tabList */
68
+ tabList: { type: Array, default: undefined }
65
69
  },
66
70
  data() {
67
71
  return {
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 解析 n20-common-lib 全局注册的前缀组件名。
3
+ * install 时 prefix 可能是 N20 / Cl / cl 等,需按实际注册名回退。
4
+ * @param {typeof import('vue').default} Vue
5
+ * @param {string[]} candidates 候选名(kebab / Pascal 均可)
6
+ * @param {string} [fallback]
7
+ */
8
+ export function resolvePrefixedComponent(Vue, candidates, fallback) {
9
+ if (!Vue || !candidates?.length) {
10
+ return fallback || candidates?.[0]
11
+ }
12
+ for (const name of candidates) {
13
+ if (Vue.component(name)) {
14
+ return name
15
+ }
16
+ }
17
+ return fallback || candidates[0]
18
+ }
19
+
20
+ const TABLE_PRO_CANDIDATES = [
21
+ 'N20-table-pro',
22
+ 'N20TablePro',
23
+ 'Cl-table-pro',
24
+ 'ClTablePro',
25
+ 'cl-table-pro',
26
+ 'clTablePro'
27
+ ]
28
+
29
+ export function resolveTableProComponent(Vue) {
30
+ return resolvePrefixedComponent(Vue, TABLE_PRO_CANDIDATES, 'N20-table-pro')
31
+ }