# 一键保存说明

一键保存 点击保存后,同时保存Form表单和Table表格内容
Author songgangyu@expservice.com.cn

# 前端示例

检索 新增
<template>
  <div>
    <ht-form ref="searchFm" ref-form="searchFm" divider="检索分栏" :form-list="searchList">
      <ht-search-bar>
        <ht-button @click="handleSearch()">检索</ht-button>
      </ht-search-bar>
    </ht-form>
    <ht-table
      ref="searchTable"
      ref-table="searchTable"
      :table-head="searchTableHead"
      :table-data="searchTableData"
    >
      <ht-button icon="ht-icon-chuangkou01" @click="handleSave">新增</ht-button>
    </ht-table>
    <div
      v-if="dialogVisible"
      :visible.sync="dialogVisible"
      :row-data="rowData"
      :operate-type="operateType"
    >
      <ht-dialog
        v-if="dialogVisible"
        title="弹出窗口样例"
        :visible.sync="dialogVisible"
        :row-data="rowData"
        :operate-type="operateType"
      >
        <template slot="footer">
          <ht-button type="primary" confirm="true" @click="handleInsert">保存</ht-button>
        </template>
        <div style="text-align:left">
          <ht-form
            ref="searchEditFm"
            ref-form="searchEditFm"
            divider="基础信息"
            :form-list="searchEditList"
          />
          <ht-table
            ref="searchEditTable"
            ref-table="searchEditTable"
            :table-head="searchEditTableHead"
            :table-data="searchEditTableData"
          >
            <ht-button @click="handleAdd">新增行</ht-button>
          </ht-table>
        </div>
      </ht-dialog>
    </div>
  </div>
