af-mobile-client-vue3 1.2.24 → 1.2.25

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.2.24",
4
+ "version": "1.2.25",
5
5
  "packageManager": "pnpm@10.12.3",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -1,159 +1,159 @@
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
- mobileUtil.execute({
29
- funcName: 'takePicture',
30
- param: {},
31
- callbackFunc: (result: any) => {
32
- if (result.status === 'success') {
33
- handlePhotoUpload(result.data)
34
- }
35
- },
36
- })
37
- }
38
-
39
- // 处理拍照后的上传
40
- function getImageMimeType(fileName: string): string {
41
- const ext = fileName.split('.').pop()?.toLowerCase()
42
- if (ext === 'jpg' || ext === 'jpeg')
43
- return 'image/jpeg'
44
- if (ext === 'png')
45
- return 'image/png'
46
- if (ext === 'gif')
47
- return 'image/gif'
48
- return 'image/png' // 默认
49
- }
50
-
51
- function handlePhotoUpload(photoData: any) {
52
- // 添加临时预览
53
- const mimeType = getImageMimeType(photoData.filePath)
54
- const tempFile = {
55
- uid: Date.now() + Math.random().toString(36).substr(2, 5),
56
- name: photoData.filePath.split('/').pop(),
57
- status: 'uploading',
58
- message: '上传中...',
59
- url: `data:${mimeType};base64,${photoData.content}`,
60
- }
61
-
62
- if (!imageList.value) {
63
- imageList.value = [tempFile]
64
- }
65
- else {
66
- imageList.value.push(tempFile)
67
- }
68
-
69
- const param = {
70
- resUploadMode: props.uploadMode,
71
- pathKey: 'Default',
72
- formType: 'image',
73
- useType: 'Default',
74
- resUploadStock: '1',
75
- filename: photoData.name,
76
- filesize: photoData.size,
77
- f_operator: 'server',
78
- imgPath: photoData.filePath,
79
- urlPath: `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
80
- }
81
- // 上传到服务器
82
- mobileUtil.execute({
83
- funcName: 'uploadResource',
84
- param,
85
- callbackFunc: (result: any) => {
86
- if (result.status === 'success') {
87
- const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
88
- if (index !== -1) {
89
- imageList.value[index].uid = result.data.id
90
- imageList.value[index].id = result.data.id
91
- delete imageList.value[index].message
92
- imageList.value[index].status = 'done'
93
- imageList.value[index].url = result.data.f_downloadpath
94
- }
95
- }
96
- else {
97
- const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
98
- if (index !== -1) {
99
- imageList.value[index].status = 'failed'
100
- imageList.value[index].message = '上传失败'
101
- }
102
- }
103
-
104
- if (props.outerIndex !== undefined)
105
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
106
- else
107
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
108
- },
109
- })
110
- }
111
-
112
- // 删除图片
113
- function deleteFileFunction(file: any) {
114
- if (file.id) {
115
- deleteFile({ ids: [file.id], f_state: '删除' }).then((res: any) => {
116
- if (res.msg !== undefined) {
117
- const targetIndex = imageList.value.findIndex(item => item.id === file.id)
118
- if (targetIndex !== -1) {
119
- imageList.value.splice(targetIndex, 1)
120
- if (props.outerIndex !== undefined)
121
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
122
- else
123
- emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
124
- }
125
- }
126
- })
127
- }
128
- }
129
- </script>
130
-
131
- <template>
132
- <div class="uploader-container">
133
- <van-button
134
- v-if="props.attr?.addOrEdit !== 'readonly'"
135
- icon="photograph"
136
- type="primary"
137
- @click="triggerCamera"
138
- >
139
- 拍照
140
- </van-button>
141
-
142
- <van-uploader
143
- v-model="imageList"
144
- :show-upload="false"
145
- :deletable="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin'"
146
- :multiple="props.authority === 'admin'"
147
- :preview-image="true"
148
- :before-delete="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin' ? deleteFileFunction : undefined"
149
- />
150
- </div>
151
- </template>
152
-
153
- <style scoped lang="less">
154
- .uploader-container {
155
- display: flex;
156
- flex-direction: column;
157
- gap: 16px;
158
- }
159
- </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
+ mobileUtil.execute({
29
+ funcName: 'takePicture',
30
+ param: {},
31
+ callbackFunc: (result: any) => {
32
+ if (result.status === 'success') {
33
+ handlePhotoUpload(result.data)
34
+ }
35
+ },
36
+ })
37
+ }
38
+
39
+ // 处理拍照后的上传
40
+ function getImageMimeType(fileName: string): string {
41
+ const ext = fileName.split('.').pop()?.toLowerCase()
42
+ if (ext === 'jpg' || ext === 'jpeg')
43
+ return 'image/jpeg'
44
+ if (ext === 'png')
45
+ return 'image/png'
46
+ if (ext === 'gif')
47
+ return 'image/gif'
48
+ return 'image/png' // 默认
49
+ }
50
+
51
+ function handlePhotoUpload(photoData: any) {
52
+ // 添加临时预览
53
+ const mimeType = getImageMimeType(photoData.filePath)
54
+ const tempFile = {
55
+ uid: Date.now() + Math.random().toString(36).substr(2, 5),
56
+ name: photoData.filePath.split('/').pop(),
57
+ status: 'uploading',
58
+ message: '上传中...',
59
+ url: `data:${mimeType};base64,${photoData.content}`,
60
+ }
61
+
62
+ if (!imageList.value) {
63
+ imageList.value = [tempFile]
64
+ }
65
+ else {
66
+ imageList.value.push(tempFile)
67
+ }
68
+
69
+ const param = {
70
+ resUploadMode: props.uploadMode,
71
+ pathKey: 'Default',
72
+ formType: 'image',
73
+ useType: 'Default',
74
+ resUploadStock: '1',
75
+ filename: photoData.name,
76
+ filesize: photoData.size,
77
+ f_operator: 'server',
78
+ imgPath: photoData.filePath,
79
+ urlPath: `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
80
+ }
81
+ // 上传到服务器
82
+ mobileUtil.execute({
83
+ funcName: 'uploadResource',
84
+ param,
85
+ callbackFunc: (result: any) => {
86
+ if (result.status === 'success') {
87
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
88
+ if (index !== -1) {
89
+ imageList.value[index].uid = result.data.id
90
+ imageList.value[index].id = result.data.id
91
+ delete imageList.value[index].message
92
+ imageList.value[index].status = 'done'
93
+ imageList.value[index].url = result.data.f_downloadpath
94
+ }
95
+ }
96
+ else {
97
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
98
+ if (index !== -1) {
99
+ imageList.value[index].status = 'failed'
100
+ imageList.value[index].message = '上传失败'
101
+ }
102
+ }
103
+
104
+ if (props.outerIndex !== undefined)
105
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
106
+ else
107
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
108
+ },
109
+ })
110
+ }
111
+
112
+ // 删除图片
113
+ function deleteFileFunction(file: any) {
114
+ if (file.id) {
115
+ deleteFile({ ids: [file.id], f_state: '删除' }).then((res: any) => {
116
+ if (res.msg !== undefined) {
117
+ const targetIndex = imageList.value.findIndex(item => item.id === file.id)
118
+ if (targetIndex !== -1) {
119
+ imageList.value.splice(targetIndex, 1)
120
+ if (props.outerIndex !== undefined)
121
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
122
+ else
123
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
124
+ }
125
+ }
126
+ })
127
+ }
128
+ }
129
+ </script>
130
+
131
+ <template>
132
+ <div class="uploader-container">
133
+ <van-button
134
+ v-if="props.attr?.addOrEdit !== 'readonly'"
135
+ icon="photograph"
136
+ type="primary"
137
+ @click="triggerCamera"
138
+ >
139
+ 拍照
140
+ </van-button>
141
+
142
+ <van-uploader
143
+ v-model="imageList"
144
+ :show-upload="false"
145
+ :deletable="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin'"
146
+ :multiple="props.authority === 'admin'"
147
+ :preview-image="true"
148
+ :before-delete="props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin' ? deleteFileFunction : undefined"
149
+ />
150
+ </div>
151
+ </template>
152
+
153
+ <style scoped lang="less">
154
+ .uploader-container {
155
+ display: flex;
156
+ flex-direction: column;
157
+ gap: 16px;
158
+ }
159
+ </style>
@@ -1,133 +1,172 @@
1
- <script setup lang="ts">
2
- import XForm from '@af-mobile-client-vue3/components/data/XForm/index.vue'
3
- import { getConfigByName } from '@af-mobile-client-vue3/services/api/common'
4
- import {
5
- Button as VanButton,
6
- Tab as VanTab,
7
- Tabs as VanTabs,
8
- } from 'vant'
9
- import { defineEmits, defineProps, onBeforeMount, ref, watch } from 'vue'
10
-
11
- const props = withDefaults(defineProps<{
12
- configName?: string
13
- serviceName?: string
14
- groupFormData?: object
15
- mode?: string
16
- }>(), {
17
- configName: '',
18
- serviceName: undefined,
19
- groupFormData: () => ({}),
20
- mode: '查询',
21
- })
22
- const emit = defineEmits(['submit'])
23
-
24
- interface Form {
25
- configName?: string
26
- serviceName?: string
27
- groupFormData?: object
28
- mode?: string
29
- }
30
-
31
- const groupItems = ref([])
32
- const formData = ref({})
33
- const submitGroup = ref(false)
34
- const submitSimple = ref(false)
35
- const isInit = ref(false)
36
- const initStatus = ref(false)
37
- const propsData = ref({})
38
-
39
- // 组件初始化函数
40
- function init(params: Form) {
41
- initStatus.value = true
42
- propsData.value = {
43
- // configName: '',
44
- // serviceName: undefined,
45
- // groupFormData: () => ({}),
46
- // mode: '查询',
47
- configName: props.configName,
48
- serviceName: props.serviceName,
49
- groupFormData: props.groupFormData,
50
- mode: props.mode,
51
- ...params,
52
- }
53
- formData.value = propsData.value.groupFormData
54
- getConfigByName(propsData.value.configName, (result) => {
55
- if (result?.groups) {
56
- // submitGroup.value = true
57
- groupItems.value = result.groups
58
- result.groups.forEach((group) => {
59
- if (!formData.value[group.groupName])
60
- formData.value[group.groupName] = {}
61
- if (group.showSubmitBtn)
62
- submitGroup.value = true
63
- })
64
- }
65
- else {
66
- submitSimple.value = result.showSubmitBtn
67
- groupItems.value = [{ ...result }]
68
- }
69
- isInit.value = true
70
- }, propsData.value.serviceName)
71
- }
72
- watch(() => props.groupFormData, (_val) => {
73
- formData.value = { ...formData.value, ...props.groupFormData }
74
- })
75
- onBeforeMount(() => {
76
- if (!initStatus.value)
77
- init(props)
78
- })
79
- const xFormListRef = ref([])
80
- async function submit() {
81
- for (const res of xFormListRef.value) {
82
- await res.validate()
83
- formData.value[res.formGroupName] = res.form
84
- }
85
- emit('submit', formData.value)
86
- }
87
-
88
- // function initXForm(index: number) {
89
- // 获取自身示例
90
- // refs[`xFormListRef-${index}`].init({})
91
- // }
92
-
93
- defineExpose({ init })
94
- </script>
95
-
96
- <template>
97
- <div v-if="isInit" id="x-form-group">
98
- <VanTabs scrollspy sticky>
99
- <VanTab
100
- v-for="(item, index) in groupItems"
101
- :key="item.groupName ? (item.groupName + index) : index"
102
- :title="item.describe ? item.describe : item.tableName "
103
- >
104
- <div class="x-form-group-item">
105
- <!-- :ref="`xFormListRef-${index}`" -->
106
- <XForm
107
- ref="xFormListRef"
108
- :mode="props.mode"
109
- :group-form-items="item"
110
- :form-data="item.groupName ? formData[item.groupName] : formData"
111
- :form-name="item.groupName"
112
- :service-name="props.serviceName"
113
- :submit-button="submitSimple"
114
- @on-submit="submit"
115
- />
116
- </div>
117
- </VanTab>
118
- </VanTabs>
119
- <VanButton v-if="submitGroup" round block type="primary" @click="submit">
120
- 提交
121
- </VanButton>
122
- </div>
123
- </template>
124
-
125
- <style scoped lang="less">
126
- #x-form-group {
127
- background-color: rgb(247, 248, 250);
128
- padding-bottom: 10px;
129
- .x-form-group-item {
130
- margin: 20px 0;
131
- }
132
- }
133
- </style>
1
+ <script setup lang="ts">
2
+ import XForm from '@af-mobile-client-vue3/components/data/XForm/index.vue'
3
+ import { getConfigByName } from '@af-mobile-client-vue3/services/api/common'
4
+ import {
5
+ Button as VanButton,
6
+ Tab as VanTab,
7
+ Tabs as VanTabs,
8
+ } from 'vant'
9
+ import { defineEmits, defineProps, onBeforeMount, ref, watch, useSlots, computed } from 'vue'
10
+
11
+ const props = withDefaults(defineProps<{
12
+ configName?: string
13
+ serviceName?: string
14
+ groupFormData?: object
15
+ mode?: string
16
+ }>(), {
17
+ configName: '',
18
+ serviceName: undefined,
19
+ groupFormData: () => ({}),
20
+ mode: '查询',
21
+ })
22
+ const emit = defineEmits(['submit'])
23
+
24
+ interface Form {
25
+ configName?: string
26
+ serviceName?: string
27
+ groupFormData?: object
28
+ mode?: string
29
+ }
30
+
31
+ const groupItems = ref([])
32
+ const formData = ref({})
33
+ const submitGroup = ref(false)
34
+ const submitSimple = ref(false)
35
+ const isInit = ref(false)
36
+ const initStatus = ref(false)
37
+ const propsData = ref<Partial<Form>>({})
38
+
39
+ const slots = useSlots()
40
+ const renderableGroupItems = computed(() => {
41
+ return groupItems.value.filter(item => {
42
+ if (item.formGroupType === 'slot') {
43
+ return !!(item.slotName && slots[item.slotName])
44
+ }
45
+ return true
46
+ })
47
+ })
48
+
49
+ // 组件初始化函数
50
+ function init(params: Form) {
51
+ initStatus.value = true
52
+ propsData.value = {
53
+ configName: props.configName,
54
+ serviceName: props.serviceName,
55
+ groupFormData: props.groupFormData,
56
+ mode: props.mode,
57
+ ...params,
58
+ }
59
+ formData.value = propsData.value.groupFormData
60
+ getConfigByName(propsData.value.configName, (result) => {
61
+ console.log('result===', result)
62
+ if (result?.groups) {
63
+ groupItems.value = result.groups
64
+ result.groups.forEach((group) => {
65
+ if (!formData.value[group.groupName])
66
+ formData.value[group.groupName] = {}
67
+ if (group.showSubmitBtn)
68
+ submitGroup.value = true
69
+ })
70
+ }
71
+ else {
72
+ submitSimple.value = result.showSubmitBtn
73
+ groupItems.value = [{ ...result }]
74
+ }
75
+ isInit.value = true
76
+ }, propsData.value.serviceName)
77
+ }
78
+ watch(() => props.groupFormData, (_val) => {
79
+ formData.value = { ...formData.value, ...props.groupFormData }
80
+ })
81
+ onBeforeMount(() => {
82
+ if (!initStatus.value)
83
+ init(props)
84
+ })
85
+ interface XFormLike {
86
+ validate: () => Promise<void>
87
+ formGroupName?: string
88
+ form?: any
89
+ getFormData?: () => object
90
+ }
91
+ const xFormListRef = ref<XFormLike[]>([])
92
+
93
+ // 注册表单实例,避免重复
94
+ function setRef(refValue: XFormLike) {
95
+ if (refValue && !xFormListRef.value.includes(refValue)) {
96
+ xFormListRef.value.push(refValue)
97
+ }
98
+ }
99
+ // 注销表单实例
100
+ function removeRef(refValue: XFormLike) {
101
+ const idx = xFormListRef.value.indexOf(refValue)
102
+ if (idx !== -1) xFormListRef.value.splice(idx, 1)
103
+ }
104
+
105
+ async function submit() {
106
+ for (const res of xFormListRef.value) {
107
+ try {
108
+ await res.validate()
109
+ if (res.formGroupName && res.form) {
110
+ formData.value[res.formGroupName] = res.form
111
+ } else if (typeof res.getFormData === 'function') {
112
+ Object.assign(formData.value, res.getFormData())
113
+ }
114
+ } catch (msg) {
115
+ console.log('error:', msg)
116
+ return
117
+ }
118
+ }
119
+ console.log('formData.value===', formData.value)
120
+ emit('submit', formData.value)
121
+ }
122
+
123
+ defineExpose({ init, removeRef, xFormListRef })
124
+ </script>
125
+
126
+ <template>
127
+ <div v-if="isInit" id="x-form-group">
128
+ <VanTabs scrollspy sticky>
129
+ <VanTab
130
+ v-for="(item, index) in renderableGroupItems"
131
+ :key="item.groupName ? (item.groupName + index) : index"
132
+ :title="item.describe ? item.describe : item.tableName "
133
+ >
134
+ <div class="x-form-group-item">
135
+ <template v-if="item.formGroupType === 'slot'">
136
+ <slot :name="item.slotName"
137
+ :item="item"
138
+ :form-data="item.groupName ? formData[item.groupName] : formData"
139
+ :set-ref="setRef"
140
+ :remove-ref="removeRef"
141
+ />
142
+ </template>
143
+ <template v-else>
144
+ <XForm
145
+ ref="xFormListRef"
146
+ :mode="props.mode"
147
+ :group-form-items="item"
148
+ :form-data="item.groupName ? formData[item.groupName] : formData"
149
+ :form-name="item.groupName"
150
+ :service-name="props.serviceName"
151
+ :submit-button="submitSimple"
152
+ @on-submit="submit"
153
+ />
154
+ </template>
155
+ </div>
156
+ </VanTab>
157
+ </VanTabs>
158
+ <VanButton v-if="submitGroup" round block type="primary" @click="submit">
159
+ 提交
160
+ </VanButton>
161
+ </div>
162
+ </template>
163
+
164
+ <style scoped lang="less">
165
+ #x-form-group {
166
+ background-color: rgb(247, 248, 250);
167
+ padding-bottom: 10px;
168
+ .x-form-group-item {
169
+ margin: 20px 0;
170
+ }
171
+ }
172
+ </style>