Files
crm-uni/pages/customer/followup.vue
T

505 lines
13 KiB
Vue
Raw Normal View History

2025-03-14 16:55:12 +08:00
<template>
<view>
2025-03-19 15:39:21 +08:00
<uni-nav-bar leftText=" " :fixed="true" :border="false" leftIcon="back" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" :title="$t('navbar.addfollowup')"></uni-nav-bar>
2025-03-14 16:55:12 +08:00
<view class="content">
<view class="cell">
2025-03-19 15:39:21 +08:00
<view>{{$t('customer.followtype')}}</view>
2025-03-14 16:55:12 +08:00
<view class="right">
2025-03-17 17:43:41 +08:00
<u-radio-group v-model="form.type">
2025-03-19 15:39:21 +08:00
<u-radio name="1">{{$t('customer.Online')}}</u-radio>
<u-radio name="0">{{$t('customer.Visit')}}</u-radio>
2025-03-14 16:55:12 +08:00
</u-radio-group>
</view>
</view>
2025-03-17 17:43:41 +08:00
<u-line></u-line>
2025-03-14 16:55:12 +08:00
<view class="cell">
2025-03-19 15:39:21 +08:00
<view>{{$t('customer.followtime')}} </view>
2025-03-14 16:55:12 +08:00
<view class="right" @click="show = true">
2025-03-19 17:04:43 +08:00
<input :placeholder="$t('customer.followtime')" v-model="form.firstFollowtime" disabled />
2025-03-14 16:55:12 +08:00
</view>
</view>
2025-03-17 17:43:41 +08:00
<u-line></u-line>
<view class="cell1" v-if="form.type==1">
2025-03-19 15:39:21 +08:00
<view>{{$t('customer.updataimg')}}</view>
2025-03-17 17:43:41 +08:00
<view style="margin-top: 10px;">
<uni-file-picker fileMediatype="image" limit="1" :del-icon="false" disable-preview @select="select" @delete="delfn('picture')">
2025-03-14 16:55:12 +08:00
<image src="/static/image/customer/upimg.png" mode="" class="addimg"></image>
</uni-file-picker>
</view>
</view>
2025-03-17 17:43:41 +08:00
<view class="cell" v-if="form.type==0">
2025-03-19 15:39:21 +08:00
<view>{{$t('customer.followlocation')}}</view>
2025-03-14 16:55:12 +08:00
<view class="right">
2025-03-19 15:39:21 +08:00
<input :placeholder="$t('customer.followlocation')" disabled v-model="form.address" />
2025-03-14 16:55:12 +08:00
</view>
</view>
2025-03-17 17:43:41 +08:00
<u-line v-if="form.type==0"></u-line>
<view class="cell1" v-if="form.type==0">
2025-03-19 15:39:21 +08:00
<view>{{$t('customer.locationimg')}}</view>
2025-03-14 16:55:12 +08:00
<view style="margin-top: 10px;">
2025-03-17 17:43:41 +08:00
<uni-file-picker fileMediatype="image" limit="1" :del-icon="false" disable-preview @select="select1" @delete="delfn('picture')">
2025-03-14 16:55:12 +08:00
<image src="/static/image/customer/upimg.png" mode="" class="addimg"></image>
</uni-file-picker>
</view>
</view>
2025-03-17 17:43:41 +08:00
<u-line v-if="form.type==0"></u-line>
<view class="cell1" v-if="form.type==0">
2025-03-19 15:39:21 +08:00
<view>{{$t('customer.Visitcontent')}}</view>
2025-03-14 16:55:12 +08:00
<view class="yuyin">
2025-03-19 15:39:21 +08:00
<textarea auto-height v-model="form.audio_text" :placeholder="$t('customer.Visiinputhd')"/>
2025-03-15 15:46:16 +08:00
<image src="/static/image/customer/yuyin.png" mode="" @tap="startRecognizebd"></image>
2025-03-14 16:55:12 +08:00
</view>
</view>
2025-03-17 17:43:41 +08:00
<u-line v-if="form.type==0"></u-line>
<view class="cell1" v-if="form.type==0">
2025-03-19 15:39:21 +08:00
<view>{{$t('customer.Remarks')}}</view>
2025-03-15 15:46:16 +08:00
<view class="yuyin">
2025-03-19 15:39:21 +08:00
<textarea auto-height v-model="form.remark" :placeholder="$t('customer.Remarksinputhd')"/>
<!-- <image src="/static/image/customer/yuyin.png" mode="" @tap="startRecognize"></image> -->
2025-03-14 16:55:12 +08:00
</view>
</view>
</view>
<u-gap height="10" bgColor="#F6F6F6"></u-gap>
<view class="steps">
<view class="title">
2025-03-19 15:39:21 +08:00
{{$t('customer.followrecord')}} <text class="status" :style="{color:(datas.follow == 0 ?'#3471FF' : datas.follow == 1 ?'#27C89C': '#FF4040')}">{{datas.follow_txt}}</text>
2025-03-14 16:55:12 +08:00
</view>
2025-03-17 17:43:41 +08:00
<u-steps v-if="followList.length>0" :current="followList.length" direction="column" dot>
<u-steps-item :title="item.type_txt" v-for="(item, index) in followList" :key="index">
2025-03-14 16:55:12 +08:00
<view slot="desc" class="desc">
2025-03-17 17:43:41 +08:00
<view class="" v-if="item.type==0">
2025-03-14 16:55:12 +08:00
<view class="">
2025-03-17 17:43:41 +08:00
<view class="">
2025-03-19 15:39:21 +08:00
{{$t('times')}} :{{item.created_at}}
2025-03-17 17:43:41 +08:00
</view>
2025-03-19 15:39:21 +08:00
<view class="">{{$t('address')}}:{{item.address}}</view>
<view class="">{{$t('customer.Visitcontent')}}:{{item.audio_text}}</view>
2025-03-14 16:55:12 +08:00
</view>
<view class="">
2025-03-17 17:43:41 +08:00
<image :src="item.oss_picture" mode=""></image>
2025-03-14 16:55:12 +08:00
</view>
</view>
2025-03-17 17:43:41 +08:00
<view class="" v-else>
<view class="">
2025-03-19 15:39:21 +08:00
{{$t('times')}}:{{item.created_at}}
2025-03-17 17:43:41 +08:00
</view>
<view class="">
<image :src="item.oss_picture" mode=""></image>
</view>
2025-03-14 16:55:12 +08:00
</view>
</view>
</u-steps-item>
</u-steps>
2025-03-17 17:43:41 +08:00
<empty :list="followList"></empty>
2025-03-14 16:55:12 +08:00
</view>
<u-gap height="100" bgColor="#fff"></u-gap>
<view class="foot">
2025-03-19 15:39:21 +08:00
<button class="btn1" @click="shownexttime=true">{{$t('customer.NextReminder')}}</button>
<button class="btn" @click="nextFn">{{$t('submit')}}</button>
2025-03-14 16:55:12 +08:00
</view>
<u-datetime-picker
:show="show"
v-model="firstFollow"
mode="datetime"
@confirm="confirm"
@cancel="cancel"
@change="change"
></u-datetime-picker>
2025-03-17 17:43:41 +08:00
<u-datetime-picker
:show="shownexttime"
v-model="nexttime"
mode="datetime"
@confirm="confirmnext"
@cancel="cancelnext"
></u-datetime-picker>
2025-03-14 16:55:12 +08:00
</view>
</template>
<script>
2025-03-15 15:46:16 +08:00
import permision from "@/common/permission.js"
2025-03-14 16:55:12 +08:00
import { request,showToast } from '@/api/apiT.js'
export default {
data() {
return {
2025-03-17 17:43:41 +08:00
id:'',
2025-03-14 16:55:12 +08:00
show:false,
2025-03-17 17:43:41 +08:00
shownexttime:false,
firstFollow: Number(new Date()),
nexttime: Number(new Date()),
followList:[],
2025-03-14 16:55:12 +08:00
form: {
2025-03-17 17:43:41 +08:00
customer_id:'',
type: '1',
address: '',
lat: '',
lng: '',
firstFollowtime:'',
audio_text:'',
remark:'',
picture:''
2025-03-19 15:39:21 +08:00
},
datas:''
2025-03-14 16:55:12 +08:00
}
},
2025-03-17 17:43:41 +08:00
onLoad(options) {
console.log(options);
this.id = options.id;
this.form.customer_id = options.id;
var that = this
this.getlocahost()
this.followlistfn()
2025-03-14 16:55:12 +08:00
},
methods: {
leftClick(){
uni.navigateBack({})
2025-03-15 15:46:16 +08:00
},
2025-03-17 17:43:41 +08:00
// 获取当前位置
2025-03-18 13:32:13 +08:00
getarrd(longitude,latitude){
const apiKey = 'AIzaSyBGvV-vX7C72PEbcYrTdmUj3qrubejWzNw';
2025-03-19 15:39:21 +08:00
uni.request({
url: `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${apiKey}`,
success: (res) => {
if (res.data.results && res.data.results.length > 0) {
this.form.address = res.data.results[0].formatted_address; // 获取格式化地址
console.log(res.data.results[0].formatted_address)
} else {
console.error('未找到地址');
}
},
fail: (err) => {
console.error('请求失败:', err);
},
2025-03-18 13:32:13 +08:00
});
},
2025-03-17 17:43:41 +08:00
getlocahost(){
2025-03-19 15:39:21 +08:00
var that = this
2025-03-17 17:43:41 +08:00
uni.getLocation({
type: 'wgs84', // 默认为 wgs84 坐标系,可选 gcj02
success: function (res) {
console.log('当前位置的经度:' + res.longitude);
console.log('当前位置的纬度:' + res.latitude);
2025-03-19 15:39:21 +08:00
that.getarrd(res.longitude,res.latitude)
2025-03-17 17:43:41 +08:00
// 如果需要将经纬度转换为具体地址,可以使用逆地理编码
// 这里调用 uni.chooseLocation 或第三方地图服务(如高德、腾讯地图)
},
fail: function (err) {
console.log('获取位置失败:', err);
},
complete: function () {
console.log('获取位置完成');
}
});
2025-03-18 13:32:13 +08:00
2025-03-17 17:43:41 +08:00
// uni.chooseLocation({
// success: function (res) {
// console.log('位置名称:' + res.name);
// console.log('详细地址:' + res.address);
// console.log('经度:' + res.longitude);
// console.log('纬度:' + res.latitude);
// },
// fail: function (err) {
// console.log('选择位置失败:', err);
// }
// });
2025-03-14 16:55:12 +08:00
},
2025-03-17 17:43:41 +08:00
// 跟进记录
followlistfn(){
request({
url:'follow/list',
method:'get',
data: {
customer_id:this.id,
}
}).then(res => {
2025-03-19 15:39:21 +08:00
this.datas = res.data.data
2025-03-18 13:32:13 +08:00
this.followList = res.data.data
2025-03-17 17:43:41 +08:00
console.log(res.data.data)
})
2025-03-14 16:55:12 +08:00
},
2025-03-17 17:43:41 +08:00
//保存跟进
nextFn(){
request({
url:'follow/save',
method:'post',
data:this.form
}).then(res => {
uni.showToast({
title: res.data.message,
icon: 'none'
});
if(res.data.code ==0){
2025-03-19 15:39:21 +08:00
setTimeout(() => {
uni.navigateBack({})
}, 1000);
2025-03-17 17:43:41 +08:00
}
})
2025-03-14 16:55:12 +08:00
},
result(time, mode) {
const timeFormat = uni.$u.timeFormat
switch (mode) {
case 'datetime':
return timeFormat(time, 'yyyy-mm-dd hh:MM')
case 'date':
return timeFormat(time, 'yyyy-mm-dd')
case 'year-month':
return timeFormat(time, 'yyyy-mm')
case 'time':
return time
default:
return ''
}
},
2025-03-17 17:43:41 +08:00
cancel() {
this.show = false
},
confirm(e) {
this.show = false
this.form.firstFollowtime = this.result(e.value, e.mode)
console.log(this.result(e.value, e.mode))
},
cancelnext() {
this.shownexttime = false
},
confirmnext(e) {
this.shownexttime = false
this.nexttime = this.result(e.value, e.mode)
console.log(this.nexttime)
2025-03-19 15:39:21 +08:00
request({
url:'customer/followNext',
method:'post',
data:{
id:this.id,
follow_next:e.value
}
}).then(res => {
uni.showToast({
title: res.data.message,
icon: 'none'
});
if(res.data.code ==0){
setTimeout(() => {
uni.navigateBack({})
}, 1000);
}
})
2025-03-17 17:43:41 +08:00
},
change(e) {
// console.log('change', e)
},
2025-03-15 15:46:16 +08:00
delfn(name){
console.log(name)
that.carinfo[name] =''
},
uploadfn(imgUrl,name){
var that = this;
uni.uploadFile({
2025-03-17 17:43:41 +08:00
url: 'http://192.168.50.4:8787/upload',
2025-03-15 15:46:16 +08:00
filePath: imgUrl,
2025-03-17 17:43:41 +08:00
name: 'files',
2025-03-15 15:46:16 +08:00
header:{'content-type': 'application/x-www-form-urlencoded','Authorization': 'Bearer ' + uni.getStorageSync('token')},
success: (uploadFileRes) => {
const path=JSON.parse(uploadFileRes.data)
2025-03-17 17:43:41 +08:00
that.form[name] = path.data.path
console.log(that.form)
2025-03-15 15:46:16 +08:00
}
});
},
select(e){
const tempFilePaths = e.tempFilePaths;
const imgUrl=tempFilePaths[0]
2025-03-17 17:43:41 +08:00
this.uploadfn(imgUrl,'picture')
2025-03-15 15:46:16 +08:00
},
select1(e){
const tempFilePaths = e.tempFilePaths;
const imgUrl=tempFilePaths[0]
2025-03-17 17:43:41 +08:00
this.uploadfn(imgUrl,'picture')
2025-03-15 15:46:16 +08:00
},
async startRecognizebd () {
// #ifdef APP-PLUS
let status = await this.checkPermission();
if (status !== 1) {
return;
}
// #endif
// TODO ios 在没有请求过权限之前无法得知是否有相关权限,这种状态下需要直接调用语音,会弹出正在识别的toast
var options = {};
var that = this;
options.engine = 'baidu';
2025-03-19 17:04:43 +08:00
that.form.audio_text = "";
2025-03-15 15:46:16 +08:00
plus.speech.startRecognize(options, function (s) {
console.log(s);
2025-03-19 17:04:43 +08:00
that.form.audio_text += s;
2025-03-15 15:46:16 +08:00
}, function (e) {
console.log("语音识别失败:" + e.message);
});
},
async startRecognize () {
// #ifdef APP-PLUS
let status = await this.checkPermission();
if (status !== 1) {
return;
}
// #endif
// TODO ios 在没有请求过权限之前无法得知是否有相关权限,这种状态下需要直接调用语音,会弹出正在识别的toast
var options = {};
var that = this;
2025-03-19 17:04:43 +08:00
that.form.remark = "";
2025-03-15 15:46:16 +08:00
options.engine = 'iFly';
options.punctuation = false; // 是否需要标点符号
options.timeout = 10 * 1000; //语音录入持续时长
plus.speech.startRecognize(options, function(s) {
console.log(s);
2025-03-19 17:04:43 +08:00
that.form.remark += s;
2025-03-15 15:46:16 +08:00
// 关闭语音
plus.speech.stopRecognize();
}, function (e) {
console.log("语音识别失败:" + e.message);
});
},
// #ifdef APP-PLUS
async checkPermission() {
let status = permision.isIOS ? await permision.requestIOS('record') :
await permision.requestAndroid('android.permission.RECORD_AUDIO');
if (status === null || status === 1) {
status = 1;
} else if (status === 2) {
uni.showModal({
content: "系统麦克风已关闭",
confirmText: "确定",
showCancel: false,
success: function(res) {
}
})
} else {
uni.showModal({
content: "需要麦克风权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
}
// #endif
2025-03-14 16:55:12 +08:00
}
}
</script>
<style lang="scss">
textarea {
min-height: 200rpx;// 设置默认高度 ,解决设置自适应高度之后高度无效问题
margin-top: 15px;
background-color: #F6F6F6;
padding: 15px;
border-radius: 10px;
}
.u-divider{
margin: 0px 0px !important;
}
.content{
padding-top: 10px;
background: #FFFFFF;
// height: calc(100vh - 44px);
}
.cell{
position: relative;
display: flex;
justify-content:space-between;
padding: 18px 20px;
align-items: center;
font-size: 32rpx;
font-weight: 500;
font-size: 32rpx;
color: #666666;
}
.arrowright{
margin-right: -15px;
}
.cell1{
font-size: 32rpx;
padding: 18px 20px;
align-items: center;
font-weight: 500;
color: #666666;
}
.right{
text-align: right;
color: #222222;
}
.foot{
position: fixed;
bottom: 0px;
width: 100%;
height: 90px;
background: #FFFFFF;
z-index: 1;
display: flex;
padding-top: 20px;
}
.btn{
2025-03-19 15:39:21 +08:00
font-size: 28rpx;
2025-03-14 16:55:12 +08:00
background: linear-gradient(to right, #3471FF, #6776FF);
color: white;
border-radius: 50px;
width: 45%;
height: 96rpx;
line-height: 96rpx;
margin-top: 20px;
}
.btn1{
2025-03-19 15:39:21 +08:00
font-size: 28rpx;
2025-03-14 16:55:12 +08:00
border: 1px solid #3471FF;
background: #fff;
color: #3471FF;
border-radius: 50px;
width: 45%;
height: 96rpx;
line-height: 96rpx;
margin-top: 20px;
}
.addimg{
width: 110px;
height:110px;
}
.yuyin{
position: relative;
image{
width: 20px;
height:24px;
position: absolute;
right: 15px;
bottom: 10px;
}
}
.steps{
padding: 15px;
2025-03-19 15:39:21 +08:00
background: #fff;
2025-03-14 16:55:12 +08:00
image{
width: 44px;
height:44px;
}
.desc{
display: flex;
justify-content: space-between;
align-items: center;
color: #999999;
}
.title{
color: #666666;
font-size: 32rpx;
margin-bottom: 15px;
.status{
font-size: 28rpx;
color: #3471FF;
margin-left: 10px;
}
}
}
</style>