@ebiz/designer-components 0.0.18-kzy.5 → 0.0.18-kzy.7

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": "@ebiz/designer-components",
3
- "version": "0.0.18-kzy.5",
3
+ "version": "0.0.18-kzy.7",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,7 +22,7 @@
22
22
  "@opentiny/vue-icon": "~3.20.0",
23
23
  "axios": "^1.8.1",
24
24
  "echarts": "^5.6.0",
25
- "tdesign-vue-next": "^1.11.5",
25
+ "tdesign-vue-next": "^1.12.0",
26
26
  "unplugin-auto-import": "^19.0.0",
27
27
  "unplugin-vue-components": "^28.0.0",
28
28
  "vue": "^3.5.13",
@@ -8,9 +8,9 @@ import axios from 'axios'
8
8
  import { TinyNotify } from '@opentiny/vue'
9
9
 
10
10
  // 本地开发使用
11
- const API_BASE_URL = "http://localhost:8090/api";
11
+ // const API_BASE_URL = "http://localhost:8090/api";
12
12
  // 组件发布使用
13
- // const API_BASE_URL = 'http://' + window.location.host + '/api'
13
+ const API_BASE_URL = 'http://' + window.location.host + '/api'
14
14
 
15
15
  /**
16
16
  * 创建axios实例
@@ -66,6 +66,10 @@ const props = defineProps({
66
66
  onFinish: {//调用接口后置事件
67
67
  type: Function,
68
68
  default: () => { }
69
+ },
70
+ onError: {
71
+ type: Function,
72
+ default: () => { }
69
73
  }
70
74
  })
71
75
 
@@ -82,20 +86,43 @@ const apiMap = [
82
86
  'MULTIPLE_DATA_LINK_SEARCH'
83
87
  ];
84
88
 
85
- const emit = defineEmits(['click'])
89
+ const emit = defineEmits(['click', 'success', 'error', 'prepare'])
86
90
 
87
- const { text, isNormal, disabled, type, size, apiConfig, data, isUseMethod } = toRefs(props)
91
+ const { text, isNormal, disabled, type, size, variant, apiConfig, data } = toRefs(props)
88
92
 
89
93
  const tooltipText = ref('请先设置接口配置')
90
94
 
91
- const click = () => {
95
+ // 检查API配置是否有效
96
+ const isValidApiConfig = () => {
97
+ return apiConfig.value && apiConfig.value.apiId &&
98
+ typeof apiConfig.value.apiType === 'number' &&
99
+ apiConfig.value.apiType >= 0 &&
100
+ apiConfig.value.apiType < apiMap.length;
101
+ }
102
+
103
+ const click = async () => {
92
104
  props.onClick()
93
- if (!isNormal.value && apiConfig.value.apiId) {
94
- props.onPrepare()
105
+ // 如果是普通按钮或API配置无效,不调用接口
106
+ if (isNormal.value || !isValidApiConfig()) {
107
+ return
108
+ }
109
+
110
+ try {
111
+ // 调用前置处理并获取返回值
112
+ const shouldContinue = await Promise.resolve(props.onPrepare())
113
+ emit('prepare', shouldContinue)
114
+
115
+ // 只有当返回值明确为false时才中断,undefined或其他值都继续执行
116
+ if (shouldContinue === false) {
117
+ return null
118
+ }
119
+
95
120
  dataService.fetch(data.value, { key: apiConfig.value.key, apiId: apiConfig.value.apiId, apiType: apiMap[apiConfig.value.apiType] }).then((res) => {
96
121
  emit('click', res)
97
122
  props.onFinish()
98
123
  })
124
+ } catch (error) {
125
+ props.onError(error)
99
126
  }
100
127
  }
101
128
 
@@ -0,0 +1,341 @@
1
+ <template>
2
+ <div class="ebiz-descriptions">
3
+ <div v-if="title || $slots.title" class="ebiz-descriptions__header">
4
+ <slot name="title">{{ title }}</slot>
5
+ </div>
6
+ <table
7
+ class="ebiz-descriptions__body"
8
+ :class="[
9
+ `ebiz-descriptions--${size}`,
10
+ { 'ebiz-descriptions__body--fixed': tableLayout === 'fixed' },
11
+ { 'ebiz-descriptions__body--border': border }
12
+ ]"
13
+ :style="border && tableBorderColor ? {'--td-descriptions-border-color': tableBorderColor} : {}"
14
+ >
15
+ <tbody>
16
+ <template v-if="rows.length > 0">
17
+ <template v-if="layout === 'horizontal'">
18
+ <tr v-for="(row, rowIndex) in rows" :key="`row-${rowIndex}`">
19
+ <template v-for="(item, colIndex) in row" :key="`${rowIndex}-${colIndex}`">
20
+ <template v-if="itemLayout === 'horizontal'">
21
+ <th
22
+ class="ebiz-descriptions__label"
23
+ :class="[item.labelClassName]"
24
+ >
25
+ <slot :name="`${item.slotName}-label`">
26
+ {{ item.label }}
27
+ </slot>
28
+ <span v-if="colon" class="ebiz-descriptions__colon">:</span>
29
+ </th>
30
+ <td
31
+ :colspan="item.span * 2 - 1"
32
+ class="ebiz-descriptions__content"
33
+ :class="[item.contentClassName]"
34
+ >
35
+ <slot :name="item.slotName">
36
+ {{ item.content }}
37
+ </slot>
38
+ </td>
39
+ </template>
40
+ </template>
41
+ </tr>
42
+ </template>
43
+ <template v-else>
44
+ <template v-for="(row, rowIndex) in rows" :key="`${rowIndex}`">
45
+ <template v-if="itemLayout === 'horizontal'">
46
+ <tr>
47
+ <template v-for="(item, colIndex) in row" :key="`label-${rowIndex}-${colIndex}`">
48
+ <th
49
+ class="ebiz-descriptions__label"
50
+ :class="[item.labelClassName]"
51
+ >
52
+ <slot :name="`${item.slotName}-label`">
53
+ {{ item.label }}
54
+ </slot>
55
+ <span v-if="colon" class="ebiz-descriptions__colon">:</span>
56
+ </th>
57
+ <td
58
+ :colspan="item.span"
59
+ class="ebiz-descriptions__content"
60
+ :class="[item.contentClassName]"
61
+ >
62
+ <slot :name="item.slotName">
63
+ {{ item.content }}
64
+ </slot>
65
+ </td>
66
+ </template>
67
+ </tr>
68
+ </template>
69
+ <template v-else>
70
+ <tr>
71
+ <template v-for="(item, colIndex) in row" :key="`label-${rowIndex}-${colIndex}`">
72
+ <th
73
+ :colspan="item.span"
74
+ class="ebiz-descriptions__label"
75
+ :class="[item.labelClassName]"
76
+ >
77
+ <slot :name="`${item.slotName}-label`">
78
+ {{ item.label }}
79
+ </slot>
80
+ <span v-if="colon" class="ebiz-descriptions__colon">:</span>
81
+ </th>
82
+ </template>
83
+ </tr>
84
+ <tr>
85
+ <template v-for="(item, colIndex) in row" :key="`content-${rowIndex}-${colIndex}`">
86
+ <td
87
+ :colspan="item.span"
88
+ class="ebiz-descriptions__content"
89
+ :class="[item.contentClassName]"
90
+ >
91
+ <slot :name="item.slotName">
92
+ {{ item.content }}
93
+ </slot>
94
+ </td>
95
+ </template>
96
+ </tr>
97
+ </template>
98
+ </template>
99
+ </template>
100
+ </template>
101
+ <template v-else>
102
+ <slot></slot>
103
+ </template>
104
+ </tbody>
105
+ </table>
106
+ </div>
107
+ </template>
108
+
109
+ <script setup>
110
+ import { defineProps, defineEmits, computed, useSlots, ref } from 'vue';
111
+ import { isArray, isNil } from 'lodash-es';
112
+
113
+ const props = defineProps({
114
+ border: {
115
+ type: Boolean,
116
+ default: false
117
+ },
118
+ colon: {
119
+ type: Boolean,
120
+ default: false
121
+ },
122
+ column: {
123
+ type: Number,
124
+ default: 2
125
+ },
126
+ layout: {
127
+ type: String,
128
+ default: 'horizontal',
129
+ validator(val) {
130
+ return ['horizontal', 'vertical'].includes(val);
131
+ }
132
+ },
133
+ itemLayout: {
134
+ type: String,
135
+ default: 'horizontal',
136
+ validator(val) {
137
+ return ['horizontal', 'vertical'].includes(val);
138
+ }
139
+ },
140
+ size: {
141
+ type: String,
142
+ default: 'medium',
143
+ validator(val) {
144
+ return ['small', 'medium', 'large'].includes(val);
145
+ }
146
+ },
147
+ title: {
148
+ type: String,
149
+ default: ''
150
+ },
151
+ tableLayout: {
152
+ type: String,
153
+ default: 'fixed',
154
+ validator(val) {
155
+ return ['auto', 'fixed'].includes(val);
156
+ }
157
+ },
158
+ tableBorderColor: {
159
+ type: String,
160
+ default: ''
161
+ },
162
+ items: {
163
+ type: Array,
164
+ default: () => []
165
+ }
166
+ });
167
+
168
+ defineEmits([]);
169
+
170
+ const slots = useSlots();
171
+ const itemsType = ref('slots');
172
+
173
+ // 计算行数据
174
+ const rows = computed(() => {
175
+ // 1. 获取要渲染的 items
176
+ let items = [];
177
+
178
+ if (isArray(props.items) && props.items.length > 0) {
179
+ // 从 props.items 获取数据
180
+ items = props.items.map(item => ({
181
+ label: item.label,
182
+ content: item.content,
183
+ span: item.span || 1,
184
+ labelClassName: item.labelClassName || '',
185
+ contentClassName: item.contentClassName || '',
186
+ slotName: item.name || `item-${items.length}`
187
+ }));
188
+ itemsType.value = 'props';
189
+ } else {
190
+ // 从 slots 获取数据
191
+ const defaultSlot = slots.default?.();
192
+ if (defaultSlot) {
193
+ items = defaultSlot
194
+ .filter(vnode => vnode.type &&
195
+ (vnode.type.name === 'EbizDescriptionsItem' ||
196
+ vnode.type.__name === 'EbizDescriptionsItem'))
197
+ .map((vnode, index) => {
198
+ const props = vnode.props || {};
199
+ const span = isNil(props.span) ? 1 : Number(props.span);
200
+ return {
201
+ label: props.label || '',
202
+ content: '',
203
+ span: span > props.column ? props.column : span,
204
+ labelClassName: props.labelClassName || '',
205
+ contentClassName: props.contentClassName || '',
206
+ slotName: props.name || `item-${index}`,
207
+ children: vnode.children
208
+ };
209
+ });
210
+ itemsType.value = 'slots';
211
+ }
212
+ }
213
+
214
+ // 2. 判断布局,如果是垂直布局则直接返回
215
+ if (props.layout === 'vertical') {
216
+ return [items];
217
+ }
218
+
219
+ // 3. 水平布局时,计算每一行的item
220
+ const rows = [];
221
+ let currentRow = [];
222
+ let currentRowSpan = 0;
223
+ const { column } = props;
224
+
225
+ items.forEach((item, index) => {
226
+ const span = item.span || 1;
227
+
228
+ // 如果当前行放不下了,创建新行
229
+ if (currentRowSpan + span > column) {
230
+ rows.push(currentRow);
231
+ currentRow = [];
232
+ currentRowSpan = 0;
233
+ }
234
+
235
+ // 添加到当前行
236
+ currentRow.push({
237
+ ...item,
238
+ span: span
239
+ });
240
+
241
+ currentRowSpan += span;
242
+
243
+ // 处理最后一个元素
244
+ if (index === items.length - 1) {
245
+ // 最后一个元素填充剩余空间
246
+ if (currentRowSpan < column) {
247
+ currentRow[currentRow.length - 1].span += (column - currentRowSpan);
248
+ }
249
+ rows.push(currentRow);
250
+ }
251
+ });
252
+
253
+ return rows;
254
+ });
255
+ </script>
256
+
257
+ <style scoped>
258
+ .ebiz-descriptions {
259
+ font-size: 14px;
260
+ color: rgba(0, 0, 0, 0.9);
261
+ line-height: 22px;
262
+ }
263
+
264
+ .ebiz-descriptions__header {
265
+ margin-bottom: 16px;
266
+ }
267
+
268
+ .ebiz-descriptions__header > * {
269
+ font-size: 16px;
270
+ font-weight: 700;
271
+ color: rgba(0, 0, 0, 0.9);
272
+ line-height: 24px;
273
+ }
274
+
275
+ .ebiz-descriptions__body {
276
+ width: 100%;
277
+ border-collapse: collapse;
278
+ table-layout: fixed;
279
+ }
280
+
281
+ .ebiz-descriptions__body--fixed {
282
+ table-layout: fixed;
283
+ }
284
+
285
+ .ebiz-descriptions__body--border {
286
+ border-collapse: separate;
287
+ border-spacing: 0;
288
+ border-top: 1px solid var(--td-descriptions-border-color, #dcdcdc);
289
+ border-left: 1px solid var(--td-descriptions-border-color, #dcdcdc);
290
+ }
291
+
292
+ .ebiz-descriptions__body--border .ebiz-descriptions__label,
293
+ .ebiz-descriptions__body--border .ebiz-descriptions__content {
294
+ border-right: 1px solid var(--td-descriptions-border-color, #dcdcdc);
295
+ border-bottom: 1px solid var(--td-descriptions-border-color, #dcdcdc);
296
+ }
297
+
298
+ .ebiz-descriptions__label {
299
+ font-weight: normal;
300
+ color: rgba(0, 0, 0, 0.6);
301
+ background-color: #fafafa;
302
+ text-align: left;
303
+ padding: 12px 24px 12px 12px;
304
+ vertical-align: top;
305
+ }
306
+
307
+ .ebiz-descriptions__colon {
308
+ margin: 0 8px 0 2px;
309
+ }
310
+
311
+ .ebiz-descriptions__content {
312
+ padding: 12px;
313
+ text-align: left;
314
+ vertical-align: top;
315
+ }
316
+
317
+ /* 尺寸相关样式 */
318
+ .ebiz-descriptions--small .ebiz-descriptions__label,
319
+ .ebiz-descriptions--small .ebiz-descriptions__content {
320
+ padding: 8px;
321
+ }
322
+
323
+ .ebiz-descriptions--medium .ebiz-descriptions__label,
324
+ .ebiz-descriptions--medium .ebiz-descriptions__content {
325
+ padding: 12px;
326
+ }
327
+
328
+ .ebiz-descriptions--large .ebiz-descriptions__label,
329
+ .ebiz-descriptions--large .ebiz-descriptions__content {
330
+ padding: 16px;
331
+ }
332
+
333
+ /* 不同布局的padding调整 */
334
+ .ebiz-descriptions__body:not(.ebiz-descriptions__body--border) .ebiz-descriptions__label {
335
+ padding-left: 0;
336
+ }
337
+
338
+ .ebiz-descriptions__body:not(.ebiz-descriptions__body--border) .ebiz-descriptions__content {
339
+ padding-right: 0;
340
+ }
341
+ </style>
@@ -0,0 +1,48 @@
1
+ <template>
2
+ <div>
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { defineProps } from 'vue';
9
+
10
+ defineProps({
11
+ label: {
12
+ type: String,
13
+ default: ''
14
+ },
15
+ span: {
16
+ type: Number,
17
+ default: 1
18
+ },
19
+ className: {
20
+ type: String,
21
+ default: ''
22
+ },
23
+ labelClassName: {
24
+ type: String,
25
+ default: ''
26
+ },
27
+ contentClassName: {
28
+ type: String,
29
+ default: ''
30
+ },
31
+ layout: {
32
+ type: String,
33
+ default: '',
34
+ validator(val) {
35
+ if (!val) return true;
36
+ return ['horizontal', 'vertical'].includes(val);
37
+ }
38
+ }
39
+ });
40
+ </script>
41
+
42
+ <style scoped>
43
+ .ebiz-descriptions-item {
44
+ box-sizing: border-box;
45
+ padding: 0;
46
+ margin: 0;
47
+ }
48
+ </style>
@@ -2,8 +2,7 @@
2
2
  <t-select ref="selectRef" v-model="selectedValue" :loading="loading" :filterable="true" @search="handleRemoteSearch"
