@ebiz/designer-components 0.1.38 → 0.1.39

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.1.38",
3
+ "version": "0.1.39",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -121,6 +121,103 @@
121
121
  @change="handleChange" />
122
122
  </template>
123
123
 
124
+ <!-- 子表格 -->
125
+ <template v-else-if="type === 'table'">
126
+ <div class="ebiz-form-table">
127
+ <t-space direction="vertical" style="width: 100%;">
128
+ <!-- 添加按钮 -->
129
+ <div>
130
+ <t-button theme="primary" variant="outline" @click="addTableRow" :disabled="disabled">
131
+ <template #icon>
132
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
133
+ <path d="M8 1V15M1 8H15" stroke="currentColor" stroke-width="1.5"
134
+ stroke-linecap="round" />
135
+ </svg>
136
+ </template>
137
+ {{ addButtonText }}
138
+ </t-button>
139
+ </div>
140
+
141
+ <!-- 表格 -->
142
+ <t-table :data="computedModelValue" :columns="computedTableColumns" :pagination="false" row-key="__id"
143
+ size="small" bordered :empty="'暂无数据'" >
144
+ <!-- 序号列 -->
145
+ <template #index="{ rowIndex }">
146
+ {{ rowIndex + 1 }}
147
+ </template>
148
+
149
+ <!-- 动态渲染每个数据列 -->
150
+ <template v-for="col in tableColumns" :key="col.key" #[col.key]="{ row, rowIndex }">
151
+ <!-- 输入框 -->
152
+ <t-input v-if="!col.type || col.type === 'input'" v-model="row[col.key]"
153
+ :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
154
+ :clearable="col.clearable !== false" :maxlength="col.maxlength"
155
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
156
+
157
+ <!-- 数字输入框 -->
158
+ <t-input-number v-else-if="col.type === 'number'" v-model="row[col.key]"
159
+ :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
160
+ :min="col.min" :max="col.max" :step="col.step || 1"
161
+ :decimal-places="col.decimalPlaces"
162
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
163
+
164
+ <!-- 选择器 -->
165
+ <t-select v-else-if="col.type === 'select'" v-model="row[col.key]"
166
+ :disabled="disabled" :placeholder="col.placeholder || `请选择${col.name}`"
167
+ :options="col.options || []" :clearable="col.clearable !== false"
168
+ :filterable="col.filterable" :multiple="col.multiple"
169
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
170
+
171
+ <!-- 日期选择器 -->
172
+ <t-date-picker v-else-if="col.type === 'date'" v-model="row[col.key]"
173
+ :disabled="disabled" :placeholder="col.placeholder || `请选择${col.name}`"
174
+ :format="col.format || 'YYYY-MM-DD'" :clearable="col.clearable !== false"
175
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
176
+
177
+ <!-- 日期时间选择器 -->
178
+ <t-date-picker v-else-if="col.type === 'datetime'" v-model="row[col.key]"
179
+ :disabled="disabled" :placeholder="col.placeholder || `请选择${col.name}`"
180
+ :format="col.format || 'YYYY-MM-DD HH:mm:ss'" enable-time-picker
181
+ :clearable="col.clearable !== false"
182
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
183
+
184
+ <!-- 开关 -->
185
+ <t-switch v-else-if="col.type === 'switch'" v-model="row[col.key]"
186
+ :disabled="disabled"
187
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
188
+
189
+ <!-- 文本域 -->
190
+ <t-textarea v-else-if="col.type === 'textarea'" v-model="row[col.key]"
191
+ :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
192
+ :autosize="col.autosize || { minRows: 2, maxRows: 4 }" :maxlength="col.maxlength"
193
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
194
+
195
+ <!-- 单选框组 -->
196
+ <t-radio-group v-else-if="col.type === 'radio'" v-model="row[col.key]"
197
+ :disabled="disabled" :options="col.options || []"
198
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
199
+
200
+ <!-- 复选框组 -->
201
+ <t-checkbox-group v-else-if="col.type === 'checkbox'" v-model="row[col.key]"
202
+ :disabled="disabled" :options="col.options || []" :max="col.max"
203
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
204
+
205
+ <!-- 默认文本显示 -->
206
+ <span v-else>{{ row[col.key] || '' }}</span>
207
+ </template>
208
+
209
+ <!-- 操作列 -->
210
+ <template v-if="showTableActions" #actions="{ rowIndex }">
211
+ <t-button theme="danger" variant="text" size="small" :disabled="disabled"
212
+ @click="removeTableRow(rowIndex)">
213
+ 删除
214
+ </t-button>
215
+ </template>
216
+ </t-table>
217
+ </t-space>
218
+ </div>
219
+ </template>
220
+
124
221
  <!-- 默认插槽,用于自定义控件 -->
