af-mobile-client-vue3 1.1.53 → 1.2.0
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/.env +4 -1
- package/.node-version +1 -0
- package/.vscode/extensions.json +6 -1
- package/.vscode/settings.json +32 -27
- package/build/vite/index.ts +29 -29
- package/build/vite/optimize.ts +34 -0
- package/build/vite/vconsole.ts +5 -2
- package/commitlint.config.ts +32 -0
- package/eslint.config.ts +30 -0
- package/index.html +15 -9
- package/mock/data.ts +15 -15
- package/mock/index.ts +2 -0
- package/mock/modules/prose.mock.ts +5 -8
- package/mock/util.ts +19 -0
- package/netlify.toml +2 -2
- package/package.json +66 -66
- package/postcss.config.ts +27 -0
- package/public/favicon.ico +0 -0
- package/public/pwa-192x192.png +0 -0
- package/public/pwa-512x512.png +0 -0
- package/public/safari-pinned-tab.svg +3 -31
- package/src/App.vue +45 -7
- package/src/components/core/ImageUploader/index.vue +159 -159
- package/src/components/core/NavBar/index.vue +33 -2
- package/src/components/core/Tabbar/index.vue +17 -25
- package/src/components/data/XBadge/index.vue +4 -9
- package/src/components/data/XCellDetail/index.vue +0 -1
- package/src/components/data/XCellList/index.vue +2 -1
- package/src/components/data/XOlMap/utils/wgs84ToGcj02.js +154 -154
- package/src/composables/dark.ts +5 -0
- package/src/config/routes.ts +5 -0
- package/src/layout/PageLayout.vue +1 -43
- package/src/locales/en-US.json +81 -2
- package/src/locales/zh-CN.json +81 -2
- package/src/main.ts +6 -6
- package/src/router/index.ts +13 -39
- package/src/router/routes.ts +6 -4
- package/src/stores/index.ts +4 -0
- package/src/stores/modules/user.ts +11 -10
- package/src/styles/app.less +16 -57
- package/src/styles/login.less +22 -8
- package/src/styles/var.less +16 -0
- package/src/{vue-router.d.ts → types/vue-router.d.ts} +2 -0
- package/src/utils/i18n.ts +72 -41
- package/src/utils/inline-px-to-vw.ts +28 -0
- package/src/utils/set-page-title.ts +1 -1
- package/src/views/component/XCellListView/index.vue +96 -13
- package/src/views/component/XFormGroupView/index.vue +39 -2
- package/src/views/component/XFormView/index.vue +9 -30
- package/src/views/component/XOlMapView/XLocationPicker/index.vue +118 -118
- package/src/views/component/index.vue +36 -6
- package/src/views/user/login/LoginForm.vue +6 -6
- package/src/views/user/login/LoginTitle.vue +14 -6
- package/src/views/user/my/index.vue +17 -14
- package/tsconfig.json +0 -1
- package/uno.config.ts +39 -24
- package/vite.config.ts +7 -29
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -1
- package/LICENSE +0 -21
- package/eslint.config.js +0 -15
- package/mock/modules/demo.mock.ts +0 -20
- package/src/components.d.ts +0 -53
- package/src/stores/modules/routeTransitionName.ts +0 -26
- package/src/typing.ts +0 -3
- package/src/views/component/XFormView/oldindex.vue +0 -70
- /package/src/{env.d.ts → types/env.d.ts} +0 -0
- /package/src/{settings.ts → types/settings.ts} +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// 此文件不支持热更新,修改后需要重启生效
|
|
2
|
+
|
|
3
|
+
// 需要转换的 fixed 选择器列表
|
|
4
|
+
const rootContainingBlockSelectorList = [
|
|
5
|
+
'.van-tabbar',
|
|
6
|
+
'.van-popup',
|
|
7
|
+
'.van-popup--bottom',
|
|
8
|
+
'.van-popup--top',
|
|
9
|
+
'.van-popup--left',
|
|
10
|
+
'.van-popup--right',
|
|
11
|
+
// 在这里添加你的选择器
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
plugins: {
|
|
16
|
+
'autoprefixer': {},
|
|
17
|
+
|
|
18
|
+
// https://github.com/wswmsword/postcss-mobile-forever
|
|
19
|
+
'postcss-mobile-forever': {
|
|
20
|
+
appSelector: '#app',
|
|
21
|
+
viewportWidth: 375,
|
|
22
|
+
maxDisplayWidth: 800,
|
|
23
|
+
border: true,
|
|
24
|
+
rootContainingBlockSelectorList,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
package/public/favicon.ico
CHANGED
|
Binary file
|
package/public/pwa-192x192.png
CHANGED
|
Binary file
|
package/public/pwa-512x512.png
CHANGED
|
Binary file
|
|
@@ -1,32 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"
|
|
4
|
-
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
|
5
|
-
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
|
6
|
-
preserveAspectRatio="xMidYMid meet">
|
|
7
|
-
<metadata>
|
|
8
|
-
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
|
9
|
-
</metadata>
|
|
10
|
-
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
|
11
|
-
fill="#000000" stroke="none">
|
|
12
|
-
<path d="M2232 4334 c-45 -23 -67 -67 -63 -130 0 -11 55 -113 121 -227 l120
|
|
13
|
-
-207 -186 -322 c-103 -178 -229 -397 -282 -488 -53 -91 -110 -190 -127 -220
|
|
14
|
-
-17 -30 -168 -291 -335 -580 -167 -289 -310 -536 -317 -550 -8 -14 -25 -43
|
|
15
|
-
-38 -65 -13 -22 -32 -56 -43 -75 -19 -35 -203 -354 -269 -465 -63 -106 -53
|
|
16
|
-
-178 30 -222 29 -15 171 -16 1717 -16 1855 0 1719 -5 1769 59 37 47 28 87 -50
|
|
17
|
-
224 -39 69 -80 141 -91 160 -26 45 -236 409 -465 805 -99 171 -191 330 -205
|
|
18
|
-
355 -14 25 -76 133 -138 240 -96 165 -210 363 -280 485 -10 17 -93 161 -185
|
|
19
|
-
320 -92 160 -176 305 -186 323 l-19 32 109 188 c140 242 133 229 133 273 -1
|
|
20
|
-
69 -58 119 -136 119 -58 0 -88 -30 -172 -174 -43 -75 -82 -136 -85 -136 -4 0
|
|
21
|
-
-13 12 -20 28 -46 90 -146 246 -166 259 -35 23 -102 27 -141 7z m388 -926 c30
|
|
22
|
-
-51 64 -111 76 -133 12 -22 68 -119 124 -215 56 -96 112 -193 125 -215 12 -22
|
|
23
|
-
55 -96 95 -165 40 -69 91 -156 113 -195 22 -38 126 -218 232 -400 105 -181
|
|
24
|
-
203 -350 217 -375 14 -25 74 -128 133 -230 60 -102 116 -198 125 -215 62 -107
|
|
25
|
-
123 -211 130 -220 6 -7 -140 -10 -433 -9 l-441 1 -1 314 c-1 273 -4 321 -19
|
|
26
|
-
369 -46 138 -98 215 -202 292 -63 47 -139 87 -171 89 -2 1 -25 6 -51 14 -95
|
|
27
|
-
25 -271 2 -362 -47 -153 -82 -263 -228 -296 -390 -4 -18 -7 -170 -8 -337 l-1
|
|
28
|
-
-304 -442 -1 c-293 -1 -439 2 -433 9 7 9 77 127 135 230 13 22 128 220 255
|
|
29
|
-
440 128 220 241 416 252 435 74 129 221 382 274 473 35 59 64 109 64 111 0 3
|
|
30
|
-
22 40 49 84 27 44 50 84 52 89 10 26 344 593 349 593 3 0 30 -42 60 -92z"/>
|
|
31
|
-
</g>
|
|
1
|
+
<svg width="683" height="683" viewBox="0 0 683 683" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M297.6 104.8C291.6 107.867 288.667 113.733 289.2 122.133C289.2 123.6 296.533 137.2 305.333 152.4L321.333 180L296.533 222.933C282.8 246.667 266 275.867 258.933 288C251.867 300.133 244.267 313.333 242 317.333C239.733 321.333 219.6 356.133 197.333 394.667C175.067 433.2 156 466.133 155.067 468C154 469.867 151.733 473.733 150 476.667C148.267 479.6 145.733 484.133 144.267 486.667C141.733 491.333 117.2 533.867 108.4 548.667C100 562.8 101.333 572.4 112.4 578.267C116.267 580.267 135.2 580.4 341.333 580.4C588.667 580.4 570.533 581.067 577.2 572.533C582.133 566.267 580.933 560.933 570.533 542.667C565.333 533.467 559.867 523.867 558.4 521.333C554.933 515.333 526.933 466.8 496.4 414C483.2 391.2 470.933 370 469.067 366.667C467.2 363.333 458.933 348.933 450.667 334.667C437.867 312.667 422.667 286.267 413.333 270C412 267.733 400.933 248.533 388.667 227.333C376.4 206 365.2 186.667 363.867 184.267L361.333 180L375.867 154.933C394.533 122.667 393.6 124.4 393.6 118.533C393.467 109.333 385.867 102.667 375.467 102.667C367.733 102.667 363.733 106.667 352.533 125.867C346.8 135.867 341.6 144 341.2 144C340.667 144 339.467 142.4 338.533 140.267C332.4 128.267 319.067 107.467 316.4 105.733C311.733 102.667 302.8 102.133 297.6 104.8ZM349.333 228.267C353.333 235.067 357.867 243.067 359.467 246C361.067 248.933 368.533 261.867 376 274.667C383.467 287.467 390.933 300.4 392.667 303.333C394.267 306.267 400 316.133 405.333 325.333C410.667 334.533 417.467 346.133 420.4 351.333C423.333 356.4 437.2 380.4 451.333 404.667C465.333 428.8 478.4 451.333 480.267 454.667C482.133 458 490.133 471.733 498 485.333C506 498.933 513.467 511.733 514.667 514C522.933 528.267 531.067 542.133 532 543.333C532.8 544.267 513.333 544.667 474.267 544.533L415.467 544.4L415.333 502.533C415.2 466.133 414.8 459.733 412.8 453.333C406.667 434.933 399.733 424.667 385.867 414.4C377.467 408.133 367.333 402.8 363.067 402.533C362.8 402.4 359.733 401.733 356.267 400.667C343.6 397.333 320.133 400.4 308 406.933C287.6 417.867 272.933 437.333 268.533 458.933C268 461.333 267.6 481.6 267.467 503.867V544.495L415.467 544.4L208.4 544.533C169.333 544.667 149.867 544.267 150.667 543.333C151.6 542.133 160.933 526.4 168.667 512.667C170.4 509.733 185.733 483.333 202.667 454C219.733 424.667 234.8 398.533 236.267 396C246.133 378.8 265.733 345.067 272.8 332.933C277.467 325.067 281.333 318.4 281.333 318.133C281.333 317.733 284.267 312.8 287.867 306.933C291.467 301.067 294.533 295.733 294.8 295.067C296.133 291.6 340.667 216 341.333 216C341.733 216 345.333 221.6 349.333 228.267Z" fill="black"/>
|
|
3
|
+
<path d="M415.333 502.533L415.467 544.4L267.467 544.495V503.867C267.6 481.6 268 461.333 268.533 458.933C272.933 437.333 287.6 417.867 308 406.933C320.133 400.4 343.6 397.333 356.267 400.667C359.733 401.733 362.8 402.4 363.067 402.533C367.333 402.8 377.467 408.133 385.867 414.4C399.733 424.667 406.667 434.933 412.8 453.333C414.8 459.733 415.2 466.133 415.333 502.533Z" fill="#FB4D31"/>
|
|
32
4
|
</svg>
|
package/src/App.vue
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { ConfigProviderThemeVars } from 'vant'
|
|
3
|
+
import NavBar from '@af-mobile-client-vue3/components/core/NavBar/index.vue'
|
|
4
|
+
import Tabbar from '@af-mobile-client-vue3/components/core/Tabbar/index.vue'
|
|
5
|
+
import { isDark, preferredDark } from '@af-mobile-client-vue3/composables/dark'
|
|
6
|
+
import useRouteCacheStore from '@af-mobile-client-vue3/stores/modules/routeCache'
|
|
3
7
|
import { useHead } from '@unhead/vue'
|
|
4
8
|
import {
|
|
5
9
|
ConfigProvider as VanConfigProvider,
|
|
6
10
|
} from 'vant/es'
|
|
7
|
-
import { reactive } from 'vue'
|
|
11
|
+
import { computed, reactive } from 'vue'
|
|
8
12
|
|
|
9
13
|
useHead({
|
|
10
14
|
title: '智慧燃气',
|
|
@@ -15,29 +19,63 @@ useHead({
|
|
|
15
19
|
},
|
|
16
20
|
{
|
|
17
21
|
name: 'theme-color',
|
|
18
|
-
content: () => '#ffffff',
|
|
22
|
+
content: () => isDark.value ? '#00aba9' : '#ffffff',
|
|
19
23
|
},
|
|
20
24
|
],
|
|
25
|
+
link: [
|
|
26
|
+
{
|
|
27
|
+
rel: 'icon',
|
|
28
|
+
type: 'image/svg+xml',
|
|
29
|
+
href: () => preferredDark.value ? '/favicon-dark.svg' : '/favicon.svg',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const routeCacheStore = useRouteCacheStore()
|
|
35
|
+
|
|
36
|
+
const keepAliveRouteNames = computed(() => {
|
|
37
|
+
return routeCacheStore.routeCaches as string[]
|
|
21
38
|
})
|
|
39
|
+
|
|
40
|
+
const mode = computed(() => {
|
|
41
|
+
return isDark.value ? 'dark' : 'light'
|
|
42
|
+
})
|
|
43
|
+
|
|
22
44
|
const themeVars: ConfigProviderThemeVars = reactive({
|
|
23
45
|
baseFont: 'PingFangSC-Regular-woff2, serif',
|
|
24
46
|
})
|
|
25
47
|
</script>
|
|
26
48
|
|
|
27
49
|
<template>
|
|
28
|
-
<VanConfigProvider theme="
|
|
50
|
+
<VanConfigProvider :theme="mode" :theme-vars="themeVars" theme-vars-scope="global">
|
|
51
|
+
<NavBar />
|
|
29
52
|
<router-view v-slot="{ Component }">
|
|
30
|
-
<
|
|
53
|
+
<section class="app-wrapper">
|
|
54
|
+
<keep-alive :include="keepAliveRouteNames">
|
|
55
|
+
<component :is="Component" />
|
|
56
|
+
</keep-alive>
|
|
57
|
+
</section>
|
|
31
58
|
</router-view>
|
|
59
|
+
<Tabbar />
|
|
32
60
|
</VanConfigProvider>
|
|
33
61
|
</template>
|
|
34
62
|
|
|
35
63
|
<style>
|
|
36
|
-
@import
|
|
37
|
-
input[type=
|
|
64
|
+
@import './font-style/font.css';
|
|
65
|
+
input[type='password']::-ms-reveal {
|
|
38
66
|
display: none;
|
|
39
67
|
}
|
|
40
|
-
input[type=
|
|
68
|
+
input[type='password']::-ms-clear {
|
|
41
69
|
display: none;
|
|
42
70
|
}
|
|
43
71
|
</style>
|
|
72
|
+
|
|
73
|
+
<style scoped>
|
|
74
|
+
.app-wrapper {
|
|
75
|
+
width: 100%;
|
|
76
|
+
/**
|
|
77
|
+
TODO 源框架问题:增加后动画无效
|
|
78
|
+
*/
|
|
79
|
+
position: relative;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -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,12 +1,43 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { NavBar as VanNavBar } from 'vant/es'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { useI18n } from 'vue-i18n'
|
|
5
|
+
import { useRoute, useRouter } from 'vue-router'
|
|
6
|
+
|
|
7
|
+
const route = useRoute()
|
|
8
|
+
const router = useRouter()
|
|
3
9
|
|
|
4
10
|
// back
|
|
5
|
-
|
|
11
|
+
function onBack() {
|
|
12
|
+
if (window.history.state.back)
|
|
13
|
+
history.back()
|
|
14
|
+
else
|
|
15
|
+
router.replace('/')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { t } = useI18n()
|
|
19
|
+
|
|
20
|
+
const title = computed(() => {
|
|
21
|
+
if (!route.meta)
|
|
22
|
+
return ''
|
|
23
|
+
|
|
24
|
+
return route.meta.i18n ? t(route.meta.i18n) : (route.meta.title || '')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const showNavBar = computed(() => route.meta.navBar)
|
|
28
|
+
|
|
29
|
+
const showLeftArrow = computed(() => route.name && route.name === 'ComponentView')
|
|
6
30
|
</script>
|
|
7
31
|
|
|
8
32
|
<template>
|
|
9
|
-
<VanNavBar
|
|
33
|
+
<VanNavBar
|
|
34
|
+
v-show="showNavBar"
|
|
35
|
+
:title="title"
|
|
36
|
+
:fixed="true"
|
|
37
|
+
clickable placeholder
|
|
38
|
+
:left-arrow="!showLeftArrow"
|
|
39
|
+
@click-left="onBack"
|
|
40
|
+
/>
|
|
10
41
|
</template>
|
|
11
42
|
|
|
12
43
|
<style scoped></style>
|
|
@@ -1,38 +1,30 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { ref } from 'vue'
|
|
2
|
+
import { routeWhiteList } from '@af-mobile-client-vue3/config/routes'
|
|
3
|
+
import { Tabbar as VanTabbar, TabbarItem as VanTabbarItem } from 'vant'
|
|
4
|
+
import { computed, ref } from 'vue'
|
|
5
|
+
import { useRoute } from 'vue-router'
|
|
5
6
|
import 'vant/lib/tabbar-item/index.css'
|
|
6
7
|
import 'vant/lib/tabbar/index.css'
|
|
7
8
|
|
|
8
|
-
const Props = defineProps<{
|
|
9
|
-
tabbarData: [
|
|
10
|
-
{
|
|
11
|
-
icon: string
|
|
12
|
-
title: string
|
|
13
|
-
to: {
|
|
14
|
-
name: string
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
]
|
|
18
|
-
}>()
|
|
19
9
|
const active = ref(0)
|
|
10
|
+
const route = useRoute()
|
|
11
|
+
|
|
12
|
+
const show = computed(() => route.name && routeWhiteList.includes(route.name as string))
|
|
20
13
|
</script>
|
|
21
14
|
|
|
22
15
|
<template>
|
|
23
|
-
<VanTabbar v-
|
|
24
|
-
<VanTabbarItem
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
>
|
|
29
|
-
<template v-if="item.icon.startsWith('svg:')" #icon="props">
|
|
30
|
-
<SvgIcon :name="item.icon.split(':')[1]" :class-name="props.active ? 'svg-icon-active-blue' : ``" />
|
|
16
|
+
<VanTabbar v-if="show" v-model="active" route placeholder>
|
|
17
|
+
<VanTabbarItem replace to="/Component/main">
|
|
18
|
+
{{ '首页' }}
|
|
19
|
+
<template #icon>
|
|
20
|
+
<div class="i-carbon:home" />
|
|
31
21
|
</template>
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
</VanTabbarItem>
|
|
23
|
+
<VanTabbarItem replace to="/user/my">
|
|
24
|
+
{{ '我的' }}
|
|
25
|
+
<template #icon>
|
|
26
|
+
<div class="i-carbon:user" />
|
|
34
27
|
</template>
|
|
35
|
-
{{ item.title }}
|
|
36
28
|
</VanTabbarItem>
|
|
37
29
|
</VanTabbar>
|
|
38
30
|
</template>
|
|
@@ -3,15 +3,10 @@ import { getDictItemByValue } from '@af-mobile-client-vue3/utils/dictUtil'
|
|
|
3
3
|
import { ref } from 'vue'
|
|
4
4
|
|
|
5
5
|
const { serviceName, dictName, dictValue } = withDefaults(defineProps<{
|
|
6
|
-
serviceName
|
|
7
|
-
dictName
|
|
8
|
-
dictValue:
|
|
9
|
-
|
|
10
|
-
}>(), {
|
|
11
|
-
serviceName: undefined,
|
|
12
|
-
dictName: '',
|
|
13
|
-
isColor: true,
|
|
14
|
-
})
|
|
6
|
+
serviceName: string
|
|
7
|
+
dictName: string
|
|
8
|
+
dictValue: string
|
|
9
|
+
}>(), {})
|
|
15
10
|
|
|
16
11
|
// 字面值
|
|
17
12
|
const label = ref('')
|