@ebiz/designer-components 0.0.18-kzy.3 → 0.0.18-kzy.4

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.
@@ -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: true
276
+ default: false
278
277
  }
279
278
  });
280
279
 
@@ -0,0 +1,238 @@
1
+ <template>
2
+ <t-select ref="selectRef" v-model="selectedValue" :loading="loading" :filterable="true" @search="handleRemoteSearch"
3
+ :multiple="multiple" :placeholder="placeholder" :clearable="clearable" :disabled="disabled" :size="size"
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>
7
+ <template #prefixIcon v-if="$slots.prefix">
8
+ <slot name="prefix"></slot>
9
+ </template>
10
+ <template #suffixIcon v-if="$slots.suffix">
11
+ <slot name="suffix"></slot>
12
+ </template>
13
+ </t-select>
14
+ </template>
15
+
16
+ <script>
17
+ export default {
18
+ name: "EbizRemoteSelect"
19
+ }
20
+ </script>
21
+
22
+ <script setup>
23
+ import { ref, onMounted, toRefs, computed } from 'vue';
24
+ import { Select as TSelect, Option as TOption } from '@opentiny/vue';
25
+ import dataService from '../apiService/simpleDataService';
26
+
27
+ const props = defineProps({
28
+ /**
29
+ * API配置
30
+ */
31
+ apiConfig: {
32
+ type: Object,
33
+ required: true,
34
+ default: () => ({
35
+ key: null,
36
+ apiId: null,
37
+ apiType: ''
38
+ })
39
+ },
40
+ /**
41
+ * 查询参数
42
+ */
43
+ queryParams: {
44
+ type: Object,
45
+ default: () => ({
46
+ name: ''
47
+ })
48
+ },
49
+ /**
50
+ * 是否多选
51
+ */
52
+ multiple: {
53
+ type: Boolean,
54
+ default: false
55
+ },
56
+ /**
57
+ * 占位符文本
58
+ */
59
+ placeholder: {
60
+ type: String,
61
+ default: '请选择'
62
+ },
63
+ /**
64
+ * 是否可清空
65
+ */
66
+ clearable: {
67
+ type: Boolean,
68
+ default: true
69
+ },
70
+ /**
71
+ * 是否禁用
72
+ */
73
+ disabled: {
74
+ type: Boolean,
75
+ default: false
76
+ },
77
+ /**
78
+ * 默认值
79
+ */
80
+ modelValue: {
81
+ type: [String, Number, Array],
82
+ default: ''
83
+ },
84
+ /**
85
+ * 选项配置
86
+ */
87
+ labelField: {
88
+ type: String,
89
+ default: ''
90
+ },
91
+ valueField: {
92
+ type: String,
93
+ default: ''
94
+ },
95
+ /**
96
+ * 组件尺寸
97
+ */
98
+ size: {
99
+ type: String,
100
+ default: '',
101
+ validator: (val) => ['small', 'medium', 'large'].includes(val)
102
+ },
103
+ /**
104
+ * 空数据文本
105
+ */
106
+ emptyText: {
107
+ type: String,
108
+ default: '暂无数据'
109
+ },
110
+ /**
111
+ * 弹出层配置
112
+ */
113
+ popupProps: {
114
+ type: Object,
115
+ default: () => ({})
116
+ }
117
+ });
118
+
119
+ const { modelValue, apiConfig, labelField, valueField, queryParams } = toRefs(props)
120
+
121
+ const emit = defineEmits(['update:modelValue', 'change', 'selectOption', 'focus', 'blur']);
122
+
123
+ // 选中的值
124
+ const selectedValue = computed({
125
+ get: () => modelValue.value,
126
+ set: (val) => {
127
+ emit('update:modelValue', val);
128
+ }
129
+ });
130
+
131
+ // 选项列表
132
+ const options = ref([]);
133
+
134
+ // 加载状态
135
+ const loading = ref(false);
136
+
137
+ // 获取select组件实例
138
+ const selectRef = ref(null);
139
+
140
+ // 暴露focus方法
141
+ const focus = () => {
142
+ selectRef.value?.focus();
143
+ };
144
+
145
+ defineExpose({
146
+ focus,
147
+ selectRef,
148
+ options
149
+ });
150
+
151
+ // 远程搜索处理函数
152
+ const handleRemoteSearch = async (keyword) => {
153
+ loading.value = true;
154
+ try {
155
+ const params = {
156
+ queryParams: {
157
+ ...queryParams.value,
158
+ name: keyword
159
+ }
160
+ };
161
+ const res = await dataService.fetch(params, {
162
+ key: props.apiConfig.key,
163
+ apiId: props.apiConfig.apiId,
164
+ apiType: 'MULTIPLE_DATA_SEARCH'
165
+ });
166
+
167
+ options.value = res.data.map(item => ({
168
+ ...item,
169
+ label: labelField.value ? item[labelField.value] : (item.label || item.name),
170
+ value: valueField.value ? item[valueField.value] : (item.value || item.id)
171
+ }));
172
+ } catch (error) {
173
+ console.error('远程搜索失败:', error);
174
+ options.value = [];
175
+ } finally {
176
+ loading.value = false;
177
+ }
178
+
179
+ };
180
+
181
+ // 处理获取焦点事件
182
+ const handleFocus = (value, context) => {
183
+ emit('focus', value, context);
184
+ // 当选项为空时,初始加载数据
185
+ if (options.value.length === 0) {
186
+ handleRemoteSearch('');
187
+ }
188
+ };
189
+
190
+ // 处理失去焦点事件
191
+ const handleBlur = (value, context) => {
192
+ emit('blur', value, context);
193
+ };
194
+
195
+ // 值变化处理函数
196
+ const handleChange = (value, context) => {
197
+ emit('update:modelValue', value);
198
+ emit('change', value, context);
199
+ if (value instanceof Array) {
200
+ emit('selectOption', options.value.filter(item => value.includes(item.value)));
201
+ } else {
202
+ emit('selectOption', options.value.find(item => item.value === value));
203
+ }
204
+ };
205
+
206
+ // 组件挂载时,如果有默认值则加载对应的选项
207
+ onMounted(async () => {
208
+ if (apiConfig.value.apiId && apiConfig.value.apiType >= 0) {
209
+ loading.value = true;
210
+ try {
211
+ const params = {
212
+ queryParams: queryParams.value
213
+ };
214
+ const res = await dataService.fetch(params, {
215
+ key: props.apiConfig.key,
216
+ apiId: props.apiConfig.apiId,
217
+ apiType: 'MULTIPLE_DATA_SEARCH'
218
+ });
219
+
220
+ options.value = res.data.map(item => ({
221
+ ...item,
222
+ label: labelField.value ? item[labelField.value] : (item.label || item.name),
223
+ value: valueField.value ? item[valueField.value] : (item.value || item.id)
224
+ }));
225
+ } catch (error) {
226
+ console.error('加载默认选项失败:', error);
227
+ } finally {
228
+ loading.value = false;
229
+ }
230
+ }
231
+ });
232
+ </script>
233
+
234
+ <style scoped>
235
+ .t-select {
236
+ width: 100%;
237
+ }
238
+ </style>
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 TinyGridEditorSelect from './components/TinyGridEditorSelect.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
+ TinyGridEditorSelect // 表格编辑专用select组件
160
176
  };
