npmapps 1.0.17 → 1.0.19
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/app/1sof699xfunpgawzmkhgcaxdnfb0cg0c.upxs +0 -0
- package/app/7dabc59eefbf4019c136a31c0ef09699.upxs +0 -0
- package/app/c7fa1b80a8f81f3c66504f7e1feea6a8.upxs +0 -0
- package/app/k810yo1ilnquiszd7kluoo9wy0tybjkm.upxs +0 -0
- package/app/lwe6jsl7cch4poe6uxzknoaj5jnjol2i.upxs +0 -0
- package/app/mbfth0qs7vounquunbib6uqgzrxsurdb.upxs +0 -0
- package/app/vgkjchovl3otihcvhnnq4wdlnans6ypw.upxs +0 -0
- package/app/ymz8vrrolp3vmfzbhavhuwfa6l1xoqts.upxs +0 -0
- package/app/yqpp2xezdlglkpgujhb7e6jv3ym59yqr.upxs +0 -0
- package/package.json +3 -2
- package/app/sort/SortTableDemo.vue +0 -93
- package/app/sort/index.vue +0 -109
- package/app/sort/sortTable.md +0 -71
- package/app/sort/sortTable.vue +0 -321
- package/app.md +0 -2
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npmapps",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
|
+
"description": "",
|
|
4
5
|
"main": "index.js",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -8,5 +9,5 @@
|
|
|
8
9
|
"keywords": [],
|
|
9
10
|
"author": "",
|
|
10
11
|
"license": "ISC",
|
|
11
|
-
"
|
|
12
|
+
"type": "commonjs"
|
|
12
13
|
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<!-- 演示:工具显隐开关 -->
|
|
4
|
-
<div style="margin-bottom:12px; display:flex; gap:16px; align-items:center; flex-wrap:wrap;">
|
|
5
|
-
<el-switch v-model="ui.showMoveUpDown" active-text="显示 上/下移" inactive-text="隐藏 上/下移" />
|
|
6
|
-
<el-switch v-model="ui.showMoveTopBottom" active-text="显示 置顶/置底" inactive-text="隐藏 置顶/置底" />
|
|
7
|
-
<el-switch v-model="ui.showMoveTo" active-text="显示 移至" inactive-text="隐藏 移至" />
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<!-- 排序工具栏示例:完整用法 -->
|
|
11
|
-
<sort-table
|
|
12
|
-
:data.sync="tableData"
|
|
13
|
-
:selectData="selectedRows"
|
|
14
|
-
sortKey="order"
|
|
15
|
-
rowKey="id"
|
|
16
|
-
:tableRef="$refs.elTable"
|
|
17
|
-
:showMoveUpDown="ui.showMoveUpDown"
|
|
18
|
-
:showMoveTopBottom="ui.showMoveTopBottom"
|
|
19
|
-
:showMoveTo="ui.showMoveTo"
|
|
20
|
-
/>
|
|
21
|
-
|
|
22
|
-
<!-- 表格主体:打开 selection,保留选择 -->
|
|
23
|
-
<el-table
|
|
24
|
-
ref="elTable"
|
|
25
|
-
:data="tableData"
|
|
26
|
-
row-key="id"
|
|
27
|
-
:reserve-selection="true"
|
|
28
|
-
style="width: 100%"
|
|
29
|
-
@selection-change="handleSelectionChange"
|
|
30
|
-
>
|
|
31
|
-
<el-table-column type="selection" width="55" />
|
|
32
|
-
<el-table-column prop="order" label="排序字段" width="120" />
|
|
33
|
-
<el-table-column prop="date" label="日期" width="180" />
|
|
34
|
-
<el-table-column prop="name" label="姓名" width="180" />
|
|
35
|
-
<el-table-column prop="address" label="地址" />
|
|
36
|
-
</el-table>
|
|
37
|
-
</div>
|
|
38
|
-
</template>
|
|
39
|
-
|
|
40
|
-
<script>
|
|
41
|
-
import sortTable from './sortTable.vue';
|
|
42
|
-
|
|
43
|
-
const mockApi = () => {
|
|
44
|
-
return new Promise((resolve) => {
|
|
45
|
-
setTimeout(() => {
|
|
46
|
-
resolve([
|
|
47
|
-
{ id: '1', date: '2016-05-02', name: '王小虎 1', address: '上海市普陀区金沙江路 1518 弄', order: '1' },
|
|
48
|
-
{ id: '2', date: '2016-05-04', name: '王小虎 2', address: '上海市普陀区金沙江路 1517 弄', order: '3' },
|
|
49
|
-
{ id: '3', date: '2016-05-01', name: '王小虎 3', address: '上海市普陀区金沙江路 1519 弄', order: '5' },
|
|
50
|
-
{ id: '4', date: '2016-05-03', name: '王小虎 4', address: '上海市普陀区金沙江路 1520 弄', order: '7' },
|
|
51
|
-
{ id: '5', date: '2016-05-05', name: '王小虎 5', address: '上海市普陀区金沙江路 1521 弄', order: '9' },
|
|
52
|
-
{ id: '6', date: '2016-05-06', name: '王小虎 6', address: '上海市普陀区金沙江路 1522 弄', order: '11' },
|
|
53
|
-
{ id: '7', date: '2016-05-07', name: '王小虎 7', address: '上海市普陀区金沙江路 1523 弄', order: '13' },
|
|
54
|
-
{ id: '8', date: '2016-05-08', name: '王小虎 8', address: '上海市普陀区金沙江路 1524 弄', order: '15' },
|
|
55
|
-
]);
|
|
56
|
-
}, 300);
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export default {
|
|
61
|
-
name: 'SortTableDemo',
|
|
62
|
-
components: { sortTable },
|
|
63
|
-
data() {
|
|
64
|
-
return {
|
|
65
|
-
tableData: [],
|
|
66
|
-
selectedRows: [],
|
|
67
|
-
ui: {
|
|
68
|
-
showMoveUpDown: true,
|
|
69
|
-
showMoveTopBottom: true,
|
|
70
|
-
showMoveTo: true,
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
},
|
|
74
|
-
async mounted() {
|
|
75
|
-
await this.load();
|
|
76
|
-
},
|
|
77
|
-
methods: {
|
|
78
|
-
async load() {
|
|
79
|
-
const res = await mockApi();
|
|
80
|
-
this.tableData = res;
|
|
81
|
-
},
|
|
82
|
-
handleSelectionChange(val) {
|
|
83
|
-
this.selectedRows = val || [];
|
|
84
|
-
// 演示:观察选中与数据变化
|
|
85
|
-
// console.log('selectedRows:', this.selectedRows.map(r => r.id));
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
</script>
|
|
90
|
-
|
|
91
|
-
<style scoped>
|
|
92
|
-
</style>
|
|
93
|
-
|
package/app/sort/index.vue
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<sort-table :data.sync="tableData" :selectData="selectedRows" sortKey="order1" :tableRef="$refs.elTable" />
|
|
4
|
-
<el-table ref="elTable" :data="tableData" row-key="id" :reserve-selection="true" style="width: 100%"
|
|
5
|
-
@selection-change="handleSelectionChange">
|
|
6
|
-
<el-table-column type="selection" width="55"></el-table-column>
|
|
7
|
-
<el-table-column prop="order1" label="排序字段" width="120" />
|
|
8
|
-
<el-table-column prop="date" label="日期" width="180" />
|
|
9
|
-
<el-table-column prop="name" label="姓名" width="180" />
|
|
10
|
-
<el-table-column prop="address" label="地址" />
|
|
11
|
-
</el-table>
|
|
12
|
-
</div>
|
|
13
|
-
</template>
|
|
14
|
-
|
|
15
|
-
<script>
|
|
16
|
-
import sortTable from './sortTable.vue';
|
|
17
|
-
const getDataApi = () => {
|
|
18
|
-
return new Promise((resolve, reject) => {
|
|
19
|
-
setTimeout(() => {
|
|
20
|
-
resolve( [
|
|
21
|
-
{
|
|
22
|
-
id: '1',
|
|
23
|
-
date: '2016-05-02',
|
|
24
|
-
name: '王小虎1',
|
|
25
|
-
address: '上海市普陀区金沙江路 1518 弄',
|
|
26
|
-
order1: '2'
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: '2',
|
|
30
|
-
date: '2016-05-04',
|
|
31
|
-
name: '王小虎2',
|
|
32
|
-
address: '上海市普陀区金沙江路 1517 弄',
|
|
33
|
-
order1: '4'
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: '3',
|
|
37
|
-
date: '2016-05-01',
|
|
38
|
-
name: '王小虎3',
|
|
39
|
-
address: '上海市普陀区金沙江路 1519 弄',
|
|
40
|
-
order1: '6'
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: '4',
|
|
44
|
-
date: '2016-05-03',
|
|
45
|
-
name: '王小虎4',
|
|
46
|
-
address: '上海市普陀区金沙江路 1520 弄',
|
|
47
|
-
order1: '8'
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
id: '5',
|
|
51
|
-
date: '2016-05-05',
|
|
52
|
-
name: '王小虎5',
|
|
53
|
-
address: '上海市普陀区金沙江路 1521 弄',
|
|
54
|
-
order1: '10'
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: '6',
|
|
58
|
-
date: '2016-05-06',
|
|
59
|
-
name: '王小虎6',
|
|
60
|
-
address: '上海市普陀区金沙江路 1522 弄',
|
|
61
|
-
order1: '12'
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: '7',
|
|
65
|
-
date: '2016-05-07',
|
|
66
|
-
name: '王小虎7',
|
|
67
|
-
address: '上海市普陀区金沙江路 1523 弄',
|
|
68
|
-
order1: '14'
|
|
69
|
-
},
|
|
70
|
-
])
|
|
71
|
-
}, 1000)
|
|
72
|
-
})
|
|
73
|
-
}
|
|
74
|
-
export default {
|
|
75
|
-
name: 'sort',
|
|
76
|
-
data() {
|
|
77
|
-
return {
|
|
78
|
-
tableData:[],
|
|
79
|
-
selectedRows: []
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
components: {
|
|
83
|
-
sortTable
|
|
84
|
-
},
|
|
85
|
-
watch: {
|
|
86
|
-
tableData: {
|
|
87
|
-
handler(newVal) {
|
|
88
|
-
console.log(newVal,'new');
|
|
89
|
-
console.log(this.selectedRows,'selectedRows');
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
-
immediate: true
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
async mounted() {
|
|
96
|
-
await this.getTableData()
|
|
97
|
-
},
|
|
98
|
-
methods: {
|
|
99
|
-
async getTableData() {
|
|
100
|
-
const res = await getDataApi()
|
|
101
|
-
this.tableData = res
|
|
102
|
-
},
|
|
103
|
-
handleSelectionChange(val) {
|
|
104
|
-
this.selectedRows = val;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
</script>
|
package/app/sort/sortTable.md
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# sortTable 排序工具栏使用文档
|
|
2
|
-
|
|
3
|
-
## 概述
|
|
4
|
-
- 为 `Element UI` 的 `el-table` 提供通用排序工具栏。
|
|
5
|
-
- 支持拖拽行排序、批量上/下移、置顶/置底、移动到指定位置,并在排序后自动恢复行选择状态。
|
|
6
|
-
|
|
7
|
-
## 依赖与环境
|
|
8
|
-
- 运行环境:`Vue 2.x + element-ui`
|
|
9
|
-
- 依赖:`sortablejs`
|
|
10
|
-
- 组件路径:`src/views/element/sort/sortTable.vue`
|
|
11
|
-
|
|
12
|
-
## Props
|
|
13
|
-
- `data: Array` 表格数据源(建议使用 `.sync`)
|
|
14
|
-
- `selectData: Array` 当前选中行数组(来自父级 `@selection-change`)
|
|
15
|
-
- `sortKey: String` 排序字段名,排序后会按槽位顺序写回到该字段;默认 `order`
|
|
16
|
-
- `rowKey: String` 行唯一标识字段名,用于恢复选择;默认 `id`
|
|
17
|
-
- `tableRef: Object` `el-table` 的 `ref`,组件将基于此挂载拖拽与恢复选择
|
|
18
|
-
- `showMoveUpDown: Boolean` 是否显示上移/下移按钮组;默认 `true`
|
|
19
|
-
- `showMoveTopBottom: Boolean` 是否显示置顶/置底按钮组;默认 `true`
|
|
20
|
-
- `showMoveTo: Boolean` 是否显示“移动到指定位置”;默认 `true`
|
|
21
|
-
|
|
22
|
-
## 事件
|
|
23
|
-
- `update:data(nextArray)` 排序变更后触发,回传最新数组以配合 `.sync`
|
|
24
|
-
|
|
25
|
-
## 使用步骤
|
|
26
|
-
1. 在父组件中准备 `tableData`(数组)与 `selectedRows`(数组)两个响应式字段。
|
|
27
|
-
2. 渲染 `el-table`,打开行选择列(`type="selection"`),并监听 `@selection-change` 将选中行赋给 `selectedRows`。
|
|
28
|
-
3. 在表格上方(或任意位置)渲染 `<sort-table>`,传入:
|
|
29
|
-
- `:data.sync="tableData"`
|
|
30
|
-
- `:selectData="selectedRows"`
|
|
31
|
-
- `sortKey="order"`(或你的实际字段)
|
|
32
|
-
- `:tableRef="$refs.elTable"`
|
|
33
|
-
4. 可根据需要通过 `showMoveUpDown`、`showMoveTopBottom`、`showMoveTo` 控制工具按钮可见性。
|
|
34
|
-
|
|
35
|
-
## 最小示例(片段)
|
|
36
|
-
```vue
|
|
37
|
-
<template>
|
|
38
|
-
<div>
|
|
39
|
-
<sort-table
|
|
40
|
-
:data.sync="tableData"
|
|
41
|
-
:selectData="selectedRows"
|
|
42
|
-
sortKey="order"
|
|
43
|
-
:tableRef="$refs.elTable"
|
|
44
|
-
/>
|
|
45
|
-
<el-table
|
|
46
|
-
ref="elTable"
|
|
47
|
-
:data="tableData"
|
|
48
|
-
row-key="id"
|
|
49
|
-
:reserve-selection="true"
|
|
50
|
-
@selection-change="selectedRows = $event"
|
|
51
|
-
>
|
|
52
|
-
<el-table-column type="selection" width="55" />
|
|
53
|
-
<el-table-column prop="order" label="排序" width="120" />
|
|
54
|
-
<el-table-column prop="name" label="姓名" />
|
|
55
|
-
</el-table>
|
|
56
|
-
</div>
|
|
57
|
-
</template>
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## 行为说明
|
|
61
|
-
- 拖拽排序:直接拖动表格行即可更改顺序。
|
|
62
|
-
- 批量移动:当选中多行时,上下移会跳过相邻已选行,保持块内相对顺序。
|
|
63
|
-
- 置顶/置底:抽取选中块放到顶部或底部。
|
|
64
|
-
- 移至:在剩余元素中将选中块插入到目标下标(1 开始)。
|
|
65
|
-
- 选择恢复:排序后自动恢复之前选中的行(依赖 `rowKey`)。
|
|
66
|
-
|
|
67
|
-
## 常见问题
|
|
68
|
-
- 未传 `tableRef`:拖拽不会生效,且选择恢复不可用。
|
|
69
|
-
- `rowKey` 不唯一:选择恢复可能失败,请确保唯一性。
|
|
70
|
-
- 远端保存:本组件只负责产生新顺序数组,请在父组件中监听 `update:data` 并进行保存。
|
|
71
|
-
|
package/app/sort/sortTable.vue
DELETED
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
组件:sortTable
|
|
3
|
-
功能:为 Element UI 的 `el-table` 提供通用排序工具栏,支持:
|
|
4
|
-
- 拖拽行排序(基于 SortableJS,直接拖动表格行)
|
|
5
|
-
- 批量上移/下移/置顶/置底 已选行
|
|
6
|
-
- 将选中行一次性移动到指定位置(按序插入)
|
|
7
|
-
- 排序后自动保持原选择状态(恢复 selection)
|
|
8
|
-
|
|
9
|
-
使用约定:
|
|
10
|
-
- 需要传入 `tableRef`(表格的 ref),以便挂载拖拽和处理选择恢复
|
|
11
|
-
- 需要指定排序字段 `sortKey`,组件会在 emit 新数据前按该字段写回排序值(slot 顺序)
|
|
12
|
-
- 通过 `data.sync` 接收并更新列表数据;通过 `selectData` 接收已选中的行数据
|
|
13
|
-
|
|
14
|
-
主要 Props:
|
|
15
|
-
- data: Array 表格当前数据(支持 .sync)
|
|
16
|
-
- selectData: Array 当前选中行数组(来自父级 @selection-change)
|
|
17
|
-
- sortKey: String 排序字段名(写回顺序时会按此字段覆盖)
|
|
18
|
-
- rowKey: String 用于选择恢复的行唯一标识字段名
|
|
19
|
-
- tableRef: Object 表格组件的 ref(必须传入)
|
|
20
|
-
- showMoveUpDown: Bool 是否显示上移/下移按钮组
|
|
21
|
-
- showMoveTopBottom: Bool是否显示置顶/置底按钮组
|
|
22
|
-
- showMoveTo: Bool 是否显示移动到指定位置的输入与按钮
|
|
23
|
-
|
|
24
|
-
事件:
|
|
25
|
-
- update:data(nextArray) 排序变更后,向父组件回传最新的数组(用于 .sync)
|
|
26
|
-
|
|
27
|
-
注意:组件不直接修改传入的 `data` 引用,而是拷贝后计算并通过事件回传;
|
|
28
|
-
拖拽排序仅在首次挂载时绑定一次。
|
|
29
|
-
-->
|
|
30
|
-
<template>
|
|
31
|
-
<div class="sort-toolbar">
|
|
32
|
-
<div class="actions">
|
|
33
|
-
<!-- 上移/下移:跳过相邻已选行,保持块内相对顺序 -->
|
|
34
|
-
<el-button-group v-if="showMoveUpDown">
|
|
35
|
-
<el-button size="mini" @click="moveUp" :disabled="disabled">上移</el-button>
|
|
36
|
-
<el-button size="mini" @click="moveDown" :disabled="disabled">下移</el-button>
|
|
37
|
-
</el-button-group>
|
|
38
|
-
<div class="dividerLine" v-if="showMoveTopBottom"></div>
|
|
39
|
-
<!-- 置顶/置底:将选中行抽出形成块,拼接到顶部或底部 -->
|
|
40
|
-
<el-button-group v-if="showMoveTopBottom">
|
|
41
|
-
<el-button size="mini" @click="moveTop" :disabled="disabled">置顶</el-button>
|
|
42
|
-
<el-button size="mini" @click="moveBottom" :disabled="disabled">置底</el-button>
|
|
43
|
-
</el-button-group>
|
|
44
|
-
<div class="dividerLine" v-if="showMoveTo"></div>
|
|
45
|
-
<!-- 指定位置移动:将选中块插入目标下标(1 开始) -->
|
|
46
|
-
<div class="move-to" v-if="showMoveTo">
|
|
47
|
-
<el-input-number
|
|
48
|
-
size="mini"
|
|
49
|
-
v-model="targetIndex"
|
|
50
|
-
:min="1"
|
|
51
|
-
:max="dataMax"
|
|
52
|
-
controls-position="right"
|
|
53
|
-
:controls="false"
|
|
54
|
-
style="width: 60px"
|
|
55
|
-
/>
|
|
56
|
-
<el-button size="mini" @click="moveTo" :disabled="disabled || !validTarget"
|
|
57
|
-
>移至</el-button
|
|
58
|
-
>
|
|
59
|
-
</div>
|
|
60
|
-
<!-- <div class="info">
|
|
61
|
-
<span>选中 {{ selectedIndices.length }} 项</span>
|
|
62
|
-
</div> -->
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</template>
|
|
66
|
-
<script>
|
|
67
|
-
import Sortable from "sortablejs";
|
|
68
|
-
export default {
|
|
69
|
-
name: "sortTable",
|
|
70
|
-
props: {
|
|
71
|
-
// 表格数据源(建议使用 .sync)
|
|
72
|
-
data: {
|
|
73
|
-
type: Array,
|
|
74
|
-
default: () => [],
|
|
75
|
-
},
|
|
76
|
-
// 当前选中行数组(来自 el-table 的 @selection-change)
|
|
77
|
-
selectData: {
|
|
78
|
-
type: Array,
|
|
79
|
-
default: () => [],
|
|
80
|
-
},
|
|
81
|
-
// 排序字段名:会使用内部 slot 顺序写回到每个行的此字段
|
|
82
|
-
sortKey: {
|
|
83
|
-
type: String,
|
|
84
|
-
default: "order",
|
|
85
|
-
},
|
|
86
|
-
// 行唯一标识字段:用于排序后选择恢复
|
|
87
|
-
rowKey: {
|
|
88
|
-
type: String,
|
|
89
|
-
default: "id",
|
|
90
|
-
},
|
|
91
|
-
// el-table 的 ref,用于挂载拖拽与恢复选择
|
|
92
|
-
tableRef: {
|
|
93
|
-
type: Object,
|
|
94
|
-
default: null,
|
|
95
|
-
},
|
|
96
|
-
// 是否显示上移/下移按钮组
|
|
97
|
-
showMoveUpDown: {
|
|
98
|
-
type: Boolean,
|
|
99
|
-
default: true,
|
|
100
|
-
},
|
|
101
|
-
// 是否显示置顶/置底按钮组
|
|
102
|
-
showMoveTopBottom: {
|
|
103
|
-
type: Boolean,
|
|
104
|
-
default: true,
|
|
105
|
-
},
|
|
106
|
-
// 是否显示“移动到指定位置”功能
|
|
107
|
-
showMoveTo: {
|
|
108
|
-
type: Boolean,
|
|
109
|
-
default: true,
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
data() {
|
|
113
|
-
return {
|
|
114
|
-
// 目标位置的 1-based 下标(与 UI 交互)
|
|
115
|
-
targetIndex: undefined,
|
|
116
|
-
// 记录初始或最新的排序槽位(从数据中读取 sortKey 形成的顺序)
|
|
117
|
-
orderSlots: [],
|
|
118
|
-
// 最近一次排序变更前,选中行的 rowKey 集合,用于恢复 selection
|
|
119
|
-
lastSelectedKeys: [],
|
|
120
|
-
};
|
|
121
|
-
},
|
|
122
|
-
created() {},
|
|
123
|
-
watch: {
|
|
124
|
-
data: {
|
|
125
|
-
handler(newVal, oldVal) {
|
|
126
|
-
const d = newVal || [];
|
|
127
|
-
// 记录当前数据对应的排序槽位(用于写回 sortKey)
|
|
128
|
-
this.orderSlots = d.map((item) => item[this.sortKey]);
|
|
129
|
-
},
|
|
130
|
-
immediate: true,
|
|
131
|
-
},
|
|
132
|
-
tableRef: {
|
|
133
|
-
handler(newVal, oldVal) {
|
|
134
|
-
// 首次拿到表格 ref 时,挂载拖拽
|
|
135
|
-
this.attachSortable();
|
|
136
|
-
},
|
|
137
|
-
immediate: true,
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
computed: {
|
|
141
|
-
// 将 selectData 映射为在 data 中的下标列表,并按升序返回
|
|
142
|
-
selectedIndices() {
|
|
143
|
-
const res = [];
|
|
144
|
-
const d = this.data || [];
|
|
145
|
-
const s = this.selectData || [];
|
|
146
|
-
for (let i = 0; i < s.length; i++) {
|
|
147
|
-
const idx = d.indexOf(s[i]);
|
|
148
|
-
if (idx !== -1) res.push(idx);
|
|
149
|
-
}
|
|
150
|
-
return res.sort((a, b) => a - b);
|
|
151
|
-
},
|
|
152
|
-
// 是否禁用操作:无选择时禁用
|
|
153
|
-
disabled() {
|
|
154
|
-
return this.selectedIndices.length === 0;
|
|
155
|
-
},
|
|
156
|
-
// 数据最大长度,用于输入校验
|
|
157
|
-
dataMax() {
|
|
158
|
-
return (this.data || []).length;
|
|
159
|
-
},
|
|
160
|
-
// 目标输入是否合法(1 <= n <= dataMax)
|
|
161
|
-
validTarget() {
|
|
162
|
-
const n = Number(this.targetIndex);
|
|
163
|
-
return Number.isInteger(n) && n >= 1 && n <= this.dataMax;
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
methods: {
|
|
167
|
-
// 挂载拖拽能力:只绑定一次,避免重复实例化
|
|
168
|
-
attachSortable() {
|
|
169
|
-
const ref = this.tableRef;
|
|
170
|
-
if (!ref || !ref.$el) return;
|
|
171
|
-
const tbody = ref.$el.querySelector(".el-table__body-wrapper tbody");
|
|
172
|
-
if (!tbody) return;
|
|
173
|
-
if (this._sortable) return;
|
|
174
|
-
this._sortable = new Sortable(tbody, {
|
|
175
|
-
animation: 150,
|
|
176
|
-
onEnd: (evt) => {
|
|
177
|
-
if (!evt || evt.oldIndex === undefined || evt.newIndex === undefined) return;
|
|
178
|
-
const arr = this.data.slice();
|
|
179
|
-
const [moved] = arr.splice(evt.oldIndex, 1);
|
|
180
|
-
arr.splice(evt.newIndex, 0, moved);
|
|
181
|
-
this.emitData(arr);
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
},
|
|
185
|
-
// 按记录的 slot 顺序写回 sortKey,确保排序字段与展示一致
|
|
186
|
-
applySlotOrder(arr) {
|
|
187
|
-
const slots = this.orderSlots || [];
|
|
188
|
-
const n = Math.min(slots.length, arr.length);
|
|
189
|
-
for (let i = 0; i < n; i++) {
|
|
190
|
-
if (arr[i]) arr[i][this.sortKey] = slots[i];
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
// 统一的回传:写回排序字段、记录选择、触发更新、在 UI 更新后恢复选择
|
|
194
|
-
emitData(next) {
|
|
195
|
-
const key = this.rowKey;
|
|
196
|
-
this.lastSelectedKeys = (this.selectData || []).map((r) => r && r[key]);
|
|
197
|
-
this.applySlotOrder(next);
|
|
198
|
-
this.$emit("update:data", next);
|
|
199
|
-
this.$nextTick(() => {
|
|
200
|
-
this.restoreSelection();
|
|
201
|
-
});
|
|
202
|
-
},
|
|
203
|
-
// 根据上次记录的 rowKey 集合恢复 el-table 的选择状态
|
|
204
|
-
restoreSelection() {
|
|
205
|
-
const ref = this.tableRef;
|
|
206
|
-
if (!ref || !ref.clearSelection || !ref.toggleRowSelection) return;
|
|
207
|
-
const key = this.rowKey;
|
|
208
|
-
const set = new Set(this.lastSelectedKeys || []);
|
|
209
|
-
ref.clearSelection();
|
|
210
|
-
const rows = this.data || [];
|
|
211
|
-
for (let i = 0; i < rows.length; i++) {
|
|
212
|
-
const row = rows[i];
|
|
213
|
-
if (set.has(row && row[key])) {
|
|
214
|
-
ref.toggleRowSelection(row, true);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
// 上移:按选中下标,从前到后遍历,避免与相邻块冲突
|
|
219
|
-
moveUp() {
|
|
220
|
-
if (this.disabled) return;
|
|
221
|
-
const arr = this.data.slice();
|
|
222
|
-
const set = new Set(this.selectedIndices);
|
|
223
|
-
for (let i = 0; i < this.selectedIndices.length; i++) {
|
|
224
|
-
const idx = this.selectedIndices[i];
|
|
225
|
-
if (idx > 0 && !set.has(idx - 1)) {
|
|
226
|
-
const tmp = arr[idx - 1];
|
|
227
|
-
arr[idx - 1] = arr[idx];
|
|
228
|
-
arr[idx] = tmp;
|
|
229
|
-
set.delete(idx);
|
|
230
|
-
set.add(idx - 1);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
this.emitData(arr);
|
|
234
|
-
},
|
|
235
|
-
// 下移:从后到前遍历,避免与相邻块冲突
|
|
236
|
-
moveDown() {
|
|
237
|
-
if (this.disabled) return;
|
|
238
|
-
const arr = this.data.slice();
|
|
239
|
-
const set = new Set(this.selectedIndices);
|
|
240
|
-
for (let i = this.selectedIndices.length - 1; i >= 0; i--) {
|
|
241
|
-
const idx = this.selectedIndices[i];
|
|
242
|
-
if (idx < arr.length - 1 && !set.has(idx + 1)) {
|
|
243
|
-
const tmp = arr[idx + 1];
|
|
244
|
-
arr[idx + 1] = arr[idx];
|
|
245
|
-
arr[idx] = tmp;
|
|
246
|
-
set.delete(idx);
|
|
247
|
-
set.add(idx + 1);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
this.emitData(arr);
|
|
251
|
-
},
|
|
252
|
-
// 置顶:抽取选中块 + 其余元素,拼接为 [块, 其余]
|
|
253
|
-
moveTop() {
|
|
254
|
-
if (this.disabled) return;
|
|
255
|
-
const arr = this.data.slice();
|
|
256
|
-
const block = [];
|
|
257
|
-
const set = new Set(this.selectedIndices);
|
|
258
|
-
for (let i = 0; i < arr.length; i++) {
|
|
259
|
-
if (set.has(i)) block.push(arr[i]);
|
|
260
|
-
}
|
|
261
|
-
const rest = arr.filter((_, i) => !set.has(i));
|
|
262
|
-
const next = block.concat(rest);
|
|
263
|
-
this.emitData(next);
|
|
264
|
-
},
|
|
265
|
-
// 置底:抽取选中块 + 其余元素,拼接为 [其余, 块]
|
|
266
|
-
moveBottom() {
|
|
267
|
-
if (this.disabled) return;
|
|
268
|
-
const arr = this.data.slice();
|
|
269
|
-
const block = [];
|
|
270
|
-
const set = new Set(this.selectedIndices);
|
|
271
|
-
for (let i = 0; i < arr.length; i++) {
|
|
272
|
-
if (set.has(i)) block.push(arr[i]);
|
|
273
|
-
}
|
|
274
|
-
const rest = arr.filter((_, i) => !set.has(i));
|
|
275
|
-
const next = rest.concat(block);
|
|
276
|
-
this.emitData(next);
|
|
277
|
-
},
|
|
278
|
-
// 移至:在剩余元素中按目标位置(1-based)插入选中块
|
|
279
|
-
moveTo() {
|
|
280
|
-
if (this.disabled || !this.validTarget) return;
|
|
281
|
-
const arr = this.data.slice();
|
|
282
|
-
const set = new Set(this.selectedIndices);
|
|
283
|
-
const block = [];
|
|
284
|
-
for (let i = 0; i < arr.length; i++) {
|
|
285
|
-
if (set.has(i)) block.push(arr[i]);
|
|
286
|
-
}
|
|
287
|
-
const rest = arr.filter((_, i) => !set.has(i));
|
|
288
|
-
let pos = Number(this.targetIndex) - 1;
|
|
289
|
-
if (pos < 0) pos = 0;
|
|
290
|
-
if (pos > rest.length) pos = rest.length;
|
|
291
|
-
rest.splice(pos, 0, ...block);
|
|
292
|
-
this.emitData(rest);
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
};
|
|
296
|
-
</script>
|
|
297
|
-
<style scoped>
|
|
298
|
-
.dividerLine {
|
|
299
|
-
width: 1px;
|
|
300
|
-
height: 24px;
|
|
301
|
-
background-color: #e4e7ed;
|
|
302
|
-
}
|
|
303
|
-
.sort-toolbar {
|
|
304
|
-
margin-bottom: 12px;
|
|
305
|
-
}
|
|
306
|
-
.actions {
|
|
307
|
-
display: flex;
|
|
308
|
-
align-items: center;
|
|
309
|
-
gap: 12px;
|
|
310
|
-
flex-wrap: wrap;
|
|
311
|
-
}
|
|
312
|
-
.move-to {
|
|
313
|
-
display: flex;
|
|
314
|
-
align-items: center;
|
|
315
|
-
gap: 8px;
|
|
316
|
-
}
|
|
317
|
-
.info {
|
|
318
|
-
color: #909399;
|
|
319
|
-
font-size: 13px;
|
|
320
|
-
}
|
|
321
|
-
</style>
|
package/app.md
DELETED