n20-common-lib 2.22.43 → 2.22.45

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": "n20-common-lib",
3
- "version": "2.22.43",
3
+ "version": "2.22.45",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -1,16 +1,29 @@
1
1
  <template>
2
- <el-date-picker
3
- ref="date-picker"
4
- class="n20-date-editor"
5
- :class="{ 'has-value': clearable && value }"
6
- v-bind="$attrs"
7
- :value="value"
8
- :clearable="clearable"
9
- v-on="$listeners"
10
- />
2
+ <div class="n20-date-picker" :class="{ 'has-long-term': showLongTerm }">
3
+ <el-date-picker
4
+ ref="date-picker"
5
+ class="n20-date-editor"
6
+ :class="{ 'has-value': clearable && value }"
7
+ v-bind="$attrs"
8
+ :value="value"
9
+ :clearable="clearable"
10
+ :disabled="datePickerDisabled"
11
+ v-on="listeners"
12
+ />
13
+ <el-checkbox
14
+ v-if="showLongTerm"
15
+ v-model="longTermC"
16
+ class="n20-date-picker__long-term"
17
+ :disabled="checkboxDisabled"
18
+ >
19
+ {{ longTermLabel | $lc }}
20
+ </el-checkbox>
21
+ </div>
11
22
  </template>
12
23
 
13
24
  <script>
