af-mobile-client-vue3 1.0.90 → 1.0.93

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,13 +1,10 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.0.90",
4
+ "version": "1.0.93",
5
+ "packageManager": "pnpm@10.7.0",
5
6
  "description": "Vue + Vite component lib",
6
7
  "license": "MIT",
7
- "engines": {
8
- "node": ">=18.12.0",
9
- "pnpm": ">=8.15.0"
10
- },
11
8
  "scripts": {
12
9
  "dev": "cross-env MOCK_SERVER_PORT=8086 vite",
13
10
  "build": "vue-tsc --noEmit && vite build",
@@ -19,90 +16,105 @@
19
16
  "release": "bumpp --commit --push --tag"
20
17
  },
21
18
  "dependencies": {
22
- "@micro-zoe/micro-app": "1.0.0-rc.4",
23
- "@unhead/vue": "^1.8.11",
24
- "@vant/area-data": "^1.5.1",
25
- "@vant/touch-emulator": "^1.4.0",
26
- "@vant/use": "^1.6.0",
27
- "@vueuse/core": "^10.9.0",
19
+ "@micro-zoe/micro-app": "1.0.0-rc.24",
20
+ "@vant/area-data": "^2.0.0",
28
21
  "animate.css": "^4.1.1",
29
- "axios": "^1.6.7",
30
22
  "crypto-js": "^4.2.0",
31
- "echarts": "^5.5.0",
23
+ "qs": "^6.14.0",
24
+ "store": "^2.0.12",
25
+ "@unhead/vue": "^2.0.5",
26
+ "@vant/touch-emulator": "^1.4.0",
27
+ "@vant/use": "^1.6.0",
28
+ "@vueuse/core": "^13.1.0",
29
+ "axios": "^1.8.4",
30
+ "echarts": "^5.6.0",
32
31
  "lodash-es": "^4.17.21",
33
32
  "nprogress": "^0.2.0",
34
- "pinia": "^2.1.7",
35
- "pinia-plugin-persistedstate": "^3.2.1",
36
- "pnpm": "^9.4.0",
37
- "qs": "^6.11.2",
33
+ "pinia": "^3.0.2",
34
+ "pinia-plugin-persistedstate": "^4.2.0",
38
35
  "resize-detector": "^0.3.0",
39
- "store": "^2.0.12",
40
- "vant": "^4.9.0",
36
+ "vant": "^4.9.18",
41
37
  "vconsole": "^3.15.1",
42
- "vue": "^3.4.21",
43
- "vue-router": "^4.3.0"
38
+ "vue": "^3.5.13",
39
+ "vue-i18n": "^11.1.3",
40
+ "vue-router": "^4.5.0"
44
41
  },
