af-mobile-client-vue3 1.0.84 → 1.0.86

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.
Files changed (25) hide show
  1. package/package.json +1 -1
  2. package/src/components/data/XCellList/index.vue +5 -2
  3. package/src/components/data/XCellListFilter/index.vue +1 -1
  4. package/src/components/data/XFormItem/index.vue +16 -2
  5. package/src/components/data/XReportGrid/XAddReport/XAddReport.vue +202 -0
  6. package/src/components/data/XReportGrid/XAddReport/index.js +3 -0
  7. package/src/components/data/XReportGrid/XAddReport/index.md +56 -0
  8. package/src/components/data/XReportGrid/XAddReport/index.ts +10 -0
  9. package/src/components/data/XReportGrid/XReport.vue +973 -0
  10. package/src/components/data/XReportGrid/XReportDemo.vue +33 -0
  11. package/src/components/data/XReportGrid/XReportDesign.vue +597 -0
  12. package/src/components/data/XReportGrid/XReportDrawer/XReportDrawer.vue +148 -0
  13. package/src/components/data/XReportGrid/XReportDrawer/index.js +3 -0
  14. package/src/components/data/XReportGrid/XReportDrawer/index.ts +10 -0
  15. package/src/components/data/XReportGrid/XReportJsonRender.vue +386 -0
  16. package/src/components/data/XReportGrid/XReportTrGroup.vue +592 -0
  17. package/src/components/data/XReportGrid/index.md +44 -0
  18. package/src/components/data/XReportGrid/print.js +184 -0
  19. package/src/layout/GridView/index.vue +16 -0
  20. package/src/layout/PageLayout.vue +5 -4
  21. package/src/router/routes.ts +18 -0
  22. package/src/views/component/XCellListView/index.vue +5 -5
  23. package/src/views/component/XFormGroupView/index.vue +2 -2
  24. package/src/views/component/XReportGridView/index.vue +18 -0
  25. package/src/views/component/index.vue +4 -0