</template>
<script>
// 实际引入JS
// import { updateShowSearchApply, saveShowSearchApply } from '@/api/oem/cms/orderMng/order';
export default {
  data() {
    return {
      operateType: '', // 操作类型,
      dialogVisible: false, // 编辑页控制显示隐藏
      searchList: [
        {
          type: 'text',
          format: [0, 'isNumberLetter', 100],
          label: '查询条件1',
          field: 'outNo',
          colspan: 2 // 占用输入域个数,最大不能超过4
        },
        {
          type: 'text',
          format: [0, 'isNumberLetter', 50],
          label: '查询条件2',
          field: 'outNo2',
          colspan: 2
        },
        {
          type: 'text',
          format: [0, 'isNumberLetter', 50],
          label: '查询条件3',
          field: 'outNo3',
          colspan: 2
        },
        {
          type: 'text',
          format: [0, 'isNumberLetter', 50],
          label: '查询条件4',
          field: 'outNo4',
          colspan: 2
        }
      ],
      searchTableHead: [
        {
          type: 'action',
          label: '操作',
          content: ['编辑'],
          action: [
            (row, index) => this.handleEdit(row, index)
          ]
        },
        {
          label: '用户账号',
          prop: 'account',
          align: 'right',
          query: true
        },
        {
          label: '用户姓名',
          prop: 'userName',
          query: true
        },
        {
          type: 'tag',
          label: '部门',
          prop: 'dname',
          query: true
        },
        {
          label: '工号',
          prop: 'userSn'
        }
      ],
      searchTableData: {},
      searchEditList: [
        {
          type: 'text',
          format: [1, 'isAny', 30],
          label: '用户账号',
          field: 'account',
          colspan: 2
        },
        {
          type: 'text',
          format: [1, 'isAny', 50],
          label: '用户姓名',
          field: 'userName',
          colspan: 2
        },
        {
          type: 'text',
          format: [1, 'isAny', 50],
          label: '部门',
          field: 'dname',
          colspan: 2
        },
        {
          type: 'text',
          format: [1, 'isAny', 50],
          label: '工号',
          field: 'userSn',
          colspan: 2
        },
        ,
        {
          type: 'text',
          field: 'itemTableData',
          hidden: true//隐藏 用于存放明细表格内容
        }
      ],
      searchEditTableHead: [
        {
          type: 'action',
          label: '操作',
          content: ['删除'], // 行操作按钮编辑
          action: [
            (row, index) => this.delete(row, index, '200101')
          ]
        },
        {
          label: '编号',
          prop: 'codeId',
          width: 'auto'
          // align: 'right'// 设置table的column列对齐方式
        },
        {
          type: 'dic',
          label: '代码',
          format: [1, 'isDic', 30], // 设置table的编辑列格式
          prop: 'tCode',
          dicType: 'e#1001=code1:1002=code2:1003=code3', // 样例字典编写
          // dicType: 't#FUNC-DEPT-DEPTID', //实际编写示例
          // dicRemote: (dicType, query) => findDictFromTable(dicType, query), //调用后台表选类
          cb: (row, field, dicType, label, value) => this.handleDictCB(row, field, value),
          edit: true, // 设置table的column是否可编辑
          // isreload: true, // 设置table的列字典类型是否重新加载
          width: 'auto'
        },
        {
          type: 'datetime',
          format: [1, 'isDateTime', 30],
          label: '日期',
          prop: 'birthDate',
          dateType: 'datetime',
          edit: true,
          width: 'auto'
        },
        {
          type: 'number',
          label: '数量',
          prop: 'cont',
          edit: true,
          min: 1,// 设置input的value最小值
          max: 3, // 设置input的value最大值    size	设置input的大小属性
          format: [1, 'isNumberLetter', 30],
          width: 'auto'
        } ,
        {
          type: 'dic',
          format: [0, 'isDic', 30],
          dicType: 'e#1101=男:1102=女', 
          // dicType: 'xb',
          label: '性别',
          edit: true,
          prop: 'sex',
          width: 'auto'
        },
         {
          format: [0, 'isAny', 6],
          label: '备注',
          prop: 'remark',
          edit: true,
          width: 'auto'
        },
      ],
      searchEditTableData: {}
    };
  },
  mounted() {
    // 初始化执行查询
    this.$nextTick(() => {
      this.handleSearch();
    });
  },
  methods: {
    /**
     * @todo: 把明细内容赋值到form表单中
     * @author: songgangyu@scisoft.cn
     * @Date: 2023-05-30 15:27:33
     */    
    putDtl() {
      //JSON.parse(tableRef.recordsList) 将recordsListJSON格式字符串转换为js对象
      const feeItemArr = JSON.parse(JSON.stringify(this.$refs['searchEditTable'].recordsList));
      feeItemArr.forEach((val) => {
        val.sex = val.sex.v;
        val.tCode = val.tCode.v;
      });
      //JSON.stringify(feeItemArr)   JSON.stringify() 方法将 js 对象转换为字符串
      this.$refs['searchEditFm'].model.itemTableData = JSON.stringify(feeItemArr);
    },
    /**
     * @todo:表选回调
     * @author: qinpeng
     * @Date: 2023-05-26 09:27:36
     */
    handleDictCB(res, field, value) {
      switch (field) {
        case 'tCode':
          // 编号赋值
          value.codeId = res.value;
          break;
      }
    },
    /**
     * @todo: 删除行事件
     * @author: qinpeng
     * @Date: 2023-05-26 09:26:26
     */
    async delete(row, index, type) {
      try {
        // searchTable.recordsList当前行数据是否存在值
        if (this.$refs['searchEditTable'].recordsList && this.$refs['searchEditTable'].recordsList.length > 0)
          this.$refs['searchEditTable'].recordsList.splice(index, 1);
      } catch (e) {
        this.$notify.message(e, 'error');
      }
    },
    /**
     * @todo:虚拟数据查询方法
     * @author: qinpeng
     * @Date: 2023-05-26 09:28:12
     */
    async handleSearch2(event) {
      try {
        this.searchEditTableData = {
          'current': 1,
          'pages': 1,
          'size': 10,
          'total': 7,
          'hitCount': true,
          'searchCount': true,
          'orders': [],
          'optimizeCountSql': true,
          'records':
          [{ 'transHash': {}}
          ]
        };
      } catch (e) {
        this.$notify.message(e, 'error');
      }
    },
    /**
     * @todo:新增行事件
     * @author: qinpeng
     * @Date: 2023-05-26 09:26:54
     */
    async handleAdd() {
      //必须先保存表单信息,才可以添加明细
      if (!this.id) {
        this.$notify.message('请先保存基础信息!');
        return;
      }
      // searchTable当前行ref属性
      this.$refs['searchEditTable'].createRow();
    },
    /**
     * @todo: 保存方法
     * @author: songgangyu@scisoft.cn
     * @Date: 2023-05-25 15:17:42
     */
    async handleInsert() {
      try {
        // 修改  update(form的ref, id, js.url)
        if (this.id) {
          this.putDtl();
          const updateData = await this.$crud.update(this.$refs['searchEditFm'], this.id, updateShowSearchApply);
          if (updateData) {
            // 修改回调调用父页面查询(实际需启用)
            // this.$parent.handleSearch();
          }
        } else {
          // 新增  save(form的ref,  js.url)
          const saveData = await this.$crud.save(this.$refs['searchEditFm'], saveShowSearchApply);
          if (saveData)
            // 根据返回值,给form表单赋ID,否则再点保存时,会导致保存重复新增
            this.id = saveData.id;
            // 保存回调调用父页面查询
            // this.$parent.handleSearch();
        }
      } catch (e) {
        this.$notify.message(e, 'error');
      }
    },
    /**
     * @todo: 新增按钮
     * @author: songgangyu@scisoft.cn
     * @Date: 2023-05-25 15:21:54
     */
    handleSave() {
      //
      this.$nextTick(() => {
        this.handleSearch2();
      });
      this.operateType = 'insert';
      this.dialogVisible = true;
      this.rowData = {};
    },
    /**
     * @todo: 编辑按钮
     * @author: songgangyu@scisoft.cn
     * @Date: 2023-05-25 15:22:11
     */
    handleEdit(row) {
      this.$nextTick(() => {
        this.handleSearch2();
      });
      this.operateType = 'update';
      this.dialogVisible = true;
      this.rowData = row;
      // 编辑赋值(正式需要放到created() {}中)
      if (this.rowData.id) {
        this.id = this.rowData.id;
        this.$crud.setEditValue(this.searchEditList, this.rowData);
        this.searchEditList[0].disabled = true;
      }
    },
    /**
     * @todo: 查询赋默认值
     * @author: songgangyu@scisoft.cn
     * @Date: 2023-05-25 15:22:30
     */
    async handleSearch(event) {
      try {
        this.searchTableData = {
          'current': 1,
          'pages': 1,
          'size': 10,
          'total': 7,
          'hitCount': true,
          'searchCount': true,
          'orders': [],
          'optimizeCountSql': true,
          'records':
          [{ 'transHash': {}},
            { 'userSn': '1011', 'userName': '用户01', 'account': '1011', 'dname': '测试', 'rownums': 1 },
            { 'userSn': '1012', 'userName': '用户02', 'account': '1012', 'dname': '研发', 'rownums': 2 },
            { 'userSn': '1013', 'userName': '用户03', 'account': '1013', 'dname': '商务', 'rownums': 3 },
            { 'userSn': '1014', 'userName': '用户04', 'account': '1014', 'dname': '经理', 'rownums': 4 },
            { 'userSn': '1015', 'userName': '用户05', 'account': '1015', 'dname': '测试', 'rownums': 5 },
            { 'userSn': '1016', 'userName': '用户06', 'account': '1016', 'dname': '测试', 'rownums': 6 },
            { 'userSn': '1017', 'userName': '用户07', 'account': '1017', 'dname': '测试', 'rownums': 7 }
          ] };
      } catch (e) {
        this.$notify.message(e, 'error');
      }
    }
  }
};
</script>








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
Expand Copy

