br-dionysus 0.3.2 → 0.3.4

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.
@@ -40,12 +40,12 @@ import { ref, onMounted, useSlots, watch } from 'vue'
40
40
 
41
41
  const props = withDefaults(defineProps<{
42
42
  /** 列最小宽度 */
43
- minWidth?: string,
43
+ minWidth?: number,
44
44
  /** 列最大宽度 */
45
- maxWidth?: string
45
+ maxWidth?: number
46
46
  }>(), {
47
- minWidth: '200px',
48
- maxWidth: '300px'
47
+ minWidth: 290,
48
+ maxWidth: 400
49
49
  })
50
50
 
51
51
  const createHash = (hashLength: number = 24) => {
@@ -53,8 +53,8 @@ const createHash = (hashLength: number = 24) => {
53
53
  }
54
54
 
55
55
  const init = () => {
56
- document.documentElement.style.setProperty('--m-inline-min-width', props.minWidth)
57
- document.documentElement.style.setProperty('--m-inline-max-width', props.maxWidth)
56
+ document.documentElement.style.setProperty('--m-inline-min-width', props.minWidth + 'px')
57
+ document.documentElement.style.setProperty('--m-inline-max-width', props.maxWidth + 'px')
58
58
  }
59
59
  watch(
60
60
  () => [props.minWidth, props.maxWidth],
@@ -75,7 +75,7 @@ const checkMultiLine = () => {
75
75
  const boxEl: Element | null = document.querySelector('.' + boxClassName + ' [data-box]')
76
76
  if (!boxEl) return false
77
77
  const itemEls: NodeListOf<Element> = boxEl.querySelectorAll('.' + boxClassName + ' [data-item]')
78
- return boxEl.clientWidth < itemEls.length * 200
78
+ return boxEl.clientWidth < itemEls.length * props.minWidth
79
79
  }
80
80
 
81
81
  const resizeObserver = new ResizeObserver(() => {
@@ -0,0 +1,35 @@
1
+ <script setup>
2
+ import demo from './demo.vue'
3
+ </script>
4
+
5
+ # 下拉表格选择器
6
+ =================
7
+
8
+ ### 1) 基础用法
9
+ <Preview comp-name="MSelectTable" demo-name="demo">
10
+ <demo />
11
+ </Preview>
12
+
13
+ ### 2) Attributes
14
+
15
+ | 参数 | 说明 | 类型 | 可选值 | 默认值 |
16
+ |------------------|:-----------------:|:-------:|:------------------:|:---------:|
17
+ | value / v-model | 绑定值 | number | - | '' |
18
+ | min | 设置数值输入框允许的最小值 | number | - | -Infinity |
19
+ | max | 设置数值输入框允许的最大值 | number | - | Infinity |
20
+ | step | 数值输入框步长 | number | - | 1 |
21
+ | step-strictly | 是否只能输入 step 的倍数 | boolean | - | true |
22
+ | size | 数值输入框尺寸 | string | large, small, mini | - |
23
+ | disabled | 是否禁用数值输入框 | boolean | - | false |
24
+ | placeholder | 输入框默认 placeholder | string | - | '' |
25
+ | thousandth-place | 输入框是否显示千分位 | boolean | - | false |
26
+ | no-border | 是否不要边框 | boolean | - | false |
27
+ | no-spacing | 不要边距 | boolean | - | false |
28
+
29
+ ### 3) Events
30
+
31
+ | 方法名 | 说明 | 参数 |
32
+ |--------|:---------------:|:----------------------:|
33
+ | change | 绑定值被改变时触发 | currentValue, oldValue |
34
+ | focus | 当 input 获得焦点时触发 | (event: Event) |
35
+ | blur | 当 input 失去焦点时触发 | (event: Event) |
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <div class="g-m-select-table-box">
3
+ <m-select-table
4
+ v-model="code"
5
+ placeholder="请选择单号"
6
+ :remoteMethod="remoteMethod"
7
+ :options="options"
8
+ :tableTitle="commodityOptionsTitle"
9
+ focusShow
10
+ ></m-select-table>
11
+
12
+ <div class="m-box">
13
+ <p class="u-tt">tt</p>
14
+ <div class="u-test"></div>
15
+ </div>
16
+ </div>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { ref } from 'vue'
21
+
22
+ const code = ref<string>('')
23
+ const options = ref<Option[]>()
24
+ const commodityOptionsTitle: TableTitle[] = [{
25
+ prop: 'sn',
26
+ label: '单号'
27
+ }, {
28
+ prop: 'fileName',
29
+ label: '文件名'
30
+ }, {
31
+ prop: 'createdUserName',
32
+ label: '上传者'
33
+ }, {
34
+ prop: 'createdTimeStr',
35
+ label: '上传时间'
36
+ }]
37
+
38
+ const mockData = [{
39
+ sn: 'SC201564981241',
40
+ fileName: '测试文件1.zip',
41
+ createdUserName: '绫波丽',
42
+ createdTimeStr: '2024-05-06'
43
+ }, {
44
+ sn: 'SC201564981242',
45
+ fileName: '测试文件2.zip',
46
+ createdUserName: '绫波丽',
47
+ createdTimeStr: '2024-05-06'
48
+ }, {
49
+ sn: 'SC201564981243',
50
+ fileName: '测试文件3.zip',
51
+ createdUserName: '绫波丽',
52
+ createdTimeStr: '2024-05-06'
53
+ }, {
54
+ sn: 'SC201564981244',
55
+ fileName: '测试文件4.zip',
56
+ createdUserName: '绫波丽',
57
+ createdTimeStr: '2024-05-06'
58
+ }]
59
+
60
+ const remoteMethod = async (query: string = '') => {
61
+ options.value = mockData.map(item => ({
62
+ label: item.fileName,
63
+ value: item.sn,
64
+ sn: item.sn,
65
+ fileName: item.fileName,
66
+ createdUserName: item.createdUserName,
67
+ createdTimeStr: item.createdTimeStr
68
+ }))
69
+ }
70
+ </script>
71
+
72
+ <style lang="scss">
73
+ .m-box {
74
+ overflow: auto;
75
+ position: relative;
76
+ width: 20px;
77
+ height: 100px;
78
+ border: 1px solid #00ff00;
79
+
80
+ .u-tt {
81
+ position: absolute;
82
+ top: 0;
83
+ }
84
+
85
+ .u-test {
86
+ height: 300px;
87
+ }
88
+ }
89
+ </style>
@@ -0,0 +1,10 @@
1
+ import { App, Plugin } from 'vue'
2
+ import MSelectTable from './src/index.vue'
3
+
4
+ export const MSelectTablePlugin: Plugin = {
5
+ install(app: App) {
6
+ app.component('m-select-table', MSelectTable)
7
+ }
8
+ }
9
+
10
+ export { MSelectTable }
@@ -0,0 +1,392 @@
1
+ <template>
2
+ <div class="g-box g-select-table-box">
3
+ <div class="u-input-gp">
4
+ <el-input
5
+ v-model="_this.vValue"
6
+ :class="_this.inputClass"
7
+ :size="props.size"
8
+ :placeholder="props.placeholder"
9
+ @input="remoteMethod($event);_this.showPanel = true"
10
+ @focus="focus"
11
+ @keyup.enter="addRowData"
12
+ :disabled="props.disabled"
13
+ />
14
+ <div
15
+ class="el-icon el-icon-circle-close"
16
+ v-if="props.clearable && _this.vValue"
17
+ @click="empty"
18
+ >
19
+ <el-icon><CircleClose /></el-icon>
20
+ </div>
21
+ </div>
22
+
23
+ <div
24
+ class="m-table-box"
25
+ v-show="_this.showPanel"
26
+ :style="_this.boxStyle"
27
+ >
28
+ <div
29
+ v-if="isSelect"
30
+ class="btn-box"
31
+ >
32
+ <el-button
33
+ @click="selectMultiple"
34
+ size="small"
35
+ >
36
+ 确认
37
+ </el-button>
38
+ </div>
39
+ <el-table
40
+ :ref="mTableRef"
41
+ class="m-table s-table"
42
+ :data="optionsMap"
43
+ size="small"
44
+ @currentChange="handleCurrentChange"
45
+ :rowClassName="tableRowClassName"
46
+ @selectionChange="handleSelectionChange"
47
+ >
48
+ <el-table-column
49
+ v-if="props.isSelect"
50
+ type="selection"
51
+ width="55"
52
+ />
53
+ <el-table-column
54
+ showOverflowTooltip
55
+ v-for="item in tableTitle"
56
+ :key="item.prop"
57
+ :prop="item.prop"
58
+ :label="item.label"
59
+ :width="item.width"
60
+ :minWidth="item.minWidth"
61
+ :filters="item.filters"
62
+ :headerAlign="item.headerAlign"
63
+ >
64
+ <template #default="scope">
65
+ {{ optionsMap[scope.$index][item.prop] || '-' }}
66
+ </template>
67
+ </el-table-column>
68
+ </el-table>
69
+
70
+ <!--分页-->
71
+ <div
72
+ class="m-page"
73
+ v-if="props.total"
74
+ >
75
+ <el-pagination
76
+ v-model:currentPage="page.currentPage"
77
+ v-model:pageSize="page.pageSize"
78
+ hideOnSinglePage
79
+ small
80
+ :pageSizes="page.pageSizesOptions"
81
+ layout="total, prev, pager, next, sizes"
82
+ :total="page.total"
83
+ @currentChange="toPage"
84
+ @sizeChange="toPage(page.currentPage, $event)"
85
+ />
86
+ </div>
87
+ </div>
88
+
89
+ <div
90
+ class="m-mask"
91
+ v-show="_this.showPanel"
92
+ @click="clickOutside"
93
+ />
94
+ </div>
95
+ </template>
96
+
97
+ <script setup lang="ts">
98
+ import { reactive, computed, watch, nextTick, ref } from 'vue'
99
+ import { ElMessage } from 'element-plus'
100
+ import { Page } from './../../typings/class'
101
+
102
+ const createHash = (hashLength: number = 24) => {
103
+ return Array.from(Array(Number(hashLength) || 24), () => Math.floor(Math.random() * 36).toString(36)).join('')
104
+ }
105
+
106
+ const props = withDefaults(defineProps<{
107
+ modelValue?: string | number,
108
+ placeholder?: string,
109
+ disabled?: boolean,
110
+ options: Option[],
111
+ tableTitle: any[],
112
+ remoteMethod?: Function,
113
+ allowCreate?: boolean, // 是否创建条目
114
+ focusShow?: boolean, // 聚焦显示
115
+ isSelect?: boolean, // 是否多选
116
+ clearable?: boolean,
117
+ size?: 'small' | 'large' | '',
118
+ total?: number | null // 总数据量,当有值时,出现分页器
119
+ }>(), {
120
+ modelValue: '',
121
+ placeholder: '请输入',
122
+ disabled: false,
123
+ options: () => [],
124
+ tableTitle: () => [],
125
+ remoteMethod: () => {},
126
+ allowCreate: false,
127
+ focusShow: false,
128
+ isSelect: false,
129
+ clearable: false,
130
+ size: '',
131
+ total: null
132
+ })
133
+
134
+ const _this = reactive<{
135
+ vValue: string,
136
+ vLabel: string,
137
+ oValue: string,
138
+ inputClass: string,
139
+ masterPieceLoading: boolean,
140
+ showPanel: boolean, // 显示面板
141
+ selectedRow: Option | {},
142
+ boxStyle: any,
143
+ selectKeys: string[]
144
+ }>({
145
+ vValue: '',
146
+ vLabel: '',
147
+ oValue: '',
148
+ inputClass: 'j-' + createHash(),
149
+ masterPieceLoading: false,
150
+ showPanel: false, // 显示面板
151
+ selectedRow: {},
152
+ boxStyle: {
153
+ },
154
+ selectKeys: []
155
+ })
156
+
157
+ const emit = defineEmits(['update:modelValue', 'selectMultiple', 'change', 'selected'])
158
+
159
+ const optionsMap = computed(() => {
160
+ if (!props.allowCreate || !_this.vValue) return props.options
161
+ const checkSame = props.options.some(item => item.label === _this.vValue)
162
+ if (checkSame) return props.options
163
+ const nodeObj: any = {}
164
+ props.tableTitle.forEach(key => {
165
+ nodeObj[key] = null
166
+ })
167
+ return [...props.options, {
168
+ ...nodeObj,
169
+ label: _this.vValue,
170
+ value: '#' + createHash(),
171
+ className: 's-add'
172
+ // [props.labelKey]: _this.vValue
173
+ }
174
+ ]
175
+ })
176
+
177
+ // 分页
178
+ const page = reactive<Page>(new Page({
179
+ total: 0,
180
+ pageSize: 5,
181
+ currentPage: 1,
182
+ pageSizesOptions: [5, 10, 20]
183
+ }))
184
+ const toPage = (currentPage: number = 1, pageSize: number = page.pageSize) => {
185
+ page.currentPage = currentPage
186
+ page.pageSize = pageSize
187
+ props.remoteMethod(_this.vValue || '', page)
188
+ }
189
+
190
+ watch(
191
+ () => _this.showPanel,
192
+ () => {
193
+ const style = {}
194
+ if (_this.showPanel) {
195
+ const element = document.querySelector('.' + _this.inputClass)
196
+ if (!element) return
197
+ const rect = element.getBoundingClientRect()
198
+ if (!rect) return
199
+ const bottom = window.innerHeight - rect.top - rect.height
200
+ const right = window.innerWidth - rect.left - rect.width
201
+ if (bottom < 250) {
202
+ Object.assign(style, { bottom: (bottom + rect.height) + 'px' })
203
+ Object.assign(style, { transform: 'translateY(-10px)' })
204
+ } else {
205
+ Object.assign(style, { top: (rect.top + rect.height) + 'px' })
206
+ Object.assign(style, { transform: 'translateY(10px)' })
207
+ }
208
+ if ((window.innerWidth - rect.left) < 550) {
209
+ Object.assign(style, { right: right + 'px' })
210
+ } else {
211
+ Object.assign(style, { left: rect.left + 'px' })
212
+ }
213
+
214
+ Object.assign(style, { opacity: 1 })
215
+ }
216
+ if (!_this.showPanel) {
217
+ Object.assign(style, { opacity: 0 })
218
+ }
219
+
220
+ nextTick(() => {
221
+ _this.boxStyle = style
222
+ })
223
+ }
224
+ )
225
+
226
+ watch(
227
+ () => props.modelValue,
228
+ () => {
229
+ _this.vValue = props.options.find(item => item.value === props.modelValue)?.label || props.modelValue as string
230
+ }
231
+ )
232
+
233
+ watch(() => props.total, () => {
234
+ page.total = props.total as number
235
+ })
236
+
237
+ _this.vValue = props.options.find(item => item.value === props.modelValue)?.label || props.modelValue as string
238
+
239
+ const empty = () => {
240
+ _this.selectedRow = {}
241
+ selected()
242
+ }
243
+
244
+ const handleSelectionChange = (val: string[]) => {
245
+ _this.selectKeys = val
246
+ }
247
+
248
+ const mTableRef = ref<any>(null)
249
+
250
+ const selectMultiple = () => {
251
+ if (!_this.selectKeys.length) return ElMessage.warning('请勾选数据')
252
+ const arr = JSON.parse(JSON.stringify(_this.selectKeys))
253
+ _this.selectKeys = []
254
+ mTableRef.value.clearSelection()
255
+ _this.showPanel = false
256
+ emit('selectMultiple', arr)
257
+ }
258
+
259
+ const handleCurrentChange = (currentRow: Option) => {
260
+ if (!currentRow) return false
261
+ _this.showPanel = false
262
+ _this.vValue = currentRow?.label || ''
263
+ _this.selectedRow = currentRow
264
+ selected()
265
+ }
266
+
267
+ const addRowData = () => {
268
+ handleCurrentChange(optionsMap.value[optionsMap.value.length - 1])
269
+ }
270
+
271
+ const selected = () => {
272
+ emit('update:modelValue', (_this.selectedRow as Option)?.value || '')
273
+ emit('change', (_this.selectedRow as Option)?.value || '')
274
+ emit('selected', _this.selectedRow)
275
+ }
276
+
277
+ const clickOutside = () => {
278
+ if (!_this.showPanel) return false
279
+ _this.showPanel = false
280
+ _this.vValue = (_this.selectedRow as Option)?.label || _this.oValue
281
+ }
282
+
283
+ /** 关闭 */
284
+ const close = () => {
285
+ if (!_this.showPanel) return false
286
+ _this.showPanel = false
287
+ }
288
+
289
+ const focus = () => {
290
+ _this.oValue = _this.vValue
291
+ if (props.focusShow) {
292
+ _this.showPanel = true
293
+ if (props.total !== null) {
294
+ props.remoteMethod(_this.vValue || '', page)
295
+ } else {
296
+ props.remoteMethod(_this.vValue || '')
297
+ }
298
+ }
299
+ }
300
+
301
+ const tableRowClassName = ({ row }: any) => {
302
+ return row.className
303
+ }
304
+
305
+ defineExpose({
306
+ close
307
+ })
308
+ </script>
309
+
310
+ <style scoped lang="scss">
311
+ .g-select-table-box {
312
+ position: relative;
313
+ line-height: normal;
314
+
315
+ .u-input-gp {
316
+ position: relative;
317
+ //height: 24px;
318
+
319
+ &:hover .el-icon-circle-close {
320
+ display: block;
321
+ }
322
+ }
323
+
324
+ .el-icon-circle-close {
325
+ display: none;
326
+ position: absolute;
327
+ top: 50%;
328
+ right: 10px;
329
+ font-size: 14px;
330
+ color: #c0c4cc;
331
+ transform: translateY(-50%);
332
+ cursor: pointer;
333
+
334
+ &:hover {
335
+ color: #a4a7ad;
336
+ }
337
+ }
338
+
339
+ .m-table-box {
340
+ position: fixed;
341
+ //top: 100%;
342
+ //left: 0;
343
+ opacity: 0;
344
+ z-index: 9;
345
+ padding: 8px;
346
+ width: 500px;
347
+ border: 1px solid var(--el-border-color-lighter, #ebeef5);
348
+ -webkit-box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
349
+ box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
350
+ background: var(--el-bg-color, #fff);
351
+ border-radius: 4px;
352
+ }
353
+
354
+ .m-mask {
355
+ position: fixed;
356
+ top: 0;
357
+ left: 0;
358
+ z-index: 8;
359
+ width: 100%;
360
+ height: 100%;
361
+ }
362
+
363
+ .s-table {
364
+ width: 100%;
365
+ }
366
+ }
367
+
368
+ .btn-box{
369
+ text-align: right;
370
+ padding-bottom: 6px;
371
+ }
372
+
373
+ .m-page {
374
+ padding-top: 4px;
375
+ }
376
+ </style>
377
+
378
+ <style lang="scss">
379
+ .g-select-table-box {
380
+ .s-add {
381
+ background-color: rgba(135, 208, 104, 0.1);
382
+ }
383
+
384
+ .u-input-gp .el-input {
385
+ display: flex;
386
+ }
387
+
388
+ .el-table {
389
+ --el-table-header-bg-color: var(--m-list-el-table-header-bg-color, #f5f5f5);
390
+ }
391
+ }
392
+ </style>
package/packages/index.ts CHANGED
@@ -8,6 +8,8 @@ import { TabPagePlugin } from './TabPage'
8
8
  import { MInputNumberPlugin } from './MInputNumber'
9
9
  import { MInlinePlugin } from './MInline'
10
10
  import { SkinConfigPlugin } from 'packages/SkinConfig'
11
+ import { MSelectTablePlugin } from 'packages/MSelectTable'
12
+ import hook from 'packages/Hook'
11
13
 
12
14
  const BrPlugin: Plugin = {
13
15
  install(app: App) {
@@ -17,6 +19,7 @@ const BrPlugin: Plugin = {
17
19
  MInputNumberPlugin.install?.(app)
18
20
  MInlinePlugin.install?.(app)
19
21
  SkinConfigPlugin.install?.(app)
22
+ MSelectTablePlugin.install?.(app)
20
23
  }
21
24
  }
22
25
 
@@ -28,3 +31,6 @@ export * from './TabPage'
28
31
  export * from './MInputNumber'
29
32
  export * from './MInline'
30
33
  export * from './SkinConfig'
34
+ export * from './MSelectTable'
35
+
36
+ export { hook }
@@ -34,5 +34,11 @@
34
34
  "compZhName": "皮肤设置",
35
35
  "compDesc": "这是一个皮肤设置组件",
36
36
  "compClassName": "skin-cConfig"
37
+ },
38
+ {
39
+ "compName": "MSelectTable",
40
+ "compZhName": "下拉表格选择器",
41
+ "compDesc": "这是一个下拉表格选择器组件",
42
+ "compClassName": "m-select-table"
37
43
  }
38
44
  ]
@@ -0,0 +1,22 @@
1
+ /** 分页 */
2
+ export class Page {
3
+ /** 总数 */
4
+ total: number
5
+ /** 分页大小 */
6
+ pageSize: number
7
+ /** 页码 */
8
+ currentPage: number
9
+ /** 分页大小可选项 */
10
+ pageSizesOptions: number[]
11
+
12
+ constructor ({ total = 100, pageSize = 50, currentPage = 1, pageSizesOptions = [50, 100, 300, 500, 800] } = {}) {
13
+ /** 总数 */
14
+ this.total = total
15
+ /** 分页大小 */
16
+ this.pageSize = pageSize
17
+ /** 页码 */
18
+ this.currentPage = currentPage
19
+ /** 分页大小可选项 */
20
+ this.pageSizesOptions = pageSizesOptions
21
+ }
22
+ }
@@ -0,0 +1,75 @@
1
+ /** 表单标识 */
2
+ export enum FormTag {
3
+ /** 要货需求(请购) */
4
+ PR = 'PR',
5
+ /** 采购(采购) */
6
+ PO = 'PO',
7
+ /** 销售单(销售) */
8
+ SO = 'SO',
9
+ /** 需求汇总(汇总请购单) */
10
+ CR = 'CR',
11
+ /** 调价单(调价单) */
12
+ TJ = 'TJ',
13
+ /** 入库单 */
14
+ RD = 'RD',
15
+ }
16
+
17
+ /** 单据状态枚举 */
18
+ export enum BillStatus {
19
+ /** 草稿 */
20
+ DRAFT = 0,
21
+
22
+ /** 待提交 */
23
+ WAIT = 1,
24
+
25
+ /** 已提交 */
26
+ SUBMIT = 2,
27
+
28
+ /** 已审核 */
29
+ REVIEWED = 3,
30
+
31
+ /** 已生效 */
32
+ EFFECTIVE = 4,
33
+
34
+ /** 已失效 */
35
+ EXPIRED = 5,
36
+
37
+ /** 已终止 */
38
+ TERMINATED = 6,
39
+
40
+ /** 已关闭 */
41
+ CLOSED = 7
42
+ }
43
+
44
+ /** 流程实例状态枚举 */
45
+ export enum FlowStatus {
46
+ /** 保存 */
47
+ SAVE = 0,
48
+
49
+ /** 处理 */
50
+ REVIEWED = 1,
51
+
52
+ /** 通过 */
53
+ EFFECTIVE = 2,
54
+
55
+ /** 驳回 */
56
+ EXPIRED = 3,
57
+
58
+ /** 撤销 */
59
+ TERMINATED = 4,
60
+
61
+ /** 终止 */
62
+ CLOSED = 5
63
+ }
64
+
65
+ /** 首尾页标识枚举 */
66
+ export enum FirstOrLast {
67
+ /** 只有一页 */
68
+ ONE = 'one',
69
+ /** 首页 */
70
+ FIRST = 'First',
71
+ /** 尾页 */
72
+ LAST = 'Last',
73
+ /** 其他 */
74
+ OTHER = 'other'
75
+ }