af-mobile-client-vue3 1.2.23 → 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.23",
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>
@@ -171,7 +171,7 @@ async function loadFormConfig(): Promise<void> {
171
171
  getConfigByName(props.configName!, (result) => {
172
172
  if (result) {
173
173
  // 如果有 paramLogicName,自动请求并赋值
174
- if (result.paramLogicName && !props.formData) {
174
+ if (result.paramLogicName && (!props.formData || (props.formData && Object.keys(props.formData).length === 0))) {
175
175
  let logicParam = props.paramLogicNameParam
176
176
  if (typeof logicParam === 'string') {
177
177
  try {
@@ -220,11 +220,9 @@ async function initWithGroupFormItems() {
220
220
 
221
221
  // 设置表单配置
222
222
  function setupFormConfig(config: GroupFormItems) {
223
- console.log('>>>> config: ', JSON.stringify(config))
224
223
  formConfig.value = config
225
224
  myServiceName.value = props.serviceName || ''
226
225
  formGroupName.value = config.groupName || 'default'
227
- console.log('>>>> props.formData: ', props.formData)
228
226
  form.value = props.formData || {}
229
227
 
230
228
  // 清理并重新设置验证规则
@@ -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>
@@ -724,7 +724,7 @@ function onTreeSelectFinish(value) {
724
724
  }
725
725
 
726
726
  function emitFunc(func, data) {
727
- emits('xFormItemEmitFunc', func, data, data?.model ? this.form[data.model] : this.form)
727
+ emits('xFormItemEmitFunc', func, data, data?.model ? form[data.model] : form)
728
728
  }
729
729
  </script>
730
730