@ebiz/designer-components 0.0.18-kzy.3 → 0.0.18-kzy.5
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/dist/designer-components.css +2 -2
- package/dist/index.mjs +43839 -39549
- package/package.json +1 -1
- package/src/apiService/simpleDataService.js +7 -4
- package/src/components/Button.vue +1 -1
- package/src/components/EbizDetailBlock.vue +82 -0
- package/src/components/EbizDialog.vue +249 -0
- package/src/components/EbizGridEditorSelect.vue +238 -0
- package/src/components/EbizPageHeader.vue +96 -0
- package/src/components/EbizRemoteSelect.vue +7 -5
- package/src/components/EbizTimePicker.vue +144 -0
- package/src/components/EbizTree.vue +153 -0
- package/src/components/EbizTreeSelector.vue +423 -0
- package/src/components/TddesignButton.vue +153 -0
- package/src/components/TdesignUpload.vue +2 -3
- package/src/index.js +17 -1
- package/src/router/index.js +44 -0
- package/src/views/Button.vue +7 -3
- package/src/views/EbizDetailBlockDemo.vue +31 -0
- package/src/views/Home.vue +31 -2
- package/src/views/PageHeaderDemo.vue +105 -0
- package/src/views/TimePickerDemo.vue +147 -0
- package/src/views/TreeDemo.vue +255 -0
- package/src/views/TreeSelectorDemo.vue +246 -0
- package/src/components/Button copy.vue +0 -104
@@ -0,0 +1,423 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="ebiz-tree-selector">
|
3
|
+
<div class="selected-items" v-if="modelValue && modelValue.length">
|
4
|
+
<div v-for="(item, index) in modelValue" :key="index" class="selected-item">
|
5
|
+
<span class="item-text">{{ item.label }}</span>
|
6
|
+
<span class="item-remove" @click.stop="removeItem(index)">×</span>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<t-button @click="showDialog" variant="text" theme="primary" size="small"> 添加 </t-button>
|
10
|
+
|
11
|
+
<EbizDialog v-model:visible="dialogVisible" header="选择人员/部门" width="800px" placement="center" confirmBtn="确定"
|
12
|
+
cancelBtn="取消" @confirm="handleConfirm" @cancel="handleCancel">
|
13
|
+
<div class="selector-container">
|
14
|
+
<!-- 左侧选择区域 -->
|
15
|
+
<div class="left-panel">
|
16
|
+
<!-- 顶部搜索区域 -->
|
17
|
+
<div class="search-box">
|
18
|
+
<t-input v-model="searchText" placeholder="搜索成员、部门或标签" clearable>
|
19
|
+
<template #suffix-icon>
|
20
|
+
<t-icon name="search"></t-icon>
|
21
|
+
</template>
|
22
|
+
</t-input>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<!-- 选项卡 -->
|
26
|
+
<t-tabs v-model="activeTab" class="selector-tabs">
|
27
|
+
<t-tab-panel value="organization" label="组织架构" :destroyOnHide="false">
|
28
|
+
|
29
|
+
</t-tab-panel>
|
30
|
+
<!-- <t-tab-panel value="department" label="部门"></t-tab-panel> -->
|
31
|
+
<!-- <t-tab-panel value="position" label="岗位"></t-tab-panel> -->
|
32
|
+
<t-tab-panel value="employee" label="员工"></t-tab-panel>
|
33
|
+
</t-tabs>
|
34
|
+
|
35
|
+
<!-- 树形结构区域 -->
|
36
|
+
<div class="tree-content">
|
37
|
+
<div v-if="loading" class="loading-container">
|
38
|
+
<t-loading />
|
39
|
+
</div>
|
40
|
+
<EbizTree ref="organizationTree" v-show="activeTab === 'organization'" checkable :items="organizationData"
|
41
|
+
v-model="checkedNodes" v-model:expanded="expandedNodes" :disable-check="disableCheck"
|
42
|
+
:keys="{ label: 'name', value: 'id', children: 'childs' }" @change="handleChange" />
|
43
|
+
<!-- <EbizTree
|
44
|
+
v-else-if="activeTab === 'department'"
|
45
|
+
checkable
|
46
|
+
:items="filteredData."
|
47
|
+
v-model="checkedNodes"
|
48
|
+
:disable-check="disableCheck"
|
49
|
+
/>
|
50
|
+
<EbizTree
|
51
|
+
v-else-if="activeTab === 'position'"
|
52
|
+
checkable
|
53
|
+
:items="filteredData.position"
|
54
|
+
v-model="checkedNodes"
|
55
|
+
:disable-check="disableCheck"
|
56
|
+
/> -->
|
57
|
+
<EbizTree ref="employeeTree" v-show="activeTab === 'employee'" checkable :items="employeeData"
|
58
|
+
:keys="{ label: 'label', value: 'bindid', children: 'childs' }" v-model="checkedUserNodes"
|
59
|
+
:disable-check="disableCheck" />
|
60
|
+
</div>
|
61
|
+
</div>
|
62
|
+
|
63
|
+
<!-- 右侧已选区域 -->
|
64
|
+
<div class="right-panel">
|
65
|
+
<div class="selected-title">已选择的部门、成员</div>
|
66
|
+
<div class="selected-count">共 {{ [...selectPreview, ...selectPreviewUser].length }} 项</div>
|
67
|
+
<div class="selected-list">
|
68
|
+
<div v-for="(item, index) in [...selectPreview,...selectPreviewUser]" :key="index"
|
69
|
+
class="selected-list-item">
|
70
|
+
<t-icon :name="getIconForType(item)" class="item-icon" />
|
71
|
+
<span class="selected-label">{{ item.label }}</span>
|
72
|
+
<t-icon name="close-circle" class="remove-icon" @click="removePreviewItem(item)" />
|
73
|
+
</div>
|
74
|
+
<div v-if="[...selectPreview, ...selectPreviewUser].length === 0" class="no-selection">
|
75
|
+
<t-icon name="info-circle" />
|
76
|
+
<span style="user-select: none">请在左侧选择部门或成员</span>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
</div>
|
81
|
+
</EbizDialog>
|
82
|
+
</div>
|
83
|
+
</template>
|
84
|
+
|
85
|
+
<script setup>
|
86
|
+
import { onMounted, ref, watch } from 'vue'
|
87
|
+
import {
|
88
|
+
Button as TButton,
|
89
|
+
Icon as TIcon,
|
90
|
+
Input as TInput,
|
91
|
+
Loading as TLoading,
|
92
|
+
TabPanel as TTabPanel,
|
93
|
+
Tabs as TTabs
|
94
|
+
} from 'tdesign-vue-next'
|
95
|
+
import EbizDialog from './TdesignDialog.vue'
|
96
|
+
import EbizTree from './EbizTree.vue'
|
97
|
+
import dataService from '../apiService/simpleDataService'
|
98
|
+
|
99
|
+
const props = defineProps({
|
100
|
+
// 选中的数据,支持v-model
|
101
|
+
modelValue: {
|
102
|
+
type: Array,
|
103
|
+
default: () => []
|
104
|
+
},
|
105
|
+
// 树形数据,可以是单个数组或分类对象
|
106
|
+
data: {
|
107
|
+
type: [Array, Object],
|
108
|
+
default: () => []
|
109
|
+
},
|
110
|
+
// 禁用选择的节点
|
111
|
+
disableCheck: {
|
112
|
+
type: Function,
|
113
|
+
default: null
|
114
|
+
},
|
115
|
+
// 标题
|
116
|
+
title: {
|
117
|
+
type: String,
|
118
|
+
default: '选择人员/部门'
|
119
|
+
}
|
120
|
+
})
|
121
|
+
|
122
|
+
const emit = defineEmits(['update:modelValue', 'change'])
|
123
|
+
|
124
|
+
// 弹窗显示状态
|
125
|
+
const dialogVisible = ref(false)
|
126
|
+
// 搜索文本
|
127
|
+
const searchText = ref('')
|
128
|
+
// 选中的节点
|
129
|
+
const checkedNodes = ref([])
|
130
|
+
const checkedUserNodes = ref([]);
|
131
|
+
// 展开的节点
|
132
|
+
const expandedNodes = ref([])
|
133
|
+
// 当前活动的选项卡
|
134
|
+
const activeTab = ref('organization')
|
135
|
+
// 弹窗内预览的选中项
|
136
|
+
const selectPreview = ref([])
|
137
|
+
const selectPreviewUser = ref([])
|
138
|
+
// 加载状态
|
139
|
+
const loading = ref(false)
|
140
|
+
// API返回的数据
|
141
|
+
const organizationData = ref([])
|
142
|
+
const employeeData = ref([])
|
143
|
+
const organizationTree = ref()
|
144
|
+
const employeeTree = ref()
|
145
|
+
|
146
|
+
// 获取API数据
|
147
|
+
const fetchApiData = async () => {
|
148
|
+
loading.value = true
|
149
|
+
try {
|
150
|
+
organizationData.value = await dataService.fetch(
|
151
|
+
{ keyWord: activeTab.value === 'organization' ? searchText.value : '' },
|
152
|
+
{},
|
153
|
+
'/appdata/execute/plugin?key=organizational_structure'
|
154
|
+
)
|
155
|
+
employeeData.value = (await dataService.fetch(
|
156
|
+
{keyWord:activeTab.value === 'employee' ? searchText.value : ''},
|
157
|
+
{},
|
158
|
+
"/appdata/execute/plugin?key=all_active_employesse"
|
159
|
+
)).map(i=>({
|
160
|
+
label:i.name,
|
161
|
+
bindid:i.id,
|
162
|
+
type:'employee'
|
163
|
+
}))
|
164
|
+
} catch (error) {
|
165
|
+
} finally {
|
166
|
+
loading.value = false
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
// 根据类型获取对应图标
|
171
|
+
const getIconForType = (item) => {
|
172
|
+
const iconMap = {
|
173
|
+
GS: 'internet',
|
174
|
+
DEPT: 'folder',
|
175
|
+
GW: 'user-list',
|
176
|
+
employee: 'user'
|
177
|
+
}
|
178
|
+
return iconMap[item.type] || 'user'
|
179
|
+
}
|
180
|
+
|
181
|
+
// 显示对话框
|
182
|
+
function showDialog() {
|
183
|
+
// 获取API数据
|
184
|
+
fetchApiData()
|
185
|
+
// 重置选中的节点
|
186
|
+
checkedNodes.value = props.modelValue.filter(i => i.type !== 'employee').map((item) => item.bindid);
|
187
|
+
checkedUserNodes.value = props.modelValue.filter(i => i.type === 'employee').map((item) => item.bindid);
|
188
|
+
dialogVisible.value = true
|
189
|
+
}
|
190
|
+
|
191
|
+
// 确认选择
|
192
|
+
function handleConfirm() {
|
193
|
+
const finalValue = [...selectPreview.value.map((item) => ({
|
194
|
+
label: item.label,
|
195
|
+
bindid: item.bindid,
|
196
|
+
type: item.type
|
197
|
+
})), ...selectPreviewUser.value];
|
198
|
+
emit(
|
199
|
+
'update:modelValue', finalValue
|
200
|
+
)
|
201
|
+
emit('change', finalValue)
|
202
|
+
dialogVisible.value = false
|
203
|
+
}
|
204
|
+
|
205
|
+
// 取消选择
|
206
|
+
function handleCancel() {
|
207
|
+
dialogVisible.value = false
|
208
|
+
}
|
209
|
+
|
210
|
+
// 移除已选择预览中的项目
|
211
|
+
function removePreviewItem(item) {
|
212
|
+
const isUser = item.type == 'employee';
|
213
|
+
if(isUser){
|
214
|
+
let index = selectPreviewUser.value.findIndex(i=>i.bindid === item.bindid);
|
215
|
+
const removedUser = selectPreviewUser.value[index];
|
216
|
+
selectPreviewUser.value.splice(index,1);
|
217
|
+
checkedUserNodes.value = checkedUserNodes.value.filter((value) => value !== removedUser.bindid)
|
218
|
+
return
|
219
|
+
}
|
220
|
+
let index = selectPreview.value.findIndex(i => i.bindid === item.bindid);
|
221
|
+
const removedItem = selectPreview.value[index];
|
222
|
+
selectPreview.value.splice(index, 1)
|
223
|
+
checkedNodes.value = checkedNodes.value.filter((value) => value !== removedItem.bindid)
|
224
|
+
}
|
225
|
+
|
226
|
+
// 移除选中项
|
227
|
+
function removeItem(index) {
|
228
|
+
const newValue = [...props.modelValue]
|
229
|
+
newValue.splice(index, 1)
|
230
|
+
console.log(newValue,230)
|
231
|
+
emit('update:modelValue', newValue)
|
232
|
+
emit('change', newValue)
|
233
|
+
}
|
234
|
+
|
235
|
+
// 监听选中节点变化,同步到预览
|
236
|
+
watch(
|
237
|
+
checkedNodes,
|
238
|
+
(newValues) => {
|
239
|
+
if (!organizationTree.value.treeRef) return
|
240
|
+
selectPreview.value = newValues.map(organizationTree.value.treeRef.getItem).map((i) => ({
|
241
|
+
label: i?.label,
|
242
|
+
type: i?.data?.type || i.type,
|
243
|
+
bindid: i?.value
|
244
|
+
}))
|
245
|
+
},
|
246
|
+
{ deep: true }
|
247
|
+
)
|
248
|
+
watch(checkedUserNodes,nVal=>{
|
249
|
+
selectPreviewUser.value = employeeData.value.filter(i=>nVal.includes(i.bindid))
|
250
|
+
},{deep:true})
|
251
|
+
// 监听modelValue变化,同步到选中节点
|
252
|
+
watch(
|
253
|
+
() => props.modelValue,
|
254
|
+
(newValue) => {
|
255
|
+
checkedNodes.value = newValue.filter(i=>i.type!=='employee').map((item) => item.bindid);
|
256
|
+
checkedUserNodes.value = newValue.filter(i => i.type === 'employee').map((item) => item.bindid);
|
257
|
+
console.log(newValue,checkedUserNodes.value,267)
|
258
|
+
},
|
259
|
+
{ deep: true ,immediate:true}
|
260
|
+
)
|
261
|
+
watch(activeTab,nVal=>{
|
262
|
+
searchText.value = ''
|
263
|
+
})
|
264
|
+
watch(searchText,nVal=>{
|
265
|
+
fetchApiData()
|
266
|
+
})
|
267
|
+
// 组件挂载时,预加载数据
|
268
|
+
onMounted(() => {
|
269
|
+
showDialog()
|
270
|
+
})
|
271
|
+
</script>
|
272
|
+
|
273
|
+
<style scoped>
|
274
|
+
.ebiz-tree-selector {
|
275
|
+
display: flex;
|
276
|
+
flex-wrap: wrap;
|
277
|
+
align-items: center;
|
278
|
+
gap: 8px;
|
279
|
+
min-height: 32px;
|
280
|
+
}
|
281
|
+
|
282
|
+
.selected-items {
|
283
|
+
display: flex;
|
284
|
+
flex-wrap: wrap;
|
285
|
+
gap: 4px;
|
286
|
+
}
|
287
|
+
|
288
|
+
.selected-item {
|
289
|
+
display: flex;
|
290
|
+
align-items: center;
|
291
|
+
padding: 4px 8px;
|
292
|
+
background-color: #f0f0f0;
|
293
|
+
border-radius: 4px;
|
294
|
+
font-size: 14px;
|
295
|
+
}
|
296
|
+
|
297
|
+
.item-text {
|
298
|
+
margin-right: 4px;
|
299
|
+
}
|
300
|
+
|
301
|
+
.item-remove {
|
302
|
+
cursor: pointer;
|
303
|
+
color: #999;
|
304
|
+
font-size: 16px;
|
305
|
+
}
|
306
|
+
|
307
|
+
.item-remove:hover {
|
308
|
+
color: #ff4d4f;
|
309
|
+
}
|
310
|
+
|
311
|
+
.add-button {
|
312
|
+
display: flex;
|
313
|
+
align-items: center;
|
314
|
+
}
|
315
|
+
|
316
|
+
.selector-container {
|
317
|
+
display: flex;
|
318
|
+
height: 500px;
|
319
|
+
}
|
320
|
+
|
321
|
+
.left-panel {
|
322
|
+
flex: 1;
|
323
|
+
display: flex;
|
324
|
+
flex-direction: column;
|
325
|
+
border-right: 1px solid #e0e0e0;
|
326
|
+
padding-right: 16px;
|
327
|
+
}
|
328
|
+
|
329
|
+
.right-panel {
|
330
|
+
width: 280px;
|
331
|
+
display: flex;
|
332
|
+
flex-direction: column;
|
333
|
+
padding-left: 16px;
|
334
|
+
}
|
335
|
+
|
336
|
+
.search-box {
|
337
|
+
margin-bottom: 12px;
|
338
|
+
}
|
339
|
+
|
340
|
+
.selector-tabs {
|
341
|
+
margin-bottom: 12px;
|
342
|
+
}
|
343
|
+
|
344
|
+
.tree-content {
|
345
|
+
flex: 1;
|
346
|
+
overflow: auto;
|
347
|
+
border: 1px solid #e0e0e0;
|
348
|
+
border-radius: 4px;
|
349
|
+
padding: 12px;
|
350
|
+
position: relative;
|
351
|
+
}
|
352
|
+
|
353
|
+
.loading-container {
|
354
|
+
position: absolute;
|
355
|
+
top: 0;
|
356
|
+
left: 0;
|
357
|
+
right: 0;
|
358
|
+
bottom: 0;
|
359
|
+
display: flex;
|
360
|
+
align-items: center;
|
361
|
+
justify-content: center;
|
362
|
+
background-color: rgba(255, 255, 255, 0.7);
|
363
|
+
}
|
364
|
+
|
365
|
+
.selected-title {
|
366
|
+
font-weight: bold;
|
367
|
+
margin-bottom: 8px;
|
368
|
+
}
|
369
|
+
|
370
|
+
.selected-count {
|
371
|
+
color: #999;
|
372
|
+
font-size: 12px;
|
373
|
+
margin-bottom: 12px;
|
374
|
+
}
|
375
|
+
|
376
|
+
.selected-list {
|
377
|
+
flex: 1;
|
378
|
+
overflow: auto;
|
379
|
+
border: 1px solid #e0e0e0;
|
380
|
+
border-radius: 4px;
|
381
|
+
padding: 8px;
|
382
|
+
}
|
383
|
+
|
384
|
+
.selected-list-item {
|
385
|
+
display: flex;
|
386
|
+
align-items: center;
|
387
|
+
padding: 8px;
|
388
|
+
border-bottom: 1px solid #f0f0f0;
|
389
|
+
}
|
390
|
+
|
391
|
+
.selected-list-item:last-child {
|
392
|
+
border-bottom: none;
|
393
|
+
}
|
394
|
+
|
395
|
+
.item-icon {
|
396
|
+
color: #0052d9;
|
397
|
+
margin-right: 8px;
|
398
|
+
}
|
399
|
+
|
400
|
+
.selected-label {
|
401
|
+
flex: 1;
|
402
|
+
user-select: none;
|
403
|
+
}
|
404
|
+
|
405
|
+
.remove-icon {
|
406
|
+
color: #999;
|
407
|
+
cursor: pointer;
|
408
|
+
}
|
409
|
+
|
410
|
+
.remove-icon:hover {
|
411
|
+
color: #ff4d4f;
|
412
|
+
}
|
413
|
+
|
414
|
+
.no-selection {
|
415
|
+
display: flex;
|
416
|
+
flex-direction: column;
|
417
|
+
align-items: center;
|
418
|
+
justify-content: center;
|
419
|
+
height: 100%;
|
420
|
+
color: #999;
|
421
|
+
gap: 8px;
|
422
|
+
}
|
423
|
+
</style>
|
@@ -0,0 +1,153 @@
|
|
1
|
+
<template>
|
2
|
+
<tiny-tooltip :content="!isNormal && !apiConfig.apiId ? tooltipText : ''" placement="top">
|
3
|
+
<t-button :disabled="disabled || loading" :theme="type" :size="size" @click="click">
|
4
|
+
<slot>{{ text }}</slot>
|
5
|
+
</t-button>
|
6
|
+
</tiny-tooltip>
|
7
|
+
</template>
|
8
|
+
|
9
|
+
|
10
|
+
<script lang='js'>
|
11
|
+
export default {
|
12
|
+
name: "EbizButton"
|
13
|
+
}
|
14
|
+
</script>
|
15
|
+
|
16
|
+
<script lang='js' setup>
|
17
|
+
|
18
|
+
import { ref, reactive, computed, toRef, toRefs } from 'vue';
|
19
|
+
import { Button as TButton } from 'tdesign-vue-next';
|
20
|
+
import { TinyTooltip } from '@opentiny/vue';
|
21
|
+
import dataService from '../apiService/simpleDataService';
|
22
|
+
|
23
|
+
const props = defineProps({
|
24
|
+
text: {
|
25
|
+
type: String,
|
26
|
+
default: ''
|
27
|
+
},
|
28
|
+
isNormal: {//是否为普通按钮
|
29
|
+
type: Boolean,
|
30
|
+
default: false
|
31
|
+
},
|
32
|
+
disabled: {
|
33
|
+
type: Boolean,
|
34
|
+
default: false
|
35
|
+
},
|
36
|
+
type: {//'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info'
|
37
|
+
type: String,
|
38
|
+
default: 'primary'
|
39
|
+
},
|
40
|
+
size: {//'large' | 'medium' | 'small'
|
41
|
+
type: String,
|
42
|
+
default: 'medium'
|
43
|
+
},
|
44
|
+
apiConfig: {//接口配置
|
45
|
+
type: Object,
|
46
|
+
default: () => ({
|
47
|
+
key: null,
|
48
|
+
apiId: null,
|
49
|
+
apiType: ''
|
50
|
+
})
|
51
|
+
},
|
52
|
+
data: {
|
53
|
+
type: Object,
|
54
|
+
default: () => ({})
|
55
|
+
},
|
56
|
+
onClick: {
|
57
|
+
type: Function,
|
58
|
+
default: () => { }
|
59
|
+
},
|
60
|
+
onPrepare: {//调用接口前置事件
|
61
|
+
type: Function,
|
62
|
+
default: () => true // 默认返回true,允许继续执行
|
63
|
+
},
|
64
|
+
onFinish: {//调用接口后置事件
|
65
|
+
type: Function,
|
66
|
+
default: () => { }
|
67
|
+
},
|
68
|
+
onError: {
|
69
|
+
type: Function,
|
70
|
+
default: () => { }
|
71
|
+
}
|
72
|
+
})
|
73
|
+
|
74
|
+
const apiMap = [
|
75
|
+
'MULTIPLE_DATA_SEARCH',
|
76
|
+
'DETAILS_DATA',
|
77
|
+
'INTERFACE_PLUGIN',
|
78
|
+
'DATA_INSERT',
|
79
|
+
'BATCH_DATA_INSERT',
|
80
|
+
'DATA_MODIFY',
|
81
|
+
'BATCH_DATA_MODIFY',
|
82
|
+
'DEL_DATA',
|
83
|
+
'BATCH_DEL_DATA',
|
84
|
+
'MULTIPLE_DATA_LINK_SEARCH'
|
85
|
+
];
|
86
|
+
|
87
|
+
const emit = defineEmits(['click', 'success', 'error', 'loading', 'prepare'])
|
88
|
+
|
89
|
+
const { text, isNormal, disabled, type, size, variant, apiConfig, data } = toRefs(props)
|
90
|
+
|
91
|
+
const tooltipText = ref('请先设置接口配置')
|
92
|
+
const loading = ref(false)
|
93
|
+
|
94
|
+
// 检查API配置是否有效
|
95
|
+
const isValidApiConfig = () => {
|
96
|
+
return apiConfig.value && apiConfig.value.apiId &&
|
97
|
+
typeof apiConfig.value.apiType === 'number' &&
|
98
|
+
apiConfig.value.apiType >= 0 &&
|
99
|
+
apiConfig.value.apiType < apiMap.length;
|
100
|
+
}
|
101
|
+
|
102
|
+
const click = async (e) => {
|
103
|
+
// 总是调用onClick回调
|
104
|
+
props.onClick()
|
105
|
+
emit('click', e)
|
106
|
+
|
107
|
+
// 如果是普通按钮或API配置无效,不调用接口
|
108
|
+
if (isNormal.value || !isValidApiConfig()) {
|
109
|
+
return
|
110
|
+
}
|
111
|
+
|
112
|
+
try {
|
113
|
+
// 设置加载状态
|
114
|
+
loading.value = true
|
115
|
+
emit('loading', true)
|
116
|
+
|
117
|
+
// 调用前置处理并获取返回值
|
118
|
+
const shouldContinue = props.onPrepare()
|
119
|
+
emit('prepare', shouldContinue)
|
120
|
+
|
121
|
+
// 只有当返回值明确为false时才中断,undefined或其他值都继续执行
|
122
|
+
if (shouldContinue === false) {
|
123
|
+
return null
|
124
|
+
}
|
125
|
+
|
126
|
+
// 调用接口
|
127
|
+
const apiType = apiMap[apiConfig.value.apiType]
|
128
|
+
const res = await dataService.fetch(data.value, {
|
129
|
+
key: apiConfig.value.key,
|
130
|
+
apiId: apiConfig.value.apiId,
|
131
|
+
apiType
|
132
|
+
})
|
133
|
+
|
134
|
+
// 成功处理
|
135
|
+
emit('success', res)
|
136
|
+
props.onFinish(res)
|
137
|
+
|
138
|
+
return res
|
139
|
+
} catch (error) {
|
140
|
+
// 错误处理
|
141
|
+
emit('error', error)
|
142
|
+
props.onError(error)
|
143
|
+
|
144
|
+
return null
|
145
|
+
} finally {
|
146
|
+
// 清除加载状态
|
147
|
+
loading.value = false
|
148
|
+
emit('loading', false)
|
149
|
+
}
|
150
|
+
}
|
151
|
+
</script>
|
152
|
+
|
153
|
+
<style lang='less' scoped></style>
|
@@ -4,7 +4,6 @@
|
|
4
4
|
:draggable="draggable" :fileListDisplay="fileListDisplay" :files="internalFiles" :format="format"
|
5
5
|
:formatRequest="formatRequest" :headers="headers" :isBatchUpload="isBatchUpload" :max="max" :method="method"
|
6
6
|
:multiple="multiple" :name="name" :placeholder="placeholder"
|
7
|
-
:requestMethod="useInternalUpload ? customRequestMethod : requestMethod"
|
8
7
|
:showUploadProgress="showUploadProgress" :sizeLimit="sizeLimit" :status="status" :theme="theme" :tips="tips"
|
9
8
|
:uploadAllFilesInOneRequest="uploadAllFilesInOneRequest" :uploadButton="uploadButton"
|
10
9
|
:useMockProgress="useMockProgress" :withCredentials="withCredentials" v-model="modelValue"
|
@@ -58,7 +57,7 @@ import { Upload as TUpload } from 'tdesign-vue-next';
|
|
58
57
|
import dataService from '../apiService/simpleDataService';
|
59
58
|
|
60
59
|
// 内部上传地址常量
|
61
|
-
const INTERNAL_UPLOAD_URL = '/file/upload';
|
60
|
+
const INTERNAL_UPLOAD_URL = '/api/file/app/td-upload';
|
62
61
|
|
63
62
|
// 内部维护的文件列表,代替直接使用props.files
|
64
63
|
const internalFiles = ref([]);
|
@@ -274,7 +273,7 @@ const props = defineProps({
|
|
274
273
|
// 是否使用内部上传服务,如果为true则忽略action参数
|
275
274
|
useInternalUpload: {
|
276
275
|
type: Boolean,
|
277
|
-
default:
|
276
|
+
default: false
|
278
277
|
}
|
279
278
|
});
|
280
279
|
|
package/src/index.js
CHANGED
@@ -23,6 +23,7 @@ import EbizRouteBreadcrumb from "./components/EbizRouteBreadcrumb.vue";
|
|
23
23
|
import EbizRichTextEditor from "./components/EbizRichTextEditor.vue";
|
24
24
|
import EbizFileUpload from "./components/EbizFileUpload.vue";
|
25
25
|
import EbizTabHeader from "./components/EbizTabHeader.vue";
|
26
|
+
import EbizPageHeader from "./components/EbizPageHeader.vue";
|
26
27
|
import TdesignCalendar from "./components/TdesignCalendar/index.vue";
|
27
28
|
import TdesignCollapse from "./components/TdesignCollapse.vue";
|
28
29
|
import TdesignCollapsePanel from "./components/TdesignCollapsePanel.vue";
|
@@ -52,6 +53,11 @@ import EbizTable from "./components/EbizTable.vue";
|
|
52
53
|
import EbizTableColumn from './components/EbizTableColumn.vue';
|
53
54
|
import EbizTableSort from './components/EbizTableSort.vue';
|
54
55
|
import EbizStatusBadge from "./components/EbizStatusBadge.vue";
|
56
|
+
import EbizDetailBlock from './components/EbizDetailBlock.vue';
|
57
|
+
import EbizTree from './components/EbizTree.vue';
|
58
|
+
import EbizTreeSelector from './components/EbizTreeSelector.vue';
|
59
|
+
import EbizTimePicker from './components/EbizTimePicker.vue';
|
60
|
+
import EbizGridEditorSelect from './components/EbizGridEditorSelect.vue';
|
55
61
|
import { MessagePlugin as EbizMessage } from 'tdesign-vue-next';
|
56
62
|
|
57
63
|
// 导入简洁数据服务
|
@@ -91,6 +97,8 @@ export {
|
|
91
97
|
EbizRouteBreadcrumb,
|
92
98
|
EbizRichTextEditor,
|
93
99
|
EbizTabHeader,
|
100
|
+
// 页面头部组件
|
101
|
+
EbizPageHeader,
|
94
102
|
// 思维导图
|
95
103
|
EbizMindmap,
|
96
104
|
// 文件上传组件
|
@@ -156,5 +164,13 @@ export {
|
|
156
164
|
// 表格排序组件
|
157
165
|
EbizTableSort,
|
158
166
|
// 状态标记组件
|
159
|
-
EbizStatusBadge
|
167
|
+
EbizStatusBadge,
|
168
|
+
// 详情块组件
|
169
|
+
EbizDetailBlock,
|
170
|
+
// 树组件
|
171
|
+
EbizTree,
|
172
|
+
EbizTreeSelector,
|
173
|
+
// 时间选择器组件
|
174
|
+
EbizTimePicker,
|
175
|
+
EbizGridEditorSelect // 表格编辑专用select组件
|
160
176
|
};
|
package/src/router/index.js
CHANGED
@@ -244,6 +244,12 @@ const routes = [
|
|
244
244
|
component: () => import('../views/DialogDemo.vue'),
|
245
245
|
meta: { title: 'TDesign对话框组件示例' }
|
246
246
|
},
|
247
|
+
{
|
248
|
+
path: '/page-header',
|
249
|
+
name: 'PageHeader',
|
250
|
+
component: () => import('../views/PageHeaderDemo.vue'),
|
251
|
+
meta: { title: 'Ebiz页面头部组件示例', icon: 'header' }
|
252
|
+
},
|
247
253
|
{
|
248
254
|
path: '/table-demo',
|
249
255
|
name: 'TableDemo',
|
@@ -261,6 +267,44 @@ const routes = [
|
|
261
267
|
name: 'TableSort',
|
262
268
|
component: () => import('../views/TableSortDemo.vue'),
|
263
269
|
meta: { title: 'Ebiz表格排序组件示例' }
|
270
|
+
},
|
271
|
+
{
|
272
|
+
path: '/ebiz-detail-block',
|
273
|
+
name: 'EbizDetailBlock',
|
274
|
+
component: () => import('../views/EbizDetailBlockDemo.vue'),
|
275
|
+
meta: { title: 'Ebiz详情块组件示例' }
|
276
|
+
},
|
277
|
+
{
|
278
|
+
path: '/tree',
|
279
|
+
name: 'Tree',
|
280
|
+
component: () => import('../views/TreeDemo.vue'),
|
281
|
+
meta: { title: 'Ebiz树组件示例' }
|
282
|
+
},
|
283
|
+
{
|
284
|
+
path: '/tree-demo',
|
285
|
+
name: 'TreeDemo',
|
286
|
+
component: () => import('../views/TreeDemo.vue'),
|
287
|
+
meta: { title: 'Ebiz树组件示例' }
|
288
|
+
},
|
289
|
+
{
|
290
|
+
path: '/tree-selector-demo',
|
291
|
+
name: 'TreeSelectorDemo',
|
292
|
+
component: () => import('../views/TreeSelectorDemo.vue'),
|
293
|
+
meta: {
|
294
|
+
title: '树形选择器'
|
295
|
+
}
|
296
|
+
},
|
297
|
+
{
|
298
|
+
path: '/tree-selector',
|
299
|
+
name: 'TreeSelector',
|
300
|
+
component: () => import('../views/TreeSelectorDemo.vue'),
|
301
|
+
meta: { title: '树选择器组件示例' }
|
302
|
+
},
|
303
|
+
{
|
304
|
+
path: '/time-picker',
|
305
|
+
name: 'TimePicker',
|
306
|
+
component: () => import('../views/TimePickerDemo.vue'),
|
307
|
+
meta: { title: '时间选择器组件示例' }
|
264
308
|
}
|
265
309
|
]
|
266
310
|
|