3
3
  :multiple="multiple" :placeholder="placeholder" :clearable="clearable" :disabled="disabled" :size="size"
4
4
  :empty="emptyText" :popupProps="popupProps" @change="handleChange" @focus="handleFocus" @blur="handleBlur">
5
- <t-option v-for="item in options" :key="item.value" :value="item.value" :label="item.label"
6
- @click="handleClick(item.value)"></t-option>
5
+ <t-option v-for="item in options" :key="item.value" :value="item.value" :label="item.label"></t-option>
7
6
  <template #prefixIcon v-if="$slots.prefix">
8
7
  <slot name="prefix"></slot>
9
8
  </template>
@@ -98,7 +97,7 @@ const props = defineProps({
98
97
  size: {
99
98
  type: String,
100
99
  default: '',
101
- validator: (val) => ['small', 'medium', 'large'].includes(val)
100
+ validator: (val) => ['small', 'medium', 'large', ''].includes(val)
102
101
  },
103
102
  /**
104
103
  * 空数据文本
@@ -0,0 +1,48 @@
1
+ <template>
2
+ <t-popconfirm
3
+ :content="content"
4
+ @cancel="handleCancel"
5
+ @confirm="handleConfirm"
6
+ >
7
+ <slot></slot>
8
+ </t-popconfirm>
9
+ </template>
10
+
11
+ <script>
12
+ export default {
13
+ name: "EbizPopconfirm"
14
+ }
15
+ </script>
16
+
17
+ <script setup>
18
+ import { Popconfirm as TPopconfirm } from 'tdesign-vue-next';
19
+
20
+ // 定义属性
21
+ const props = defineProps({
22
+ // 确认框内容
23
+ content: {
24
+ type: [String, Function],
25
+ default: ''
26
+ }
27
+ });
28
+
29
+ // 定义事件
30
+ const emit = defineEmits([
31
+ 'cancel',
32
+ 'confirm'
33
+ ]);
34
+
35
+ // 取消按钮点击事件
36
+ const handleCancel = ({ e }) => {
37
+ emit('cancel', { e });
38
+ };
39
+
40
+ // 确认按钮点击事件
41
+ const handleConfirm = ({ e }) => {
42
+ emit('confirm', { e });
43
+ };
44
+ </script>
45
+
46
+ <style lang="less" scoped>
47
+ /* 自定义样式可在此处添加 */
48
+ </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <t-table :data="data" :columns="mergedColumns" :row-key="rowKey" :vertical-align="verticalAlign"
3
+ <t-table ref="tableRef" :data="data" :columns="mergedColumns" :row-key="rowKey" :vertical-align="verticalAlign"
4
4
  :horizontal-align="horizontalAlign" :size="size" :bordered="bordered" :stripe="stripe" :hover="hover"
5
5
  :height="height" :max-height="maxHeight" :loading="loading" :loading-props="loadingProps" :load-more="loadMore"
6
6
  :empty="empty" :table-layout="tableLayout" :cell-empty-content="cellEmptyContent" :pagination="pagination"
@@ -459,6 +459,10 @@ const handleDragSort = (params) => {
459
459
  const handleValidate = (context) => {
460
460
  emit('validate', context);
461
461
  };
462
+ const tableRef = ref();
463
+ defineExpose({
464
+ tableRef
465
+ })
462
466
  </script>
463
467
 
464
468
  <style lang="less" scoped>