@ebiz/designer-components 0.0.18-beta.5 → 0.0.18-beta.7

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.0.18-beta.5",
3
+ "version": "0.0.18-beta.7",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -4,11 +4,11 @@
4
4
  * 所有接口均使用POST方法提交
5
5
  */
6
6
 
7
- import axios from "axios";
8
- import { TinyNotify } from "@opentiny/vue";
7
+ import axios from 'axios'
8
+ import { TinyNotify } from '@opentiny/vue'
9
9
 
10
10
  // 从环境变量获取API基础URL
11
- const API_BASE_URL = window.location.host + "/api";
11
+ const API_BASE_URL = 'http://' + window.location.host + "/api";
12
12
 
13
13
  /**
14
14
  * 创建axios实例
@@ -17,10 +17,10 @@ const axiosInstance = axios.create({
17
17
  baseURL: API_BASE_URL,
18
18
  timeout: 30000,
19
19
  headers: {
20
- "Content-Type": "application/json",
21
- Accept: "application/json",
22
- },
23
- });
20
+ 'Content-Type': 'application/json',
21
+ Accept: 'application/json'
22
+ }
23
+ })
24
24
 
25
25
  /**
26
26
  * 请求拦截器
@@ -28,117 +28,117 @@ const axiosInstance = axios.create({
28
28
  axiosInstance.interceptors.request.use(
29
29
  (config) => {
30
30
  // 添加认证信息
31
- const token = localStorage.getItem("token");
31
+ const token = localStorage.getItem('token')
32
32
  if (token) {
33
- config.headers["Authorization"] = `Bearer ${token}`;
33
+ config.headers['Authorization'] = `Bearer ${token}`
34
34
  }
35
-
35
+
36
36
  // 如果是FormData格式,不设置Content-Type,让浏览器自动设置
37
37
  if (config.data instanceof FormData) {
38
- config.headers["Content-Type"] = undefined;
38
+ config.headers['Content-Type'] = undefined
39
39
  }
40
-
41
- return config;
40
+
41
+ return config
42
42
  },
43
43
  (error) => {
44
- return Promise.reject(error);
44
+ return Promise.reject(error)
45
45
  }
46
- );
46
+ )
47
47
 
48
48
  /**
49
49
  * 响应拦截器
50
50
  */
51
51
  axiosInstance.interceptors.response.use(
52
52
  (response) => {
53
- const { data } = response;
53
+ const { data } = response
54
54
 
55
55
  // 根据后端API的响应格式进行调整
56
56
  if (data.code === 0) {
57
- return data.data;
57
+ return data.data
58
58
  } else {
59
59
  // message.error(data.message || '请求失败');
60
- return Promise.reject(new Error(data.message || data.msg || "请求失败"));
60
+ return Promise.reject(new Error(data.message || data.msg || '请求失败'))
61
61
  }
62
62
  },
63
63
  (error) => {
64
64
  // 错误处理
65
65
  if (error.response) {
66
- const { status } = error.response;
66
+ const { status } = error.response
67
67
 
68
68
  switch (status) {
69
69
  case 401:
70
70
  TinyNotify({
71
- type: "warning",
72
- title: "未授权",
73
- message: "您无权限访问,请授权后重试",
74
- position: "top-right",
75
- duration: 2000,
76
- });
77
- break;
71
+ type: 'warning',
72
+ title: '未授权',
73
+ message: '您无权限访问,请授权后重试',
74
+ position: 'top-right',
75
+ duration: 2000
76
+ })
77
+ break
78
78
  case 403:
79
79
  TinyNotify({
80
- type: "warning",
81
- title: "权限不足",
82
- message: "禁止访问,权限不足",
83
- position: "top-right",
84
- duration: 2000,
85
- });
86
- break;
80
+ type: 'warning',
81
+ title: '权限不足',
82
+ message: '禁止访问,权限不足',
83
+ position: 'top-right',
84
+ duration: 2000
85
+ })
86
+ break
87
87
  case 404:
88
88
  TinyNotify({
89
- type: "warning",
90
- title: "404",
91
- message: "请求的资源不存在",
92
- position: "top-right",
93
- duration: 2000,
94
- });
95
- break;
89
+ type: 'warning',
90
+ title: '404',
91
+ message: '请求的资源不存在',
92
+ position: 'top-right',
93
+ duration: 2000
94
+ })
95
+ break
96
96
  case 500:
97
97
  TinyNotify({
98
- type: "warning",
99
- title: "错误",
100
- message: "服务器内部错误",
101
- position: "top-right",
102
- duration: 2000,
103
- });
104
- break;
98
+ type: 'warning',
99
+ title: '错误',
100
+ message: '服务器内部错误',
101
+ position: 'top-right',
102
+ duration: 2000
103
+ })
104
+ break
105
105
  default:
106
106
  TinyNotify({
107
- type: "warning",
108
- title: "请求失败",
107
+ type: 'warning',
108
+ title: '请求失败',
109
109
  message: error.message,
110
- position: "top-right",
111
- duration: 2000,
112
- });
110
+ position: 'top-right',
111
+ duration: 2000
112
+ })
113
113
  }
114
114
  } else {
115
115
  TinyNotify({
116
- type: "error",
117
- title: "网络错误",
116
+ type: 'error',
117
+ title: '网络错误',
118
118
  message: error.message,
119
- position: "top-right",
120
- duration: 2000,
121
- });
119
+ position: 'top-right',
120
+ duration: 2000
121
+ })
122
122
  // message.error('网络错误,请检查您的网络连接');
