@ebiz/designer-components 0.0.30 → 0.0.32

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,228 @@
1
+ <template>
2
+ <div>
3
+ <div>
4
+ <EbizTdesignCard :bordered="false">
5
+ <EbizPageHeader></EbizPageHeader>
6
+ <EbizDivider></EbizDivider>
7
+ <!-- 表单区域 -->
8
+ <ebiz-s-form v-model="formDataValue" ref="formRef" layout="inline" labelAlign="top"
9
+ @submit="handleSearch" @reset="handleReset">
10
+ <slot name="form"></slot>
11
+ <template #buttons>
12
+ <t-space>
13
+ <EbizTdesignButton theme="primary" type="submit">搜索</EbizTdesignButton>
14
+ <EbizTdesignButton theme="default" type="reset">重置</EbizTdesignButton>
15
+ </t-space>
16
+ </template>
17
+ </ebiz-s-form>
18
+ </EbizTdesignCard>
19
+ </div>
20
+
21
+ <EbizTdesignCard :bordered="false" style="margin-top: 16px;">
22
+ <div>
23
+ <!-- 表格区域 -->
24
+ <div style="display: flex; justify-content: space-between; align-items: center;">
25
+ <div style="display: flex; align-items: center;">
26
+ <slot name="left"></slot>
27
+ <slot name="search">
28
+ <div v-if="defaultSearch" style="margin-left: 10px;">
29
+ <t-input v-model="searchValue" placeholder="请输入搜索内容" @change="handleSearch"></t-input>
30
+ </div>
31
+ </slot>
32
+ </div>
33
+ <div style="display: flex; align-items: center; justify-content: flex-end;">
34
+ <slot name="right"></slot>
35
+ <t-space size="10px">
36
+ <EbizTdesignButton theme="info" variant="outline" @click="handleSearch">
37
+ <t-icon name="refresh" />
38
+ </EbizTdesignButton>
39
+ <EbizTableSort></EbizTableSort>
40
+ </t-space>
41
+ </div>
42
+ </div>
43
+ <div style="margin-top: 8px;">
44
+ <slot :data="data"></slot>
45
+ </div>
46
+ </div>
47
+
48
+ <div v-if="pageEnable" style="margin-top: 16px;">
49
+ <!-- 分页区域 -->
50
+ <t-pagination v-model:current="currentPage" v-model:page-size="pageSize" :total="total"
51
+ :page-size-options="pageSizeOptions" @change="onPageChange" />
52
+ </div>
53
+ </EbizTdesignCard>
54
+ </div>
55
+ </template>
56
+
57
+ <script>
58
+ /**
59
+ * @displayName 数据组件
60
+ * @description 数据组件,用于获取和展示数据列表
61
+ * @category 数据展示
62
+ * @name EbizSData
63
+ */
64
+ export default {
65
+ name: "EbizSData"
66
+ }
67
+ </script>
68
+
69
+ <script setup>
70
+ import { defineProps, defineEmits, ref, onMounted, computed } from 'vue';
71
+ import { dataService, EbizSFormItem, EbizTableSort, EbizTdesignButton, EbizTdesignCard, EbizPageHeader } from "../../../index"
72
+ import { Button as TButton, Space as TSpace } from 'tdesign-vue-next';
73
+
74
+ const currentPage = ref(1)
75
+ const pageSize = ref(10)
76
+ const hasMore = ref(true)
77
+ const refreshing = ref(false)
78
+ const total = ref(0)
79
+ const pageSizeOptions = ref([10, 20, 50, 100])
80
+
81
+ const data = ref([]);
82
+
83
+ const props = defineProps({
84
+ showPageHeader: {
85
+ type: Boolean,
86
+ default: true
87
+ },
88
+ defaultSearch: {
89
+ type: Boolean,
90
+ default: false
91
+ },
92
+ showDivider: {
93
+ type: Boolean,
94
+ default: false
95
+ },
96
+ pageEnable: {
97
+ type: Boolean,
98
+ default: true
99
+ },
100
+ defaultParams: {},
101
+ // 接口配置
102
+ apiConfig: {
103
+ type: Object,
104
+ default: () => ({})
105
+ },
106
+ formData: {
107
+ type: Object,
108
+ default: () => ({})
109
+ },
110
+ labelKey: {
111
+ type: String,
112
+ default: 'name'
113
+ },
114
+ // 分页大小
115
+ size: {
116
+ type: Number,
117
+ default: 10
118
+ },
119
+ /**
120
+ * 头部内容
121
+ */
122
+ header: {
123
+ type: String,
124
+ default: undefined
125
+ },
126
+ /**
127
+ * 底部内容
128
+ */
129
+ footer: {
130
+ type: String,
131
+ default: undefined
132
+ },
133
+ /**
134
+ * 是否展示分割线
135
+ */
136
+ split: {
137
+ type: Boolean,
138
+ default: true
139
+ },
140
+ /**
141
+ * 是否展示斑马纹
142
+ */
143
+ stripe: {
144
+ type: Boolean,
145
+ default: false
146
+ },
147
+ /**
148
+ * 是否开启虚拟滚动
149
+ */
150
+ async: {
151
+ type: Boolean,
152
+ default: false
153
+ },
154
+ fetchUrl: {
155
+ type: String,
156
+ default: undefined
157
+ }
158
+ });
159
+
160
+ const defaultFormData = ref({ ...props.formData })
161
+ const searchValue = ref('')
162
+
163
+ const loadData = async () => {
164
+
165
+ let params = {
166
+ keyword: searchValue.value
167
+ }
168
+ if (props.pageEnable) {
169
+ params = {
170
+ page: currentPage.value,
171
+ pagesize: pageSize.value,
172
+ }
173
+ }
174
+ const res = await dataService.fetch({ ...params, ...props.defaultParams, ...props.formData }, props.apiConfig, props.fetchUrl)
175
+
176
+ data.value = res.data || []
177
+ total.value = res.total || 0
178
+
179
+ // 判断是否还有更多数据
180
+ hasMore.value = (res.data || []).length === pageSize.value
181
+ refreshing.value = false
182
+ }
183
+
184
+ const emit = defineEmits(['scroll', 'load-more', 'update:formData']);
185
+
186
+ // 组件挂载时加载数据
187
+ onMounted(() => {
188
+ currentPage.value = 1
189
+ pageSize.value = props.size
190
+ loadData()
191
+ })
192
+
193
+ // 搜索
194
+ const handleSearch = (context) => {
195
+ const result = emit('search', context)
196
+ if (result === false) {
197
+ return
198
+ }
199
+ currentPage.value = 1
200
+ loadData()
201
+ }
202
+
203
+ // 重置
204
+ const handleReset = (context) => {
205
+ const result = emit('reset', context)
206
+ if (result === false) {
207
+ return
208
+ }
209
+ emit('update:formData', { ...defaultFormData.value })
210
+ currentPage.value = 1
211
+ loadData()
212
+ }
213
+
214
+
215
+ const onPageChange = () => {
216
+ loadData()
217
+ }
218
+
219
+ const formDataValue = computed({
220
+ get: () => props.formData,
221
+ set: (value) => {
222
+ emit('update:formData', value)
223
+ }
224
+ })
225
+
226
+ </script>
227
+
228
+ <style></style>
@@ -0,0 +1,385 @@
1
+ <template>
2
+ <t-form ref="formRef" :class="['ebiz-s-form', className]" :colon="colon" :data="data" :disabled="disabled"
3
+ :label-align="labelAlign" :label-width="labelWidth" :layout="layout" :reset-type="resetType"
4
+ :reset-on-semi-controlled="resetOnSemiControlled" :rules="rules" :scroll-to-first-error="scrollToFirstError"
5
+ :show-error-message="showErrorMessage" :status-icon="statusIcon" :style="customStyle" @reset="handleReset"
6
+ @submit="handleSubmit" @validate="handleValidate">
7
+
8
+ <slot></slot>
9
+
10
+ <ebiz-s-form-item label=" ">
11
+ <slot name="buttons">
12
+ <t-space>
13
+ <t-button v-if="showCancelButton" :theme="cancelButtonTheme" type="reset">{{
14
+ cancelButtonText
15
+ }}</t-button>
16
+ <t-button v-if="showSubmitButton" theme="primary" :loading="loading" type="submit">{{
17
+ submitButtonText }}</t-button>
18
+ </t-space>
19
+ </slot>
20
+ </ebiz-s-form-item>
21
+ </t-form>
22
+ </template>
23
+
24
+ <script>
25
+ /**
26
+ * @displayName PC端表单
27
+ * @description PC端表单组件,基于TDesign Form封装,支持表单验证、API提交等功能
28
+ * @category 表单组件
29
+ * @name EbizSForm
30
+ */
31
+ export default {
32
+ name: "EbizSForm"
33
+ }
34
+ </script>
35
+
36
+ <script setup>
37
+ import { defineProps, defineEmits, ref, reactive, computed } from 'vue';
38
+ import { Form as TForm, Button as TButton, Space as TSpace, MessagePlugin } from 'tdesign-vue-next';
39
+ import dataService from "../../../apiService/simpleDataService";
40
+ import EbizSFormItem from './item.vue';
41
+
42
+ const props = defineProps({
43
+ /**
44
+ * 表单数据对象
45
+ */
46
+ data: {
47
+ type: Object,
48
+ default: () => ({})
49
+ },
50
+ /**
51
+ * 表单域标签的位置
52
+ * @options left|right|top
53
+ */
54
+ labelAlign: {
55
+ type: String,
56
+ default: 'right',
57
+ validator: (val) => ['left', 'right', 'top'].includes(val)
58
+ },
59
+ /**
60
+ * 表单布局
61
+ * @options vertical|inline
62
+ */
63
+ layout: {
64
+ type: String,
65
+ default: 'vertical',
66
+ validator: (val) => ['vertical', 'inline'].includes(val)
67
+ },
68
+ /**
69
+ * 是否显示必填标记
70
+ */
71
+ colon: {
72
+ type: Boolean,
73
+ default: false
74
+ },
75
+ /**
76
+ * 是否禁用整个表单
77
+ */
78
+ disabled: {
79
+ type: Boolean,
80
+ default: false
81
+ },
82
+ /**
83
+ * 表单域标签的宽度
84
+ */
85
+ labelWidth: {
86
+ type: [String, Number],
87
+ default: '100px'
88
+ },
89
+ /**
90
+ * 表单校验规则
91
+ */
92
+ rules: {
93
+ type: Object,
94
+ default: () => ({})
95
+ },
96
+ /**
97
+ * 是否显示校验错误信息
98
+ */
99
+ showErrorMessage: {
100
+ type: Boolean,
101
+ default: true
102
+ },
103
+ /**
104
+ * 是否显示校验图标
105
+ */
106
+ statusIcon: {
107
+ type: Boolean,
108
+ default: false
109
+ },
110
+ /**
111
+ * 表单重置时的方式
112
+ * @options empty|initial
113
+ */
114
+ resetType: {
115
+ type: String,
116
+ default: 'empty',
117
+ validator: (val) => ['empty', 'initial'].includes(val)
118
+ },
119
+ /**
120
+ * 重置时是否清空所有非受控字段
121
+ */
122
+ resetOnSemiControlled: {
123
+ type: Boolean,
124
+ default: false
125
+ },
126
+ /**
127
+ * 表单校验不通过时,是否自动定位到第一个错误字段
128
+ */
129
+ scrollToFirstError: {
130
+ type: Boolean,
131
+ default: true
132
+ },
133
+ /**
134
+ * 自定义样式
135
+ */
136
+ customStyle: {
137
+ type: [String, Object],
138
+ default: ''
139
+ },
140
+ /**
141
+ * 自定义类名
142
+ */
143
+ className: {
144
+ type: String,
145
+ default: ''
146
+ },
147
+ /**
148
+ * 是否显示表单按钮(提交和取消)
149
+ */
150
+ showButtons: {
151
+ type: Boolean,
152
+ default: true
153
+ },
154
+ /**
155
+ * 是否显示提交按钮
156
+ */
157
+ showSubmitButton: {
158
+ type: Boolean,
159
+ default: true
160
+ },
161
+ /**
162
+ * 提交按钮文字
163
+ */
164
+ submitButtonText: {
165
+ type: String,
166
+ default: '提交'
167
+ },
168
+ /**
169
+ * 是否显示取消按钮
170
+ */
171
+ showCancelButton: {
172
+ type: Boolean,
173
+ default: true
174
+ },
175
+ /**
176
+ * 取消按钮文字
177
+ */
178
+ cancelButtonText: {
179
+ type: String,
180
+ default: '取消'
181
+ },
182
+ /**
183
+ * 取消按钮主题
184
+ */
185
+ cancelButtonTheme: {
186
+ type: String,
187
+ default: 'default'
188
+ },
189
+ /**
190
+ * API配置,用于表单提交
191
+ */
192
+ apiConfig: {
193
+ type: Object,
194
+ default: () => null
195
+ },
196
+ /**
197
+ * 是否在提交成功后重置表单
198
+ */
199
+ resetOnSuccess: {
200
+ type: Boolean,
201
+ default: false
202
+ },
203
+ /**
204
+ * 提交成功后的消息提示
205
+ */
206
+ successMessage: {
207
+ type: String,
208
+ default: '提交成功'
209
+ },
210
+ /**
211
+ * 提交失败后的消息提示
212
+ */
213
+ errorMessage: {
214
+ type: String,
215
+ default: '提交失败'
216
+ },
217
+ /**
218
+ * 是否显示提交结果消息
219
+ */
220
+ showResultMessage: {
221
+ type: Boolean,
222
+ default: true
223
+ }
224
+ });
225
+
226
+ const emit = defineEmits([
227
+ 'reset',
228
+ 'submit',
229
+ 'validate',
230
+ 'success',
231
+ 'error',
232
+ 'cancel'
233
+ ]);
234
+
235
+ const formRef = ref(null);
236
+ const loading = ref(false);
237
+
238
+ /**
239
+ * 提交表单
240
+ */
241
+ const handleFormSubmit = async () => {
242
+ if (!formRef.value) return;
243
+
244
+ const validateResult = await formRef.value.validate();
245
+ if (validateResult !== true) {
246
+ return;
247
+ }
248
+
249
+ emit('submit', props.data);
250
+
251
+ if (props.apiConfig) {
252
+ await submitFormData();
253
+ }
254
+ };
255
+
256
+ /**
257
+ * 调用API提交表单数据
258
+ */
259
+ const submitFormData = async () => {
260
+ try {
261
+ loading.value = true;
262
+
263
+ const { url, method, headers, params } = props.apiConfig;
264
+ const response = await dataService.request({
265
+ url,
266
+ method: method || 'post',
267
+ data: props.data,
268
+ params,
269
+ headers
270
+ });
271
+
272
+ if (props.showResultMessage) {
273
+ MessagePlugin.success(props.successMessage);
274
+ }
275
+
276
+ emit('success', response);
277
+
278
+ if (props.resetOnSuccess && formRef.value) {
279
+ formRef.value.reset();
280
+ }
281
+
282
+ return response;
283
+ } catch (error) {
284
+ if (props.showResultMessage) {
285
+ MessagePlugin.error(props.errorMessage || error.message || '提交失败');
286
+ }
287
+
288
+ emit('error', error);
289
+ return null;
290
+ } finally {
291
+ loading.value = false;
292
+ }
293
+ };
294
+
295
+ /**
296
+ * 表单重置
297
+ * @param {Object} context 表单上下文
298
+ */
299
+ const handleReset = (context) => {
300
+ console.log('handleReset', context);
301
+ emit('reset', context);
302
+ };
303
+
304
+ /**
305
+ * 表单验证
306
+ * @param {Object} result 表单验证结果
307
+ */
308
+ const handleValidate = (result) => {
309
+ emit('validate', result);
310
+ };
311
+
312
+
313
+ /**
314
+ * 表单提交事件,由内置的HTML表单submit事件触发
315
+ */
316
+ const handleSubmit = (context) => {
317
+ console.log('handleSubmit', context);
318
+
319
+ // 这里只做转发,实际提交逻辑在handleFormSubmit中
320
+ emit('submit', context);
321
+ };
322
+
323
+ // 暴露方法给父组件
324
+ defineExpose({
325
+ /**
326
+ * 提交表单方法
327
+ */
328
+ submit: handleFormSubmit,
329
+ /**
330
+ * 重置表单方法
331
+ */
332
+ reset: () => {
333
+ if (formRef.value) {
334
+ formRef.value.reset();
335
+ }
336
+ },
337
+ /**
338
+ * 校验表单方法
339
+ * @returns {Promise} 校验结果Promise
340
+ */
341
+ validate: async () => {
342
+ if (formRef.value) {
343
+ return await formRef.value.validate();
344
+ }
345
+ return false;
346
+ },
347
+ /**
348
+ * 校验特定字段
349
+ * @param {Array|String} fields 字段名称或字段名称数组
350
+ * @returns {Promise} 校验结果Promise
351
+ */
352
+ validateFields: async (fields) => {
353
+ if (formRef.value) {
354
+ return await formRef.value.validateFields(fields);
355
+ }
356
+ return false;
357
+ },
358
+ /**
359
+ * 清除校验结果
360
+ * @param {Array|String} fields 字段名称或字段名称数组,不传则清除所有
361
+ */
362
+ clearValidate: (fields) => {
363
+ if (formRef.value) {
364
+ formRef.value.clearValidate(fields);
365
+ }
366
+ },
367
+ /**
368
+ * 获取表单DOM元素
369
+ */
370
+ getFormElement: () => formRef.value
371
+ });
372
+ </script>
373
+
374
+ <style scoped>
375
+ .ebiz-s-form {
376
+ width: 100%;
377
+ }
378
+
379
+ .form-buttons {
380
+ margin-top: 24px;
381
+ padding: 8px 0;
382
+ display: flex;
383
+ justify-content: center;
384
+ }
385
+ </style>