@ebiz/designer-components 0.0.18-beta.24 → 0.0.18-beta.26

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,117 @@
1
+ <script setup>
2
+ import { computed, inject, onMounted, getCurrentInstance } from 'vue';
3
+
4
+ const props = defineProps({
5
+ // 列唯一标识
6
+ colKey: {
7
+ type: String,
8
+ required: true
9
+ },
10
+ // 列标题
11
+ title: {
12
+ type: String,
13
+ default: ''
14
+ },
15
+ // 列宽度
16
+ width: {
17
+ type: [String, Number],
18
+ default: ''
19
+ },
20
+ // 最小列宽
21
+ minWidth: {
22
+ type: [String, Number],
23
+ default: ''
24
+ },
25
+ // 水平对齐方式
26
+ align: {
27
+ type: String,
28
+ default: 'left',
29
+ validator: (val) => ['left', 'center', 'right'].includes(val)
30
+ },
31
+ // 固定列位置
32
+ fixed: {
33
+ type: String,
34
+ default: '',
35
+ validator: (val) => ['', 'left', 'right'].includes(val)
36
+ },
37
+ // 列类型
38
+ type: {
39
+ type: String,
40
+ default: '',
41
+ validator: (val) => ['', 'multiple', 'single', 'index', 'expand'].includes(val)
42
+ },
43
+ // 超出省略
44
+ ellipsis: {
45
+ type: Boolean,
46
+ default: false
47
+ },
48
+ // 标题超出省略
49
+ ellipsisTitle: {
50
+ type: Boolean,
51
+ default: false
52
+ },
53
+ // 是否支持排序
54
+ sorter: {
55
+ type: Boolean,
56
+ default: false
57
+ },
58
+ // 过滤配置
59
+ filter: {
60
+ type: Object,
61
+ default: null
62
+ },
63
+ // 自定义类名
64
+ className: {
65
+ type: String,
66
+ default: ''
67
+ },
68
+ // 是否禁用
69
+ disabled: {
70
+ type: Boolean,
71
+ default: false
72
+ }
73
+ });
74
+
75
+ // 获取父级表格组件实例
76
+ const tableCtx = inject('tableCtx', null);
77
+ const instance = getCurrentInstance();
78
+
79
+ // 格式化列配置对象
80
+ const columnConfig = computed(() => {
81
+ return {
82
+ colKey: props.colKey,
83
+ title: props.title,
84
+ width: props.width,
85
+ minWidth: props.minWidth,
86
+ align: props.align,
87
+ fixed: props.fixed || '',
88
+ type: props.type || '',
89
+ ellipsis: props.ellipsis,
90
+ ellipsisTitle: props.ellipsisTitle,
91
+ sorter: props.sorter,
92
+ filter: props.filter,
93
+ className: props.className,
94
+ disabled: props.disabled,
95
+ cell: instance.slots.default ? props.colKey : undefined,
96
+ title: instance.slots.title ? `title-${props.colKey}` : undefined
97
+ };
98
+ });
99
+
100
+ // 在组件挂载时将列配置注册到表格组件
101
+ onMounted(() => {
102
+ if (tableCtx && typeof tableCtx.registerColumn === 'function') {
103
+ tableCtx.registerColumn(columnConfig.value);
104
+ } else {
105
+ console.warn('EbizTableColumn必须在EbizTable内部使用');
106
+ }
107
+ });
108
+ </script>
109
+
110
+ <template>
111
+ <template v-if="$slots.default">
112
+ <slot></slot>
113
+ </template>
114
+ <template v-if="$slots.title">
115
+ <slot name="title"></slot>
116
+ </template>
117
+ </template>
@@ -0,0 +1,181 @@
1
+ <template>
2
+ <div class="ebiz-table-sort">
3
+ <t-popup :visible="sortVisible" trigger="click" placement="bottom-right" :overlay-style="{ width: '450px' }"
4
+ @visible-change="handleVisibleChange" :destroy-on-close="false">
5
+ <template #content>
6
+ <t-card :title="popupTitle" size="small" :bordered="false">
7
+ <div class="sort-content">
8
+ <div class="sort-item" v-for="(item, index) in sortItems" :key="index">
9
+ <div class="sort-item-content" style="display: flex; margin-bottom: 12px;">
10
+ <t-select v-model="item.type" :placeholder="fieldPlaceholder" :options="fieldOptions" :clearable="false"
11
+ :style="{ width: '170px' }" @change="handleSortChange" />
12
+ <t-select v-model="item.sort" :placeholder="orderPlaceholder" :options="orderOptions" :clearable="false"
13
+ :style="{ width: '170px', marginLeft: '8px' }" @change="handleSortChange" />
14
+ <t-button theme="default" variant="text" shape="circle" @click="removeSortItem(index)"
15
+ v-if="sortItems.length > 1" :style="{ marginLeft: '8px', flexShrink: 0 }">
16
+ <template #icon>
17
+ <t-icon name="close" />
18
+ </template>
19
+ </t-button>
20
+ </div>
21
+ </div>
22
+ <div class="sort-footer">
23
+ <t-button theme="default" variant="outline" @click="addSortItem"
24
+ v-if="sortItems.length < maxSortItems">
25
+ <template #icon>
26
+ <t-icon name="add" />
27
+ </template>
28
+ {{ addConditionText }}
29
+ </t-button>
30
+ </div>
31
+ </div>
32
+ </t-card>
33
+ </template>
34
+ <t-button block variant="outline" :title="title">
35
+ <template #icon>
36
+ <t-icon :name="icon" />
37
+ </template>
38
+ <div v-if="buttonText">
39
+ {{ buttonText }}
40
+ </div>
41
+ </t-button>
42
+ </t-popup>
43
+ </div>
44
+ </template>
45
+
46
+ <script>
47
+ export default {
48
+ name: "EbizTableSort"
49
+ }
50
+ </script>
51
+
52
+ <script setup>
53
+ import { ref, computed, defineProps, defineEmits, watch } from 'vue';
54
+ import { Button as TButton, Popup as TPopup, Card as TCard, Select as TSelect, Icon as TIcon } from 'tdesign-vue-next';
55
+
56
+ const props = defineProps({
57
+ // v-model绑定值
58
+ modelValue: {
59
+ type: Array,
60
+ default: () => []
61
+ },
62
+ // 按钮图标
63
+ icon: {
64
+ type: String,
65
+ default: 'sort'
66
+ },
67
+ // 按钮文本
68
+ buttonText: {
69
+ type: String,
70
+ default: ''
71
+ },
72
+ // 按钮标题
73
+ title: {
74
+ type: String,
75
+ default: '排序'
76
+ },
77
+ // 弹出层标题
78
+ popupTitle: {
79
+ type: String,
80
+ default: '排序'
81
+ },
82
+ // 字段选项
83
+ fieldOptions: {
84
+ type: Array,
85
+ default: () => []
86
+ },
87
+ // 字段占位符
88
+ fieldPlaceholder: {
89
+ type: String,
90
+ default: '选择字段'
91
+ },
92
+ // 排序选项
93
+ orderOptions: {
94
+ type: Array,
95
+ default: () => [
96
+ { value: 'asc', label: '升序 ↑' },
97
+ { value: 'desc', label: '降序 ↓' }
98
+ ]
99
+ },
100
+ // 排序占位符
101
+ orderPlaceholder: {
102
+ type: String,
103
+ default: '选择排序'
104
+ },
105
+ // 最大排序条件数量
106
+ maxSortItems: {
107
+ type: Number,
108
+ default: 5
109
+ },
110
+ // 添加条件文本
111
+ addConditionText: {
112
+ type: String,
113
+ default: '添加排序条件'
114
+ }
115
+ });
116
+
117
+ const emit = defineEmits(['update:modelValue', 'sort', 'visible-change']);
118
+
119
+ // 排序弹出层显示状态
120
+ const sortVisible = ref(false);
121
+
122
+ // 排序条件列表
123
+ const sortItems = ref([]);
124
+
125
+ // 监听modelValue变化
126
+ watch(() => props.modelValue, (newVal) => {
127
+ if (newVal && newVal.length > 0) {
128
+ sortItems.value = JSON.parse(JSON.stringify(newVal));
129
+ } else {
130
+ // 默认添加一个空的排序条件
131
+ sortItems.value = [{ type: '', sort: 'desc' }];
132
+ }
133
+ }, { immediate: true });
134
+
135
+ // 添加排序条件
136
+ const addSortItem = () => {
137
+ if (sortItems.value.length < props.maxSortItems) {
138
+ sortItems.value.push({ type: '', sort: 'desc' });
139
+ }
140
+ };
141
+
142
+ // 移除排序条件
143
+ const removeSortItem = (index) => {
144
+ sortItems.value.splice(index, 1);
145
+ handleSortChange();
146
+ };
147
+
148
+ // 处理排序变更
149
+ const handleSortChange = () => {
150
+ // 过滤有效的排序条件(字段和排序方向都不为空)
151
+ const validSortItems = sortItems.value.filter(item => item.type && item.sort);
152
+ emit('update:modelValue', validSortItems);
153
+ emit('sort', validSortItems);
154
+ };
155
+
156
+ // 弹出层可见性变化
157
+ const handleVisibleChange = (visible) => {
158
+ sortVisible.value = visible;
159
+ emit('visible-change', visible);
160
+ };
161
+ </script>
162
+
163
+ <style lang="less" scoped>
164
+ .ebiz-table-sort {
165
+ display: inline-block;
166
+
167
+ .sort-content {
168
+ min-width: 400px;
169
+ width: 100%;
170
+ }
171
+
172
+ .sort-footer {
173
+ margin-top: 16px;
174
+ }
175
+
176
+ .sort-actions {
177
+ display: flex;
178
+ justify-content: flex-end;
179
+ }
180
+ }
181
+ </style>
@@ -0,0 +1,125 @@
1
+ <template>
2
+ <t-tree
3
+ v-bind="$attrs"
4
+ :data="data"
5
+ :value="modelValue"
6
+ :expanded="expandedModel"
7
+ :actived="activedModel"
8
+ @change="handleChange"
9
+ @expand="handleExpand"
10
+ @active="handleActive"
11
+ @select="$emit('select', $event)"
12
+ @drag-start="$emit('drag-start', $event)"
13
+ @drag-end="$emit('drag-end', $event)"
14
+ @drag-over="$emit('drag-over', $event)"
15
+ @drag-leave="$emit('drag-leave', $event)"
16
+ @drag-drop="$emit('drag-drop', $event)"
17
+ @click="$emit('click', $event)"
18
+ @load="$emit('load', $event)"
19
+ >
20
+ <template v-if="$slots.default" #default="slotProps">
21
+ <slot :node="slotProps.node"></slot>
22
+ </template>
23
+ <template v-if="$slots.empty" #empty>
24
+ <slot name="empty"></slot>
25
+ </template>
26
+ <template v-if="$slots.icon" #icon="slotProps">
27
+ <slot name="icon" :node="slotProps.node"></slot>
28
+ </template>
29
+ <template v-if="$slots.label" #label="slotProps">
30
+ <slot name="label" :node="slotProps.node"></slot>
31
+ </template>
32
+ <template v-if="$slots.line" #line="slotProps">
33
+ <slot name="line" :node="slotProps.node"></slot>
34
+ </template>
35
+ <template v-if="$slots.operations" #operations="slotProps">
36
+ <slot name="operations" :node="slotProps.node"></slot>
37
+ </template>
38
+ </t-tree>
39
+ </template>
40
+
41
+ <script setup>
42
+ import { Tree as TTree } from 'tdesign-vue-next';
43
+ import { computed } from 'vue';
44
+
45
+ // 定义组件属性
46
+ const props = defineProps({
47
+ // 选中值
48
+ modelValue: {
49
+ type: Array,
50
+ default: () => []
51
+ },
52
+ // 展开节点
53
+ expanded: {
54
+ type: Array,
55
+ default: () => []
56
+ },
57
+ // 激活节点
58
+ actived: {
59
+ type: Array,
60
+ default: () => []
61
+ },
62
+ // 数据
63
+ items: {
64
+ type: Array,
65
+ default: () => []
66
+ }
67
+ });
68
+
69
+ // 定义组件事件
70
+ const emit = defineEmits([
71
+ 'update:modelValue',
72
+ 'update:expanded',
73
+ 'update:actived',
74
+ 'change',
75
+ 'expand',
76
+ 'active',
77
+ 'select',
78
+ 'click',
79
+ 'load',
80
+ 'drag-start',
81
+ 'drag-end',
82
+ 'drag-over',
83
+ 'drag-leave',
84
+ 'drag-drop'
85
+ ]);
86
+
87
+ // 处理items映射到data
88
+ const data = computed(() => props.items);
89
+
90
+ // 展开事件
91
+ const expandedModel = computed({
92
+ get: () => props.expanded,
93
+ set: (val) => emit('update:expanded', val)
94
+ });
95
+
96
+ // 激活节点
97
+ const activedModel = computed({
98
+ get: () => props.actived,
99
+ set: (val) => emit('update:actived', val)
100
+ });
101
+
102
+ // 值改变事件
103
+ const handleChange = (val, context) => {
104
+ emit('update:modelValue', val);
105
+ emit('change', val, context);
106
+ };
107
+
108
+ // 节点展开事件
109
+ const handleExpand = (val, context) => {
110
+ emit('update:expanded', val);
111
+ emit('expand', val, context);
112
+ };
113
+
114
+ // 节点激活事件
115
+ const handleActive = (val, context) => {
116
+ emit('update:actived', val);
117
+ emit('active', val, context);
118
+ };
119
+ </script>
120
+
121
+ <style>
122
+ .t-tree {
123
+ width: 100%;
124
+ }
125
+ </style>
package/src/index.js CHANGED
@@ -48,8 +48,10 @@ import EbizEmployeeInfo from "./components/EbizEmployeeInfo.vue";
48
48
  import EbizAlert from "./components/TdesignAlert.vue";
