f
This commit is contained in:
1
.env
1
.env
@@ -15,3 +15,4 @@ VITE_API_BASE_URL=https://www.quannengcha.com/api/v1
|
||||
|
||||
# 想用本地接口时注释掉上面那行,取消下面这行注释:
|
||||
# VITE_API_BASE_URL=http://127.0.0.1:8888/api/v1
|
||||
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# 生产构建:指向 qnc-server-v3 线上网关
|
||||
VITE_API_BASE_URL=https://www.quannengcha.com/api/v1
|
||||
# qnc-webview-v3 H5 站点(小程序 web-view / 分享链接)
|
||||
VITE_H5_ORIGIN=https://www.quannengcha.com
|
||||
|
||||
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -10,6 +10,7 @@ declare module 'vue' {
|
||||
AppFooter: typeof import('./src/components/AppFooter.vue')['default']
|
||||
AppLogos: typeof import('./src/components/AppLogos.vue')['default']
|
||||
InputEntry: typeof import('./src/components/InputEntry.vue')['default']
|
||||
ReportShareBar: typeof import('./src/components/report/ReportShareBar.vue')['default']
|
||||
VehicleBlockFallback: typeof import('./src/components/report/VehicleBlockFallback.vue')['default']
|
||||
VehicleBlockQCXG1H7Y: typeof import('./src/components/report/blocks/VehicleBlockQCXG1H7Y.vue')['default']
|
||||
VehicleBlockQCXG1U4U: typeof import('./src/components/report/blocks/VehicleBlockQCXG1U4U.vue')['default']
|
||||
|
||||
2
env.d.ts
vendored
2
env.d.ts
vendored
@@ -5,6 +5,8 @@ interface ImportMetaEnv {
|
||||
readonly VITE_API_BASE_URL?: string
|
||||
/** H5 开发代理目标,仅 vite 使用,默认 http://127.0.0.1:8888 */
|
||||
readonly VITE_API_PROXY_TARGET?: string
|
||||
/** qnc-webview-v3 站点根地址,如 https://www.quannengcha.com */
|
||||
readonly VITE_H5_ORIGIN?: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
@@ -29,6 +29,16 @@ export async function getQueryList(params = {}, requestConfig) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
/** 生成报告分享链接(与 H5 ShareReportButton POST /query/generate_share_link 一致) */
|
||||
export async function generateQueryShareLink(body, requestConfig = {}) {
|
||||
const res = await http.post('/query/generate_share_link', body, {
|
||||
skipLoading: true,
|
||||
skipBizToast: true,
|
||||
...requestConfig,
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
|
||||
/** 创建查询临时单(与 H5 `InquireForm` 一致:POST /api/v1/query/service/:product) */
|
||||
export async function postQueryService(productEn, body, requestConfig) {
|
||||
const enc = encodeURIComponent(productEn)
|
||||
|
||||
107
src/components/report/ReportShareBar.vue
Normal file
107
src/components/report/ReportShareBar.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { fetchReportShareUrl } from '@/utils/reportH5Link'
|
||||
|
||||
const props = defineProps({
|
||||
orderId: { type: String, default: '' },
|
||||
orderNo: { type: String, default: '' },
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
async function copyLink() {
|
||||
if (loading.value)
|
||||
return
|
||||
if (!props.orderId && !props.orderNo) {
|
||||
uni.showToast({ title: '缺少订单信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
uni.showLoading({ title: '生成链接…', mask: true })
|
||||
try {
|
||||
const url = await fetchReportShareUrl({
|
||||
orderId: props.orderId,
|
||||
orderNo: props.orderNo,
|
||||
})
|
||||
uni.hideLoading()
|
||||
await new Promise((resolve, reject) => {
|
||||
uni.setClipboardData({
|
||||
data: url,
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
uni.showToast({ title: '链接已复制,可在浏览器打开', icon: 'none', duration: 2500 })
|
||||
}
|
||||
catch (e) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: e?.message || '复制失败',
|
||||
icon: 'none',
|
||||
duration: 2500,
|
||||
})
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="share-bar">
|
||||
<view class="share-bar-inner">
|
||||
<button
|
||||
class="share-btn"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
@tap="copyLink"
|
||||
>
|
||||
<view class="share-btn-icon i-carbon-copy" />
|
||||
<text>{{ loading ? '生成中…' : '复制链接' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.share-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
border-top: 1rpx solid #e5e6eb;
|
||||
box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.share-bar-inner {
|
||||
padding: 20rpx 24rpx calc(20rpx + env(safe-area-inset-bottom));
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background: linear-gradient(90deg, #1768ff 0%, #4d94ff 100%);
|
||||
border-radius: 44rpx;
|
||||
}
|
||||
|
||||
.share-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.share-btn-icon {
|
||||
font-size: 36rpx;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
/** 人车核验简版(与 H5 CQCXGGB2Q:verify_code) */
|
||||
/** 人车核验简版(与 H5 CQCXGGB2Q:verify_code 1 一致 / 0 不一致) */
|
||||
const props = defineProps({
|
||||
blockTitle: { type: String, default: '' },
|
||||
apiId: { type: String, default: '' },
|
||||
@@ -23,7 +23,7 @@ const isMatch = computed(() => {
|
||||
const n = Number(code)
|
||||
if (n === 1)
|
||||
return true
|
||||
if (n === 2)
|
||||
if (n === 0)
|
||||
return false
|
||||
return null
|
||||
})
|
||||
@@ -32,7 +32,7 @@ const resultText = computed(() => {
|
||||
if (isMatch.value === true)
|
||||
return '一致'
|
||||
if (isMatch.value === false)
|
||||
return '不匹配'
|
||||
return '不一致'
|
||||
return '暂无结果'
|
||||
})
|
||||
|
||||
|
||||
17
src/config/h5.js
Normal file
17
src/config/h5.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* qnc-webview-v3 H5 站点根地址(不含 /api/v1)
|
||||
* 小程序内 web-view 打开报告页、分享链接拼接用。
|
||||
*/
|
||||
export function getH5Origin() {
|
||||
const fromEnv = import.meta.env.VITE_H5_ORIGIN?.trim()
|
||||
if (fromEnv)
|
||||
return fromEnv.replace(/\/$/, '')
|
||||
|
||||
const apiBase = import.meta.env.VITE_API_BASE_URL?.trim() || ''
|
||||
if (/quannengcha\.com/i.test(apiBase))
|
||||
return apiBase.replace(/\/api\/v1\/?$/i, '').replace(/\/$/, '') || 'https://www.quannengcha.com'
|
||||
|
||||
// 本地联调 API 时,H5 仍默认走线上(web-view 无法打开 localhost)
|
||||
// return 'https://www.quannengcha.com'
|
||||
return 'http://127.0.0.1:5678'
|
||||
}
|
||||
@@ -17,7 +17,7 @@ export const VEHICLE_API_TITLES = {
|
||||
QCXG3Z3L: '车辆维保详细版查询',
|
||||
QCXG1H7Y: '车辆过户简版查询',
|
||||
QCXG4I1Z: '车辆过户详版查询',
|
||||
QCXGGB2Q: '车辆二要素核验 V1',
|
||||
QCXGGB2Q: '车辆二要素核验简版',
|
||||
QCXGP00W: '车辆出险详版查询',
|
||||
QCXGYTS2: '人车核验(详版)',
|
||||
QCXGGJ3A: '车辆 VIN 码查询号牌简版',
|
||||
|
||||
@@ -150,6 +150,16 @@
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/report/webview",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationBarTitleText": "完整版报告",
|
||||
"navigationStyle": "default",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/toolbox/category",
|
||||
"type": "page",
|
||||
|
||||
@@ -3,6 +3,7 @@ import { onLoad, onUnload } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { getQueryDetailByOrderId, getQueryDetailByOrderNo } from '@/api'
|
||||
import VehicleReportShell from '@/components/report/VehicleReportShell.vue'
|
||||
import ReportShareBar from '@/components/report/ReportShareBar.vue'
|
||||
import { parseEncryptedQueryReport } from '@/utils/queryReportParse'
|
||||
import { normalizeVehicleQueryData } from '@/utils/vehicleReportNormalize'
|
||||
|
||||
@@ -85,7 +86,7 @@ async function load(opts = {}) {
|
||||
if (parsed.ok) {
|
||||
queryParams.value = parsed.queryParams
|
||||
rows.value = parsed.rows
|
||||
errText.value = rows.value.length ? '' : '暂无报告模块数据'
|
||||
errText.value = rows.value.length ? '' : '渲染失败,可复制底部链接在浏览器查看'
|
||||
}
|
||||
else {
|
||||
rows.value = normalizeVehicleQueryData([])
|
||||
@@ -105,7 +106,7 @@ async function load(opts = {}) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="page-root">
|
||||
<view class="page-root" :class="{ 'has-share-bar': orderId || orderNo }">
|
||||
<view v-if="loading && !pending" class="state">
|
||||
加载中…
|
||||
</view>
|
||||
@@ -124,6 +125,11 @@ async function load(opts = {}) {
|
||||
:rows="rows"
|
||||
/>
|
||||
</view>
|
||||
<ReportShareBar
|
||||
v-if="orderId || orderNo"
|
||||
:order-id="orderId"
|
||||
:order-no="orderNo"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -134,8 +140,12 @@ async function load(opts = {}) {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.page-root.has-share-bar {
|
||||
padding-bottom: calc(128rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.state {
|
||||
padding: 100rpx 32rpx;
|
||||
padding: 48rpx 32rpx 0;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #86909c;
|
||||
|
||||
57
src/pages/report/webview.vue
Normal file
57
src/pages/report/webview.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '完整版报告',
|
||||
navigationStyle: 'default',
|
||||
navigationBarBackgroundColor: '#ffffff',
|
||||
navigationBarTextStyle: 'black',
|
||||
},
|
||||
})
|
||||
|
||||
const pageUrl = ref('')
|
||||
const loadError = ref('')
|
||||
|
||||
onLoad((options) => {
|
||||
const raw = options?.url || ''
|
||||
if (!raw) {
|
||||
loadError.value = '缺少报告地址'
|
||||
return
|
||||
}
|
||||
try {
|
||||
pageUrl.value = decodeURIComponent(raw)
|
||||
}
|
||||
catch {
|
||||
pageUrl.value = raw
|
||||
}
|
||||
if (!/^https:\/\//i.test(pageUrl.value)) {
|
||||
loadError.value = '报告链接无效'
|
||||
pageUrl.value = ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<web-view v-if="pageUrl" :src="pageUrl" />
|
||||
<view v-else class="err">
|
||||
<text>{{ loadError || '无法加载报告' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.err {
|
||||
padding: 80rpx 32rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #86909c;
|
||||
}
|
||||
</style>
|
||||
@@ -65,17 +65,17 @@ const maskedName = computed(() => {
|
||||
return name.length > 1 ? name[0] + '*'.repeat(name.length - 1) : '*';
|
||||
});
|
||||
|
||||
// verify_code: 1 一致,2 不匹配
|
||||
// verify_code: 1 一致,0 不一致
|
||||
const isMatch = computed(() => {
|
||||
const code = props.data?.verify_code;
|
||||
if (code === 1) return true;
|
||||
if (code === 2) return false;
|
||||
return null; // 无有效数据时
|
||||
const n = Number(props.data?.verify_code);
|
||||
if (n === 1) return true;
|
||||
if (n === 0) return false;
|
||||
return null;
|
||||
});
|
||||
|
||||
const resultText = computed(() => {
|
||||
if (isMatch.value === true) return '一致';
|
||||
if (isMatch.value === false) return '不匹配';
|
||||
if (isMatch.value === false) return '不一致';
|
||||
return '暂无结果';
|
||||
});
|
||||
|
||||
|
||||
27
src/utils/reportH5Link.js
Normal file
27
src/utils/reportH5Link.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { generateQueryShareLink } from '@/api/query'
|
||||
import { getH5Origin } from '@/config/h5'
|
||||
|
||||
/** 登录用户直接打开 H5 报告(需 H5 端同账号登录) */
|
||||
export function buildDirectReportUrl({ orderId, orderNo }) {
|
||||
const base = getH5Origin()
|
||||
if (orderNo)
|
||||
return `${base}/report?orderNo=${encodeURIComponent(orderNo)}`
|
||||
if (orderId)
|
||||
return `${base}/report?orderId=${encodeURIComponent(String(orderId))}`
|
||||
return ''
|
||||
}
|
||||
|
||||
/** 与 H5 ShareReportButton 一致:POST /query/generate_share_link → /report/share/:id */
|
||||
export async function fetchReportShareUrl({ orderId, orderNo }) {
|
||||
const body = orderId
|
||||
? { order_id: String(orderId) }
|
||||
: { order_no: String(orderNo) }
|
||||
|
||||
const res = await generateQueryShareLink(body)
|
||||
const linkId = res?.data?.share_link
|
||||
if (res?.code !== 200 || !linkId)
|
||||
throw new Error(res?.msg || '生成分享链接失败')
|
||||
|
||||
const base = getH5Origin()
|
||||
return `${base}/report/share/${encodeURIComponent(linkId)}`
|
||||
}
|
||||
1
uni-pages.d.ts
vendored
1
uni-pages.d.ts
vendored
@@ -19,6 +19,7 @@ type _LocationUrl =
|
||||
"/pages/legal/privacy-policy" |
|
||||
"/pages/legal/user-agreement" |
|
||||
"/pages/report/detail" |
|
||||
"/pages/report/webview" |
|
||||
"/pages/toolbox/category" |
|
||||
"/pages/toolbox/index" |
|
||||
"/pages/toolbox/query";
|
||||
|
||||
Reference in New Issue
Block a user