333
This commit is contained in:
parent
3fccba37d3
commit
0482e7af25
|
|
@ -1,10 +1,10 @@
|
|||
# 生产环境配置
|
||||
ENV = 'production'
|
||||
|
||||
VITE_APP_API_HOST = 'https://gift.zpc-xy.com/'
|
||||
VITE_APP_API_HOST = 'https://wc.zpc-xy.com/'
|
||||
|
||||
# 生产环境
|
||||
VITE_APP_BASE_API = 'https://gift.zpc-xy.com/'
|
||||
VITE_APP_BASE_API = 'https://wc.zpc-xy.com/'
|
||||
|
||||
# 路由前缀
|
||||
VITE_APP_ROUTER_PREFIX = '/'
|
||||
|
|
@ -13,7 +13,7 @@ VITE_APP_ROUTER_PREFIX = '/'
|
|||
VITE_APP_UPLOAD_URL = 'Common/UploadFile'
|
||||
|
||||
#socket API
|
||||
VITE_APP_SOCKET_API = 'https://gift.zpc-xy.com/msghub'
|
||||
VITE_APP_SOCKET_API = 'https://wc.zpc-xy.com/msghub'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
VITE_BUILD_COMPRESS = gzip
|
||||
VITE_BUILD_COMPRESS = gzip
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="/favicon.jpg">
|
||||
<title>工作相机</title>
|
||||
<title>随工水印相机后台管理</title>
|
||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||
<style>
|
||||
html,
|
||||
|
|
|
|||
|
|
@ -1,652 +0,0 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 主页面内容 -->
|
||||
<view>
|
||||
<image class="logo" src="/static/logo.jpg"></image>
|
||||
<text class="title">{{ title }}</text>
|
||||
<view class="btn-view">
|
||||
<button type="primary" class="btn" @click="handleStartCapture">
|
||||
开始拍摄
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 隐藏的canvas用于水印处理 -->
|
||||
<canvas canvas-id="watermarkCanvas" style="
|
||||
position: fixed;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
width: 2000px;
|
||||
height: 2000px;
|
||||
"></canvas>
|
||||
|
||||
<!-- 弹窗内容 -->
|
||||
<uni-popup ref="popup" :is-mask-click="false" type="bottom" border-radius="0 0 0 0">
|
||||
<view class="container">
|
||||
<view style="height: 80rpx"></view>
|
||||
|
||||
<!-- 图片预览区域 -->
|
||||
<view class="preview-box">
|
||||
<text class="preview-title">图片预览</text>
|
||||
<view class="preview-img">
|
||||
<image :src="imageSrc" @click="handlePreviewImage" mode="aspectFit" class="img"></image>
|
||||
</view>
|
||||
|
||||
<!-- 图片尺寸和文件大小信息 -->
|
||||
<view class="size-info" v-if="imageSizeInfo.original.width > 0 && false">
|
||||
<text class="size-text">
|
||||
原图尺寸: {{ imageSizeInfo.original.width }} x
|
||||
{{ imageSizeInfo.original.height }}
|
||||
</text>
|
||||
<text class="size-text" v-if="imageSizeInfo.original.fileSize">
|
||||
原图大小: {{ imageSizeInfo.original.fileSize.sizeKB }} KB
|
||||
</text>
|
||||
<text class="size-text">
|
||||
水印图片尺寸: {{ imageSizeInfo.watermark.width }} x
|
||||
{{ imageSizeInfo.watermark.height }}
|
||||
</text>
|
||||
<text class="size-text" v-if="imageSizeInfo.watermark.fileSize">
|
||||
水印图片大小: {{ imageSizeInfo.watermark.fileSize.sizeKB }} KB
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-item">
|
||||
<text class="label">位置:</text>
|
||||
<input v-model="locationInfo" type="text" class="input worker-input" />
|
||||
</view>
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-item">
|
||||
<text class="label">请填写工作内容:</text>
|
||||
<uni-combox :candidates="candidates" :clear-able="true" placeholder="请填写工作内容"
|
||||
@select="handleComboxSelect" v-model="workContent"></uni-combox>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">请选择部门:</text>
|
||||
<picker :range="departments" @change="handleDeptChange">
|
||||
<view class="picker">{{ departments[deptIndex] }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">请填写施工人员:</text>
|
||||
<view v-for="(worker, index) in workers" :key="index" class="worker-row">
|
||||
<input type="text" v-model="workers[index]" class="input worker-input" />
|
||||
<view class="btns">
|
||||
<button class="btn_f" size="mini" @click="addWorker">+</button>
|
||||
<button v-if="workers.length > 0" class="btn_f" size="mini" @click="removeWorker(index)">
|
||||
-
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">请选择项目状态:</text>
|
||||
<picker :range="statusList" @change="handleStatusChange">
|
||||
<view class="picker">{{ statusList[statusIndex] }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="footer">
|
||||
<button type="primary" class="btn-cancel" @click="handleRetakePhoto">
|
||||
重拍
|
||||
</button>
|
||||
<button type="primary" class="btn-cancel" @click="handleRetakeCancel">
|
||||
清空
|
||||
</button>
|
||||
<button type="primary" class="btn-submit" @click="handleSaveAndSubmit">
|
||||
保存本地并提交
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref
|
||||
} from "vue";
|
||||
import {
|
||||
getLocation,
|
||||
chooseImage,
|
||||
addWatermark,
|
||||
formatDate,
|
||||
getCachedLogo,
|
||||
saveImageToPhotosAlbum,
|
||||
imageToBase64,
|
||||
} from "../../common/utils";
|
||||
import {
|
||||
getLocationTranslate,
|
||||
getLocationGeocoder,
|
||||
} from "../../common/mapTranslateResult";
|
||||
import {
|
||||
getConfig,
|
||||
addWatermarkRecord
|
||||
} from "../../common/server";
|
||||
import {
|
||||
onLoad
|
||||
} from "@dcloudio/uni-app";
|
||||
|
||||
// ==================== 响应式数据 ====================
|
||||
// 基础数据
|
||||
const title = ref("水印相机");
|
||||
const candidates = ref([]);
|
||||
const workContent = ref(""); // 工作内容
|
||||
const popup = ref(null);
|
||||
|
||||
// 位置信息
|
||||
const locations = ref({
|
||||
location: {
|
||||
lat: null,
|
||||
lng: null,
|
||||
},
|
||||
translate: {
|
||||
lat: null,
|
||||
lng: null,
|
||||
locationInfo: null,
|
||||
},
|
||||
});
|
||||
|
||||
// 图片相关
|
||||
const imageSrc = ref("");
|
||||
const originalImageSrc = ref("");
|
||||
const imageSizeInfo = ref({
|
||||
original: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
fileSize: null,
|
||||
},
|
||||
watermark: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
fileSize: null,
|
||||
},
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const departments = ref(["请选择"]);
|
||||
const deptIndex = ref(0);
|
||||
const workers = ref([""]);
|
||||
const statusList = ref([]);
|
||||
const statusIndex = ref(0);
|
||||
|
||||
// 其他信息
|
||||
const locationInfo = ref("");
|
||||
const currentTime = ref("");
|
||||
let logo = ""; // 非响应式数据
|
||||
|
||||
// ==================== 事件处理函数 ====================
|
||||
// 开始拍摄
|
||||
const handleStartCapture = async () => {
|
||||
uni.showLoading({
|
||||
title: "加载中。。。",
|
||||
});
|
||||
|
||||
try {
|
||||
// 获取位置信息
|
||||
const locationData = await getLocation();
|
||||
console.log("系统坐标", locationData);
|
||||
|
||||
const location = locationData.latitude + "," + locationData.longitude;
|
||||
console.log("经纬度", location);
|
||||
|
||||
const translate = await getLocationTranslate(location);
|
||||
console.log("转换后", translate);
|
||||
|
||||
const t_location = translate.lat + "," + translate.lng;
|
||||
const geocoderResult = await getLocationGeocoder(t_location);
|
||||
console.log("地理编码结果", geocoderResult);
|
||||
|
||||
// 保存位置信息
|
||||
locations.value.location.lat = locationData.latitude;
|
||||
locations.value.location.lng = locationData.longitude;
|
||||
locations.value.translate.lat = translate.lat;
|
||||
locations.value.translate.lng = translate.lng;
|
||||
locations.value.translate.locationInfo = geocoderResult || "未知位置";
|
||||
locationInfo.value = geocoderResult || "未知位置";
|
||||
currentTime.value = new Date();
|
||||
|
||||
// 选择图片
|
||||
const image = await chooseImage();
|
||||
console.log("图片", image);
|
||||
originalImageSrc.value = image.tempFilePaths[0];
|
||||
imageSrc.value = image.tempFilePaths[0];
|
||||
|
||||
await addWatermarkToImage();
|
||||
uni.hideLoading();
|
||||
popup.value.open();
|
||||
} catch (error) {
|
||||
console.log("错误", error);
|
||||
uni.hideLoading();
|
||||
}
|
||||
};
|
||||
|
||||
// 预览图片
|
||||
const handlePreviewImage = () => {
|
||||
uni.previewImage({
|
||||
urls: [imageSrc.value]
|
||||
});
|
||||
};
|
||||
|
||||
// 表单事件处理
|
||||
const handleComboxSelect = async (value) => {
|
||||
console.log("选择的工作内容:", value);
|
||||
if (locationData[value] != null) {
|
||||
var tempData = locationData[value];
|
||||
|
||||
// 设置部门
|
||||
if (tempData.dept && departments.value.includes(tempData.dept)) {
|
||||
deptIndex.value = departments.value.indexOf(tempData.dept);
|
||||
}
|
||||
|
||||
// 设置项目状态
|
||||
if (tempData.status && statusList.value.includes(tempData.status)) {
|
||||
statusIndex.value = statusList.value.indexOf(tempData.status);
|
||||
}
|
||||
|
||||
// 设置施工人员
|
||||
if (tempData.workers && Array.isArray(tempData.workers)) {
|
||||
workers.value.splice(0, workers.value.length);
|
||||
workers.value.push(...tempData.workers);
|
||||
}
|
||||
await addWatermarkToImage(value);
|
||||
console.log("加载的历史数据:", tempData);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeptChange = (e) => {
|
||||
deptIndex.value = e.detail.value;
|
||||
};
|
||||
|
||||
const handleStatusChange = (e) => {
|
||||
statusIndex.value = e.detail.value;
|
||||
};
|
||||
|
||||
// 施工人员管理
|
||||
const addWorker = () => {
|
||||
workers.value.push("");
|
||||
};
|
||||
|
||||
const removeWorker = (index) => {
|
||||
if (workers.value.length > 1) {
|
||||
workers.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 业务逻辑函数 ====================
|
||||
// 添加水印
|
||||
const addWatermarkToImage = async (work = "") => {
|
||||
try {
|
||||
const watermarkInfo = {
|
||||
time: formatDate(currentTime.value),
|
||||
location: locationInfo.value,
|
||||
longitude: locations.value.translate.lng,
|
||||
latitude: locations.value.translate.lat,
|
||||
department: departments.value[deptIndex.value],
|
||||
workers: workers.value.filter((worker) => worker.trim() !== ""),
|
||||
status: statusList.value[statusIndex.value],
|
||||
remarks: workContent.value,
|
||||
};
|
||||
if (workContent.value == "" && work != "") {
|
||||
watermarkInfo.remarks = work;
|
||||
}
|
||||
console.log("水印信息:", watermarkInfo);
|
||||
console.log("原图路径:", originalImageSrc.value);
|
||||
|
||||
const watermarkResult = await addWatermark(
|
||||
originalImageSrc.value,
|
||||
watermarkInfo,
|
||||
logo,
|
||||
1
|
||||
);
|
||||
|
||||
console.log("水印结果:", watermarkResult);
|
||||
imageSrc.value = watermarkResult.filePath;
|
||||
imageSizeInfo.value = {
|
||||
original: watermarkResult.originalSize,
|
||||
watermark: watermarkResult.watermarkSize,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("添加水印失败:", error);
|
||||
uni.showToast({
|
||||
title: "添加水印失败",
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 重新拍照
|
||||
const handleRetakePhoto = () => {
|
||||
popup.value.close();
|
||||
resetFormData();
|
||||
};
|
||||
const handleRetakeCancel = async () => {
|
||||
workers.value.splice(0, workers.value.length);
|
||||
workers.value.push("");
|
||||
statusIndex.value = 0;
|
||||
deptIndex.value = 0;
|
||||
workContent.value = "";
|
||||
loadCandidates();
|
||||
await addWatermarkToImage();
|
||||
};
|
||||
var locationData = {};
|
||||
// 保存并提交
|
||||
const handleSaveAndSubmit = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: "保存中...",
|
||||
});
|
||||
|
||||
// 先添加水印
|
||||
await addWatermarkToImage();
|
||||
var saveData = {
|
||||
workContent: workContent.value,
|
||||
workers: workers.value.filter((worker) => worker.trim() !== ""),
|
||||
status: statusList.value[statusIndex.value],
|
||||
dept: departments.value[deptIndex.value],
|
||||
date: new Date().toISOString(), // 使用ISO格式确保时间格式一致
|
||||
};
|
||||
console.log(saveData);
|
||||
locationData[workContent.value] = saveData;
|
||||
uni.setStorageSync("locationData", locationData);
|
||||
var fromData = {
|
||||
locations: locations.value,
|
||||
workContent: workContent.value,
|
||||
workers: workers.value,
|
||||
status: statusList.value[statusIndex.value],
|
||||
dept: departments.value[deptIndex.value],
|
||||
};
|
||||
var _remarks = JSON.stringify(locations.value);
|
||||
var imageBase64 = await imageToBase64(imageSrc.value);
|
||||
console.log(fromData);
|
||||
const camRecordWorkDto = {
|
||||
// 部门名称
|
||||
DeptName: departments.value[deptIndex.value],
|
||||
// 图片地址
|
||||
Image: imageBase64,
|
||||
// 工作记录时间
|
||||
RecordTime: formatDate(currentTime.value),
|
||||
// 经度
|
||||
Longitude: locations.value.translate.lng,
|
||||
// 纬度
|
||||
Latitude: locations.value.translate.lat,
|
||||
// 工作地点
|
||||
Address: locationInfo.value,
|
||||
// 工作内容
|
||||
Content: workContent.value,
|
||||
// 状态
|
||||
StatusName: statusList.value[statusIndex.value],
|
||||
// 备注
|
||||
Remarks: _remarks,
|
||||
|
||||
// 工作人员列表
|
||||
Workers: workers.value,
|
||||
};
|
||||
var res = await addWatermarkRecord(camRecordWorkDto);
|
||||
console.log(res);
|
||||
if (res.code != 200) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
icon: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 保存水印图片到相册
|
||||
if (imageSrc.value) {
|
||||
await saveImageToPhotosAlbum(imageSrc.value);
|
||||
}
|
||||
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "保存成功",
|
||||
icon: "success",
|
||||
});
|
||||
|
||||
handleRetakePhoto();
|
||||
} catch (error) {
|
||||
console.error("保存失败:", error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "保存失败",
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 工具函数 ====================
|
||||
// 重置表单数据
|
||||
const resetFormData = () => {
|
||||
imageSrc.value = "";
|
||||
originalImageSrc.value = "";
|
||||
locationInfo.value = "";
|
||||
currentTime.value = "";
|
||||
imageSizeInfo.value = {
|
||||
original: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
fileSize: null,
|
||||
},
|
||||
watermark: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
fileSize: null,
|
||||
},
|
||||
};
|
||||
workers.value.splice(0, workers.value.length);
|
||||
workers.value.push("");
|
||||
statusIndex.value = 0;
|
||||
deptIndex.value = 0;
|
||||
workContent.value = "";
|
||||
loadCandidates();
|
||||
};
|
||||
|
||||
const loadCandidates = () => {
|
||||
candidates.value.splice(0, candidates.value.length);
|
||||
|
||||
// 将对象转换为数组并按时间倒序排序
|
||||
const sortedEntries = Object.entries(locationData)
|
||||
.filter(([key, data]) => data && data.date) // 过滤掉没有时间字段的数据
|
||||
.sort((a, b) => {
|
||||
const dateA = new Date(a[1].date);
|
||||
const dateB = new Date(b[1].date);
|
||||
return dateB - dateA; // 倒序排列(最新的在前)
|
||||
});
|
||||
|
||||
// 提取排序后的键名
|
||||
sortedEntries.forEach(([key, data]) => {
|
||||
console.log(`工作内容: ${key}, 时间: ${data.date}`);
|
||||
candidates.value.push(key);
|
||||
});
|
||||
};
|
||||
// ==================== 生命周期 ====================
|
||||
onLoad(async () => {
|
||||
uni.showLoading({
|
||||
title: "loading...",
|
||||
});
|
||||
|
||||
try {
|
||||
const config = await getConfig();
|
||||
console.log("配置", config);
|
||||
|
||||
logo = config.logo;
|
||||
departments.value.push(...config.deptList);
|
||||
statusList.value.push(...config.construction);
|
||||
|
||||
// 缓存logo
|
||||
await getCachedLogo(logo);
|
||||
|
||||
var _locationData = uni.getStorageSync("locationData");
|
||||
if (_locationData != null) {
|
||||
locationData = _locationData;
|
||||
loadCandidates();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("初始化失败:", error);
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.logo {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
top: 25%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100px;
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
bottom: 22%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 auto;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 35%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.preview-box {
|
||||
background: #fff;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.preview-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.preview-img {
|
||||
margin: 20rpx 0;
|
||||
width: 100%;
|
||||
height: 400rpx;
|
||||
background: #ddd;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
}
|
||||
|
||||
.preview-info {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 10rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.input {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
padding: 10rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.picker {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
padding: 10rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.worker-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.worker-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.btn_f {
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.watermark-btn {
|
||||
margin-top: 20rpx;
|
||||
background: #28a745;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
padding: 10rpx 30rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.size-info {
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.size-text {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 10rpx;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user