49
49
  import EbizDialog from "./components/TdesignDialog.vue";
50
50
  import EbizTable from "./components/EbizTable.vue";
51
+ import EbizTableColumn from './components/EbizTableColumn.vue';
52
+ import EbizTableSort from './components/EbizTableSort.vue';
53
+ import EbizTree from './components/EbizTree.vue';
51
54
  import { MessagePlugin as EbizMessage } from 'tdesign-vue-next';
52
- import EbizDetailBlock from './components/EbizDetailBlock.vue';
53
55
 
54
56
  // 导入简洁数据服务
55
57
  import dataService from "./apiService/simpleDataService";
@@ -148,6 +150,9 @@ export {
148
150
  EbizDialog,
149
151
  // 表格组件
150
152
  EbizTable,
151
- // 详情块组件
152
- EbizDetailBlock
153
+ EbizTableColumn,
154
+ // 表格排序组件
155
+ EbizTableSort,
156
+ // 树组件
157
+ EbizTree
153
158
  };
@@ -1,10 +1,13 @@
1
1
  import { createRouter, createWebHistory } from 'vue-router'
2
+ import Home from '../views/Home.vue'
3
+ import ButtonView from '../views/Button.vue'
4
+ import TableView from '../views/TableView.vue'
2
5
 
3
6
  const routes = [
4
7
  {
5
8
  path: '/',
6
9
  name: 'Home',
7
- component: () => import('../views/Home.vue'),
10
+ component: Home,
8
11
  meta: { title: '首页' }
9
12
  },
10
13
  {
@@ -15,10 +18,16 @@ const routes = [
15
18
  },
16
19
  {
17
20
  path: '/table',
18
- name: 'Table',
19
- component: () => import('../views/Table.vue'),
21
+ name: 'table',
22
+ component: TableView,
20
23
  meta: { title: '表格组件示例' }
21
24
  },
25
+ {
26
+ path: '/table-column',
27
+ name: 'table-column',
28
+ component: TableView,
29
+ meta: { title: '表格列组件示例' }
30
+ },
22
31
  {
23
32
  path: '/form',
24
33
  name: 'Form',
@@ -27,8 +36,8 @@ const routes = [
27
36
  },
28
37
  {
29
38
  path: '/button',
30
- name: 'Button',
31
- component: () => import('../views/Button.vue'),
39
+ name: 'button',
40
+ component: ButtonView,
32
41
  meta: { title: '按钮组件示例' }
33
42
  },
34
43
  {
@@ -242,10 +251,16 @@ const routes = [
242
251
  meta: { title: 'Ebiz表格组件示例' }
243
252
  },
244
253
  {
245
- path: '/ebiz-detail-block',
246
- name: 'EbizDetailBlock',
247
- component: () => import('../views/EbizDetailBlockDemo.vue'),
248
- meta: { title: 'Ebiz详情块组件示例' }
254
+ path: '/table-sort',
255
+ name: 'TableSort',
256
+ component: () => import('../views/TableSortDemo.vue'),
257
+ meta: { title: 'Ebiz表格排序组件示例' }
258
+ },
259
+ {
260
+ path: '/tree',
261
+ name: 'Tree',
262
+ component: () => import('../views/TreeDemo.vue'),
263
+ meta: { title: 'Ebiz树组件示例' }
249
264
  }
250
265
  ]
251
266
 
@@ -55,7 +55,9 @@ export default {
55
55
  { path: '/tdesign-alert', title: 'TDesign提示组件示例' },
56
56
  { path: '/tdesign-dialog', title: 'TDesign对话框组件示例' },
57
57
  { path: '/table-demo', title: 'Ebiz表格组件示例' },
58
- { path: '/ebiz-detail-block', title: 'Ebiz详情块组件示例' }
58
+ { path: '/table-column', title: 'Ebiz表格列组件示例' },
59
+ { path: '/table-sort', title: 'Ebiz表格排序组件示例' },
60
+ { path: '/tree', title: 'Ebiz树组件示例' }
59
61
  ]
60
62
 
61
63
  return {
@@ -0,0 +1,144 @@
1
+ <template>
2
+ <div class="container">
3
+ <h1>表格排序组件演示</h1>
4
+
5
+ <div class="demo-section">
6
+ <h2>基础用法</h2>
7
+ <div class="demo-item">
8
+ <EbizTableSort icon="filter-sort" :field-options="fieldOptions" @sort="handleSort"
9
+ @reset="handleReset" />
10
+ </div>
11
+ <div class="result" v-if="sortResult.length > 0">
12
+ <h3>排序结果:</h3>
13
+ <pre>{{ JSON.stringify(sortResult, null, 2) }}</pre>
14
+ </div>
15
+ </div>
16
+
17
+ <div class="demo-section">
18
+ <h2>自定义按钮</h2>
19
+ <div class="demo-item">
20
+ <EbizTableSort icon="filter" button-text="自定义排序" :field-options="fieldOptions" @sort="handleSort2" />
21
+ </div>
22
+ <div class="result" v-if="sortResult2.length > 0">
23
+ <h3>排序结果:</h3>
24
+ <pre>{{ JSON.stringify(sortResult2, null, 2) }}</pre>
25
+ </div>
26
+ </div>
27
+
28
+ <div class="demo-section">
29
+ <h2>预设排序条件</h2>
30
+ <div class="demo-item">
31
+ <EbizTableSort :field-options="fieldOptions" :default-sort="defaultSort" @sort="handleSort3" />
32
+ </div>
33
+ <div class="result" v-if="sortResult3.length > 0">
34
+ <h3>排序结果:</h3>
35
+ <pre>{{ JSON.stringify(sortResult3, null, 2) }}</pre>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="demo-section">
40
+ <h2>多条件排序</h2>
41
+ <div class="demo-item">
42
+ <EbizTableSort :field-options="fieldOptions" :max-sort-items="3" @sort="handleSort4" />
43
+ </div>
44
+ <div class="result" v-if="sortResult4.length > 0">
45
+ <h3>排序结果:</h3>
46
+ <pre>{{ JSON.stringify(sortResult4, null, 2) }}</pre>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </template>
51
+
52
+ <script setup>
53
+ import { ref } from 'vue';
54
+ import { EbizTableSort } from '../index.js';
55
+
56
+ // 字段选项
57
+ const fieldOptions = [
58
+ { value: 'name', label: '姓名' },
59
+ { value: 'age', label: '年龄' },
60
+ { value: 'address', label: '地址' },
61
+ { value: 'createTime', label: '创建时间' },
62
+ { value: 'status', label: '状态' }
63
+ ];
64
+
65
+ // 默认排序条件
66
+ const defaultSort = [
67
+ { field: 'age', order: 'desc' }
68
+ ];
69
+
70
+ // 排序结果
71
+ const sortResult = ref([]);
72
+ const sortResult2 = ref([]);
73
+ const sortResult3 = ref([]);
74
+ const sortResult4 = ref([]);
75
+
76
+ // 处理排序
77
+ const handleSort = (sort) => {
78
+ sortResult.value = sort;
79
+ console.log('排序条件:', sort);
80
+ };
81
+
82
+ const handleSort2 = (sort) => {
83
+ sortResult2.value = sort;
84
+ console.log('排序条件2:', sort);
85
+ };
86
+
87
+ const handleSort3 = (sort) => {
88
+ sortResult3.value = sort;
89
+ console.log('排序条件3:', sort);
90
+ };
91
+
92
+ const handleSort4 = (sort) => {
93
+ sortResult4.value = sort;
94
+ console.log('排序条件4:', sort);
95
+ };
96
+
97
+ // 处理重置
98
+ const handleReset = () => {
99
+ sortResult.value = [];
100
+ console.log('重置排序');
101
+ };
102
+ </script>
103
+
104
+ <style lang="less" scoped>
105
+ .container {
106
+ padding: 20px;
107
+
108
+ h1 {
109
+ margin-bottom: 20px;
110
+ }
111
+
112
+ .demo-section {
113
+ margin-bottom: 30px;
114
+ padding: 20px;
115
+ border: 1px solid #eee;
116
+ border-radius: 6px;
117
+
118
+ h2 {
119
+ margin-bottom: 16px;
120
+ font-size: 18px;
121
+ }
122
+
123
+ .demo-item {
124
+ margin-bottom: 16px;
125
+ }
126
+
127
+ .result {
128
+ margin-top: 20px;
129
+ padding: 16px;
130
+ background-color: #f9f9f9;
131
+ border-radius: 4px;
132
+
133
+ h3 {
134
+ margin-bottom: 8px;
135
+ font-size: 16px;
136
+ }
137
+
138
+ pre {
139
+ font-family: monospace;
140
+ }
141
+ }
142
+ }
143
+ }
144
+ </style>