@@ -0,0 +1,148 @@
1
+ <script setup lang="ts">
2
+ import { inject, provide, ref } from 'vue'
3
+ import { storeToRefs } from 'pinia'
4
+ import { Popup as VanPopup } from 'vant'
5
+ import XReport from '@af-mobile-client-vue3/components/data/XReportGrid/XReport.vue'
6
+ import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
7
+
8
+ // Props
9
+ const props = defineProps({
10
+ env: {
11
+ type: String,
12
+ default: 'prod',
13
+ },
14
+ })
15
+
16
+ // Emits
17
+ const emit = defineEmits<{
18
+ (e: 'close'): void
19
+ (e: 'selectRow', keys: any[], rows: any[]): void
20
+ }>()
21
+ // 状态管理
22
+ const configName = ref('')
23
+ const displayOnly = ref(true)
24
+ const serverName = ref(process.env.VUE_APP_SYSTEM_NAME)
25
+ const loading = ref(false)
26
+ const visible = ref(false)
27
+ const selectedId = ref(null)
28
+ const mixinData = ref({})
29
+ const outEnv = ref({})
30
+ const attr = ref({})
31
+
32
+ // Store
33
+ const userStore = useUserStore()
34
+ const { user: currUser } = storeToRefs(userStore)
35
+
36
+ // Refs
37
+ const mainRef = ref()
38
+
39
+ // 注入
40
+ const getParentComponentByName = inject<Function>('getParentComponentByName')
41
+ const setGlobalData = inject<Function>('setGlobalData')
42
+ const getGlobalData = inject<Function>('getGlobalData')
43
+
44
+ // 提供依赖
45
+ provide('getSelectedId', () => getSelectedId())
46
+ provide('getSelectedData', () => selectedId.value)
47
+ provide('getMixinData', () => mixinData.value)
48
+ provide('getOutEnv', () => outEnv.value)
49
+ provide('isInAModal', () => true)
50
+ provide('currUser', currUser)
51
+
52
+ // Methods
53
+ function init(params: {
54
+ configName?: string
55
+ serverName?: string
56
+ displayOnly?: boolean
57
+ selectedId?: any
58
+ outEnv?: Record<string, any>
59
+ mixinData?: Record<string, any>
60
+ attr?: Record<string, any>
61
+ }) {
62
+ const {
63
+ configName: initConfigName = '',
64
+ serverName: initServerName = process.env.VUE_APP_SYSTEM_NAME,
65
+ displayOnly: initDisplayOnly = true,
66
+ selectedId: initSelectedId = null,
67
+ outEnv: initOutEnv = {},
68
+ mixinData: initMixinData = {},
69
+ attr: initAttr = {},
70
+ } = params
71
+
72
+ configName.value = initConfigName
73
+ serverName.value = initServerName
74
+ displayOnly.value = initDisplayOnly
75
+ visible.value = true
76
+ attr.value = initAttr
77
+
78
+ if (initSelectedId)
79
+ selectedId.value = initSelectedId
80
+
81
+ mixinData.value = initMixinData
82
+ outEnv.value = initOutEnv
83
+ }
84
+
85
+ function getSelectedId() {
86
+ if (typeof selectedId.value === 'object') {
87
+ if (selectedId.value?.selectedId)
88
+ return selectedId.value.selectedId
89
+
90
+ if (Object.keys(selectedId.value).length > 0)
91
+ return selectedId.value[Object.keys(selectedId.value)[0]]
92
+ }
93
+ return selectedId.value
94
+ }
95
+
96
+ function selectRow(selectedRowKeys: any[], selectedRows: any[]) {
97
+ console.log('XReportDrawer')
98
+ emit('selectRow', selectedRowKeys, selectedRows)
99
+ }
100
+
101
+ function close() {
102
+ loading.value = false
103
+ visible.value = false
104
+ emit('close')
105
+ }
106
+
107
+ function getComponentByName(name: string) {
108
+ const innerRef = getParentComponentByName?.(name)
109
+ if (innerRef)
110
+ return innerRef
111
+
112
+ return mainRef.value?.getComponentByName(name)
113
+ }
114
+
115
+ function updateImg(data: any) {
116
+ console.log(data)
117
+ }
118
+
119
+ // 暴露方法
120
+ defineExpose({
121
+ init,
122
+ getComponentByName,
123
+ })
124
+ </script>
125
+
126
+ <template>
127
+ <VanPopup
128
+ v-model:show="visible"
129
+ position="right"
130
+ :style="{ width: '85vw', height: '100%' }"
131
+ v-bind="attr"
132
+ @close="close"
133
+ >
134
+ <XReport
135
+ ref="mainRef"
136
+ :env="env"
137
+ :use-oss-for-img="false"
138
+ :config-name="configName"
139
+ :show-img-in-cell="true"
140
+ :display-only="displayOnly"
141
+ :edit-mode="false"
142
+ :show-save-button="false"
143
+ :dont-format="true"
144
+ @update-img="updateImg"
145
+ @select-row="selectRow"
146
+ />
147
+ </VanPopup>
148
+ </template>
@@ -0,0 +1,3 @@
1
+ import XReportDrawer from './XReportDrawer'
2
+
3
+ export default XReportDrawer
@@ -0,0 +1,10 @@
1
+ import type { App } from 'vue'
2
+ import XReportDrawer from './XReportDrawer.vue'
3
+
4
+ export { XReportDrawer }
5
+
6
+ export default {
7
+ install(app: App) {
8
+ app.component('XReportDrawer', XReportDrawer)
9
+ }
10
+ }
@@ -0,0 +1,386 @@
1
+ <script setup lang="ts">
2
+ import { onBeforeMount, ref } from 'vue'
3
+ import type { Ref } from 'vue'
4
+ import { Dialog as VanDialog } from 'vant'
5
+ import Upload from '@af-mobile-client-vue3/components/core/Uploader/index.vue'
6
+
7
+ interface JsonRenderConfig {
8
+ data: Record<string, any>
9
+ content: Array<{
10
+ type: string
11
+ customFunctionForLabel: string
12
+ customFunctionForValue: string
13
+ jsonArrayDataIndex?: string
14
+ }>
15
+ title?: {
16
+ type: string
17
+ value: string
18
+ }
19
+ style?: Record<string, string>
20
+ }
21
+
22
+ // Props
23
+ const props = defineProps({
24
+ config: {
25
+ type: Object as () => JsonRenderConfig,
26
+ required: true,
27
+ },
28
+ displayOnly: {
29
+ type: Boolean,
30
+ default: false,
31
+ },
32
+ noPadding: {
33
+ type: Boolean,
34
+ default: false,
35
+ },
36
+ noTopBorder: {
37
+ type: Boolean,
38
+ default: false,
39
+ },
40
+ showTitle: {
41
+ type: Boolean,
42
+ default: true,
43
+ },
44
+ imgPrefix: {
45
+ type: String,
46
+ default: '',
47
+ },
48
+ serverName: {
49
+ type: String,
50
+ default: 'af-system',
51
+ },
52
+ })
53
+
54
+ // Emits
55
+ const emit = defineEmits<{
56
+ (e: 'updateImg', data: any): void
57
+ }>()
58
+
59
+ // 状态
60
+ const showImgModal = ref(false)
61
+ const showImageSrc = ref('')
62
+ const receivedFunction: Ref<Array<{
63
+ labelFunction: Function
64
+ valueFunction: Function
65
+ }>> = ref([])
66
+
67
+ // Methods
68
+ function checkImg(target: any[]) {
69
+ if (!target)
70
+ return []
71
+ target.forEach((obj) => {
72
+ if (obj.url === undefined)
73
+ obj.url = obj.path
74
+
75
+ if (obj.name === undefined) {
76
+ const withOutEndFix = obj.url.split('.')[0]
77
+ const temp = withOutEndFix.split('/')
78
+ obj.name = temp[temp.length - 1]
79
+ }
80
+ if (obj.status === undefined)
81
+ obj.status = 'done'
82
+ })
83
+ return target
84
+ }
85
+
86
+ function setImages(args: any[], item: any, config: any) {
87
+ // 如果基础上传组件在初始化完成后,就调用emit了setImage,此时图片并没有变化,直接返回
88
+ if (args[2] === 'created')
89
+ return
90
+
91
+ const result = {
92
+ name: item.f_project,
93
+ data: args[0],
94
+ orignalData: config,
95
+ }
96
+ emit('updateImg', result)
97
+ }
98
+
99
+ function determineCellStyle(labelFunctionReturn: any, color = '#000', borderWidth = '1px') {
100
+ if (labelFunctionReturn.style) {
101
+ // 如果声明了边框颜色
102
+ if (labelFunctionReturn.style.borderColor)
103
+ color = labelFunctionReturn.style.borderColor
104
+
105
+ // 如果声明了边框宽度
106
+ if (labelFunctionReturn.style.borderWidth)
107
+ borderWidth = labelFunctionReturn.style.borderWidth
108
+ }
109
+
110
+ // 正常边框单元格
111
+ const withBorder = {
112
+ border: `${borderWidth} solid ${color}`,
113
+ padding: '8px',
114
+ }
115
+
116
+ // 仅没有上边框单元格
117
+ const NoTopBorder = {
118
+ borderTopStyle: 'none',
119
+ borderLeft: `${borderWidth} solid ${color}`,
120
+ borderRight: `${borderWidth} solid ${color}`,
121
+ borderBottom: `${borderWidth} solid ${color}`,
122
+ padding: '8px',
123
+ }
124
+
125
+ let result = {}
126
+ // 判断表头是否有声明的样式
127
+ if (labelFunctionReturn.style !== undefined)
128
+ result = props.noTopBorder ? { ...NoTopBorder, ...labelFunctionReturn.style } : { ...withBorder, ...labelFunctionReturn.style }
129
+ else
130
+ result = props.noTopBorder ? { ...NoTopBorder } : { ...withBorder }
131
+
132
+ return result
133
+ }
134
+
135
+ function handleShowImgOk() {
136
+ showImgModal.value = false
137
+ showImageSrc.value = ''
138
+ }
139
+
140
+ function handleShowImgCancel() {
141
+ showImgModal.value = false
142
+ showImageSrc.value = ''
143
+ }
144
+
145
+ function openImgModal(img: string) {
146
+ showImageSrc.value = img
147
+ showImgModal.value = true
148
+ }
149
+
150
+ function formatImgName(imgSrc: string) {
151
+ return imgSrc.split('/').pop() || ''
152
+ }
153
+
154
+ // 生命周期
155
+ onBeforeMount(() => {
156
+ // 遍历配置,将所有JSON传递的方法保存到一个数组中,并用index来一一对应
157
+ for (let i = 0; i < props.config.content.length; i++) {
158
+ receivedFunction.value.push({
159
+ // eslint-disable-next-line no-eval
160
+ labelFunction: eval(`(${props.config.content[i].customFunctionForLabel})`),
161
+ // eslint-disable-next-line no-eval
162
+ valueFunction: eval(`(${props.config.content[i].customFunctionForValue})`),
163
+ })
164
+ }
165
+ })
166
+ </script>
167
+
168
+ <template>
169
+ <div :class="noPadding ? 'reportMainNoPadding' : 'reportMain'" id="xreportjsonrender">
170
+ <table class="reportTable" :style="config.style ? config.style : ''">
171
+ <tbody class="'reportTable'">
172
+ <!-- 标题 -->
173
+ <tr v-if="showTitle && config.title.type && config.title.type !== ''">
174
+ <td :class="noTopBorder ? 'tdWithNoTopBorder' : 'tdWithBorder'" colspan="12">
175
+ <template v-if="config.title.type === 'titleKey'">
176
+ {{ config.data[config.title.value] }}
177
+ </template>
178
+ <template v-else-if="config.title.type === 'titleValue'">
179
+ {{ config.title.value }}
180
+ </template>
181
+ </td>
182
+ </tr>
183
+ <template v-for="(row, rowIndex) in config.content">
184
+ <!-- 数据对象是一个Obj -->
185
+ <template v-if="row.type === 'jsonKey'">
186
+ <tr :key="rowIndex">
187
+ <!-- 表头 -->
188
+ <td
189
+ :class="noTopBorder ? 'tdWithNoTopBorder' : 'tdWithBorder'"
190
+ :style="determineCellStyle(receivedFunction[rowIndex].labelFunction(config, item))"
191
+ colspan="6"
192
+ >
193
+ <template v-if="receivedFunction[rowIndex].labelFunction(config).type === 'key'">
194
+ {{ config.data[receivedFunction[rowIndex].labelFunction(config).content] }}
195
+ </template>
196
+ <template v-else-if="receivedFunction[rowIndex].labelFunction(config).type === 'value'">
197
+ {{ receivedFunction[rowIndex].labelFunction(config).content }}
198
+ </template>
199
+ </td>
200
+ <!-- 内容 -->
201
+ <td
202
+ :class="noTopBorder ? 'tdWithNoTopBorder' : 'tdWithBorder'"
203
+ :style="determineCellStyle(receivedFunction[rowIndex].valueFunction(config))"
204
+ colspan="6"
205
+ >
206
+ <template v-if="displayOnly">
207
+ <template v-if="receivedFunction[rowIndex].valueFunction(config).type === 'key'">
208
+ {{ config.data[receivedFunction[rowIndex].valueFunction(config).content] }}
209
+ </template>
210
+ <template v-else-if="receivedFunction[rowIndex].valueFunction(config).type === 'value'">
211
+ {{ receivedFunction[rowIndex].valueFunction(config).content }}
212
+ </template>
213
+ </template>
214
+ <template v-else>
215
+ <template v-if="receivedFunction[rowIndex].valueFunction(config).originalKey">
216
+ <van-input v-model="config.data[receivedFunction[rowIndex].valueFunction(config).originalKey]" />
217
+ </template>
218
+ <template v-else>
219
+ <van-input v-model="config.data[receivedFunction[rowIndex].valueFunction(config).content]" />
220
+ </template>
221
+ </template>
222
+ <template v-if="receivedFunction[rowIndex].valueFunction(config).type === 'img'">
223
+ <template v-for="(img, imgIndex) in receivedFunction[rowIndex].valueFunction(config).content" :key="imgIndex">
224
+ <span class="imgText">
225
+ <van-icon type="link" />
226
+ <span @click="openImgModal(img)">{{ formatImgName(img) }}</span>
227
+ <br>
228
+ </span>
229
+ </template>
230
+ </template>
231
+ </td>
232
+ </tr>
233
+ </template>
234
+ <!-- 数据对象是一个Array -->
235
+ <template v-else-if="row.type === 'jsonArray'">
236
+ <tr v-for="(item, jsonArrayItemIndex) in config.data[row.jsonArrayDataIndex]" :key="`${rowIndex}${jsonArrayItemIndex}`">
237
+ <!-- 表头 -->
238
+ <td
239
+ :class="noTopBorder ? 'tdWithNoTopBorder' : 'tdWithBorder'"
240
+ :style="determineCellStyle(receivedFunction[rowIndex].labelFunction(config, item))"
241
+ colspan="6"
242
+ >
243
+ <template v-if="receivedFunction[rowIndex].labelFunction(config, item).type === 'key'">
244
+ {{ item[receivedFunction[rowIndex].labelFunction(config, item).content] }}
245
+ </template>
246
+ <template v-if="receivedFunction[rowIndex].labelFunction(config, item).type === 'value'">
247
+ {{ receivedFunction[rowIndex].labelFunction(config, item).content }}
248
+ </template>
249
+ </td>
250
+ <!-- 内容 -->
251
+ <td
252
+ :class="noTopBorder ? 'tdWithNoTopBorder' : 'tdWithBorder'"
253
+ :style="determineCellStyle(receivedFunction[rowIndex].valueFunction(config, item))"
254
+ colspan="6"
255
+ >
256
+ <template v-if="displayOnly">
257
+ <template v-if="receivedFunction[rowIndex].valueFunction(config, item).type === 'key'">
258
+ {{ item[receivedFunction[rowIndex].valueFunction(config, item).content] }}
259
+ </template>
260
+ <template v-if="receivedFunction[rowIndex].valueFunction(config, item).type === 'value'">
261
+ {{ receivedFunction[rowIndex].valueFunction(config, item).content }}
262
+ </template>
263
+ <template v-if="receivedFunction[rowIndex].valueFunction(config, item).type === 'imgs'">
264
+ <template v-if="!item.imgs || item.imgs.length === 0">
265
+ <p style="margin: auto">
266
+
267
+ </p>
268
+ </template>
269
+ <template v-else>
270
+ <template v-for="(img, imgIndex) in item.imgs" :key="imgIndex">
271
+ <div>
272
+ <img :src="img.path" style="max-width: 200px; max-height: 200px">
273
+ <p style="margin: auto">
274
+ {{ img.path.substring(img.path.lastIndexOf('/') + 1, img.path.lastIndexOf('.')) }}
275
+ </p>
276
+ </div>
277
+ </template>
278
+ </template>
279
+ </template>
280
+ </template>
281
+ <template v-else>
282
+ <template v-if="receivedFunction[rowIndex].valueFunction(config, item).originalKey">
283
+ <template v-if="receivedFunction[rowIndex].valueFunction(config, item).type === 'imgs'">
284
+ <Upload
285
+ :model="{
286
+ type: 'image',
287
+ accept: ['*'],
288
+ resUploadStock: 1,
289
+ pathKey: 'cs',
290
+ }"
291
+ :outer-container-index="1"
292
+ :img-prefix="imgPrefix"
293
+ :service-name="serverName"
294
+ :images="checkImg(item.imgs)"
295
+ upload-style="simple"
296
+ @set-files="(...args) => { setImages(args, item, config.data) }"
297
+ />
298
+ </template>
299
+ <template v-else>
300
+ <van-input v-model="item[receivedFunction[rowIndex].valueFunction(config, item).originalKey]" />
301
+ </template>
302
+ </template>
303
+ <template v-else>
304
+ <van-input v-model="item[receivedFunction[rowIndex].valueFunction(config, item).content]" />
305
+ </template>
306
+ </template>
307
+ </td>
308
+ </tr>
309
+ </template>
310
+ </template>
311
+ </tbody>
312
+ </table>
313
+ <!-- 图片展示弹框 -->
314
+ <VanDialog
315
+ v-model:show="showImgModal"
316
+ title="图片"
317
+ width="80%"
318
+ :z-index="1001"
319
+ @confirm="handleShowImgOk"
320
+ @cancel="handleShowImgCancel"
321
+ >
322
+ <div style="width: 100%;display: flex;justify-content: center;align-items: center">
323
+ <img :src="showImageSrc" alt="图片">
324
+ </div>
325
+ </VanDialog>
326
+ </div>
327
+ </template>
328
+
329
+ <style scoped lang="less">
330
+ .imgSrc {
331
+ color: rgb( 24,144,255 );
332
+ font-size: 0.9em;
333
+ margin: auto;
334
+ }
335
+ .imgSrc:hover {
336
+ color: rgba(24, 144, 255, 0.8);
337
+ font-size: 0.9em;
338
+ cursor: pointer;
339
+ }
340
+ .reportMain {
341
+ text-align: center;
342
+ margin: 0 auto;
343
+ font-size: 16px;
344
+ color: #000;
345
+ background-color: #fff;
346
+ border-radius: 8px;
347
+ }
348
+
349
+ .reportMainNoPadding {
350
+ text-align: center;
351
+ margin: 0 auto;
352
+ font-size: 16px;
353
+ color: #000;
354
+ background-color: #fff;
355
+ border-radius: 8px;
356
+ }
357
+
358
+ .reportTitle {
359
+ font-weight: bold;
360
+ }
361
+
362
+ .reportTable {
363
+ width: 100%;
364
+ border-collapse: collapse;
365
+ table-layout: fixed;
366
+ word-break: break-all;
367
+ }
368
+
369
+ .tdWithBorder {
370
+ border: 1px solid #000;
371
+ padding: 8px;
372
+ }
373
+
374
+ .imgText:hover {
375
+ color: rgb(24, 144, 255);
376
+ cursor: pointer;
377
+ }
378
+
379
+ .tdWithNoTopBorder {
380
+ border-top-style: none;
381
+ border-left: 1px solid #000;
382
+ border-right: 1px solid #000;
383
+ border-bottom: 1px solid #000;
384
+ padding: 8px;
385
+ }
386
+ </style>