arms-app 1.0.78 → 1.0.80
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 +1 -1
- package/public/1.d +24 -0
- package/view/5.d +0 -422
package/package.json
CHANGED
package/public/1.d
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function listToTree(list) {
|
|
2
|
+
const map = new Map();
|
|
3
|
+
const tree = [];
|
|
4
|
+
|
|
5
|
+
// 1. 初始化 map
|
|
6
|
+
list.forEach(item => {
|
|
7
|
+
map.set(item.id, { ...item, children: [] });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// 2. 组装父子关系
|
|
11
|
+
list.forEach(item => {
|
|
12
|
+
const node = map.get(item.id);
|
|
13
|
+
const parentId = item.parentId;
|
|
14
|
+
|
|
15
|
+
// parentId 为 null / undefined / 不存在于 map → 根节点
|
|
16
|
+
if (parentId == null || !map.has(parentId)) {
|
|
17
|
+
tree.push(node);
|
|
18
|
+
} else {
|
|
19
|
+
map.get(parentId).children.push(node);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return tree;
|
|
24
|
+
}
|
package/view/5.d
DELETED
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<el-dialog
|
|
3
|
-
title="发放"
|
|
4
|
-
:visible.sync="dialogVisible"
|
|
5
|
-
width="980px"
|
|
6
|
-
:close-on-click-modal="false"
|
|
7
|
-
@close="onClose"
|
|
8
|
-
>
|
|
9
|
-
<div class="issue-body">
|
|
10
|
-
<div class="issue-col">
|
|
11
|
-
<div class="issue-col-head">
|
|
12
|
-
<div class="issue-col-title">待选列表</div>
|
|
13
|
-
<div class="issue-col-count">
|
|
14
|
-
{{ issueLeftSelected.length }}/{{ issueLeftTotal }}
|
|
15
|
-
</div>
|
|
16
|
-
</div>
|
|
17
|
-
<el-input
|
|
18
|
-
v-model="issueLeftKeyword"
|
|
19
|
-
size="small"
|
|
20
|
-
placeholder="请输入"
|
|
21
|
-
clearable
|
|
22
|
-
prefix-icon="el-icon-search"
|
|
23
|
-
class="issue-search"
|
|
24
|
-
/>
|
|
25
|
-
<el-table
|
|
26
|
-
ref="issueLeftTable"
|
|
27
|
-
:data="issueLeftPageRows"
|
|
28
|
-
border
|
|
29
|
-
stripe
|
|
30
|
-
size="small"
|
|
31
|
-
height="380"
|
|
32
|
-
@selection-change="onIssueLeftSelectionChange"
|
|
33
|
-
>
|
|
34
|
-
<el-table-column type="selection" width="46" />
|
|
35
|
-
<el-table-column label="人员">
|
|
36
|
-
<template slot-scope="scope">
|
|
37
|
-
{{ formatUserLabel(scope.row) }}
|
|
38
|
-
</template>
|
|
39
|
-
</el-table-column>
|
|
40
|
-
</el-table>
|
|
41
|
-
<div class="issue-pagination">
|
|
42
|
-
<el-pagination
|
|
43
|
-
small
|
|
44
|
-
:current-page="issueLeftPage"
|
|
45
|
-
:page-size="issueLeftPageSize"
|
|
46
|
-
layout="total, prev, pager, next"
|
|
47
|
-
:total="issueLeftTotal"
|
|
48
|
-
@current-change="onIssueLeftPageChange"
|
|
49
|
-
/>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
|
|
53
|
-
<div class="issue-transfer">
|
|
54
|
-
<el-button
|
|
55
|
-
size="mini"
|
|
56
|
-
type="primary"
|
|
57
|
-
:disabled="issueLeftSelected.length === 0"
|
|
58
|
-
@click="onIssueMoveToRight"
|
|
59
|
-
>
|
|
60
|
-
<i class="el-icon-arrow-right" />
|
|
61
|
-
</el-button>
|
|
62
|
-
<el-button
|
|
63
|
-
size="mini"
|
|
64
|
-
type="primary"
|
|
65
|
-
:disabled="issueRightSelected.length === 0"
|
|
66
|
-
@click="onIssueMoveToLeft"
|
|
67
|
-
>
|
|
68
|
-
<i class="el-icon-arrow-left" />
|
|
69
|
-
</el-button>
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
<div class="issue-col">
|
|
73
|
-
<div class="issue-col-head">
|
|
74
|
-
<div class="issue-col-title">当前佩戴</div>
|
|
75
|
-
<div class="issue-col-count">
|
|
76
|
-
{{ issueSelectedUsers.length }}/{{ limitCount === -1 ? '不限' : limitCount }}
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
<el-input
|
|
80
|
-
v-model="issueRightKeyword"
|
|
81
|
-
size="small"
|
|
82
|
-
placeholder="请输入"
|
|
83
|
-
clearable
|
|
84
|
-
prefix-icon="el-icon-search"
|
|
85
|
-
class="issue-search"
|
|
86
|
-
/>
|
|
87
|
-
<el-table
|
|
88
|
-
ref="issueRightTable"
|
|
89
|
-
:data="issueRightPageRows"
|
|
90
|
-
border
|
|
91
|
-
stripe
|
|
92
|
-
size="small"
|
|
93
|
-
height="380"
|
|
94
|
-
@selection-change="onIssueRightSelectionChange"
|
|
95
|
-
>
|
|
96
|
-
<el-table-column type="selection" width="46" />
|
|
97
|
-
<el-table-column label="人员">
|
|
98
|
-
<template slot-scope="scope">
|
|
99
|
-
{{ formatUserLabel(scope.row) }}
|
|
100
|
-
</template>
|
|
101
|
-
</el-table-column>
|
|
102
|
-
</el-table>
|
|
103
|
-
<div class="issue-pagination">
|
|
104
|
-
<el-pagination
|
|
105
|
-
small
|
|
106
|
-
:current-page="issueRightPage"
|
|
107
|
-
:page-size="issueRightPageSize"
|
|
108
|
-
layout="total, prev, pager, next"
|
|
109
|
-
:total="issueRightTotal"
|
|
110
|
-
@current-change="onIssueRightPageChange"
|
|
111
|
-
/>
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
<span slot="footer" class="dialog-footer">
|
|
116
|
-
<el-button size="small" @click="onCancel">取消</el-button>
|
|
117
|
-
<el-button size="small" type="primary" @click="onConfirm">
|
|
118
|
-
发放
|
|
119
|
-
</el-button>
|
|
120
|
-
</span>
|
|
121
|
-
</el-dialog>
|
|
122
|
-
</template>
|
|
123
|
-
|
|
124
|
-
<script>
|
|
125
|
-
import { get } from "../utils/request";
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 勋章发放弹窗组件
|
|
129
|
-
* 用于管理勋章的发放对象,支持左右穿梭框选择用户
|
|
130
|
-
*/
|
|
131
|
-
export default {
|
|
132
|
-
name: "MedalIssueDialog",
|
|
133
|
-
props: {
|
|
134
|
-
// 控制弹窗显示
|
|
135
|
-
visible: {
|
|
136
|
-
type: Boolean,
|
|
137
|
-
default: false
|
|
138
|
-
},
|
|
139
|
-
limitCount: {
|
|
140
|
-
type: Number,
|
|
141
|
-
default: -1
|
|
142
|
-
},
|
|
143
|
-
// 当前操作的勋章对象
|
|
144
|
-
medal: {
|
|
145
|
-
type: Object,
|
|
146
|
-
default: null
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
data() {
|
|
150
|
-
return {
|
|
151
|
-
issueUserPool: [], // 所有可用用户列表
|
|
152
|
-
issueSelectedUsers: [], // 当前勋章已选中的用户列表
|
|
153
|
-
issueLeftKeyword: "", // 左侧(待选)搜索关键字
|
|
154
|
-
issueRightKeyword: "", // 右侧(已选)搜索关键字
|
|
155
|
-
issueLeftPage: 1, // 左侧当前页码
|
|
156
|
-
issueRightPage: 1, // 右侧当前页码
|
|
157
|
-
issueLeftPageSize: 10, // 左侧每页显示数量
|
|
158
|
-
issueRightPageSize: 10, // 右侧每页显示数量
|
|
159
|
-
issueLeftSelected: [], // 左侧表格当前勾选的行(准备移入右侧)
|
|
160
|
-
issueRightSelected: [] // 右侧表格当前勾选的行(准备移出右侧)
|
|
161
|
-
};
|
|
162
|
-
},
|
|
163
|
-
computed: {
|
|
164
|
-
dialogVisible: {
|
|
165
|
-
get() {
|
|
166
|
-
return this.visible;
|
|
167
|
-
},
|
|
168
|
-
set(val) {
|
|
169
|
-
this.$emit("update:visible", val);
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
// 左侧待选列表总数
|
|
173
|
-
issueLeftTotal() {
|
|
174
|
-
return this.issueLeftFiltered.length;
|
|
175
|
-
},
|
|
176
|
-
// 右侧已选列表总数
|
|
177
|
-
issueRightTotal() {
|
|
178
|
-
return this.issueRightFiltered.length;
|
|
179
|
-
},
|
|
180
|
-
// 左侧过滤后的列表(排除已选用户,并根据关键字过滤)
|
|
181
|
-
issueLeftFiltered() {
|
|
182
|
-
const selectedSet = new Set((this.issueSelectedUsers || []).map(u => u.userNo));
|
|
183
|
-
const keyword = (this.issueLeftKeyword || "").trim();
|
|
184
|
-
const pool = Array.isArray(this.issueUserPool) ? this.issueUserPool : [];
|
|
185
|
-
// 过滤掉已选中的用户
|
|
186
|
-
const list = pool.filter(u => !selectedSet.has(u.userNo));
|
|
187
|
-
if (!keyword) return list;
|
|
188
|
-
// 根据关键字过滤
|
|
189
|
-
return list.filter(u => this.formatUserLabel(u).includes(keyword));
|
|
190
|
-
},
|
|
191
|
-
// 右侧过滤后的列表(根据关键字过滤)
|
|
192
|
-
issueRightFiltered() {
|
|
193
|
-
const keyword = (this.issueRightKeyword || "").trim();
|
|
194
|
-
const list = Array.isArray(this.issueSelectedUsers) ? this.issueSelectedUsers : [];
|
|
195
|
-
if (!keyword) return list;
|
|
196
|
-
return list.filter(u => this.formatUserLabel(u).includes(keyword));
|
|
197
|
-
},
|
|
198
|
-
// 左侧当前页显示的数据
|
|
199
|
-
issueLeftPageRows() {
|
|
200
|
-
const start = (this.issueLeftPage - 1) * this.issueLeftPageSize;
|
|
201
|
-
return this.issueLeftFiltered.slice(start, start + this.issueLeftPageSize);
|
|
202
|
-
},
|
|
203
|
-
// 右侧当前页显示的数据
|
|
204
|
-
issueRightPageRows() {
|
|
205
|
-
const start = (this.issueRightPage - 1) * this.issueRightPageSize;
|
|
206
|
-
return this.issueRightFiltered.slice(start, start + this.issueRightPageSize);
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
watch: {
|
|
210
|
-
// 监听 visible 变化,为 true 时初始化数据
|
|
211
|
-
visible: {
|
|
212
|
-
handler(val) {
|
|
213
|
-
if (val) this.init();
|
|
214
|
-
console.log(this.medal)
|
|
215
|
-
},
|
|
216
|
-
immediate: true
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
methods: {
|
|
220
|
-
/**
|
|
221
|
-
* 初始化数据
|
|
222
|
-
* 1. 确保用户池数据已加载
|
|
223
|
-
* 2. 根据服务端返回的已发放用户初始化选中状态
|
|
224
|
-
*/
|
|
225
|
-
async init() {
|
|
226
|
-
const serverIssuedUsers = await this.ensureIssueUserPool();
|
|
227
|
-
const row = this.medal;
|
|
228
|
-
if (!row) return;
|
|
229
|
-
|
|
230
|
-
this.issueSelectedUsers = (serverIssuedUsers || []).map(u => ({ ...u }));
|
|
231
|
-
|
|
232
|
-
// 重置搜索和分页状态
|
|
233
|
-
this.issueLeftKeyword = "";
|
|
234
|
-
this.issueRightKeyword = "";
|
|
235
|
-
this.issueLeftPage = 1;
|
|
236
|
-
this.issueRightPage = 1;
|
|
237
|
-
this.issueLeftSelected = [];
|
|
238
|
-
this.issueRightSelected = [];
|
|
239
|
-
// 清空表格选中状态
|
|
240
|
-
this.$nextTick(() => {
|
|
241
|
-
this.$refs.issueLeftTable && this.$refs.issueLeftTable.clearSelection && this.$refs.issueLeftTable.clearSelection();
|
|
242
|
-
this.$refs.issueRightTable && this.$refs.issueRightTable.clearSelection && this.$refs.issueRightTable.clearSelection();
|
|
243
|
-
});
|
|
244
|
-
},
|
|
245
|
-
// 格式化用户显示名称 (姓名/工号)
|
|
246
|
-
formatUserLabel(user) {
|
|
247
|
-
if (!user) return "";
|
|
248
|
-
const name = user.displayName || "";
|
|
249
|
-
const no = user.userNo || "";
|
|
250
|
-
if (name && no) return `${name}/${no}`;
|
|
251
|
-
return name || no || "";
|
|
252
|
-
},
|
|
253
|
-
// 确保加载所有用户列表数据,并返回已发放用户
|
|
254
|
-
async ensureIssueUserPool() {
|
|
255
|
-
try {
|
|
256
|
-
const res = await get('/medal/users');
|
|
257
|
-
if (res) {
|
|
258
|
-
// 兼容新旧接口结构
|
|
259
|
-
if (res.allUsers) {
|
|
260
|
-
this.issueUserPool = res.allUsers;
|
|
261
|
-
return res.issuedUsers || [];
|
|
262
|
-
} else if (Array.isArray(res)) {
|
|
263
|
-
this.issueUserPool = res;
|
|
264
|
-
return [];
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
} catch (err) {
|
|
268
|
-
console.error(err);
|
|
269
|
-
this.$message.error('获取用户列表失败');
|
|
270
|
-
}
|
|
271
|
-
return [];
|
|
272
|
-
},
|
|
273
|
-
// 关闭弹窗时的处理
|
|
274
|
-
onClose() {
|
|
275
|
-
this.issueLeftSelected = [];
|
|
276
|
-
this.issueRightSelected = [];
|
|
277
|
-
this.$nextTick(() => {
|
|
278
|
-
this.$refs.issueLeftTable && this.$refs.issueLeftTable.clearSelection && this.$refs.issueLeftTable.clearSelection();
|
|
279
|
-
this.$refs.issueRightTable && this.$refs.issueRightTable.clearSelection && this.$refs.issueRightTable.clearSelection();
|
|
280
|
-
});
|
|
281
|
-
this.$emit('update:visible', false);
|
|
282
|
-
},
|
|
283
|
-
// 点击取消
|
|
284
|
-
onCancel() {
|
|
285
|
-
this.$emit('update:visible', false);
|
|
286
|
-
},
|
|
287
|
-
// 点击确定,校验并发射 confirm 事件
|
|
288
|
-
onConfirm() {
|
|
289
|
-
const medal = this.medal;
|
|
290
|
-
if (!medal) return;
|
|
291
|
-
const total = this.issueSelectedUsers.length;
|
|
292
|
-
// 校验限量勋章的数量限制
|
|
293
|
-
if (this.limitCount !== -1 && total > this.limitCount) {
|
|
294
|
-
this.$message({
|
|
295
|
-
type: "warning",
|
|
296
|
-
message: `超出发放数量上限(上限:${this.limitCount})`
|
|
297
|
-
});
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
this.$emit('confirm', {
|
|
302
|
-
medal,
|
|
303
|
-
users: this.issueSelectedUsers
|
|
304
|
-
});
|
|
305
|
-
},
|
|
306
|
-
// 左侧表格选中变化
|
|
307
|
-
onIssueLeftSelectionChange(list) {
|
|
308
|
-
this.issueLeftSelected = Array.isArray(list) ? list.slice() : [];
|
|
309
|
-
},
|
|
310
|
-
// 右侧表格选中变化
|
|
311
|
-
onIssueRightSelectionChange(list) {
|
|
312
|
-
this.issueRightSelected = Array.isArray(list) ? list.slice() : [];
|
|
313
|
-
},
|
|
314
|
-
// 将左侧选中用户移动到右侧(添加)
|
|
315
|
-
onIssueMoveToRight() {
|
|
316
|
-
const selected = Array.isArray(this.issueLeftSelected) ? this.issueLeftSelected : [];
|
|
317
|
-
if (selected.length === 0) return;
|
|
318
|
-
|
|
319
|
-
// 预先计算添加后的总数
|
|
320
|
-
const currentCount = this.issueSelectedUsers.length;
|
|
321
|
-
// 过滤掉已经在右侧的用户,计算实际新增数量
|
|
322
|
-
const existingIds = new Set(this.issueSelectedUsers.map(u => u.userNo));
|
|
323
|
-
const newUsers = selected.filter(u => !existingIds.has(u.userNo));
|
|
324
|
-
|
|
325
|
-
if (this.limitCount !== -1 && (currentCount + newUsers.length) > this.limitCount) {
|
|
326
|
-
this.$message({
|
|
327
|
-
type: "warning",
|
|
328
|
-
message: `选择的用户数量超出限制(上限:${this.limitCount},当前已选:${currentCount},尝试添加:${newUsers.length})`
|
|
329
|
-
});
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const map = new Map((this.issueSelectedUsers || []).map(u => [u.userNo, u]));
|
|
334
|
-
selected.forEach(u => {
|
|
335
|
-
if (!u || !u.userNo) return;
|
|
336
|
-
if (!map.has(u.userNo)) map.set(u.userNo, { ...u });
|
|
337
|
-
});
|
|
338
|
-
this.issueSelectedUsers = Array.from(map.values());
|
|
339
|
-
this.issueLeftSelected = [];
|
|
340
|
-
this.$nextTick(() => {
|
|
341
|
-
this.$refs.issueLeftTable && this.$refs.issueLeftTable.clearSelection && this.$refs.issueLeftTable.clearSelection();
|
|
342
|
-
});
|
|
343
|
-
// 调整分页如果当前页为空
|
|
344
|
-
const maxPage = Math.max(1, Math.ceil(this.issueLeftTotal / this.issueLeftPageSize));
|
|
345
|
-
if (this.issueLeftPage > maxPage) this.issueLeftPage = maxPage;
|
|
346
|
-
},
|
|
347
|
-
// 将右侧选中用户移动到左侧(移除)
|
|
348
|
-
onIssueMoveToLeft() {
|
|
349
|
-
const selected = Array.isArray(this.issueRightSelected) ? this.issueRightSelected : [];
|
|
350
|
-
if (selected.length === 0) return;
|
|
351
|
-
const removeSet = new Set(selected.map(u => u.userNo));
|
|
352
|
-
this.issueSelectedUsers = (this.issueSelectedUsers || []).filter(u => !removeSet.has(u.userNo));
|
|
353
|
-
this.issueRightSelected = [];
|
|
354
|
-
this.$nextTick(() => {
|
|
355
|
-
this.$refs.issueRightTable && this.$refs.issueRightTable.clearSelection && this.$refs.issueRightTable.clearSelection();
|
|
356
|
-
});
|
|
357
|
-
// 调整分页
|
|
358
|
-
const maxPage = Math.max(1, Math.ceil(this.issueRightTotal / this.issueRightPageSize));
|
|
359
|
-
if (this.issueRightPage > maxPage) this.issueRightPage = maxPage;
|
|
360
|
-
},
|
|
361
|
-
// 左侧分页变化
|
|
362
|
-
onIssueLeftPageChange(page) {
|
|
363
|
-
this.issueLeftPage = page;
|
|
364
|
-
this.issueLeftSelected = [];
|
|
365
|
-
this.$nextTick(() => {
|
|
366
|
-
this.$refs.issueLeftTable && this.$refs.issueLeftTable.clearSelection && this.$refs.issueLeftTable.clearSelection();
|
|
367
|
-
});
|
|
368
|
-
},
|
|
369
|
-
// 右侧分页变化
|
|
370
|
-
onIssueRightPageChange(page) {
|
|
371
|
-
this.issueRightPage = page;
|
|
372
|
-
this.issueRightSelected = [];
|
|
373
|
-
this.$nextTick(() => {
|
|
374
|
-
this.$refs.issueRightTable && this.$refs.issueRightTable.clearSelection && this.$refs.issueRightTable.clearSelection();
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
</script>
|
|
380
|
-
|
|
381
|
-
<style scoped>
|
|
382
|
-
.issue-body {
|
|
383
|
-
display: flex;
|
|
384
|
-
align-items: flex-start;
|
|
385
|
-
gap: 12px;
|
|
386
|
-
}
|
|
387
|
-
.issue-col {
|
|
388
|
-
flex: 1;
|
|
389
|
-
min-width: 0;
|
|
390
|
-
}
|
|
391
|
-
.issue-col-head {
|
|
392
|
-
display: flex;
|
|
393
|
-
align-items: center;
|
|
394
|
-
justify-content: space-between;
|
|
395
|
-
margin-bottom: 8px;
|
|
396
|
-
}
|
|
397
|
-
.issue-col-title {
|
|
398
|
-
font-size: 14px;
|
|
399
|
-
color: #303133;
|
|
400
|
-
}
|
|
401
|
-
.issue-col-count {
|
|
402
|
-
font-size: 13px;
|
|
403
|
-
color: #909399;
|
|
404
|
-
}
|
|
405
|
-
.issue-search {
|
|
406
|
-
margin-bottom: 10px;
|
|
407
|
-
}
|
|
408
|
-
.issue-transfer {
|
|
409
|
-
width: 72px;
|
|
410
|
-
flex-shrink: 0;
|
|
411
|
-
display: flex;
|
|
412
|
-
flex-direction: column;
|
|
413
|
-
align-items: center;
|
|
414
|
-
justify-content: center;
|
|
415
|
-
gap: 10px;
|
|
416
|
-
}
|
|
417
|
-
.issue-pagination {
|
|
418
|
-
margin-top: 10px;
|
|
419
|
-
display: flex;
|
|
420
|
-
justify-content: center;
|
|
421
|
-
}
|
|
422
|
-
</style>
|