af-mobile-client-vue3 1.1.16 → 1.1.18

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,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.1.16",
4
+ "version": "1.1.18",
5
5
  "description": "Vue + Vite component lib",
6
6
  "license": "MIT",
7
7
  "engines": {
@@ -0,0 +1,163 @@
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
+ console.log('>>>> 拍照 后 上传', result)
34
+ if (result.status === 'success') {
35
+ handlePhotoUpload(result.data)
36
+ }
37
+ },
38
+ })
39
+ }
40
+
41
+ // 处理拍照后的上传
42
+ function handlePhotoUpload(photoData: any) {
43
+ const formData = new FormData()
44
+ formData.append('resUploadMode', props.uploadMode)
45
+ formData.append('pathKey', 'Default')
46
+ formData.append('formType', 'image')
47
+ formData.append('useType', 'Default')
48
+ formData.append('resUploadStock', '1')
49
+ formData.append('filename', photoData.name)
50
+ formData.append('filesize', (photoData.size / 1024 / 1024).toFixed(4))
51
+ formData.append('f_operator', 'server')
52
+ formData.append('imgPath', photoData.filePath)
53
+ formData.append('urlPath', '/api/af-revenue/resource/upload')
54
+
55
+ // 添加临时预览
56
+ const tempFile = {
57
+ uid: Date.now() + Math.random().toString(36).substr(2, 5),
58
+ name: photoData.name,
59
+ status: 'uploading',
60
+ message: '上传中...',
61
+ url: `data:image/png;base64,${photoData.content}`,
62
+ }
63
+
64
+ if (!imageList.value) {
65
+ imageList.value = [tempFile]
66
+ }
67
+ else {
68
+ imageList.value.push(tempFile)
69
+ }
70
+
71
+ const param = {
72
+ resUploadMode: props.uploadMode,
73
+ pathKey: 'Default',
74
+ formType: 'image',
75
+ useType: 'Default',
76
+ resUploadStock: '1',
77
+ filename: photoData.name,
78
+ filesize: photoData.size,
79
+ f_operator: 'server',
80
+ imgPath: photoData.filePath,
81
+ urlPath: '/api/af-revenue/resource/upload',
82
+ }
83
+ console.log('>>>> 上传 param: ', JSON.stringify(param))
84
+ // 上传到服务器
85
+ mobileUtil.execute({
86
+ funcName: 'uploadResource',
87
+ param,
88
+ callbackFunc: (result: any) => {
89
+ console.log('>>>> 上传结果: ', result)
90
+ if (result.status === 'success') {
91
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
92
+ if (index !== -1) {
93
+ imageList.value[index].uid = result.data.id
94
+ imageList.value[index].id = result.data.id
95
+ delete imageList.value[index].message
96
+ imageList.value[index].status = 'done'
97
+ imageList.value[index].url = result.data.f_downloadpath
98
+ }
99
+ }
100
+ else {
101
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
102
+ if (index !== -1) {
103
+ imageList.value[index].status = 'failed'
104
+ imageList.value[index].message = '上传失败'
105
+ }
106
+ }
107
+
108
+ if (props.outerIndex !== undefined)
109
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
110
+ else
111
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
112
+ },
113
+ })
114
+ }
115
+
116
+ // 删除图片
117
+ function deleteFileFunction(file: any) {
118
+ if (file.id) {
119
+ deleteFile({ ids: [file.id], f_state: '删除' }).then((res: any) => {
120
+ if (res.msg !== undefined) {
121
+ const targetIndex = imageList.value.findIndex(item => item.id === file.id)
122
+ if (targetIndex !== -1) {
123
+ imageList.value.splice(targetIndex, 1)
124
+ if (props.outerIndex !== undefined)
125
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
126
+ else
127
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
128
+ }
129
+ }
130
+ })
131
+ }
132
+ }
133
+ </script>
134
+
135
+ <template>
136
+ <div class="uploader-container">
137
+ <van-button
138
+ v-if="props.attr?.addOrEdit !== 'readonly'"
139
+ icon="photograph"
140
+ type="primary"
141
+ @click="triggerCamera"
142
+ >
143
+ 拍照
144
+ </van-button>
145
+
146
+ <van-uploader
147
+ v-model="imageList"
148
+ :show-upload="false"
149
+ :deletable="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin'"
150
+ :multiple="props.authority === 'admin'"
151
+ :preview-image="true"
152
+ :before-delete="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin' ? deleteFileFunction : undefined"
153
+ />
154
+ </div>
155
+ </template>
156
+
157
+ <style scoped lang="less">
158
+ .uploader-container {
159
+ display: flex;
160
+ flex-direction: column;
161
+ gap: 16px;
162
+ }
163
+ </style>
@@ -53,11 +53,13 @@ function init(params: Form) {
53
53
  formData.value = propsData.value.groupFormData
54
54
  getConfigByName(propsData.value.configName, (result) => {
55
55
  if (result?.groups) {
56
- submitGroup.value = true
56
+ // submitGroup.value = true
57
57
  groupItems.value = result.groups
58
58
  result.groups.forEach((group) => {
59
59
  if (!formData.value[group.groupName])
60
60
  formData.value[group.groupName] = {}
61
+ if (group.showSubmitBtn)
62
+ submitGroup.value = true
61
63
  })
62
64
  }
63
65
  else {
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { FieldType } from 'vant'
3
3
  import type { Numeric } from 'vant/es/utils'
4
+ import ImageUploader from '@af-mobile-client-vue3/components/core/ImageUploader/index.vue'
4
5
  import Uploader from '@af-mobile-client-vue3/components/core/Uploader/index.vue'
5
6
  import XGridDropOption from '@af-mobile-client-vue3/components/core/XGridDropOption/index.vue'
6
7
  import XMultiSelect from '@af-mobile-client-vue3/components/core/XMultiSelect/index.vue'
@@ -759,9 +760,9 @@ function handleAddressConfirm(location) {
759
760
  </template>
760
761
  </VanField>
761
762
 
762
- <!-- 图片文件上传 -->
763
+ <!-- 文件上传 -->
763
764
  <VanField
764
- v-if="(attr.type === 'image' || attr.type === 'file') && showItem"
765
+ v-if="attr.type === 'file' && showItem"
765
766
  name="uploader"
766
767
  :label="labelData"
767
768
  :label-align="labelAlign"
@@ -779,6 +780,26 @@ function handleAddressConfirm(location) {
779
780
  </template>
780
781
  </VanField>
781
782
 
783
+ <!-- 图片上传, 手机端拍照 -->
784
+ <VanField
785
+ v-if="attr.type === 'image' && showItem"
786
+ name="image"
787
+ :label="labelData"
788
+ :label-align="labelAlign"
789
+ :input-align="attr.inputAlign ? attr.inputAlign : 'left'"
790
+ :rules="[{ required: attr.rule.required === 'true', message: '请选择' }]"
791
+ >
792
+ <template #input>
793
+ <ImageUploader
794
+ upload-mode="server"
795
+ :image-list="(localValue as any[])"
796
+ authority="admin"
797
+ :attr="attr"
798
+ @update-file-list="updateFile"
799
+ />
800
+ </template>
801
+ </VanField>
802
+
782
803
  <!-- 选择器 -->
783
804
  <VanField
784
805
  v-if="attr.type === 'picker' && showItem"
@@ -15,12 +15,15 @@ const props = withDefaults(defineProps<Props>(), {
15
15
  defaultCenter: () => {
16
16
  try {
17
17
  mobileUtil.execute({
18
- param: undefined,
19
- funcName: 'getPhoneStatus',
18
+ param: {},
19
+ funcName: 'getLocationResult',
20
20
  callbackFunc: (result) => {
21
- const locationResult = result as PhoneLocationStatus
22
- if (locationResult.f_latitude && locationResult.f_longitude) {
23
- return [locationResult.f_longitude, locationResult.f_latitude]
21
+ const res = result as PhoneLocationStatus
22
+ if (res.status === 'success') {
23
+ const locationResult = JSON.parse(res.data.location)
24
+ if (locationResult.longitude && locationResult.latitude) {
25
+ return [locationResult.longitude, locationResult.latitude]
26
+ }
24
27
  }
25
28
  },
26
29
  })
@@ -818,16 +818,19 @@ async function handleLocation() {
818
818
 
819
819
  try {
820
820
  mobileUtil.execute({
821
- param: undefined,
822
- funcName: 'getPhoneStatus',
821
+ param: {},
822
+ funcName: 'getLocationResult',
823
823
  callbackFunc: (result) => {
824
- const locationResult = result as PhoneLocationStatus
825
- if (locationResult.f_latitude && locationResult.f_longitude) {
826
- const center: [number, number] = [locationResult.f_longitude, locationResult.f_latitude]
827
- setCenterAndZoom(center, getZoom())
828
- // 更新位置图标
829
- if (isFollowingLocation.value) {
830
- updateLocationMarker(center)
824
+ const res = result as PhoneLocationStatus
825
+ if (res.status === 'success') {
826
+ const locationResult = JSON.parse(res.data.location)
827
+ if (locationResult.longitude && locationResult.latitude) {
828
+ const center: [number, number] = [locationResult.longitude, locationResult.latitude]
829
+ setCenterAndZoom(center, getZoom())
830
+ // 更新位置图标
831
+ if (isFollowingLocation.value) {
832
+ updateLocationMarker(center)
833
+ }
831
834
  }
832
835
  }
833
836
  },
@@ -10,12 +10,12 @@ export interface LocationResult {
10
10
 
11
11
  /** 手机状态接口 */
12
12
  export interface PhoneLocationStatus {
13
- /** 纬度 */
14
- f_latitude: number
15
- /** 经度 */
16
- f_longitude: number
17
- /** 其他可能的状态属性 */
18
- [key: string]: unknown
13
+
14
+ status: string
15
+
16
+ data: {
17
+ location: string;
18
+ }
19
19
  }
20
20
 
21
21
  /** 初始化参数接口 */
@@ -20,7 +20,15 @@ export class mobileUtil {
20
20
  // 执行flutter端函数
21
21
  static execute(locationParam: Param): any {
22
22
  locationParam.callBackMethodName = `mobile_func_${Math.random().toString(36).substring(7)}`
23
- window[locationParam.callBackMethodName] = locationParam.callbackFunc
23
+ if (window.__MICRO_APP_ENVIRONMENT__) {
24
+ console.warn('我在微前端环境中')
25
+ // 微前端应用中需要绑定函数到真实的window中
26
+ window.rawWindow[locationParam.callBackMethodName] = locationParam.callbackFunc
27
+ }
28
+ else {
29
+ console.warn('我不在微前端环境中')
30
+ window[locationParam.callBackMethodName] = locationParam.callbackFunc
31
+ }
24
32
  window[locationParam.funcName].postMessage(JSON.stringify(locationParam))
25
33
  }
26
34
  }
@@ -1,11 +1,92 @@
1
1
  <script setup lang="ts">
2
2
  import XCellList from '@af-mobile-client-vue3/components/data/XCellList/index.vue'
3
3
  import NormalDataLayout from '@af-mobile-client-vue3/components/layout/NormalDataLayout/index.vue'
4
- import { ref } from 'vue'
4
+ import { defineEmits, ref } from 'vue'
5
+ import { useRouter } from 'vue-router'
6
+
7
+ // 定义事件
8
+ const emit = defineEmits(['deleteRow'])
9
+ // 访问路由
10
+ const router = useRouter()
11
+ // 获取默认值
12
+ const idKey = ref('o_id')
5
13
 
6
14
  // 简易crud表单测试
7
- const configName = ref('defectDispatchPhoneCRUD')
8
- const serviceName = ref('af-linepatrol')
15
+ const configName = ref('crud_oper_log_manage')
16
+ const serviceName = ref('af-system')
17
+
18
+ // 资源权限测试
19
+ // const configName = ref('crud_sources_test')
20
+ // const serviceName = ref('af-system')
21
+
22
+ // 实际业务测试
23
+ // const configName = ref('lngChargeAuditMobileCRUD')
24
+ // const serviceName = ref('af-gaslink')
25
+
26
+ // 跳转到详情页面
27
+ // function toDetail(item) {
28
+ // router.push({
29
+ // name: 'XCellDetailView',
30
+ // params: { id: item[idKey.value] }, // 如果使用命名路由,推荐使用路由参数而不是直接构建 URL
31
+ // query: {
32
+ // operName: item[operNameKey.value],
33
+ // method:item[methodKey.value],
34
+ // requestMethod:item[requestMethodKey.value],
35
+ // operatorType:item[operatorTypeKey.value],
36
+ // operUrl:item[operUrlKey.value],
37
+ // operIp:item[operIpKey.value],
38
+ // costTime:item[costTimeKey.value],
39
+ // operTime:item[operTimeKey.value],
40
+ //
41
+ // title: item[titleKey.value],
42
+ // businessType: item[businessTypeKey.value],
43
+ // status:item[statusKey.value]
44
+ // }
45
+ // })
46
+ // }
47
+
48
+ // 跳转到表单——以表单组来渲染纯表单
49
+ function toDetail(item) {
50
+ router.push({
51
+ name: 'XFormGroupView',
52
+ query: {
53
+ id: item[idKey.value],
54
+ // id: item.rr_id,
55
+ // o_id: item.o_id,
56
+ },
57
+ })
58
+ }
59
+
60
+ // 新增功能
61
+ // function addOption(totalCount) {
62
+ // router.push({
63
+ // name: 'XFormView',
64
+ // params: { id: totalCount, openid: totalCount },
65
+ // query: {
66
+ // configName: configName.value,
67
+ // serviceName: serviceName.value,
68
+ // mode: '新增',
69
+ // },
70
+ // })
71
+ // }
72
+
73
+ // 修改功能
74
+ // function updateRow(result) {
75
+ // router.push({
76
+ // name: 'XFormView',
77
+ // params: { id: result.o_id, openid: result.o_id },
78
+ // query: {
79
+ // configName: configName.value,
80
+ // serviceName: serviceName.value,
81
+ // mode: '修改',
82
+ // },
83
+ // })
84
+ // }
85
+
86
+ // 删除功能
87
+ function deleteRow(result) {
88
+ emit('deleteRow', result.o_id)
89
+ }
9
90
  </script>
10
91
 
11
92
  <template>
@@ -14,6 +95,10 @@ const serviceName = ref('af-linepatrol')
14
95
  <XCellList
15
96
  :config-name="configName"
16
97
  :service-name="serviceName"
98
+ :fix-query-form="{ o_f_oper_name: 'edu_test' }"
99
+ :id-key="idKey"
100
+ @to-detail="toDetail"
101
+ @delete-row="deleteRow"
17
102
  />
18
103
  </template>
19
104
  </NormalDataLayout>