af-mobile-client-vue3 1.0.54

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 (156) hide show
  1. package/.editorconfig +38 -0
  2. package/.env +7 -0
  3. package/.env.development +4 -0
  4. package/.env.envoiceShow +7 -0
  5. package/.env.production +7 -0
  6. package/.husky/commit-msg +1 -0
  7. package/.husky/pre-commit +1 -0
  8. package/.vscode/extensions.json +7 -0
  9. package/.vscode/settings.json +61 -0
  10. package/LICENSE +21 -0
  11. package/README.md +181 -0
  12. package/af-example-mobile-vue-web.iml +9 -0
  13. package/build/vite/index.ts +91 -0
  14. package/build/vite/vconsole.ts +44 -0
  15. package/eslint.config.js +7 -0
  16. package/index.html +17 -0
  17. package/mock/data.ts +20 -0
  18. package/mock/index.ts +5 -0
  19. package/mock/modules/prose.mock.ts +16 -0
  20. package/mock/modules/user.mock.ts +152 -0
  21. package/netlify.toml +12 -0
  22. package/package.json +107 -0
  23. package/public/favicon-dark.svg +4 -0
  24. package/public/favicon.ico +0 -0
  25. package/public/favicon.svg +4 -0
  26. package/public/pwa-192x192.png +0 -0
  27. package/public/pwa-512x512.png +0 -0
  28. package/public/safari-pinned-tab.svg +32 -0
  29. package/scripts/verifyCommit.js +19 -0
  30. package/src/App.vue +43 -0
  31. package/src/api/mock/index.ts +30 -0
  32. package/src/api/user/index.ts +40 -0
  33. package/src/assets/common/default-user-profile.png +0 -0
  34. package/src/assets/img/apps/apply-web.png +0 -0
  35. package/src/assets/img/apps/example-web.png +0 -0
  36. package/src/assets/img/apps/iot-web.png +0 -0
  37. package/src/assets/img/apps/linepatrol-web.png +0 -0
  38. package/src/assets/img/apps/monitor-web.png +0 -0
  39. package/src/assets/img/apps/oa-web.png +0 -0
  40. package/src/assets/img/apps/revenue-web.png +0 -0
  41. package/src/assets/img/apps/safe-check-web.png +0 -0
  42. package/src/assets/img/component/logo.png +0 -0
  43. package/src/assets/img/home/banner1.png +0 -0
  44. package/src/assets/img/home/banner2.png +0 -0
  45. package/src/assets/img/home/banner3.png +0 -0
  46. package/src/assets/img/home/banner4.png +0 -0
  47. package/src/assets/img/home/notice/icon.png +0 -0
  48. package/src/assets/img/user/login/background-shadow-1.svg +20 -0
  49. package/src/assets/img/user/login/logo-background.svg +20 -0
  50. package/src/assets/img/user/login/logo.png +0 -0
  51. package/src/assets/img/user/my/exit-login.png +0 -0
  52. package/src/assets/img/user/my/setting-arrow.png +0 -0
  53. package/src/assets/img/user/my/setting.png +0 -0
  54. package/src/bootstrap.ts +32 -0
  55. package/src/components/core/App/MicroAppView.vue +59 -0
  56. package/src/components/core/BeautifulLoading/index.vue +47 -0
  57. package/src/components/core/NavBar/index.vue +12 -0
  58. package/src/components/core/SvgIcon/index.vue +61 -0
  59. package/src/components/core/Tabbar/index.vue +38 -0
  60. package/src/components/core/Uploader/index.vue +104 -0
  61. package/src/components/core/XMultiSelect/index.vue +196 -0
  62. package/src/components/core/XSelect/index.vue +130 -0
  63. package/src/components/data/XBadge/index.vue +85 -0
  64. package/src/components/data/XCellDetail/index.vue +106 -0
  65. package/src/components/data/XCellList/index.vue +358 -0
  66. package/src/components/data/XCellListFilter/index.vue +392 -0
  67. package/src/components/data/XForm/index.vue +127 -0
  68. package/src/components/data/XFormItem/index.vue +472 -0
  69. package/src/components/data/XReportForm/XReportFormJsonRender.vue +220 -0
  70. package/src/components/data/XReportForm/index.vue +1058 -0
  71. package/src/components/layout/NormalDataLayout/index.vue +70 -0
  72. package/src/components/layout/TabBarLayout/index.vue +40 -0
  73. package/src/components.d.ts +53 -0
  74. package/src/enums/requestEnum.ts +25 -0
  75. package/src/env.d.ts +16 -0
  76. package/src/font-style/PingFangSC-Regular.woff2 +0 -0
  77. package/src/font-style/font.css +4 -0
  78. package/src/hooks/useCommon.ts +9 -0
  79. package/src/hooks/useLogin.ts +97 -0
  80. package/src/icons/svg/bird.svg +1 -0
  81. package/src/icons/svg/check-in.svg +33 -0
  82. package/src/icons/svg/dark.svg +5 -0
  83. package/src/icons/svg/github.svg +5 -0
  84. package/src/icons/svg/light.svg +5 -0
  85. package/src/icons/svg/link.svg +5 -0
  86. package/src/icons/svg/loadError.svg +1 -0
  87. package/src/icons/svg/notFound.svg +1 -0
  88. package/src/icons/svgo.yml +22 -0
  89. package/src/layout/PageLayout.vue +51 -0
  90. package/src/layout/SingleLayout.vue +35 -0
  91. package/src/locales/en-US.json +25 -0
  92. package/src/locales/zh-CN.json +25 -0
  93. package/src/main.ts +48 -0
  94. package/src/plugins/AppData.ts +38 -0
  95. package/src/plugins/GetLoginInfoService.ts +10 -0
  96. package/src/plugins/index.ts +11 -0
  97. package/src/router/README.md +8 -0
  98. package/src/router/guards.ts +60 -0
  99. package/src/router/index.ts +60 -0
  100. package/src/router/invoiceRoutes.ts +33 -0
  101. package/src/router/routes.ts +84 -0
  102. package/src/services/api/Login.ts +6 -0
  103. package/src/services/api/common.ts +98 -0
  104. package/src/services/api/index.ts +7 -0
  105. package/src/services/api/manage.ts +8 -0
  106. package/src/services/restTools.ts +37 -0
  107. package/src/settings.ts +1 -0
  108. package/src/stores/index.ts +7 -0
  109. package/src/stores/modules/cachedView.ts +31 -0
  110. package/src/stores/modules/counter.ts +19 -0
  111. package/src/stores/modules/routeTransitionName.ts +26 -0
  112. package/src/stores/modules/setting.ts +28 -0
  113. package/src/stores/modules/user.ts +180 -0
  114. package/src/stores/mutation-type.ts +7 -0
  115. package/src/styles/app.less +67 -0
  116. package/src/styles/login.less +81 -0
  117. package/src/typing.ts +3 -0
  118. package/src/utils/Storage.ts +124 -0
  119. package/src/utils/authority-utils.ts +87 -0
  120. package/src/utils/common.ts +41 -0
  121. package/src/utils/crypto.ts +39 -0
  122. package/src/utils/dataUtil.ts +42 -0
  123. package/src/utils/dictUtil.ts +51 -0
  124. package/src/utils/http/index.ts +158 -0
  125. package/src/utils/i18n.ts +41 -0
  126. package/src/utils/indexedDB.ts +180 -0
  127. package/src/utils/local-storage.ts +9 -0
  128. package/src/utils/mobileUtil.ts +26 -0
  129. package/src/utils/progress.ts +19 -0
  130. package/src/utils/routerUtil.ts +271 -0
  131. package/src/utils/set-page-title.ts +7 -0
  132. package/src/utils/validate.ts +6 -0
  133. package/src/views/chat/index.vue +153 -0
  134. package/src/views/common/LoadError.vue +64 -0
  135. package/src/views/common/NotFound.vue +68 -0
  136. package/src/views/component/EvaluateRecordView/index.vue +40 -0
  137. package/src/views/component/XCellDetailView/index.vue +216 -0
  138. package/src/views/component/XCellListView/index.vue +36 -0
  139. package/src/views/component/XFormView/index.vue +478 -0
  140. package/src/views/component/XReportFormIframeView/index.vue +45 -0
  141. package/src/views/component/XReportFormView/index.vue +295 -0
  142. package/src/views/component/index.vue +111 -0
  143. package/src/views/component/menu.vue +117 -0
  144. package/src/views/component/notice.vue +46 -0
  145. package/src/views/component/topNav.vue +36 -0
  146. package/src/views/invoiceShow/index.vue +62 -0
  147. package/src/views/user/login/ForgetPasswordForm.vue +93 -0
  148. package/src/views/user/login/LoginForm.vue +145 -0
  149. package/src/views/user/login/LoginTitle.vue +68 -0
  150. package/src/views/user/login/LoginWave.vue +109 -0
  151. package/src/views/user/login/index.vue +22 -0
  152. package/src/views/user/my/index.vue +230 -0
  153. package/src/vue-router.d.ts +9 -0
  154. package/tsconfig.json +43 -0
  155. package/uno.config.ts +32 -0
  156. package/vite.config.ts +110 -0
