arms-app 1.0.68 → 1.0.70

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": "arms-app",
3
- "version": "1.0.68",
3
+ "version": "1.0.70",
4
4
  "description": "一个基于 Express 的 Web 应用1",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -5,7 +5,7 @@
5
5
  <h1>列拖拽表格</h1>
6
6
  <p class="desc">通过拖拽表头,使用 sortablejs 实现列换位。</p>
7
7
 
8
- <!-- 表格:通过 columns 数组驱动可拖拽列配置,rows 负责行数据 -->
8
+ <!-- 表格:通过 columns 数组驱动所有可拖拽列(含序号/操作列),rows 负责行数据 -->
9
9
  <el-table
10
10
  ref="draggableTable"
11
11
  :key="tableKey"
@@ -15,13 +15,10 @@
15
15
  style="width: 100%"
16
16
  height="420"
17
17
  >
18
- <!-- 固定的序号列,不参与拖拽换位(无拖拽手柄) -->
19
- <el-table-column type="index" label="#" width="60" />
20
-
21
- <!-- 动态业务列 + 操作列:所有可拖拽的列,由 columns 数组驱动 -->
18
+ <!-- 所有列(包含序号列/业务列/操作列)均由 columns 数组统一驱动,可参与拖拽 -->
22
19
  <el-table-column
23
20
  v-for="col in columns"
24
- :key="col.prop"
21
+ :key="col.key || col.prop"
25
22
  :prop="col.prop"
26
23
  :label="col.label"
27
24
  :width="col.width"
@@ -42,6 +39,9 @@
42
39
  <el-button type="text" size="mini" @click="onEdit(scope.row)">编辑</el-button>
43
40
  <el-button type="text" size="mini" @click="onDelete(scope.row)">删除</el-button>
44
41
  </template>
42
+ <template v-else-if="col.isIndex">
43
+ <span>{{ scope.$index + 1 }}</span>
44
+ </template>
45
45
  <template v-else>
46
46
  <span>{{ scope.row[col.prop] }}</span>
47
47
  </template>
@@ -93,14 +93,15 @@ export default {
93
93
  ],
94
94
  // 列配置:用于驱动 el-table-column 渲染,同时作为拖拽排序的数据源
95
95
  columns: [
96
- { prop: "name", label: "姓名", width: 160, align: "center" },
97
- { prop: "customerTag", label: "客户标签", width: 120, align: "center" },
98
- { prop: "manager", label: "客户人", width: 160, align: "center" },
99
- { prop: "contribution", label: "贡献积分", width: 120, align: "right", headerAlign: "center" },
100
- { prop: "pass1", label: "通关1", width: 100, align: "center" },
101
- { prop: "pass2", label: "通关2", width: 100, align: "center" },
102
- { prop: "pass3", label: "通关3", width: 100, align: "center" },
103
- { prop: "__operation__", label: "操作", width: 180, align: "center", headerAlign: "center", isOperation: true },
96
+ { key: "index", label: "#", width: 60, align: "center", isIndex: true },
97
+ { key: "name", prop: "name", label: "姓名", width: 160, align: "center" },
98
+ { key: "customerTag", prop: "customerTag", label: "客户标签", width: 120, align: "center" },
99
+ { key: "manager", prop: "manager", label: "客户人", width: 160, align: "center" },
100
+ { key: "contribution", prop: "contribution", label: "贡献积分", width: 120, align: "right", headerAlign: "center" },
101
+ { key: "pass1", prop: "pass1", label: "通关1", width: 100, align: "center" },
102
+ { key: "pass2", prop: "pass2", label: "通关2", width: 100, align: "center" },
103
+ { key: "pass3", prop: "pass3", label: "通关3", width: 100, align: "center" },
104
+ { key: "operation", prop: "__operation__", label: "操作", width: 180, align: "center", headerAlign: "center", isOperation: true },
104
105
  ],
105
106
  // sortablejs 实例引用,用于在组件销毁或重建时手动销毁,避免内存泄露
106
107
  headerSortable: null,
