n20-common-lib 3.1.15 → 3.1.16

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": "3.1.15",
3
+ "version": "3.1.16",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -56,6 +56,8 @@
56
56
  @import './v3/header.scss';
57
57
  @import './v3/footer.scss';
58
58
  @import './v3/radioCard.scss';
59
+ @import './v3/collapse.scss';
60
+ @import './v3/anchor.scss';
59
61
 
60
62
  //临时引入
61
63
  @import '../../components/ChildRange/style.scss';
@@ -144,21 +144,53 @@ iframe {
144
144
 
145
145
  /* 内外边距 — 4px 基础单位,所有值均为 4 的倍数 */
146
146
  @mixin pm-fn($n) {
147
- .pa-#{$n} { padding: #{$n}px; }
148
- .pt-#{$n} { padding-top: #{$n}px; }
149
- .pr-#{$n} { padding-right: #{$n}px; }
150
- .pb-#{$n} { padding-bottom: #{$n}px; }
151
- .pl-#{$n} { padding-left: #{$n}px; }
152
- .px-#{$n} { padding-left: #{$n}px; padding-right: #{$n}px; }
153
- .py-#{$n} { padding-top: #{$n}px; padding-bottom: #{$n}px; }
154
-
155
- .ma-#{$n} { margin: #{$n}px; }
156
- .mt-#{$n} { margin-top: #{$n}px; }
157
- .mr-#{$n} { margin-right: #{$n}px; }
158
- .mb-#{$n} { margin-bottom: #{$n}px; }
159
- .ml-#{$n} { margin-left: #{$n}px; }
160
- .mx-#{$n} { margin-left: #{$n}px; margin-right: #{$n}px; }
161
- .my-#{$n} { margin-top: #{$n}px; margin-bottom: #{$n}px; }
147
+ .pa-#{$n} {
148
+ padding: #{$n}px;
149
+ }
150
+ .pt-#{$n} {
151
+ padding-top: #{$n}px;
152
+ }
153
+ .pr-#{$n} {
154
+ padding-right: #{$n}px;
155
+ }
156
+ .pb-#{$n} {
157
+ padding-bottom: #{$n}px;
158
+ }
159
+ .pl-#{$n} {
160
+ padding-left: #{$n}px;
161
+ }
162
+ .px-#{$n} {
163
+ padding-left: #{$n}px;
164
+ padding-right: #{$n}px;
165
+ }
166
+ .py-#{$n} {
167
+ padding-top: #{$n}px;
168
+ padding-bottom: #{$n}px;
169
+ }
170
+
171
+ .ma-#{$n} {
172
+ margin: #{$n}px;
173
+ }
174
+ .mt-#{$n} {
175
+ margin-top: #{$n}px;
176
+ }
177
+ .mr-#{$n} {
178
+ margin-right: #{$n}px;
179
+ }
180
+ .mb-#{$n} {
181
+ margin-bottom: #{$n}px;
182
+ }
183
+ .ml-#{$n} {
184
+ margin-left: #{$n}px;
185
+ }
186
+ .mx-#{$n} {
187
+ margin-left: #{$n}px;
188
+ margin-right: #{$n}px;
189
+ }
190
+ .my-#{$n} {
191
+ margin-top: #{$n}px;
192
+ margin-bottom: #{$n}px;
193
+ }
162
194
  }
163
195
 
164
196
  $spacing-values: 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48, 64;
@@ -624,6 +656,7 @@ $list: 45, 60, 80, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 2
624
656
  /* height: 100%; 速龙浏览器下子元素无法继承父元素的高速度 */
625
657
  .n20-page-content {
626
658
  height: 100%;
659
+ padding-top: 4px;
627
660
  overflow-y: auto;
628
661
  overflow-x: hidden;
629
662
  }