@@ -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
 
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div>
3
3
  <ebiz-route-breadcrumb />
4
- <ebiz-button />
4
+ <ebiz-button :text="测试" :apiConfig="{ apiId: 802, apiType: 5 }" @prepare="onPrepare" />
5
5
  </div>
6
6
  </template>
7
7
 
@@ -12,9 +12,13 @@ export default {
12
12
  name: 'ButtonDemo',
13
13
  components: {
14
14
  EbizButton
15
+ },
16
+ methods: {
17
+ onPrepare() {
18
+
19
+ }
15
20
  }
16
21
  }
17
22
  </script>
18
23
 
19
- <style scoped>
20
- </style>
24
+ <style scoped></style>
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <div>
3
+ <ebiz-route-breadcrumb />
4
+ <ebiz-detail-block :model="model" :labelMap="labelMap" :gap="10" :labelSize="20" :labelColor="'#000'" :valueSize="20" :valueColor="'#000'"/>
5
+ </div>
6
+ </template>
7
+
8
+ <script>
9
+ import { EbizDetailBlock } from '@/index.js'
10
+
11
+ export default {
12
+ name: 'EbizDetailBlockDemo',
13
+ components: {
14
+ EbizDetailBlock
15
+ },
16
+ data() {
17
+ return {
18
+ model: {
19
+ "name": "张三",
20
+ "sex": "男"
21
+ },
22
+ labelMap:{
23
+ "name": "姓名",
24
+ "sex": "性别"
25
+ }
26
+ }
27
+ },
28
+ }
29
+ </script>
30
+
31
+ <style scoped></style>
@@ -1,12 +1,22 @@
1
1
  <template>
