前进之路 发表于 2025-4-12 17:40:04

腾讯地图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]
查看完整版本: 腾讯地图web端签名校验方法