@@ -0,0 +1,119 @@
1
+ .n20-anchor-v3 {
2
+ box-sizing: border-box;
3
+ display: inline-flex;
4
+ align-items: flex-start;
5
+ gap: 4px;
6
+
7
+ & *,
8
+ & *::before,
9
+ & *::after {
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ &.is-static {
14
+ position: static;
15
+ }
16
+
17
+ &__indicator {
18
+ flex-shrink: 0;
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ padding: 12px 8px;
23
+ background: #fff;
24
+ border: 1px solid var(--border-2, #e5e6eb);
25
+ border-radius: 15.5px;
26
+ filter: drop-shadow(0px 8px 10px rgba(0, 0, 0, 0.1));
27
+ }
28
+
29
+ &__indicator-bar {
30
+ flex-shrink: 0;
31
+ width: 2px;
32
+ height: 4px;
33
+ background: var(--fill-4, #c9cdd4);
34
+ transition: height 0.2s ease, background 0.2s ease;
35
+
36
+ & + & {
37
+ margin-top: 4px;
38
+ }
39
+
40
+ &.is-active {
41
+ height: 16px;
42
+ background: var(--primary-6, #007aff);
43
+ }
44
+ }
45
+
46
+ &__list {
47
+ display: flex;
48
+ flex-direction: column;
49
+ align-items: flex-start;
50
+ padding: 8px;
51
+ background: #fff;
52
+ border: 1px solid var(--border-2, #e5e6eb);
53
+ border-radius: 4px;
54
+ filter: drop-shadow(0px 8px 10px rgba(0, 0, 0, 0.1));
55
+ }
56
+
57
+ &__item {
58
+ flex-shrink: 0;
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 8px;
62
+ width: 104px;
63
+ padding: 4px 8px;
64
+ border-radius: 2px;
65
+ cursor: pointer;
66
+ user-select: none;
67
+ text-decoration: none;
68
+ color: inherit;
69
+ outline: none;
70
+ transition: background-color 0.15s ease;
71
+
72
+ &:hover,
73
+ &:focus,
74
+ &:active {
75
+ text-decoration: none;
76
+ color: inherit;
77
+ }
78
+
79
+ &:not(.is-active):not(.is-disabled):hover {
80
+ background: var(--fill-1, #f7f8fa);
81
+ }
82
+
83
+ &.is-active {
84
+ .n20-anchor-v3__bar {
85
+ height: 12px;
86
+ background: var(--primary-6, #007aff);
87
+ }
88
+ .n20-anchor-v3__label {
89
+ color: var(--primary-6, #007aff);
90
+ }
91
+ }
92
+
93
+ &.is-disabled {
94
+ cursor: not-allowed;
95
+ pointer-events: none;
96
+ opacity: 0.5;
97
+ }
98
+ }
99
+
100
+ &__bar {
101
+ flex-shrink: 0;
102
+ width: 2px;
103
+ height: 2px;
104
+ background: var(--fill-4, #c9cdd4);
105
+ border-radius: 0 4px 4px 0;
106
+ transition: height 0.15s ease, background 0.15s ease;
107
+ }
108
+
109
+ &__label {
110
+ flex-shrink: 0;
111
+ font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
112
+ font-size: 13px;
113
+ font-weight: 400;
114
+ line-height: 22px;
115
+ color: var(--text-1, #1d2129);
116
+ white-space: nowrap;
117
+ word-break: break-word;
118
+ }
119
+ }
@@ -0,0 +1,71 @@
1
+ .n20-page-collapse {
2
+ background: $--background-color-base;
3
+ padding: 12px 16px;
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 12px;
7
+ box-sizing: border-box;
8
+
9
+ &__header {
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ cursor: pointer;
14
+ user-select: none;
15
+ }
16
+
17
+ &__title {
18
+ font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
19
+ font-size: 14px;
20
+ font-weight: 500;
21
+ line-height: 22px;
22
+ color: $--color-info;
23
+ }
24
+
25
+ &__action {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 4px;
29
+ padding: 1px 4px;
30
+ border-radius: 2px;
31
+ cursor: pointer;
32
+ overflow: hidden;
33
+ }
34
+
35
+ &__action-text {
36
+ font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
37
+ font-size: 13px;
38
+ font-weight: 400;
39
+ line-height: 22px;
40
+ color: $--color-primary;
41
+ }
42
+
43
+ &__arrow {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ color: $--color-primary;
47
+ transition: transform 0.2s ease;
48
+ }
49
+
50
+ &__arrow-up {
51
+ transform: rotate(0deg);
52
+ }
53
+
54
+ &__arrow-down {
55
+ transform: rotate(180deg);
56
+ }
57
+
58
+ &__body-wrap {
59
+ display: grid;
60
+ grid-template-rows: 0fr;
61
+ transition: grid-template-rows 0.3s ease;
62
+
63
+ &.is-expanded {
64
+ grid-template-rows: 1fr;
65
+ }
66
+ }
67
+
68
+ &__body {
69
+ overflow: hidden;
70
+ }
71
+ }
@@ -0,0 +1,226 @@
1
+ <template>
2
+ <div
3
+ :class="anchorClasses"
4
+ :style="containerStyle"
5
+ @mouseenter="onMouseEnter"
6
+ @mouseleave="onMouseLeave"
7
+ >
8
+ <div v-show="menuVisible" class="n20-anchor-v3__list">
9
+ <a
10
+ v-for="item in normalizedOptions"
11
+ :key="item.value"
12
+ :href="'#' + item.value"
13
+ :class="['n20-anchor-v3__item', { 'is-active': isActive(item), 'is-disabled': item.disabled }]"
14
+ @click="handleClick(item, $event)"
15
+ >
16
+ <span class="n20-anchor-v3__bar"></span>
17
+ <span class="n20-anchor-v3__label">{{ item.label }}</span>
18
+ </a>
19
+ </div>
20
+ <div class="n20-anchor-v3__indicator">
21
+ <span
22
+ v-for="(item, idx) in normalizedOptions"
23
+ :key="`ind-${item.value}`"
24
+ :class="['n20-anchor-v3__indicator-bar', { 'is-active': idx === activeIndex }]"
25
+ ></span>
26
+ </div>
27
+ </div>
28
+ </template>
29
+
30
+ <script>
31
+ export default {
32
+ name: 'AnchorV3',
33
+ props: {
34
+ options: {
35
+ type: Array,
36
+ required: true,
37
+ default: () => []
38
+ },
39
+ position: {
40
+ type: String,
41
+ default: 'fixed',
42
+ validator: (v) => ['fixed', 'absolute', 'static'].includes(v)
43
+ },
44
+ offsetTop: {
45
+ type: Number,
46
+ default: 80
47
+ },
48
+ offsetRight: {
49
+ type: Number,
50
+ default: 24
51
+ },
52
+ scrollSpy: {
53
+ type: Boolean,
54
+ default: true
55
+ },
56
+ scrollContainer: {
57
+ type: [String, HTMLElement],
58
+ default: null
59
+ }
60
+ },
61
+ data() {
62
+ return {
63
+ currentHash: '',
64
+ ticking: false,
65
+ sortedOptions: [],
66
+ scrollTarget: null,
67
+ menuVisible: false
68
+ }
69
+ },
70
+ computed: {
71
+ anchorClasses() {
72
+ return ['n20-anchor-v3', `is-${this.position}`]
73
+ },
74
+ normalizedOptions() {
75
+ return (this.options || []).filter((o) => o && o.value != null && o.label != null)
76
+ },
77
+ activeIndex() {
78
+ return this.normalizedOptions.findIndex((o) => this.isActive(o))
79
+ },
80
+ containerStyle() {
81
+ if (this.position === 'static') return {}
82
+ return {
83
+ position: this.position,
84
+ top: `${this.offsetTop}px`,
85
+ right: `${this.offsetRight}px`,
86
+ zIndex: 100
87
+ }
88
+ },
89
+ triggerY() {
90
+ return this.offsetTop
91
+ }
92
+ },
93
+ watch: {
94
+ normalizedOptions() {
95
+ this.sortedOptions = []
96
+ },
97
+ scrollContainer() {
98
+ if (this.scrollSpy) {
99
+ this.bindScrollTarget()
100
+ }
101
+ },
102
+ scrollSpy(val) {
103
+ if (val) {
104
+ this.bindScrollTarget()
105
+ } else {
106
+ this.unbindScrollTarget()
107
+ }
108
+ }
109
+ },
110
+ mounted() {
111
+ this.currentHash = (typeof window !== 'undefined' ? window.location.hash : '') || ''
112
+ window.addEventListener('hashchange', this.onHashChange)
113
+ if (this.scrollSpy) {
114
+ this.bindScrollTarget()
115
+ }
116
+ },
117
+ beforeDestroy() {
118
+ window.removeEventListener('hashchange', this.onHashChange)
119
+ if (this.scrollSpy) {
120
+ this.unbindScrollTarget()
121
+ }
122
+ },
123
+ methods: {
124
+ isActive(item) {
125
+ return '#' + item.value === this.currentHash
126
+ },
127
+ handleClick(item, e) {
128
+ if (item.disabled) {
129
+ e.preventDefault()
130
+ return
131
+ }
132
+ this.$emit('click', item)
133
+ },
134
+ onMouseEnter() {
135
+ this.menuVisible = true
136
+ },
137
+ onMouseLeave() {
138
+ this.menuVisible = false
139
+ },
140
+ onHashChange() {
141
+ this.currentHash = window.location.hash || ''
142
+ },
143
+ resolveScrollTarget() {
144
+ if (typeof window === 'undefined' || typeof document === 'undefined') return null
145
+ const c = this.scrollContainer
146
+ if (c === 'window') return window
147
+ if (typeof c === 'string' && c) return document.querySelector(c) || this.findScrollableParent() || window
148
+ if (c && c.nodeType === 1) return c
149
+ return this.findScrollableParent() || window
150
+ },
151
+ findScrollableParent() {
152
+ let el = this.$el && this.$el.parentElement
153
+ while (el && el !== document.documentElement) {
154
+ const style = window.getComputedStyle(el)
155
+ const overflowY = style.overflowY
156
+ const overflow = style.overflow
157
+ if (overflowY === 'auto' || overflowY === 'scroll' || overflow === 'auto' || overflow === 'scroll') {
158
+ return el
159
+ }
160
+ el = el.parentElement
161
+ }
162
+ return null
163
+ },
164
+ bindScrollTarget() {
165
+ this.unbindScrollTarget()
166
+ this.scrollTarget = this.resolveScrollTarget()
167
+ if (this.scrollTarget) {
168
+ this.scrollTarget.addEventListener('scroll', this.onScroll, { passive: true })
169
+ this.$nextTick(() => this.updateActiveSection())
170
+ }
171
+ },
172
+ unbindScrollTarget() {
173
+ if (this.scrollTarget) {
174
+ this.scrollTarget.removeEventListener('scroll', this.onScroll)
175
+ this.scrollTarget = null
176
+ }
177
+ },
178
+ onScroll() {
179
+ if (this.ticking) return
180
+ this.ticking = true
181
+ requestAnimationFrame(() => {
182
+ this.updateActiveSection()
183
+ this.ticking = false
184
+ })
185
+ },
186
+ getSortedOptions() {
187
+ if (this.sortedOptions.length === this.normalizedOptions.length && this.sortedOptions.length > 0) {
188
+ return this.sortedOptions
189
+ }
190
+ const pageY = (typeof window !== 'undefined' ? window.pageYOffset : 0) || 0
191
+ this.sortedOptions = [...this.normalizedOptions].sort((a, b) => {
192
+ const elA = typeof document !== 'undefined' ? document.getElementById(String(a.value)) : null
193
+ const elB = typeof document !== 'undefined' ? document.getElementById(String(b.value)) : null
194
+ const topA = elA ? elA.getBoundingClientRect().top + pageY : 0
195
+ const topB = elB ? elB.getBoundingClientRect().top + pageY : 0
196
+ return topA - topB
197
+ })
198
+ return this.sortedOptions
199
+ },
200
+ updateActiveSection() {
201
+ if (typeof window === 'undefined' || typeof document === 'undefined') return
202
+ const triggerY = this.triggerY
203
+ const sorted = this.getSortedOptions()
204
+ let activeValue = null
205
+ for (const option of sorted) {
206
+ const el = document.getElementById(String(option.value))
207
+ if (!el) continue
208
+ const rect = el.getBoundingClientRect()
209
+ if (rect.top <= triggerY) {
210
+ activeValue = option.value
211
+ } else {
212
+ break
213
+ }
214
+ }
215
+ if (activeValue) {
216
+ const newHash = '#' + activeValue
217
+ if (this.currentHash !== newHash) {
218
+ this.currentHash = newHash
219
+ }
220
+ } else if (this.currentHash) {
221
+ this.currentHash = ''
222
+ }
223
+ }
224
+ }
225
+ }
226
+ </script>
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <div class="n20-page-collapse">
3
+ <div class="n20-page-collapse__header" @click="toggle">
4
+ <div class="n20-page-collapse__title">
5
+ <slot name="title">{{ title }}</slot>
6
+ </div>
7
+ <div class="n20-page-collapse__action">
8
+ <span class="n20-page-collapse__action-text">{{ actionText }}</span>
9
+ <i
10
+ class="n20-page-collapse__arrow"
11
+ :class="isExpanded ? 'n20-page-collapse__arrow-up' : 'n20-page-collapse__arrow-down'"
12
+ >
13
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
14
+ <path d="M3 9.5L7 5.5L11 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
15
+ </svg>
16
+ </i>
17
+ </div>
18
+ </div>
19
+ <div class="n20-page-collapse__body-wrap" :class="{ 'is-expanded': isExpanded }">
20
+ <div class="n20-page-collapse__body">
21
+ <slot></slot>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </template>
26
+
27
+ <script>
28
+ import { $lc } from '../../../utils/i18n/index'
29
+
30
+ export default {
31
+ name: 'Collapse',
32
+ props: {
33
+ title: {
34
+ type: String,
35
+ default: ''
36
+ },
37
+ expanded: {
38
+ type: Boolean,
39
+ default: null
40
+ }
41
+ },
42
+ data() {
43
+ return {
44
+ localExpanded: true
45
+ }
46
+ },
47
+ computed: {
48
+ isControlled() {
49
+ return this.expanded !== null
50
+ },
51
+ isExpanded() {
52
+ return this.isControlled ? this.expanded : this.localExpanded
53
+ },
54
+ actionText() {
55
+ return this.isExpanded ? $lc('收起') : $lc('展开')
56
+ }
57
+ },
58
+ methods: {
59
+ toggle() {
60
+ const next = !this.isExpanded
61
+ if (!this.isControlled) {
62
+ this.localExpanded = next
63
+ }
64
+ this.$emit('update:expanded', next)
65
+ this.$emit('change', next)
66
+ }
67
+ }
68
+ }
69
+ </script>
@@ -3,11 +3,11 @@
3
3
  <div class="n20-page-header-left">
4
4
  <div v-if="showBack" class="n20-page-header-v3__back" @click="handleBack">
5
5
  <i :class="icon"></i>
6
- <span class="n20-page-header-v3__back-text">{{ backText }}</span>
6
+ <span class="n20-page-header-v3__back-text">{{ title }}</span>
7
7
  </div>
8
8
  <div v-if="showBack" class="n20-page-header-v3__divider"></div>
9
9
  <div class="n20-page-header-v3__title">
10
- <slot name="title">{{ title }}</slot>
10
+ <slot name="content">{{ content }}</slot>
11
11
  </div>
12
12
  </div>
13
13
  <div :class="`n20-page-header-right ${extraClass}`">
@@ -28,11 +28,11 @@ export default {
28
28
  type: Boolean,
29
29
  default: true
30
30
  },
31
- backText: {
31
+ title: {
32
32
  type: String,
33
33
  default: '返回'
34
34
  },
35
- title: {
35
+ content: {
36
36
  type: String,
37
37
  default: ''
38
38
  },
@@ -47,7 +47,7 @@ export default {
47
47
  },
48
48
  methods: {
49
49
  handleBack() {
50
- this.$emit('onBack')
50
+ this.$emit('back')
51
51
  }
52
52
  }
53
53
  }
@@ -190,7 +190,7 @@ const keysDefault = {
190
190
  }
191
191
 
192
192
  export default {
193
- name: 'UploadList',
193
+ name: 'FileUploadTableV3',
194
194
  components: {
195
195
  Upload,
196
196
  Dialog,
package/src/index.js CHANGED
@@ -124,6 +124,8 @@ import TimePicker from './components/TimePicker/index.vue'
124
124
  import Tree from './components/Tree/index.vue'
125
125
  import Upload from './components/Upload/index.vue'
126
126
  import UploadMsg from './components/Upload/uploadMsg.vue'
127
+ import AnchorV3 from './components/v3/Anchor/index.vue'
128
+ import Collapse from './components/v3/Collapse/index.vue'
127
129
  import Footer from './components/v3/Footer/index.vue'
128
130
  import Header from './components/v3/Header/index.vue'
129
131
  import RadioCard from './components/v3/RadioCard/index.vue'
@@ -287,7 +289,9 @@ const components = [
287
289
  SecondaryTabV3,
288
290
  Header,
289
291
  Footer,
290
- RadioCard
292
+ RadioCard,
293
+ Collapse,
294
+ AnchorV3
291
295
  ]
292
296
 
293
297
  const install = function (Vue, opts = { prefix: 'Cl', i18nConfig: {} }) {
@@ -437,6 +441,8 @@ export {
437
441
  Header,
438
442
  Footer,
439
443
  RadioCard,
444
+ Collapse,
445
+ AnchorV3,
440
446
  asyncGetRelaNos,
441
447
  // 方法
442
448
  auth,