wui-components-v2 1.1.55 → 1.1.57
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/components/custom-date-picker/custom-date-picker.vue +4 -6
- package/components/form-control/form-control.vue +114 -4
- package/components/scan-input/scan-input.vue +180 -86
- package/components/user-choose/user-choose.vue +4 -5
- package/components/wui-select-list/wui-select-list.vue +14 -1
- package/components/wui-user/wui-user.vue +1 -1
- package/package.json +1 -1
- package/type.ts +1 -0
- package/utils/control-type-supportor.ts +4 -0
|
@@ -26,9 +26,6 @@ interface SelectOption {
|
|
|
26
26
|
value: string | number
|
|
27
27
|
[key: string]: any
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
console.log(props.type, 'props.type')
|
|
31
|
-
|
|
32
29
|
const open = ref(false)
|
|
33
30
|
const currentValue = ref<any>(props.modelValue)
|
|
34
31
|
function clear() {
|
|
@@ -38,7 +35,6 @@ function clear() {
|
|
|
38
35
|
watch(
|
|
39
36
|
() => props.modelValue,
|
|
40
37
|
(val) => {
|
|
41
|
-
console.log(val, 'val1111')
|
|
42
38
|
currentValue.value = val
|
|
43
39
|
},
|
|
44
40
|
)
|
|
@@ -55,8 +51,10 @@ const labelText = computed(() => {
|
|
|
55
51
|
} = {
|
|
56
52
|
date: 'YYYY-MM-DD',
|
|
57
53
|
datetime: 'YYYY-MM-DD HH:mm',
|
|
54
|
+
year:'YYYY'
|
|
58
55
|
}
|
|
59
|
-
|
|
56
|
+
|
|
57
|
+
if (['date', 'datetime', 'year'].includes(props.type)) {
|
|
60
58
|
return currentValue.value ? dayjs(currentValue.value).format(formatMap[props.type] as string) : ''
|
|
61
59
|
}
|
|
62
60
|
else {
|
|
@@ -79,7 +77,7 @@ const labelText = computed(() => {
|
|
|
79
77
|
</div>
|
|
80
78
|
|
|
81
79
|
<wd-calendar
|
|
82
|
-
v-if="['date', 'datetime'].includes(type)"
|
|
80
|
+
v-if="['date', 'datetime',].includes(type)"
|
|
83
81
|
v-model="currentValue"
|
|
84
82
|
v-model:visible="open"
|
|
85
83
|
label-key="label"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed, onBeforeMount, ref, toRaw } from 'vue'
|
|
2
|
+
import { computed, onBeforeMount, ref, toRaw, watch } from 'vue'
|
|
3
3
|
import type { FormSchema, FormSchemaIssue } from '@wot-ui/ui/components/wd-form/types'
|
|
4
4
|
import dayjs from 'dayjs/esm/index'
|
|
5
5
|
import type { Enums, Fields, Groups } from '../../type'
|
|
@@ -11,6 +11,7 @@ import CustomDatePicker from '../custom-date-picker/custom-date-picker.vue'
|
|
|
11
11
|
import addAddressPage from '../add-address-page/add-address-page.vue'
|
|
12
12
|
import userChoose from '../user-choose/user-choose.vue'
|
|
13
13
|
import { generateHighResolutionID } from '../../utils/index'
|
|
14
|
+
import scanInput from '../scan-input/scan-input.vue'
|
|
14
15
|
// import { enums } from '../../api/page'
|
|
15
16
|
defineOptions({
|
|
16
17
|
name: 'FormControl',
|
|
@@ -50,6 +51,19 @@ const { filteredFields: fields } = useCompanyFieldFilter(
|
|
|
50
51
|
props.companyFilter, // 是否自适应显示快递公司填充框
|
|
51
52
|
props.companyFieldSourceId, // 快递公司对应字段
|
|
52
53
|
)
|
|
54
|
+
const giveDefaultValue =(value:any)=>{
|
|
55
|
+
const {extInfo={}} = uni.getStorageSync('userInfo')||{}
|
|
56
|
+
const rawDefaultValue=extInfo.fieldMap||{};
|
|
57
|
+
const defaultValue: Record<string, any> = {}
|
|
58
|
+
for (const key in rawDefaultValue) {
|
|
59
|
+
if (Object.prototype.hasOwnProperty.call(rawDefaultValue, key)) {
|
|
60
|
+
const val = rawDefaultValue[key]
|
|
61
|
+
defaultValue[key] = typeof val === 'string' && val.includes('@R@') ? val.split('@R@')[0] : val
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return defaultValue[value]||{}
|
|
65
|
+
}
|
|
66
|
+
giveDefaultValue({})
|
|
53
67
|
// 初始化表单数据
|
|
54
68
|
function initFormData() {
|
|
55
69
|
const models: { [key: string]: any } = {}
|
|
@@ -78,17 +92,93 @@ function initFormData() {
|
|
|
78
92
|
return models[item.sourceId] = (props.entity && dayjs(props.entity[item.sourceId]).valueOf()) || (item.transDefaultValue && dayjs(item.transDefaultValue).valueOf()) || null
|
|
79
93
|
}
|
|
80
94
|
|
|
95
|
+
// 评分
|
|
96
|
+
if (ControlTypeSupportor.getControlType(item, props.entity && props.entity[item.sourceId]) === 'progress') {
|
|
97
|
+
const val = props.entity?.[item.sourceId]
|
|
98
|
+
return models[item.sourceId] = (val !== undefined && val !== null && val !== '') ? Number(val) : 0
|
|
99
|
+
}
|
|
100
|
+
|
|
81
101
|
return models[item.sourceId] = (props.entity && props.entity[item.sourceId]) || item.transDefaultValue || ''
|
|
82
102
|
})
|
|
83
103
|
|
|
84
|
-
|
|
104
|
+
// 遍历 models,处理 $$user. 前缀
|
|
105
|
+
const processedModels: Record<string, any> = {}
|
|
106
|
+
for (const key in models) {
|
|
107
|
+
if (Object.prototype.hasOwnProperty.call(models, key)) {
|
|
108
|
+
const val = models[key]
|
|
109
|
+
if (typeof val === 'string' && val.includes('$$user.')) {
|
|
110
|
+
processedModels[key] = giveDefaultValue(val.split('$$user.')[1])
|
|
111
|
+
} else {
|
|
112
|
+
processedModels[key] = val
|
|
113
|
+
}
|
|
114
|
+
}
|
|
85
115
|
}
|
|
86
116
|
|
|
117
|
+
model.value = {
|
|
118
|
+
...processedModels,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
//-s
|
|
123
|
+
const shouldMonitor=computed(()=>{
|
|
124
|
+
const newList=fields.value.filter((item:any)=>item.relValueField||item.relValueField3).map((innerItem:any)=>({
|
|
125
|
+
id: innerItem.sourceId,
|
|
126
|
+
title: innerItem.title,
|
|
127
|
+
relValueField: innerItem.relValueField.id,
|
|
128
|
+
relValueField3: innerItem.relValueField3.id,
|
|
129
|
+
}))
|
|
130
|
+
console.log('shouldMonitor:', newList)
|
|
131
|
+
return newList
|
|
132
|
+
})
|
|
133
|
+
//-e
|
|
134
|
+
|
|
135
|
+
// 存储变化后的数据,{ [fieldKey]: newValue, ... }
|
|
136
|
+
const changedData = ref<Record<string, any>>({})
|
|
137
|
+
const initialModel = ref<Record<string, any>>({})
|
|
138
|
+
|
|
139
|
+
// 监听 shouldMonitor 中 relValueField / relValueField3 对应的 model 字段
|
|
140
|
+
watch(
|
|
141
|
+
() => {
|
|
142
|
+
const snapshot: Record<string, any> = {}
|
|
143
|
+
shouldMonitor.value?.forEach((item: any) => {
|
|
144
|
+
if (item.relValueField)
|
|
145
|
+
snapshot.relValueField = model.value[item.relValueField]
|
|
146
|
+
if (item.relValueField3)
|
|
147
|
+
snapshot.relValueField3 = model.value[item.relValueField3]
|
|
148
|
+
})
|
|
149
|
+
return snapshot
|
|
150
|
+
},
|
|
151
|
+
(newVal) => {
|
|
152
|
+
// 只存储与初始值不同的字段(表单变化的值)
|
|
153
|
+
const changed: Record<string, any> = {}
|
|
154
|
+
shouldMonitor.value?.forEach((item: any) => {
|
|
155
|
+
if (item.relValueField && newVal.relValueField !== initialModel.value[item.relValueField]) {
|
|
156
|
+
changed.relValueField = newVal.relValueField
|
|
157
|
+
}
|
|
158
|
+
if (item.relValueField3 && newVal.relValueField3 !== initialModel.value[item.relValueField3]) {
|
|
159
|
+
changed.relValueField3 = newVal.relValueField3
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
changedData.value = changed
|
|
163
|
+
|
|
164
|
+
// 当所有被监听的字段都有值时,存入缓存供 select-list 页面读取
|
|
165
|
+
const values = Object.values(newVal)
|
|
166
|
+
const allExist = values.length > 0 && values.every(v => v !== null && v !== undefined && v !== '')
|
|
167
|
+
if (allExist) {
|
|
168
|
+
uni.setStorageSync('paramsData', {
|
|
169
|
+
changedData: changedData.value,
|
|
170
|
+
rawData: shouldMonitor.value,
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
)
|
|
175
|
+
|
|
87
176
|
onBeforeMount(() => {
|
|
88
177
|
action.value = uni.getStorageSync('BASE_URL')
|
|
89
178
|
hydrocarbonProgramToken.value = uni.getStorageSync('HYDROCARBON_PROGRAM_TOKEN')
|
|
90
179
|
token.value = uni.getStorageSync('TOKEN')
|
|
91
180
|
initFormData()
|
|
181
|
+
initialModel.value = JSON.parse(JSON.stringify(model.value))
|
|
92
182
|
})
|
|
93
183
|
function formatSelectColumns(columns: any) {
|
|
94
184
|
return columns.map((item: any) => {
|
|
@@ -124,7 +214,6 @@ const schema = computed((): FormSchema => {
|
|
|
124
214
|
|
|
125
215
|
// 监听文件上传成功
|
|
126
216
|
function handleFileChange(e: any, item: Fields) {
|
|
127
|
-
console.log(e, item,'2222222222222')
|
|
128
217
|
model.value[item.sourceId] = e.fileList
|
|
129
218
|
}
|
|
130
219
|
|
|
@@ -155,7 +244,6 @@ function submit() {
|
|
|
155
244
|
})
|
|
156
245
|
}
|
|
157
246
|
}
|
|
158
|
-
console.log('submit', model.value)
|
|
159
247
|
resolve(data)
|
|
160
248
|
}
|
|
161
249
|
else {
|
|
@@ -169,10 +257,16 @@ function submit() {
|
|
|
169
257
|
})
|
|
170
258
|
}
|
|
171
259
|
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
172
264
|
// 暴露方法/变量给父组件
|
|
173
265
|
defineExpose({
|
|
174
266
|
submit,
|
|
175
267
|
})
|
|
268
|
+
|
|
269
|
+
|
|
176
270
|
</script>
|
|
177
271
|
|
|
178
272
|
<template>
|
|
@@ -393,6 +487,22 @@ defineExpose({
|
|
|
393
487
|
<wd-input-number v-model="model[item.sourceId]" :disabled="item.disabled || item.rowEditType === 'readonly'" :min="Number(item.min || 0)" :max="Number(item.max || Infinity)" />
|
|
394
488
|
</view>
|
|
395
489
|
</wd-form-item>
|
|
490
|
+
<wd-form-item
|
|
491
|
+
v-else-if="ControlTypeSupportor.getControlType(item, props.entity && props.entity[item.sourceId]) === 'QRCode'"
|
|
492
|
+
:class="{ 'no-border-top': fields.indexOf(item) === 0 }"
|
|
493
|
+
:prop="item.sourceId"
|
|
494
|
+
:title="item.title"
|
|
495
|
+
>
|
|
496
|
+
<scanInput v-model="model[item.sourceId]" :disabled="item.disabled || item.rowEditType === 'readonly'" />
|
|
497
|
+
</wd-form-item>
|
|
498
|
+
<wd-form-item
|
|
499
|
+
v-else-if="ControlTypeSupportor.getControlType(item, props.entity && props.entity[item.sourceId]) === 'progress'"
|
|
500
|
+
:class="{ 'no-border-top': fields.indexOf(item) === 0 }"
|
|
501
|
+
:prop="item.sourceId"
|
|
502
|
+
:title="item.title"
|
|
503
|
+
>
|
|
504
|
+
<wd-rate v-model="model[item.sourceId]" :disabled="item.disabled || item.rowEditType === 'readonly'" />
|
|
505
|
+
</wd-form-item>
|
|
396
506
|
<!-- done -->
|
|
397
507
|
<wd-form-item
|
|
398
508
|
v-else-if="ControlTypeSupportor.getControlType(item, props.entity && props.entity[item.sourceId]) === 'yes-no-switch'"
|
|
@@ -1,34 +1,72 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
|
2
|
+
import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
|
3
3
|
|
|
4
4
|
defineOptions({
|
|
5
5
|
name: 'ScanInput',
|
|
6
6
|
})
|
|
7
7
|
|
|
8
|
+
// ---- 类型 ----
|
|
9
|
+
type Html5QrcodeInstance = {
|
|
10
|
+
start: (config: object, opts: object, onSuccess: (text: string) => void, onFailure: () => void) => Promise<void>
|
|
11
|
+
stop: () => Promise<void>
|
|
12
|
+
clear: () => Promise<void>
|
|
13
|
+
isScanning?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ---- Props ----
|
|
8
17
|
const props = withDefaults(defineProps<{
|
|
18
|
+
/** v-model 绑定值 */
|
|
19
|
+
modelValue?: string
|
|
20
|
+
/** 自动启动扫码 */
|
|
9
21
|
autoStart?: boolean
|
|
22
|
+
/** 占位文本 */
|
|
10
23
|
placeholder?: string
|
|
24
|
+
/** 是否禁用 */
|
|
25
|
+
disabled?: boolean
|
|
26
|
+
/** 扫码成功后自动关闭弹窗 (H5) */
|
|
27
|
+
autoCloseOnSuccess?: boolean
|
|
28
|
+
/** 扫码成功后是否展示 toast 提示 */
|
|
29
|
+
showSuccessToast?: boolean
|
|
11
30
|
}>(), {
|
|
31
|
+
modelValue: '',
|
|
12
32
|
autoStart: false,
|
|
13
33
|
placeholder: '请输入或扫描二维码/条形码',
|
|
34
|
+
disabled: false,
|
|
35
|
+
autoCloseOnSuccess: true,
|
|
36
|
+
showSuccessToast: true,
|
|
14
37
|
})
|
|
15
38
|
|
|
16
39
|
const emit = defineEmits<{
|
|
40
|
+
(e: 'update:modelValue', val: string): void
|
|
17
41
|
(e: 'scan', data: string): void
|
|
18
42
|
(e: 'input', data: string): void
|
|
19
43
|
(e: 'error', error: string): void
|
|
20
44
|
}>()
|
|
21
45
|
|
|
22
|
-
|
|
46
|
+
// ---- 状态 ----
|
|
47
|
+
const inputValue = ref(props.modelValue)
|
|
23
48
|
const isScanning = ref(false)
|
|
24
49
|
const showScanModal = ref(false)
|
|
25
50
|
const isLoading = ref(false)
|
|
26
|
-
let html5QrcodeScanner:
|
|
51
|
+
let html5QrcodeScanner: Html5QrcodeInstance | null = null
|
|
52
|
+
let scanLocked = false
|
|
53
|
+
|
|
54
|
+
// v-model 同步
|
|
55
|
+
watch(() => props.modelValue, (val) => { inputValue.value = val })
|
|
56
|
+
watch(inputValue, (val) => { emit('update:modelValue', val); emit('input', val) })
|
|
57
|
+
|
|
58
|
+
/** 外部可调用的方法 */
|
|
59
|
+
function clear() { inputValue.value = '' }
|
|
60
|
+
function focus() { /* wd-input 聚焦可通过 ref 实现 */ }
|
|
61
|
+
|
|
62
|
+
defineExpose({ clear, focus, startScan: handleStartScan, stopScan })
|
|
27
63
|
|
|
64
|
+
// ======================== H5 平台扫码 ========================
|
|
28
65
|
// #ifdef H5
|
|
29
66
|
import { Html5Qrcode } from 'html5-qrcode'
|
|
30
67
|
|
|
31
|
-
|
|
68
|
+
/** 环境检测 */
|
|
69
|
+
function checkCameraEnvironment(): { ok: boolean; reason?: string } {
|
|
32
70
|
const hostname = window.location.hostname
|
|
33
71
|
if (hostname === 'localhost' || hostname === '127.0.0.1') return { ok: true }
|
|
34
72
|
if (window.location.protocol !== 'https:') {
|
|
@@ -38,7 +76,7 @@ function checkCameraEnvironment(): { ok: boolean, reason?: string } {
|
|
|
38
76
|
}
|
|
39
77
|
|
|
40
78
|
async function startScan() {
|
|
41
|
-
if (isScanning.value) return
|
|
79
|
+
if (isScanning.value || scanLocked) return
|
|
42
80
|
const envCheck = checkCameraEnvironment()
|
|
43
81
|
if (!envCheck.ok) {
|
|
44
82
|
uni.showToast({ title: envCheck.reason || '环境不支持', icon: 'none' })
|
|
@@ -48,40 +86,48 @@ async function startScan() {
|
|
|
48
86
|
isLoading.value = true
|
|
49
87
|
isScanning.value = true
|
|
50
88
|
showScanModal.value = true
|
|
89
|
+
scanLocked = false
|
|
51
90
|
|
|
52
91
|
try {
|
|
53
92
|
await nextTick()
|
|
54
|
-
// 减少延时,库已打包进 bundle 无需等待加载
|
|
55
93
|
await new Promise(r => setTimeout(r, 200))
|
|
56
94
|
|
|
57
95
|
const container = document.getElementById('h5-scan-reader')
|
|
58
|
-
if (!container) throw new Error('
|
|
96
|
+
if (!container) throw new Error('扫码容器未找到')
|
|
59
97
|
|
|
60
|
-
html5QrcodeScanner = new Html5Qrcode('h5-scan-reader')
|
|
98
|
+
html5QrcodeScanner = new Html5Qrcode('h5-scan-reader') as unknown as Html5QrcodeInstance
|
|
61
99
|
|
|
62
100
|
await html5QrcodeScanner.start(
|
|
63
101
|
{ facingMode: 'environment' },
|
|
64
|
-
{
|
|
65
|
-
fps: 5,
|
|
66
|
-
// 不设置 qrbox,全屏显示摄像头画面
|
|
67
|
-
},
|
|
102
|
+
{ fps: 5 },
|
|
68
103
|
(decodedText: string) => {
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
|
|
104
|
+
if (scanLocked || !isScanning.value) return
|
|
105
|
+
scanLocked = true // 加锁防止重复触发
|
|
106
|
+
|
|
107
|
+
console.log('[ScanInput] 扫码成功:', decodedText)
|
|
72
108
|
inputValue.value = decodedText
|
|
73
109
|
emit('scan', decodedText)
|
|
74
|
-
|
|
75
|
-
stopScan()
|
|
76
|
-
uni.showToast({ title: '扫描成功', icon: 'success' })
|
|
110
|
+
if (props.showSuccessToast) uni.showToast({ title: '扫描成功', icon: 'success' })
|
|
111
|
+
if (props.autoCloseOnSuccess) stopScan()
|
|
77
112
|
},
|
|
78
|
-
() => {}, //
|
|
113
|
+
() => {}, // 忽略中间帧未识别错误
|
|
79
114
|
)
|
|
115
|
+
|
|
116
|
+
// JS 强制全屏 video(html5-qrcode 库会覆盖 CSS,需运行时修正)
|
|
117
|
+
const videoEl = document.querySelector('#h5-scan-reader video') as HTMLVideoElement | null
|
|
118
|
+
if (videoEl) {
|
|
119
|
+
Object.assign(videoEl.style, {
|
|
120
|
+
width: '100%',
|
|
121
|
+
height: '100vh',
|
|
122
|
+
objectFit: 'cover',
|
|
123
|
+
objectPosition: 'center',
|
|
124
|
+
})
|
|
125
|
+
}
|
|
80
126
|
}
|
|
81
|
-
catch (error:
|
|
82
|
-
console.error('
|
|
127
|
+
catch (error: unknown) {
|
|
128
|
+
console.error('[ScanInput] 启动失败:', error)
|
|
129
|
+
const errMsg = error instanceof Error ? error.message : String(error)
|
|
83
130
|
let msg = '摄像头访问失败'
|
|
84
|
-
const errMsg = error.message || ''
|
|
85
131
|
if (/Permission|NotAllowed/i.test(errMsg)) msg = '请允许浏览器使用摄像头'
|
|
86
132
|
else if (/NotFound|not found/i.test(errMsg)) msg = '未检测到摄像头设备'
|
|
87
133
|
else if (/secure origin|insecure/i.test(errMsg)) msg = '需要HTTPS环境才能使用摄像头'
|
|
@@ -99,24 +145,25 @@ async function startScan() {
|
|
|
99
145
|
async function stopScan() {
|
|
100
146
|
isScanning.value = false
|
|
101
147
|
showScanModal.value = false
|
|
148
|
+
scanLocked = false
|
|
102
149
|
if (html5QrcodeScanner) {
|
|
103
150
|
try {
|
|
104
151
|
if (html5QrcodeScanner.isScanning) await html5QrcodeScanner.stop()
|
|
105
|
-
html5QrcodeScanner.clear()
|
|
152
|
+
await html5QrcodeScanner.clear()
|
|
106
153
|
}
|
|
107
|
-
catch { /* ignore */ }
|
|
154
|
+
catch { /* ignore cleanup error */ }
|
|
108
155
|
html5QrcodeScanner = null
|
|
109
156
|
}
|
|
110
157
|
}
|
|
111
158
|
// #endif
|
|
112
159
|
|
|
160
|
+
// ======================== 非 H5 平台扫码 ========================
|
|
113
161
|
function startUniScanCode() {
|
|
114
162
|
// #ifndef H5
|
|
115
163
|
uni.scanCode({
|
|
116
|
-
success(res:
|
|
164
|
+
success(res: UniApp.ScanCodeSuccessRes) {
|
|
117
165
|
inputValue.value = res.result
|
|
118
166
|
emit('scan', res.result)
|
|
119
|
-
emit('input', res.result)
|
|
120
167
|
uni.showToast({ title: '扫描成功', icon: 'success' })
|
|
121
168
|
},
|
|
122
169
|
fail() { emit('error', '扫描已取消') },
|
|
@@ -124,7 +171,9 @@ function startUniScanCode() {
|
|
|
124
171
|
// #endif
|
|
125
172
|
}
|
|
126
173
|
|
|
174
|
+
// ======================== 公共方法 ========================
|
|
127
175
|
function handleStartScan() {
|
|
176
|
+
if (props.disabled) return
|
|
128
177
|
// #ifdef H5
|
|
129
178
|
isScanning.value ? stopScan() : startScan()
|
|
130
179
|
// #endif
|
|
@@ -133,17 +182,30 @@ function handleStartScan() {
|
|
|
133
182
|
// #endif
|
|
134
183
|
}
|
|
135
184
|
|
|
185
|
+
/** 点击遮罩层关闭 (仅 H5) */
|
|
186
|
+
function handleModalMaskClick() {
|
|
187
|
+
// #ifdef H5
|
|
188
|
+
stopScan()
|
|
189
|
+
// #endif
|
|
190
|
+
}
|
|
191
|
+
|
|
136
192
|
function handleConfirm() {
|
|
137
|
-
|
|
193
|
+
const val = inputValue.value.trim()
|
|
194
|
+
if (val) emit('scan', val)
|
|
138
195
|
}
|
|
139
196
|
|
|
140
|
-
|
|
141
|
-
|
|
197
|
+
// ---- 生命周期 ----
|
|
198
|
+
onMounted(() => { if (props.autoStart && !props.disabled) handleStartScan() })
|
|
199
|
+
onUnmounted(() => {
|
|
200
|
+
// #ifdef H5
|
|
201
|
+
stopScan()
|
|
202
|
+
// #endif
|
|
203
|
+
})
|
|
142
204
|
</script>
|
|
143
205
|
|
|
144
206
|
<template>
|
|
145
207
|
<view class="si-wrap">
|
|
146
|
-
<!--
|
|
208
|
+
<!-- 输入框 -->
|
|
147
209
|
<wd-input
|
|
148
210
|
v-model="inputValue"
|
|
149
211
|
class="!pr-3"
|
|
@@ -152,37 +214,43 @@ onUnmounted(() => { /* #ifdef H5 */ stopScan() /* #endif */ })
|
|
|
152
214
|
confirm-type="search"
|
|
153
215
|
clearable
|
|
154
216
|
no-border
|
|
217
|
+
:disabled="disabled"
|
|
155
218
|
custom-class="si-wd-input"
|
|
156
219
|
@confirm="handleConfirm"
|
|
157
|
-
@change="(val: string) => emit('input', val)"
|
|
158
220
|
suffix-icon="scan"
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
</wd-input>
|
|
221
|
+
@clicksuffixicon="handleStartScan"
|
|
222
|
+
/>
|
|
162
223
|
|
|
163
224
|
<!-- #ifdef H5 -->
|
|
164
|
-
<!--
|
|
165
|
-
<view
|
|
225
|
+
<!-- 全屏扫码弹窗 -->
|
|
226
|
+
<view
|
|
227
|
+
v-if="showScanModal"
|
|
228
|
+
class="si-modal"
|
|
229
|
+
catchtouchmove
|
|
230
|
+
@click="handleModalMaskClick"
|
|
231
|
+
>
|
|
166
232
|
<!-- 摄像头区域 -->
|
|
167
|
-
<view class="si-camera-area">
|
|
233
|
+
<view class="si-camera-area" @click.stop>
|
|
168
234
|
<!-- 加载中 -->
|
|
169
235
|
<view v-if="isLoading" class="si-loading-mask">
|
|
170
236
|
<view class="si-loading-box">
|
|
171
237
|
<text class="si-loading-text">正在启动摄像头...</text>
|
|
172
238
|
</view>
|
|
173
239
|
</view>
|
|
174
|
-
<!-- 渲染容器 -->
|
|
175
|
-
<div id="h5-scan-reader" class="si-reader" />
|
|
176
|
-
<!-- 扫描框装饰 -->
|
|
177
|
-
<view v-show="isScanning && !isLoading" class="si-scan-frame">
|
|
178
|
-
<view class="si-scan-line" />
|
|
179
|
-
</view>
|
|
180
|
-
</view>
|
|
181
240
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
241
|
+
<!-- 渲染容器 + 扫描线 -->
|
|
242
|
+
<view class="si-reader-wrap overflow-hidden">
|
|
243
|
+
<div id="h5-scan-reader" class="si-reader" />
|
|
244
|
+
<view v-show="isScanning && !isLoading" class="si-scan-frame">
|
|
245
|
+
<view class="si-scan-line" />
|
|
246
|
+
</view>
|
|
247
|
+
</view>
|
|
185
248
|
</view>
|
|
249
|
+
<!-- 底部提示栏 -->
|
|
250
|
+
<view class="si-modal-footer" @click.stop>
|
|
251
|
+
<text class="si-tip">将二维码 / 条形码放入框内即可自动识别</text>
|
|
252
|
+
<text class="si-close-text" @click="stopScan">关闭</text>
|
|
253
|
+
</view>s
|
|
186
254
|
</view>
|
|
187
255
|
<!-- #endif -->
|
|
188
256
|
</view>
|
|
@@ -191,17 +259,18 @@ onUnmounted(() => { /* #ifdef H5 */ stopScan() /* #endif */ })
|
|
|
191
259
|
<!-- 全局样式:html5-qrcode 内部元素 -->
|
|
192
260
|
<style lang="scss">
|
|
193
261
|
#h5-scan-reader {
|
|
194
|
-
width: 100
|
|
195
|
-
height:
|
|
196
|
-
border-radius: inherit;
|
|
197
|
-
display: flex;
|
|
262
|
+
width: 100% !important;
|
|
263
|
+
height: 100vh !important;
|
|
198
264
|
|
|
199
265
|
video {
|
|
200
|
-
|
|
201
|
-
|
|
266
|
+
width: 100% !important;
|
|
267
|
+
height: 100vh !important;
|
|
268
|
+
object-fit: cover !important;
|
|
269
|
+
object-position: center !important;
|
|
202
270
|
}
|
|
203
271
|
|
|
204
|
-
#qr-shaded-region
|
|
272
|
+
#qr-shaded-region,
|
|
273
|
+
#html5-qrcode-anchor-scan-region {
|
|
205
274
|
border-radius: inherit;
|
|
206
275
|
}
|
|
207
276
|
}
|
|
@@ -210,76 +279,85 @@ onUnmounted(() => { /* #ifdef H5 */ stopScan() /* #endif */ })
|
|
|
210
279
|
<style scoped lang="scss">
|
|
211
280
|
.si-wrap {
|
|
212
281
|
width: 100%;
|
|
213
|
-
padding: 16rpx;
|
|
214
282
|
box-sizing: border-box;
|
|
215
283
|
|
|
216
|
-
// ----
|
|
284
|
+
// ---- 输入框容器 ----
|
|
217
285
|
:deep(.si-wd-input) {
|
|
218
|
-
background:
|
|
286
|
+
background: transparent;
|
|
219
287
|
border-radius: 10rpx;
|
|
220
|
-
border:
|
|
288
|
+
border: none;
|
|
221
289
|
padding-right: 4rpx;
|
|
290
|
+
|
|
291
|
+
&.is-disabled {
|
|
292
|
+
opacity: 0.6;
|
|
293
|
+
}
|
|
222
294
|
}
|
|
223
295
|
|
|
224
|
-
// ----
|
|
296
|
+
// ---- 全屏扫码弹窗 ----
|
|
225
297
|
.si-modal {
|
|
226
298
|
position: fixed;
|
|
227
|
-
|
|
299
|
+
inset: 0;
|
|
228
300
|
z-index: 999;
|
|
229
301
|
background: #000;
|
|
230
|
-
display: flex;
|
|
231
|
-
flex-direction: column;
|
|
232
302
|
}
|
|
233
303
|
|
|
234
304
|
.si-camera-area {
|
|
235
|
-
flex: 1;
|
|
236
305
|
width: 100%;
|
|
306
|
+
height: 100%;
|
|
237
307
|
position: relative;
|
|
238
308
|
overflow: hidden;
|
|
239
309
|
|
|
240
|
-
|
|
310
|
+
// 摄像头包装容器(全屏)
|
|
311
|
+
.si-reader-wrap {
|
|
241
312
|
position: absolute;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
313
|
+
inset: 0;
|
|
314
|
+
overflow: hidden;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 摄像头渲染容器
|
|
318
|
+
.si-reader {
|
|
245
319
|
width: 100%;
|
|
246
|
-
height:
|
|
320
|
+
height: 100%;
|
|
247
321
|
}
|
|
248
322
|
|
|
249
|
-
//
|
|
323
|
+
// 扫描动画线(全屏时收缩在视频可视区域中间)
|
|
250
324
|
.si-scan-frame {
|
|
251
325
|
position: absolute;
|
|
252
|
-
top:
|
|
326
|
+
top: 0;
|
|
327
|
+
bottom: 0;
|
|
253
328
|
left: 0;
|
|
254
|
-
|
|
255
|
-
width: 100%;
|
|
256
|
-
height: 50vh;
|
|
329
|
+
right:0;
|
|
257
330
|
pointer-events: none;
|
|
258
331
|
z-index: 5;
|
|
259
332
|
overflow: hidden;
|
|
333
|
+
width:100vw;
|
|
334
|
+
height: 100vh;
|
|
260
335
|
|
|
261
336
|
.si-scan-line {
|
|
262
337
|
position: absolute;
|
|
263
|
-
top:
|
|
338
|
+
top: 2%;
|
|
264
339
|
left: 10rpx;
|
|
265
340
|
right: 10rpx;
|
|
266
341
|
height: 6rpx;
|
|
267
|
-
background: #
|
|
342
|
+
background: #07c160;
|
|
268
343
|
border-radius: 3rpx;
|
|
269
|
-
box-shadow: 0 0 20rpx 4rpx rgba(7,193,96,0.7);
|
|
344
|
+
box-shadow: 0 0 20rpx 4rpx rgba(7, 193, 96, 0.7);
|
|
270
345
|
animation: si-sweep 2.5s ease-in-out infinite;
|
|
271
346
|
}
|
|
272
347
|
}
|
|
273
348
|
|
|
349
|
+
// 加载中遮罩
|
|
274
350
|
.si-loading-mask {
|
|
275
351
|
position: absolute;
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
352
|
+
inset: 0;
|
|
353
|
+
display: flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
justify-content: center;
|
|
279
356
|
z-index: 10;
|
|
357
|
+
background: rgba(0, 0, 0, 0.5);
|
|
280
358
|
|
|
281
359
|
.si-loading-box {
|
|
282
|
-
background: rgba(0,0,0,0.75);
|
|
360
|
+
background: rgba(0, 0, 0, 0.75);
|
|
283
361
|
padding: 30rpx 48rpx;
|
|
284
362
|
border-radius: 16rpx;
|
|
285
363
|
|
|
@@ -291,22 +369,38 @@ onUnmounted(() => { /* #ifdef H5 */ stopScan() /* #endif */ })
|
|
|
291
369
|
}
|
|
292
370
|
}
|
|
293
371
|
|
|
372
|
+
// 底部提示栏(绝对定位在弹窗底部)
|
|
294
373
|
.si-modal-footer {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
374
|
+
position: absolute;
|
|
375
|
+
left: 0;
|
|
376
|
+
right: 0;
|
|
377
|
+
bottom: 0;
|
|
378
|
+
z-index: 10;
|
|
379
|
+
padding: 40rpx 32rpx calc(80rpx + env(safe-area-inset-bottom, 0));
|
|
380
|
+
display: flex;
|
|
381
|
+
flex-direction: column;
|
|
382
|
+
align-items: center;
|
|
383
|
+
gap: 24rpx;
|
|
298
384
|
|
|
299
385
|
.si-tip {
|
|
300
|
-
color: rgba(255,255,255,0.65);
|
|
386
|
+
color: rgba(255, 255, 255, 0.65);
|
|
301
387
|
font-size: 26rpx;
|
|
302
388
|
letter-spacing: 2rpx;
|
|
303
389
|
}
|
|
390
|
+
|
|
391
|
+
.si-close-text {
|
|
392
|
+
color: rgba(255, 255, 255, 0.85);
|
|
393
|
+
font-size: 30rpx;
|
|
394
|
+
padding: 12rpx 48rpx;
|
|
395
|
+
border: 2rpx solid rgba(255, 255, 255, 0.3);
|
|
396
|
+
border-radius: 40rpx;
|
|
397
|
+
}
|
|
304
398
|
}
|
|
305
399
|
}
|
|
306
400
|
|
|
307
401
|
@keyframes si-sweep {
|
|
308
|
-
0% { top:
|
|
402
|
+
0% { top:30%; opacity: 1; }
|
|
309
403
|
50% { opacity: 1; }
|
|
310
|
-
100% { top:
|
|
404
|
+
100% { top: 70%; opacity: 0.3; }
|
|
311
405
|
}
|
|
312
406
|
</style>
|
|
@@ -66,7 +66,6 @@ const fetchDataById = async (id: string) => {
|
|
|
66
66
|
if (!id || !props.sourceId) return
|
|
67
67
|
const newId=Array.isArray(id)?id[0]:id
|
|
68
68
|
const requestId = newId?.split('@R@')[0] || props.sourceId
|
|
69
|
-
console.log('fetchDataById', requestId, props.sourceId,id)
|
|
70
69
|
try {
|
|
71
70
|
const config = await pageConfig(props.sourceId)
|
|
72
71
|
const newConfig=config.ltmplConfig
|
|
@@ -96,18 +95,18 @@ const emitIfNeeded = (val: string) => {
|
|
|
96
95
|
emitVal = val + '@R@' + showValue.name
|
|
97
96
|
}
|
|
98
97
|
if (emitVal !== props.modelValue) {
|
|
99
|
-
console.log('emit update:modelValue', emitVal)
|
|
100
98
|
emit('update:modelValue', emitVal)
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
onMounted(() => {
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
const raw = currentValue.value
|
|
104
|
+
if (raw && typeof raw === 'string') {
|
|
105
|
+
const requestId = raw.includes('$$user.') ? raw.split('$$user.')[1] : raw
|
|
106
|
+
fetchDataById(requestId)
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
uni.$on(EVENT_NAME, (id: string) => {
|
|
110
|
-
console.log('user-choose 接收到id:', id)
|
|
111
110
|
currentValue.value = id
|
|
112
111
|
fetchDataById(id)
|
|
113
112
|
})
|
|
@@ -141,6 +141,7 @@ async function getEnums() {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
|
|
144
145
|
// @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用this.$refs.paging.reload()即可
|
|
145
146
|
async function queryList(pageNo: number, pageSize: number) {
|
|
146
147
|
// 此处请求仅为演示,请替换为自己项目中的请求
|
|
@@ -155,7 +156,16 @@ async function queryList(pageNo: number, pageSize: number) {
|
|
|
155
156
|
// paging.value.reload() // 手动触发加载
|
|
156
157
|
// })
|
|
157
158
|
|
|
158
|
-
|
|
159
|
+
const search=uni.getStorageSync('paramsData')?.changedData || {}
|
|
160
|
+
const newParams={
|
|
161
|
+
thirdCriteria:search.relValueField3 || '',
|
|
162
|
+
secondCriteria:search.relValueField?.toString() || '',
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
const params = Object.entries(newParams)
|
|
166
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
167
|
+
.join('&');
|
|
168
|
+
let qre = searchData.value?`${searchData.value+'&'+params}`:params
|
|
159
169
|
if (config.value.defaultCriteriaValue) {
|
|
160
170
|
for (const key in config.value.defaultCriteriaValue) {
|
|
161
171
|
if (Object.prototype.hasOwnProperty.call(config.value.defaultCriteriaValue, key)) {
|
|
@@ -237,6 +247,9 @@ const formatAddress = (value:string) => {
|
|
|
237
247
|
const checkboxType:any=computed(()=>{
|
|
238
248
|
return ['relselect','relselect-extdis'].includes(extControlType.value)?'':'square'
|
|
239
249
|
})
|
|
250
|
+
onUnmounted(() => {
|
|
251
|
+
uni.removeStorageSync('paramsData')
|
|
252
|
+
})
|
|
240
253
|
</script>
|
|
241
254
|
|
|
242
255
|
<template>
|
|
@@ -99,8 +99,8 @@ function setting() {
|
|
|
99
99
|
/> -->
|
|
100
100
|
<!-- <wd-cell title="修改密码" icon="keywords" :is-link="true" :clickable="true" /> -->
|
|
101
101
|
<wd-cell title="系统设置" icon="setting" :is-link="true" :clickable="true" @click="setting" />
|
|
102
|
-
<wd-cell title="退出登录" icon="logout" :is-link="true" :clickable="true" @click="quit" />
|
|
103
102
|
<wd-cell title="清空缓存" :is-link="true" :clickable="true" @click="clearCache" />
|
|
103
|
+
<wd-cell title="退出登录" icon="logout" :is-link="true" :clickable="true" @click="quit" />
|
|
104
104
|
</wd-cell-group>
|
|
105
105
|
<wd-dialog />
|
|
106
106
|
</view>
|
package/package.json
CHANGED
package/type.ts
CHANGED
|
@@ -82,6 +82,9 @@ SupportInputTypes.add('ltree-entity-select-value')
|
|
|
82
82
|
SupportInputTypes.add('ltree-entity-select')
|
|
83
83
|
SupportInputTypes.add('field-history')
|
|
84
84
|
SupportInputTypes.add('relfile')
|
|
85
|
+
SupportInputTypes.add('QRCode')
|
|
86
|
+
SupportInputTypes.add('progress')
|
|
87
|
+
|
|
85
88
|
|
|
86
89
|
const CUSTOM_VIEW_CONTROL_MAP: { [key: string]: string } = {}
|
|
87
90
|
|
|
@@ -139,6 +142,7 @@ ControlTypeSupportor.getControlType = function (fieldConfig: Fields, fieldValue?
|
|
|
139
142
|
if (fieldValue && Array.isArray(fieldValue) && itemType === 'relselect' && fieldValue.length > 0 && fieldValue[0].includes('valid')) {
|
|
140
143
|
itemType = 'file'
|
|
141
144
|
}
|
|
145
|
+
console.log('itemType', itemType)
|
|
142
146
|
return itemType
|
|
143
147
|
}
|
|
144
148
|
|