# 后端示例

  • Controller
  /**
   * @remark:Form更新Controller层
   * @param: id
   * @param: params
   * @return: java.util.Map<java.lang.String, java.lang.Object>
   * @author: 宋刚宇
   * @date: 2023/5/30 16:27
   * @version: 1.0.1
   * Modification History:
   * Date       Author       Version          Description
   * -----------------------------------------------------------
   * 2023/5/30    宋刚宇         v1.0.1            init
   */
  @ApiOperation(value = "更新->", notes = "更新")
  @ApiImplicitParams({
      @ApiImplicitParam(name = "id", value = "主键", dataTypeClass = Long.class),
      @ApiImplicitParam(name = "param", value = "更新参数", dataTypeClass = Map.class)
  })
  @PutMapping(value = "/update/{id}")
  public Map<String, Object> update(@PathVariable("id") Long id, @RequestBody Map<String, Object> param)
  {
    return csOemSeBaClaimUserService.update(id, param);
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • Service
  /**
   * @remark:Form更新Controller层
   * @param: id
   * @param: params
   * @return: java.util.Map<java.lang.String, java.lang.Object>
   * @author: 宋刚宇
   * @date: 2023/5/30 16:27
   * @version: 1.0.1
   * Modification History:
   * Date       Author       Version          Description
   * -----------------------------------------------------------
   * 2023/5/30    宋刚宇         v1.0.1            init
   */
  Map<String, Object> update(Long id, Map<String, Object> param);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • Impl
  /**
   * @remark:Form更新Impl层
   * @param: id
   * @param: params
   * @return: java.util.Map<java.lang.String, java.lang.Object>
   * @author: 宋刚宇
   * @date: 2023/5/30 17:32
   * @version: 1.0.1
   * Modification History:
   * Date       Author       Version          Description
   * -----------------------------------------------------------
   * 2023/5/30    宋刚宇         v1.0.1            init
   */
  @Override
  public Map<String, Object> update(Long id, Map<String, Object> param)
  {
  AssertMyUtil.notNull(id, BizCode.METHOD_ARGS_NOTNULL, "id");
    CsOemSeBaClaimUserPO csOemSeBaClaimUserPO = csOemSeBaClaimUserMapper.selectById(id);
    Map2ObjUtil.toObject(param, csOemSeBaClaimUserPO);
    csOemSeBaClaimUserMapper.updateById(csOemSeBaClaimUserPO);
    List<CsOemSeBaClaimUserDtlPO> csOemSeBaClaimUserDtlPOS = new ArrayList<>();
    List<CsOemSeBaClaimUserDtlPO> updatePOS = new ArrayList<>();
    List<CsOemSeBaClaimUserDtlPO> savePOS = new ArrayList<>();
    if (param.get("itemTableData") != null)
    {
      Object itemTableData = param.remove("itemTableData").toString();
      JSONArray array = JSONUtil.parseArray(itemTableData);
      csOemSeBaClaimUserDtlPOS = JSONUtil.toList(array, CsOemSeBaClaimUserDtlPO.class);
    }
    if (!CollectionUtils.isEmpty(csOemSeBaClaimUserDtlPOS))
    {
      csOemSeBaClaimUserDtlPOS.stream().forEach(csOemSeBaClaimUserDtlPO ->
      {
        if (csOemSeBaClaimUserDtlPO.getId() != null)
        {
          CsOemSeBaClaimUserDtlPO csOemSeBaClaimUserDtlPO1 = csOemSeBaClaimUserDtlMapper.selectById(csOemSeBaClaimUserDtlPO.getId());
          csOemSeBaClaimUserDtlPO1.setUserId(csOemSeBaClaimUserDtlPO.getUserId());
          csOemSeBaClaimUserDtlPO1.setUserName(csOemSeBaClaimUserDtlPO.getUserName());
          csOemSeBaClaimUserDtlPO1.setUserCode(csOemSeBaClaimUserDtlPO.getUserCode());
          csOemSeBaClaimUserDtlPO1.setRemark(csOemSeBaClaimUserDtlPO.getRemark());
          csOemSeBaClaimUserDtlPO1.setStatus(csOemSeBaClaimUserDtlPO.getStatus());
          updatePOS.add(csOemSeBaClaimUserDtlPO1);
        } else if (!org.apache.commons.lang.StringUtils.isEmpty(csOemSeBaClaimUserDtlPO.getUserName()))
        {
          savePOS.add(csOemSeBaClaimUserDtlPO);
        }
      });
      if (!CollectionUtils.isEmpty(updatePOS))
      {
        csOemSeBaClaimUserDtlMapper.updateBatch(updatePOS, CsOemSeBaClaimUserDtlPO.class);
      }
      if (!CollectionUtils.isEmpty(savePOS))
      {
        csOemSeBaClaimUserDtlMapper.saveBatch(savePOS, CsOemSeBaClaimUserDtlPO.class);
      }
    }
    return transResultService.transResult(csOemSeBaClaimUserPO.toMaps(), this.getTransDic());
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 版本

  • v1.0.1