45
42
  "devDependencies": {
46
- "@antfu/eslint-config": "2.8.0",
47
43
  "@types/crypto-js": "^4.2.2",
48
- "@types/lodash-es": "^4.17.12",
49
- "@types/node": "^20.11.25",
50
- "@types/nprogress": "^0.2.3",
51
44
  "@types/store": "^2.0.5",
52
- "@unocss/eslint-plugin": "^0.58.5",
53
- "@unocss/preset-rem-to-px": "^0.58.5",
54
- "@vitejs/plugin-legacy": "^5.3.1",
55
- "@vitejs/plugin-vue": "^5.0.4",
56
- "autoprefixer": "^10.4.18",
57
- "bumpp": "^9.4.0",
58
- "commitizen": "^4.3.0",
59
- "consola": "^3.2.3",
60
- "cross-env": "^7.0.3",
45
+ "commitizen": "^4.3.1",
61
46
  "cz-emoji-chinese": "^0.3.1",
62
- "eslint": "npm:eslint-ts-patch@8.57.0-0",
63
- "eslint-ts-patch": "8.57.0-0",
64
- "husky": "^9.0.11",
65
- "less": "^4.2.0",
66
- "mockjs": "^1.1.0",
67
- "postcss-mobile-forever": "^4.1.1",
68
- "rollup": "^4.12.1",
69
- "terser": "^5.29.1",
70
- "typescript": "^5.4.2",
71
- "unocss": "^0.58.5",
72
- "unplugin-auto-import": "^0.17.5",
73
- "unplugin-vue-components": "^0.26.0",
74
- "unplugin-vue-router": "^0.8.4",
75
- "vite": "^5.1.5",
47
+ "eslint-ts-patch": "^8.57.0-0",
48
+ "husky": "^9.1.7",
76
49
  "vite-plugin-compression": "^0.5.1",
77
- "vite-plugin-mock-dev-server": "^1.4.7",
78
- "vite-plugin-pwa": "^0.19.2",
79
- "vite-plugin-sitemap": "^0.5.3",
80
50
  "vite-plugin-svg-icons": "^2.0.1",
81
- "vite-plugin-vconsole": "^2.1.1",
82
51
  "vite-plugin-vue-layouts": "^0.11.0",
83
- "vitest": "^1.3.1",
84
- "vue-tsc": "^2.0.6"
52
+ "@antfu/eslint-config": "^4.12.0",
53
+ "@commitlint/cli": "^19.8.0",
54
+ "@commitlint/config-conventional": "^19.8.0",
55
+ "@commitlint/types": "^19.8.0",
56
+ "@iconify-json/carbon": "^1.2.8",
57
+ "@intlify/unplugin-vue-i18n": "^6.0.5",
58
+ "@types/lodash-es": "^4.17.12",
59
+ "@types/node": "^22.14.1",
60
+ "@types/nprogress": "^0.2.3",
61
+ "@unocss/eslint-plugin": "^66.1.0-beta.11",
62
+ "@unocss/preset-rem-to-px": "66.1.0-beta.11",
63
+ "@vant/auto-import-resolver": "^1.3.0",
64
+ "@vitejs/plugin-legacy": "^6.0.2",
65
+ "@vitejs/plugin-vue": "^5.2.3",
66
+ "autoprefixer": "^10.4.21",
67
+ "bumpp": "^10.1.0",
68
+ "consola": "^3.4.2",
69
+ "cross-env": "^7.0.3",
70
+ "eslint": "^9.24.0",
71
+ "eslint-plugin-format": "^1.0.1",
72
+ "less": "^4.3.0",
73
+ "lint-staged": "^15.5.1",
74
+ "mockjs": "^1.1.0",
75
+ "postcss-mobile-forever": "^5.0.0",
76
+ "rollup": "^4.40.0",
77
+ "simple-git-hooks": "^2.12.1",
78
+ "terser": "^5.39.0",
79
+ "typescript": "^5.8.3",
80
+ "unocss": "^66.1.0-beta.11",
81
+ "unplugin-auto-import": "^19.1.2",
82
+ "unplugin-vue-components": "^28.4.1",
83
+ "unplugin-vue-router": "^0.12.0",
84
+ "vite": "^6.2.6",
85
+ "vite-plugin-mock-dev-server": "^1.8.5",
86
+ "vite-plugin-pwa": "^1.0.0",
87
+ "vite-plugin-sitemap": "^0.7.1",
88
+ "vite-plugin-vconsole": "^2.1.1",
89
+ "vite-plugin-vue-devtools": "^7.7.2",
90
+ "vitest": "^3.1.1",
91
+ "vue-tsc": "^2.2.8"
85
92
  },
86
93
  "pnpm": {
94
+ "allowedDeprecatedVersions": {
95
+ "glob": "7.2.3",
96
+ "inflight": "1.0.6",
97
+ "sourcemap-codec": "1.4.8"
98
+ },
87
99
  "peerDependencyRules": {
88
- "ignoreMissing": [
89
- "postcss",
90
- "esbuild"
91
- ],
92
100
  "allowedVersions": {
93
- "rollup": "^4.x"
101
+ "typescript": "5.8.2"
94
102
  }
95
- }
96
- },
97
- "config": {
98
- "commitizen": {
99
- "path": "./node_modules/cz-emoji-chinese"
100
103
  },
101
- "cz-emoji-chinese": {
102
- "skipQuestions": [
103
- "body",
104
- "scope"
105
- ]
106
- }
104
+ "onlyBuiltDependencies": [
105
+ "core-js",
106
+ "esbuild",
107
+ "simple-git-hooks"
108
+ ]
109
+ },
110
+ "resolutions": {
111
+ "vite": "^6.2.4"
112
+ },
113
+ "simple-git-hooks": {
114
+ "pre-commit": "pnpm lint-staged",
115
+ "commit-msg": "pnpm commitlint $1"
116
+ },
117
+ "lint-staged": {
118
+ "*": "eslint --fix"
107
119
  }
108
120
  }
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { ref } from 'vue'
3
2
  import { getDictItemByValue } from '@af-mobile-client-vue3/utils/dictUtil'
3
+ import { ref } from 'vue'
4
4
 
