vue-2024-ui 0.0.13 → 0.0.15

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": "vue-2024-ui",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "src/components/index.js",
@@ -38,23 +38,25 @@
38
38
  </el-checkbox-group>
39
39
  <!--select-->
40
40
  <el-select v-model="ctrlValue" :placeholder="model[ctrlKey].label" v-bind="selectModel"
41
- v-else-if="ctrlType == 'select'">
42
- <template #default v-if="model[ctrlKey]?.options?.some(a => a.options)">
41
+ v-else-if="ctrlType === 'select'">
42
+ <template #default>
43
43
  <slot :name="`${props.t}-${ctrlKey}`" :options="model[ctrlKey].options">
44
- <el-option-group v-for="group in model[ctrlKey].options" :label="group.label">
45
- <el-option v-bind="option" v-for="option in group.options"
44
+ <!-- 根据是否存在子选项渲染不同结构 -->
45
+ <template v-if="model[ctrlKey]?.options?.some(a => a.options)">
46
+ <el-option-group v-for="group in model[ctrlKey].options" :key="group.label"
47
+ :label="group.label">
48
+ <el-option v-bind="option" v-for="option in group.options" :key="option.value"
49
+ @click.native="selectModel.change && selectModel.change(option)">
50
+ <slot :name="`${props.t}-${ctrlKey}-options`" :option></slot>
51
+ </el-option>
52
+ </el-option-group>
53
+ </template>
54
+ <template v-else>
55
+ <el-option v-bind="option" v-for="option in model[ctrlKey].options" :key="option.value"
46
56
  @click.native="selectModel.change && selectModel.change(option)">
47
57
  <slot :name="`${props.t}-${ctrlKey}-options`" :option></slot>
48
58
  </el-option>
49
- </el-option-group>
50
- </slot>
51
- </template>
52
- <template #default v-else>
53
- <slot :name="`${props.t}-${ctrlKey}`" :options="model[ctrlKey].options">
54
- <el-option v-bind="option" v-for="option in model[ctrlKey].options"
55
- @click.native="selectModel.change && selectModel.change(option)">
56
- <slot :name="`${props.t}-${ctrlKey}-options`" :option></slot>
57
- </el-option>
59
+ </template>
58
60
  </slot>
59
61
  </template>
60
62
  <template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
@@ -62,7 +64,7 @@
62
64
  </template>
63
65
  </el-select>
64
66
  <!--tree-select-->
65
- <el-tree-select v-model="ctrlValue" :placeholder="model[ctrlKey].label" v-bind="itemInfo"
67
+ <el-tree-select v-model="ctrlValue" :placeholder="model[ctrlKey].label" v-bind="itemInfo" :data="treeSelectData"
66
68
  v-else-if="ctrlType == 'tree-select'">
67
69
  <template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
68
70
  <slot :name="`${props.t}-${ctrlKey}-${slot}`" :data> </slot>
@@ -107,7 +109,8 @@
107
109
  </template>
108
110
  </el-transfer>
109
111
  <!--cascader-->
110
- <el-cascader v-model="ctrlValue" v-bind="itemInfo" v-else-if="ctrlType == 'cascader'">
112
+ <el-cascader v-model="ctrlValue" v-bind="itemInfo" :options="model[ctrlKey].options"
113
+ v-else-if="ctrlType == 'cascader'">
111
114
  <template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
112
115
  <slot :name="`${props.t}-${ctrlKey}-${slot}`" :data> </slot>
113
116
  </template>
@@ -134,7 +137,7 @@
134
137
  </el-input-number>
135
138
  <!--input-->
136
139
  <el-input v-model="ctrlValue" :placeholder="model[ctrlKey].label" :maxlength="itemInfo?.max || 25"
137
- :clearable="true" v-bind="itemInfo" v-else>
140
+ :clearable="true" v-bind="{ ...itemInfo, type: ctrlType }" v-else>
138
141
  <template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
139
142
  <slot :name="`${props.t}-${ctrlKey}-${slot}`" :data> </slot>
140
143
  </template>
@@ -144,14 +147,14 @@
144
147
 
145
148
  <script setup>
146
149
  defineOptions({ inheritAttrs: false });
147
- import { onMounted, useSlots } from "vue";
148
- import { filterObject } from ".";
150
+ import { onMounted, useSlots, ref } from "vue";
149
151
  const ctrlValue = defineModel({ required: true });