123
123
  }
124
124
 
125
- return Promise.reject(error);
125
+ return Promise.reject(error)
126
126
  }
127
- );
127
+ )
128
128
 
129
129
  let apiMap = {
130
- MULTIPLE_DATA_SEARCH: "/appdata/select",
131
- DETAILS_DATA: "/appdata/detailData",
132
- INTERFACE_PLUGIN: "/appdata/plugin",
133
- DATA_INSERT: "/appdata/addData",
134
- BATCH_DATA_INSERT: "/appdata/addDatas",
135
- DATA_MODIFY: "/appdata/updateData",
136
- BATCH_DATA_MODIFY: "/appdata/updateDatas",
137
- DEL_DATA: "/appdata/delData",
138
- BATCH_DEL_DATA: "/appdata/delDatas",
139
- MULTIPLE_DATA_LINK_SEARCH: "/api/appdata/link/select",
140
- FILE_UPLOAD: "/api/file/upload", // 文件上传API
141
- };
130
+ MULTIPLE_DATA_SEARCH: '/appdata/select',
131
+ DETAILS_DATA: '/appdata/detailData',
132
+ INTERFACE_PLUGIN: '/appdata/plugin',
133
+ DATA_INSERT: '/appdata/addData',
134
+ BATCH_DATA_INSERT: '/appdata/addDatas',
135
+ DATA_MODIFY: '/appdata/updateData',
136
+ BATCH_DATA_MODIFY: '/appdata/updateDatas',
137
+ DEL_DATA: '/appdata/delData',
138
+ BATCH_DEL_DATA: '/appdata/delDatas',
139
+ MULTIPLE_DATA_LINK_SEARCH: '/api/appdata/link/select',
140
+ FILE_UPLOAD: '/api/file/upload' // 文件上传API
141
+ }
142
142
 
143
143
  /**
144
144
  * 数据服务
@@ -154,29 +154,32 @@ const dataService = {
154
154
  * @returns {Promise<any>} 响应数据
155
155
  * @example
156
156
  */
157
- fetch: (params = {}, apiConfig = {}, url = "") => {
157
+ fetch: (params = {}, apiConfig = {}, url = '') => {
158
158
  if (!url) {
159
- url = apiMap[apiConfig.apiType];
159
+ url = apiMap[apiConfig.apiType]
160
160
  }
161
161
 
162
- const { apiId = "", ...restConfig } = apiConfig;
162
+ const { apiId = '', ...restConfig } = apiConfig
163
163
 
164
164
  const defaultConfig = {
165
165
  // 默认列表查询配置
166
166
  headers: {
167
- "X-List-Query": "true",
167
+ 'X-List-Query': 'true'
168
168
  },
169
- timeout: 20000,
170
- };
169
+ timeout: 20000
170
+ }
171
171
  if (!params) {
172
- params = {};
172
+ params = {}
173
+ }
174
+ params.apiId = apiConfig.apiId
175
+ if (apiConfig.key) {
176
+ params.key = apiConfig.key
173
177
  }
174
- params.apiId = apiConfig.apiId;
175
178
 
176
- const config = { ...defaultConfig, ...restConfig };
177
- return axiosInstance.post(url, params, config);
179
+ const config = { ...defaultConfig, ...restConfig }
180
+ return axiosInstance.post(url, params, config)
178
181
  },
179
-
182
+
180
183
  /**
181
184
  * 文件上传
182
185
  * @param {string} url - 上传URL,默认使用apiMap中的FILE_UPLOAD
@@ -184,16 +187,16 @@ const dataService = {
184
187
  * @param {Function} onProgress - 上传进度回调函数,参数为0-100的进度百分比
185
188
  * @returns {Promise<any>} 上传响应数据
186
189
  */