@@ -0,0 +1,47 @@
1
+ <script setup>
2
+ import SvgIcon from './components/core/SvgIcon/index.vue'
3
+ </script>
4
+
5
+ <template>
6
+ <div class="main">
7
+ <div class="bird-container">
8
+ <SvgIcon name="bird" class="bird" />
9
+ </div>
10
+ <div class="loading-text">
11
+ 加载中,请稍候...
12
+ </div>
13
+ </div>
14
+ </template>
15
+
16
+ <style scoped>
17
+ .main {
18
+ display: flex;
19
+ flex-direction: column;
20
+ justify-content: center;
21
+ align-items: center;
22
+ height: 100vh;
23
+ }
24
+
25
+ .bird-container {
26
+ width: 60px;
27
+ height: 60px;
28
+ animation: fade 3s ease-out infinite;
29
+ }
30
+
31
+ .bird {
32
+ width: 60px;
33
+ height: 60px;
34
+ }
35
+
36
+ .loading-text {
37
+ margin-top: 20px;
38
+ font-size: 14px;
39
+ color: #333;
40
+ }
41
+
42
+ @keyframes fade {
43
+ 0% { opacity: 1; }
44
+ 50% { opacity: 0.3; }
45
+ 100% { opacity: 1; }
46
+ }
47
+ </style>
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+ import { NavBar as VanNavBar } from 'vant/es'
3
+
4
+ // back
5
+ const onClickLeft = () => history.back()
6
+ </script>
7
+
8
+ <template>
9
+ <VanNavBar placeholder left-arrow fixed @click-left="onClickLeft" />
10
+ </template>
11
+
12
+ <style scoped></style>
@@ -0,0 +1,61 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { isExternal } from '@af-mobile-client-vue3/utils/validate'
4
+
5
+ interface Props {
6
+ name: string
7
+ className?: string
8
+ }
9
+ const props = withDefaults(defineProps<Props>(), {
10
+ name: '',
11
+ className: '',
12
+ })
13
+
14
+ const isExternalIcon = computed(() => isExternal(props.name))
15
+ const iconName = computed(() => `#icon-${props.name}`)
16
+ const svgClass = computed(() => {
17
+ if (props.className)
18
+ return `svg-icon ${props.className}`
19
+ else
20
+ return 'svg-icon'
21
+ })
22
+ // 外链 icon
23
+ const styleExternalIcon = computed(() => {
24
+ return {
25
+ 'mask': `url(${props.name}) no-repeat 50% 50%`,
26
+ '-webkit-mask': `url(${props.name}) no-repeat 50% 50%`,
27
+ }
28
+ })
29
+ </script>
30
+
31
+ <template>
32
+ <div
33
+ v-if="isExternalIcon"
34
+ :style="styleExternalIcon"
35
+ class="svg-external-icon svg-icon"
36
+ v-bind="$attrs"
37
+ />
38
+ <svg v-else :class="svgClass" aria-hidden="true" v-bind="$attrs">
39
+ <use :xlink:href="iconName" />
40
+ </svg>
41
+ </template>
42
+
43
+ <style scoped>
44
+ .svg-icon {
45
+ width: 1em;
46
+ height: 1em;
47
+ vertical-align: -0.15em;
48
+ fill: currentColor;
49
+ overflow: hidden;
50
+ }
51
+
52
+ .svg-external-icon {
53
+ background-color: currentColor;
54
+ mask-size: cover !important;
55
+ display: inline-block;
56
+ }
57
+
58
+ .svg-icon-active-blue {
59
+ color: #1989FA
60
+ }
61
+ </style>
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import SvgIcon from '@af-mobile-client-vue3/components/core/SvgIcon/index.vue'
4
+ import { Icon as VanIcon, Tabbar as VanTabbar, TabbarItem as VanTabbarItem } from 'vant'
5
+ import 'vant/lib/tabbar-item/index.css'
6
+ import 'vant/lib/tabbar/index.css'
7
+
8
+ const Props = defineProps<{
9
+ tabbarData: [
10
+ {
11
+ icon: string
12
+ title: string
13
+ to: {
14
+ name: string
15
+ }
16
+ },
17
+ ]
18
+ }>()
19
+ const active = ref(0)
20
+ </script>
21
+
22
+ <template>
23
+ <VanTabbar v-model="active" :placeholder="true" :route="true" fixed>
24
+ <VanTabbarItem
25
+ v-for="(item, index) in Props.tabbarData"
26
+ :key="index"
27
+ :to="item.to"
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' : ``" />
31
+ </template>
32
+ <template v-else #icon="props">
33
+ <VanIcon :name="item.icon" />
34
+ </template>
35
+ {{ item.title }}
36
+ </VanTabbarItem>
37
+ </VanTabbar>
38
+ </template>
@@ -0,0 +1,104 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Uploader as vanUploader,
4
+ } from 'vant'
5
+ import { ref } from 'vue'
6
+ import { deleteFile, upload } from '@af-mobile-client-vue3/services/api/common'
7
+
8
+ const props = defineProps({
9
+ imageList: Array<any>,
10
+ outerIndex: { default: undefined },
11
+ authority: { default: 'user' },
12
+ uploadMode: { default: 'server' },
13
+ })
14
+ const emit = defineEmits(['updateFileList'])
15
+
16
+ const imageList = ref([])
17
+
18
+ // 上传图片
19
+ function fileUpload(file: any): void {
20
+ const formData = new FormData()
21
+
22
+ formData.append('avatar', file.file)
23
+ formData.append('resUploadMode', props.uploadMode)
24
+ formData.append('pathKey', 'Default')
25
+
26
+ formData.append('formType', 'image')
27
+ formData.append('useType', 'Default')
28
+ formData.append('resUploadStock', '1')
29
+ formData.append('filename', file.file.name)
30
+ formData.append('filesize', (file.file.size / 1024 / 1024).toFixed(4))
31
+ formData.append('f_operator', '')
32
+
33
+ imageList.value[imageList.value.length - 1].message = '上传中...'
34
+ imageList.value[imageList.value.length - 1].status = 'uploading'
35
+ imageList.value[imageList.value.length - 1]!.name = file.file.name
36
+
37
+ upload(formData, 'af-system', { 'Content-Type': 'multipart/form-data' }).then((res: any) => {
38
+ if (res.success || res.id) {
39
+ imageList.value[imageList.value.length - 1].uid = res.data.id
40
+ imageList.value[imageList.value.length - 1].id = res.data.id
41
+ delete imageList.value[imageList.value.length - 1].message
42
+ imageList.value[imageList.value.length - 1].status = 'done'
43
+ imageList.value[imageList.value.length - 1].url = res.data.f_downloadpath
44
+ }
45
+ else {
46
+ imageList.value[imageList.value.length - 1].status = 'failed'
47
+ imageList.value[imageList.value.length - 1].message = '上传失败'
48
+ }
49
+ if (props.outerIndex !== undefined)
50
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
51
+ else
52
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
53
+ })
54
+ }
55
+
56
+ // 删除图片
57
+ function deleteFileFunction(file: any) {
58
+ if (file.id) {
59
+ deleteFile({ id: file.id, f_state: '删除' }).then((res: any) => {
60
+ if (res.id) {
61
+ let target = -1
62
+ for (let i = 0; i < imageList.value.length; i++) {
63
+ if (imageList.value[i].id === Number.parseInt(res.id)) {
64
+ target = i
65
+ break
66
+ }
67
+ }
68
+ imageList.value.splice(target, 1)
69
+ if (props.outerIndex !== undefined)
70
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'), props.outerIndex)
71
+ else
72
+ emit('updateFileList', imageList.value.filter(item => item.status === 'done'))
73
+ }
74
+ })
75
+ }
76
+ }
77
+
78
+ // ------------------------- 初始化 -------------------------
79
+ initComponent()
80
+
81
+ function initComponent() {
82
+ if (props.imageList.length > 0)
83
+ imageList.value = JSON.parse(JSON.stringify(props.imageList))
84
+ }
85
+ </script>
86
+
87
+ <template>
88
+ <van-uploader
89
+ v-if="props.authority === 'user'"
90
+ v-model="imageList"
91
+ :after-read="fileUpload"
92
+ :show-upload="false"
93
+ :deletable="false"
94
+ />
95
+ <van-uploader
96
+ v-else-if="props.authority === 'admin'"
97
+ v-model="imageList"
98
+ :after-read="fileUpload"
99
+ :before-delete="deleteFileFunction"
100
+ />
101
+ </template>
102
+
103
+ <style scoped lang="less">
104
+ </style>
@@ -0,0 +1,196 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Field as VanField,
4
+ Picker as VanPicker,
5
+ Popup as VanPopup,
6
+ Cell as VanCell,
7
+ CellGroup as VanCellGroup,
8
+ Checkbox as VanCheckbox,
9
+ CheckboxGroup as VanCheckboxGroup,
10
+ } from 'vant'
11
+ import {ref,reactive,watch,computed} from 'vue'
12
+ const props = defineProps({
13
+ columns: {
14
+ type: Array,
15
+ default: function () {
16
+ return []
17
+ }
18
+ },
19
+ selectValue: {
20
+ type: Array,
21
+ default: function () {
22
+ return []
23
+ }
24
+ },
25
+ option: {
26
+ type: Object,
27
+ default: function () {
28
+ return { label: 'label', value: 'value' }
29
+ }
30
+ },
31
+ // 是否支持搜索
32
+ isSearch: {
33
+ type: Boolean,
34
+ default: true
35
+ }
36
+ })
37
+ let emits = defineEmits(['input','confirm','change','cancel'])
38
+ let {columns,selectValue,option,isSearch} = props
39
+ let show = ref(false)
40
+ let searchVal = ref('')
41
+ let columnsData = reactive(JSON.parse(JSON.stringify(columns)))
42
+ let checkboxValue = reactive(JSON.parse(JSON.stringify(selectValue)))
43
+ let checkedAll = ref(false)
44
+ let resultValue = reactive(JSON.parse(JSON.stringify(selectValue)))
45
+ let checkboxGroup = ref()
46
+ let checkboxes = ref()
47
+ function search (val) {
48
+ if (val) {
49
+ columnsData = columnsData.filter(item => {
50
+ return item[option.label].indexOf(val) > -1
51
+ })
52
+ } else {
53
+ columnsData = JSON.parse(JSON.stringify(columns))
54
+ }
55
+ }
56
+ function getData (val) {
57
+ const res = columnsData.filter(item => {
58
+ return val.indexOf(item[option.value]) > -1
59
+ })
60
+ return res
61
+ }
62
+ function onConfirm () {
63
+ resultValue = checkboxValue
64
+ show.value = !show.value
65
+ emits('confirm', resultValue, getData(resultValue))
66
+ }
67
+ function change (val) {
68
+ emits('change', val, getData(resultValue))
69
+ }
70
+ function cancel () {
71
+ show.value = !show.value
72
+ emits('cancel', resultValue)
73
+ }
74
+ const toggle = (index) => {
75
+ // 假设 checkboxValue 是一个对象,我们通过 index 来切换对应 checkbox 的值
76
+ checkboxValue.value[index] = !checkboxValue.value[index];
77
+ }
78
+ const toggleAll = () => {
79
+ // 遍历 checkboxValue 并设置所有 checkbox 的值为 checkedAll 的值
80
+ Object.keys(checkboxValue.value).forEach((key) => {
81
+ checkboxValue.value[key] = checkedAll.value;
82
+ });
83
+ }
84
+ function showPopu (disabled) {
85
+ columnsData = JSON.parse(JSON.stringify(columns))
86
+ checkboxValue = JSON.parse(JSON.stringify(selectValue))
87
+ resultValue = JSON.parse(JSON.stringify(selectValue))
88
+ if (disabled !== undefined && disabled !== false) {
89
+ return false
90
+ } else {
91
+ show.value = !show.value
92
+ }
93
+ }
94
+ watch(()=> selectValue,(newVal, oldVal)=>{
95
+ resultValue = newVal
96
+ })
97
+ watch(()=> resultValue,(newVal, oldVal)=>{
98
+ searchVal.value = ''
99
+ columnsData = JSON.parse(JSON.stringify(columns))
100
+ emits('input', newVal)
101
+ })
102
+ watch(()=> columnsData,(newVal, oldVal)=>{
103
+ if (newVal.length && newVal.length === checkboxValue.length) {
104
+ checkedAll.value = true
105
+ } else {
106
+ checkedAll.value = false
107
+ }
108
+ })
109
+ watch(()=> checkboxValue,(newVal, oldVal)=>{
110
+ if (newVal.length && newVal.length === columnsData.length) {
111
+ checkedAll.value = true
112
+ } else {
113
+ checkedAll.value = false
114
+ }
115
+ })
116
+ watch(checkedAll, (newVal) => {
117
+ toggleAll();
118
+ })
119
+ let resultLabel = computed({
120
+ get () {
121
+ const res = columns.filter(item => {
122
+ return resultValue.indexOf(item[this.option.value]) > -1
123
+ })
124
+ const resLabel = res.map(item => {
125
+ return item[this.option.label]
126
+ })
127
+ return resLabel.join(',')
128
+ },
129
+ set () {
130
+
131
+ }
132
+ })
133
+ </script>
134
+
135
+ <template>
136
+ <div class="dh-field">
137
+ <div class="van-hairline--bottom">
138
+ <van-field
139
+ v-model="resultLabel"
140
+ v-bind="$attrs"
141
+ readonly
142
+ :is-link="$attrs.disabled === undefined"
143
+ error-message-align='right'
144
+ input-align="right"
145
+ class="dh-cell"
146
+ @click="showPopu($attrs.disabled)"
147
+ />
148
+ <van-popup v-model="show" position="bottom" class="" >
149
+ <div class="van-picker__toolbar">
150
+ <button type="button" class="van-picker__cancel" @click="cancel">取消</button>
151
+ <div class="van-ellipsis van-picker__title">{{$attrs.label}}</div>
152
+ <button type="button" class="van-picker__confirm" @click="onConfirm">确认</button>
153
+ </div>
154
+ <div style="max-height:264px; overflow-y:auto;">
155
+ <van-field v-if="isSearch" v-model="searchVal" input-align="left" placeholder="搜索" @input="search"/>
156
+ <van-cell title="全选">
157
+ <template #right-icon>
158
+ <van-checkbox v-model="checkedAll" name="all" @click="toggleAll"/>
159
+ </template>
160
+ </van-cell>
161
+ <van-checkbox-group ref="checkboxGroup" v-model="checkboxValue" @change="change">
162
+ <van-cell-group>
163
+ <van-cell
164
+ v-for="(item, index) in columnsData"
165
+ :key="item[option.value]"
166
+ :title="item[option.label]"
167
+ clickable
168
+ @click="toggle(index)"
169
+ >
170
+ <template #right-icon>
171
+ <van-checkbox ref="checkboxes" :name="item[option.value]" />
172
+ </template>
173
+ </van-cell>
174
+ </van-cell-group>
175
+ </van-checkbox-group>
176
+ </div>
177
+ </van-popup>
178
+ </div>
179
+ </div>
180
+ </template>
181
+
182
+ <style lang="scss" scoped>
183
+ .dh-field {
184
+ padding: 0;
185
+ background:#fff;
186
+ .dh-cell.van-cell {
187
+ padding: 10px 0;
188
+ }
189
+ .dh-cell.van-cell--required::before {
190
+ left: -8px;
191
+ }
192
+ .van-popup {
193
+ border-radius: 20px 20px 0 0;
194
+ }
195
+ }
196
+ </style>
@@ -0,0 +1,130 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Field as VanField,
4
+ Picker as VanPicker,
5
+ Popup as VanPopup,
6
+ } from 'vant'
7
+ import {watch,computed,ref,reactive} from 'vue'
8
+ const emits = defineEmits(['confirm','change','cancel','input'])
9
+ const props = defineProps({
10
+ columns: {
11
+ type: Array,
12
+ default: function () {
13
+ return []
14
+ }
15
+ },
16
+ selectValue: {
17
+ type: [String, Number],
18
+ default: ''
19
+ },
20
+ option: {
21
+ type: Object,
22
+ default: function () {
23
+ return { label: 'label', value: 'value' }
24
+ }
25
+ },
26
+ isSearch: {
27
+ type: Boolean,
28
+ default: false
29
+ },
30
+ offOption: { // 关闭option配置key-value;当数据是非集合的数组的时候,开启
31
+ type: Boolean,
32
+ default: false
33
+ }
34
+ })
35
+ let {columns,selectValue,option,isSearch,offOption} = props
36
+ let show = ref(false)
37
+ let searchVal = ref('')
38
+ let resultValue = ref('')
39
+ let columnsData = reactive([])
40
+
41
+
42
+ let resultLabel = computed({
43
+ get () {
44
+ const res = columns.filter(item => {
45
+ const data = offOption ? item : item[option.value]
46
+ return data === resultValue.value
47
+ })
48
+ let label = ''
49
+ if (res.length) {
50
+ label = offOption ? res[0] : res[0][option.label]
51
+ }
52
+
53
+ return label
54
+ },
55
+ set () {
56
+
57
+ }
58
+ })
59
+ function search (val) {
60
+ if (val) {
61
+ columnsData = columnsData.filter(item => {
62
+ const data = offOption ? item : item[option.label]
63
+
64
+ return data.indexOf(val) > -1
65
+ })
66
+ } else {
67
+ columnsData = JSON.parse(JSON.stringify(columns))
68
+ }
69
+ }
70
+ function onConfirm (value, index) {
71
+ const data = offOption ? value : value[option.value]
72
+
73
+ resultValue.value = data
74
+ show.value = !show.value
75
+ emits('confirm', value, index, resultValue.value)
76
+ }
77
+ function change (val, index) {
78
+ emits('change', val, index, resultValue.value)
79
+ }
80
+ function cancel (val, index) {
81
+ show.value = !show.value
82
+ emits('cancel', val, index, resultValue.value)
83
+ }
84
+ function showPopu (disabled) {
85
+ columnsData = JSON.parse(JSON.stringify(columns))
86
+ resultValue.value = selectValue + ''
87
+ if (disabled !== undefined && disabled !== false) {
88
+ return false
89
+ } else {
90
+ show.value = !show.value
91
+ }
92
+ }
93
+
94
+ watch(()=> selectValue,(newVal, oldVal)=>{
95
+ resultValue.value = newVal + ''
96
+ })
97
+ watch(()=> resultValue, (newVal, oldVal)=>{
98
+ searchVal.value = ''
99
+ columnsData = JSON.parse(JSON.stringify(columns))
100
+ emits('input', newVal)
101
+ })
102
+ </script>
103
+
104
+ <template>
105
+ <van-field
106
+ v-model="resultLabel"
107
+ v-bind="$attrs"
108
+ readonly
109
+ :is-link="true"
110
+ error-message-align='right'
111
+ @click="showPopu($attrs.disabled)"
112
+ />
113
+ <van-popup v-model="show" position="bottom">
114
+ <van-field v-if="isSearch" v-model="searchVal" input-align="left" placeholder="搜索" @input="search"/>
115
+ <van-picker
116
+ v-bind="$attrs"
117
+ :columns="columnsData"
118
+ show-toolbar
119
+ @cancel="cancel"
120
+ @confirm="onConfirm"
121
+ @change="change"
122
+ :value-key="option.label"
123
+ />
124
+ </van-popup>
125
+ </template>
126
+
127
+
128
+ <style lang="scss" scoped>
129
+
130
+ </style>
@@ -0,0 +1,85 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { getDictItemByValue } from '@af-mobile-client-vue3/utils/dictUtil'
4
+
5
+ const { dictName, dictValue } = withDefaults(defineProps<{
6
+ dictName?: string
7
+ dictValue: any
8
+ isColor?: boolean
9
+ }>(), {
10
+ dictName: '',
11
+ isColor: true,
12
+ })
13
+
14
+ // 字面值
15
+ const label = ref('')
16
+
17
+ // 样式类名
18
+ const className = ref('')
19
+
20
+ initComponent()
21
+
22
+ // 组件初始化
23
+ function initComponent() {
24
+ if (!dictValue) {
25
+ label.value = '-'
26
+ return
27
+ }
28
+ if (!dictName || dictName === '') {
29
+ label.value = dictValue.toString()
30
+ return
31
+ }
32
+ getDictItemByValue(dictName, dictValue, (result) => {
33
+ if (result == null) {
34
+ label.value = dictValue.toString()
35
+ }
36
+ else {
37
+ label.value = result.label
38
+ className.value = `badge-${result.status}`
39
+ }
40
+ })
41
+ }
42
+ </script>
43
+
44
+ <template>
45
+ <span :class="className !== '' ? className : undefined">{{ label }}</span>
46
+ </template>
47
+
48
+ <style scoped lang="less">
49
+ .badge-blue {
50
+ color: #1890ff;
51
+ }
52
+ .badge-green {
53
+ color: #52c41a;
54
+ }
55
+ .badge-orange {
56
+ color: #fa8c16;
57
+ }
58
+ .badge-cyan {
59
+ color: #13c2c2;
60
+ }
61
+ .badge-yellow {
62
+ color: #fadb14;
63
+ }
64
+ .badge-red {
65
+ color: #f5222d;
66
+ }
67
+ .badge-pink {
68
+ color: #eb2f96;
69
+ }
70
+ .badge-purple {
71
+ color: #722ed1;
72
+ }
73
+ .badge-geekblue {
74
+ color: #2f54eb;
75
+ }
76
+ .badge-volcano {
77
+ color: #fa541c;
78
+ }
79
+ .badge-gold {
80
+ color: #faad14;
81
+ }
82
+ .badge-lime {
83
+ color: #a0d911;
84
+ }
85
+ </style>