af-mobile-client-vue3 1.1.39 → 1.1.41

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/index.html CHANGED
@@ -9,6 +9,7 @@
9
9
  <body>
10
10
  <div id="system-app"></div>
11
11
  <script type="module" src="/src/main.ts"></script>
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
12
13
  <noscript>
13
14
  This website requires JavaScript to function properly.
14
15
  Please enable JavaScript to continue.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.1.39",
4
+ "version": "1.1.41",
5
5
  "description": "Vue + Vite component lib",
6
6
  "license": "MIT",
7
7
  "engines": {
@@ -19,13 +19,13 @@
19
19
  "release": "bumpp --commit --push --tag"
20
20
  },
21
21
  "dependencies": {
22
+ "@iconify/vue": "4.3.0",
22
23
  "@micro-zoe/micro-app": "1.0.0-rc.24",
23
- "@vant/area-data": "^2.0.0",
24
24
  "@unhead/vue": "^2.0.5",
25
+ "@vant/area-data": "^2.0.0",
25
26
  "@vant/touch-emulator": "^1.4.0",
26
27
  "@vant/use": "^1.6.0",
27
28
  "@vueuse/core": "^13.1.0",
28
- "@iconify/vue": "4.3.0",
29
29
  "animate.css": "^4.1.1",
30
30
  "axios": "^1.8.4",
31
31
  "crypto-js": "^4.2.0",
@@ -41,7 +41,8 @@
41
41
  "vant": "^4.9.18",
42
42
  "vconsole": "^3.15.1",
43
43
  "vue": "^3.5.13",
44
- "vue-router": "^4.5.0"
44
+ "vue-router": "^4.5.0",
45
+ "vue3-hash-calendar": "^1.1.3"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@antfu/eslint-config": "^4.12.0",
@@ -1,160 +1,160 @@
1
- <script setup lang="ts">
2
- import { deleteFile } from '@af-mobile-client-vue3/services/api/common'
3
- import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
4
- import {
5
- Button as vanButton,
6
- Uploader as vanUploader,
7
- } from 'vant'
8
- import { ref, watch } from 'vue'
9
-
10
- const props = defineProps({
11
- imageList: Array<any>,
12
- outerIndex: { default: undefined },
13
- authority: { default: 'user' },
14
- uploadMode: { default: 'server' },
15
- attr: { type: Object as () => { addOrEdit?: string }, default: () => ({}) },
16
- })
17
- const emit = defineEmits(['updateFileList'])
18
-
19
- const imageList = ref<Array<any>>(props.imageList ?? [])
20
-
21
- // 同步 props.imageList 到内部 imageList,保证每次变化都响应
22
- watch(() => props.imageList, (newVal) => {
23
- imageList.value = Array.isArray(newVal) ? [...newVal] : []
24
- }, { immediate: true })
25
-
26
- // 触发拍照
27
- function triggerCamera() {
28
- console.log('>>>> uploader 上传111')
29
- mobileUtil.execute({
30
- funcName: 'takePicture',
31
- param: {},
32
- callbackFunc: (result: any) => {
33
- if (result.status === 'success') {
34
- handlePhotoUpload(result.data)
35
- }
36
- },
37
- })
38
- }
39
-
40
- // 处理拍照后的上传
41
- function handlePhotoUpload(photoData: any) {
42
- const formData = new FormData()
43
- formData.append('resUploadMode', props.uploadMode)
44
- formData.append('pathKey', 'Default')
45
- formData.append('formType', 'image')
46
- formData.append('useType', 'Default')
47
- formData.append('resUploadStock', '1')
48
- formData.append('filename', photoData.name)
49
- formData.append('filesize', (photoData.size / 1024 / 1024).toFixed(4))
50
- formData.append('f_operator', 'server')
51
- formData.append('imgPath', photoData.filePath)
52
- formData.append('urlPath', `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`)
53
-
54
- // 添加临时预览
55
- const tempFile = {
56
- uid: Date.now() + Math.random().toString(36).substr(2, 5),
57
- name: photoData.name,
58
- status: 'uploading',
59
- message: '上传中...',
60
- url: `data:image/png;base64,${photoData.content}`,
61
- }
62
-
63
- if (!imageList.value) {
64
- imageList.value = [tempFile]
65
- }
66
- else {
67
- imageList.value.push(tempFile)
68
- }
69
-
70
- const param = {
71
- resUploadMode: props.uploadMode,
72
- pathKey: 'Default',
73
- formType: 'image',
74
- useType: 'Default',
75
- resUploadStock: '1',
76
- filename: photoData.name,
77
- filesize: photoData.size,
78
- f_operator: 'server',
79
- imgPath: photoData.filePath,
80
- urlPath: `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
81
- }
82
- // 上传到服务器
83
- mobileUtil.execute({
84
- funcName: 'uploadResource',
85
- param,
86
- callbackFunc: (result: any) => {
87
- if (result.status === 'success') {
88
- const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
89
- if (index !== -1) {
90
- imageList.value[index].uid = result.data.id
91
- imageList.value[index].id = result.data.id
92
- delete imageList.value[index].message
93
- imageList.value[index].status = 'done'
94
- imageList.value[index].url = result.data.f_downloadpath
95
- }
96
- }
97
- else {
98
- const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
99
- if (index !== -1) {
100
- imageList.value[index].status = 'failed'
101
- imageList.value[index].message = '上传失败'
102
- }
103
- }
104
-
105
- if (props.outerIndex !== undefined)
106
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
107
- else
108
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
109
- },
110
- })
111
- }
112
-
113
- // 删除图片
114
- function deleteFileFunction(file: any) {
115
- if (file.id) {
116
- deleteFile({ ids: [file.id], f_state: '删除' }).then((res: any) => {
117
- if (res.msg !== undefined) {
118
- const targetIndex = imageList.value.findIndex(item => item.id === file.id)
119
- if (targetIndex !== -1) {
120
- imageList.value.splice(targetIndex, 1)
121
- if (props.outerIndex !== undefined)
122
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
123
- else
124
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
125
- }
126
- }
127
- })
128
- }
129
- }
130
- </script>
131
-
132
- <template>
133
- <div class="uploader-container">
134
- <van-button
135
- v-if="props.attr?.addOrEdit !== 'readonly'"
136
- icon="photograph"
137
- type="primary"
138
- @click="triggerCamera"
139
- >
140
- 拍照
141
- </van-button>
142
-
143
- <van-uploader
144
- v-model="imageList"
145
- :show-upload="false"
146
- :deletable="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin'"
147
- :multiple="props.authority === 'admin'"
148
- :preview-image="true"
149
- :before-delete="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin' ? deleteFileFunction : undefined"
150
- />
151
- </div>
152
- </template>
153
-
154
- <style scoped lang="less">
155
- .uploader-container {
156
- display: flex;
157
- flex-direction: column;
158
- gap: 16px;
159
- }
160
- </style>
1
+ <script setup lang="ts">
2
+ import { deleteFile } from '@af-mobile-client-vue3/services/api/common'
3
+ import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
4
+ import {
5
+ Button as vanButton,
6
+ Uploader as vanUploader,
7
+ } from 'vant'
8
+ import { ref, watch } from 'vue'
9
+
10
+ const props = defineProps({
11
+ imageList: Array<any>,
12
+ outerIndex: { default: undefined },
13
+ authority: { default: 'user' },
14
+ uploadMode: { default: 'server' },
15
+ attr: { type: Object as () => { addOrEdit?: string }, default: () => ({}) },
16
+ })
17
+ const emit = defineEmits(['updateFileList'])
18
+
19
+ const imageList = ref<Array<any>>(props.imageList ?? [])
20
+
21
+ // 同步 props.imageList 到内部 imageList,保证每次变化都响应
22
+ watch(() => props.imageList, (newVal) => {
23
+ imageList.value = Array.isArray(newVal) ? [...newVal] : []
24
+ }, { immediate: true })
25
+
26
+ // 触发拍照
27
+ function triggerCamera() {
28
+ console.log('>>>> uploader 上传111')
29
+ mobileUtil.execute({
30
+ funcName: 'takePicture',
31
+ param: {},
32
+ callbackFunc: (result: any) => {
33
+ if (result.status === 'success') {
34
+ handlePhotoUpload(result.data)
35
+ }
36
+ },
37
+ })
38
+ }
39
+
40
+ // 处理拍照后的上传
41
+ function handlePhotoUpload(photoData: any) {
42
+ const formData = new FormData()
43
+ formData.append('resUploadMode', props.uploadMode)
44
+ formData.append('pathKey', 'Default')
45
+ formData.append('formType', 'image')
46
+ formData.append('useType', 'Default')
47
+ formData.append('resUploadStock', '1')
48
+ formData.append('filename', photoData.name)
49
+ formData.append('filesize', (photoData.size / 1024 / 1024).toFixed(4))
50
+ formData.append('f_operator', 'server')
51
+ formData.append('imgPath', photoData.filePath)
52
+ formData.append('urlPath', `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`)
53
+
54
+ // 添加临时预览
55
+ const tempFile = {
56
+ uid: Date.now() + Math.random().toString(36).substr(2, 5),
57
+ name: photoData.name,
58
+ status: 'uploading',
59
+ message: '上传中...',
60
+ url: `data:image/png;base64,${photoData.content}`,
61
+ }
62
+
63
+ if (!imageList.value) {
64
+ imageList.value = [tempFile]
65
+ }
66
+ else {
67
+ imageList.value.push(tempFile)
68
+ }
69
+
70
+ const param = {
71
+ resUploadMode: props.uploadMode,
72
+ pathKey: 'Default',
73
+ formType: 'image',
74
+ useType: 'Default',
75
+ resUploadStock: '1',
76
+ filename: photoData.name,
77
+ filesize: photoData.size,
78
+ f_operator: 'server',
79
+ imgPath: photoData.filePath,
80
+ urlPath: `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
81
+ }
82
+ // 上传到服务器
83
+ mobileUtil.execute({
84
+ funcName: 'uploadResource',
85
+ param,
86
+ callbackFunc: (result: any) => {
87
+ if (result.status === 'success') {
88
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
89
+ if (index !== -1) {
90
+ imageList.value[index].uid = result.data.id
91
+ imageList.value[index].id = result.data.id
92
+ delete imageList.value[index].message
93
+ imageList.value[index].status = 'done'
94
+ imageList.value[index].url = result.data.f_downloadpath
95
+ }
96
+ }
97
+ else {
98
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
99
+ if (index !== -1) {
100
+ imageList.value[index].status = 'failed'
101
+ imageList.value[index].message = '上传失败'
102
+ }
103
+ }
104
+
105
+ if (props.outerIndex !== undefined)
106
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
107
+ else
108
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
109
+ },
110
+ })
111
+ }
112
+
113
+ // 删除图片
114
+ function deleteFileFunction(file: any) {
115
+ if (file.id) {
116
+ deleteFile({ ids: [file.id], f_state: '删除' }).then((res: any) => {
117
+ if (res.msg !== undefined) {
118
+ const targetIndex = imageList.value.findIndex(item => item.id === file.id)
119
+ if (targetIndex !== -1) {
120
+ imageList.value.splice(targetIndex, 1)
121
+ if (props.outerIndex !== undefined)
122
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
123
+ else
124
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
125
+ }
126
+ }
127
+ })
128
+ }
129
+ }
130
+ </script>
131
+
132
+ <template>
133
+ <div class="uploader-container">
134
+ <van-button
135
+ v-if="props.attr?.addOrEdit !== 'readonly'"
136
+ icon="photograph"
137
+ type="primary"
138
+ @click="triggerCamera"
139
+ >
140
+ 拍照
141
+ </van-button>
142
+
143
+ <van-uploader
144
+ v-model="imageList"
145
+ :show-upload="false"
146
+ :deletable="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin'"
147
+ :multiple="props.authority === 'admin'"
148
+ :preview-image="true"
149
+ :before-delete="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin' ? deleteFileFunction : undefined"
150
+ />
151
+ </div>
152
+ </template>
153
+
154
+ <style scoped lang="less">
155
+ .uploader-container {
156
+ display: flex;
157
+ flex-direction: column;
158
+ gap: 16px;
159
+ }
160
+ </style>
@@ -586,7 +586,7 @@ defineExpose({
586
586
  @load="onLoad"
587
587
  >
588
588
  <div v-for="(item, index) in list" :key="`card_${index}`" class="card_item_main">
589
- <VanRow gutter="20" class="card_item_header" align="center" @click="emit('toDetail', item)">
589
+ <VanRow gutter="20" class="card_item_header" align="center">
590
590
  <VanCol :span="24">
591
591
  <div class="title-row">
592
592
  <div v-for="(column) in mainColumns" :key="`main_${column.dataIndex}`" class="main-title">
@@ -622,7 +622,7 @@ defineExpose({
622
622
  </div>
623
623
  </VanCol>
624
624
  </VanRow>
625
- <VanRow gutter="20" class="card_item_details" @click="emit('toDetail', item)">
625
+ <VanRow gutter="20" class="card_item_details">
626
626
  <VanCol v-for="column of detailColumns" :key="`details_${column.dataIndex}`" :span="column.span">
627
627
  <p>
628
628
  {{ `${column.title}: ` }}
@@ -634,7 +634,7 @@ defineExpose({
634
634
  </p>
635
635
  </VanCol>
636
636
  </VanRow>
637
- <VanRow v-if="tagList.length > 0" gutter="20" class="tag-row" @click="emit('toDetail', item)">
637
+ <VanRow v-if="tagList.length > 0" gutter="20" class="tag-row">
638
638
  <VanCol :span="24">
639
639
  <div class="tag-container">
640
640
  <div class="tag-wrapper">
@@ -670,7 +670,6 @@ defineExpose({
670
670
  v-if="footColumns && footColumns.length > 0"
671
671
  gutter="20"
672
672
  class="card_item_footer"
673
- @click="emit('toDetail', item)"
674
673
  >
675
674
  <VanCol v-for="column of footColumns" :key="`foot_${column.dataIndex}`" :span="12">
676
675
  <p>
@@ -710,7 +709,7 @@ defineExpose({
710
709
  v-for="button in getActionGroups(item, index).main"
711
710
  :key="button.func"
712
711
  type="primary"
713
- size="small"
712
+ size="normal"
714
713
  class="action-btn"
715
714
  @click="onSelectMenu(item, button)"
716
715
  >
@@ -953,9 +952,8 @@ defineExpose({
953
952
 
954
953
  .action-btn {
955
954
  min-width: 76px;
956
- height: 32px;
957
- border-radius: 16px;
958
- font-size: 14px;
955
+ border-radius: 10px;
956
+ font-size: 1rem;
959
957
  transition: all 0.2s ease;
960
958
  &:active {
961
959
  transform: scale(0.95);
@@ -970,7 +968,7 @@ defineExpose({
970
968
  align-items: center;
971
969
  padding: 8px 12px;
972
970
  background-color: #fff;
973
- gap: 8px;
971
+ gap: 1px;
974
972
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.02);
975
973
  position: sticky;
976
974
  top: 0;
@@ -985,6 +983,7 @@ defineExpose({
985
983
  background-color: var(--van-background);
986
984
  padding: 4px 12px;
987
985
  border: 1px solid rgba(0, 0, 0, 0.01);
986
+ height: 40px;
988
987
  }
989
988
  :deep(.van-field__left-icon) {
990
989
  color: var(--van-text-color-2);
@@ -10,7 +10,7 @@ import {
10
10
  Icon as VanIcon,
11
11
  Row as VanRow,
12
12
  } from 'vant'
13
- import { computed, defineEmits, defineModel, defineProps, ref } from 'vue'
13
+ import { computed, defineEmits, defineModel, defineProps, onMounted, ref } from 'vue'
14
14
  import QrScanner from './QrScanner/index.vue'
15
15
  import VpnRecognition from './VpnRecognition/index.vue'
16
16
 
@@ -93,6 +93,31 @@ const scanMenuShow = ref(false)
93
93
  const filterMenuShow = ref(false)
94
94
  const showScanner = ref(true)
95
95
 
96
+ onMounted(async () => {
97
+ // 监听应用状态变化
98
+ window.addEventListener('appstate-change', handleAppStateChange as EventListener)
99
+ })
100
+
101
+ // 定义应用状态事件的类型
102
+ interface AppStateEvent extends CustomEvent {
103
+ detail: {
104
+ appState: 'afterhidden' | 'beforeshow' | 'aftershow'
105
+ }
106
+ }
107
+ // 处理应用状态变化
108
+ function handleAppStateChange(e: AppStateEvent) {
109
+ if (e.detail.appState === 'afterhidden') {
110
+ // console.log('>>>> 组件 已卸载')
111
+ filterMenuShow.value = false
112
+ }
113
+ else if (e.detail.appState === 'beforeshow') {
114
+ // console.log('>>>> 组件 即将重新渲染')
115
+ }
116
+ else if (e.detail.appState === 'aftershow') {
117
+ // console.log('>>>> 组件 已经重新渲染')
118
+ }
119
+ }
120
+
96
121
  // 获取默认扫描模式
97
122
  function getDefaultScanType() {
98
123
  // 如果指定了默认模式且该模式有效,则使用默认模式
@@ -259,7 +284,7 @@ function handleCloseScanButton() {
259
284
  <VanDropdownItem v-if="showScanButton" v-model="scanMenuShow" @change="handleScanMenuChange">
260
285
  <template #title>
261
286
  <div class="filter-icon-box">
262
- <VanIcon name="apps-o" size="24" class="filter-icon" />
287
+ <i class="fas fa-qrcode"></i>
263
288
  </div>
264
289
  </template>
265
290
 
@@ -309,7 +334,7 @@ function handleCloseScanButton() {
309
334
  <VanDropdownItem v-if="!props.buttonState || props.buttonState.filter !== false" v-model="filterMenuShow" @change="handleFilterMenuChange">
310
335
  <template #title>
311
336
  <div class="filter-icon-box">
312
- <VanIcon name="filter-o" size="24" class="filter-icon" />
337
+ <i class="fas fa-filter"></i>
313
338
  </div>
314
339
  </template>
315
340
  <div class="dropdown-title">
@@ -468,13 +493,6 @@ function handleCloseScanButton() {
468
493
  }
469
494
  }
470
495
 
471
- .filter-icon {
472
- color: #333;
473
- background-color: rgba(245,245,245);
474
- position: relative;
475
- z-index: 1;
476
- }
477
-
478
496
  .filter-icon-box {
479
497
  display: flex;
480
498
  align-items: center;