ソースを参照

新增拍摄界面,优化UI布局,美化样式,js逻辑没动

zhangxike 2 日 前
コミット
d3f96924a8
2 ファイル変更402 行追加1 行削除
  1. 401 0
      pages/work/record/Camera.uvue
  2. 1 1
      pages/work/record/RecordList.uvue

+ 401 - 0
pages/work/record/Camera.uvue

@@ -0,0 +1,401 @@
+<template>
+	<view class="camera-container">
+		<!-- 全屏相机预览 -->
+		<camera class="camera-preview" 
+			:resolution="'medium'" 
+			:device-position="devicePosition"
+			:flash="flash" 
+			:frame-size="frameSize" 
+			@stop="handleStop" 
+			@error="handleError" 
+			@initdone="handleInitDone">
+		</camera>
+
+		<!-- 左上角:闪光灯图标按钮 -->
+		<view class="top-left">
+			<button class="flash-btn" @click="switchFlash">
+				<text class="icon">{{ flash === 'torch' ? '💡' : '🔦' }}</text>
+			</button>
+		</view>
+
+		<!-- 右上角:图像质量设置 -->
+		<view class="top-right">
+			<view class="quality-setting">
+				<text class="setting-label">成像质量</text>
+				<radio-group class="quality-group" @change="takePhotoQualityChange">
+					<radio class="quality-radio" value="normal" :checked="quality === 'normal'">普通</radio>
+					<radio class="quality-radio" value="high" :checked="quality === 'high'">高清</radio>
+					<radio class="quality-radio" value="original" :checked="quality === 'original'">原图</radio>
+				</radio-group>
+			</view>
+		</view>
+
+		<!-- 底部中间:圆形拍照按钮 -->
+		<view class="bottom-center">
+			<button class="shoot-btn" @click="handleTakePhoto">
+				<view class="shoot-inner"></view>
+			</button>
+		</view>
+
+		<!-- 左下角:相册预览 -->
+		<view class="bottom-left">
+			<view class="album-preview" @click="handleScanCode">
+				<image class="preview-img" v-if="imageSrc" :src="imageSrc"></image>
+				<text class="preview-tip" v-else>相册</text>
+			</view>
+		</view>
+
+		<!-- 下方:缩放控制(位置保持不变) -->
+		<view class="zoom-control">
+			<text class="setting-label">预览缩放</text>
+			<view class="zoom-container">
+				<slider class="zoom-slider" 
+					:disabled="maxZoom <= 1" 
+					:show-value="true" 
+					:min="1"
+					:max="maxZoom" 
+					:value="1" 
+					@change="zoomSliderChange" />
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				devicePosition: "back",
+				flash: "off",
+				frameSize: "medium",
+				listener: null as CameraContextCameraFrameListener | null,
+				maxZoom: 0,
+				imageSrc: "",
+				quality: "normal",
+				timeout: 30,
+				compressed: false,
+				videoSrc: "",
+				startRecordStatus: false,
+				remain: 0,
+				intervalId: -1,
+				timeoutStr: '30'
+			}
+		},
+		onLoad() {
+
+		},
+
+		methods: {
+			handleScanCode() {
+				uni.navigateTo({
+					url: "/pages/component/camera/camera-scan-code"
+				})
+			},
+			switchDevicePosition() {
+				this.devicePosition = this.devicePosition == "back" ? "front" : "back"
+			},
+
+			switchFlash() {
+				this.flash = this.flash == "torch" ? "off" : "torch"
+			},
+
+			handleStop(e : UniCameraStopEvent) {
+				console.log("stop", e.detail);
+			},
+			handleError(e : UniCameraErrorEvent) {
+				console.log("error", e.detail);
+			},
+			handleInitDone(e : UniCameraInitDoneEvent) {
+				console.log("initdone", e.detail);
+				this.maxZoom = e.detail.maxZoom ?? 0
+			},
+			zoomSliderChange(event : UniSliderChangeEvent) {
+				const value = event.detail.value
+				const context = uni.createCameraContext();
+				context?.setZoom({
+					zoom: value,
+					success: (e : any) => {
+						console.log(e);
+					}
+				} as CameraContextSetZoomOptions)
+			},
+			handleTakePhoto() {
+				const context = uni.createCameraContext();
+				context?.takePhoto({
+					quality: this.quality,
+					selfieMirror: false,
+					success: (res : CameraContextTakePhotoResult) => {
+						console.log("res.tempImagePath", res.tempImagePath);
+						this.imageSrc = res.tempImagePath ?? ''
+					},
+					fail: (e : any) => {
+						console.log("take photo", e);
+					}
+				} as CameraContextTakePhotoOptions)
+			},
+			takePhotoQualityChange(event : UniRadioGroupChangeEvent) {
+				this.quality = event.detail.value
+				console.log("quality", this.quality);
+			},
+
+			setOnFrameListener() {
+				const context = uni.createCameraContext();
+				this.listener = context?.onCameraFrame((frame : CameraContextOnCameraFrame) => {
+					console.log("OnFrame :", frame);
+				})
+			},
+			startFrameListener() {
+				this.listener?.start({
+					success: (res : any) => {
+						console.log("startFrameListener success", res);
+					}
+				} as CameraContextCameraFrameListenerStartOptions)
+
+			},
+			stopFrameListener() {
+				this.listener?.stop({
+					success: (res : any) => {
+						console.log("stopFrameListener success", res);
+					}
+				} as CameraContextCameraFrameListenerStopOptions)
+			},
+			startRecord() {
+				const context = uni.createCameraContext();
+				let timeout = this.getTimeout()
+				this.timeout = timeout
+				context?.startRecord({
+					timeout: timeout,
+					selfieMirror: false,
+					timeoutCallback: (res : any) => {
+						console.log("timeoutCallback", res);
+						this.startRecordStatus = false
+						if (typeof res != "string") {
+							const result = res as CameraContextStartRecordTimeoutResult
+							this.videoSrc = result.tempVideoPath ?? ''
+						}
+						clearInterval(this.intervalId)
+					},
+					success: (res : any) => {
+						this.startRecordStatus = true
+						console.log("start record success", res);
+						this.remain = timeout
+						this.intervalId = setInterval(() => {
+							if (this.remain <= 0) {
+								clearInterval(this.intervalId)
+							} else {
+								this.remain--
+							}
+						}, 1000)
+					},
+					fail: (res : any) => {
+						console.log("start record fail", res);
+						this.startRecordStatus = false
+						this.remain = 0
+						clearInterval(this.intervalId)
+					}
+				} as CameraContextStartRecordOptions)
+			},
+			stopRecord() {
+				this.startRecordStatus = false
+				const context = uni.createCameraContext();
+				context?.stopRecord({
+					compressed: this.compressed,
+					success: (res : CameraContextStopRecordResult) => {
+						console.log("stop record success", res);
+						this.videoSrc = res.tempVideoPath ?? ''
+					},
+					fail: (res : any) => {
+						console.log("stop record fail", res);
+					}
+				} as CameraContextStopRecordOptions)
+				clearInterval(this.intervalId)
+				this.remain = 0
+			},
+			startRecordCompressChange(event : UniRadioGroupChangeEvent) {
+				this.compressed = parseInt(event.detail.value) == 1
+			},
+			timeoutInput(event : UniInputEvent) {
+				this.timeoutStr = event.detail.value
+			},
+			getTimeout() : number {
+				let value = parseInt(this.timeoutStr)
+				// #ifdef APP-ANDROID
+				if (value == NaN) {
+					// #endif
+					// #ifndef APP-ANDROID
+					if (value !== value) {
+						// #endif
+						return 30
+					} else {
+						if (value < 1) {
+							return 1
+						} else if (value > 60 * 5) {
+							return 60 * 5
+						} else {
+							return value
+						}
+					}
+				}
+			}
+		}
+</script>
+
+<style scope>
+	/* 基础容器 */
+	.camera-container {
+		width: 100%;
+		height: 100%;
+		position: relative;
+		overflow: hidden;
+		background-color: #000;
+	}
+
+	/* 相机预览 */
+	.camera-preview {
+		width: 100%;
+		height: 100%;
+		position: absolute;
+		top: 0;
+		left: 0;
+	}
+
+	/* 左上角:闪光灯按钮 */
+	.top-left {
+		position: absolute;
+		top: 40rpx;
+		left: 40rpx;
+		z-index: 10;
+	}
+
+	.flash-btn {
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 100rpx;
+		background-color: rgba(0, 0, 0, 0.4);
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 0;
+		border: none;
+	}
+
+	.icon {
+		font-size: 48rpx;
+		color: #e0e0e0; /* 银色 */
+	}
+
+	/* 右上角:图像质量设置 */
+	.top-right {
+		position: absolute;
+		top: 40rpx;
+		right: 40rpx;
+		z-index: 10;
+		background-color: rgba(0, 0, 0, 0.4);
+		padding: 20rpx 30rpx;
+		border-radius: 16rpx;
+	}
+
+	/* 底部中间:拍照按钮 */
+	.bottom-center {
+		position: absolute;
+		bottom: 240rpx;
+		left: 50%;
+		transform: translateX(-50%);
+		z-index: 10;
+	}
+
+	.shoot-btn {
+		width: 180rpx;
+		height: 180rpx;
+		border-radius: 180rpx;
+		background-color: #ffffff;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 0;
+		border: 8rpx solid rgba(255, 255, 255, 0.3);
+		box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0);
+	}
+
+	.shoot-inner {
+		width: 140rpx;
+		height: 140rpx;
+		border-radius: 140rpx;
+		background-color: #f0f0f0;
+	}
+
+	/* 左下角:相册预览 */
+	.bottom-left {
+		position: absolute;
+		bottom: 240rpx;
+		left: 40rpx;
+		z-index: 10;
+	}
+
+	.album-preview {
+		width: 130rpx;
+		height: 130rpx;
+		border: 4rpx solid #e0e0e0;
+		border-radius: 16rpx;
+		overflow: hidden;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background-color: rgba(255, 255, 255, 0.1);
+	}
+
+	.preview-img {
+		width: 100%;
+		height: 100%;
+		object-fit: cover;
+	}
+
+	.preview-tip {
+		color: #e0e0e0; /* 银色 */
+		font-size: 26rpx;
+	}
+
+	/* 下方:缩放控制 */
+	.zoom-control {
+		position: absolute;
+		bottom: 40rpx;
+		left: 0;
+		width: 100%;
+		padding: 0 40rpx;
+		box-sizing: border-box;
+		z-index: 10;
+	}
+
+	.setting-label {
+		display: block;
+		color: #e0e0e0; /* 银色 */
+		font-size: 28rpx;
+		margin-bottom: 16rpx;
+		font-weight: 500;
+	}
+
+	/* 质量选择样式 */
+	.quality-group {
+		display: flex;
+		gap: 10;
+	}
+
+	.quality-radio {
+		color: #e0e0e0; /* 银色 */
+		font-size: 26rpx;
+		display: flex;
+		align-items: center;
+		gap: 10;
+	}
+
+	/* 缩放控制样式 */
+	.zoom-container {
+		width: 100%;
+		padding: 10rpx 0;
+	}
+
+	.zoom-slider {
+		width: 100%;
+		height: 4px;
+	}
+</style>

+ 1 - 1
pages/work/record/RecordList.uvue

@@ -157,7 +157,7 @@
 
 	const cancel = (id : number) => {
 		uni.navigateTo({
-			url: `/pages/work/record/PhotoList?id=${id}`
+			url: `/pages/work/record/Camera?id=${id}`
 		})
 	}
 </script>