@@ -154,12 +155,9 @@ export default {
154
155
  onEnd: (evt) => {
155
156
  const { oldIndex, newIndex } = evt;
156
157
  if (oldIndex == null || newIndex == null) return;
157
-
158
- // 0 列是 index 序号列,从第 1 列开始才对应 columns[0]
159
- if (oldIndex === 0 || newIndex === 0) return;
160
- const from = oldIndex - 1;
161
- const to = newIndex - 1;
162
-
158
+ if (oldIndex === newIndex) return;
159
+ const from = oldIndex;
160
+ const to = newIndex;
163
161
  if (from < 0 || to < 0 || from >= this.columns.length || to >= this.columns.length) return;
164
162
 
165
163
  // 交换 columns 中两列的位置,实现“换列”
@@ -180,30 +178,3 @@ export default {
180
178
  };
181
179
  </script>
182
180
 
183
- <style scoped>
184
- .draggable-columns-table-page {
185
- padding: 24px;
186
- }
187
- h1 {
188
- margin: 0 0 12px;
189
- font-size: 20px;
190
- }
191
- .desc {
192
- margin: 0 0 16px;
193
- color: #666;
194
- }
195
- .header-draggable {
196
- display: inline-flex;
197
- align-items: center;
198
- cursor: move;
199
- user-select: none;
200
- }
201
- .drag-icon {
202
- margin-right: 4px;
203
- font-size: 14px;
204
- color: #999;
205
- }
206
- .header-label {
207
- white-space: nowrap;
208
- }
209
- </style>
@@ -15,28 +15,21 @@
15
15
  size="small"
16
16
  style="width: 100%"
17
17
  >
18
- <!-- 序号列 -->
19
- <el-table-column type="index" label="序号" width="60" align="center" header-align="center" />
20
- <!-- 基础信息列 -->
21
- <el-table-column prop="name" label="人员姓名" min-width="100" align="center" header-align="center" />
22
- <el-table-column label="性别" width="80" align="center" header-align="center">
23
- <template slot-scope="scope">
18
+ <el-table-column
19
+ v-for="col in columns"
20
+ :key="col.key || col.prop || col.type || col.label"
21
+ :type="col.type"
22
+ :prop="col.prop"
23
+ :label="col.label"
24
+ :width="col.width"
25
+ :min-width="col.minWidth"
26
+ :align="col.align"
27
+ :header-align="col.headerAlign"
28
+ >
29
+ <template v-if="col.slot === 'sex'" slot-scope="scope">
24
30
  {{ formatSex(scope.row.sex) }}
25
31
  </template>
26
- </el-table-column>
27
- <el-table-column prop="postName" label="岗位" min-width="80" align="center" header-align="center" />
28
- <el-table-column prop="happenPerio" label="入行日期" min-width="110" align="center" header-align="center" />
29
- <el-table-column prop="telNo" label="联系方式" min-width="130" align="center" header-align="center" />
30
- <el-table-column prop="dtlAddr" label="现居住地" min-width="120" align="center" header-align="center" />
31
- <el-table-column prop="emergContactPersonName" label="紧急联系人" min-width="100" align="center" header-align="center" />
32
- <el-table-column prop="emergContactPhoneNo" label="紧急联系方式" min-width="130" align="center" header-align="center" />
33
- <el-table-column prop="birthDt" label="出生日期" min-width="110" align="center" header-align="center" />
34
- <el-table-column prop="最高EduDegreeDesc" label="最高学历" min-width="90" align="center" header-align="center" />
35
- <el-table-column prop="createTm" label="创建日期" min-width="110" align="center" header-align="center" />
36
- <el-table-column prop="rmk" label="备注" min-width="120" align="center" header-align="center" />
37
- <!-- 操作列:详情、编辑、删除 -->
38
- <el-table-column label="操作" width="200" align="center" header-align="center">
39
- <template slot-scope="scope">
32
+ <template v-else-if="col.slot === 'operation'" slot-scope="scope">
40
33
  <el-button type="text" size="mini" @click="onView(scope.row)">详情</el-button>
41
34
  <el-button type="text" size="mini" @click="onEdit(scope.row)">编辑</el-button>
42
35
  <el-button type="text" size="mini" @click="onRemove(scope.row)">删除</el-button>
@@ -250,6 +243,22 @@ export default {
250
243
  delFlag: "0",
251
244
  },
252
245
  ],
246
+ columns: [
247
+ { key: "index", type: "index", label: "序号", width: 60, align: "center", headerAlign: "center" },
248
+ { key: "name", prop: "name", label: "人员姓名", minWidth: 100, align: "center", headerAlign: "center" },
249
+ { key: "sex", label: "性别", width: 80, align: "center", headerAlign: "center", slot: "sex" },
250
+ { key: "postName", prop: "postName", label: "岗位", minWidth: 80, align: "center", headerAlign: "center" },
251
+ { key: "happenPerio", prop: "happenPerio", label: "入行日期", minWidth: 110, align: "center", headerAlign: "center" },
252
+ { key: "telNo", prop: "telNo", label: "联系方式", minWidth: 130, align: "center", headerAlign: "center" },
253
+ { key: "dtlAddr", prop: "dtlAddr", label: "现居住地", minWidth: 120, align: "center", headerAlign: "center" },
254
+ { key: "emergContactPersonName", prop: "emergContactPersonName", label: "紧急联系人", minWidth: 100, align: "center", headerAlign: "center" },
255
+ { key: "emergContactPhoneNo", prop: "emergContactPhoneNo", label: "紧急联系方式", minWidth: 130, align: "center", headerAlign: "center" },
256
+ { key: "birthDt", prop: "birthDt", label: "出生日期", minWidth: 110, align: "center", headerAlign: "center" },
257
+ { key: "highestEduDegreeDesc", prop: "highestEduDegreeDesc", label: "最高学历", minWidth: 90, align: "center", headerAlign: "center" },
258
+ { key: "createTm", prop: "createTm", label: "创建日期", minWidth: 110, align: "center", headerAlign: "center" },
259
+ { key: "rmk", prop: "rmk", label: "备注", minWidth: 120, align: "center", headerAlign: "center" },
260
+ { key: "operation", label: "操作", width: 200, align: "center", headerAlign: "center", slot: "operation" },
261
+ ],
253
262
  // 弹窗显隐状态
254
263
  dialogVisible: false,
255
264
  // 弹窗模式:add 新增 / edit 编辑 / view 查看
package/view/1.js DELETED
@@ -1,23 +0,0 @@
1
- // vue.config.js
2
- const { merge } = require('webpack-merge');
3
- const baseConfig = require('./build/webpack.base.config');
4
- const baseProxy = require('./build/base.proxy');
5
- const persionProxy = require('./build/persio n.proxy');
6
-
7
- module.exports = merge(baseConfig, {
8
- devServer: {
9
- historyApiFallback: true,
10
- port: 8081,
11
- proxy: {
12
- ...baseProxy,
13
- ...persionProxy,
14
- },
15
- },
16
- // 新增:sourcemap 配置
17
- configureWebpack: {
18
- // 根据环境自动切换 sourcemap
19
- devtool: process.env.NODE_ENV === 'development'
20
- ? 'cheap-module-source-map' // 开发环境
21
- : false // 生产环境
22
- }
23
- });
package/view/1.vue DELETED
@@ -1,117 +0,0 @@
1
- <template>
2
- <div class="page">
3
- <h3>悬停停自动,手动仍可滚(无缝)</h3>
4
-
5
- <vue-seamless-scroll
6
- ref="ss"
7
- class="warp"
8
- :data="list"
9
- :class-option="option"
10
- @mouseenter.native="hovering = true"
11
- @mouseleave.native="hovering = false"
12
- @mousewheel.native.prevent="onWheel"
13
- @touchstart.native.passive="onTouchStart"
14
- @touchmove.native.prevent="onTouchMove"
15
- >
16
- <div class="list">
17
- <div class="item" v-for="(item, index) in list" :key="index">
18
- {{ item }}
19
- </div>
20
- </div>
21
- </vue-seamless-scroll>
22
- </div>
23
- </template>
24
-
25
- <script>
26
- import VueSeamlessScroll from "vue-seamless-scroll";
27
-
28
- export default {
29
- name: "SeamlessHoverManualView",
30
- components: {
31
- VueSeamlessScroll,
32
- },
33
- data() {
34
- return {
35
- list: [],
36
- lastTouchY: 0,
37
- hovering: false,
38
- autoStep: 0.3,
39
- };
40
- },
41
- computed: {
42
- option() {
43
- return {
44
- step: this.hovering ? 0 : this.autoStep,
45
- direction: 1,
46
- hoverStop: false,
47
- limitMoveNum: 1,
48
- openWatch: true,
49
- };
50
- },
51
- },
52
- mounted() {
53
- this.fetchData();
54
- },
55
- methods: {
56
- async fetchData() {
57
- // 模拟接口请求
58
- console.log('开始获取数据...');
59
- await new Promise((resolve) => setTimeout(resolve, 1000));
60
- const data = Array.from({ length: 5000 }, (_, i) => `列表项 ${i + 1}`);
61
- // 使用 Object.freeze 冻结数据,避免 Vue 深度监听,显著提升大数据量下的渲染性能
62
- this.list = Object.freeze(data);
63
- console.log('数据获取完成,共', this.list.length, '条');
64
- this.$nextTick(() => {
65
- if (this.$refs.ss && this.$refs.ss.reset) this.$refs.ss.reset();
66
- });
67
- },
68
- onWheel(e) {
69
- const ss = this.$refs.ss;
70
- if (!ss) return;
71
-
72
- ss.yPos -= e.deltaY;
73
-
74
- if (ss.yPos > 0) ss.yPos = 0;
75
-
76
- if (ss.realBoxHeight && Math.abs(ss.yPos) > ss.realBoxHeight / 2) {
77
- ss.yPos = 0;
78
- }
79
- },
80
- onTouchStart(e) {
81
- this.lastTouchY = e.touches[0].clientY;
82
- },
83
- onTouchMove(e) {
84
- const y = e.touches[0].clientY;
85
- const delta = this.lastTouchY - y;
86
- this.lastTouchY = y;
87
- this.onWheel({ deltaY: delta });
88
- },
89
- },
90
- };
91
- </script>
92
-
93
- <style scoped>
94
- .page {
95
- padding: 20px;
96
- font-family: Arial, Helvetica, sans-serif;
97
- }
98
-
99
- .warp {
100
- height: 260px;
101
- overflow: hidden;
102
- border: 1px solid #ccc;
103
- border-radius: 4px;
104
- }
105
-
106
- .list {
107
- padding: 0 12px;
108
- }
109
-
110
- .item {
111
- height: 40px;
112
- line-height: 40px;
113
- border-bottom: 1px dashed #ddd;
114
- box-sizing: border-box;
115
- }
116
- </style>
117
-
package/view/111.js DELETED
@@ -1,35 +0,0 @@
1
- function deepEqual(a, b, seen = new WeakMap()) {
2
- if (Object.is(a, b)) return true
3
-
4
- if (
5
- typeof a !== 'object' ||
6
- typeof b !== 'object' ||
7
- a === null ||
8
- b === null
9
- ) return false
10
-
11
- if (seen.get(a) === b) return true
12
- seen.set(a, b)
13
-
14
- if (a.constructor !== b.constructor) return false
15
-
16
- if (a instanceof Date) {
17
- return a.getTime() === b.getTime()
18
- }
19
-
20
- if (a instanceof RegExp) {
21
- return a.source === b.source && a.flags === b.flags
22
- }
23
-
24
- const keysA = Reflect.ownKeys(a)
25
- const keysB = Reflect.ownKeys(b)
26
-
27
- if (keysA.length !== keysB.length) return false
28
-
29
- for (const key of keysA) {
30
- if (!keysB.includes(key)) return false
31
- if (!deepEqual(a[key], b[key], seen)) return false
32
- }
33
-
34
- return true
35
- }
package/view/2.js DELETED
@@ -1,90 +0,0 @@
1
- function traverseChildren(arr) {
2
- if (!Array.isArray(arr)) return;
3
-
4
- for (let i = 0; i < arr.length; i++) {
5
- const item = arr[i];
6
-
7
- // 处理当前节点
8
- console.log('当前节点:', item.name || item.id);
9
-
10
- // 如果有children,递归遍历
11
- if (item.children && Array.isArray(item.children) && item.children.length > 0) {
12
- console.log(`进入 ${item.name} 的子节点:`);
13
- traverseChildren(item.children);
14
- console.log(`离开 ${item.name} 的子节点`);
15
- }
16
- }
17
- }
18
- function getAllNodes(arr) {
19
- let result = [];
20
-
21
- function traverse(nodes) {
22
- if (!Array.isArray(nodes)) return;
23
-
24
- for (let i = 0; i < nodes.length; i++) {
25
- const node = nodes[i];
26
-
27
- // 将当前节点添加到结果数组
28
- result.push(node);
29
-
30
- // 递归处理子节点
31
- if (node.children && Array.isArray(node.children)) {
32
- traverse(node.children);
33
- }
34
- }
35
- }
36
-
37
- traverse(arr);
38
- return result;
39
- }
40
-
41
- function flattenIterative(arr, childrenKey = 'children') {
42
- const result = [];
43
- const stack = [...arr]; // 使用栈来模拟递归
44
-
45
- while (stack.length > 0) {
46
- const node = stack.pop();
47
-
48
- if (!node) continue;
49
-
50
- // 添加当前节点
51
- result.push(node);
52
-
53
- // 将子节点逆序压入栈中(保持原顺序)
54
- const children = node[childrenKey];
55
- if (Array.isArray(children) && children.length > 0) {
56
- // 从后往前压栈,这样前面出来的顺序是正确的
57
- for (let i = children.length - 1; i >= 0; i--) {
58
- stack.push(children[i]);
59
- }
60
- }
61
- }
62
-
63
- return result;
64
- }
65
-
66
-
67
-
68
- //
69
- function getAllOrgIds(arr) {
70
- let result = [];
71
-
72
- function traverse(nodes) {
73
- if (!Array.isArray(nodes)) return;
74
-
75
- for (let node of nodes) {
76
- // 如果当前节点有 orgid,添加到结果中
77
- if (node.orgid !== undefined && node.orgid !== null) {
78
- result.push(node.orgid);
79
- }
80
-
81
- // 如果当前节点有 children,递归遍历
82
- if (node.children && Array.isArray(node.children) && node.children.length > 0) {
83
- traverse(node.children);
84
- }
85
- }
86
- }
87
-
88
- traverse(arr);
89
- return result;
90
- }
package/view/2.vue DELETED
@@ -1,129 +0,0 @@
1
- <template>
2
- <el-dialog
3
- :visible.sync="visibleInner"
4
- title="列表"
5
- width="800px"
6
- :close-on-click-modal="false"
7
- @open="onOpen"
8
- @closed="onClosed"
9
- >
10
- <!-- 表格只渲染当前页 rows(10条) -->
11
- <el-table
12
- :data="rows"
13
- height="400"
14
- border
15
- row-key="id"
16
- >
17
- <el-table-column prop="id" label="ID" width="120" />
18
- <el-table-column prop="name" label="名称" />
19
- <el-table-column prop="status" label="状态" width="120" />
20
- </el-table>
21
-
22
- <div style="margin-top: 12px; text-align: right;">
23
- <el-pagination
24
- background
25
- layout="total, prev, pager, next, sizes"
26
- :total="total"
27
- :current-page.sync="page"
28
- :page-size.sync="pageSize"
29
- :page-sizes="[10, 20, 50, 100]"
30
- @current-change="refreshPage"
31
- @size-change="onSizeChange"
32
- />
33
- </div>
34
-
35
- <span slot="footer" class="dialog-footer">
36
- <el-button @click="close">关闭</el-button>
37
- </span>
38
- </el-dialog>
39
- </template>
40
-
41
- <script>
42
- export default {
43
- name: "ChildDialog",
44
- props: {
45
- // 只控制显隐,不要用 props 传 5000 条
46
- visible: { type: Boolean, default: false }
47
- },
48
- data() {
49
- return {
50
- // 内部 visible(避免直接改 props)
51
- visibleInner: false,
52
-
53
- // 只存 UI 需要的(轻量)
54
- page: 1,
55
- pageSize: 10,
56
- total: 0,
57
- rows: []
58
- };
59
- },
60
-
61
- watch: {
62
- // 父组件控制打开/关闭
63
- visible: {
64
- immediate: true,
65
- handler(v) {
66
- this.visibleInner = v;
67
- }
68
- },
69
- // 同步回父组件(:visible.sync)
70
- visibleInner(v) {
71
- this.$emit("update:visible", v);
72
- }
73
- },
74
-
75
- created() {
76
- // 非响应式大数据容器(不要放 data 里)
77
- this._rawList = [];
78
- this._hasData = false;
79
- },
80
-
81
- methods: {
82
- /** 父组件 open 后调用:this.$refs.dlg.setData(bigList) */
83
- setData(list) {
84
- // 关键:不放 data,不做深拷贝,不 deep watch
85
- this._rawList = Array.isArray(list) ? list : [];
86
- this._hasData = true;
87
-
88
- // 初始化分页
89
- this.page = 1;
90
- this.total = this._rawList.length;
91
-
92
- // 如果 dialog 已经打开,立刻刷新;否则等 onOpen 再刷
93
- if (this.visibleInner) this.refreshPage();
94
- },
95
-
96
- onOpen() {
97
- // dialog 打开后,如果数据已设置,刷新一次(渲染10条)
98
- if (this._hasData) this.refreshPage();
99
- },
100
-
101
- onClosed() {
102
- // 关闭时只清理轻量状态,避免销毁/重建造成卡顿
103
- this.rows = [];
104
- this.page = 1;
105
- // 如果你希望下次打开仍复用数据,就别清 _rawList
106
- // this._rawList = [];
107
- // this._hasData = false;
108
- },
109
-
110
- refreshPage() {
111
- const start = (this.page - 1) * this.pageSize;
112
- const end = start + this.pageSize;
113
-
114
- // 只 slice 当前页,rows 才是响应式的
115
- this.rows = this._rawList.slice(start, end);
116
- this.total = this._rawList.length;
117
- },
118
-
119
- onSizeChange() {
120
- this.page = 1;
121
- this.refreshPage();
122
- },
123
-
124
- close() {
125
- this.visibleInner = false;
126
- }
127
- }
128
- };
129
- </script>