2
2
  <div class="home">
3
- <h1>组件库示例</h1>
3
+ <h1>Ebiz 组件库</h1>
4
4
  <div class="component-list">
5
5
  <div v-for="(item, index) in components" :key="index" class="component-item">
6
6
  <router-link :to="item.path" class="component-link">
7
7
  {{ item.title }}
8
8
  </router-link>
9
9
  </div>
10
+
11
+ <router-link to="/tree-demo" class="component-item">
12
+ <div class="component-title">树组件</div>
13
+ <div class="component-desc">用于展示层级结构和操作的组件</div>
14
+ </router-link>
15
+
16
+ <router-link to="/tree-selector-demo" class="component-item">
17
+ <div class="component-title">树形选择器</div>
18
+ <div class="component-desc">基于树组件的选择器,支持搜索和多选</div>
19
+ </router-link>
10
20
  </div>
11
21
  </div>
12
22
  </template>
@@ -54,10 +64,15 @@ export default {
54
64
  { path: '/ebiz-employee-info', title: 'Ebiz员工信息组件示例' },
55
65
  { path: '/tdesign-alert', title: 'TDesign提示组件示例' },
56
66
  { path: '/tdesign-dialog', title: 'TDesign对话框组件示例' },
67
+ { path: '/page-header', title: 'Ebiz页面头部组件示例' },
57
68
  { path: '/table-demo', title: 'Ebiz表格组件示例' },
58
69
  { path: '/status-badge', title: 'Ebiz状态标记组件示例' },
70
+ { path: '/ebiz-detail-block', title: 'Ebiz详情块组件示例' },
59
71
  { path: '/table-column', title: 'Ebiz表格列组件示例' },
60
- { path: '/table-sort', title: 'Ebiz表格排序组件示例' }
72
+ { path: '/table-sort', title: 'Ebiz表格排序组件示例' },
73
+ { path: '/tree', title: 'Ebiz树组件示例' },
74
+ { path: '/tree-selector', title: 'Ebiz树选择器组件示例' },
75
+ { path: '/time-picker', title: 'Ebiz时间选择器组件示例' }
61
76
  ]
62
77
 
63
78
  return {
@@ -95,4 +110,18 @@ export default {
95
110
  .component-link:hover {
96
111
  color: #1890ff;
97
112
  }
113
+
114
+ .component-title {
115
+ font-size: 16px;
116
+ font-weight: bold;
117
+ margin-bottom: 10px;
118
+ }
119
+
120
+ .component-desc {
121
+ font-size: 14px;
122
+ }
123
+
124
+ .component-item:hover {
125
+ background-color: #e6e6e6;
126
+ }
98
127
  </style>