187
- upload: (url = "", formData, onProgress = () => {}) => {
190
+ upload: (url = '', formData, onProgress = () => {}) => {
188
191
  // 如果没有指定URL,使用默认的文件上传URL
189
192
  if (!url) {
190
- url = apiMap.FILE_UPLOAD;
193
+ url = apiMap.FILE_UPLOAD
191
194
  }
192
-
195
+
193
196
  // 确保FormData中的文件字段名是'file'
194
- let fixedFormData = new FormData();
195
- let fileFound = false;
196
-
197
+ let fixedFormData = new FormData()
198
+ let fileFound = false
199
+
197
200
  // 检查并修复FormData
198
201
  if (formData instanceof FormData) {
199
202
  // 由于FormData不能直接检查内容,我们使用迭代器
@@ -201,40 +204,40 @@ const dataService = {
201
204
  // 在某些旧浏览器中可能不支持entries()
202
205
  if (typeof formData.entries === 'function') {
203
206
  for (let pair of formData.entries()) {
204
- const [key, value] = pair;
205
-
207
+ const [key, value] = pair
208
+
206
209
  if (value instanceof File || value instanceof Blob) {
207
210
  // 找到文件,使用正确的字段名
208
- console.log(`Found file in field ${key}, adding as 'file'`);
209
- fixedFormData.append('file', value);
210
- fileFound = true;
211
+ console.log(`Found file in field ${key}, adding as 'file'`)
212
+ fixedFormData.append('file', value)
213
+ fileFound = true
211
214
  } else {
212
215
  // 保留其他字段
213
- fixedFormData.append(key, value);
216
+ fixedFormData.append(key, value)
214
217
  }
215
218
  }
216
219
  } else {
217
220
  // 如果不支持entries(),假设formData已经是正确的,直接使用
218
- console.log('FormData.entries() not supported, using original FormData');
219
- fixedFormData = formData;
220
- fileFound = true;
221
+ console.log('FormData.entries() not supported, using original FormData')
222
+ fixedFormData = formData
223
+ fileFound = true
221
224
  }
222
225
  } catch (e) {
223
- console.error('Error processing FormData:', e);
226
+ console.error('Error processing FormData:', e)
224
227
  // 出错时使用原始FormData
225
- fixedFormData = formData;
228
+ fixedFormData = formData
226
229
  }
227
230
  } else {
228
- console.error('Invalid FormData:', formData);
229
- return Promise.reject(new Error('FormData is required for file upload'));
231
+ console.error('Invalid FormData:', formData)
232
+ return Promise.reject(new Error('FormData is required for file upload'))
230
233
  }
231
-
234
+
232
235
  // 如果没有找到文件,返回错误
233
236
  if (!fileFound && typeof formData.entries === 'function') {
234
- console.error('No file found in FormData');
235
- return Promise.reject(new Error('No file found in FormData'));
237
+ console.error('No file found in FormData')
238
+ return Promise.reject(new Error('No file found in FormData'))
236
239
  }
237
-
240
+
238
241
  // 上传配置
239
242
  const config = {
240
243
  timeout: 60000, // 上传超时时间加长
@@ -244,33 +247,32 @@ const dataService = {
244
247
  },
245
248
  onUploadProgress: (progressEvent) => {
246
249
  // 计算上传进度百分比
247
- const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
248
- onProgress(percentCompleted);
250
+ const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
251
+ onProgress(percentCompleted)
249
252
  }
250
- };
251
-
253
+ }
254
+
252
255
  // 发起上传请求
253
- console.log('Sending file upload request to:', url);
254
- return axiosInstance.post(url, fixedFormData, config)
255
- .then(response => {
256
- console.log('Upload server response:', response);
257
-
258
- // 处理文件路径
259
- // 如果返回的是相对路径,转换为完整URL
260
- if (typeof response === 'string' && !response.startsWith('http')) {
261
- // 判断路径是否以斜杠开头
262
- const baseUrl = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL;
263
- const filePath = response.startsWith('/') ? response : `/${response}`;
264
-
265
- // 构建完整URL
266
- const fullUrl = `${baseUrl}/files${filePath}`;
267
- console.log('Converted file path to full URL:', fullUrl);
268
- return fullUrl;
269
- }
270
-
271
- return response;
272
- });
256
+ console.log('Sending file upload request to:', url)
257
+ return axiosInstance.post(url, fixedFormData, config).then((response) => {
258
+ console.log('Upload server response:', response)
259
+
260
+ // 处理文件路径
261
+ // 如果返回的是相对路径,转换为完整URL
262
+ if (typeof response === 'string' && !response.startsWith('http')) {
263
+ // 判断路径是否以斜杠开头
264
+ const baseUrl = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL
265
+ const filePath = response.startsWith('/') ? response : `/${response}`
266
+
267
+ // 构建完整URL
268
+ const fullUrl = `${baseUrl}/files${filePath}`
269
+ console.log('Converted file path to full URL:', fullUrl)
270
+ return fullUrl
271
+ }
272
+
273
+ return response
274
+ })
273
275
  }
274
- };
276
+ }
275
277
 