5
5
  const { serviceName, dictName, dictValue } = withDefaults(defineProps<{
6
6
  serviceName?: string
@@ -4,6 +4,7 @@ import {
4
4
  BackTop as VanBackTop,
5
5
  Button as VanButton,
6
6
  Col as VanCol,
7
+ Icon as VanIcon,
7
8
  List as VanList,
8
9
  Popover as VanPopover,
9
10
  PullRefresh as VanPullRefresh,
@@ -31,7 +32,7 @@ const { configName, serviceName, fixQueryForm } = withDefaults(defineProps<{
31
32
  serviceName: undefined,
32
33
  })
33
34
 
34
- const emit = defineEmits(['toDetail', 'addOption', 'handleCall', 'handleLocation'])
35
+ const emit = defineEmits([])
35
36
 
36
37
  const router = useRouter()
37
38
 
@@ -54,6 +55,9 @@ const detailColumns = ref([])
54
55
  // 标签列
55
56
  const tagList = ref([])
56
57
 
58
+ // 标题按钮列
59
+ const btnList = ref([])
60
+
57
61
  // 底部列
58
62
  const footColumns = ref([])
59
63
 
@@ -89,7 +93,7 @@ const pageSize = 20
89
93
 
90
94
  const searchValue = ref('')
91
95
 
92
- const inputSpan = ref(20)
96
+ const inputSpan = ref(16)
93
97
 
94
98
  // 数据加载状态
95
99
  const loading = ref(false)
@@ -144,6 +148,9 @@ function initComponent() {
144
148
  allActions.value.push({ text: item.actionArr[j].text, func: item.actionArr[j].func })
145
149
  }
146
150
 
151
+ if (item.showTitleBtn)
152
+ btnList.value.push(item)
153
+
147
154
  if (result.showSortIcon && item.sortable) {
148
155
  orderList.value.push({
149
156
  title: item.title,
@@ -306,15 +313,10 @@ function addOption() {
306
313
  // emit('addOption', totalCount.value)
307
314
  }
308
315
 
309
- // 处理拨打电话
310
- // function handleCall(item: any) {
311
- // emit('handleCall', item)
312
- // }
313
-
314
- // 处理查看位置
315
- // function handleLocation(item: any) {
316
- // emit('handleLocation', item)
317
- // }
316
+ // 处理按钮点击
317
+ function handleButtonClick(btn, item) {
318
+ emit(`${btn.btnIcon}`, item)
319
+ }
318
320
  </script>
319
321
 
320
322
  <template>
@@ -330,7 +332,7 @@ function addOption() {
330
332
  @search="onRefresh"
331
333
  />
332
334
  </VanCol>
333
- <VanCol span="4" class="search-icon">
335
+ <VanCol span="8" class="search-icon">
334
336
  <XCellListFilter
335
337
  v-model:sortord-val="sortordVal"
336
338
  v-model:order-val="orderVal"
@@ -380,27 +382,22 @@ function addOption() {
380
382
  />
381
383
  </p>
382
384
  </div>
383
- <!-- <div class="action-buttons">
384
- <VanButton
385
- class="action-button"
386
- icon="phone"
387
- size="small"
388
- @click.stop="handleCall(item)"
389
- />
385
+ <div class="action-buttons">
390
386
  <VanButton
387
+ v-for="btn in btnList"
388
+ :key="btn.dataIndex"
391
389
  class="action-button"
392
- icon="location"
390
+ :icon="btn.btnIcon"
393
391
  size="small"
394
- @click.stop="handleLocation(item)"
392
+ @click.stop="handleButtonClick(btn, item)"
395
393
  />
396
- </div> -->
394
+ </div>
397
395
  </div>
398
396
  </VanCol>
399
397
  </VanRow>
400
398
  <VanRow gutter="20" class="card_item_details" @click="emit('toDetail', item)">
401
399
  <VanCol v-for="column of detailColumns" :key="`details_${column.dataIndex}`" :span="column.span">
402
400
  <p>
403
- {{ column.title }}:
404
401
  <XBadge
405
402
  :style="handleFunctionStyle(column.styleFunctionForValue, item[column.dataIndex])"
406
403
  :dict-name="column.dictName" :dict-value="item[column.dataIndex]"
@@ -426,11 +423,15 @@ function addOption() {
426
423
  size="medium"
427
424
  class="tag-item"
428
425
  >
429
- {{ column.title }}:<XBadge
430
- :dict-name="column.dictName"
431
- :dict-value="item[column.dataIndex]"
432
- :service-name="serviceName"
433
- />
426
+ <div class="tag-content">
427
+ <VanIcon v-if="column.tagIcon" :name="column.tagIcon" class="tag-icon" />
428
+ <span class="tag-title">{{ column.title }}:</span>
429
+ <XBadge
430
+ :dict-name="column.dictName"
431
+ :dict-value="item[column.dataIndex]"
432
+ :service-name="serviceName"
433
+ />
434
+ </div>
434
435
  </VanTag>
435
436
  </VanCol>
436
437
  </div>
@@ -610,6 +611,20 @@ function addOption() {
610
611
  display: inline-flex;
611
612
  align-items: center;
612
613
  }
614
+ .tag-content {
615
+ display: flex;
616
+ align-items: center;
617
+ white-space: nowrap;
618
+ }
619
+ .tag-icon {
620
+ margin-right: 4px;
621
+ font-size: 14px;
622
+ display: inline-flex;
623
+ align-items: center;
624
+ }
625
+ .tag-title {
626
+ font-weight: normal;
627
+ }
613
628
  }
614
629
  }
615
630
  }
@@ -667,9 +682,10 @@ function addOption() {
667
682
  .search-icon {
668
683
  display: flex;
669
684
  align-items: center;
670
- justify-content: center;
685
+ justify-content: flex-end;
671
686
  height: 100%;
672
- padding: var(--van-search-padding);
687
+ padding: 0;
688
+ flex-shrink: 0;
673
689
  }
674
690
  }
675
691
 
@@ -0,0 +1,207 @@
1
+ <script setup lang="ts">
2
+ import { nextTick, onMounted, onUnmounted, ref } from 'vue'
3
+ import { Icon as VanIcon } from 'vant'
4
+ import { startScanAnimation, stopScanAnimation } from './startScanAnimation'
5
+
6
+ // 扫码框的引用
7
+ const scannerRef = ref<HTMLDivElement>()
8
+ // 扫码动画的引用
9
+ const scanLineRef = ref<HTMLDivElement>()
10
+ // 闪光灯状态
11
+ const isLightOn = ref(false)
12
+
13
+ // 初始化扫描动画
14
+ async function initScanAnimation() {
15
+ // 确保元素已渲染完成
16
+ await nextTick()
17
+ // 添加延迟确保元素尺寸已计算完成
18
+ setTimeout(() => {
19
+ if (scannerRef.value && scanLineRef.value) {
20
+ // 设置初始位置
21
+ scanLineRef.value.style.top = '0px'
22
+ startScanAnimation(scanLineRef, scannerRef)
23
+ }
24
+ }, 300)
25
+ }
26
+
27
+ // 切换闪光灯
28
+ function toggleLight() {
29
+ isLightOn.value = !isLightOn.value
30
+ }
31
+
32
+ onMounted(() => {
33
+ initScanAnimation()
34
+ })
35
+
36
+ onUnmounted(() => {
37
+ stopScanAnimation()
38
+ })
39
+ </script>
40
+
41
+ <template>
42
+ <div class="qr-scanner">
43
+ <div class="scanner-container">
44
+ <div class="scanner-bg">
45
+ <div ref="scannerRef" class="scanner-box">
46
+ <div class="corner-lt" />
47
+ <div class="corner-rt" />
48
+ <div class="corner-lb" />
49
+ <div class="corner-rb" />
50
+ <div ref="scanLineRef" class="scan-line" />
51
+ <div class="light-btn" @click="toggleLight">
52
+ <VanIcon :name="isLightOn ? 'bulb-o' : 'bulb'" class="light-icon" />
53
+ </div>
54
+ </div>
55
+ <div class="scanner-tip">
56
+ 将二维码放入框内,即可自动扫描
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </template>
62
+
63
+ <style scoped lang="less">
64
+ .qr-scanner {
65
+ width: 100%;
66
+ height: 100%;
67
+ background-color: #fff;
68
+ display: flex;
69
+ flex-direction: column;
70
+ border-radius: 8px;
71
+ overflow: hidden;
72
+
73
+ .scanner-container {
74
+ flex: 1;
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: center;
78
+ justify-content: center;
79
+ padding: 20px;
80
+ background-color: #fff;
81
+
82
+ .scanner-bg {
83
+ width: 320px;
84
+ height: 320px;
85
+ background-color: #f0f0f0;
86
+ border-radius: 8px;
87
+ padding: 20px;
88
+ display: flex;
89
+ flex-direction: column;
90
+ align-items: center;
91
+ justify-content: flex-start;
92
+ padding-top: 40px;
93
+ }
94
+
95
+ .scanner-box {
96
+ width: 200px;
97
+ height: 200px;
98
+ position: relative;
99
+ background-color: transparent;
100
+ border: 1px solid #1989fa;
101
+ border-radius: 0;
102
+ box-sizing: border-box;
103
+ margin: 0;
104
+
105
+ &::before {
106
+ content: '';
107
+ position: absolute;
108
+ top: 0;
109
+ left: 0;
110
+ right: 0;
111
+ bottom: 0;
112
+ border: none;
113
+ z-index: 1;
114
+ }
115
+
116
+ .corner-lt,
117
+ .corner-rt,
118
+ .corner-lb,
119
+ .corner-rb {
120
+ position: absolute;
121
+ width: 24px;
122
+ height: 24px;
123
+ border-color: #1989fa;
124
+ border-style: solid;
125
+ z-index: 2;
126
+ margin: 0;
127
+ }
128
+
129
+ .corner-lt {
130
+ top: -3px;
131
+ left: -3px;
132
+ border-width: 3px 0 0 3px;
133
+ border-radius: 0;
134
+ margin: -8px 0 0 -8px;
135
+ }
136
+
137
+ .corner-rt {
138
+ top: -3px;
139
+ right: -3px;
140
+ border-width: 3px 3px 0 0;
141
+ border-radius: 0;
142
+ margin: -8px -8px 0 0;
143
+ }
144
+
145
+ .corner-lb {
146
+ bottom: -3px;
147
+ left: -3px;
148
+ border-width: 0 0 3px 3px;
149
+ border-radius: 0;
150
+ margin: 0 0 -8px -8px;
151
+ }
152
+
153
+ .corner-rb {
154
+ bottom: -3px;
155
+ right: -3px;
156
+ border-width: 0 3px 3px 0;
157
+ border-radius: 0;
158
+ margin: 0 -8px -8px 0;
159
+ }
160
+
161
+ .scan-line {
162
+ position: absolute;
163
+ left: 0;
164
+ top: 0;
165
+ width: 100%;
166
+ height: 2px;
167
+ background: linear-gradient(to right, transparent, #1989fa, transparent);
168
+ box-shadow: 0 0 4px #1989fa;
169
+ z-index: 3;
170
+ }
171
+
172
+ .light-btn {
173
+ position: absolute;
174
+ right: -20px;
175
+ bottom: -20px;
176
+ width: 40px;
177
+ height: 40px;
178
+ border-radius: 50%;
179
+ background-color: rgba(44, 44, 44, 0.5);
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: center;
183
+ cursor: pointer;
184
+ z-index: 3;
185
+
186
+ .light-icon {
187
+ font-size: 20px;
188
+ color: #fff;
189
+ }
190
+
191
+ &:active {
192
+ opacity: 0.8;
193
+ }
194
+ }
195
+ }
196
+
197
+ .scanner-tip {
198
+ color: #666;
199
+ font-size: 14px;
200
+ text-align: center;
201
+ margin-top: 30px;
202
+ margin-bottom: 0;
203
+ width: 100%;
204
+ }
205
+ }
206
+ }
207
+ </style>
@@ -0,0 +1,53 @@
1
+ import type { Ref } from 'vue'
2
+
3
+ let animationTimer: ReturnType<typeof setInterval> | null = null
4
+
5
+ // 开始扫码动画
6
+ export function startScanAnimation(scanLineRef: Ref<HTMLDivElement | undefined>, scannerRef: Ref<HTMLDivElement | undefined>) {
7
+ // 先停止之前的动画
8
+ stopScanAnimation()
9
+
10
+ if (!scanLineRef.value || !scannerRef.value)
11
+ return
12
+
13
+ // 确保扫描线初始位置正确
14
+ scanLineRef.value.style.top = '0px'
15
+
16
+ let direction = 'down'
17
+ let position = 0
18
+ const speed = 2
19
+ const maxPosition = scannerRef.value.offsetHeight || 200
20
+
21
+ // 确保maxPosition不为0
22
+ if (maxPosition <= 10) {
23
+ // 如果容器高度获取失败,使用一个默认延迟后再尝试
24
+ setTimeout(() => {
25
+ startScanAnimation(scanLineRef, scannerRef)
26
+ }, 500)
27
+ return
28
+ }
29
+
30
+ animationTimer = setInterval(() => {
31
+ if (direction === 'down') {
32
+ position += speed
33
+ if (position >= maxPosition - 2)
34
+ direction = 'up'
35
+ }
36
+ else {
37
+ position -= speed
38
+ if (position <= 0)
39
+ direction = 'down'
40
+ }
41
+ if (scanLineRef.value)
42
+ scanLineRef.value.style.top = `${position}px`
43
+ }, 20)
44
+
45
+ return animationTimer
46
+ }
47
+
48
+ export function stopScanAnimation() {
49
+ if (animationTimer) {
50
+ clearInterval(animationTimer)
51
+ animationTimer = null
52
+ }
53
+ }