Compare commits
20 Commits
a0f783fc5a
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59485ce24c | ||
|
|
a97fabbef8 | ||
|
|
9f12490fb5 | ||
|
|
05df7bff69 | ||
|
|
485a9511a1 | ||
|
|
eddd2a4bf7 | ||
|
|
22326d4ff8 | ||
|
|
03b2f64389 | ||
|
|
eecfa777a8 | ||
|
|
a1f18ade8f | ||
|
|
3da8fe4027 | ||
|
|
052e616e45 | ||
|
|
1f2bb96ef3 | ||
|
|
d32152912a | ||
|
|
a55cdb8121 | ||
|
|
26430aea3d | ||
|
|
bfbb1a1cf4 | ||
|
|
dc9183664c | ||
|
|
c36aa01f0a | ||
|
|
dbe7effec5 |
9
package-lock.json
generated
9
package-lock.json
generated
@@ -30,6 +30,7 @@
|
||||
"minio": "^8.0.6",
|
||||
"minio-js": "^1.0.7",
|
||||
"pinia": "2.0.20",
|
||||
"uniapp-video-player": "^1.3.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "3.4.21",
|
||||
"vue-i18n": "9.14.5"
|
||||
@@ -12288,6 +12289,14 @@
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/uniapp-video-player": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/uniapp-video-player/-/uniapp-video-player-1.3.0.tgz",
|
||||
"integrity": "sha512-WZa/U836ou053pzqijL2EzDrN1VX2e7Dj4R/xSMKzhcotaTqTLTc9xL832xkgwPDFmL5MEecGuyPsw2sE4hutA==",
|
||||
"peerDependencies": {
|
||||
"vue": "^2.6.0 || ^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"minio": "^8.0.6",
|
||||
"minio-js": "^1.0.7",
|
||||
"pinia": "2.0.20",
|
||||
"uniapp-video-player": "^1.3.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "3.4.21",
|
||||
"vue-i18n": "9.14.5"
|
||||
|
||||
@@ -216,7 +216,7 @@ export function addMapForReport(data) {
|
||||
//新增请假申请
|
||||
export function addLeaveApply(data) {
|
||||
return request.post({
|
||||
url: "/crm/jys/app/appLeaveApply/add",
|
||||
url: "/crm/app/appLeaveApply/add",
|
||||
data,
|
||||
},{
|
||||
isTransformResponse:false
|
||||
@@ -226,7 +226,7 @@ export function addLeaveApply(data) {
|
||||
//我的表单
|
||||
export function getLeaveApplyList(data) {
|
||||
return request.get({
|
||||
url: "/crm/jys/app/appLeaveApply/list",
|
||||
url: "/crm/app/appLeaveApply/list",
|
||||
data
|
||||
},{
|
||||
isTransformResponse:false
|
||||
@@ -236,7 +236,7 @@ export function getLeaveApplyList(data) {
|
||||
//可销假表单
|
||||
export function getBackList(data) {
|
||||
return request.get({
|
||||
url: "/crm/jys/app/appLeaveApply/getBackList",
|
||||
url: "/crm/app/appLeaveApply/getBackList",
|
||||
data
|
||||
},{
|
||||
isTransformResponse:false
|
||||
@@ -246,7 +246,7 @@ export function getBackList(data) {
|
||||
//我的审批
|
||||
export function getMyReviewList(data) {
|
||||
return request.get({
|
||||
url: "/crm/jys/app/appLeaveApply/myReview",
|
||||
url: "/crm/app/appLeaveApply/myReview",
|
||||
data
|
||||
},{
|
||||
isTransformResponse:false
|
||||
@@ -256,7 +256,7 @@ export function getMyReviewList(data) {
|
||||
//可销假表单
|
||||
export function getBackLeaveList(data) {
|
||||
return request.get({
|
||||
url: "/crm/jys/app/appLeaveApply/myBackLeave",
|
||||
url: "/crm/app/appLeaveApply/myBackLeave",
|
||||
data
|
||||
},{
|
||||
isTransformResponse:false
|
||||
@@ -266,7 +266,7 @@ export function getBackLeaveList(data) {
|
||||
//提交审批
|
||||
export function addReviewLeave(data) {
|
||||
return request.post({
|
||||
url: "/crm/jys/app/appLeaveApply/addReview",
|
||||
url: "/crm/app/appLeaveApply/addReview",
|
||||
data,
|
||||
},{
|
||||
isTransformResponse:false
|
||||
@@ -276,7 +276,7 @@ export function addReviewLeave(data) {
|
||||
//新增销假申请
|
||||
export function addBackApply(data) {
|
||||
return request.post({
|
||||
url: "/crm/jys/app/appLeaveApply/addBackApply",
|
||||
url: "/crm/app/appLeaveApply/addBackApply",
|
||||
data,
|
||||
},{
|
||||
isTransformResponse:false
|
||||
@@ -286,7 +286,7 @@ export function addBackApply(data) {
|
||||
//可销假表单
|
||||
export function getDelLeaveApply(data) {
|
||||
return request.get({
|
||||
url: "/crm/jys/app/appLeaveApply/delLeaveApply",
|
||||
url: "/crm/app/appLeaveApply/delLeaveApply",
|
||||
data
|
||||
},{
|
||||
isTransformResponse:false
|
||||
|
||||
@@ -10,34 +10,23 @@ export function addStartMap(data) {
|
||||
});
|
||||
}
|
||||
|
||||
// //出差打卡接口
|
||||
// export function businessTripClockIn(data) {
|
||||
// return request.post({
|
||||
// url: '/crm/app/appVisistMap/businessTripClockIn',
|
||||
// data
|
||||
// },{isTransformResponse:false});
|
||||
// }
|
||||
|
||||
// //新增地图开始打卡接口
|
||||
// export function addStartMapForClockIn(data) {
|
||||
// return request.post({
|
||||
// url: "/crm/app/appVisistMap/StartaddForClockIn",
|
||||
// data,
|
||||
// },{
|
||||
// isTransformResponse:false
|
||||
// });
|
||||
// }
|
||||
|
||||
//出差打卡接口
|
||||
export function businessTripClockIn(data) {
|
||||
return request('/app/appVisistMap/businessTripClockIn', data, 'POST', 'application/json;charset=UTF-8');
|
||||
}
|
||||
|
||||
//新增地图开始打卡接口
|
||||
export function addStartMapForClockIn(data) {
|
||||
return request('/app/appVisistMap/StartaddForClockIn', data, 'POST', 'application/json;charset=UTF-8');
|
||||
}
|
||||
//出差打卡接口
|
||||
export function businessTripClockIn(data) {
|
||||
return request.post({
|
||||
url: '/crm/app/appVisistMap/businessTripClockIn',
|
||||
data
|
||||
},{isTransformResponse:false});
|
||||
}
|
||||
|
||||
//新增地图开始打卡接口
|
||||
export function addStartMapForClockIn(data) {
|
||||
return request.post({
|
||||
url: "/crm/app/appVisistMap/StartaddForClockIn",
|
||||
data,
|
||||
},{
|
||||
isTransformResponse:false
|
||||
});
|
||||
}
|
||||
|
||||
//打卡信息查看
|
||||
export function CheckInInformationViewing(data) {
|
||||
|
||||
@@ -184,7 +184,7 @@ const getCurrentMonth = ()=>{
|
||||
padding:20rpx 0 0;
|
||||
/* #endif */
|
||||
width: calc(100% / 4 - 12px); /* 减去一些间隙以避免溢出 */
|
||||
margin: 10rpx; /* 可选,用于添加间距 */
|
||||
margin: 9rpx; /* 可选,用于添加间距 */
|
||||
}
|
||||
|
||||
.month-item.active {
|
||||
|
||||
221
src/components/chooseMedia.vue
Normal file
221
src/components/chooseMedia.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<!-- 自定义底部弹窗选择器 -->
|
||||
<view class="picker-model" v-if="showPicker">
|
||||
<view class="picker-content">
|
||||
<view class="picker-item" @click="takePhoto">
|
||||
<text>拍照</text>
|
||||
</view>
|
||||
<view class="picker-item" @click="choosePhoto">
|
||||
<text>选择照片</text>
|
||||
</view>
|
||||
<view class="picker-item" @click="takeVideo">
|
||||
<text>拍摄视频</text>
|
||||
</view>
|
||||
<view class="picker-item" @click="chooseVideoFromAlbum">
|
||||
<text>选择视频</text>
|
||||
</view>
|
||||
<view class="picker-item" @click="closePicker">
|
||||
<text>取消</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import {compressImageUni} from '@/utils/common.js'
|
||||
import { minioUpload } from '@/api/polling.js'
|
||||
|
||||
let showPicker = ref(false);
|
||||
const openPicker = () => {
|
||||
showPicker.value = true;
|
||||
}
|
||||
const closePicker = () => {
|
||||
showPicker.value = false;
|
||||
emit("closeMedia")
|
||||
}
|
||||
|
||||
const takePhoto = () => {
|
||||
captureMedia("image", "camera");
|
||||
}
|
||||
|
||||
const choosePhoto = () => {
|
||||
captureMedia("image", "album");
|
||||
}
|
||||
|
||||
const takeVideo = () => {
|
||||
captureMedia("video", "camera");
|
||||
}
|
||||
|
||||
const chooseVideoFromAlbum = () => {
|
||||
captureMedia("video", "album");
|
||||
}
|
||||
|
||||
const captureMedia = async (type, source) => {
|
||||
showPicker.value = false;
|
||||
try {
|
||||
if (type === "image") {
|
||||
const res = await uni.chooseImage({
|
||||
count: 1,
|
||||
sourceType: [source],
|
||||
});
|
||||
processMedia(res.tempFilePaths, "image", source);
|
||||
} else {
|
||||
const res = await uni.chooseVideo({
|
||||
sourceType: [source],
|
||||
// sourceType: ['album', 'camera'], // 来源:相册和相机
|
||||
maxDuration: 60, // 最大时长(秒)
|
||||
camera: 'back', // 使用后置摄像头
|
||||
compressed: true, // 压缩视频
|
||||
});
|
||||
processMedia([res.tempFilePath], "video", source);
|
||||
}
|
||||
} catch (error) {
|
||||
emit("closeMedia")
|
||||
}
|
||||
}
|
||||
|
||||
// 调用父组件的方法
|
||||
const emit = defineEmits(['getMediaArr','closeMedia']);
|
||||
|
||||
// 执行压缩并上传
|
||||
const processMedia = async(files, type, source) => {
|
||||
let returnArr = [];
|
||||
let res = files.map(async file=>{
|
||||
return new Promise(async(resolve, reject) => {
|
||||
try {
|
||||
const info = {
|
||||
path: file,
|
||||
type: type,
|
||||
source: source,
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
// #ifdef APP-PLUS
|
||||
// 进行压缩图片
|
||||
if(type=="image"){
|
||||
info.path = await compressImageUni(file);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 保存选择记录
|
||||
let data = await saveSelectionRecord(info);
|
||||
returnArr.push({
|
||||
fileName:data.fileName,
|
||||
fileType:type
|
||||
})
|
||||
resolve(returnArr)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
await Promise.all(res);
|
||||
// console.log("returnArr=>",returnArr)
|
||||
|
||||
showPicker.value = false;
|
||||
emit('getMediaArr',returnArr);
|
||||
|
||||
// 根据来源做不同处理
|
||||
// if (source === "camera") {
|
||||
// handleCameraMedia(info);
|
||||
// } else {
|
||||
// handleAlbumMedia(info);
|
||||
// }
|
||||
}
|
||||
|
||||
// 保存到云
|
||||
const saveSelectionRecord = (info) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// 执行上传
|
||||
let param = {
|
||||
filePath: info.path,
|
||||
name: 'file',
|
||||
formData: {
|
||||
directory:'polling'
|
||||
},
|
||||
}
|
||||
let res = await minioUpload(param);
|
||||
resolve(res.data);
|
||||
} catch (error) {
|
||||
resolve(error);
|
||||
}
|
||||
// 保存到本地存储
|
||||
// const records = uni.getStorageSync("mediaRecords") || [];
|
||||
// records.unshift(info);
|
||||
// uni.setStorageSync("mediaRecords", records.slice(0, 10)); // 只保留最近10条
|
||||
})
|
||||
}
|
||||
|
||||
const handleCameraMedia = (info) => {
|
||||
// 从相机拍摄的媒体文件处理
|
||||
if (info.type === "image") {
|
||||
// 相机拍摄的照片可能有地理位置信息
|
||||
extractImageMetadata(info.path);
|
||||
} else {
|
||||
// 相机拍摄的视频
|
||||
uni.showToast({
|
||||
title: "已保存拍摄的视频",
|
||||
icon: "success",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const handleAlbumMedia = (info) => {
|
||||
// 从相册选择的媒体文件处理
|
||||
console.log("从相册选择:", info);
|
||||
}
|
||||
|
||||
const extractImageMetadata = (filePath) => {
|
||||
// 获取图片元数据(部分平台支持)
|
||||
uni.getImageInfo({
|
||||
src: filePath,
|
||||
success: (res) => {
|
||||
console.log("图片元数据:", res);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
openPicker
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.picker-model{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 9999;
|
||||
.picker-content{
|
||||
width:100%;
|
||||
position: absolute;
|
||||
bottom:0;
|
||||
left:0;
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx 10rpx 0 0;
|
||||
.picker-item{
|
||||
border-bottom:1px solid #E7E7E7;
|
||||
text-align: center;
|
||||
height:90rpx;
|
||||
line-height: 90rpx;
|
||||
// #ifdef APP-PLUS
|
||||
height:120rpx;
|
||||
line-height: 120rpx;
|
||||
// #endif
|
||||
&:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
text{
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<!-- 放大后的遮罩层 -->
|
||||
<view v-if="isEnlarged" class="image-preview-overlay" @click="handleClose">
|
||||
<view v-if="isEnlarged" class="image-preview-overlay">
|
||||
<view class="image-preview-close" @click="handleClose">
|
||||
<uni-icons type="closeempty" size="18" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<block v-if="getFileType(mediaUrl)=='image'">
|
||||
<image
|
||||
:src="mediaUrl"
|
||||
@@ -10,12 +13,24 @@
|
||||
</block>
|
||||
<view :class="videoClass" v-else-if="getFileType(mediaUrl)=='video'">
|
||||
<!-- object-fit="cover" -->
|
||||
<video :src="mediaUrl" controls @loadedmetadata="onVideoLoaded"></video>
|
||||
<!-- <video :src="mediaUrl" controls @loadedmetadata="onVideoLoaded"></video> -->
|
||||
<!--
|
||||
src: '' // 视频地址
|
||||
autoplay: false // 是否自动播放
|
||||
loop: false // 是否循环播放
|
||||
controls: false // 是否显示控制栏
|
||||
muted: false // 是否静音
|
||||
isLoading: false // Android系统加载时显示loading(为了遮挡安卓的黑色按钮)
|
||||
objectFit: 'contain' // 视频尺寸与video区域的适应模式
|
||||
poster: '' // 视频封面
|
||||
-->
|
||||
<DomVideoPlayer :src="mediaUrl" controls autoplay />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import DomVideoPlayer from 'uniapp-video-player'
|
||||
import { ref,watch } from 'vue';
|
||||
import {getFileType} from '@/utils/common.js';
|
||||
const props = defineProps({
|
||||
@@ -54,7 +69,7 @@ const onVideoLoaded = (e) => {
|
||||
let w = e.detail.width;
|
||||
let h = e.detail.height;
|
||||
// 你也可以在这里进行后续操作,比如根据宽高比调整UI
|
||||
console.log(w,h)
|
||||
// console.log(w,h)
|
||||
if(h>w){
|
||||
videoClass.value="enlarged-image"
|
||||
}else{
|
||||
@@ -77,6 +92,21 @@ const onVideoLoaded = (e) => {
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
.image-preview-overlay .image-preview-close{
|
||||
position: absolute;
|
||||
width:50rpx;
|
||||
height:50rpx;
|
||||
line-height: 50rpx;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
right:20rpx;
|
||||
top:20rpx;
|
||||
/* #ifdef APP-PLUS */
|
||||
top:44rpx;
|
||||
/* #endif */
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
:deep(uni-video){
|
||||
width:100%;
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />",
|
||||
"<uses-permission android:name=\"android.permission.NFC\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.nfc\" android:required=\"true\"/>",
|
||||
"<uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\"/>"
|
||||
"<uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\"/>"
|
||||
],
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a" ],
|
||||
"minSdkVersion" : 23,
|
||||
@@ -87,22 +87,22 @@
|
||||
"unipush" : {
|
||||
"version" : "2",
|
||||
"offline" : true,
|
||||
"icons" : {
|
||||
"push" : {
|
||||
"ldpi" : "/static/images/icon.png",
|
||||
"mdpi" : "/static/images/icon.png",
|
||||
"hdpi" : "/static/images/icon.png",
|
||||
"xhdpi" : "/static/images/icon.png",
|
||||
"xxhdpi" : "/static/images/icon.png"
|
||||
},
|
||||
"small" : {
|
||||
"ldpi" : "/static/images/icon_small.png",
|
||||
"mdpi" : "/static/images/icon_small.png",
|
||||
"hdpi" : "/static/images/icon_small.png",
|
||||
"xhdpi" : "/static/images/icon_small.png",
|
||||
"xxhdpi" : "/static/images/icon_small.png"
|
||||
}
|
||||
}
|
||||
"icons" : {
|
||||
"push" : {
|
||||
"ldpi" : "/static/images/icon.png",
|
||||
"mdpi" : "/static/images/icon.png",
|
||||
"hdpi" : "/static/images/icon.png",
|
||||
"xhdpi" : "/static/images/icon.png",
|
||||
"xxhdpi" : "/static/images/icon.png"
|
||||
},
|
||||
"small" : {
|
||||
"ldpi" : "/static/images/icon_small.png",
|
||||
"mdpi" : "/static/images/icon_small.png",
|
||||
"hdpi" : "/static/images/icon_small.png",
|
||||
"xhdpi" : "/static/images/icon_small.png",
|
||||
"xxhdpi" : "/static/images/icon_small.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,3 +155,5 @@
|
||||
"template" : "index.html"
|
||||
}
|
||||
}
|
||||
/* ios打包配置 */
|
||||
|
||||
|
||||
@@ -730,6 +730,28 @@
|
||||
{
|
||||
"path": "pages/business/CRM/leave/reviewDetail",
|
||||
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
//************************任务板块页面
|
||||
{
|
||||
"path": "pages/business/CRM/scheduler/index",
|
||||
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/CRM/scheduler/taskPlanAdded",
|
||||
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/CRM/scheduler/taskListViewing",
|
||||
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
|
||||
@@ -111,23 +111,17 @@ onMounted(() => {
|
||||
onShow(() => {
|
||||
uni.$on('isRefresh', function () {
|
||||
isRefresh.value = true
|
||||
getApplyList();
|
||||
getlist();
|
||||
})
|
||||
})
|
||||
|
||||
function getApplyList() {
|
||||
getlist({
|
||||
applyUserName: searchValue.value
|
||||
})
|
||||
}
|
||||
|
||||
let isRefresh = ref(false)
|
||||
|
||||
let searchValue = ref(null)
|
||||
// 查询搜索跳转
|
||||
let handleSearch = () => {
|
||||
isRefresh.value = true
|
||||
getApplyList();
|
||||
getlist();
|
||||
}
|
||||
|
||||
const pageSize = ref(4);
|
||||
@@ -162,10 +156,12 @@ let passList = ref([])
|
||||
//已审批
|
||||
let reviewList = ref([])
|
||||
|
||||
const getlist = async (param) => {
|
||||
const getlist = async () => {
|
||||
loading.value = true
|
||||
|
||||
let res = await getMyReviewList(param);
|
||||
let res = await getMyReviewList({
|
||||
applyUserName: searchValue.value
|
||||
});
|
||||
detailLists.value = res.rows;
|
||||
reviewListA.value = res.rows.filter(t => (t.status == '待审批' || t.status == '审批中') && t
|
||||
.applyType == '请假' && t.reviewerIdR == null)
|
||||
|
||||
@@ -8,12 +8,19 @@
|
||||
<view class="top-height" :style="{ paddingTop: navBarPaddingTop + 'px' }"></view>
|
||||
|
||||
<view class="inner-box">
|
||||
<view class="cu-form-group">
|
||||
<textarea v-model="form.remark" placeholder="请输入签到备注内容" name="input"></textarea>
|
||||
</view>
|
||||
<view class="btn-box">
|
||||
<view class="cu-form-group" style="height: 200px;padding:10px;">
|
||||
<uni-forms ref="formRef" :model="form" label-width="100px">
|
||||
<uni-forms-item label="签到备注内容" name="understandTheWay"
|
||||
class="uni-forms-item is-direction-top is-top">
|
||||
<uni-easyinput type="textarea" autoHeight v-model="form.remark" placeholder="请输入签到备注内容"
|
||||
class="form-texarea" />
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
|
||||
<view class="btn-box" style="padding-top: 50px;">
|
||||
<button type="primary" @click="addInsertMapClockIn">签到</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -27,20 +34,20 @@ import { parseTime } from '@/utils/datetime.js';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { getNavBarPaddingTop } from '@/utils/system.js'
|
||||
let form = reactive({
|
||||
addressForStart: null,
|
||||
addressForEnd: null,
|
||||
createId: null,
|
||||
staffName: null,
|
||||
visistCode: null,
|
||||
visistId: null,
|
||||
mapId: null,
|
||||
remark: null
|
||||
})
|
||||
addressForStart: null,
|
||||
addressForEnd: null,
|
||||
createId: null,
|
||||
staffName: null,
|
||||
visistCode: null,
|
||||
visistId: null,
|
||||
mapId: null,
|
||||
remark: null
|
||||
})
|
||||
|
||||
// 获取导航栏高度用于内容区域padding
|
||||
const navBarPaddingTop = ref(0);
|
||||
onMounted(() => {
|
||||
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
|
||||
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
|
||||
})
|
||||
|
||||
onLoad(option => {
|
||||
|
||||
@@ -1,538 +1,344 @@
|
||||
<template>
|
||||
<view class="con-body">
|
||||
<view class="content">
|
||||
<view class="pub-add-box1" @click="addInsertMapForSignIn">
|
||||
<view style="color: aliceblue; font-size: 22px;">签到</view>
|
||||
</view>
|
||||
<view class="con-bg">
|
||||
<!-- 头部 -->
|
||||
<customHeader ref="customHeaderRef" :title="'签到打卡'" :leftFlag="true" :rightFlag="false"></customHeader>
|
||||
|
||||
<view class="pub-add-box" @click="addInsertMapClockIn">
|
||||
<view style="color: aliceblue; font-size: 22px;">打卡</view>
|
||||
</view>
|
||||
<!-- 高度来避免头部遮挡 -->
|
||||
<view class="top-height"></view>
|
||||
|
||||
<!-- 自定义打卡确认弹窗 -->
|
||||
<view class="clock-in-modal" v-if="showClockInModal" @click="cancelClockIn">
|
||||
<view class="modal-content" @click.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">请选择工作状态</text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<text class="address-text">{{ clockInAddress }}</text>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<view class="btn-group">
|
||||
<button class="cancel-btn" @click="businessTripClockIn">出差</button>
|
||||
<button class="confirm-btn" @click="confirmClockIn">未出差</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 正文内容 -->
|
||||
<view class="white-bg">
|
||||
<image src="../../../../static/images/business/btn-qd.png" class="btn-image" @click="handleCheckIn"/>
|
||||
<!-- <image src="../../../../static/images/business/btn-dk.png" class="btn-image" @click="handleClockIn"/>-->
|
||||
<image src="../../../../static/images/business/btn-dk.png" class="btn-image" @click="handleClick"/>
|
||||
<view class="check-desc">
|
||||
业务人员可通过
|
||||
<text class="font-orange">签到</text>
|
||||
或
|
||||
<text
|
||||
class="font-blue">打卡
|
||||
</text>
|
||||
进行行为记录,该时间会和走访报告中的时间进行关联,便于查看。
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<closeable-modal v-model="modalVisible"
|
||||
title="确认在此处打卡?"
|
||||
:content="form.addressForStart"
|
||||
closeTip="轻触空白处关闭"
|
||||
cancelText="出差"
|
||||
confirmText="未出差"
|
||||
contentAlign="center"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="handleCancel"/>
|
||||
|
||||
<!-- 打卡遮罩层 -->
|
||||
<!-- <view class="check-con" v-if="checkFlag">
|
||||
<view class="check-in">
|
||||
<view class="check-tip">打卡</view>
|
||||
<view class="check-title">确定要在此处打卡吗?</view>
|
||||
<view class="check-location">
|
||||
<uni-icons type="location-filled" size="30" color="#0395E0"></uni-icons> 亚洲金融大厦
|
||||
</view>
|
||||
<view class="check-address">北京市朝阳区天辰东路1号院</view>
|
||||
<view class="check-footer">
|
||||
<button class="btn-default" type="default" @click="handleCancel" size="mini">取 消</button>
|
||||
<button class="btn-primary" type="primary" @click="handleSubmit" size="mini">确 定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addStartMapForClockIn, businessTripClockIn } from '@/api/crm/activity/map.js';
|
||||
<script setup>
|
||||
import {ref, onMounted, reactive} from 'vue'
|
||||
import customHeader from '@/components/customHeader.vue'
|
||||
import {addStartMapForClockIn, businessTripClockIn} from '../../../../api/crm/activity/map';
|
||||
import CloseableModal from "@/components/closeableModal.vue";
|
||||
import {MapApiConfig} from "../../../../constants/mapApiConstants";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
arrUrl: null,
|
||||
mescroll: undefined,
|
||||
// 打卡相关数据
|
||||
showClockInModal: false,
|
||||
clockInAddress: '',
|
||||
clockInData: null,
|
||||
downOption: {
|
||||
auto: false //是否在初始化后,自动执行downCallback; 默认true
|
||||
},
|
||||
upOption: {
|
||||
onScroll: true,
|
||||
use: true, // 是否启用上拉加载; 默认true
|
||||
auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
|
||||
page: {
|
||||
num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
|
||||
size: 10 // 每页数据的数量,默认10
|
||||
},
|
||||
noMoreSize: 1, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
|
||||
empty: {
|
||||
tip: '暂无相关数据'
|
||||
}
|
||||
},
|
||||
dataList: [],
|
||||
dzList: [],
|
||||
searchContent: '',
|
||||
form: {
|
||||
addressForStart: null,
|
||||
addressForEnd: null,
|
||||
createId: null,
|
||||
staffName: null,
|
||||
visistCode: null,
|
||||
visistId: null,
|
||||
mapId: null,
|
||||
}
|
||||
let form = reactive({
|
||||
addressForStart: null,
|
||||
addressForEnd: null,
|
||||
createId: null,
|
||||
staffName: null,
|
||||
visistCode: null,
|
||||
visistId: null,
|
||||
remark: null,
|
||||
mapId: null
|
||||
})
|
||||
// 签到
|
||||
let handleCheckIn = () => {
|
||||
uni.navigateTo({
|
||||
url: './addRearkSignIn'
|
||||
})
|
||||
}
|
||||
// 打卡
|
||||
// let handleClockIn = () => {
|
||||
// uni.getLocation({
|
||||
// type: 'wgs84',
|
||||
// success: (res) => {
|
||||
// const latiude = res.latitude;
|
||||
// const longitude = res.longitude;
|
||||
// //进行解析
|
||||
// inverseGeocoding(latiude, longitude);
|
||||
// },
|
||||
// fail: function (err) {
|
||||
// console.log("获取地址失败" + err)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
let modalVisible = ref(false);
|
||||
let handleClick = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: (res) => {
|
||||
const latitude = res.latitude;
|
||||
const longitude = res.longitude;
|
||||
console.log('纬度:',latitude,',经度:', longitude);
|
||||
inverseGeocoding(latitude, longitude);
|
||||
},
|
||||
fail: function (err) {
|
||||
console.log("获取地址失败" + err)
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 修改顶部导航背景色
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: '#ffffff',
|
||||
backgroundColor: '#29abe2',
|
||||
animation: {
|
||||
duration: 400,
|
||||
timingFunc: 'easeIn'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
// 反馈提示
|
||||
const feedback = (res, callback)=>{
|
||||
if(res.code===200){
|
||||
if(callback) callback();
|
||||
uni.showToast({
|
||||
title: MapApiConfig.OTHER.typeName === form.addressForStart ? MapApiConfig.OTHER.typeName : res.msg,
|
||||
icon: 'success'
|
||||
})
|
||||
let date = new Date();
|
||||
this.form.createTime = this.dateFormat("YYYY-mm-dd", date);
|
||||
},
|
||||
onShow() {
|
||||
this.arrUrl = uni.getStorageSync("avatar");
|
||||
},
|
||||
methods: {
|
||||
addInsertMap() {
|
||||
uni.navigateTo({
|
||||
url: './insertMap'
|
||||
})
|
||||
},
|
||||
//签到
|
||||
addInsertMapForSignIn() {
|
||||
setTimeout(()=>{
|
||||
uni.navigateBack(1);
|
||||
}, 500)
|
||||
}
|
||||
else{
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
})
|
||||
}
|
||||
}
|
||||
// 出差
|
||||
const handleCancel = () => {
|
||||
businessTripClockIn(form).then(res=>{
|
||||
feedback(res,()=>console.log('出差打卡成功'));
|
||||
}).catch(e=>{
|
||||
console.log(e);
|
||||
})
|
||||
}
|
||||
// 打卡
|
||||
let handleConfirm = () => {
|
||||
addStartMapForClockIn(form).then(res=>{
|
||||
feedback(res, ()=>console.log('打卡成功'));
|
||||
}).catch(e=>{
|
||||
console.log(e)
|
||||
})
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: './addRearkSignIn'
|
||||
})
|
||||
// 判定是否在范围内
|
||||
function isWithinRange(lat, lon, centerLat, centerLon, rangeKm) {
|
||||
const distance = haversineDistance(centerLat, centerLon, lat, lon);
|
||||
return distance <= rangeKm;
|
||||
}
|
||||
|
||||
},
|
||||
//打卡
|
||||
addInsertMapClockIn() {
|
||||
var that = this;
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: (res) => {
|
||||
const latiude = res.latitude;
|
||||
const longitude = res.longitude;
|
||||
//进行解析
|
||||
this.inverseGeocoding(latiude, longitude);
|
||||
},
|
||||
fail: function (err) {
|
||||
console.log("获取地址失败" + err)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 显示打卡确认弹窗
|
||||
showClockInConfirm(address, data) {
|
||||
this.clockInAddress = address;
|
||||
this.clockInData = data;
|
||||
this.showClockInModal = true;
|
||||
},
|
||||
// 取消打卡(点击遮罩层)
|
||||
cancelClockIn() {
|
||||
this.showClockInModal = false;
|
||||
this.clockInAddress = '';
|
||||
this.clockInData = null;
|
||||
},
|
||||
// 确认打卡
|
||||
confirmClockIn() {
|
||||
const that = this;
|
||||
addStartMapForClockIn(this.clockInData).then(res => {
|
||||
if (res.code == 200) {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: res.msg,
|
||||
duration: 1500,
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack(1)
|
||||
}, 500)
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg,
|
||||
});
|
||||
}
|
||||
that.cancelClockIn(); // 关闭弹窗
|
||||
})
|
||||
},
|
||||
// 出差打卡
|
||||
businessTripClockIn() {
|
||||
const that = this;
|
||||
businessTripClockIn(this.clockInData).then(res => {
|
||||
if (res.code == 200) {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: res.msg,
|
||||
duration: 1500,
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack(1)
|
||||
}, 500)
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg,
|
||||
});
|
||||
}
|
||||
that.cancelClockIn(); // 关闭弹窗
|
||||
})
|
||||
},
|
||||
isWithinRange(lat, lon, centerLat, centerLon, rangeKm) {
|
||||
const distance = this.haversineDistance(centerLat, centerLon, lat, lon);
|
||||
return distance <= rangeKm;
|
||||
},
|
||||
dateFormat(fmt, date) {
|
||||
let ret;
|
||||
const opt = {
|
||||
"Y+": date.getFullYear().toString(), // 年
|
||||
"m+": (date.getMonth() + 1).toString(), // 月
|
||||
"d+": date.getDate().toString(), // 日
|
||||
"H+": date.getHours().toString(), // 时
|
||||
"M+": date.getMinutes().toString(), // 分
|
||||
"S+": date.getSeconds().toString() // 秒
|
||||
// 有其他格式化字符需求可以继续添加,必须转化成字符串
|
||||
};
|
||||
for (let k in opt) {
|
||||
ret = new RegExp("(" + k + ")").exec(fmt);
|
||||
if (ret) {
|
||||
fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
|
||||
};
|
||||
};
|
||||
return fmt;
|
||||
},
|
||||
haversineDistance(lat1, lon1, lat2, lon2, radius = 6371) {
|
||||
const dLat = this.degToRad(lat2 - lat1);
|
||||
const dLon = this.degToRad(lon2 - lon1);
|
||||
const a =
|
||||
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(this.degToRad(lat1)) * Math.cos(this.degToRad(lat2)) *
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
return radius * c; // 距离,单位:公里
|
||||
},
|
||||
degToRad(deg) {
|
||||
return deg * (Math.PI / 180);
|
||||
},
|
||||
inverseGeocoding(latiude, longitude) {
|
||||
var that = this;
|
||||
let points = longitude + ',' + latiude
|
||||
const apiURL = 'https://tiles.geovisearth.com/geo/v1/geocode/regeo';
|
||||
const params = {
|
||||
lat: latiude,
|
||||
function haversineDistance(lat1, lon1, lat2, lon2, radius = 6371) {
|
||||
const dLat = degToRad(lat2 - lat1);
|
||||
const dLon = degToRad(lon2 - lon1);
|
||||
const a =
|
||||
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(degToRad(lat1)) * Math.cos(degToRad(lat2)) *
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
return radius * c; // 距离,单位:公里
|
||||
}
|
||||
|
||||
// 角度转弧度
|
||||
function degToRad(deg) {
|
||||
return deg * (Math.PI / 180);
|
||||
}
|
||||
|
||||
//解析地址
|
||||
function inverseGeocoding(latitude, longitude) {
|
||||
uni.showLoading();
|
||||
const apiURL = MapApiConfig.URL;
|
||||
const params = {
|
||||
lat: latitude,
|
||||
lng: longitude,
|
||||
radius: 1000,
|
||||
pageSize: 1,
|
||||
currentPage: 1,
|
||||
};
|
||||
const token = '66c87c897f0251295afdc794e4fbf73046a070338a726fe04f06cece6cb1ffdf';
|
||||
uni.request({
|
||||
//classify: 220100
|
||||
};
|
||||
const token = MapApiConfig.token;
|
||||
uni.request({
|
||||
url: apiURL,
|
||||
method: 'GET',
|
||||
data: params,
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + token
|
||||
'Authorization': 'Bearer ' + token
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode == 200 && res.data.status == 200) {
|
||||
let resdata = res.data.data.rows[0].address
|
||||
if (resdata == null) {
|
||||
const latiude1 = 34.1360;
|
||||
const longitude1 = 108.9126;
|
||||
if (this.isWithinRange(latiude1, longitude1, latiude, longitude, 1)) {
|
||||
this.form.addressForStart = "西安办事处位置打卡"
|
||||
let data = {
|
||||
mapId: that.form.mapId,
|
||||
addressForStart: that.form.addressForStart,
|
||||
cusName: that.form.cusName,
|
||||
cusId: that.form.cusId,
|
||||
remark: that.form.remark,
|
||||
path: longitude1 + "," + latiude1
|
||||
modalVisible.value=true;
|
||||
console.log(res, "经纬度解析成功")
|
||||
if (res.statusCode === 200 && res.data.status === 200) {
|
||||
let resdata = res.data.data.rows[0].address
|
||||
if (resdata == null) {
|
||||
console.log(resdata.srcLat)
|
||||
if (isWithinRange(latitude1, longitude1, latiude, longitude, 1)) {
|
||||
form.addressForStart = MapApiConfig.XI_AN_BAN.typeName;
|
||||
form.path = MapApiConfig.XI_AN_BAN.longitude + ',' + MapApiConfig.XI_AN_BAN.latitude ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
form.addressForStart = resdata;
|
||||
form.path = longitude + ',' + latitude; // 经度,纬度
|
||||
}
|
||||
that.showClockInConfirm(that.form.addressForStart, data);
|
||||
}
|
||||
} else {
|
||||
this.form.addressForStart = resdata;
|
||||
let data = {
|
||||
mapId: that.form.mapId,
|
||||
addressForStart: that.form.addressForStart,
|
||||
cusName: that.form.cusName,
|
||||
cusId: that.form.cusId,
|
||||
path: points
|
||||
}
|
||||
that.showClockInConfirm(that.form.addressForStart, data);
|
||||
form.addressForStart = MapApiConfig.OTHER.typeName;
|
||||
form.path = longitude + ',' + latitude; // 经度,纬度
|
||||
}
|
||||
} else {
|
||||
this.form.addressForStart = "第三方维护打卡"
|
||||
let data = {
|
||||
mapId: that.form.mapId,
|
||||
addressForStart: that.form.addressForStart,
|
||||
cusName: that.form.cusName,
|
||||
cusId: that.form.cusId,
|
||||
path: points
|
||||
}
|
||||
that.showClockInConfirm(that.form.addressForStart, data);
|
||||
}
|
||||
uni.hideLoading();
|
||||
},
|
||||
fail(e) {
|
||||
console.log("获取位置失败", e)
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: function (err) {
|
||||
console.log("调用失败" + err)
|
||||
}
|
||||
console.log("获取位置失败", e)
|
||||
uni.hideLoading();
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bg-green {
|
||||
background-color: #29abe2;
|
||||
<style scoped>
|
||||
.white-bg {
|
||||
width: 650rpx;
|
||||
margin: 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
padding: 50rpx;
|
||||
/* #ifdef APP-PLUS */
|
||||
height: calc(100vh - 125px);
|
||||
/* #endif */
|
||||
/* #ifndef APP-PLUS */
|
||||
height: calc(100vh - 98px);
|
||||
/* #endif */
|
||||
|
||||
|
||||
}
|
||||
|
||||
.avatar {
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
width: 50rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
margin-right: 10px;
|
||||
.btn-image {
|
||||
width: 340rpx;
|
||||
height: 340rpx;
|
||||
margin: 30rpx auto 60rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pub-add-box {
|
||||
z-index: 9999;
|
||||
width: 320upx;
|
||||
height: 320upx;
|
||||
border-radius: 50%;
|
||||
background: #13579E;
|
||||
position: fixed;
|
||||
right: 205upx;
|
||||
bottom: 200upx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0px 0px 80upx #13579E;
|
||||
|
||||
.img {
|
||||
width: 40upx;
|
||||
height: 40upx;
|
||||
}
|
||||
.check-desc {
|
||||
background-color: #F5F5F5;
|
||||
padding: 40rpx 50rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 10px;
|
||||
margin-top: 100rpx;
|
||||
}
|
||||
|
||||
.pub-add-box1 {
|
||||
z-index: 9999;
|
||||
width: 320upx;
|
||||
height: 320upx;
|
||||
border-radius: 50%;
|
||||
background: #EA8021;
|
||||
position: fixed;
|
||||
right: 205upx;
|
||||
top: 100upx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0px 0px 80upx #EA8021;
|
||||
|
||||
.img {
|
||||
width: 40upx;
|
||||
height: 40upx;
|
||||
}
|
||||
.check-desc .font-orange {
|
||||
color: #F5813A;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.avatarInner {
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
width: 50rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
margin-right: 10px;
|
||||
.check-desc .font-blue {
|
||||
color: #2CBAEF;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info-item-flex2 {
|
||||
width: 100rpx;
|
||||
}
|
||||
|
||||
.info-item1 {
|
||||
align-items: center;
|
||||
// height: 100rpx;
|
||||
}
|
||||
|
||||
.scroolView {
|
||||
width: 630rpx;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
// 筛选
|
||||
// .screen-cont{
|
||||
// height: 80upx;
|
||||
// background: #fff;
|
||||
// margin-top: 100upx;
|
||||
// }
|
||||
// 客户列表
|
||||
.cus-list {
|
||||
|
||||
background: #fff;
|
||||
|
||||
.item {
|
||||
padding: 20upx 40upx;
|
||||
border-bottom: 1px solid #f7f7f7;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.text {
|
||||
font-size: 32upx;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.btn-box {
|
||||
.deal-btn {
|
||||
width: 100upx;
|
||||
text-align: center;
|
||||
padding: 10upx 0;
|
||||
font-size: 24upx;
|
||||
border-radius: 4upx;
|
||||
|
||||
&.deal {
|
||||
background: #29abe2;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.un-deal {
|
||||
background: #EFEFEF;
|
||||
color: #BDBDBD;
|
||||
}
|
||||
}
|
||||
|
||||
.nameContent {
|
||||
font-size: 17upx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
font-size: 32upx;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.cus-info {
|
||||
.info-item {
|
||||
&.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.img-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.img {
|
||||
width: 40upx;
|
||||
height: 40upx;
|
||||
margin-left: 10upx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
font-size: 28upx;
|
||||
color: #737373;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 自定义打卡弹窗样式 */
|
||||
.clock-in-modal {
|
||||
/* 弹窗处理 */
|
||||
.check-con {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9999;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* 遮罩层内容样式 */
|
||||
.check-in {
|
||||
background-color: #fff;
|
||||
padding: 40rpx 30rpx 60rpx;
|
||||
border-radius: 10px;
|
||||
/* width: 620rpx; */
|
||||
width: 560rpx;
|
||||
position: absolute;
|
||||
top: 48%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.check-in .check-tip {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.check-in .check-title {
|
||||
font-size: 32rpx;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.check-in .check-location {
|
||||
color: #0395E0;
|
||||
font-size: 42rpx;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 30rpx 0;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 80%;
|
||||
max-width: 500rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
|
||||
.check-in .check-location .uniui-location-filled {
|
||||
font-weight: normal;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 40rpx 30rpx 20rpx;
|
||||
text-align: center;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
.check-in .check-address {
|
||||
color: #919191;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 80rpx;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 30rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.address-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 20rpx 30rpx 30rpx;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.check-in .check-footer {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.cancel-btn,
|
||||
.confirm-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
text-align: center;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
border: none;
|
||||
.check-in .check-footer .btn-default,
|
||||
.check-in .check-footer .btn-primary {
|
||||
background-color: #fff;
|
||||
border: 1px solid #05A3F4;
|
||||
color: #05A3F4;
|
||||
border-radius: 25px;
|
||||
padding: 0rpx 80rpx;
|
||||
font-size: 34rpx;
|
||||
/* margin-left: 0;
|
||||
margin-right: 20rpx; */
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
background-color: #13579E;
|
||||
.check-in .check-footer .btn-primary {
|
||||
background-color: #05A3F4;
|
||||
border: 1px solid #05A3F4;
|
||||
color: #fff;
|
||||
/* padding: 0rpx 60rpx; */
|
||||
}
|
||||
</style>
|
||||
118
src/pages/business/CRM/scheduler/index.vue
Normal file
118
src/pages/business/CRM/scheduler/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<view class="con-body">
|
||||
<view class="con-bg">
|
||||
<customHeader ref="customHeaderRef" :title="'任务版块'" :leftFlag="true">
|
||||
</customHeader>
|
||||
<!-- #ifdef H5 -->
|
||||
<view style="height:50rpx"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 高度来避免头部遮挡 -->
|
||||
<view class="top-height"></view>
|
||||
|
||||
<view class="white-bg">
|
||||
<navigator url="/pages/business/CRM/scheduler/taskListViewing">
|
||||
<view class="list-item item-padding">
|
||||
<img class="l-icon" :src="'static/images/business/icon-zfsp.png'"/>
|
||||
<text>任务清单查看</text>
|
||||
<view class="list-right">
|
||||
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</navigator>
|
||||
<view class="item-border"></view>
|
||||
<navigator url="/pages/business/CRM/scheduler/taskPlanAdded">
|
||||
<view class="list-item item-padding">
|
||||
<img class="l-icon" :src="'static/images/business/icon-khryss.png'"/>
|
||||
<text>任务计划新增</text>
|
||||
<view class="list-right">
|
||||
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</navigator>
|
||||
<view class="item-border"></view>
|
||||
<navigator url="/pages/business/CRM/plan/planView">
|
||||
<view class="list-item item-padding">
|
||||
<img class="l-icon" :src="'static/images/business/icon-jhck.png'"/>
|
||||
<text>任务计划查看</text>
|
||||
<view class="list-right">
|
||||
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, onMounted} from 'vue'
|
||||
import customHeader from '@/components/customHeader.vue'
|
||||
import {getNavBarPaddingTop} from '@/utils/system.js'
|
||||
// 获取导航栏高度用于内容区域padding
|
||||
const navBarPaddingTop = ref(0);
|
||||
onMounted(() => {
|
||||
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.head-pic .head-right img {
|
||||
display: block;
|
||||
width: 28rpx;
|
||||
height: 25rpx;
|
||||
margin-left: auto;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.backlog-b-item img {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
margin: 0 auto 10rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.white-bg {
|
||||
width: 590rpx; /*690*/
|
||||
padding: 30rpx 50rpx;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.list-item.item-padding {
|
||||
padding: 45rpx 0;
|
||||
}
|
||||
|
||||
.list-item.item-padding:first-child {
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
.list-item.item-padding:last-child {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.item-border {
|
||||
height: 1px;
|
||||
background-color: #E7E7E7;
|
||||
width: 640rpx;
|
||||
}
|
||||
|
||||
.list-item .l-icon {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.list-item .list-right {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
219
src/pages/business/CRM/scheduler/taskListViewing.vue
Normal file
219
src/pages/business/CRM/scheduler/taskListViewing.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<view class="con-body">
|
||||
<view class="con-bg">
|
||||
<!-- 头部 -->
|
||||
<customHeader ref="customHeaderRef" :title="'任务清单查看'" :leftFlag="true" :rightFlag="true">
|
||||
<template #right>
|
||||
<view class="head-right">
|
||||
|
||||
</view>
|
||||
</template>
|
||||
</customHeader>
|
||||
|
||||
<!-- 高度来避免头部遮挡 -->
|
||||
<view class="top-height" :style="{ paddingTop: navBarPaddingTop + 'px' }"></view>
|
||||
|
||||
<!-- 正文内容 -->
|
||||
<view class="all-body">
|
||||
<!-- 搜索 @blur="blur" @focus="focus" @input="input" @cancel="cancel" @clear="clear"-->
|
||||
<!-- <view class="search">
|
||||
<uni-search-bar class="custom-search" radius="28" placeholder="请输入客户名称" clearButton="auto"
|
||||
cancelButton="none" bgColor="#6FA2F8" textColor="#ffffff"
|
||||
v-model="searchValue"
|
||||
/>
|
||||
<button type="default" @click="handleSearch" size="mini" class="btn-search">查询</button>
|
||||
</view> -->
|
||||
|
||||
<!-- 分页部分 -->
|
||||
<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"
|
||||
:up="upOption" :down="downOption" :fixed="false" textColor="#ffffff" bgColor="#ffffff"
|
||||
class="scroll-h" :class="{'loading-scroll':cssFlag}"
|
||||
>
|
||||
<view class="white-bg margin-bottom20" v-for="(item, index) in list" :key="index" @click="handleDetail(item)">
|
||||
<view class="report-list">
|
||||
<view class="title">{{ item.title }}</view>
|
||||
<view class="r-list">
|
||||
<view class="r-name">{{ item.name }}</view>
|
||||
<view class="r-right btn-orange" size="mini">{{ item.statusName }}</view>
|
||||
</view>
|
||||
<view class="border-bottom"></view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">报告类型</view>
|
||||
<view class="r-right">{{ item.reportTypeName }}</view>
|
||||
</view>
|
||||
<view class="border-bottom"></view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">报告人</view>
|
||||
<view class="r-right">{{ item.reportPeople }}</view>
|
||||
</view>
|
||||
<view class="border-bottom"></view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">报告日期</view>
|
||||
<view class="r-right">{{ item.dateStr }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-uni>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import customHeader from '@/components/customHeader.vue'
|
||||
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
|
||||
import { getNavBarPaddingTop } from '@/utils/system.js'
|
||||
import { SalesManTaskList } from '@/api/crm/api_ys.js'
|
||||
|
||||
// 获取导航栏高度用于内容区域padding
|
||||
const navBarPaddingTop = ref(0);
|
||||
onMounted(() => {
|
||||
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
|
||||
})
|
||||
|
||||
let searchValue = ref(null)
|
||||
// 查询搜索跳转
|
||||
let handleSearch = () => {
|
||||
console.log(searchValue.value)
|
||||
}
|
||||
|
||||
// 新增
|
||||
let handleAdd = ()=>{
|
||||
uni.navigateTo({ url:'/pages/business/CRM/visitorReportAdd' })
|
||||
}
|
||||
|
||||
// 查询列表
|
||||
let list = ref([]);
|
||||
const mescrollRef = ref(null);
|
||||
const upOption = ref({
|
||||
page: { num: 0, size: 10 },
|
||||
noMoreSize: 5,
|
||||
empty: {
|
||||
tip: '~ 空空如也 ~',
|
||||
icon: "../../static/images/mescroll-empty.png"
|
||||
},
|
||||
textLoading: '加载中...',
|
||||
textNoMore: '已经到底了'
|
||||
});
|
||||
|
||||
const downOption = ref({
|
||||
auto: true,
|
||||
textInOffset: '下拉刷新',
|
||||
textOutOffset: '释放更新',
|
||||
textLoading: '刷新中...'
|
||||
});
|
||||
|
||||
let cssFlag=ref(false);//控制样式
|
||||
const mescrollInit = (mescroll) => {
|
||||
cssFlag.value = true;
|
||||
mescrollRef.value = mescroll;
|
||||
};
|
||||
|
||||
// 下拉刷新
|
||||
const downCallback = async (mescroll) => {
|
||||
try {
|
||||
setTimeout(async ()=>{
|
||||
const res = await getSalesManTaskList(1, upOption.value.page.size);
|
||||
cssFlag.value = false;
|
||||
list.value = res.list;
|
||||
mescroll.resetUpScroll();
|
||||
},500);
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
} finally {
|
||||
setTimeout(async ()=>{
|
||||
mescroll.endSuccess();
|
||||
},500);
|
||||
}
|
||||
}
|
||||
// 上拉加载更多
|
||||
const upCallback = async (mescroll) => {
|
||||
try {
|
||||
setTimeout(async ()=>{
|
||||
const res = await getSalesManTaskList(mescroll.num, mescroll.size);
|
||||
if (mescroll.num === 1) {
|
||||
list.value = res.list;
|
||||
} else {
|
||||
list.value.push(...res.list);
|
||||
}
|
||||
mescroll.endBySize(res.list.length, res.total);
|
||||
},500);
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据列表
|
||||
const getSalesManTaskList = (pageIndex, pageSize) => {
|
||||
return new Promise(async (resolve) => {
|
||||
let param = {
|
||||
pageIndex,
|
||||
pageSize
|
||||
}
|
||||
|
||||
let res = await SalesManTaskList(param);
|
||||
resolve({
|
||||
list: res.list,
|
||||
total: res.totalCount
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 跳转到详情
|
||||
let handleDetail=(item)=>{
|
||||
uni.navigateTo({
|
||||
url: "/pages/business/CRM/visitorReportDetail?id="+item.id
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.all-body {
|
||||
/* #ifdef APP-PLUS */
|
||||
top: 150rpx;
|
||||
height: calc(100vh - 75px);
|
||||
/* #endif */
|
||||
/* #ifndef APP-PLUS */
|
||||
top:120rpx;
|
||||
height: calc(100vh);
|
||||
/* #endif */
|
||||
}
|
||||
.search{
|
||||
display: flex;
|
||||
}
|
||||
.search .btn-search{
|
||||
border:none;
|
||||
background: none;
|
||||
line-height: normal;
|
||||
color:#fff;
|
||||
line-height: 56rpx !important;
|
||||
padding:10rpx 0 0;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.search .btn-search::after{
|
||||
display: none;
|
||||
}
|
||||
.search .custom-search{
|
||||
width:80%;
|
||||
|
||||
}
|
||||
.search .custom-search.uni-searchbar{
|
||||
padding-right:0 !important;
|
||||
}
|
||||
|
||||
.scroll-h{
|
||||
/* #ifdef APP-PLUS */
|
||||
height: calc(100vh - 120px);
|
||||
/* #endif */
|
||||
/* #ifndef APP-PLUS */
|
||||
height: calc(100vh - 110px);
|
||||
/* #endif */
|
||||
}
|
||||
.white-bg{
|
||||
padding-bottom:10rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
388
src/pages/business/CRM/scheduler/taskPlanAdded.vue
Normal file
388
src/pages/business/CRM/scheduler/taskPlanAdded.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<view class="con-body">
|
||||
<view class="con-bg">
|
||||
<!-- 头部 -->
|
||||
<customHeader ref="customHeaderRef" :title="'任务计划新增'" :leftFlag="true" :rightFlag="true">
|
||||
<template #right>
|
||||
<view class="head-right" @click="submitForm">
|
||||
<uni-icons custom-prefix="iconfont" type="icon-phonebaocun" size="22"
|
||||
color="#B7D2FF"></uni-icons>保存
|
||||
</view>
|
||||
</template>
|
||||
</customHeader>
|
||||
|
||||
<!-- 高度来避免头部遮挡 -->
|
||||
<view class="top-height"></view>
|
||||
|
||||
<!-- 正文内容 -->
|
||||
<view class="white-bg">
|
||||
<view class="form-con">
|
||||
<uni-forms ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<uni-forms-item label="标签属性" name="cusName" class="f-c-right">
|
||||
<view
|
||||
@click="chooseCustomer"
|
||||
class="form-item-container"
|
||||
>
|
||||
<text class="name">{{ formData.cusName || '点击选择主线任务' }}</text>
|
||||
</view>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="计划内容" name="opportunityType" class="f-c-right">
|
||||
<picker @change="onOpportunityTypeChange" :value="opportunityTypeIndex" :range="array"
|
||||
:range-key="'name'">
|
||||
<view class="picker">
|
||||
{{ array[opportunityTypeIndex]?.name || '请选择计划内容' }}
|
||||
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
|
||||
</view>
|
||||
</picker>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="计划开始时间" name="plannedStartTime" class="f-c-right">
|
||||
<picker mode="date" :value="formData.plannedStartTime" @change="onStartTimeChange">
|
||||
<view class="picker">
|
||||
{{ formData.plannedStartTime || '请选择计划开始时间' }}
|
||||
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
|
||||
</view>
|
||||
</picker>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="计划完成时间" name="plannedCompTime" class="f-c-right">
|
||||
<picker mode="date" :value="formData.plannedCompTime" @change="onCompTimeChange">
|
||||
<view class="picker">
|
||||
{{ formData.plannedCompTime || '请选择计划完成时间' }}
|
||||
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
|
||||
</view>
|
||||
</picker>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="内容描述" name="understandTheWay"
|
||||
class="uni-forms-item is-direction-top is-top">
|
||||
<uni-easyinput type="textarea" autoHeight v-model="formData.understandTheWay"
|
||||
placeholder="请输入内容描述" class="form-texarea" />
|
||||
</uni-forms-item>
|
||||
|
||||
</uni-forms>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref, onMounted, reactive, onUnmounted, computed
|
||||
} from 'vue'
|
||||
import customHeader from '@/components/customHeader.vue'
|
||||
import {
|
||||
getGuestList
|
||||
} from '@/api/business.js'
|
||||
import { isEmpty } from '@/utils/validate.js'
|
||||
import {crmMarketInformationAdd } from '@/api/crm/api_ys.js'
|
||||
|
||||
|
||||
let customerUser = reactive({})
|
||||
// 客户相关
|
||||
const guestList = ref([])
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
cusId: null,
|
||||
cusName: null,
|
||||
opportunityType: "", // 机会类型
|
||||
plannedStartTime: "", // 计划开始时间
|
||||
plannedCompTime: "", // 计划完成时间
|
||||
understandTheWay: "", // 了解途径
|
||||
opportunityDescription: "", // 机会描述
|
||||
opportunityStatus: "", // 机会所处状态
|
||||
predictedAmount: "", // 预测金额或情况
|
||||
competitionSituation: "", // 竞争情况
|
||||
remark: "", // 备注
|
||||
picture: "", // 图片
|
||||
informationType: "市场机会" // 信息类型
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
cusName: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请选择主线任务'
|
||||
}]
|
||||
},
|
||||
opportunityType: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请选择计划内容'
|
||||
}]
|
||||
},
|
||||
plannedStartTime: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请选择计划开始时间'
|
||||
}]
|
||||
},
|
||||
plannedCompTime: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
errorMessage: '请选择计划完成时间'
|
||||
},
|
||||
{
|
||||
validateFunction: function(rule, value, data, callback) {
|
||||
if (value && data.plannedStartTime) {
|
||||
if (new Date(value) < new Date(data.plannedStartTime)) {
|
||||
callback('计划完成时间不能早于计划开始时间')
|
||||
return
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
understandTheWay: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请输入内容描述'
|
||||
}]
|
||||
},
|
||||
opportunityDescription: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请输入机会描述'
|
||||
}]
|
||||
},
|
||||
opportunityStatus: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请输入机会所处状态'
|
||||
}]
|
||||
},
|
||||
predictedAmount: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请输入预测金额或情况'
|
||||
}]
|
||||
},
|
||||
competitionSituation: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请输入竞争情况'
|
||||
}]
|
||||
},
|
||||
}
|
||||
const imgList = ref([])
|
||||
|
||||
// picker 相关
|
||||
const index = ref(0)
|
||||
const array = ref([{
|
||||
id: 0,
|
||||
name: '日常走访'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: '业务招待'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '技术交流'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '来厂参观'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '党建活动'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '文体活动'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '联谊活动'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '礼尚往来'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '高层交流'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: '组合拳服务活动'
|
||||
}
|
||||
])
|
||||
const form = ref({
|
||||
cusId: null,
|
||||
cusName: null,
|
||||
opportunityType: array.value[0].name, // 机会类型
|
||||
understandTheWay: "", // 了解途径
|
||||
opportunityDescription: "", // 机会描述
|
||||
opportunityStatus: "", // 机会所处状态
|
||||
predictedAmount: "", // 预测金额或情况
|
||||
competitionSituation: "", // 竞争情况
|
||||
remark: "", // 备注
|
||||
picture: "", // 图片
|
||||
informationType: "市场机会" // 信息类型
|
||||
})
|
||||
|
||||
// picker 选项
|
||||
const opportunityTypeOptions = ref([{
|
||||
id: 0,
|
||||
name: '新产品需求'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: '新客户开发'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '批产任务'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '新研任务'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '二筛服务'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '对手失利'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '其它'
|
||||
}
|
||||
])
|
||||
const opportunityTypeIndex = ref(0)
|
||||
|
||||
// 表单引用 & 客户选择器引用
|
||||
const formRef = ref(null)
|
||||
const customHeaderRef = ref(null)
|
||||
// 选择客户页面跳转
|
||||
function chooseCustomer(){
|
||||
uni.navigateTo({
|
||||
url: '/pages/business/CRM/marketActivity/chooseCus'
|
||||
|
||||
})
|
||||
}
|
||||
//定义数据接收的值
|
||||
let selectedCustomer = reactive(null)
|
||||
|
||||
//监听时间
|
||||
onMounted(() => {
|
||||
// 设置机会类型的默认值
|
||||
formData.value.opportunityType = array.value[0].name;
|
||||
opportunityTypeIndex.value = 0;
|
||||
|
||||
// 原有的监听客户选择事件
|
||||
uni.$on('onCustomerSelected', handleCustomerSelected);
|
||||
})
|
||||
//取消监听
|
||||
onUnmounted(() => {
|
||||
uni.$off('onCustomerSelected', handleCustomerSelected)
|
||||
})
|
||||
|
||||
//处理 接收数据
|
||||
const handleCustomerSelected = (data) => {
|
||||
formData.value.cusName = data.cusName
|
||||
formData.value.cusId = data.cusId
|
||||
console.log("收到客户数据的值:", customerUser)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
// 表单校验
|
||||
await formRef.value.validate()
|
||||
const res = await crmMarketInformationAdd(formData.value);
|
||||
console.log(res)
|
||||
uni.showToast({
|
||||
title: '提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
uni.$emit('refreshMarketList');
|
||||
setTimeout(() => {
|
||||
uni.navigateBack(1);
|
||||
}, 1500);
|
||||
|
||||
console.log('表单数据:', formData.value)
|
||||
} catch (err) {
|
||||
console.log('表单验证失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const onOpportunityTypeChange = (e) => {
|
||||
opportunityTypeIndex.value = e.detail.value
|
||||
console.log('opportunityTypeIndex:', array.value[e.detail.value]?.name)
|
||||
formData.value.opportunityType = array.value[e.detail.value]?.name || ''
|
||||
}
|
||||
|
||||
// 计划开始时间变化事件
|
||||
const onStartTimeChange = (e) => {
|
||||
formData.value.plannedStartTime = e.detail.value
|
||||
// 如果已选择完成时间,重新校验完成时间
|
||||
if (formData.value.plannedCompTime) {
|
||||
formRef.value.validateField('plannedCompTime')
|
||||
}
|
||||
}
|
||||
|
||||
// 计划完成时间变化事件
|
||||
const onCompTimeChange = (e) => {
|
||||
formData.value.plannedCompTime = e.detail.value
|
||||
// 校验完成时间
|
||||
formRef.value.validateField('plannedCompTime')
|
||||
}
|
||||
|
||||
|
||||
// 如果你原来在 onShow 中做了类似这样:
|
||||
// let res = currPage.data.cusData; 判断是否传入了客户信息
|
||||
// 那么在 Vue3 中通常是通过路由参数或者 Vuex/Pinia 等状态管理获取
|
||||
// 暂时不做,如你后续需要可继续补充
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.white-bg {
|
||||
width: 750rpx;
|
||||
padding: 30rpx 0 0;
|
||||
margin-bottom: 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.form-con {
|
||||
/* #ifdef APP-PLUS */
|
||||
min-height: calc(100vh - 100px);
|
||||
/* #endif */
|
||||
/* #ifndef APP-PLUS */
|
||||
min-height: calc(100vh - 80px);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
:deep(.uni-date-x) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:deep(.uni-date-x .icon-calendar) {
|
||||
float: right;
|
||||
margin-top: 15rpx;
|
||||
margin-right: 20rpx;
|
||||
background: url('../../../static/images/business/icon-date.png') no-repeat;
|
||||
background-size: 32rpx 35rpx;
|
||||
width: 32rpx;
|
||||
height: 35rpx;
|
||||
}
|
||||
|
||||
:deep(.uni-date-x .icon-calendar::before) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.uni-date-x .uni-date__x-input) {
|
||||
padding-left: 20rpx;
|
||||
color: #919191;
|
||||
}
|
||||
</style>
|
||||
@@ -166,7 +166,7 @@ const downCallback = async (mescroll) => {
|
||||
setTimeout(async ()=>{
|
||||
// getTaskCount();
|
||||
getList();
|
||||
mescroll.resetUpScroll();
|
||||
// mescroll.resetUpScroll();
|
||||
},500);
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
<!-- 任务(巡检)状态 1=未发布 2=已发布 3 进行中 4 已完成 5 已过期 -->
|
||||
<!-- 状态为3进行中时 进度>0执行中 进度=0为待执行 -->
|
||||
<block v-if="item.taskStatus==3">
|
||||
<img v-if="item.groupFinishNum==0" :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-if="item.beginTime" :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
</block>
|
||||
<img v-else-if="item.taskStatus==4" :src="'static/images/polling/icon-complete.png'" class="img-complete" />
|
||||
<img v-else-if="item.taskStatus==5" :src="'static/images/polling/icon-Expired.png'" class="img-w" />
|
||||
@@ -43,7 +43,23 @@
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">开始时间<span class="r-gray">{{ parseTime(item.planTime,'{h}:{i}') }}</span></view>
|
||||
<view v-if="item.beginTime">
|
||||
执行时间<span class="r-gray">{{ parseTime(item.beginTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
<view v-else>
|
||||
开始时间<span class="r-gray">{{ parseTime(item.planTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">
|
||||
任务状态<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.beginTime">执行中</block>
|
||||
<block v-else>待执行</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
</view>
|
||||
<view class="r-l-right">任务时长<span class="r-gray">{{ item.workHour }}小时</span></view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -52,17 +68,8 @@
|
||||
<view class="r-l-left">
|
||||
完成进度<span class="r-gray"><span :class="{'r-red':item.groupFinishNum<item.groupNum}">{{item.groupFinishNum}}</span>/{{item.groupNum}}</span>
|
||||
</view>
|
||||
<view class="r-l-right">完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view>任务状态
|
||||
<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.count==0">待执行</block>
|
||||
<block v-else>执行中</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
<view class="r-l-right">
|
||||
完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -93,8 +100,26 @@
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">开始时间<span class="r-gray">{{ parseTime(item.planTime,'{h}:{i}') }}</span></view>
|
||||
<view class="r-l-right">任务时长<span class="r-gray">{{ item.workHour }}小时</span></view>
|
||||
<view v-if="item.beginTime">
|
||||
执行时间<span class="r-gray">{{ parseTime(item.beginTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
<view v-else>
|
||||
开始时间<span class="r-gray">{{ parseTime(item.planTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">
|
||||
任务状态<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.beginTime">执行中</block>
|
||||
<block v-else>待执行</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
</view>
|
||||
<view class="r-l-right">
|
||||
任务时长<span class="r-gray">{{ item.workHour }}小时</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
@@ -102,17 +127,8 @@
|
||||
<view class="r-l-left">
|
||||
完成进度<span class="r-gray"><span :class="{'r-red':item.groupFinishNum<item.groupNum}">{{item.groupFinishNum}}</span>/{{item.groupNum}}</span>
|
||||
</view>
|
||||
<view class="r-l-right">完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view>任务状态
|
||||
<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.groupFinishNum==0">待执行</block>
|
||||
<block v-else>执行中</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
<view class="r-l-right">
|
||||
完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -138,7 +154,7 @@
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left" style="width:220rpx">跟踪次数<span class="r-gray">{{ item.logNum }}</span></view>
|
||||
<view class="r-l-right">最近跟踪时间<span class="r-gray">{{ parseTime(item.lastLogTime,'{m}-{d} {h}:{i}') }}</span></view>
|
||||
<view class="r-l-right">最近跟踪时间<span class="r-gray">{{ parseTime(item.lastLogTime,'{y}-{m}-{d} {h}:{i}') }}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-border" v-if="index<list3.length-1"></view>
|
||||
@@ -154,7 +170,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref,onMounted } from 'vue'
|
||||
import { onLoad,onHide } from '@dcloudio/uni-app';
|
||||
import { onLoad,onShow,onHide } from '@dcloudio/uni-app';
|
||||
import customHeader from '@/components/customHeader.vue';
|
||||
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
|
||||
import { parseTime } from '@/utils/datetime.js';
|
||||
@@ -172,6 +188,7 @@ onLoad(option => {
|
||||
let list1 = ref([]);
|
||||
let list2 = ref([]);
|
||||
let list3 = ref([]);
|
||||
let mescroll = null; // 用于存放mescroll实例
|
||||
const mescrollRef = ref(null);
|
||||
const upOption = ref({
|
||||
use: false,
|
||||
@@ -193,15 +210,23 @@ const downOption = ref({
|
||||
});
|
||||
|
||||
let cssFlag=ref(false);//控制样式
|
||||
const mescrollInit = (mescroll) => {
|
||||
|
||||
onShow(()=>{
|
||||
if(mescroll)
|
||||
mescroll.triggerDownScroll()
|
||||
})
|
||||
|
||||
const mescrollInit = (mescrollInstance) => {
|
||||
cssFlag.value = true;
|
||||
mescroll = mescrollInstance;
|
||||
// mescroll.setMescroll(mescrollRef.value); // 绑定mescroll实例
|
||||
mescrollRef.value = mescroll;
|
||||
};
|
||||
|
||||
// 下拉刷新
|
||||
const downCallback = async (mescroll) => {
|
||||
try {
|
||||
console.log("下拉刷新")
|
||||
// console.log("下拉刷新")
|
||||
const res = await getList(1, upOption.value.page.size);
|
||||
cssFlag.value = false;
|
||||
list1.value = res.list1 || [];
|
||||
@@ -337,6 +362,7 @@ const handleJump = (item)=>{
|
||||
}
|
||||
.report-list .r-list .r-left{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.report-list .r-list .r-gray{
|
||||
margin-left:10rpx;
|
||||
@@ -352,7 +378,7 @@ const handleJump = (item)=>{
|
||||
width:525rpx
|
||||
}
|
||||
.r-left .r-l-left{
|
||||
width:280rpx;
|
||||
width:345rpx;
|
||||
}
|
||||
.r-left .r-l-right{
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</multipleSelect>
|
||||
</block>
|
||||
<!-- 判断 -->
|
||||
<block v-if="item.pointType==3">
|
||||
<block v-else-if="item.pointType==3">
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
|
||||
@@ -141,13 +141,23 @@
|
||||
<view>{{ item.pointName }}</view>
|
||||
</view>
|
||||
<view class="img-flex">
|
||||
<view class="img-show" v-for="(item2,index) in imgArr2" :key="index" @click="showMediaPreview(item2)">
|
||||
<img :src="item2.shortUrl" />
|
||||
<view class="img-show" v-for="(item2,index2) in item.imgArr2" :key="index2" @click="showMediaPreview(item2)">
|
||||
<view class="img-delete" @click.stop="handleDelete(item,index,index2)">
|
||||
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<image :src="item2.shortUrl" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="img-con" @click="chooseImage(item)">
|
||||
<view class="img-con" @click="chooseImage(item,index)">
|
||||
<img :src="'static/images/polling/icon-AddPic.png'" class="img-pic" />
|
||||
<view>添加照片</view>
|
||||
|
||||
<!-- loading -->
|
||||
<view class="upload-loading" v-if="item.loading">
|
||||
<uni-icons type="refreshempty" size="30" color="#C9C9C9"></uni-icons>
|
||||
<view>上传中....</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</block>
|
||||
<!-- 视频 -->
|
||||
@@ -157,15 +167,33 @@
|
||||
<view>{{ item.pointName }}</view>
|
||||
</view>
|
||||
<view class="img-flex">
|
||||
<view class="img-show" v-for="(item2,index) in videoArr2" :key="index" @click="showMediaPreview(item2)">
|
||||
<video :src="item2.url" controls v-show="videoShow"></video>
|
||||
<view class="img-show" v-for="(item2,index2) in item.videoArr2" :key="index2" @click="showMediaPreview(item2)">
|
||||
<view class="img-delete" @click.stop="handleDelete(item,index,index2)">
|
||||
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<view class="img-icon">
|
||||
<img :src="'static/images/polling/icon-play.png'" />
|
||||
</view>
|
||||
<!-- <video :src="item2.url" controls v-show="videoShow"></video> -->
|
||||
<DomVideoPlayer :src="item2.url" objectFit="cover" />
|
||||
</view>
|
||||
<view class="img-con" @click="chooseVideo(item)">
|
||||
<view class="img-con" @click="chooseVideo(item,index)">
|
||||
<img :src="'static/images/polling/icon-AddVideo.png'" class="img-pic" />
|
||||
<view>添加视频</view>
|
||||
<!-- loading -->
|
||||
<view class="upload-loading" v-if="item.loading">
|
||||
<uni-icons type="refreshempty" size="30" color="#C9C9C9"></uni-icons>
|
||||
<view>上传中....</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
<view class="r-left" style="width:100%">
|
||||
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
|
||||
<view>{{ item.pointName }}</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="report-border" :style="{borderColor:index<optionObj.pointList.length-1?'#E7E7E7':'#fff'}"></view>
|
||||
</view>
|
||||
<!-- 状态是4-已完成 5-已过期的不能再修改了 -->
|
||||
@@ -226,7 +254,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref,onMounted,onUnmounted,nextTick,computed,reactive,getCurrentInstance } from 'vue'
|
||||
import { onLoad,onHide} from '@dcloudio/uni-app';
|
||||
import { onLoad,onShow,onHide} from '@dcloudio/uni-app';
|
||||
import { MINIO_KEY } from '@/enums/cacheEnums';
|
||||
|
||||
import customHeader from '@/components/customHeader.vue';
|
||||
@@ -236,14 +264,16 @@ import pollingShowModal from "@/components/pollingShowModal.vue";
|
||||
import customShowModal from "@/components/customShowModal.vue"
|
||||
import mediaPreview from "@/components/mediaPreview.vue"
|
||||
import NFCTemplate from "@/components/NFCTemplate.vue"
|
||||
import DomVideoPlayer from 'uniapp-video-player'
|
||||
|
||||
import { parseTime } from '@/utils/datetime.js';
|
||||
import { formatTaskStatus } from '@/utils/status.js';
|
||||
import { taskGroupDetail,submitResult,minioUpload } from '@/api/polling.js'
|
||||
import {compressImageUni} from '@/utils/common.js'
|
||||
import {compressImageUni,getVideoFirstFrame} from '@/utils/common.js'
|
||||
// import {uploadFileMinio} from '@/utils/minio.js'
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
let imgLoading = ref(false);
|
||||
let taskId = ref(undefined);
|
||||
let groupId = ref(undefined);
|
||||
let minioObj = {};
|
||||
@@ -254,6 +284,11 @@ onLoad(option => {
|
||||
|
||||
minioObj = JSON.parse(uni.getStorageSync(MINIO_KEY) || "\{\}")
|
||||
// console.log(minioObj)
|
||||
getList();
|
||||
|
||||
})
|
||||
|
||||
onShow(()=>{
|
||||
|
||||
})
|
||||
|
||||
@@ -263,6 +298,7 @@ const mescrollInit = (mescroll) => {
|
||||
mescrollRef.value = mescroll;
|
||||
};
|
||||
const downOption = ref({
|
||||
use:false,
|
||||
auto: true,
|
||||
textInOffset: '下拉刷新',
|
||||
textOutOffset: '释放更新',
|
||||
@@ -293,34 +329,35 @@ const getList = async() => {
|
||||
let res = await taskGroupDetail(param);
|
||||
let data = res||{};
|
||||
data.pointList.forEach(item=>{
|
||||
item['imgArr']=[];
|
||||
item['imgArr2']=[];
|
||||
item['videoArr']=[];
|
||||
item['videoArr2']=[];
|
||||
item['chooseList']=[];
|
||||
item.loading=false;
|
||||
if(item.resultContent){
|
||||
if(item.pointType==2 ||item.pointType==1){
|
||||
item['chooseList'] = item.resultContent.split(",") //JSON.parse(item.resultContent)
|
||||
item['chooseList'] = item.resultContent.split(",")||[]; //JSON.parse(item.resultContent)
|
||||
}else if(item.pointType==7){
|
||||
imgArr.value=[];
|
||||
imgArr2.value=[];
|
||||
let imgList = item.resultContent?.split(",")||[];
|
||||
imgList.forEach(imgUrl=>{
|
||||
imgArr.value.push(imgUrl);
|
||||
imgArr2.value.push({
|
||||
item.imgArr.push(imgUrl);
|
||||
item.imgArr2.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+imgUrl,
|
||||
url:minioObj.minioUrl +"/"+imgUrl,
|
||||
})
|
||||
})
|
||||
}else if(item.pointType==8){
|
||||
videoArr.value=[];
|
||||
videoArr2.value=[]
|
||||
let videoList = item.resultContent?.split(",")||[];
|
||||
videoList.forEach(videoUrl=>{
|
||||
videoArr.value.push(videoUrl);
|
||||
videoArr2.value.push({
|
||||
item.videoArr.push(videoUrl);
|
||||
item.videoArr2.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+videoUrl,
|
||||
url:minioObj.minioUrl +"/"+videoUrl,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
// console.log(data.pointList)
|
||||
optionObj.value = data
|
||||
@@ -356,11 +393,10 @@ const changeCheck = (param,param2,item) => {
|
||||
}
|
||||
|
||||
// 照片及拍照处理
|
||||
const imgArr = ref([]);
|
||||
const imgArr2=ref([]);
|
||||
const chooseImage = (item) => {
|
||||
console.log("item=>",item)
|
||||
const chooseImage = (item,index) => {
|
||||
// console.log("item=>",item)
|
||||
try {
|
||||
|
||||
uni.chooseImage({
|
||||
// count: 1, // 默认是9,这里设置为1次只选1张
|
||||
// sizeType: ['compressed'], // 可以指定是原图还是压缩图,可选 'original' 或 'compressed'
|
||||
@@ -388,22 +424,27 @@ const chooseImage = (item) => {
|
||||
directory:'polling'
|
||||
},
|
||||
}
|
||||
optionObj.value.pointList[index].loading=true;
|
||||
minioUpload(param).then(res=>{
|
||||
let data = res.data;
|
||||
// console.log("444图片上传成功=>",data)
|
||||
// imgArr2.value.push(data.fileUrl)
|
||||
imgArr2.value.push({
|
||||
item.imgArr2.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
})
|
||||
imgArr.value.push(data.fileName)//传给后台的路径
|
||||
item.resultContent = imgArr.value.join(",")
|
||||
})
|
||||
item.imgArr.push(data.fileName)//传给后台的路径
|
||||
|
||||
optionObj.value.pointList[index].imgArr=[...item.imgArr]
|
||||
optionObj.value.pointList[index].imgArr2=[...item.imgArr2]
|
||||
optionObj.value.pointList[index].resultContent = item.imgArr.join(",");
|
||||
// console.log("上传成功后=>",optionObj.value.pointList[index])
|
||||
}).finally(()=>{
|
||||
optionObj.value.pointList[index].loading=false;
|
||||
})
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择图片失败', err);
|
||||
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -412,22 +453,20 @@ const chooseImage = (item) => {
|
||||
};
|
||||
|
||||
// 视频处理
|
||||
const videoSrc = ref('');
|
||||
const videoArr = ref([]);
|
||||
const videoArr2 = ref([]);
|
||||
const chooseVideo = (item) => {
|
||||
const chooseVideo = (item,index) => {
|
||||
uni.chooseVideo({
|
||||
sourceType: ['album', 'camera'], // 来源:相册和相机
|
||||
maxDuration: 60, // 最大时长(秒)
|
||||
camera: 'back', // 使用后置摄像头
|
||||
compressed: true, // 压缩视频
|
||||
success: (res) => {
|
||||
console.log("res=>",res)
|
||||
success: async (res) => {
|
||||
// console.log("res=>",res)
|
||||
// videoSrc.value=res.tempFilePath;
|
||||
// videoArr.value.push(res.tempFilePath)
|
||||
console.log('视频路径:', res.tempFilePath);
|
||||
// console.log('视频路径:', res.tempFilePath);
|
||||
// console.log('视频时长:', res.duration);
|
||||
// console.log('视频大小:', res.size);
|
||||
|
||||
// 执行上传
|
||||
let param = {
|
||||
filePath: res.tempFilePath,
|
||||
@@ -436,17 +475,23 @@ const chooseVideo = (item) => {
|
||||
directory:'polling'
|
||||
},
|
||||
}
|
||||
optionObj.value.pointList[index].loading=true;
|
||||
minioUpload(param).then(uploadRes=>{
|
||||
let data = uploadRes.data;
|
||||
videoArr.value.push(data.fileName)//传给后台的路径
|
||||
videoArr2.value.push(data.fileUrl)
|
||||
item.resultContent = videoArr.value.join(",")
|
||||
item.videoArr2.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:data.fileUrl
|
||||
});
|
||||
item.videoArr.push(data.fileName)//传给后台的路径
|
||||
|
||||
optionObj.value.pointList[index].videoArr=[...item.videoArr];
|
||||
optionObj.value.pointList[index].videoArr2=[...item.videoArr2];
|
||||
optionObj.value.pointList[index].resultContent = item.videoArr.join(",");
|
||||
// console.log(optionObj.value.pointList[index])
|
||||
}).finally(()=>{
|
||||
|
||||
optionObj.value.pointList[index].loading=false;
|
||||
})
|
||||
|
||||
},
|
||||
fail: (err) => {
|
||||
},fail: (err) => {
|
||||
console.error('选择视频失败:', err);
|
||||
}
|
||||
});
|
||||
@@ -467,6 +512,26 @@ const handlePreviewClose=()=>{
|
||||
videoShow.value = true;
|
||||
}
|
||||
|
||||
// 视频或图片删除 根据数组下标删除数组里的某个图片或视频
|
||||
const handleDelete=(item,index,index2)=>{
|
||||
let imgArr2 = optionObj.value.pointList[index].imgArr2;
|
||||
let videoArr2 = optionObj.value.pointList[index].videoArr2
|
||||
if(imgArr2.length>0){
|
||||
item.imgArr.splice(index2, 1);
|
||||
item.imgArr2.splice(index2, 1);
|
||||
optionObj.value.pointList[index].imgArr = [...item.imgArr]
|
||||
optionObj.value.pointList[index].imgArr2 = [...item.imgArr2]
|
||||
optionObj.value.pointList[index].resultContent = item.imgArr.join(",");
|
||||
}else if(videoArr2.length>0){
|
||||
item.videoArr.splice(index2, 1);
|
||||
item.videoArr2.splice(index2, 1);
|
||||
optionObj.value.pointList[index].videoArr = [...item.videoArr]
|
||||
optionObj.value.pointList[index].videoArr2 = [...item.videoArr2]
|
||||
optionObj.value.pointList[index].resultContent = item.videoArr.join(",");
|
||||
}
|
||||
// console.log("删除后=>",optionObj.value.pointList[index])
|
||||
}
|
||||
|
||||
// nfc 处理
|
||||
let nfcShow = ref(false);
|
||||
const nfcReaderRef = ref(null);
|
||||
@@ -490,9 +555,9 @@ const nfcClose = async(item) => {
|
||||
|
||||
const handleNfcData=(data)=>{
|
||||
console.log("NFC数据:", data);
|
||||
console.log("NFC数据1111:", optionObj.value.pointList,nfcIndex.value)
|
||||
// console.log("NFC数据1111:", optionObj.value.pointList,nfcIndex.value)
|
||||
optionObj.value.pointList[nfcIndex.value].resultContent = data;
|
||||
console.log("NFC数据1111:", optionObj.value.pointList[nfcIndex.value])
|
||||
// console.log("NFC数据1111:", optionObj.value.pointList[nfcIndex.value])
|
||||
// nfcShow.value = false;
|
||||
}
|
||||
|
||||
@@ -546,7 +611,7 @@ const handleSubmit=()=>{
|
||||
groupId:groupId.value,
|
||||
resultList:arr
|
||||
}
|
||||
console.log(flag)
|
||||
// console.log("submitParam=>",submitParam)
|
||||
|
||||
// 都答好了直接提交
|
||||
if(!flag){
|
||||
@@ -633,10 +698,10 @@ onUnmounted(() => {
|
||||
|
||||
.scroll-h{
|
||||
/* #ifdef APP-PLUS */
|
||||
height:calc(100vh - 78px) !important;
|
||||
height:calc(100vh - 76px) !important;
|
||||
/* #endif */
|
||||
/* #ifndef APP-PLUS */
|
||||
height: calc(100vh - 60px) !important;
|
||||
height: calc(100vh - 58px) !important;
|
||||
/* #endif */
|
||||
}
|
||||
.head-right{}
|
||||
@@ -743,11 +808,15 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
flex-flow:row wrap;
|
||||
margin-bottom:30rpx;
|
||||
/* gap:30rpx 20rpx ; */
|
||||
width:100%;
|
||||
}
|
||||
.report-list .img-show img,
|
||||
.report-list .img-show video{
|
||||
.report-list .img-show video,
|
||||
.report-list .img-show image,
|
||||
.report-list .img-show .player-wrapper{
|
||||
width:210rpx;
|
||||
height: 140rpx;
|
||||
height:140rpx;
|
||||
}
|
||||
.report-list .img-show :deep(.uni-video-cover-play-button){
|
||||
width:64rpx;
|
||||
@@ -775,6 +844,41 @@ onUnmounted(() => {
|
||||
margin:20rpx 20rpx 0 0;
|
||||
}
|
||||
|
||||
.report-list .img-show{
|
||||
position: relative;
|
||||
}
|
||||
.report-list .img-show .img-delete{
|
||||
position: absolute;
|
||||
/* #ifndef APP-PLUS */
|
||||
right:4rpx;
|
||||
/* #endif */
|
||||
/* #ifdef APP-PLUS */
|
||||
right:4rpx;
|
||||
/* #endif */
|
||||
top:4rpx;
|
||||
width:34rpx;
|
||||
height:34rpx;
|
||||
line-height:34rpx;
|
||||
border-radius:3rpx;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
text-align: center;
|
||||
z-index:1;
|
||||
}
|
||||
.report-list .img-show .img-icon{
|
||||
position: absolute;
|
||||
top:50%;
|
||||
left: 50%;
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
margin-left:-32rpx;
|
||||
margin-top:-32rpx;
|
||||
z-index:1;
|
||||
}
|
||||
.report-list .img-show .img-icon img{
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
}
|
||||
|
||||
.bg-border{
|
||||
width:750rpx;
|
||||
height:20rpx;
|
||||
|
||||
@@ -54,16 +54,20 @@
|
||||
<view class="report-border"></view>
|
||||
<view class="report-list">问题点位照片或视频</view>
|
||||
<view class="report-list">
|
||||
<block v-for="(item,index) in problemObj.listFile" :key="index">
|
||||
<view class="img-flex">
|
||||
<view class="img-show" v-if="getFileType(item)=='image'" @click="showMediaPreview(minioObj.minioUrl+'/'+item)">
|
||||
<img :src="minioObj.minioThumbUrl+'/'+item" />
|
||||
<view class="img-flex">
|
||||
<block v-for="(item2,index2) in problemObj.listFile" :key="index2">
|
||||
<view class="img-show" v-if="getFileType(item2)=='image'" @click="showMediaPreview(item2)">
|
||||
<image :src="minioObj.minioThumbUrl+'/'+item2" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="img-show" v-else-if="getFileType(item)=='video'" @click="showMediaPreview(minioObj.minioUrl+'/'+item)">
|
||||
<video :src="minioObj.minioUrl+'/'+item" controls v-show="videoShow"></video>
|
||||
<view class="img-show" v-else-if="getFileType(item2)=='video'" @click="showMediaPreview(item2)">
|
||||
<view class="img-icon">
|
||||
<img :src="'static/images/polling/icon-play.png'" />
|
||||
</view>
|
||||
<!-- <video :src="minioObj.minioUrl+'/'+item" controls v-show="videoShow"></video> -->
|
||||
<DomVideoPlayer :src="minioObj.minioUrl+'/'+item2" objectFit="cover" v-show="videoShow" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 当天提的 增加按钮 修改和删除 -->
|
||||
@@ -87,12 +91,16 @@
|
||||
<view class="report-list">检查点位照片或视频</view>
|
||||
<view class="report-list">
|
||||
<view class="img-flex">
|
||||
<block v-for="(item,index) in item.listFile" :key="index">
|
||||
<view class="img-show" v-if="getFileType(item)=='image'" @click="showMediaPreview(minioObj.minioUrl+'/'+item)">
|
||||
<img :src="minioObj.minioThumbUrl+'/'+item" />
|
||||
<block v-for="(item2,index2) in item.listFile" :key="index2">
|
||||
<view class="img-show" v-if="getFileType(item2)=='image'" @click="showMediaPreview(item2)">
|
||||
<image :src="minioObj.minioThumbUrl+'/'+item2" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="img-show" v-else-if="getFileType(item)=='video'" @click="showMediaPreview(minioObj.minioUrl+'/'+item)">
|
||||
<video :src="minioObj.minioUrl+'/'+item" controls v-show="videoShow"></video>
|
||||
<view class="img-show" v-else-if="getFileType(item2)=='video'" @click="showMediaPreview(item2)">
|
||||
<view class="img-icon">
|
||||
<img :src="'static/images/polling/icon-play.png'" />
|
||||
</view>
|
||||
<!-- <video :src="minioObj.minioUrl+'/'+item" controls v-show="videoShow"></video> -->
|
||||
<DomVideoPlayer :src="minioObj.minioUrl+'/'+item2" objectFit="cover" v-show="videoShow" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@@ -114,6 +122,7 @@ import { onLoad,onShow,onHide} from '@dcloudio/uni-app';
|
||||
import customHeader from '@/components/customHeader.vue';
|
||||
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
|
||||
import mediaPreview from "@/components/mediaPreview.vue"
|
||||
import DomVideoPlayer from 'uniapp-video-player'
|
||||
import { parseTime } from '@/utils/datetime.js';
|
||||
import {getFileType} from '@/utils/common.js';
|
||||
import { problemDetail,problemDel } from '@/api/polling.js'
|
||||
@@ -180,9 +189,10 @@ let isVisible= ref(false);//放大处理
|
||||
let mediaUrl= ref('');//放大地址
|
||||
let videoShow = ref(true);
|
||||
const showMediaPreview=(url)=>{
|
||||
url = minioObj.minioUrl+'/'+url;
|
||||
isVisible.value = true;
|
||||
videoShow.value = false;
|
||||
mediaUrl.value = url
|
||||
mediaUrl.value = url;
|
||||
}
|
||||
const handlePreviewClose=()=>{
|
||||
isVisible.value = false;
|
||||
@@ -323,6 +333,7 @@ const handleDelete=()=>{
|
||||
display: flex;
|
||||
flex-flow:row wrap;
|
||||
margin-bottom:30rpx;
|
||||
width:100%;
|
||||
}
|
||||
.report-list .img-con{
|
||||
background-color: #EEEEEE;
|
||||
@@ -342,10 +353,28 @@ const handleDelete=()=>{
|
||||
width: calc(100% / 3 - 10px); /* 减去一些间隙以避免溢出 */
|
||||
margin:20rpx 20rpx 0 0;
|
||||
}
|
||||
|
||||
.report-list .img-show{
|
||||
position: relative;
|
||||
}
|
||||
.report-list .img-show .img-icon{
|
||||
position: absolute;
|
||||
top:50%;
|
||||
left: 50%;
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
margin-left:-32rpx;
|
||||
margin-top:-32rpx;
|
||||
z-index:1;
|
||||
}
|
||||
.report-list .img-show .img-icon img{
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
}
|
||||
|
||||
.report-list .img-show img,
|
||||
.report-list .img-show video{
|
||||
.report-list .img-show video,
|
||||
.report-list .img-show image,
|
||||
.report-list .img-show .player-wrapper{
|
||||
width:210rpx;
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left" style="width:220rpx">跟踪次数<span class="r-gray">{{ item.logNum }}</span></view>
|
||||
<view class="r-l-right">最近跟踪时间<span class="r-gray">{{parseTime(item.lastLogTime,'{m}-{d} {h}:{i}') }}</span></view>
|
||||
<view class="r-l-right">最近跟踪时间<span class="r-gray">{{parseTime(item.lastLogTime,'{y}-{m}-{d} {h}:{i}') }}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-border" v-if="index<list.length-1"></view>
|
||||
@@ -152,7 +152,7 @@ const downCallback = async (mescroll) => {
|
||||
const res = await getList(1, upOption.value.page.size);
|
||||
cssFlag.value = false;
|
||||
list.value = res.list;
|
||||
mescroll.resetUpScroll();
|
||||
// mescroll.resetUpScroll();
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
} finally {
|
||||
|
||||
@@ -42,21 +42,40 @@
|
||||
<view class="report-list" style="display:block;">
|
||||
<view class="r-title">问题点位照片或视频 <text>*</text></view>
|
||||
<view class="img-flex">
|
||||
<view class="img-show" v-for="(item,index) in imgArr" :key="index">
|
||||
<img :src="item" />
|
||||
<view class="img-show" v-for="(item2,index2) in imgArr" :key="index2" @click="showMediaPreview(item2)">
|
||||
<view class="img-delete" @click.stop="handleDelete(imgArr,index2)">
|
||||
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<image :src="item2.shortUrl" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="img-show" v-for="(item,index) in videoArr" :key="index">
|
||||
<video :src="item" controls></video>
|
||||
<view class="img-show" v-for="(item2,index2) in videoArr" :key="index2" @click="showMediaPreview(item2)">
|
||||
<view class="img-delete" @click.stop="handleDelete(videoArr,index2)">
|
||||
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<view class="img-icon">
|
||||
<img :src="'static/images/polling/icon-play.png'" />
|
||||
</view>
|
||||
<!-- <video :src="item" controls></video> -->
|
||||
<DomVideoPlayer :src="item2.url" objectFit="cover" />
|
||||
</view>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="img-con" @click="chooseMedia">
|
||||
|
||||
<!-- loading -->
|
||||
<view class="img-con" v-if="imgLoading">
|
||||
<view class="upload-loading">
|
||||
<uni-icons type="refreshempty" size="30" color="#C9C9C9"></uni-icons>
|
||||
<view>上传中....</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="img-con" @click="chooseMedia" v-else>
|
||||
<img :src="'static/images/polling/icon-AddPorV.png'" class="img-pic" />
|
||||
</view>
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<view class="img-con">
|
||||
<!-- <view class="img-con">
|
||||
<img :src="'static/images/polling/icon-AddPorV.png'" class="img-pic" />
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
@@ -66,34 +85,47 @@
|
||||
</view>
|
||||
</mescroll-uni>
|
||||
</view>
|
||||
|
||||
<!-- 图片放大 -->
|
||||
<mediaPreview :visible="isVisible" :url="mediaUrl" @close="handlePreviewClose"></mediaPreview>
|
||||
|
||||
<!-- 选择图片或者视频 -->
|
||||
<chooseMediaVue ref="chooseMediaRef" @getMediaArr="getMediaArr" @closeMedia="closeMedia"></chooseMediaVue>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref,onMounted,onUnmounted,nextTick,computed,reactive } from 'vue'
|
||||
import { ref,onMounted,onUnmounted,nextTick,computed,reactive,getCurrentInstance } from 'vue'
|
||||
import { onLoad,onHide, onShow} from '@dcloudio/uni-app';
|
||||
import customHeader from '@/components/customHeader.vue';
|
||||
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
|
||||
import mediaPreview from "@/components/mediaPreview.vue"
|
||||
import chooseMediaVue from '@/components/chooseMedia.vue'
|
||||
import DomVideoPlayer from 'uniapp-video-player'
|
||||
import { getUserInfo } from '@/api/auth.js'
|
||||
import { problemDetail,problemAddLog,minioUpload } from '@/api/polling.js'
|
||||
import { MINIO_KEY } from '@/enums/cacheEnums';
|
||||
import {showAlert,showLoading,hideLoading} from '@/utils/message.js'
|
||||
import {compressImageUni} from '@/utils/common.js'
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
let problemId = ref('');
|
||||
let realname = ref('');
|
||||
let desc = ref('');//描述
|
||||
let minioObj = {};
|
||||
onLoad(async option => {
|
||||
// console.log(option)
|
||||
problemId.value = option.problemId;
|
||||
|
||||
minioObj = JSON.parse(uni.getStorageSync(MINIO_KEY) || "\{\}")
|
||||
let userinfo = await getUserInfo({});
|
||||
realname.value = userinfo.realname
|
||||
})
|
||||
|
||||
onShow(()=>{
|
||||
getList();
|
||||
})
|
||||
onShow(()=>{
|
||||
imgLoading.value=false;
|
||||
})
|
||||
|
||||
// 查询列表
|
||||
let list = ref([]);
|
||||
@@ -105,6 +137,7 @@ const mescrollInit = (mescroll) => {
|
||||
mescrollRef.value = mescroll;
|
||||
};
|
||||
const downOption = ref({
|
||||
use:false,
|
||||
auto: false,
|
||||
textInOffset: '下拉刷新',
|
||||
textOutOffset: '释放更新',
|
||||
@@ -133,8 +166,11 @@ const getList = async () => {
|
||||
let mediaArr = ref([]);//传给后台的地址
|
||||
let imgArr=ref([]);//图片 后台返回的
|
||||
let videoArr = ref([]);//视频 后台返回的
|
||||
let imgLoading = ref(false)
|
||||
const chooseMedia = () => {
|
||||
uni.chooseMedia({
|
||||
proxy.$refs["chooseMediaRef"].openPicker();
|
||||
imgLoading.value=true;
|
||||
/* uni.chooseMedia({
|
||||
count: 9,
|
||||
mediaType: ['image', 'video'], // 指定可选择图片和视频
|
||||
sourceType: ['album', 'camera'],
|
||||
@@ -160,18 +196,85 @@ const chooseMedia = () => {
|
||||
directory:'polling'
|
||||
},
|
||||
}
|
||||
imgLoading.value=true;
|
||||
minioUpload(param).then(res=>{
|
||||
let data = res.data;
|
||||
mediaArr.value.push(data.fileName);
|
||||
if (file.fileType === 'image') {// 图片
|
||||
imgArr.value.push(data.fileUrl)
|
||||
} else if (file.type === 'video') {// 视频
|
||||
videoArr.value.push(data.fileUrl)
|
||||
imgArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
name:data.fileName
|
||||
})
|
||||
} else if (file.fileType === 'video') {// 视频
|
||||
videoArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
name:data.fileName
|
||||
})
|
||||
}
|
||||
}).finally(()=>{
|
||||
imgLoading.value=false;
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
const closeMedia = ()=>{
|
||||
imgLoading.value=false;
|
||||
}
|
||||
|
||||
// 插件回调
|
||||
const getMediaArr=(arr)=>{
|
||||
// console.log("插件回调=>",arr)
|
||||
try {
|
||||
arr.forEach(data=>{
|
||||
mediaArr.value.push(data.fileName);
|
||||
if(data.fileType=="image"){
|
||||
imgArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
name:data.fileName
|
||||
})
|
||||
}else if(data.fileType == "video"){
|
||||
videoArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
name:data.fileName
|
||||
})
|
||||
}
|
||||
})
|
||||
// console.log(imgArr.value,videoArr.value)
|
||||
imgLoading.value=false;
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
imgLoading.value=false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 视频或图片删除 根据数组下标删除数组里的某个图片或视频
|
||||
const handleDelete=(arr,index2)=>{
|
||||
arr.splice(index2, 1);
|
||||
let item = arr[index2];
|
||||
mediaArr.value = mediaArr.value.filter(item2=>item2!=item.name);
|
||||
// console.log("删除后=>",mediaArr.value)
|
||||
}
|
||||
|
||||
// 放大视频或图片
|
||||
let isVisible= ref(false);//放大处理
|
||||
let mediaUrl= ref('');//放大地址
|
||||
let videoShow = ref(true);
|
||||
const showMediaPreview=(item)=>{
|
||||
// console.log("调用放大视频==>",item)
|
||||
isVisible.value = true;
|
||||
videoShow.value = false;
|
||||
mediaUrl.value = item.url
|
||||
}
|
||||
const handlePreviewClose=()=>{
|
||||
isVisible.value = false;
|
||||
videoShow.value = true;
|
||||
}
|
||||
|
||||
// 提交
|
||||
@@ -181,7 +284,7 @@ const handleSubmit=()=>{
|
||||
logVedio:mediaArr.value.join(","),
|
||||
logDesc:desc.value
|
||||
}
|
||||
console.log("problemAddLog=>",param)
|
||||
// console.log("problemAddLog=>",param)
|
||||
problemAddLog(param).then(res=>{
|
||||
showAlert("新建问题跟踪成功!");
|
||||
uni.navigateBack();
|
||||
@@ -311,6 +414,27 @@ const handleSubmit=()=>{
|
||||
padding-top:5rpx;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.report-list .img-flex{
|
||||
display: flex;
|
||||
flex-flow:row wrap;
|
||||
margin-bottom:30rpx;
|
||||
width:100%;
|
||||
}
|
||||
.report-list .img-show img,
|
||||
.report-list .img-show image,
|
||||
.report-list .img-show video,
|
||||
.report-list .img-show .player-wrapper{
|
||||
width:210rpx;
|
||||
height: 140rpx;
|
||||
}
|
||||
.report-list .img-show :deep(.uni-video-cover-play-button){
|
||||
width:64rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
font-size:60rpx;
|
||||
}
|
||||
|
||||
.report-list .img-con{
|
||||
background-color: #EEEEEE;
|
||||
width:210rpx;
|
||||
@@ -335,6 +459,41 @@ const handleSubmit=()=>{
|
||||
margin-left:0;
|
||||
}
|
||||
|
||||
.report-list .img-show{
|
||||
position: relative;
|
||||
}
|
||||
.report-list .img-show .img-delete{
|
||||
position: absolute;
|
||||
/* #ifndef APP-PLUS */
|
||||
right:4rpx;
|
||||
/* #endif */
|
||||
/* #ifdef APP-PLUS */
|
||||
right:4rpx;
|
||||
/* #endif */
|
||||
top:8rpx;
|
||||
width:34rpx;
|
||||
height:34rpx;
|
||||
line-height:34rpx;
|
||||
border-radius:3rpx;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
text-align: center;
|
||||
z-index:1;
|
||||
}
|
||||
.report-list .img-show .img-icon{
|
||||
position: absolute;
|
||||
top:50%;
|
||||
left: 50%;
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
margin-left:-32rpx;
|
||||
margin-top:-32rpx;
|
||||
z-index:1;
|
||||
}
|
||||
.report-list .img-show .img-icon img{
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
}
|
||||
|
||||
.btn-submit{
|
||||
width:400rpx;
|
||||
margin:90rpx auto;
|
||||
|
||||
@@ -45,21 +45,40 @@
|
||||
<view class="report-list" style="display:block;">
|
||||
<view class="r-title">问题点位照片或视频 <text>*</text></view>
|
||||
<view class="img-flex">
|
||||
<view class="img-show" v-for="(item,index) in imgArr" :key="index" @click="showMediaPreview(item)">
|
||||
<img :src="item.shortUrl" />
|
||||
<view class="img-show" v-for="(item2,index2) in imgArr" :key="index2" @click="showMediaPreview(item2)">
|
||||
<view class="img-delete" @click.stop="handleDelete(imgArr,index2)">
|
||||
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<!-- <img :src="item2.shortUrl" /> -->
|
||||
<image :src="item2.shortUrl" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="img-show" v-for="(item,index) in videoArr" :key="index" @click="showMediaPreview(item)">
|
||||
<video :src="item.url" controls></video>
|
||||
<view class="img-show" v-for="(item2,index2) in videoArr" :key="index2" @click="showMediaPreview(item2)">
|
||||
<view class="img-delete" @click.stop="handleDelete(videoArr,index2)">
|
||||
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<view class="img-icon">
|
||||
<img :src="'static/images/polling/icon-play.png'" />
|
||||
</view>
|
||||
<!-- <video :src="item.url" controls></video> -->
|
||||
<DomVideoPlayer :src="item2.url" objectFit="cover" />
|
||||
</view>
|
||||
|
||||
<!-- loading -->
|
||||
<view class="img-con" v-if="imgLoading">
|
||||
<view class="upload-loading">
|
||||
<uni-icons type="refreshempty" size="30" color="#C9C9C9"></uni-icons>
|
||||
<view>上传中....</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="img-con" @click="chooseMedia" v-else>
|
||||
<img :src="'static/images/polling/icon-AddPorV.png'" class="img-pic" />
|
||||
</view>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="img-con" @click="chooseMedia">
|
||||
<img :src="'static/images/polling/icon-AddPorV.png'" class="img-pic" />
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<view class="img-con">
|
||||
<!-- <view class="img-con">
|
||||
<img :src="'static/images/polling/icon-AddPorV.png'" class="img-pic" />
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
@@ -72,20 +91,26 @@
|
||||
|
||||
<!-- 图片放大 -->
|
||||
<mediaPreview :visible="isVisible" :url="mediaUrl" @close="handlePreviewClose"></mediaPreview>
|
||||
|
||||
<!-- 选择图片或者视频 -->
|
||||
<chooseMediaVue ref="chooseMediaRef" @getMediaArr="getMediaArr" @closeMedia="closeMedia"></chooseMediaVue>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref,onMounted,onUnmounted,nextTick,computed,reactive, watch } from 'vue'
|
||||
import { onLoad,onHide} from '@dcloudio/uni-app';
|
||||
import { ref,onMounted,onUnmounted,nextTick,computed,reactive, watch,getCurrentInstance } from 'vue'
|
||||
import { onLoad,onShow,onHide} from '@dcloudio/uni-app';
|
||||
import customHeader from '@/components/customHeader.vue';
|
||||
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
|
||||
import chooseMediaVue from '@/components/chooseMedia.vue'
|
||||
import mediaPreview from "@/components/mediaPreview.vue"
|
||||
import DomVideoPlayer from 'uniapp-video-player'
|
||||
import { getUserInfo } from '@/api/auth.js'
|
||||
import { problemDetail,problemAdd,problemEdit,minioUpload } from '@/api/polling.js'
|
||||
import {compressImageUni,getFileType} from '@/utils/common.js'
|
||||
import {showAlert,showLoading,hideLoading} from '@/utils/message.js'
|
||||
import { MINIO_KEY } from '@/enums/cacheEnums';
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
let taskId = ref('');//任务id
|
||||
let groupId = ref('');//组id
|
||||
@@ -102,7 +127,12 @@ onLoad(async option => {
|
||||
minioObj = JSON.parse(uni.getStorageSync(MINIO_KEY) || "\{\}")
|
||||
|
||||
let userinfo = await getUserInfo({});
|
||||
realname.value = userinfo.realname
|
||||
realname.value = userinfo.realname;
|
||||
getList();
|
||||
})
|
||||
|
||||
onShow(()=>{
|
||||
imgLoading.value=false;
|
||||
})
|
||||
|
||||
// 查询列表
|
||||
@@ -115,6 +145,7 @@ const mescrollInit = (mescroll) => {
|
||||
mescrollRef.value = mescroll;
|
||||
};
|
||||
const downOption = ref({
|
||||
use:false,
|
||||
auto: true,
|
||||
textInOffset: '下拉刷新',
|
||||
textOutOffset: '释放更新',
|
||||
@@ -163,11 +194,13 @@ const getList = async () => {
|
||||
imgArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+item,
|
||||
url:minioObj.minioUrl +"/"+item,
|
||||
name:item
|
||||
})
|
||||
}else if(getFileType(item)=='video'){
|
||||
videoArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+item,
|
||||
url:minioObj.minioUrl +"/"+item,
|
||||
name:item
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -178,56 +211,102 @@ const getList = async () => {
|
||||
let mediaArr = ref([]);//传给后台的地址
|
||||
let imgArr=ref([]);//图片 后台返回的
|
||||
let videoArr = ref([]);//视频 后台返回的
|
||||
let imgLoading = ref(false)
|
||||
const chooseMedia = () => {
|
||||
uni.chooseMedia({
|
||||
count: 9,
|
||||
mediaType: ['image', 'video'], // 指定可选择图片和视频
|
||||
sourceType: ['album', 'camera'],
|
||||
maxDuration: 60, // 拍摄视频最长拍摄时间
|
||||
camera: 'back',
|
||||
success: (res) => {
|
||||
console.log("chooseMedia=>",res)
|
||||
res.tempFiles.forEach(async file => {
|
||||
// tempFilePath
|
||||
console.log(`文件类型: ${file.fileType}, 文件路径: ${file.tempFilePath}`);
|
||||
let compressImg = file.tempFilePath;
|
||||
// 图片进行压缩
|
||||
if (file.fileType === 'image') {
|
||||
// #ifdef APP-PLUS
|
||||
// 压缩图片
|
||||
compressImg = await compressImageUni(file.tempFilePath);
|
||||
// #endif
|
||||
}
|
||||
// console.log("chooseMedia=>")
|
||||
proxy.$refs["chooseMediaRef"].openPicker();
|
||||
imgLoading.value=true;
|
||||
// uni.chooseMedia({
|
||||
// count: 9,
|
||||
// mediaType: ['image', 'video'], // 指定可选择图片和视频
|
||||
// sourceType: ['album', 'camera'],
|
||||
// maxDuration: 60, // 拍摄视频最长拍摄时间
|
||||
// camera: 'back',
|
||||
// success: (res) => {
|
||||
// // console.log("chooseMedia=>",res)
|
||||
// res.tempFiles.forEach(async file => {
|
||||
// // console.log(`文件类型: ${file.fileType}, 文件路径: ${file.tempFilePath}`);
|
||||
// let compressImg = file.tempFilePath;
|
||||
// // 图片进行压缩
|
||||
// if (file.fileType === 'image') {
|
||||
// // #ifdef APP-PLUS
|
||||
// // 压缩图片
|
||||
// compressImg = await compressImageUni(file.tempFilePath);
|
||||
// // #endif
|
||||
// }
|
||||
|
||||
// 执行上传
|
||||
let param = {
|
||||
filePath: compressImg,
|
||||
name: 'file',
|
||||
formData: {
|
||||
directory:'polling'
|
||||
},
|
||||
}
|
||||
minioUpload(param).then(res=>{
|
||||
let data = res.data;
|
||||
mediaArr.value.push(data.fileName);
|
||||
if (file.fileType === 'image') {// 图片
|
||||
// imgArr.value.push(data.fileUrl)
|
||||
imgArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
})
|
||||
} else if (file.type === 'video') {// 视频
|
||||
// videoArr.value.push(data.fileUrl)
|
||||
videoArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
})
|
||||
}
|
||||
// // 执行上传
|
||||
// let param = {
|
||||
// filePath: compressImg,
|
||||
// name: 'file',
|
||||
// formData: {
|
||||
// directory:'polling'
|
||||
// },
|
||||
// }
|
||||
// imgLoading.value=true;
|
||||
// minioUpload(param).then(res=>{
|
||||
// let data = res.data;//console.log("上传成功后=>",res)
|
||||
// mediaArr.value.push(data.fileName);
|
||||
// if (file.fileType === 'image') {// 图片
|
||||
// imgArr.value.push({
|
||||
// shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
// url:minioObj.minioUrl +"/"+data.fileName,
|
||||
// name:data.fileName
|
||||
// })
|
||||
// } else if (file.fileType === 'video') {// 视频
|
||||
// videoArr.value.push({
|
||||
// shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
// url:minioObj.minioUrl +"/"+data.fileName,
|
||||
// name:data.fileName
|
||||
// })
|
||||
// }
|
||||
// }).finally(()=>{
|
||||
// imgLoading.value=false;
|
||||
// })
|
||||
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
const closeMedia = ()=>{
|
||||
imgLoading.value=false;
|
||||
}
|
||||
|
||||
// 插件回调
|
||||
const getMediaArr=(arr)=>{
|
||||
// console.log("插件回调=>",arr)
|
||||
try {
|
||||
arr.forEach(data=>{
|
||||
mediaArr.value.push(data.fileName);
|
||||
if(data.fileType=="image"){
|
||||
imgArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
name:data.fileName
|
||||
})
|
||||
}else if(data.fileType == "video"){
|
||||
videoArr.value.push({
|
||||
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
|
||||
url:minioObj.minioUrl +"/"+data.fileName,
|
||||
name:data.fileName
|
||||
})
|
||||
}
|
||||
})
|
||||
// console.log(imgArr.value,videoArr.value)
|
||||
imgLoading.value=false;
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
imgLoading.value=false;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 视频或图片删除 根据数组下标删除数组里的某个图片或视频
|
||||
const handleDelete=(arr,index2)=>{
|
||||
let item = arr[index2];
|
||||
arr.splice(index2, 1);
|
||||
mediaArr.value = mediaArr.value.filter(item2=>item2!=item.name);
|
||||
console.log("删除后=>",mediaArr.value)
|
||||
}
|
||||
|
||||
// 放大视频或图片
|
||||
@@ -235,6 +314,7 @@ let isVisible= ref(false);//放大处理
|
||||
let mediaUrl= ref('');//放大地址
|
||||
let videoShow = ref(true);
|
||||
const showMediaPreview=(item)=>{
|
||||
// console.log("调用放大视频==>",item)
|
||||
isVisible.value = true;
|
||||
videoShow.value = false;
|
||||
mediaUrl.value = item.url
|
||||
@@ -269,7 +349,10 @@ const handleSubmit=()=>{
|
||||
}
|
||||
// console.log("problemEdit=>",param)
|
||||
problemEdit(param).then(res=>{
|
||||
showAlert("修改问题上报成功!")
|
||||
// showAlert = (content, title = '提示',showCancel=false,succFun)
|
||||
showAlert("修改问题上报成功!","提示",false,()=>{
|
||||
uni.navigateBack()
|
||||
})
|
||||
}).finally(()=>{
|
||||
hideLoading();
|
||||
})
|
||||
@@ -283,8 +366,10 @@ const handleSubmit=()=>{
|
||||
}
|
||||
// console.log("problemAdd=>",param)
|
||||
problemAdd(param).then(res=>{
|
||||
showAlert("新建问题上报成功!");
|
||||
problemId.value = res;
|
||||
showAlert("新建问题上报成功!","提示",false,()=>{
|
||||
problemId.value = res;
|
||||
uni.navigateBack()
|
||||
})
|
||||
}).finally(()=>{
|
||||
hideLoading();
|
||||
})
|
||||
@@ -419,9 +504,12 @@ const handleSubmit=()=>{
|
||||
display: flex;
|
||||
flex-flow:row wrap;
|
||||
margin-bottom:30rpx;
|
||||
width:100%;
|
||||
}
|
||||
.report-list .img-show img,
|
||||
.report-list .img-show video{
|
||||
.report-list .img-show image,
|
||||
.report-list .img-show video,
|
||||
.report-list .img-show .player-wrapper{
|
||||
width:210rpx;
|
||||
height: 140rpx;
|
||||
}
|
||||
@@ -450,6 +538,40 @@ const handleSubmit=()=>{
|
||||
width: calc(100% / 3 - 10px); /* 减去一些间隙以避免溢出 */
|
||||
margin:20rpx 20rpx 0 0;
|
||||
}
|
||||
.report-list .img-show{
|
||||
position: relative;
|
||||
}
|
||||
.report-list .img-show .img-delete{
|
||||
position: absolute;
|
||||
/* #ifndef APP-PLUS */
|
||||
right:4rpx;
|
||||
/* #endif */
|
||||
/* #ifdef APP-PLUS */
|
||||
right:4rpx;
|
||||
/* #endif */
|
||||
top:8rpx;
|
||||
width:34rpx;
|
||||
height:34rpx;
|
||||
line-height:34rpx;
|
||||
border-radius:3rpx;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
text-align: center;
|
||||
z-index:1;
|
||||
}
|
||||
.report-list .img-show .img-icon{
|
||||
position: absolute;
|
||||
top:50%;
|
||||
left: 50%;
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
margin-left:-32rpx;
|
||||
margin-top:-32rpx;
|
||||
z-index:1;
|
||||
}
|
||||
.report-list .img-show .img-icon img{
|
||||
width: 64rpx;
|
||||
height:64rpx;
|
||||
}
|
||||
|
||||
|
||||
.btn-submit{
|
||||
|
||||
@@ -79,8 +79,8 @@
|
||||
<!-- 任务(巡检)状态 1=未发布 2=已发布 3 进行中 4 已完成 5 已过期 -->
|
||||
<!-- 状态为3进行中时 进度>0执行中 进度=0为待执行 -->
|
||||
<block v-if="item.taskStatus==3">
|
||||
<img v-if="item.groupFinishNum==0" :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-if="item.beginTime" :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
</block>
|
||||
<img v-else-if="item.taskStatus==4" :src="'static/images/polling/icon-complete.png'" class="img-complete" />
|
||||
<img v-else-if="item.taskStatus==5" :src="'static/images/polling/icon-Expired.png'" class="img-w" />
|
||||
@@ -91,8 +91,26 @@
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">开始时间<span class="r-gray">{{ parseTime(item.planTime,'{h}:{i}') }}</span></view>
|
||||
<view class="r-l-right">任务时长<span class="r-gray">{{ item.workHour }}小时</span></view>
|
||||
<view v-if="item.beginTime">
|
||||
执行时间<span class="r-gray">{{ parseTime(item.beginTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
<view v-else>
|
||||
开始时间<span class="r-gray">{{ parseTime(item.planTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">
|
||||
任务状态<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.beginTime">执行中</block>
|
||||
<block v-else>待执行</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
</view>
|
||||
<view class="r-l-right">
|
||||
任务时长<span class="r-gray">{{ item.workHour }}小时</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
@@ -100,20 +118,12 @@
|
||||
<view class="r-l-left">
|
||||
完成进度<span class="r-gray"><span :class="{'r-red':item.groupFinishNum<item.groupNum}">{{item.groupFinishNum}}</span>/{{item.groupNum}}</span>
|
||||
</view>
|
||||
<view class="r-l-right">完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view>任务状态
|
||||
<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.groupFinishNum==0">待执行</block>
|
||||
<block v-else>执行中</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
<view class="r-l-right">
|
||||
完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="report-border" v-if="index<row.list1.length-1"></view>
|
||||
</view>
|
||||
</block>
|
||||
@@ -136,8 +146,8 @@
|
||||
<!-- 任务(巡检)状态 1=未发布 2=已发布 3 进行中 4 已完成 5 已过期 -->
|
||||
<!-- 状态为3进行中时 进度>0执行中 进度=0为待执行 -->
|
||||
<block v-if="item.taskStatus==3">
|
||||
<img v-if="item.groupFinishNum==0" :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-if="item.beginTime" :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
</block>
|
||||
<img v-else-if="item.taskStatus==4" :src="'static/images/polling/icon-complete.png'" class="img-complete" />
|
||||
<img v-else-if="item.taskStatus==5" :src="'static/images/polling/icon-Expired.png'" class="img-w" />
|
||||
@@ -148,8 +158,26 @@
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">开始时间<span class="r-gray">{{ parseTime(item.planTime,'{h}:{i}') }}</span></view>
|
||||
<view class="r-l-right">任务时长<span class="r-gray">{{ item.workHour }}小时</span></view>
|
||||
<view v-if="item.beginTime">
|
||||
执行时间<span class="r-gray">{{ parseTime(item.beginTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
<view v-else>
|
||||
开始时间<span class="r-gray">{{ parseTime(item.planTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">
|
||||
任务状态<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.beginTime">执行中</block>
|
||||
<block v-else>待执行</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
</view>
|
||||
<view class="r-l-right">
|
||||
任务时长<span class="r-gray">{{ item.workHour }}小时</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
@@ -157,17 +185,8 @@
|
||||
<view class="r-l-left">
|
||||
完成进度<span class="r-gray"><span :class="{'r-red':item.groupFinishNum<item.groupNum}">{{item.groupFinishNum}}</span>/{{item.groupNum}}</span>
|
||||
</view>
|
||||
<view class="r-l-right">完成比率<span class="r-blue">{{item.percentage}}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view>任务状态
|
||||
<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.groupFinishNum==0">待执行</block>
|
||||
<block v-else>执行中</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
<view class="r-l-right">
|
||||
完成比率<span class="r-blue">{{item.percentage}}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -200,7 +219,7 @@
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left" style="width:220rpx">跟踪次数<span class="r-gray">{{ item.logNum }}</span></view>
|
||||
<view class="r-l-right">最近跟踪时间<span class="r-gray">{{ parseTime(item.lastLogTime,'{m}-{d} {h}:{i}') }}</span></view>
|
||||
<view class="r-l-right">最近跟踪时间<span class="r-gray">{{ parseTime(item.lastLogTime,'{y}-{m}-{d} {h}:{i}') }}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-border" v-if="index<row.list3.length-1"></view>
|
||||
@@ -337,12 +356,12 @@ const scrollToPosition = () => {
|
||||
// 下拉刷新
|
||||
const downCallback = async (mescroll) => {
|
||||
try {
|
||||
console.log("下拉刷新")
|
||||
// console.log("下拉刷新")
|
||||
isMonth.value=true;
|
||||
const res = await getList(1, upOption.value.page.size);
|
||||
cssFlag.value = false;
|
||||
list.value = res.list;
|
||||
mescroll.resetUpScroll();
|
||||
// mescroll.resetUpScroll();
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
} finally {
|
||||
@@ -354,7 +373,7 @@ const downCallback = async (mescroll) => {
|
||||
// 上拉加载更多
|
||||
const upCallback = async (mescroll) => {
|
||||
try {
|
||||
console.log("上拉加载更多")
|
||||
// console.log("上拉加载更多")
|
||||
let res = await getList(mescroll.num, mescroll.size);
|
||||
if (mescroll.num === 1) {
|
||||
list.value = res.list;
|
||||
@@ -633,6 +652,7 @@ const handleJump = (item)=>{
|
||||
}
|
||||
.report-list .r-list .r-left{
|
||||
display: flex;
|
||||
align-items:center;
|
||||
}
|
||||
.report-list .r-list .r-gray{
|
||||
margin-left:10rpx;
|
||||
|
||||
@@ -173,7 +173,7 @@ const downCallback = async (mescroll) => {
|
||||
const res = await getList(1, upOption.value.page.size);
|
||||
cssFlag.value = false;
|
||||
taskObj.value = res;
|
||||
mescroll.resetUpScroll();
|
||||
// mescroll.resetUpScroll();
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
} finally {
|
||||
@@ -205,160 +205,8 @@ const getList = (pageIndex, pageSize) => {
|
||||
taskId:taskId.value
|
||||
}
|
||||
let res = await taskDetail(param);
|
||||
// let res = {
|
||||
// "code": 200,
|
||||
// "msg": "操作成功",
|
||||
// "data": {
|
||||
// taskId:236,
|
||||
// taskName:'日常巡检任务AAA',
|
||||
// planTime:new Date().getTime(),
|
||||
// progress:62,
|
||||
// count:3,
|
||||
// total:9,
|
||||
// workHour:1,
|
||||
// list:[
|
||||
// {
|
||||
// workName:'技术中心机房总电源',
|
||||
// pointId:202512297899,
|
||||
// workStatus:3,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:0,
|
||||
// total:70,
|
||||
// percentage:'30%'
|
||||
// },
|
||||
// {
|
||||
// workName:'监控室消防设备阀门正常开启闭合',
|
||||
// workId:202512297899,
|
||||
// workStatus:3,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:20,
|
||||
// total:70,
|
||||
// percentage:'40%'
|
||||
// },
|
||||
// {
|
||||
// workName:'监控室10组灭火器压力指针处于绿色区域',
|
||||
// workId:202512297899,
|
||||
// workStatus:4,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:70,
|
||||
// total:70,
|
||||
// percentage:'100%'
|
||||
// },
|
||||
// {
|
||||
// workName:'库房灭火器压力指针处于绿色区',
|
||||
// workId:202512297899,
|
||||
// workStatus:5,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:70,
|
||||
// total:70,
|
||||
// percentage:'100%'
|
||||
// },
|
||||
// {
|
||||
// workName:'技术中心机房总电源',
|
||||
// pointId:202512297899,
|
||||
// workStatus:3,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:0,
|
||||
// total:70,
|
||||
// percentage:'30%'
|
||||
// },
|
||||
// {
|
||||
// workName:'监控室消防设备阀门正常开启闭合',
|
||||
// workId:202512297899,
|
||||
// workStatus:3,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:20,
|
||||
// total:70,
|
||||
// percentage:'40%'
|
||||
// },
|
||||
// {
|
||||
// workName:'监控室10组灭火器压力指针处于绿色区域',
|
||||
// workId:202512297899,
|
||||
// workStatus:4,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:70,
|
||||
// total:70,
|
||||
// percentage:'100%'
|
||||
// },
|
||||
// {
|
||||
// workName:'库房灭火器压力指针处于绿色区',
|
||||
// workId:202512297899,
|
||||
// workStatus:5,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:70,
|
||||
// total:70,
|
||||
// percentage:'100%'
|
||||
// },
|
||||
// {
|
||||
// workName:'技术中心机房总电源',
|
||||
// pointId:202512297899,
|
||||
// workStatus:3,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:0,
|
||||
// total:70,
|
||||
// percentage:'30%'
|
||||
// },
|
||||
// {
|
||||
// workName:'监控室消防设备阀门正常开启闭合',
|
||||
// workId:202512297899,
|
||||
// workStatus:3,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:20,
|
||||
// total:70,
|
||||
// percentage:'40%'
|
||||
// },
|
||||
// {
|
||||
// workName:'监控室10组灭火器压力指针处于绿色区域',
|
||||
// workId:202512297899,
|
||||
// workStatus:4,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:70,
|
||||
// total:70,
|
||||
// percentage:'100%'
|
||||
// },
|
||||
// {
|
||||
// workName:'库房灭火器压力指针处于绿色区',
|
||||
// workId:202512297899,
|
||||
// workStatus:5,
|
||||
// planTime:'10:25',
|
||||
// workHour:1,
|
||||
// count:70,
|
||||
// total:70,
|
||||
// percentage:'100%'
|
||||
// },
|
||||
// ],
|
||||
// questList:[
|
||||
// {
|
||||
// problemDesc:'西区地下车库入口防汛物资摆放',
|
||||
// problemId:202512297899,
|
||||
// problemStatus:1,
|
||||
// modifyTime:new Date().getTime(),
|
||||
// count:0,
|
||||
// },
|
||||
// {
|
||||
// problemDesc:'监控室消防设备阀门确保正常开启闭合',
|
||||
// problemId:202512297899,
|
||||
// problemStatus:2,
|
||||
// modifyTime:new Date().getTime(),
|
||||
// count:20,
|
||||
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
let data = res||{};
|
||||
progress.value = data.progress
|
||||
progress.value = (data.groupFinishNum / data.groupNum*100).toFixed()
|
||||
resolve({
|
||||
...data,
|
||||
});
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
<!-- 任务(巡检)状态 1=未发布 2=已发布 3 进行中 4 已完成 5 已过期 -->
|
||||
<!-- 状态为3进行中时 进度>0执行中 进度=0为待执行 -->
|
||||
<block v-if="item.taskStatus==3">
|
||||
<img v-if="item.count==0" :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-if="item.beginTime" :src="'static/images/polling/icon-pending.png'" class="img-w" />
|
||||
<img v-else :src="'static/images/polling/icon-start.png'" class="img-w" />
|
||||
</block>
|
||||
<img v-else-if="item.taskStatus==4" :src="'static/images/polling/icon-complete.png'" class="img-complete" />
|
||||
<img v-else-if="item.taskStatus==5" :src="'static/images/polling/icon-Expired.png'" class="img-w" />
|
||||
@@ -52,8 +52,26 @@
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">开始时间<span class="r-gray">{{ parseTime(item.planTime,'{y}-{m}-{d} {h}:{i}') }}</span></view>
|
||||
<view class="r-l-right">任务时长<span class="r-gray">{{ item.workHour }}小时</span></view>
|
||||
<view v-if="item.beginTime">
|
||||
执行时间<span class="r-gray">{{ parseTime(item.beginTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
<view v-else>
|
||||
开始时间<span class="r-gray">{{ parseTime(item.planTime,'{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view class="r-l-left">
|
||||
任务状态<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.beginTime">执行中</block>
|
||||
<block v-else>待执行</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
</view>
|
||||
<view class="r-l-right">
|
||||
任务时长<span class="r-gray">{{ item.workHour }}小时</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
@@ -61,20 +79,12 @@
|
||||
<view class="r-l-left">
|
||||
完成进度<span class="r-gray"><span :class="{'r-red':item.groupFinishNum<item.groupNum}">{{item.groupFinishNum}}</span>/{{item.groupNum}}</span>
|
||||
</view>
|
||||
<view class="r-l-right">完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="r-list">
|
||||
<view class="r-left">
|
||||
<view>任务状态
|
||||
<span class="r-gray" v-if="item.taskStatus==3">
|
||||
<block v-if="item.groupFinishNum==0">待执行</block>
|
||||
<block v-else>执行中</block>
|
||||
</span>
|
||||
<span class="r-gray" v-else>{{formatTaskStatus(item.taskStatus) }}</span>
|
||||
<view class="r-l-right">
|
||||
完成比率<span class="r-blue">{{(item.groupFinishNum/item.groupNum*100).toFixed()+'%'}}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="report-border" v-if="index<list.length-1"></view>
|
||||
</view>
|
||||
|
||||
@@ -183,13 +193,12 @@ const mescrollInit = (mescroll) => {
|
||||
|
||||
// 下拉刷新
|
||||
const downCallback = async (mescroll) => {
|
||||
console.log("mescroll=>",mescroll)
|
||||
try {
|
||||
console.log("下拉刷新");
|
||||
const res = await getList(1, upOption.value.page.size,undefined);
|
||||
cssFlag.value = false;
|
||||
list.value = res.list;
|
||||
mescroll.resetUpScroll()
|
||||
// mescroll.resetUpScroll()
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
} finally {
|
||||
|
||||
@@ -266,7 +266,7 @@ const submitForm = () => {
|
||||
//h5测试用 内网-sn123456
|
||||
//公司外网 'f3fca83f-bf56-47f4-a98b-a602ed8bddee' 529a5543-6957-401e-b090-13df6dee5429
|
||||
//友晟外网 'b97527c8-2ad4-493c-a01c-5f9d0aabaff2'
|
||||
param.uniqCode ='9a41dec6-536f-443f-9d98-8dc5c0b18332';//'1af78c0a-b878-425f-9dc5-bee42146860a'
|
||||
param.uniqCode = '9a41dec6-536f-443f-9d98-8dc5c0b18332'//'9a41dec6-536f-443f-9d98-8dc5c0b18332';//'1af78c0a-b878-425f-9dc5-bee42146860a'
|
||||
let res = await login(param);
|
||||
userStore.login(res);
|
||||
uni.switchTab({ url: '/pages/home/home' })
|
||||
|
||||
@@ -162,7 +162,7 @@ const downCallback = async (mescroll) => {
|
||||
const res = await getNoticeList(1, upOption.value.page.size);
|
||||
cssFlag.value = false;
|
||||
list.value = res.list;
|
||||
mescroll.resetUpScroll();
|
||||
// mescroll.resetUpScroll();
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
} finally {
|
||||
|
||||
@@ -103,7 +103,7 @@ const downCallback = async (mescroll) => {
|
||||
const res = await getFlowList(1, upOption.value.page.size);
|
||||
cssFlag.value = false;
|
||||
list.value = res.list;
|
||||
mescroll.resetUpScroll();
|
||||
// mescroll.resetUpScroll();
|
||||
} catch (error) {
|
||||
mescroll.endErr();
|
||||
} finally {
|
||||
|
||||
@@ -794,3 +794,24 @@ uni-button[type='primary'][plain] {
|
||||
}
|
||||
|
||||
/* 日历修改 end */
|
||||
|
||||
/* 图片loading */
|
||||
.img-flex .img-con{position: relative;}
|
||||
.img-flex .img-con .upload-loading{
|
||||
position: absolute;
|
||||
left:0;
|
||||
top:0;
|
||||
background-color: #EEEEEE;
|
||||
width:210rpx;
|
||||
height: 120rpx;
|
||||
/* line-height: 140rpx; */
|
||||
padding-top:20rpx;
|
||||
}
|
||||
.upload-loading .uni-icons{
|
||||
display: inline-block;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
@@ -284,3 +284,120 @@ export const compressImageUni = (file) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// H5 获取视频第一帧
|
||||
const getFirstFrameInH5 = (videoPath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const video = document.createElement('video')
|
||||
video.src = videoPath
|
||||
video.crossOrigin = 'anonymous'
|
||||
|
||||
video.addEventListener('loadeddata', () => {
|
||||
video.currentTime = 0.1
|
||||
})
|
||||
|
||||
video.addEventListener('seeked', () => {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
canvas.width = video.videoWidth
|
||||
canvas.height = video.videoHeight
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/jpeg', 0.8)
|
||||
resolve(dataUrl)
|
||||
})
|
||||
video.addEventListener('error', reject)
|
||||
})
|
||||
}
|
||||
|
||||
// App平台获取第一帧(Android/iOS)
|
||||
const getFirstFrameInApp = (videoPath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 方法1:使用原生VideoPlayer(推荐)
|
||||
// #ifdef APP-PLUS
|
||||
try {
|
||||
const videoPlayer = plus.video.createVideoPlayer('firstFramePlayer', {
|
||||
src: videoPath,
|
||||
autoplay: false,
|
||||
controls: false,
|
||||
showLoading: false,
|
||||
showProgress: false
|
||||
})
|
||||
|
||||
// 监听视频准备完成
|
||||
videoPlayer.addEventListener('loadeddata', () => {
|
||||
// 截图
|
||||
videoPlayer.snapshot({
|
||||
format: 'jpg',
|
||||
quality: 80
|
||||
}, (res) => {
|
||||
// res.target 是图片临时路径
|
||||
console.log('截图成功:', res)
|
||||
resolve(res.target)
|
||||
|
||||
// 销毁播放器
|
||||
videoPlayer.close()
|
||||
}, (error) => {
|
||||
console.error('截图失败:', error)
|
||||
reject(error)
|
||||
videoPlayer.close()
|
||||
})
|
||||
}, false)
|
||||
|
||||
videoPlayer.addEventListener('error', (error) => {
|
||||
console.error('视频加载失败:', error)
|
||||
reject(error)
|
||||
videoPlayer.close()
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建VideoPlayer失败:', error)
|
||||
|
||||
// 方法2:备用方案,使用HTML5 video(如果原生方法失败)
|
||||
// showAppVideo.value = true
|
||||
// showAppCanvas.value = true
|
||||
|
||||
// // 等待DOM更新后获取元素
|
||||
// setTimeout(() => {
|
||||
// const video = document.getElementById('appVideo')
|
||||
// const canvas = document.getElementById('appCanvas')
|
||||
// const ctx = canvas.getContext('2d')
|
||||
|
||||
// video.onloadedmetadata = () => {
|
||||
// canvas.width = video.videoWidth
|
||||
// canvas.height = video.videoHeight
|
||||
|
||||
// video.onseeked = () => {
|
||||
// ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
|
||||
// const dataUrl = canvas.toDataURL('image/jpeg', 0.8)
|
||||
|
||||
// // 清理
|
||||
// showAppVideo.value = false
|
||||
// showAppCanvas.value = false
|
||||
|
||||
// resolve(dataUrl)
|
||||
// }
|
||||
// video.currentTime = 0.1
|
||||
// }
|
||||
// video.onerror = reject
|
||||
// }, 100)
|
||||
}
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
|
||||
// 获取视频第一帧(跨平台处理)
|
||||
export const getVideoFirstFrame = (videoPath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef H5
|
||||
// H5平台使用canvas
|
||||
getFirstFrameInH5(videoPath).then(resolve).catch(reject)
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// App平台使用plus.video
|
||||
getFirstFrameInApp(videoPath).then(resolve).catch(reject)
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user