n20-common-lib 2.9.13 → 2.9.14

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.
Files changed (130) hide show
  1. package/nstc-g6/components/NstcCharts/NstcCharts.vue +16 -8
  2. package/package.json +8 -5
  3. package/src/assets/css/approve-card.scss +1 -1
  4. package/src/assets/css/date-quarter.scss +8 -0
  5. package/src/assets/css/expandable-pane.scss +7 -0
  6. package/src/assets/css/file-upload-table.scss +2 -3
  7. package/src/assets/css/font-icon.scss +3 -1
  8. package/src/assets/css/message.scss +6 -6
  9. package/src/assets/css/normalize.scss +4 -1
  10. package/src/assets/css/table.scss +16 -11
  11. package/src/assets/iconFont/SIMSUN.ttf +0 -0
  12. package/src/assets/iconFont/font.css +4 -0
  13. package/src/components/AIButton/index.vue +101 -0
  14. package/src/components/AdvancedFilter/formItemRender.vue +7 -4
  15. package/src/components/AdvancedFilter/index.vue +6 -7
  16. package/src/components/Anchor/index.vue +1 -0
  17. package/src/components/ApprovalButtons/index.vue +404 -82
  18. package/src/components/ApprovalButtons/indexApp.vue +1102 -0
  19. package/src/components/ApprovalButtons/selectSpr.vue +128 -0
  20. package/src/components/ApprovalButtons/setCarboncopyProp.vue +1 -1
  21. package/src/components/ApprovalButtons/setRejectToProp.vue +45 -5
  22. package/src/components/ApprovalButtons/showAppOpi.vue +265 -8
  23. package/src/components/ApprovalButtons/showOtherAttrNew.vue +161 -0
  24. package/src/components/ApprovalCard/index.vue +190 -44
  25. package/src/components/ApprovalCard/indexApp.vue +592 -0
  26. package/src/components/ApprovalRecord/approvalImgPro/child.vue +1 -1
  27. package/src/components/ApprovalRecord/approvalImgPro/index.vue +42 -5
  28. package/src/components/ApprovalRecord/index.vue +23 -6
  29. package/src/components/ApprovalRecord/indexApp.vue +79 -0
  30. package/src/components/AttachmentPass/index.vue +286 -0
  31. package/src/components/Button/button-group.vue +3 -1
  32. package/src/components/CascaderArea/index.vue +5 -6
  33. package/src/components/DatePicker/por.vue +31 -1
  34. package/src/components/DateSelect/quarterDatePicker.vue +23 -1
  35. package/src/components/Descriptions/index.vue +7 -3
  36. package/src/components/Dialog/index.vue +4 -0
  37. package/src/components/Diff/index.vue +4 -2
  38. package/src/components/DynamicField/DynamicField.vue +29 -6
  39. package/src/components/DynamicField/DynamicFieldMixin.js +13 -5
  40. package/src/components/DynamicField/DynamicFieldOptions.vue +135 -62
  41. package/src/components/DynamicField/DynamicTable.vue +11 -1
  42. package/src/components/DynamicField/contentPop.vue +110 -0
  43. package/src/components/DynamicField/tableList.vue +5 -1
  44. package/src/components/DynamicField/tableView.vue +143 -0
  45. package/src/components/ECharts/index.vue +15 -9
  46. package/src/components/Expandable/main.vue +17 -2
  47. package/src/components/FileImport/index.vue +4 -5
  48. package/src/components/FileUploadTable/FileUploadTableV3.vue +937 -0
  49. package/src/components/FileUploadTable/aiCheckDialog.vue +139 -0
  50. package/src/components/FileUploadTable/index.vue +251 -33
  51. package/src/components/FileUploadTable/jytg.svg +6 -0
  52. package/src/components/FileUploadTable/wsc.svg +3 -0
  53. package/src/components/FileUploadTable/ysc.svg +3 -0
  54. package/src/components/HandlingAdvice/index.vue +191 -0
  55. package/src/components/InputAccount/index.vue +105 -0
  56. package/src/components/InputNumber/index.vue +16 -4
  57. package/src/components/InputNumber/numberRange.vue +14 -0
  58. package/src/components/InputSearch/index.vue +8 -0
  59. package/src/components/Layout/HeaderWrap/changePwd.vue +50 -23
  60. package/src/components/Layout/HeaderWrap/index.vue +2 -2
  61. package/src/components/Layout/HeaderWrap/indexN.vue +296 -119
  62. package/src/components/Layout/SubContent/index.vue +45 -14
  63. package/src/components/Layout/indexN.vue +26 -5
  64. package/src/components/LoginTemporary/form.vue +126 -34
  65. package/src/components/LoginTemporary/index.vue +14 -3
  66. package/src/components/LoginTemporary/indexN.vue +35 -13
  67. package/src/components/Preview/index.vue +199 -0
  68. package/src/components/SelectDatePickerPro/customDatePicker.vue +7 -0
  69. package/src/components/SelectDatePickerPro/halfYearPicker.vue +165 -0
  70. package/src/components/SelectDatePickerPro/index.vue +44 -4
  71. package/src/components/SelectDatePickerPro/quarterDatePicker.vue +32 -10
  72. package/src/components/SelectTree/index.vue +10 -4
  73. package/src/components/SelectTree/pro.vue +3 -0
  74. package/src/components/ShowColumn/index copy 2.vue +545 -0
  75. package/src/components/ShowColumn/index copy.vue +566 -0
  76. package/src/components/ShowColumn/index.vue +11 -7
  77. package/src/components/Statis/statisItem.vue +8 -7
  78. package/src/components/Table/filters.js +13 -0
  79. package/src/components/Table/index.vue +4 -4
  80. package/src/components/TablePro/filterContent.vue +18 -8
  81. package/src/components/TablePro/index.js +41 -1
  82. package/src/components/TablePro/index.vue +117 -6
  83. package/src/components/TableProOperateColumn/OperateBtns.vue +11 -53
  84. package/src/components/TableProOperateColumn/OperateBtns_copy.vue +141 -0
  85. package/src/components/TableProOperateColumn/childrenOperateBtn.vue +108 -0
  86. package/src/components/TableProOperateColumn/index.vue +3 -6
  87. package/src/components/TableSetSize/index.vue +1 -1
  88. package/src/components/Tree/index.vue +21 -5
  89. package/src/components/Upload/index.vue +25 -10
  90. package/src/components/Upload/uploadMsg.vue +30 -25
  91. package/src/components/operatingStatus/index.vue +3 -4
  92. package/src/directives/VTitle/index.js +15 -5
  93. package/src/directives/watermark/index.js +140 -56
  94. package/src/i18n.json +146 -18
  95. package/src/index.js +31 -3
  96. package/src/plugins/Sign/CaMap.js +47 -8
  97. package/src/plugins/Sign/Itrus/index.js +1 -1
  98. package/src/plugins/Sign/Itrus/sign_3720.js +2 -1
  99. package/src/plugins/Sign/NetSM3/index.js +20 -11
  100. package/src/plugins/Sign/NetV3/index.js +163 -22
  101. package/src/plugins/Sign/SkfSign/index.js +55 -52
  102. package/src/plugins/Sign/bjca/index.js +229 -9
  103. package/src/plugins/Sign/hnca/index.js +5064 -0
  104. package/src/plugins/Sign/index.js +155 -103
  105. package/src/plugins/Sign/kySign/base64.js +160 -0
  106. package/src/plugins/Sign/kySign/hex.js +92 -0
  107. package/src/plugins/Sign/kySign/index.js +186 -0
  108. package/src/plugins/Sign/kySign/skf.js +937 -0
  109. package/src/plugins/Sign/kySign/test.html +463 -0
  110. package/src/plugins/Sign/sdca/index.js +73 -0
  111. package/src/plugins/Sign/sign.js +72 -52
  112. package/src/plugins/Sign/signV3/InfosecNetSignCNGAgent.min.js +1 -0
  113. package/src/plugins/Sign/signV3/sign.js +187 -0
  114. package/src/plugins/Sign/sign_back.js +172 -0
  115. package/src/utils/accountFormat.js +7 -0
  116. package/src/utils/asyncGetRelaNos.js +79 -46
  117. package/src/utils/axios.js +3 -5
  118. package/src/utils/i18n/index.js +1 -1
  119. package/src/utils/importGlobal.js +23 -12
  120. package/style/fonts/SIMSUN.5e0c362c.ttf +0 -0
  121. package/style/index.css +2 -2
  122. package/theme/blue.css +2 -2
  123. package/theme/cctcRed.css +2 -2
  124. package/theme/fonts/SIMSUN.5e0c362c.ttf +0 -0
  125. package/theme/green.css +2 -2
  126. package/theme/lightBlue.css +2 -2
  127. package/theme/orange.css +2 -2
  128. package/theme/purple.css +2 -2
  129. package/theme/red.css +2 -2
  130. package/theme/yellow.css +2 -2
