feat(detail): 参会人表新增 增值税及附加/摘要/现场照片 3 列 + 劳务协议展示, 角色=劳务形式, 费项改名 应发金额/个税税金/实发金额
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 共享相机 composable (H5 only)
|
||||
*
|
||||
* 用法: const { captured, errorMsg, startCamera, flipCamera, takePhoto, retake, confirm } = useCamera('my-video-id')
|
||||
*
|
||||
* - 自动在 onMounted 启动摄像头
|
||||
* - 自动在 onBeforeUnmount 停止摄像头
|
||||
* - 返回 ref 和方法,业务页只需负责 UI 布局
|
||||
*/
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { openCamera, stopCamera, captureFrame, describeCameraError, type Facing } from '@/utils/camera'
|
||||
|
||||
export function useCamera(videoId: string) {
|
||||
const facing = ref<Facing>('environment')
|
||||
const captured = ref<string>('')
|
||||
const errorMsg = ref<string>('')
|
||||
|
||||
function getVideoEl(): HTMLVideoElement | null {
|
||||
return document.getElementById(videoId) as HTMLVideoElement | null
|
||||
}
|
||||
|
||||
async function startCamera() {
|
||||
errorMsg.value = ''
|
||||
const video = getVideoEl()
|
||||
if (!video) {
|
||||
errorMsg.value = '视频元素未找到'
|
||||
return
|
||||
}
|
||||
try {
|
||||
await openCamera(video, facing.value)
|
||||
} catch (err) {
|
||||
errorMsg.value = describeCameraError(err)
|
||||
console.error('[camera]', err)
|
||||
}
|
||||
}
|
||||
|
||||
function flipCamera() {
|
||||
facing.value = facing.value === 'environment' ? 'user' : 'environment'
|
||||
startCamera()
|
||||
}
|
||||
|
||||
async function takePhoto() {
|
||||
const video = getVideoEl()
|
||||
if (!video) return
|
||||
try {
|
||||
captured.value = captureFrame(video)
|
||||
} catch (err) {
|
||||
uni.showToast({
|
||||
title: (err as Error).message || '截图失败',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function retake() {
|
||||
captured.value = ''
|
||||
}
|
||||
|
||||
async function confirm() {
|
||||
// POC: 假上传 + loading 给用户完整仪式感
|
||||
uni.showLoading({ title: '上传中...' })
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 800))
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '已保存 (POC 未上传)',
|
||||
icon: 'none',
|
||||
duration: 1500,
|
||||
})
|
||||
captured.value = ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
startCamera()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const video = getVideoEl()
|
||||
if (video) {
|
||||
stopCamera(video)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
facing,
|
||||
captured,
|
||||
errorMsg,
|
||||
startCamera,
|
||||
flipCamera,
|
||||
takePhoto,
|
||||
retake,
|
||||
confirm,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user