150
152
  const props = defineProps(["t", "ctrlKey", "item", "model", 'ctrlType'])
151
153
  const itemInfo = props.item;
152
154
  const ctrlKey = props.ctrlKey;
153
155
  const ctrlType = props.ctrlType;
154
156
  const model = props.model;
157
+ const treeSelectData = ref([]);
155
158
  const slots = () => Object.keys(useSlots()).filter(a => a.startsWith(`${props.t}-${ctrlKey}-`)).map(a => a.replace(`${props.t}-${ctrlKey}-`, ''));
156
159
 
157
160
  //select 默认属性
@@ -166,9 +169,63 @@ const selectRemoteMethod = (query) => {
166
169
  })
167
170
  }
168
171
 
169
- onMounted(() => {
172
+ // 新增:标记数据是否加载完成
173
+ const isOptionsLoaded = ref(false);
174
+
175
+ // 模拟异步获取数据的函数
176
+ const options = async (keyValue = 'options') => {
177
+ try {
178
+ // 这里替换为实际的异步请求
179
+ const response = await itemInfo?.options(model);
180
+ model[ctrlKey][keyValue] = response;
181
+ isOptionsLoaded.value = true;
182
+ } catch (error) {
183
+ console.error('Failed to fetch options:', error);
184
+ }
185
+ };
186
+
187
+ onMounted(async () => {
170
188
  if (itemInfo && itemInfo["remote-method"]) {
171
189
  selectModel['remote-method'] = selectRemoteMethod;
172
190
  }
191
+ // 新增:在组件挂载时调用异步获取数据的函数
192
+ // 提取公共逻辑到一个函数中
193
+ const loadOptions = (key, keyValue = 'options', targetRef = model[ctrlKey]) => {
194
+ if (typeof itemInfo.options === 'function') {
195
+ options(keyValue).catch(error => {
196
+ console.error(`Failed to load ${keyValue} for ${key}:`, error);
197
+ });
198
+ } else if (Array.isArray(itemInfo.options)) {
199
+ targetRef[keyValue] = itemInfo.options;
200
+ // 更新数据加载状态
201
+ isOptionsLoaded.value = true;
202
+ }
203
+ };
204
+
205
+ // 处理不同的 ctrlType
206
+ const typesWithOptions = ['radio', 'radio-button', 'checkbox-group', 'checkbox-group-button', 'select', 'cascader'];
207
+ if (typesWithOptions.includes(ctrlType)) {
208
+ loadOptions(ctrlType);
209
+ }
210
+
211
+ if (ctrlType === 'tree-select') {
212
+ const loadTreeSelectOptions = async () => {
213
+ try {
214
+ if (typeof itemInfo.options === 'function') {
215
+ treeSelectData.value = await itemInfo.options(model);
216
+ } else if (Array.isArray(itemInfo?.data)) {
217
+ treeSelectData.value = itemInfo.data;
218
+ } else if (itemInfo?.options && Array.isArray(itemInfo?.options)) {
219
+ treeSelectData.value = itemInfo.options;
220
+ }
221
+ // 更新数据加载状态
222
+ isOptionsLoaded.value = true;
223
+ } catch (error) {
224
+ console.error(`Failed to load data for tree-select:`, error);
225
+ }
226
+ };
227
+ await loadTreeSelectOptions();
228
+ }
229
+
173
230
  })
174
231
  </script>
@@ -1,20 +1,18 @@
1
1
  <template>
2
2
  <template v-for="(item, key) of getQueryModel(props.more)" :key="key">
3
- <el-col v-bind="{ ...model.query.cols, ...item?.search?.col }">
4
- <slot :name="`q-${key}`" :item :model>
5
- <el-form-item :label="item.label" v-bind="{ ...model.queryItems, ...item?.search }">
6
- <g-ctrl v-model="queryModel[key]" :item="item?.editor" :ctrlKey="key" :ctrlType="item.type" :model
7
- t="q">
8
- <template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
9
- <slot :name="slotKey" :data :item :model></slot>
10
- </template>
11
- </g-ctrl>
12
- <template #label>
13
- <slot :name="`q-${key}-label`" :item :model></slot>
3
+ <slot :name="`q-${key}`" :item :model>
4
+ <el-form-item :label="item.label" v-bind="{ ...model.queryItems, ...item?.search }">
5
+ <g-ctrl v-model="queryModel[key]" :item="{ ...item?.editor, ...item.search }" :ctrlKey="key"
6
+ :ctrlType="item.type" :model t="q">
7
+ <template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
8
+ <slot :name="slotKey" :data :item :model></slot>
14
9
  </template>
15
- </el-form-item>
16
- </slot>
17
- </el-col>
10
+ </g-ctrl>
11
+ <template #label>
12
+ <slot :name="`q-${key}-label`" :item :model></slot>
13
+ </template>
14
+ </el-form-item>
15
+ </slot>
18
16
  </template>
19
17
  </template>
20
18
 
@@ -6,42 +6,41 @@
6
6
  <slot :name="slotKey" :model></slot>
7
7
  </template>
8
8
  </g-query-item>
9
- <el-col v-bind="model.query.btns.col" style="width:100%;">
10
- <el-form-item style="width: 100%;">
11
- <div :style="model.query.btns.style">
12
- <el-button v-bind="model.query.query" @click="model.query.query.click()">
13
- {{ model.query.query.label }}
14
- </el-button>
15
- <el-button v-bind="model.query.reset" @click="model.query.reset.click()">
16
- {{ model.query.reset.label }}
17
- </el-button>
18
- <el-button v-bind="model.query.more" @click="model.query.more.click()"
19
- v-if="model.query.more.mode !== 3">
20
- {{ model.query.more.label }}
21
- </el-button>
22
- <el-popover v-bind="model.query.more.dialog" v-if="model.query.more.mode == 3">
23
- <template #reference>
24
- <el-button v-bind="model.query.more">
25
- {{ model.query.more.label }}
26
- </el-button>
27
- </template>
28
- <template #default>
29
- <el-form
30
- v-bind="filterObject(model.query, k => !['query', 'more', 'reset'].includes(k))">
31
- <g-query-item v-model="model" v-model:info="queryModel" :more="true">
32
- <template v-for="(slot, slotKey, index) in $slots" :key="index"
33
- #[slotKey]="data">
34
- <slot :name="slotKey" :model></slot>
35
- </template>
36
- </g-query-item>
37
- </el-form>
38
- </template>
39
- </el-popover>
40
- <slot name="q-btns"></slot>
41
- </div>
42
- </el-form-item>
43
- </el-col>
44
9
  </el-form>
10
+ <el-form style="display: flex;">
11
+ <el-form-item style="width: 100%;">
12
+ <div :style="model.query.btns.style">
13
+ <el-button v-bind="model.query.query" @click="model.query.query.click()">
14
+ {{ model.query.query.label }}
15
+ </el-button>
16
+ <el-button v-bind="model.query.reset" @click="model.query.reset.click()">
17
+ {{ model.query.reset.label }}
18
+ </el-button>
19
+ <el-button v-bind="model.query.more" @click="model.query.more.click()"
20
+ v-if="model.query.more.mode !== 3">
21
+ {{ model.query.more.label }}
22
+ </el-button>
23
+ <el-popover v-bind="model.query.more.dialog" v-if="model.query.more.mode == 3">
24
+ <template #reference>
25
+ <el-button v-bind="model.query.more">
26
+ {{ model.query.more.label }}
27
+ </el-button>
28
+ </template>
29
+ <template #default>
30
+ <el-form v-bind="filterObject(model.query, k => !['query', 'more', 'reset'].includes(k))">
31
+ <g-query-item v-model="model" v-model:info="queryModel" :more="true">
32
+ <template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
33
+ <slot :name="slotKey" :model></slot>
34
+ </template>
35
+ </g-query-item>
36
+ </el-form>
37
+ </template>
38
+ </el-popover>
39
+ <slot name="q-btns"></slot>
40
+ </div>
41
+ </el-form-item>
42
+ </el-form>
43
+
45
44
  <Transition :duration="300" name="nested">
46
45
  <div v-if="isShowMore && !model.query.more.hidden && model.query.more.mode == 1" class="outer">
47
46
  <div class="inner">
@@ -124,13 +123,9 @@ const defaultQueryAttrs = {
124
123
  isShowMore.value = false;
125
124
  }
126
125
  },
127
- cols: {
128
- span: 6, offset: 0, xs: 24, sm: 12, md: 8, lg: 6, xl: 4,
129
- },
130
126
  btns: {
131
- col: { span: 8, offset: 0, xs: 24, sm: 12, md: 8, lg: 6, xl: 4, },
132
127
  style: {
133
- width: "100%", display: "flex", justifyContent: "flex-end"
128
+ width: "100%", display: "flex", flexWrap: "wrap", justifyContent: "flex-start",
134
129
  }
135
130
  }
136
131
  }