276
- export default dataService;
278
+ export default dataService
@@ -46,6 +46,7 @@ const props = defineProps({
46
46
  apiConfig: {//接口配置
47
47
  type: Object,
48
48
  default: {
49
+ key: null,
49
50
  apiId: null,
50
51
  apiType: ''
51
52
  }
@@ -91,7 +92,7 @@ const click = () => {
91
92
  props.onClick()
92
93
  if (!isNormal.value && apiConfig.value.apiId) {
93
94
  props.onPrepare()
94
- dataService.fetch(data.value, { apiId: apiConfig.value.apiId, apiType: apiMap[apiConfig.value.apiType] }).then((res) => {
95
+ dataService.fetch(data.value, { key: apiConfig.value.key, apiId: apiConfig.value.apiId, apiType: apiMap[apiConfig.value.apiType] }).then((res) => {
95
96
  emit('click', res)
96
97
  props.onFinish()
97
98
  })
@@ -30,6 +30,7 @@ const props = defineProps({
30
30
  type: Object,
31
31
  required: true,
32
32
  default: () => ({
33
+ key: null,
33
34
  apiId: null,
34
35
  apiType: ''
35
36
  })
@@ -117,7 +118,7 @@ const handleRemoteSearch = async () => {
117
118
  const params = {
118
119
  queryParams: queryParams.value
119
120
  };
120
- const res = await dataService.fetch(params, { apiId: props.apiConfig.apiId, apiType: 'MULTIPLE_DATA_SEARCH' });
121
+ const res = await dataService.fetch(params, { key: props.apiConfig.key, apiId: props.apiConfig.apiId, apiType: 'MULTIPLE_DATA_SEARCH' });
121
122
  options.value = res.data.map(item => ({
122
123
  label: item.label || item.name,
123
124
  value: item.value || item.id
@@ -1,12 +1,7 @@
1
1
  <template>
2
2
  <tiny-tabs v-model="showTab" :tab-style="props.tabStyle" activeColor="#5e7ce0" @click="tabChange">
3
- <tiny-tab-item
4
- activeColor="#5e7ce0"
5
- v-for="i in showTabs"
6
- :key="i.value"
7
- :title="i.label"
8
- :name="i.value.toString()"
9
- />
3
+ <tiny-tab-item activeColor="#5e7ce0" v-for="i in showTabs" :key="i.value" :title="i.label"
4
+ :name="i.value.toString()" />
10
5
  </tiny-tabs>
11
6
  </template>
12
7
 
@@ -22,6 +17,7 @@ const props = defineProps({
22
17
  apiConfig: {
23
18
  type: Object,
24
19
  default: () => ({
20
+ key: null,
25
21
  apiId: null,
26
22
  apiType: 'MULTIPLE_DATA_SEARCH',
27
23
  }),
@@ -63,7 +59,7 @@ const remoteTabs = ref([])
63
59
 
64
60
  // 展示的tabs
65
61
  const showTabs = computed(() => {
66
- console.log(props.tabs,66, 110)
62
+ console.log(props.tabs, 66, 110)
67
63
  if (remoteTabs.value.length) return [{ label: '全部', value: '0' }, ...remoteTabs.value];
68
64
  if (props.tabs.length) return props.tabs
69
65
  return [{ label: '全部', value: '0' }]
@@ -87,7 +83,7 @@ watch(() => showTabs.value, () => {
87
83
 
88
84
  watch(() => props.apiConfig, (nVal) => {
89
85
  if (!nVal?.apiId) return
90
- dataService.fetch({ }, { apiId: nVal.apiId, apiType: 'MULTIPLE_DATA_SEARCH' }).then(res => {
86
+ dataService.fetch({}, { key: nVal.key, apiId: nVal.apiId, apiType: 'MULTIPLE_DATA_SEARCH' }).then(res => {
91
87
  remoteTabs.value = (res.data ?? []).map(i => ({
92
88
  label: i[props.labelKey],
93
89
  value: i[props.valueKey],
@@ -146,4 +142,4 @@ watch(() => props.apiConfig, (nVal) => {
146
142
  overflow: hidden;
147
143
  white-space: nowrap;
148
144
  }
149
- </style>
145
+ </style>
@@ -1,52 +1,51 @@
1
1
  <template>
2
2
  <div class="ebiz-title">
3
- <!-- <div class="color-block" :style="{ backgroundColor: color, width: `${blockWidth}px` }"></div> -->
4
- <!-- <div class="title-text">
3
+ <div class="color-block" :style="{ backgroundColor: color, width: `${blockWidth}px` }"></div>
4
+ <div class="title-text">
5
5
  <slot name="title">
6
6
  {{ displayTitle }}
7
7
  </slot>
8
- </div> -->
8
+ </div>
9
9
  </div>
10
10
  </template>
11
11
 
12
12
  <script setup>
13
+ import { defineProps, computed } from 'vue';
13
14
 
14
- // import { defineProps, computed } from 'vue';
15
+ /**
16
+ * EbizTitle 组件 - 带有色块的标题组件
17
+ * 支持自定义标题文本、色块颜色和色块宽度
18
+ * 包含一个按钮,点击时在标题后添加点号并触发事件
19
+ * 支持通过插槽自定义标题内容
20
+ */
21
+ const props = defineProps({
22
+ /**
23
+ * 标题文本 (当不使用插槽时显示)
24
+ */
25
+ title: {
26
+ type: String,
27
+ default: '标题'
28
+ },
29
+ /**
30
+ * 色块颜色,支持任何有效的CSS颜色值
31
+ */
32
+ color: {
33
+ type: String,
34
+ default: '#1890ff'
35
+ },
36
+ /**
37
+ * 色块宽度,单位为像素
38
+ */
39
+ blockWidth: {
40
+ type: Number,
41
+ default: 10
42
+ }
43
+ });
15
44
 
16
- // /**
17
- // * EbizTitle 组件 - 带有色块的标题组件
18
- // * 支持自定义标题文本、色块颜色和色块宽度
19
- // * 包含一个按钮,点击时在标题后添加点号并触发事件
20
- // * 支持通过插槽自定义标题内容
21
- // */
22
- // const props = defineProps({
23
- // /**
24
- // * 标题文本 (当不使用插槽时显示)
25
- // */
26
- // title: {
27
- // type: String,
28
- // default: '标题'
29
- // },
30
- // /**
31
- // * 色块颜色,支持任何有效的CSS颜色值
32
- // */
33
- // color: {
34
- // type: String,
35
- // default: '#1890ff'
36
- // },
37
- // /**
38
- // * 色块宽度,单位为像素
39
- // */
40
- // blockWidth: {
41
- // type: Number,
42
- // default: 10
43
- // }
44
- // });
45
45
 
46
- // // // 计算属性:根据点号数量生成显示的标题
47
- // const displayTitle = computed(() => {
48
- // return props.title;
49
- // });
46
+ const displayTitle = computed(() => {
47
+ return props.title;
48
+ });
50
49
  </script>
51
50
 
52
51
  <style scoped>
@@ -14,7 +14,7 @@
14
14
  :month-change="monthChange"
15
15
  :render-cell="renderCell"
16
16
  :theme="theme"
17
- :value="value"
17
+ :value="modelValue"
18
18
  :year="year"
19
19
  @cell-click="handleCellClick"
20
20
  @cell-double-click="handleCellDoubleClick"
@@ -111,7 +111,7 @@ export default {
111
111
  default: 'full',
112
112
  validator: (val) => ['full', 'card'].includes(val)
113
113
  },
114
- value: {
114
+ modelValue: {
115
115
  type: [String, Number, Array, Date],
116
116
  default: null
117
117
  },
@@ -120,10 +120,13 @@ export default {
120
120
  default: undefined
121
121
  }
122
122
  },
123
- emits: ['cell-click', 'cell-double-click', 'cell-right-click', 'month-change'],
123
+ emits: ['cell-click', 'cell-double-click', 'cell-right-click', 'month-change', 'update:modelValue'],
124
124
  methods: {
125
125
  handleCellClick(options) {
126
126
  this.$emit('cell-click', options)
127
+ if (options && options.date) {
128
+ this.$emit('update:modelValue', options.date)
129
+ }
127
130
  },
128
131
  handleCellDoubleClick(options) {
129
132
  this.$emit('cell-double-click', options)
@@ -0,0 +1,249 @@
1
+ <template>
2
+ <t-dialog
3
+ v-model:visible="isVisible"
4
+ :attach="attach"
5
+ :body="body"
6
+ :cancel-btn="cancelBtn"
7
+ :close-btn="closeBtn"
8
+ :close-on-esc-keydown="closeOnEscKeydown"
9
+ :close-on-overlay-click="closeOnOverlayClick"
10
+ :confirm-btn="confirmBtn"
11
+ :default-visible="defaultVisible"
12
+ :destroy-on-close="destroyOnClose"
13
+ :draggable="draggable"
14
+ :footer="footer"
15
+ :header="header"
16
+ :mode="mode"
17
+ :placement="placement"
18
+ :show-overlay="showOverlay"
19
+ :width="width"
20
+ :z-index="zIndex"
21
+ @cancel="handleCancel"
22
+ @close="handleClose"
23
+ @close-btn-click="handleCloseBtnClick"
24
+ @confirm="handleConfirm"
25
+ @esc-keydown="handleEscKeydown"
26
+ @overlay-click="handleOverlayClick"
27
+ @opened="handleOpened"
28
+ @closed="handleClosed"
29
+ @update:visible="handleUpdateVisible"
30
+ >
31
+ <!-- 头部插槽 -->
32
+ <template v-if="$slots.header" #header>
33
+ <slot name="header"></slot>
34
+ </template>
35
+
36
+ <!-- 内容插槽 -->
37
+ <template v-if="$slots.body" #body>
38
+ <slot name="body"></slot>
39
+ </template>
40
+
41
+ <!-- 默认插槽 -->
42
+ <slot></slot>
43
+
44
+ <!-- 页脚插槽 -->
45
+ <template v-if="$slots.footer" #footer>
46
+ <slot name="footer"></slot>
47
+ </template>
48
+
49
+ <!-- 取消按钮插槽 -->
50
+ <template v-if="$slots.cancelBtn" #cancel-btn>
51
+ <slot name="cancelBtn"></slot>
52
+ </template>
53
+
54
+ <!-- 确认按钮插槽 -->
55
+ <template v-if="$slots.confirmBtn" #confirm-btn>
56
+ <slot name="confirmBtn"></slot>
57
+ </template>
58
+ </t-dialog>
59
+ </template>
60
+
61
+ <script>
62
+ export default {
63
+ name: "EbizDialog"
64
+ }
65
+ </script>
66
+
67
+ <script setup>
68
+ import { ref, watch } from 'vue';
69
+ import { Dialog as TDialog } from 'tdesign-vue-next';
70
+
71
+ // 定义属性
72
+ const props = defineProps({
73
+ // 对话框挂载的节点
74
+ attach: {
75
+ type: [String, Function, Element],
76
+ default: 'body'
77
+ },
78
+ // 对话框内容
79
+ body: {
80
+ type: [String, Function],
81
+ default: ''
82
+ },
83
+ // 取消按钮,可自定义。值为 null 则不显示取消按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性
84
+ cancelBtn: {
85
+ type: [String, Object, null],
86
+ default: ''
87
+ },
88
+ // 关闭按钮,值为 true 显示默认关闭按钮;值为 false 则不显示关闭按钮;值类型为 string 则直接显示值;值类型为 TNode,则显示自定义按钮
89
+ closeBtn: {
90
+ type: [Boolean, String, Function],
91
+ default: true
92
+ },
93
+ // 按下 ESC 时是否触发对话框关闭
94
+ closeOnEscKeydown: {
95
+ type: Boolean,
96
+ default: undefined
97
+ },
98
+ // 点击蒙层时是否触发关闭
99
+ closeOnOverlayClick: {
100
+ type: Boolean,
101
+ default: undefined
102
+ },
103
+ // 确认按钮,可自定义。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性
104
+ confirmBtn: {
105
+ type: [String, Object, null],
106
+ default: ''
107
+ },
108
+ // 对话框默认是否显示
109
+ defaultVisible: {
110
+ type: Boolean,
111
+ default: false
112
+ },
113
+ // 是否在关闭弹框的时候销毁子元素
114
+ destroyOnClose: {
115
+ type: Boolean,
116
+ default: false
117
+ },
118
+ // 对话框是否可以拖拽
119
+ draggable: {
120
+ type: Boolean,
121
+ default: false
122
+ },
123
+ // 底部操作栏,默认会有"确认"和"取消"两个按钮
124
+ footer: {
125
+ type: [Boolean, Function],
126
+ default: true
127
+ },
128
+ // 头部内容
129
+ header: {
130
+ type: [String, Boolean, Function],
131
+ default: true
132
+ },
133
+ // 对话框类型
134
+ mode: {
135
+ type: String,
136
+ default: 'modal',
137
+ validator: (val) => ['modal', 'modeless', 'full-screen'].includes(val)
138
+ },
139
+ // 对话框位置,类似 CSS 中的 position
140
+ placement: {
141
+ type: String,
142
+ default: 'top',
143
+ validator: (val) => ['top', 'center'].includes(val)
144
+ },
145
+ // 是否显示遮罩层
146
+ showOverlay: {
147
+ type: Boolean,
148
+ default: true
149
+ },
150
+ // 控制对话框显示与隐藏
151
+ visible: {
152
+ type: Boolean,
153
+ default: undefined
154
+ },
155
+ // 对话框宽度
156
+ width: {
157
+ type: [String, Number],
158
+ default: undefined
159
+ },
160
+ // 对话框层级
161
+ zIndex: {
162
+ type: Number,
163
+ default: undefined
164
+ }
165
+ });
166
+
167
+ // 显示状态
168
+ const isVisible = ref(props.defaultVisible);
169
+
170
+ // 监听visible属性变化
171
+ watch(() => props.visible, (newValue) => {
172
+ if (newValue !== undefined) {
173
+ isVisible.value = newValue;
174
+ }
175
+ });
176
+
177
+ // 定义事件
178
+ const emit = defineEmits([
179
+ 'cancel',
180
+ 'close',
181
+ 'close-btn-click',
182
+ 'confirm',
183
+ 'esc-keydown',
184
+ 'overlay-click',
185
+ 'opened',
186
+ 'closed',
187
+ 'update:visible'
188
+ ]);
189
+
190
+ // 取消按钮点击事件
191
+ const handleCancel = (e) => {
192
+ emit('cancel', { e });
193
+ emit('close', { trigger: 'cancel', e });
194
+ emit('update:visible', false);
195
+ };
196
+
197
+ // 关闭事件
198
+ const handleClose = (context) => {
199
+ emit('close', context);
200
+ emit('update:visible', false);
201
+ };
202
+
203
+ // 关闭按钮点击事件
204
+ const handleCloseBtnClick = (e) => {
205
+ emit('close-btn-click', { e });
206
+ emit('close', { trigger: 'close-btn', e });
207
+ emit('update:visible', false);
208
+ };
209
+
210
+ // 确认按钮点击事件
211
+ const handleConfirm = (e) => {
212
+ emit('confirm', { e });
213
+ emit('update:visible', false);
214
+ };
215
+
216
+ // ESC 按键按下事件
217
+ const handleEscKeydown = (e) => {
218
+ emit('esc-keydown', { e });
219
+ emit('close', { trigger: 'esc', e });
220
+ emit('update:visible', false);
221
+ };
222
+
223
+ // 遮罩层点击事件
224
+ const handleOverlayClick = (e) => {
225
+ emit('overlay-click', { e });
226
+ emit('close', { trigger: 'overlay', e });
227
+ emit('update:visible', false);
228
+ };
229
+
230
+ // 对话框打开完成事件
231
+ const handleOpened = () => {
232
+ emit('opened');
233
+ };
234
+
235
+ // 对话框关闭完成事件
236
+ const handleClosed = () => {
237
+ emit('closed');
238
+ };
239
+
240
+ // 更新可见状态
241
+ const handleUpdateVisible = (visible) => {
242
+ isVisible.value = visible;
243
+ emit('update:visible', visible);
244
+ };
245
+ </script>
246
+
247
+ <style lang="less" scoped>
248
+ /* 自定义样式 */
249
+ </style>
@@ -181,60 +181,60 @@ const emit = defineEmits([
181
181
  ]);
182
182
 
183
183
  // 处理输入事件
184
- const handleInput = (value, { e }) => {
185
- emit('update:modelValue', value);
186
- emit('input', value, { e });
184
+ const handleInput = (value, context) => {
185
+ emit('input', value, context);
187
186
  };
188
187
 
189
188
  // 处理变化事件
190
- const handleChange = (value, { e }) => {
191
- emit('change', value, { e });
189
+ const handleChange = (value, context) => {
190
+ emit('update:modelValue', value);
191
+ emit('change', value, context);
192
192
  };
193
193
 
194
194
  // 处理失去焦点事件
195
- const handleBlur = (value, { e }) => {
196
- emit('blur', value, { e });
195
+ const handleBlur = (value, context) => {
196
+ emit('blur', value, context);
197
197
  };
198
198
 
199
199
  // 处理获取焦点事件
200
- const handleFocus = (value, { e }) => {
201
- emit('focus', value, { e });
200
+ const handleFocus = (value, context) => {
201
+ emit('focus', value, context);
202
202
  };
203
203
 
204
204
  // 处理回车事件
205
- const handleEnter = (value, { e }) => {
206
- emit('enter', value, { e });
205
+ const handleEnter = (value, context) => {
206
+ emit('enter', value, context);
207
207
  };
208
208
 
209
209
  // 处理清空事件
210
- const handleClear = ({ e }) => {
210
+ const handleClear = (context) => {
211
211
  emit('update:modelValue', '');
212
- emit('clear', { e });
212
+ emit('clear', context);
213
213
  };
214
214
 
215
215
  // 处理键盘按下事件
216
- const handleKeydown = (value, { e }) => {
217
- emit('keydown', value, { e });
216
+ const handleKeydown = (value, context) => {
217
+ emit('keydown', value, context);
218
218
  };
219
219
 
220
220
  // 处理键盘按键事件
221
- const handleKeypress = (value, { e }) => {
222
- emit('keypress', value, { e });
221
+ const handleKeypress = (value, context) => {
222
+ emit('keypress', value, context);
223
223
  };
224
224
 
225
225
  // 处理键盘弹起事件
226
- const handleKeyup = (value, { e }) => {
227
- emit('keyup', value, { e });
226
+ const handleKeyup = (value, context) => {
227
+ emit('keyup', value, context);
228
228
  };
229
229
 
230
230
  // 处理鼠标进入事件
231
- const handleMouseenter = (value, { e }) => {
232
- emit('mouseenter', value, { e });
231
+ const handleMouseenter = (value, context) => {
232
+ emit('mouseenter', value, context);
233
233
  };
234
234
 
235
235
  // 处理鼠标离开事件
236
- const handleMouseleave = (value, { e }) => {
237
- emit('mouseleave', value, { e });
236
+ const handleMouseleave = (value, context) => {
237
+ emit('mouseleave', value, context);
238
238
  };
239
239
  </script>
240
240
 
package/src/index.js CHANGED
@@ -46,6 +46,7 @@ import EbizWatermark from "./components/TdesignWatermark.vue";
46
46
  import EbizAvatar from "./components/EbizAvatar.vue";
47
47
  import EbizEmployeeInfo from "./components/EbizEmployeeInfo.vue";
48
48
  import EbizAlert from "./components/TdesignAlert.vue";
49
+ import EbizDialog from "./components/TdesignDialog.vue";
49
50
  import { MessagePlugin as EbizMessage } from 'tdesign-vue-next';
50
51
 
51
52
  // 导入简洁数据服务
@@ -140,5 +141,7 @@ export {
140
141
  // 员工信息组件
141
142
  EbizEmployeeInfo,
142
143
  // 提示组件
143
- EbizAlert
144
+ EbizAlert,
145
+ // 对话框组件
146
+ EbizDialog
144
147
  };
@@ -228,6 +228,12 @@ const routes = [
228
228
  name: 'TdesignAlert',
229
229
  component: () => import('../views/TdesignAlert.vue'),
230
230
  meta: { title: 'TDesign提示组件示例' }
231
+ },
232
+ {
233
+ path: '/tdesign-dialog',
234
+ name: 'TdesignDialog',
235
+ component: () => import('../views/DialogDemo.vue'),
236
+ meta: { title: 'TDesign对话框组件示例' }
231
237
  }
232
238
  ]
233
239
 
@@ -0,0 +1,169 @@
1
+ <template>
2
+ <div class="dialog-demo">
3
+ <h1>TDesign Dialog 组件示例</h1>
4
+
5
+ <h2>基础对话框</h2>
6
+ <div class="demo-section">
7
+ <EbizButton theme="primary" @click="showBasicDialog = true">打开基础对话框</EbizButton>
8
+ <EbizDialog
9
+ v-model:visible="showBasicDialog"
10
+ header="基础对话框"
11
+ :body="'这是一个基础对话框,用于展示简单的信息或进行简单的操作。'"
12
+ :confirm-btn="{ content: '确认', theme: 'primary' }"
13
+ cancel-btn="取消"
14
+ />
15
+ </div>
16
+
17
+ <h2>不同模式的对话框</h2>
18
+ <div class="demo-section">
19
+ <EbizButton @click="showModalDialog = true" style="margin-right: 16px;">模态对话框</EbizButton>
20
+ <EbizButton @click="showModelessDialog = true" style="margin-right: 16px;">非模态对话框</EbizButton>
21
+ <EbizButton @click="showFullscreenDialog = true">全屏对话框</EbizButton>
22
+
23
+ <!-- 模态对话框 -->
24
+ <EbizDialog
25
+ v-model:visible="showModalDialog"
26
+ header="模态对话框"
27
+ mode="modal"
28
+ :body="'模态对话框会阻止用户与页面的其他部分交互,直到对话框关闭。'"
29
+ :confirm-btn="{ content: '确认', theme: 'primary' }"
30
+ cancel-btn="取消"
31
+ />
32
+
33
+ <!-- 非模态对话框 -->
34
+ <EbizDialog
35
+ v-model:visible="showModelessDialog"
36
+ header="非模态对话框"
37
+ mode="modeless"
38
+ :body="'非模态对话框不会阻止用户与页面的其他部分交互,可以同时进行其他操作。'"
39
+ :confirm-btn="{ content: '确认', theme: 'primary' }"
40
+ cancel-btn="取消"
41
+ />
42
+
43
+ <!-- 全屏对话框 -->
44
+ <EbizDialog
45
+ v-model:visible="showFullscreenDialog"
46
+ header="全屏对话框"
47
+ mode="full-screen"
48
+ :body="'全屏对话框会占据整个屏幕,通常用于需要用户完全专注于某项任务的场景。'"
49
+ :confirm-btn="{ content: '确认', theme: 'primary' }"
50
+ cancel-btn="取消"
51
+ />
52
+ </div>
53
+
54
+ <h2>自定义内容的对话框</h2>
55
+ <div class="demo-section">
56
+ <EbizButton @click="showCustomDialog = true">打开自定义内容对话框</EbizButton>
57
+ <EbizDialog
58
+ v-model:visible="showCustomDialog"
59
+ header="自定义内容"
60
+ :width="500"
61
+ >
62
+ <template #body>
63
+ <div style="padding: 20px;">
64
+ <h3>这是自定义的内容区域</h3>
65
+ <p>可以在这里放置任何你想要展示的内容,包括表单、图表、列表等。</p>
66
+ <div style="margin-top: 20px;">
67
+ <EbizInput v-model="customInput" placeholder="请输入内容" />
68
+ </div>
69
+ </div>
70
+ </template>
71
+ <template #footer>
72
+ <div style="display: flex; justify-content: flex-end;">
73
+ <EbizButton @click="showCustomDialog = false" style="margin-right: 8px;">取消</EbizButton>
74
+ <EbizButton theme="primary" @click="handleCustomConfirm">确认</EbizButton>
75
+ </div>
76
+ </template>
77
+ </EbizDialog>
78
+ </div>
79
+
80
+ <h2>可拖拽的对话框</h2>
81
+ <div class="demo-section">
82
+ <EbizButton @click="showDraggableDialog = true">打开可拖拽对话框</EbizButton>
83
+ <EbizDialog
84
+ v-model:visible="showDraggableDialog"
85
+ header="可拖拽对话框"
86
+ :draggable="true"
87
+ :body="'这个对话框可以通过拖拽标题栏进行移动。'"
88
+ :confirm-btn="{ content: '确认', theme: 'primary' }"
89
+ cancel-btn="取消"
90
+ />
91
+ </div>
92
+
93
+ <h2>不同位置的对话框</h2>
94
+ <div class="demo-section">
95
+ <EbizButton @click="showTopDialog = true" style="margin-right: 16px;">顶部对话框</EbizButton>
96
+ <EbizButton @click="showCenterDialog = true">居中对话框</EbizButton>
97
+
98
+ <!-- 顶部对话框 -->
99
+ <EbizDialog
100
+ v-model:visible="showTopDialog"
101
+ header="顶部对话框"
102
+ placement="top"
103
+ :body="'这个对话框位于顶部。'"
104
+ :confirm-btn="{ content: '确认', theme: 'primary' }"
105
+ cancel-btn="取消"
106
+ />
107
+
108
+ <!-- 居中对话框 -->
109
+ <EbizDialog
110
+ v-model:visible="showCenterDialog"
111
+ header="居中对话框"
112
+ placement="center"
113
+ :body="'这个对话框位于页面中央。'"
114
+ :confirm-btn="{ content: '确认', theme: 'primary' }"
115
+ cancel-btn="取消"
116
+ />
117
+ </div>
118
+ </div>
119
+ </template>
120
+
121
+ <script setup>
122
+ import { ref } from 'vue';
123
+ import { EbizDialog, EbizButton, EbizInput } from '../index.js';
124
+
125
+ // 基础对话框
126
+ const showBasicDialog = ref(false);
127
+
128
+ // 不同模式的对话框
129
+ const showModalDialog = ref(false);
130
+ const showModelessDialog = ref(false);
131
+ const showFullscreenDialog = ref(false);
132
+
133
+ // 自定义内容的对话框
134
+ const showCustomDialog = ref(false);
135
+ const customInput = ref('');
136
+
137
+ // 可拖拽的对话框
138
+ const showDraggableDialog = ref(false);
139
+
140
+ // 不同位置的对话框
141
+ const showTopDialog = ref(false);
142
+ const showCenterDialog = ref(false);
143
+
144
+ // 自定义确认处理函数
145
+ const handleCustomConfirm = () => {
146
+ // 在实际应用中,这里可以添加表单验证或数据处理逻辑
147
+ console.log('自定义确认按钮点击,输入内容:', customInput.value);
148
+ showCustomDialog.value = false;
149
+ };
150
+ </script>
151
+
152
+ <style lang="less" scoped>
153
+ .dialog-demo {
154
+ padding: 20px;
155
+
156
+ h1 {
157
+ margin-bottom: 20px;
158
+ }
159
+
160
+ h2 {
161
+ margin-top: 30px;
162
+ margin-bottom: 16px;
163
+ }
164
+
165
+ .demo-section {
166
+ margin-bottom: 24px;
167
+ }
168
+ }
169
+ </style>
@@ -52,7 +52,8 @@ export default {
52
52
  { path: '/watermark', title: 'TDesign水印组件示例' },
53
53
  { path: '/ebiz-avatar', title: 'Ebiz头像组件示例' },
54
54
  { path: '/ebiz-employee-info', title: 'Ebiz员工信息组件示例' },
55
- { path: '/tdesign-alert', title: 'TDesign提示组件示例' }
55
+ { path: '/tdesign-alert', title: 'TDesign提示组件示例' },
56
+ { path: '/tdesign-dialog', title: 'TDesign对话框组件示例' }
56
57
  ]
57
58
 
58
59
  return {