25
+ const LONG_TERM_DATE = new Date(9999, 11, 31, 23, 59, 59)
26
+
14
27
  export default {
15
28
  name: 'DatePicker',
16
29
  props: {
@@ -21,7 +34,139 @@ export default {
21
34
  clearable: {
22
35
  type: Boolean,
23
36
  default: true
37
+ },
38
+ showLongTerm: {
39
+ type: Boolean,
40
+ default: false
41
+ },
42
+ longTerm: {
43
+ type: Boolean,
44
+ default: false
45
+ },
46
+ longTermLabel: {
47
+ type: String,
48
+ default: '长期'
49
+ }
50
+ },
51
+ computed: {
52
+ listeners() {
53
+ return Object.assign({}, this.$listeners, {
54
+ input: (val) => {
55
+ this.$emit('input', val)
56
+ if (this.longTerm) {
57
+ this.$emit('update:long-term', false)
58
+ this.$emit('changeLongTerm', {
59
+ longTerm: false,
60
+ value: val
61
+ })
62
+ }
63
+ }
64
+ })
65
+ },
66
+ longTermC: {
67
+ get() {
68
+ return this.longTerm
69
+ },
70
+ set(val) {
71
+ this.handleLongTermChange(val)
72
+ }
73
+ },
74
+ datePickerDisabled() {
75
+ return this.longTerm || this.isDisabled
76
+ },
77
+ checkboxDisabled() {
78
+ return this.isDisabled
79
+ },
80
+ isDisabled() {
81
+ return this.$attrs.disabled === '' || this.$attrs.disabled === true || this.$attrs.disabled === 'disabled'
82
+ }
83
+ },
84
+ methods: {
85
+ handleLongTermChange(val) {
86
+ this.$emit('update:long-term', val)
87
+
88
+ if (val) {
89
+ const longTermValue = this.getLongTermValue()
90
+ this.$emit('input', longTermValue)
91
+ this.$emit('change', longTermValue)
92
+ this.$emit('changeLongTerm', {
93
+ longTerm: true,
94
+ value: longTermValue
95
+ })
96
+ } else {
97
+ const currentValue = this.getCurrentValue()
98
+ this.$emit('input', currentValue)
99
+ this.$emit('change', currentValue)
100
+ this.$emit('changeLongTerm', {
101
+ longTerm: false,
102
+ value: currentValue
103
+ })
104
+ }
105
+ },
106
+ getLongTermValue() {
107
+ return this.getDateValue(LONG_TERM_DATE)
108
+ },
109
+ getCurrentValue() {
110
+ return this.getDateValue(new Date())
111
+ },
112
+ getDateValue(date) {
113
+ const valueFormat = this.$attrs['value-format']
114
+
115
+ if (valueFormat === 'timestamp') {
116
+ return date.getTime()
117
+ }
118
+
119
+ if (!valueFormat) {
120
+ return new Date(date.getTime())
121
+ }
122
+
123
+ const year = date.getFullYear() + ''
124
+ const month = this.padDateValue(date.getMonth() + 1)
125
+ const day = this.padDateValue(date.getDate())
126
+ const hours = this.padDateValue(date.getHours())
127
+ const minutes = this.padDateValue(date.getMinutes())
128
+ const seconds = this.padDateValue(date.getSeconds())
129
+
130
+ return valueFormat
131
+ .replace(/yyyy/g, year)
132
+ .replace(/YYYY/g, year)
133
+ .replace(/MM/g, month)
134
+ .replace(/dd/g, day)
135
+ .replace(/DD/g, day)
136
+ .replace(/HH/g, hours)
137
+ .replace(/hh/g, hours)
138
+ .replace(/mm/g, minutes)
139
+ .replace(/ss/g, seconds)
140
+ },
141
+ padDateValue(value) {
142
+ return value > 9 ? value + '' : '0' + value
24
143
  }
25
144
  }
26
145
  }
27
146
  </script>
147
+
148
+ <style scoped>
149
+ .n20-date-picker {
150
+ display: inline-block;
151
+ }
152
+
153
+ .n20-date-picker > .n20-date-editor {
154
+ width: 100%;
155
+ }
156
+
157
+ .n20-date-picker.has-long-term {
158
+ display: inline-flex;
159
+ align-items: center;
160
+ }
161
+
162
+ .n20-date-picker.has-long-term > .n20-date-editor {
163
+ flex: 1;
164
+ width: auto;
165
+ min-width: 0;
166
+ }
167
+
168
+ .n20-date-picker__long-term {
169
+ margin-left: 8px;
170
+ white-space: nowrap;
171
+ }
172
+ </style>
@@ -27,6 +27,7 @@
27
27
  <script>
28
28
  import { $lc } from '../../utils/i18n/index'
29
29
  import dayjs from 'dayjs'
30
+
30
31
  let startDate = ''
31
32
 
32
33
  const onPick = ({ maxDate, minDate }) => {
@@ -32,15 +32,11 @@
32
32
  :height="height"
33
33
  border
34
34
  @selection-change="(selection) => (selectionList = selection)"
35
+ @sort-change="onSortChange"
35
36
  >
36
37
  <slot name="selection-column">
37
38
  <el-table-column type="selection" width="50" align="center" />
38
39
  </slot>
39
- <el-table-column :label="'排序' | $lc" width="50" align="center">
40
- <template slot-scope="{ row }">
41
- <i class="n20-icon-tuodong file-upload-table__drag-handle" :data-row-key="row[keys.rowKey]"></i>
42
- </template>
43
- </el-table-column>
44
40
  <template v-if="dataPorp.slotHeader">
45
41
  <el-table-column
46
42
  v-for="item in dataPorp.slotHeader"
@@ -77,7 +73,7 @@
77
73
  </div>
78
74
  </slot>
79
75
  </el-table-column>
80
- <el-table-column :label="'附件名称' | $lc" :prop="keys.name">
76
+ <el-table-column :label="'附件名称' | $lc" :prop="keys.name" sortable="custom">
81
77
  <slot slot="header" slot-scope="scope" name="name-header" :column="scope.column">{{ '附件名称' | $lc }}</slot>
82
78
  <slot slot-scope="{ row }" name="name" :row="row">
83
79
  <span v-if="readonly">{{ row[keys.name] ? row[keys.name].replace(/\.[A-z0-9]+$/, '') : '' }}</span>
@@ -272,7 +268,7 @@
272
268
  :close-on-click-modal="false"
273
269
  :destroy-on-open="true"
274
270
  >
275
- <el-select v-model="bathType" class="m-b-s" clearable @change="handleBathChange">
271
+ <el-select v-model="bathType" class="m-b-s w-100p" clearable @change="handleBathChange">
276
272
  <el-option
277
273
  v-for="item in typeOptions"
278
274
  :key="item.type"
@@ -292,7 +288,7 @@
292
288
  :multiple="true"
293
289
  :show-file-list="true"
294
290
  :action="apiPrefix ? apiPrefix + action : action"
295
- :data="fileData"
291
+ :data="_batchFileData || fileData"
296
292
  :headers="headers"
297
293
  :accept="fileAccept"
298
294
  :size="fileSize"
@@ -322,7 +318,6 @@ import aiCheckDialog from './aiCheckDialog.vue'
322
318
  import getJsonc from '../../assets/getJsonc.js'
323
319
  import _axios from 'axios'
324
320
  import dayjs from 'dayjs'
325
- import Sortable from 'sortablejs'
326
321
  import XEUtils from 'xe-utils'
327
322
 
328
323
  import 'viewerjs/dist/viewer.css'
@@ -507,7 +502,8 @@ export default {
507
502
  previewSameOrg: false,
508
503
  seeRow: {},
509
504
  officeStatus: false,
510
- sortable: null
505
+ // 批量上传使用的文件数据(避免直接修改计算属性)
506
+ _batchFileData: null
511
507
  }
512
508
  },
513
509
  computed: {
@@ -550,63 +546,31 @@ export default {
550
546
  },
551
547
  immediate: true,
552
548
  deep: true
553
- },
554
- tableKey() {
555
- this.initSortable()
556
549
  }
557
550
  },
