|
@@ -0,0 +1,197 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <view class="photo-page">
|
|
|
|
|
+ <wd-navbar
|
|
|
|
|
+ fixed
|
|
|
|
|
+ placeholder
|
|
|
|
|
+ left-arrow
|
|
|
|
|
+ :bordered="false"
|
|
|
|
|
+ title="图片预览"
|
|
|
|
|
+ safeAreaInsetTop
|
|
|
|
|
+ @click-left="goBack"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <view class="preview-body">
|
|
|
|
|
+ <image v-if="watermarkSrc" class="preview-image" :src="watermarkSrc" mode="scaleToFill" />
|
|
|
|
|
+ <text v-else class="preview-status">{{ statusText }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view v-if="imageUrl" class="preview-footer">
|
|
|
|
|
+ <view class="btn" @click="takePhotoFlow">重拍</view>
|
|
|
|
|
+ <view class="btn primary" :class="{ disabled: !watermarkSrc || saving }" @click="download">
|
|
|
|
|
+ 下载照片
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <canvas
|
|
|
|
|
+ canvas-id="watermarkCanvas"
|
|
|
|
|
+ :style="`width: ${canvasWidth}px; height: ${canvasHeight}px; position: fixed; left: -9999px; top: 0`"
|
|
|
|
|
+ />
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { onReady, onUnload } from '@dcloudio/uni-app'
|
|
|
|
|
+import { getCurrentInstance, nextTick, ref } from 'vue'
|
|
|
|
|
+
|
|
|
|
|
+import { useSaveToAlbum } from '@/composables/photo/useSaveToAlbum'
|
|
|
|
|
+import { getLocation } from '@/lib/location'
|
|
|
|
|
+import { generateWatermark } from '@/utils/generateWatermark'
|
|
|
|
|
+import { takePhoto } from '@/utils/image'
|
|
|
|
|
+
|
|
|
|
|
+const instance = getCurrentInstance()
|
|
|
|
|
+const { saveToAlbum } = useSaveToAlbum()
|
|
|
|
|
+
|
|
|
|
|
+const imageUrl = ref('')
|
|
|
|
|
+const watermarkSrc = ref('')
|
|
|
|
|
+const statusText = ref('正在获取位置信息...')
|
|
|
|
|
+const canvasWidth = ref(10)
|
|
|
|
|
+const canvasHeight = ref(10)
|
|
|
|
|
+const taking = ref(false)
|
|
|
|
|
+const saving = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+let active = true
|
|
|
|
|
+let flowId = 0
|
|
|
|
|
+
|
|
|
|
|
+const isCurrentFlow = (id: number) => active && flowId === id
|
|
|
|
|
+
|
|
|
|
|
+const goBack = () => {
|
|
|
|
|
+ if (!active) return
|
|
|
|
|
+ active = false
|
|
|
|
|
+ flowId++
|
|
|
|
|
+ uni.navigateBack()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getImageSize = (src: string): Promise<{ width: number; height: number }> =>
|
|
|
|
|
+ new Promise((resolve, reject) => {
|
|
|
|
|
+ uni.getImageInfo({
|
|
|
|
|
+ src,
|
|
|
|
|
+ success: ({ width, height }) => resolve({ width, height }),
|
|
|
|
|
+ fail: reject,
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+const takePhotoFlow = async (): Promise<void> => {
|
|
|
|
|
+ if (taking.value || !active) return
|
|
|
|
|
+
|
|
|
|
|
+ taking.value = true
|
|
|
|
|
+ imageUrl.value = ''
|
|
|
|
|
+ watermarkSrc.value = ''
|
|
|
|
|
+ statusText.value = '正在获取位置信息...'
|
|
|
|
|
+ const currentFlow = ++flowId
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const location = await getLocation()
|
|
|
|
|
+ if (!isCurrentFlow(currentFlow)) return
|
|
|
|
|
+ if (!location?.address) {
|
|
|
|
|
+ goBack()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ statusText.value = '正在打开相机...'
|
|
|
|
|
+ const photo = await takePhoto()
|
|
|
|
|
+ if (!isCurrentFlow(currentFlow)) return
|
|
|
|
|
+
|
|
|
|
|
+ imageUrl.value = photo
|
|
|
|
|
+ statusText.value = '正在生成水印...'
|
|
|
|
|
+ const { width, height } = await getImageSize(photo)
|
|
|
|
|
+ if (!isCurrentFlow(currentFlow)) return
|
|
|
|
|
+
|
|
|
|
|
+ canvasWidth.value = width
|
|
|
|
|
+ canvasHeight.value = height
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ if (!isCurrentFlow(currentFlow)) return
|
|
|
|
|
+
|
|
|
|
|
+ watermarkSrc.value = await generateWatermark({
|
|
|
|
|
+ imageUrl: photo,
|
|
|
|
|
+ address: location.address,
|
|
|
|
|
+ canvasId: 'watermarkCanvas',
|
|
|
|
|
+ instance,
|
|
|
|
|
+ })
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (!isCurrentFlow(currentFlow)) return
|
|
|
|
|
+ if (!imageUrl.value) {
|
|
|
|
|
+ // 取消拍照或定位失败时,回到发起拍照的工作台。
|
|
|
|
|
+ goBack()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('generateWatermark error', error)
|
|
|
|
|
+ statusText.value = '照片处理失败,请重拍'
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (isCurrentFlow(currentFlow)) taking.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const download = async () => {
|
|
|
|
|
+ if (!watermarkSrc.value || saving.value) return
|
|
|
|
|
+
|
|
|
|
|
+ saving.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ await saveToAlbum(watermarkSrc.value)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('保存照片失败:', error)
|
|
|
|
|
+ uni.showToast({ title: '保存失败,请重试', icon: 'none' })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ saving.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onReady(() => {
|
|
|
|
|
+ void takePhotoFlow()
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+onUnload(() => {
|
|
|
|
|
+ active = false
|
|
|
|
|
+ flowId++
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.photo-page {
|
|
|
|
|
+ height: 100vh;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ background: #000;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.preview-body {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+
|
|
|
|
|
+ .preview-image {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ width: 750rpx;
|
|
|
|
|
+ max-width: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .preview-status {
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.preview-footer {
|
|
|
|
|
+ height: 128rpx;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 0 48rpx env(safe-area-inset-bottom);
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+
|
|
|
|
|
+ .btn {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .primary {
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .disabled {
|
|
|
|
|
+ opacity: 0.5;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|