125
222
  <template v-else>
126
223
  </template>
@@ -132,7 +229,7 @@
132
229
  <script>
133
230
  /**
134
231
  * @displayName PC端表单项
135
- * @description PC端表单项组件,基于TDesign FormItem封装,支持多种控件类型
232
+ * @description PC端表单项组件,基于TDesign FormItem封装,支持多种控件类型,包括子表格功能
136
233
  * @category 表单组件
137
234
  * @name EbizSFormItem
138
235
  */
@@ -157,7 +254,10 @@ import {
157
254
  Slider as TSlider,
158
255
  Cascader as TCascader,
159
256
  TreeSelect as TTreeSelect,
160
- ColorPicker as TColorPicker
257
+ ColorPicker as TColorPicker,
258
+ Table as TTable,
259
+ Button as TButton,
260
+ Space as TSpace
161
261
  } from 'tdesign-vue-next';
162
262
  import EbizRemoteSelect from '../../EbizRemoteSelect.vue';
163
263
  import EbizEmployeeSelector from '../../EbizEmployeeSelector.vue';
@@ -167,7 +267,7 @@ import { EbizUpload } from '../../../index.js'
167
267
  const props = defineProps({
168
268
  /**
169
269
  * 组件类型
170
- * @options input|number|textarea|select|date|datetime|time|radio|checkbox|switch|slider|upload|cascader|tree-select|color-picker|employee|dept
270
+ * @options input|number|textarea|select|date|datetime|time|radio|checkbox|switch|slider|upload|cascader|tree-select|color-picker|employee|dept|table
171
271
  */
172
272
  type: {
173
273
  type: String,
@@ -175,7 +275,7 @@ const props = defineProps({
175
275
  validator: (val) => [
176
276
  'input', 'number', 'textarea', 'select', 'date', 'datetime', 'time', 'radio',
177
277
  'checkbox', 'switch', 'slider', 'upload', 'cascader', 'tree-select',
178
- 'color-picker', 'employee', 'dept'
278
+ 'color-picker', 'employee', 'dept', 'table'
179
279
  ].includes(val)
180
280
  },
181
281
  /**
@@ -523,6 +623,32 @@ const props = defineProps({
523
623
  companyId: {
524
624
  type: [String, Number],
525
625
  default: undefined
626
+ },
627
+ /**
628
+ * 子表格列配置,用于table类型
629
+ * 示例:[
630
+ * {name:'名称', key:'name', type:'input', width:150},
631
+ * {name:'类型', key:'type', type:'select', options:[{label:'选项1', value:'1'}]},
632
+ * {name:'数量', key:'num', type:'number', min:0, max:999}
633
+ * ]
634
+ */
635
+ tableColumns: {
636
+ type: Array,
637
+ default: () => []
638
+ },
639
+ /**
640
+ * 子表格是否显示操作列
641
+ */
642
+ showTableActions: {
643
+ type: Boolean,
644
+ default: true
645
+ },
646
+ /**
647
+ * 子表格新增按钮文本
648
+ */
649
+ addButtonText: {
650
+ type: String,
651
+ default: '添加数据'
526
652
  }
527
653
  });
528
654
 
@@ -536,10 +662,48 @@ const emit = defineEmits([
536
662
  const computedModelValue = computed({
537
663
  get: () => props.modelValue,
538
664
  set: (val) => {
665
+ console.log("val",val)
539
666
  emit('update:modelValue', val);
540
667
  }
541
668
  });
542
669
 
670
+ // 表格列配置计算属性
671
+ const computedTableColumns = computed(() => {
672
+ if (props.type !== 'table' || !props.tableColumns?.length) return [];
673
+
674
+ const columns = [];
675
+
676
+ // 添加序号列
677
+ columns.push({
678
+ colKey: 'index',
679
+ title: '序号',
680
+ width: 80,
681
+ align: 'center'
682
+ });
683
+
684
+ // 添加用户配置的列
685
+ const userColumns = props.tableColumns.map(col => ({
686
+ colKey: col.key,
687
+ title: col.name,
688
+ width: col.width || undefined,
689
+ align: col.align || 'left'
690
+ }));
691
+
692
+ columns.push(...userColumns);
693
+
694
+ // 添加操作列
695
+ if (props.showTableActions) {
696
+ columns.push({
697
+ colKey: 'actions',
698
+ title: '操作',
699
+ width: 80,
700
+ align: 'center'
701
+ });
702
+ }
703
+
704
+ return columns;
705
+ });
706
+
543
707
  /**
544
708
  * 值变更事件
545
709
  * @param {*} val 新值
@@ -555,10 +719,163 @@ const handleChange = (val) => {
555
719
  const handleValidate = (validateResult) => {
556
720
  emit('validate', validateResult);
557
721
  };
722
+
723
+ /**
724
+ * 添加表格行
725
+ */
726
+ const addTableRow = () => {
727
+
728
+ const currentData = computedModelValue.value || [];
729
+ const newRow = {};
730
+
731
+ // 根据表格列配置初始化新行数据
732
+ props.tableColumns.forEach(col => {
733
+ // 根据列类型设置默认值
734
+ switch (col.type) {
735
+ case 'number':
736
+ newRow[col.key] = col.defaultValue || 0;
737
+ break;
738
+ case 'switch':
739
+ newRow[col.key] = col.defaultValue || false;
740
+ break;
741
+ case 'checkbox':
742
+ newRow[col.key] = col.defaultValue || [];
743
+ break;
744
+ case 'select':
745
+ if (col.multiple) {
746
+ newRow[col.key] = col.defaultValue || [];
747
+ } else {
748
+ newRow[col.key] = col.defaultValue || '';
749
+ }
750
+ break;
751
+ default:
752
+ newRow[col.key] = col.defaultValue || '';
753
+ break;
754
+ }
755
+ });
756
+
757
+
758
+ const newData = [...currentData, newRow];
759
+ computedModelValue.value = JSON.parse(JSON.stringify(newData));
760
+
761
+ handleChange(newData.map(({ __id, ...item }) => item));
762
+ };
763
+
764
+ /**
765
+ * 删除表格行
766
+ * @param {number} rowIndex 行索引
767
+ */
768
+ const removeTableRow = (rowIndex) => {
769
+ if (props.type !== 'table') return;
770
+
771
+ const currentData = [...(computedModelValue.value || [])];
772
+ currentData.splice(rowIndex, 1);
773
+
774
+ computedModelValue.value = currentData;
775
+ handleChange(currentData.map(({ __id, ...item }) => item));
776
+ };
777
+
778
+ /**
779
+ * 更新表格单元格数据
780
+ * @param {number} rowIndex 行索引
781
+ * @param {string} colKey 列键名
782
+ * @param {*} value 新值
783
+ */
784
+ const updateTableCell = (rowIndex, colKey, value) => {
785
+ if (props.type !== 'table') return;
786
+
787
+ const currentData = [...(computedModelValue.value || [])];
788
+ if (currentData[rowIndex]) {
789
+ currentData[rowIndex] = {
790
+ ...currentData[rowIndex],
791
+ [colKey]: value
792
+ };
793
+
794
+ computedModelValue.value = currentData;
795
+ handleChange(currentData.map(({ __id, ...item }) => item));
796
+ }
797
+ };
558
798
  </script>
559
799
 
560
800
  <style scoped>
561
801
  .custom-control {
562
802
  width: 100%;
563
803
  }
804
+
805
+ .ebiz-form-table {
806
+ width: 100%;
807
+ }
808
+
809
+ .ebiz-form-table :deep(.t-table) {
810
+ border: 1px solid var(--td-border-level-1-color);
811
+ border-radius: var(--td-radius-default);
812
+ }
813
+
814
+ .ebiz-form-table :deep(.t-table td) {
815
+ padding: 8px 12px;
816
+ }
817
+
818
+ .ebiz-form-table :deep(.t-table th) {
819
+ background-color: var(--td-bg-color-secondarycontainer);
820
+ font-weight: 500;
821
+ }
822
+
823
+ .ebiz-form-table :deep(.t-input) {
824
+ border: none;
825
+ box-shadow: none;
826
+ }
827
+
828
+ .ebiz-form-table :deep(.t-input:focus-within) {
829
+ border: 1px solid var(--td-brand-color);
830
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
831
+ }
832
+
833
+ .ebiz-form-table :deep(.t-input-number) {
834
+ border: none;
835
+ box-shadow: none;
836
+ }
837
+
838
+ .ebiz-form-table :deep(.t-input-number:focus-within) {
839
+ border: 1px solid var(--td-brand-color);
840
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
841
+ }
842
+
843
+ .ebiz-form-table :deep(.t-select) {
844
+ border: none;
845
+ box-shadow: none;
846
+ }
847
+
848
+ .ebiz-form-table :deep(.t-select:focus-within) {
849
+ border: 1px solid var(--td-brand-color);
850
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
851
+ }
852
+
853
+ .ebiz-form-table :deep(.t-date-picker) {
854
+ border: none;
855
+ box-shadow: none;
856
+ }
857
+
858
+ .ebiz-form-table :deep(.t-date-picker:focus-within) {
859
+ border: 1px solid var(--td-brand-color);
860
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
861
+ }
862
+
863
+ .ebiz-form-table :deep(.t-textarea) {
864
+ border: none;
865
+ box-shadow: none;
866
+ }
867
+
868
+ .ebiz-form-table :deep(.t-textarea:focus-within) {
869
+ border: 1px solid var(--td-brand-color);
870
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
871
+ }
872
+
873
+ .ebiz-form-table :deep(.t-switch) {
874
+ justify-content: center;
875
+ }
876
+
877
+ .ebiz-form-table :deep(.t-radio-group),
878
+ .ebiz-form-table :deep(.t-checkbox-group) {
879
+ justify-content: center;
880
+ }
564
881
  </style>
@@ -17,7 +17,11 @@
17
17
  <ebiz-s-form-item label="爱好" name="hobbies" type="checkbox" :options="hobbyOptions"></ebiz-s-form-item>
18
18
  <ebiz-s-form-item label="职业" name="occupation" type="select" :options="occupationOptions"></ebiz-s-form-item>
19
19
  <ebiz-s-form-item label="出生日期" name="birthday" type="date"></ebiz-s-form-item>
20
- <ebiz-s-form-item label="简介" name="bio" type="textarea" placeholder="请输入个人简介"></ebiz-s-form-item>
20
+ {{ formData.bio }}
21
+ <ebiz-s-form-item v-model="formData.bio" label="简介" name="bio" type="table" :tableColumns="[
22
+ { name: '名称', key: 'name' },
23
+ { name: '数量', key: 'num' }
24
+ ]" placeholder="请输入个人简介"></ebiz-s-form-item>
21
25
  </ebiz-s-form>
22
26
  </div>
23
27
  </div>
@@ -127,6 +131,8 @@ export default {
127
131
  const editFormRef = ref(null);
128
132
  const methodFormRef = ref(null);
129
133
 
134
+ const tableColumns = reactive();
135
+
130
136
  // 基础表单数据
131
137
  const formData = reactive({
132
138
  name: '',
@@ -135,7 +141,7 @@ export default {
135
141
  hobbies: [],
136
142
  occupation: '',
137
143
  birthday: '',
138
- bio: ''
144
+ bio: [{name: '123', num: 123}]
139
145
  });
140
146
 
141
147
  // 表单验证规则
@@ -0,0 +1 @@
1
+