558
551
  mounted() {
559
552
  this.getConfiguration()
560
- this.initSortable()
561
- },
562
- beforeDestroy() {
563
- this.destroySortable()
564
553
  },
565
554
  methods: {
566
- initSortable() {
567
- this.$nextTick(() => {
568
- this.destroySortable()
569
-
570
- const $tbody = this.$refs.elTable?.$el?.querySelector('.el-table__body-wrapper > table > tbody')
571
- if (!$tbody) {
572
- return
573
- }
574
-
575
- this.sortable = Sortable.create($tbody, {
576
- handle: '.file-upload-table__drag-handle',
577
- ghostClass: 'file-upload-table__sortable-ghost',
578
- setData(dataTransfer) {
579
- dataTransfer.setData('Text', '')
580
- },
581
- onEnd: ({ oldIndex, newIndex }) => {
582
- if (
583
- oldIndex === newIndex ||
584
- oldIndex === undefined ||
585
- newIndex === undefined ||
586
- oldIndex < 0 ||
587
- newIndex < 0
588
- ) {
589
- return
590
- }
591
-
592
- const targetRow = this.tableData.splice(oldIndex, 1)[0]
593
- if (!targetRow) {
594
- return
595
- }
596
- this.tableData.splice(newIndex, 0, targetRow)
597
- this.$emit('sort', this.tableData.slice())
598
- this.saveFileSort().catch(() => {
599
- this.$message.error($lc('附件排序保存失败'))
600
- })
601
- }
602
- })
603
- })
604
- },
605
- destroySortable() {
606
- if (this.sortable) {
607
- this.sortable.destroy()
608
- this.sortable = null
555
+ // 点击附件名称表头排序(替代原拖动排序)
556
+ onSortChange({ prop, order }) {
557
+ // 仅处理附件名称列;order 为 null(取消排序)时不处理
558
+ if (prop !== this.keys.name || !order) {
559
+ return
609
560
  }
561
+ const nameKey = this.keys.name
562
+ // 与显示一致:去除扩展名后再比较
563
+ const stripName = (v) => (v == null ? '' : String(v).replace(/\.[A-z0-9]+$/, ''))
564
+ const dir = order === 'ascending' ? 1 : -1
565
+ const sorted = [...this.tableData].sort(
566
+ (a, b) => stripName(a[nameKey]).localeCompare(stripName(b[nameKey]), 'zh-CN', { numeric: true }) * dir
567
+ )
568
+ // 原地更新 tableData(沿用现有 splice 修改 prop 数组的模式)
569
+ this.tableData.splice(0, this.tableData.length, ...sorted)
570
+ this.$emit('sort', this.tableData.slice())
571
+ this.saveFileSort().catch(() => {
572
+ this.$message.error($lc('附件排序保存失败'))
573
+ })
610
574
  },
611
575
  async saveFileSort() {
612
576
  const sortList = this.tableData
@@ -783,20 +747,32 @@ export default {
783
747
  if (bu) return bu(file)
784
748
  },
785
749
  handleBathChange() {
786
- let dto = JSON.parse(this.dataPorp.fileData.data)
750
+ // 创建新的文件数据对象,避免直接修改计算属性
751
+ let fileData = this.fileData || {}
752
+ let dto = JSON.parse(fileData.data || '{}')
787
753
  dto[this.keys.type] = this.bathType
788
- this.fileData.data = JSON.stringify(dto)
754
+ this._batchFileData = {
755
+ ...fileData,
756
+ data: JSON.stringify(dto)
757
+ }
789
758
  },
790
759
  batchUploadFn() {
791
760
  let $uploadwrap = this.$refs['upload-batch']
792
- let fileList = $uploadwrap.fileList
793
- if (fileList.length !== 0 && fileList.every((f) => f.status === 'success')) {
794
- this.visibleBatch = false
795
- }
761
+ // 直接提交上传,关闭对话框的逻辑在 batchSuccess 回调中处理
796
762
  $uploadwrap.submit()
797
763
  },
798
764
  batchSuccess(response, file, fileList) {
799
- let row = { ...this.dataPorp.keys }
765
+ // 创建完整的行对象,初始化状态字段
766
+ let row = {
767
+ id: 'n' + Math.random(),
768
+ [this.keys.name]: '',
769
+ [this.keys.type]: this.bathType,
770
+ [this.keys.time]: '',
771
+ [this.keys.user]: JSON.parse(sessionStorage.getItem('userInfo')).uname,
772
+ _percent: 0,
773
+ _status: undefined,
774
+ _typeDisabled: false
775
+ }
800
776
  this.tableData.splice(0, 0, row)
801
777
  this.$nextTick(() => {
802
778
  this.onSuccessFn(response, file, fileList, row)
@@ -954,13 +930,8 @@ export default {
954
930
  let FD = new FormData()
955
931
  FD.append(opt.filename, opt.file)
956
932
 
957
- let data
958
- if (window._fileData) {
959
- data = window._fileData
960
- delete window._fileData
961
- } else {
962
- data = opt.data
963
- }
933
+ // 优先使用 opt.data,其次使用 this.fileData,移除全局变量 window._fileData 的使用
934
+ let data = opt.data || this.fileData
964
935
 
965
936
  if (data) {
966
937
  let dto = JSON.parse(data.data)
@@ -1034,7 +1005,7 @@ export default {
1034
1005
  _percent = 99
1035
1006
  _status = 'error'
1036
1007
  } else {
1037
- _percent = 99
1008
+ _percent = 100
1038
1009
  _status = 'success'
1039
1010
  }
1040
1011
  setTimeout(() => {
@@ -1091,21 +1062,4 @@ export default {
1091
1062
  right: 46px;
1092
1063
  top: 10px;
1093
1064
  }
1094
-
1095
- .file-upload-table__drag-handle {
1096
- display: inline-block;
1097
- color: #909399;
1098
- font-size: 16px;
1099
- cursor: move;
1100
- }
1101
-
1102
- .file-upload-table__drag-handle:hover {
1103
- color: #606266;
1104
- }
1105
-
1106
- .file-upload-table__sortable-ghost {
1107
- opacity: 0.8;
1108
- color: #fff !important;
1109
- background: #42b983 !important;
1110
- }
1111
1065
  </style>