腾讯地图web端签名校验方法
<template><el-dialog v-model="dialogVisible" title="" width="900" :before-close="handleClose">
<div class="leftForm">
<div class="leftCard">
<div class="leftTitle">代客下单</div>
<el-form ref="formRef" id="formRef" :model="form" :rules="rules" label-width="0px">
<el-form-item label="" prop="passengerPhone">
<el-input :prefix-icon="Memo" class="completeDiv" v-model="form.passengerPhone"
placeholder="请输入乘车人手机号" />
</el-form-item>
<el-form-item label="" prop="start">
<el-input disabled :prefix-icon="Edit" class="partDiv" v-model="form.start"
placeholder="请选择乘客起点" />
<el-button @click="handleChooseAdd('start')" type="primary" class="choosePoint" round>
地图选点
</el-button>
</el-form-item>
<el-form-item label="" prop="end">
<el-input disabled :prefix-icon="Edit" class="partDiv" v-model="form.end"
placeholder="请选择乘客终点" />
<el-button @click="handleChooseAdd('end')" type="primary" class="choosePoint" round>
地图选点
</el-button>
</el-form-item>
<el-form-item label="" prop="datetime">
<el-date-picker @change="dateChange" v-model="form.datetime" type="datetimerange"
range-separator="-" start-placeholder="最早时间" end-placeholder="最迟时间" />
</el-form-item>
<el-form-item label="" prop="passengerCount">
<el-select v-model="form.passengerCount" class="completeDiv" placeholder="请选择乘客人数">
<el-option v-for="item in options" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="" prop="passengerRemark">
<el-input :prefix-icon="Edit" maxlength="50" type="textarea" class="completeDiv"
v-model="form.passengerRemark" placeholder="请输入乘客备注" />
</el-form-item>
</el-form>
<div class="tips">
温馨提示:
<div>1.下单成功后,系统将向乘客发送支付提醒短信,请乘客尽快完成支付</div>
<div>2.如果乘客未及时支付订单,请主动联系乘客完成支付</div>
</div>
</div>
<div class="mt-2 text-gray-600 text-sm">
<div class="rightTop">
<span class="rightTitle">接单司机</span>
<span class="rightText">未指定司机,订单进入抢单池,其他司机可接单</span>
</div>
<div><el-input class="partDiv" v-model="form.phone" placeholder="请输入乘车人手机号" />
<el-button type="primary" @click="chooseDriver" class="choosePoint" round>
选择司机
</el-button>
</div>
<div class="rightTitle mt-2 mb-2">出行方式</div>
<div v-if="isAccord == 0">
<div v-for="item, index in priceDtoList" :key="index" @click="selectPay(index, item)"
:class="selectPayIndex === index ? 'priceBox activeBox' : 'priceBox'">
<div class="priceTitle">{{ item.productCode == 'fit' ? '舒适' : '拼车' }}</div>
<div>
<div class="priceContent">
<div class="priceLabel">订单价格</div>
<div class="priceValue">
<el-input v-if="item.productCode == 'fit'" v-model="item.totalPrice"
@change="fitChange" style="width: 200px;" placeholder="请输入"
class="input-with-select">
<template #prepend>
¥
</template>
</el-input>
<el-input v-else v-model="item.totalPrice" @change="rentChange"
style="width: 200px;" placeholder="请输入" class="input-with-select">
<template #prepend>
¥
</template>
</el-input>
</div>
</div>
<div class="priceDesc">您可以在<span class="redText">{{ setMoney('min', item.totalPrice)
}}</span>元至<span class="redText">{{ setMoney('max',
item.totalPrice) }}</span>元之间调整订单金额</div>
</div>
</div>
<!-- <div @click="selectPay(2)" :class="selectPayIndex === 2 ? 'priceBox activeBox' : 'priceBox'">
<div class="priceTitle">独享</div>
<div>
<div class="priceContent">
<div class="priceLabel">订单价格</div>
<div class="priceValue">
<el-input v-model="input3" style="width: 200px;" placeholder="Please input"
class="input-with-select">
<template #prepend>
¥
</template>
</el-input>
</div>
</div>
<div class="priceDesc">您可以在<span class="redText">899.99</span>元至<span
class="redText">1111.99</span>元之间调整订单金额
</div>
</div>
</div> -->
</div>
<div v-else-if="isAccord == 1" class="noAccord">
当前线路不在您的运营范围,暂无法提供服务
</div>
<div v-else-if="isAccord == 3">
<div v-for=" i in 2 " class=" blankBorder">
<div class="blankLeft">
<div class="leftBlank"></div>
</div>
<div style="padding: 20px;">
<div class="leftTop"></div>
<div class="leftBottom"></div>
</div>
</div>
</div>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">立即下单</el-button>
</div>
</div>
</div>
<map-modal ref="mapRef" @change="handelChangeAddress" />
<el-dialog v-model="driverDialogVisible" title="选择司机" width="800">
<div class="driver-list">
<!-- 这里可以添加搜索框 -->
<el-input v-model="searchKeyword" style="width: 200px;" placeholder="请输入司机姓名/手机号" class="mr-4 ">
<template #prefix>
<el-icon>
<Search />
</el-icon>
</template>
</el-input>
<el-button type="primary" @click="searchDriver">搜索</el-button>
<!-- 司机列表表格 -->
<el-table :data="driverList" style="width: 100%;margin-top: 10px;">
<el-table-column prop="name" label="司机姓名" />
<el-table-column prop="phone" label="手机号" />
<el-table-column prop="carNumber" label="车牌号" />
<el-table-column label="操作">
<template #default="scope">
<el-button type="text" @click="selectDriver(scope.row)">
选择
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container" style="margin-top: 20px; text-align: right;">
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="" :total="pagination.total" @size-change="handleSizeChange"
@current-change="handleCurrentChange" layout="total, sizes, prev, pager, next" />
</div>
</div>
</el-dialog>
</el-dialog>
<TXMap ref="txMap" :dialogVisible="mapVisible" @update="mapUpdate" />
</template>
<script setup name="Dialog" lang="ts">
import { ref, reactive, watch } from 'vue'
import { listDriver } from '@/api/power/driver'
import { getPriceTemplate, setPrice, orderSubmission } from '@/api/order/operate'
import { ArrowDown, Search, Memo, Edit } from '@element-plus/icons-vue'
import { getConfigKey } from '@/api/system/config'
import mapModal from '@/components/Map/map.vue'
import md5 from './md5'
import TXMap from './TXMap.vue'
import { func } from 'vue-types'
let latitude = ref(39.913818)
let longitude = ref(116.363625)
const addressInfo = (e) => {
console.log(e)
}
let startTime = ref(0)
let endTime = ref(0)
function dateChange(value: any) {
console.log(value);
startTime.value = new Date(value).getTime() / 1000;
endTime.value = new Date(value).getTime() / 1000;
}
const txMap = ref('')
function selectAddress(address, lat, lng) {
console.log(address, lat, lng);
// this.formData.address = address
// this.formData.latitude = lat
// this.formData.longitude = lng
}
// 定义组件的props
const props = defineProps({
dialogVisible: {
type: Boolean,
default: false,
},
})
const value = ref('')
let selectPayIndex = ref(0)
let outWayType = ref('')
function selectPay(index: number, item: any) {
selectPayIndex.value = index;
outWayType.value = item.productCode;
console.log(outWayType, 'outWayType');
}
let mapVisible = ref(false)
function mapUpdate(e) {
console.log(11);
mapVisible.value = e
}
const mapRef = ref()
async function fitChange(val) {
if (val.toString().split('.').length > 2) {
ElMessage.error('最多两位小数')
return
} else {
let param = {
'price': parseFloat((val * 100).toFixed(2)),
'productCode': 'fit',
'estimateKey': estimateKey.value
}
const res = await setPrice(param)
console.log(res, 'rent');
}
}
const driverDialogVisible = ref(false)
// 添加分页相关的响应式数据
const pagination = reactive({
total: 0,
currentPage: 1,
pageSize: 10
})
const selectDriver = (driver) => {
form.phone = driver.phone
driverDialogVisible.value = false
console.log(driver.phone, 'driver');
}
// 修改 chooseDriver 方法
async function chooseDriver() {
driverDialogVisible.value = true
await getDriverList()
}
let driverList = ref([])
function searchDriver() {
pagination.currentPage = 1
getDriverList()
}
// 添加搜索关键字的响应式数据
const searchKeyword = ref('')
// 添加获取司机列表的方法
async function getDriverList() {
try {
const res = await listDriver({
name: searchKeyword.value,
pageNum: pagination.currentPage,
pageSize: pagination.pageSize
})
if (res.code === 0) {
driverList.value = res.rows
pagination.total = res.total
}
} catch (error) {
console.error('获取司机列表失败:', error)
ElMessage.error('获取司机列表失败')
}
}
// 添加分页变化处理方法
function handleCurrentChange(val: number) {
pagination.currentPage = val
getDriverList()
}
let startData = ref({})
let endData = ref({})
//地址逆解析
async function getAddress(e) {
if (e.lat && e.lon) {
let res = await getGeoCoder({
location: e.lat + ',' + e.lon,
key: '自己的key',
SK: '自己的SK'
})
console.log(res, 'res腾讯地图返回');
// from = 39.915285, 116.403857 & to=39.915285, 116.803857
if (mode.value === 'start') {
form.start = res.result.formatted_addresses.recommend
startData.value = {
lat: res.result.location.lat,
lng: res.result.location.lng, // 经度
address: res.result.address, // 地址
shortAddress: res.result.formatted_addresses.recommend,
cityCode: res.result.ad_info.phone_area_code,
adCode: res.result.ad_info.adcode, // 城市编码
}
} else if (mode.value === 'end') {
form.end = res.result.formatted_addresses.standard_address + res.result.formatted_addresses.recommend;
endData.value = {
lat: res.result.location.lat,
lng: res.result.location.lng, // 经度
address: res.result.address, // 地址
shortAddress: res.result.formatted_addresses.recommend,
cityCode: res.result.ad_info.phone_area_code,
adCode: res.result.ad_info.adcode, // 城市编码
}
}
// if (res.status === 0) {
// let locationName = res.result.address
// } else {
// showToast(res.message, 'warning')
// }
}
}
function getInstance(param) {
// https://apis.map.qq.com/ws/direction/v1/driving/?
// // from=39.915285,116.403857&to=39.915285,116.803857&output=json&callback=cb&key=[你的key]
let sig = md5(
`/ws/direction/v1/driving?callback=jsonpCallback&from=${param.slat},${param.slng}&key=${param.key}&output=jsonp&to=${param.elat},${param.elng}${param.SK}`
)
console.log(`/ws/direction/v1/driving?callback=jsonpCallback&from=${param.slat},${param.slng}&key=${param.key}&output=jsonp&to=${param.elat},${param.elat}${param.SK}`, sig);
let getData = {
callbackQuery: 'callback', // 设置callback参数的key不设置的话callback参数会自动被赋予一个随机值md5校验无法通过
callbackName: 'jsonpCallback', // 设置callback 参数的值
from: param.slat + ',' + param.slng,
key: param.key,
output: 'jsonp',
to: param.elat + ',' + param.elng,
sig
}
return jsonp('https://apis.map.qq.com/ws/direction/v1/driving', getData)
}
function getGeoCoder(param: any) {
let sig = md5(
`/ws/geocoder/v1?callback=jsonpCallback&key=${param.key}&location=${param.location}&output=jsonp${param.SK}`
)
// sig = encodeURI(sig) //url化一下
let getData = {
callbackQuery: 'callback', // 设置callback参数的key不设置的话callback参数会自动被赋予一个随机值md5校验无法通过
callbackName: 'jsonpCallback', // 设置callback 参数的值
key: param.key,
location: param.location,
output: 'jsonp',
sig
}
//签名失败的解决办法 https://lbs.qq.com/faq/serverFaq/webServiceKey
return jsonp('https://apis.map.qq.com/ws/geocoder/v1', getData)
}
function handleSizeChange(val: number) {
pagination.pageSize = val
pagination.currentPage = 1
getDriverList()
}
async function rentChange(val) {
//判断小数是否最多两位
if (val.toString().split('.').length > 2) {
ElMessage.error('最多两位小数')
return
} else {
let param = {
'price': parseFloat((val * 100).toFixed(2)),
'productCode': 'rent',
'estimateKey': estimateKey.value
}
const res = await setPrice(param)
console.log(res, 'rent');
}
}
import { jsonp } from "vue-jsonp";
import { el, sl } from 'element-plus/es/locale'
async function handelChangeAddress(e) {
const {
adcode,
province,
city,
district,
address,
lon,
lat
} = e
console.log(e)
getAddress(e);
// jsonp(url, {
// output: "jsonp",
// cache: true,
// }).then((res) => {
// console.log(res);
// }).catch((err) => {
// console.error(err, 'err');
// });
// form.value = {
// ...form.value,
// : adcode,
// : province,
// : city,
// : district,
// : lat,
// : lon,
// : address,
// }
};
let mode = ref('')
// function handleChooseAdd(_mode = 'start') {
// mapVisible.value = true
// }
const initFormData: RobForm = {
id: undefined,
robNo: undefined,
agentId: undefined,
lineId: undefined,
startProvinceId: undefined,
startProvince: undefined,
startCityId: undefined,
startCityCode: undefined,
startCity: undefined,
startDistrictId: undefined,
startAdCode: undefined,
startDistrict: undefined,
startAddress: undefined,
startLongitude: undefined,
startLatitude: undefined,
startRadius: undefined,
endProvinceId: undefined,
endProvince: undefined,
endCityId: undefined,
endCityCode: undefined,
endCity: undefined,
endDistrictId: undefined,
endAdCode: undefined,
endDistrict: undefined,
endAddress: undefined,
endLongitude: undefined,
endLatitude: undefined,
endRadius: undefined,
startTime: undefined,
endTime: undefined,
sortTime: undefined,
seat: undefined,
surplusSeat: undefined,
robProduct: undefined,
userType: 'agent_user',
userId: undefined,
status: '0',
plan: 'PART',
driverJson: undefined,
renewal: 0,
remark: undefined,
}
function handleChooseAdd(_mode = 'start') {
mode.value = _mode;
mapRef.value.open({
adcode: form,
lat: form,
lon: form,
province: form,
city: form,
district: form,
address: form,
});
}
//判断是否符合打车条件
let isAccord = ref(0)
const options = [
{
value: 1,
label: '1人',
},
{
value: 2,
label: '2人',
},
{
value: 3,
label: '3人',
},
{
value: 4,
label: '4人',
},
{
value: 5,
label: '5人',
}, {
value: 6,
label: '6人',
},
]
// 定义组件的emits
const emits = defineEmits(['update'])
// 解构props
let { dialogVisible } = toRefs(props)
// 表单ref
const formRef = ref()
// 表单数据
const form = reactive({
phone: '',
passengerPhone: '', // 乘车人手机号
start: '', // 起点
end: '', // 终点
datetime: '', // 用车时间
passengerCount: '', // 乘客人数
passengerRemark: '', // 备注
})
// 表单校验规则
const rules = {
passengerPhone: [
{ required: true, message: '请输入乘车人手机号', trigger: 'blur' },
{ pattern: /^1\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
],
start: [
{ required: true, message: '请选择乘客起点', trigger: 'change' }
],
end: [
{ required: true, message: '请选择乘客终点', trigger: 'change' }
],
datetime: [
{ required: true, message: '请选择用车时间', trigger: 'change' }
],
passengerCount: [
{ required: true, message: '请选择乘客人数', trigger: 'change' }
],
// passengerRemark: [
// { required: true, message: '请输入乘客备注', trigger: 'blur' }
// ]
}
// "startTime": 1743560580,
// "endTime": 1743560580,
// "passengerCount": 1,
// "startCityCode":"0571",
// "endCityCode":"0571",
// "mileage": 60000,
// "platformCode":"SELF",
// "startAdCode": "330109",
// "startLongitude": 116.353531,
// "startLatitude": 116.353531,
// "endAdCode": "330106",
// "endLongitude": 120.087667,
// "startAddress": "智力大厦",
// "startShortAddress": "智力大厦",
// "endAddress":"三墩",
// "endShortAddress": "三墩",
// "endLatitude": 30.322948
watch(
[
() => form.start,
() => form.end,
() => form.datetime,
() => form.passengerCount
],
() => {
if (start && end && datetime && count) {
// 所有值都不为空时触发事件
getDict();
}
},
{ deep: true }
)
let estimateKey = ref('')
let priceDtoList = ref([])
const setMoney = (type, price) => {
if (type == 'min') {
return Number(((price * ((100 - rateOver.value) / 100))).toFixed(2))
} else if (type == 'max') {
return Number(((price * ((100 + rateOver.value) / 100))).toFixed(2))
}
}
let overallMileage = ref(0)
async function checkRoute() {
// 发起请求,检查行程是否符合条件
let params = {
"startTime": startTime.value,
"endTime": endTime.value,
"passengerCount": form.passengerCount,
"startCityCode": startData.value.cityCode,
"endCityCode": endData.value.cityCode,
"mileage": overallMileage.value,
"platformCode": "SELF",
"startAdCode": startData.value.adCode,
"startLongitude": startData.value.lng,
"startLatitude": startData.value.lat,
"endAdCode": endData.value.adCode,
"endLongitude": endData.value.lng,
"startAddress": startData.value.address,
"startShortAddress": startData.value.shortAddress,
"endAddress": endData.value.address,
"endShortAddress": endData.value.shortAddress,
"endLatitude": endData.value.lat,
}
const res = await getPriceTemplate(params);
if (res.code == 500) {
isAccord.value = 1;
return;
} else if (res.code == 200) {
isAccord.value = 0;
estimateKey.value = res.data.estimateKey;
priceDtoList.value = res.data.priceDtoList;
// totalPrice
priceDtoList.value.forEach((item) => {
item.totalPrice = Number(item.totalPrice) / 100
})
}
}
//获取金额波动范围
let rateOver = ref(0)
async function getDict() {
const res = await getConfigKey('proxy_order_price_fluctuation_rate')
console.log(res, 'proxy_order_price_fluctuation_rate');
if (res.code == 200) {
rateOver.value = Number(res.data);
console.log(rateOver.value);
let ser = await getInstance({
slat: startData.value.lat,
slng: startData.value.lng,
elat: endData.value.lat,
elng: endData.value.lng,
key: 'NSBBZ-SPQLZ-63RXY-7BIAG-DKBGH-B2FUE',
SK: 'PrvmRwHoIFtYslLE3u2kSdF49GxcW1ZT'
})
overallMileage.value = ser.result.routes.distance
console.log(ser.result.routes.distance, 'res距离腾讯地图返回');
checkRoute()
}
}
function submit() {
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
let params = {
estimateKey: estimateKey.value,
//备注
passengerRemark: form.passengerRemark,
highwayType: '2',
productCode: outWayType.value,
createModel: '4',
//乘客手机号
passengerPhone: form.passengerPhone,
//司机手机号
driverPhone: form.phone,
}
const res = await orderSubmission(params)
console.log(res, '代下单提交返回res');
if (res.code == 200) {
ElMessage.success('下单成功')
emits('update', false)
}
}
})
}
function cancel() {
emits('update', false)
}
function handleClose() {
emits('update', false)
}
</script>
<style scoped>
.el-dialog {
border-radius: 8px;
}
.leftForm {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
position: relative;
}
.leftCard {
padding: 10px;
border: 1px solid #e6e6e6;
border-radius: 10px;
}
#formRef {
margin-left: 10px;
}
.leftTitle {
font-weight: 600;
font-size: 16px;
margin-bottom: 10px;
}
.tips {
font-size: 12px;
margin-left: 10px;
color: #999;
margin-top: 10px;
}
.completeDiv {
width: 350px;
}
.partDiv {
width: 249px;
}
.choosePoint {
width: 80px;
margin-left: 10px;
}
.rightTitle {
font-weight: 600;
font-size: 16px;
margin-right: 10px;
}
.rightTop {
margin-bottom: 10px;
}
.rightText {
font-size: 12px;
color: #999;
}
.priceBox {
border: 1px solid #e6e6e6;
border-radius: 8px;
padding: 10px;
margin-bottom: 15px;
display: grid;
grid-template-columns: 1fr 4fr;
cursor: pointer;
}
.activeBox {
border: 1px solid black;
}
.noAccord {
width: 100%;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
}
.priceTitle {
font-size: 18px;
font-weight: 600;
/* margin-bottom: 10px; */
display: flex;
align-items: center;
justify-content: center;
}
.priceContent {
display: flex;
align-items: center;
margin-bottom: 10px;
background-color: #f5f5f5;
border-radius: 5px;
}
.priceLabel {
color: #666;
margin-right: 10px;
padding: 10px;
}
.priceValue {
font-size: 20px;
font-weight: 600;
}
.priceDesc {
font-size: 12px;
color: #999;
}
.redText {
color: red;
}
.dialog-footer {
position: absolute;
bottom: 0;
right: 0;
padding: 10px;
/* text-align: center;
width: 350px; */
}
.blankBorder {
border: 1px solid #efefef;
width: 100%;
height: 100px;
border-radius: 15px;
display: grid;
grid-template-columns: 1fr 4fr;
margin-bottom: 10px;
}
.blankLeft {
display: flex;
align-items: center;
justify-content: center
}
.leftBlank {
width: 50px;
height: 50px;
border-right: 1px solid #e6e6e6;
background-color: #efefef;
border-radius: 10px;
}
.leftTop {
width: 100%;
height: 40px;
background-color: #efefef;
border-radius: 10px;
margin-bottom: 5px;
}
.leftBottom {
width: 100%;
height: 20px;
background-color: #efefef;
border-radius: 5px;
}
</style>
常见问题 | 腾讯位置服务 看文档一步一步来
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]