@@ -153,9 +148,8 @@ model.value.query.more.dialog = model.value.query.more.mode == 2 ? mergeObjects(
153
148
  const defaultQueryItemsAttrs = {
154
149
  type: "hidden",
155
150
  labelPosition: 'right',
156
- span: 8,
157
151
  style: {
158
- width: "100%"
152
+ width: "280px"
159
153
  }
160
154
  };
161
155
  model.value.queryItems = mergeObjects(defaultQueryItemsAttrs, options?.queryItems, model.value.queryItems);
@@ -7,17 +7,23 @@
7
7
  </template>
8
8
  <template #q-btns>
9
9
  <slot name="q-btns">
10
- <div class="btns" v-if="model.control.add.auth()">
10
+ <div class="btns">
11
11
  <slot name="control-add-left"></slot>
12
- <slot name="control-add">
12
+ <slot name="control-add" v-if="model.control.add.auth()">
13
13
  <el-button v-bind="model.control.add" @click="addRow(model)">{{
14
14
  model.control.add.label
15
15
  }}</el-button>
16
16
  </slot>
17
17
  <slot name="control-add-right"></slot>
18
18
  </div>
19
+ <div class="btns" v-if="model.control.download.auth()">
20
+ <slot name="control-download">
21
+ <el-button v-bind="model.control.download" @click="download(model)">{{
22
+ model.control.download.label
23
+ }}</el-button>
24
+ </slot>
25
+ </div>
19
26
  </slot>
20
-
21
27
  </template>
22
28
  </g-query>
23
29
  </slot>
@@ -94,6 +100,7 @@
94
100
  <div>{{ model.dialog.title }}</div>
95
101
  </slot>
96
102
  </div>
103
+ <!-- :icon="isFullscreen ? 'radix-icons:exit-full-screen' : 'radix-icons:enter-full-screen'" -->
97
104
  <!--最大化-->
98
105
  <div v-if="model?.dialog?.fullScreen === false" @click="model.dialog.fullScreen = true;"
99
106
  class="max">
@@ -148,7 +155,7 @@ import { computed, watchEffect, inject, ref } from 'vue';
148
155
  import GForm from './g-form.vue';
149
156
  import gQuery from './g-query.vue';
150
157
  import { mergeObjects, filterObject, convertToPx } from './index';
151
- import { FullScreen, Remove } from '@element-plus/icons-vue';
158
+ import { FullScreen, Remove, } from '@element-plus/icons-vue';
152
159
 
153
160
  defineOptions({
154
161
  name: "g-table",
@@ -236,6 +243,13 @@ const defaultControl = {
236
243
  message: '此操作将永久删除该文件, 是否继续?', title: '提示', options: { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
237
244
  },
238
245
  },
246
+ download: {
247
+ label: '下载', text: false, type: 'primary',
248
+ style: {
249
+ marginLeft: "10px",
250
+ },
251
+ auth: () => false,
252
+ },
239
253
  };
240
254
  model.value.control = mergeObjects(
241
255
  defaultControl,
@@ -290,44 +304,59 @@ const initFormData = Object.fromEntries(Object.entries(filterObject(model.value,
290
304
  return (val.type != 'control' && val.editor !== false && val.type != 'hidden');
291
305
  })).map(([key, val]) => [key, val.value]));
292
306
 
293
- //扩展formatter方法
294
307
  const formatterExtends = (item, str) => {
308
+ // 初始化最终值为输入的字符串
295
309
  let trueValue = str;
296
- if (item.decimals && !isNaN(str)) {
297
- if (typeof item.decimals == 'number') {
310
+ // 处理小数位数
311
+ if (item.decimals &&!isNaN(str)) {
312
+ if (typeof item.decimals === 'number') {
313
+ // 确保输入是有效的数字,然后保留指定小数位数
298
314
  trueValue = parseFloat(trueValue).toFixed(item.decimals);
299
- }
300
- else if (typeof item.decimals == 'object') {
315
+ } else if (typeof item.decimals === 'object') {
301
316
  if (item.decimals.num) {
302
317
  if (item.decimals.pad) {
318
+ // 保留指定小数位数并补零
303
319
  trueValue = parseFloat(trueValue).toFixed(item.decimals.num);
304
320
  } else {
321
+ // 保留指定小数位数但不补零
305
322
  trueValue = parseFloat(parseFloat(trueValue).toFixed(item.decimals.num));
306
323
  }
307
324
  }
308
325
  }
309
326
  }
327
+
328
+ // 处理日期格式化
310
329
  if (item.dateFormat && dayjs(str).isValid()) {
311
- if (item.dateFormat == 'date') {
312
- trueValue = dayjs(trueValue).format('YYYY-MM-DD');
313
- } else if (item.dateFormat == 'time') {
314
- trueValue = dayjs(trueValue).format('HH:mm:ss');
315
- }
316
- else if (item.dateFormat == 'datetime') {
317
- trueValue = dayjs(trueValue).format('YYYY-MM-DD HH:mm:ss');
318
- }
319
- else {
320
- trueValue = dayjs(trueValue).format(item.dateFormat)
330
+ switch (item.dateFormat) {
331
+ case 'date':
332
+ trueValue = dayjs(trueValue).format('YYYY-MM-DD');
333
+ break;
334
+ case 'time':
335
+ trueValue = dayjs(trueValue).format('HH:mm:ss');
336
+ break;
337
+ case 'datetime':
338
+ trueValue = dayjs(trueValue).format('YYYY-MM-DD HH:mm:ss');
339
+ break;
340
+ default:
341
+ trueValue = dayjs(trueValue).format(item.dateFormat);
321
342
  }
322
343
  }
344
+
345
+ // 处理前缀
323
346
  if (item.prefix && trueValue) {
347
+ // 使用模板字符串添加前缀
324
348
  trueValue = `<span>${item.prefix}${trueValue}</span>`;
325
349
  }
350
+
351
+ // 处理后缀
326
352
  if (item.suffix && trueValue) {
327
- trueValue += `<span>${item.suffix}</span>`
353
+ // 使用模板字符串添加后缀
354
+ trueValue = `${trueValue}<span>${item.suffix}</span>`;
328
355
  }
329
- return trueValue
330
- }
356
+
357
+ return trueValue;
358
+ };
359
+
331
360
 
332
361
 
333
362
  //重置查询初始值
@@ -339,6 +368,7 @@ const queryReset = (q) => {
339
368
  //查询方法
340
369
  const query = (q) => {
341
370
  queryInfo.value = q;
371
+ model.value.table.load();
342
372
  }
343
373
 
344
374
  //新增方法
@@ -429,6 +459,14 @@ const formReset = (callback) => {
429
459
  emits("reset", callback)
430
460
  }
431
461
 
462
+ const download = (model) => {
463
+ if (model.control.download?.click) {
464
+ model.control.download.click(model);
465
+ } else {
466
+ alert("download");
467
+ }
468
+ }
469
+
432
470
  //表格列
433
471
  const tableColumns = computed(() => Object.fromEntries(
434
472
  Object.entries(model.value)
@@ -178,4 +178,27 @@ function filterObject(obj, filter = (key, value) => filter(key, value)) {
178
178
  return result;
179
179
  }
180
180
 
181
- export { openModal, convertToPx, mergeObjects, filterObject };
181
+ /**
182
+ * 在树形结构中通过一个 value 值查找对应的 label,使用递归方式。
183
+ * @param {Array} treeData - 树形结构的数据数组。
184
+ * @param {*} value - 需要查找的 value 值。
185
+ * @param {string} [valueKey="value"] - 数据节点中表示 value 的键名。
186
+ * @param {string} [labelKey="label"] - 数据节点中表示 label 的键名。
187
+ * @param {string} [childrenKey="children"] - 数据节点中表示子节点的键名。
188
+ * @returns {string|null} - 如果找到对应的 label 则返回该 label,否则返回 null。
189
+ */
190
+ function findTreeLabelByValue(treeData, value, valueKey = "value", labelKey = "label", childrenKey = "children") {
191
+ for (const node of treeData) {
192
+ if (node[valueKey] === value) {
193
+ return node[labelKey];
194
+ }
195
+ if (node[childrenKey] && node[childrenKey].length > 0) {
196
+ const result = findTreeLabelByValue(node[childrenKey], value, valueKey, labelKey, childrenKey);
197
+ if (result) {
198
+ return result;
199
+ }
200
+ }
201
+ }
202
+ return null;
203
+ }
204
+ export { openModal, convertToPx, mergeObjects, filterObject, findTreeLabelByValue };