@@ -22,7 +22,7 @@ export default {
22
22
  mini: {
23
23
  type: Object,
24
24
  default: () => ({
25
- border: false,
25
+ border: true,
26
26
  size: 'mini',
27
27
  stripe: true
28
28
  })
@@ -21,16 +21,21 @@
21
21
  <img :src="node.level == 1 ? folder : desc" class="m-r-ss" alt="" />
22
22
  <div
23
23
  v-title="
24
- (data[props.value] ? '(' + data[props.value] + ') ' : '') +
25
- data[props.label] +
26
- (data && data.children && data.children.length > 0 ? '(' + data.children.length + ')' : '')
24
+ returnTitle(data) +
25
+ (props.total && data[props.children] && data[props.children].length > 0
26
+ ? '(' + data[props.total] + ')'
27
+ : data && data[props.children] && data[props.children].length > 0
28
+ ? '(' + data[props.children].length + ')'
29
+ : '')
27
30
  "
28
31
  :show-overflow-tooltip="true"
29
32
  class="cl-tree-item-label text-ellipsis"
30
33
  >
31
- {{ (data[props.value] ? '(' + data[props.value] + ') ' : '') + data[props.label] }}
34
+ {{ returnTitle(data) }}
32
35
  {{
33
- data && data[props.children] && data[props.children].length > 0
36
+ props.total && data[props.children] && data[props.children].length > 0
37
+ ? '(' + data[props.total] + ')'
38
+ : data && data[props.children] && data[props.children].length > 0
34
39
  ? '(' + data[props.children].length + ')'
35
40
  : ''
36
41
  }}
@@ -67,6 +72,10 @@
67
72
  export default {
68
73
  name: 'Tree',
69
74
  props: {
75
+ labelHasValue: {
76
+ type: Boolean,
77
+ default: true
78
+ },
70
79
  data: {
71
80
  type: Array,
72
81
  default: () => []
@@ -111,6 +120,13 @@ export default {
111
120
  this.setRefData()
112
121
  },
113
122
  methods: {
123
+ returnTitle(data) {
124
+ if (!this.labelHasValue) {
125
+ return data[this.props.label]
126
+ } else {
127
+ return (data[this.props.value] ? '(' + data[this.props.value] + ') ' : '') + data[this.props.label]
128
+ }
129
+ },
114
130
  setRefData() {
115
131
  const {
116
132
  store,
@@ -19,7 +19,7 @@
19
19
  >
20
20
  <template slot="trigger">
21
21
  <slot name="trigger">
22
- <el-input :value="fileNameC" :placeholder="placeholder" readonly :disabled="$attrs.disabled">
22
+ <el-input v-title :value="fileNameC" :placeholder="placeholder" readonly :disabled="$attrs.disabled">
23
23
  <template slot="suffix">
24
24
  <i
25
25
  v-if="fileNameC && showClear"
@@ -42,14 +42,14 @@
42
42
  :percent="percent"
43
43
  :width="width"
44
44
  title="文件导入"
45
- :showErrorExport="showErrorExport"
45
+ :show-error-export="showErrorExport"
46
46
  :pagination="pagination"
47
47
  :footer-btn="footer"
48
48
  :validate-result="validateResult"
49
49
  :validate-confirm="validateConfirm"
50
50
  @importError="importError"
51
- >
52
- </uploadMsg>
51
+ @clearFiles="clearFiles"
52
+ />
53
53
  </el-upload>
54
54
  </template>
55
55
 
@@ -204,6 +204,9 @@ export default {
204
204
  }
205
205
  },
206
206
  methods: {
207
+ clearFiles() {
208
+ this.$refs['upload'].clearFiles()
209
+ },
207
210
  importError() {
208
211
  this.$emit('importError')
209
212
  },
@@ -298,6 +301,7 @@ export default {
298
301
  let Pro = axios.post(opt.action + '?r=' + Math.random(), FD, {
299
302
  headers: Object.assign(auth.setHeaders(this.headers), { 'Content-Type': 'multipart/form-data' }),
300
303
  loading: false,
304
+ timeout: 900000,
301
305
  onUploadProgress: (arg) => {
302
306
  if (opt.onProgress) {
303
307
  arg.percent = arg.progress * 100
@@ -327,13 +331,18 @@ export default {
327
331
  this.fileUrlC = response.data
328
332
  this.fileList = fileList
329
333
 
330
- if (this.onSuccess) {
331
- this.onSuccess(response, file, fileList)
332
- }
333
-
334
334
  if (this.msgType === 'dialog') {
335
- this.percent = 100
336
- this.percentType = 'success'
335
+ if (response.code >= 900 || response.code === -1) {
336
+ this.percent = 99
337
+ this.percentType = 'error'
338
+ } else {
339
+ this.percent = 100
340
+ this.percentType = 'success'
341
+ if (this.onSuccess) {
342
+ this.onSuccess(response, file, fileList)
343
+ }
344
+ }
345
+
337
346
  this.percentMsg = response.msg || $lc('上传成功!')
338
347
  } else if (this.msgType === 'message') {
339
348
  if (!this.onSuccess) {
@@ -342,6 +351,12 @@ export default {
342
351
  message: response.msg,
343
352
  showClose: true
344
353
  })
354
+ } else {
355
+ this.onSuccess(response, file, fileList)
356
+ }
357
+ } else {
358
+ if (this.onSuccess) {
359
+ this.onSuccess(response, file, fileList)
345
360
  }
346
361
  }
347
362
  },
@@ -6,9 +6,9 @@
6
6
  :visible.sync="progressV"
7
7
  :title="title"
8
8
  append-to-body
9
- @close="handleClose"
10
9
  :close-on-click-modal="false"
11
10
  :close-on-press-escape="false"
11
+ @close="handleClose"
12
12
  >
13
13
  <div style="min-height: 28px">
14
14
  <template v-if="!hidePercent">
@@ -50,7 +50,7 @@
50
50
  <div class="bd-a">
51
51
  <div class="flex-box flex-lr flex-v m-t m-b p-l p-r">
52
52
  <span>{{ '无效数据详情' | $lc }}</span>
53
- <el-button type="text" v-if="showErrorExport" @click="importError">{{ '导出错误数据' | $lc }}</el-button>
53
+ <el-button v-if="showErrorExport" type="text" @click="importError">{{ '导出错误数据' | $lc }}</el-button>
54
54
  </div>
55
55
  <el-table ref="tables" :key="index" :data="errorListC" border style="width: 100%" height="300px">
56
56
  <el-table-column
@@ -60,20 +60,22 @@
60
60
  :label="column.label"
61
61
  :width="column.width"
62
62
  :align="column.align"
63
+ :header-align="column.headerAlign || 'center'"
63
64
  :show-overflow-tooltip="column.showOverflowTooltip"
64
- :formatter="column.formatter | colfF(column.formatterMap)"
65
65
  >
66
66
  <template slot-scope="scope">
67
- <span v-html="scope.row[column.prop]"></span>
67
+ <span v-if="column.formatter">{{ scope.row | colfFRow(column.formatter, column.formatterMap) }}</span>
68
+ <!-- eslint-disable-next-line vue/no-v-html -->
69
+ <span v-else v-html="scope.row[column.prop]"></span>
68
70
  </template>
69
71
  </el-table-column>
70
72
  </el-table>
71
73
  <div v-if="pagination" class="flex-box flex-r m-t-ss m-b-ss">
72
74
  <Pagination
73
- :pageObj="page"
75
+ :page-obj="page"
74
76
  :page-key="{ no: 'current', size: 'pageSize', total: 'total' }"
75
77
  @change="getList"
76
- ></Pagination>
78
+ />
77
79
  </div>
78
80
  </div>
79
81
  <div class="color-warning m-t-s">{{ '上述数据输入有误,请修改导入文件中相关信息!' | $lc }}</div>
@@ -81,8 +83,9 @@
81
83
  </template>
82
84
  </div>
83
85
  <div slot="footer" style="height: 32px">
84
- <template v-if="validateResult && footerBtn">
86
+ <template v-if="footerBtn">
85
87
  <el-button
88
+ v-if="validateResult.successNum > 0"
86
89
  type="primary"
87
90
  @click="
88
91
  () => {
@@ -111,10 +114,10 @@
111
114
  </template>
112
115
 
113
116
  <script>
114
- import cloneDeep from 'lodash/cloneDeep'
117
+ import XEUtils from 'xe-utils'
115
118
  import { $lc } from '../../utils/i18n/index'
116
119
  import Pagination from '../Pagination/index.vue'
117
- import { colfF } from '../Table/filters.js'
120
+ import { colfFRow } from '../Table/filters.js'
118
121
  export default {
119
122
  name: 'UploadMsg',
120
123
  components: { Pagination },
@@ -169,7 +172,7 @@ export default {
169
172
  }
170
173
  },
171
174
  filters: {
172
- colfF
175
+ colfFRow
173
176
  },
174
177
  data() {
175
178
  return {
@@ -183,20 +186,6 @@ export default {
183
186
  index: 0
184
187
  }
185
188
  },
186
- watch: {
187
- errorList: {
188
- handler() {
189
- this.errorListC = this.errorList
190
- this.page.total = this.tableData.length
191
- }
192
- },
193
- page: {
194
- handler(v) {
195
- this.pageSize = v
196
- },
197
- deep: true
198
- }
199
- },
200
189
  computed: {
201
190
  progressV: {
202
191
  get() {
@@ -225,7 +214,8 @@ export default {
225
214
  }
226
215
  _errorList.push(temp)
227
216
  })
228
- this.tableData = cloneDeep(_errorList)
217
+ // eslint-disable-next-line vue/no-side-effects-in-computed-properties
218
+ this.tableData = XEUtils.clone(_errorList, true)
229
219
  const startIndex = (this.page.current - 1) * this.page.pageSize
230
220
  const endIndex = startIndex + this.page.pageSize
231
221
  return this.pagination ? this.tableData.slice(startIndex, endIndex) : this.tableData
@@ -238,6 +228,20 @@ export default {
238
228
  }
239
229
  }
240
230
  },
231
+ watch: {
232
+ errorList: {
233
+ handler() {
234
+ this.errorListC = this.errorList
235
+ this.page.total = this.tableData.length
236
+ }
237
+ },
238
+ page: {
239
+ handler(v) {
240
+ this.pageSize = v
241
+ },
242
+ deep: true
243
+ }
244
+ },
241
245
  methods: {
242
246
  importError() {
243
247
  this.$emit('importError')
@@ -253,6 +257,7 @@ export default {
253
257
  },
254
258
  handleClose() {
255
259
  this.progressV = false
260
+ this.$emit('clearFiles')
256
261
  },
257
262
  getList() {
258
263
  const startIndex = (this.page.current - 1) * this.page.pageSize
@@ -10,10 +10,9 @@
10
10
  </template>
11
11
 
12
12
  <script>
13
- import { $lc } from '../../utils/i18n/index'
14
- import axios from '../../utils/axios'
13
+ import XEUtils from 'xe-utils'
15
14
  import getJsonc from '../../assets/getJsonc.js'
16
- import cloneDeep from 'lodash/cloneDeep'
15
+ import axios from '../../utils/axios'
17
16
  let enumData = {
18
17
  D001: '营业阶段',
19
18
  D002: '日终阶段',
@@ -64,7 +63,7 @@ export default {
64
63
  onMsg: true
65
64
  })
66
65
  this.tips = data
67
- this.list = cloneDeep(data)
66
+ this.list = XEUtils.clone(data, true)
68
67
  .map((tip) => {
69
68
  return `${tip.updateTime || tip.createtime}${tip.busNname}${enumData[tip.busState]}${
70
69
  tip.busCode.includes('CENTER') ? '(' + tip.currency + ')' : ''
@@ -40,13 +40,24 @@ function tipShow(el, arg) {
40
40
  let text = undefined
41
41
 
42
42
  if (el.$tooltipTitle) {
43
- if (!el.$tooltipTitleOverflow || el.clientWidth < el.scrollWidth) {
43
+ // 修改这里的判断逻辑
44
+ const shouldShowTooltip =
45
+ !el.$tooltipTitleOverflow || el.clientWidth < el.scrollWidth || el.offsetWidth < el.scrollWidth
46
+
47
+ if (shouldShowTooltip) {
44
48
  text = el.$tooltipTitle
45
49
  }
46
50
  } else {
47
51
  let _input = el.querySelector('.el-input__inner')
48
- if (_input && _input.clientWidth < _input.scrollWidth) {
49
- text = _input.value
52
+ if (_input) {
53
+ // 修改输入框的判断逻辑
54
+ const inputShouldShow = _input.clientWidth < _input.scrollWidth || _input.offsetWidth < _input.scrollWidth
55
+ if (inputShouldShow) {
56
+ text = _input.value
57
+ }
58
+ }
59
+ if (arg === 'tooltip' && (el.clientWidth < el.scrollWidth || el.offsetWidth < el.scrollWidth)) {
60
+ text = el.innerText
50
61
  }
51
62
  }
52
63
 
@@ -83,7 +94,6 @@ TitleDirective.install = (Vue) => {
83
94
  el.$tooltipTitle = binding.value
84
95
  let vmethods = vnode.child ? vnode.child.$options : vnode.context.$options
85
96
  vmethods.deactivated || (vmethods.deactivated = [])
86
-
87
97
  vmethods.deactivated.push(tipHide.bind(null, el))
88
98
  el.$tipShow = () => tipShow(el, binding.arg)
89
99
  el.$tipHide = () => tipHide(el)
@@ -93,7 +103,7 @@ TitleDirective.install = (Vue) => {
93
103
  update: function (el, binding) {
94
104
  el.$tooltipTitle = binding.value
95
105
  },
96
- unbind: function (el, binding) {
106
+ unbind: function (el) {
97
107
  tipHide(el)
98
108
  el.removeEventListener('mouseenter', el.$tipShow)
99
109
  el.removeEventListener('mouseleave', el.$tipHide)
@@ -1,69 +1,153 @@
1
- const addWaterMarker = (str, parentNode, font, textColor) => {
2
- const args = arguments[0]
3
- let can = document.createElement('canvas')
4
- const __wm = document.querySelector('.n20_v_watermark')
5
- let div = __wm || document.createElement('div')
6
-
7
- can.width = 220
8
- can.height = 180
9
- can.style.display = 'none'
10
- let cans = can.getContext('2d')
11
- cans.rotate((-20 * Math.PI) / 180)
12
- cans.font = font || '16px Microsoft JhengHei'
13
- cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)'
14
- cans.textAlign = 'left'
15
- cans.textBaseline = 'Middle'
16
- cans.fillText(str, can.width / 10, can.height / 2)
17
- const styleStr = `
18
- position:absolute;
19
- top:0;
20
- left:0;
21
- width:100%;
22
- height:100%;
23
- inset:0;
24
- z-index:99999;
25
- pointer-events:none;
26
- background-repeat:repeat;
27
- background-image:url('${can.toDataURL('image/png')}')`
28
- div.setAttribute('style', styleStr)
29
- div.classList.add('n20_v_watermark')
30
- if (!__wm) {
31
- parentNode.style.position = 'relative'
32
- parentNode.insertBefore(div, parentNode.firstChild)
1
+ class Watermark {
2
+ constructor() {
3
+ if (Watermark.instance) {
4
+ return Watermark.instance
5
+ }
6
+
7
+ this.isGlobalWatermarkActive = false
8
+ this.globalObserver = null
9
+ this.defaultOptions = {
10
+ text: '', // 水印文本
11
+ font: '16px Microsoft JhengHei', // 字体
12
+ textColor: 'rgba(180, 180, 180, 0.3)', // 文字颜色
13
+ width: 220, // 水印宽度
14
+ height: 180, // 水印高度
15
+ rotate: -20, // 旋转角度
16
+ zIndex: 99999, // z-index
17
+ global: false // 是否全局应用
18
+ }
19
+
20
+ Watermark.instance = this
33
21
  }
34
- const MutationObserver = window.MutationObserver || window.WebKitMutationObserver
35
- if (MutationObserver) {
36
- let mo = new MutationObserver(function () {
37
- const __wm = document.querySelector('.n20_v_watermark')
38
- // 只在__wm元素变动才重新调用 __canvasWM
39
- if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm) {
40
- // 避免一直触发
41
- mo.disconnect()
42
- mo = null
43
- addWaterMarker(str, parentNode, font, textColor)
44
- }
45
- })
46
22
 
47
- mo.observe(parentNode, {
48
- attributes: true,
49
- subtree: true,
50
- childList: true
23
+ createWatermarkCanvas(options) {
24
+ const can = document.createElement('canvas')
25
+ can.width = options.width
26
+ can.height = options.height
27
+ can.style.display = 'none'
28
+
29
+ const ctx = can.getContext('2d')
30
+ ctx.rotate((options.rotate * Math.PI) / 180)
31
+ ctx.font = options.font
32
+ ctx.fillStyle = options.textColor
33
+ ctx.textAlign = 'left'
34
+ ctx.textBaseline = 'Middle'
35
+ // 支持换行符分割文本
36
+ const lines = options.text.split('\n')
37
+ const lineHeight = parseInt(options.font) * 1.2 // 行高设为字体大小的1.2倍
38
+ const startY = can.height / 2 - ((lines.length - 1) * lineHeight) / 2
39
+
40
+ lines.forEach((line, index) => {
41
+ ctx.fillText(line, can.width / 10, startY + index * lineHeight)
51
42
  })
43
+
44
+ return can.toDataURL('image/png')
52
45
  }
53
46
 
54
- parentNode.style.position = 'relative'
55
- parentNode.insertBefore(div, parentNode.firstChild)
56
- }
47
+ createWatermarkContainer(options) {
48
+ const div = document.createElement('div')
49
+ div.className = 'n20_v_watermark'
50
+
51
+ const style = `
52
+ position:absolute;
53
+ top:0;
54
+ left:0;
55
+ width:100%;
56
+ height:100%;
57
+ inset:0;
58
+ z-index:${options.zIndex};
59
+ pointer-events:none;
60
+ background-repeat:repeat;
61
+ background-image:url('${this.createWatermarkCanvas(options)}')`
62
+
63
+ div.setAttribute('style', style)
64
+ return div
65
+ }
66
+
67
+ clearWatermark() {
68
+ const watermarks = document.querySelectorAll('.n20_v_watermark')
69
+ watermarks.forEach((wm) => wm.remove())
70
+ if (this.globalObserver) {
71
+ this.globalObserver.disconnect()
72
+ this.globalObserver = null
73
+ }
74
+ this.isGlobalWatermarkActive = false
75
+ }
76
+
77
+ addWatermark(el, options) {
78
+ // 如果全局水印已激活且当前不是全局调用,则跳过
79
+ if (this.isGlobalWatermarkActive && el !== document.body) {
80
+ return
81
+ }
57
82
 
58
- const watermark = {
59
- install: function (Vue) {
83
+ const parent = el || document.body
84
+ const watermark = this.createWatermarkContainer(options)
85
+
86
+ parent.style.position = 'relative'
87
+ parent.insertBefore(watermark, parent.firstChild)
88
+
89
+ // 如果是全局水印,设置状态
90
+ if (el === document.body) {
91
+ this.isGlobalWatermarkActive = true
92
+ }
93
+
94
+ // 监听DOM变化
95
+ const MutationObserver = window.MutationObserver || window.WebKitMutationObserver
96
+ if (MutationObserver) {
97
+ const observer = new MutationObserver(() => {
98
+ const wm = document.querySelector('.n20_v_watermark')
99
+ if (!wm || wm.getAttribute('style') !== watermark.getAttribute('style')) {
100
+ observer.disconnect()
101
+ this.addWatermark(el, options)
102
+ }
103
+ })
104
+
105
+ observer.observe(parent, {
106
+ attributes: true,
107
+ subtree: true,
108
+ childList: true
109
+ })
110
+
111
+ // 如果是全局水印,保存observer
112
+ if (el === document.body) {
113
+ this.globalObserver = observer
114
+ }
115
+ }
116
+ }
117
+
118
+ install(Vue, options = {}) {
119
+ // 合并配置
120
+ const config = { ...this.defaultOptions, ...options }
121
+
122
+ // 全局应用
123
+ if (config.global) {
124
+ this.clearWatermark()
125
+ this.addWatermark(document.body, config)
126
+ }
127
+
128
+ // 指令方式
60
129
  Vue.directive('watermark', {
61
- inserted: function (el, binding) {
62
- console.log(el, binding)
63
- addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor)
130
+ inserted: (el, binding) => {
131
+ if (this.isGlobalWatermarkActive) return
132
+
133
+ const directiveOptions = typeof binding.value === 'string' ? { text: binding.value } : binding.value
134
+ this.addWatermark(el, { ...config, ...directiveOptions, global: false })
135
+ },
136
+ unbind: (el) => {
137
+ if (!this.isGlobalWatermarkActive) {
138
+ this.clearWatermark()
139
+ }
64
140
  }
65
141
  })
66
142
  }
143
+
144
+ static getInstance() {
145
+ if (!Watermark.instance) {
146
+ Watermark.instance = new Watermark()
147
+ }
148
+ return Watermark.instance
149
+ }
67
150
  }
68
151
 
152
+ const watermark = Watermark.getInstance()
69
153
  export default watermark