f
This commit is contained in:
2
.env
2
.env
@@ -7,3 +7,5 @@
|
|||||||
# VITE_API_BASE_URL=/api/v1
|
# VITE_API_BASE_URL=/api/v1
|
||||||
# VITE_API_BASE_URL=http://127.0.0.1:8888/api/v1
|
# VITE_API_BASE_URL=http://127.0.0.1:8888/api/v1
|
||||||
VITE_API_BASE_URL=https://www.tianyuancha.cn/api/v1
|
VITE_API_BASE_URL=https://www.tianyuancha.cn/api/v1
|
||||||
|
# H5 站点根地址(报告分享 / webview 链接),默认由 VITE_API_BASE_URL 推导
|
||||||
|
VITE_H5_BASE_URL=https://www.tianyuancha.cn
|
||||||
|
|||||||
@@ -35,3 +35,9 @@ export async function postQueryService(productEn, body, requestConfig) {
|
|||||||
const res = await http.post(`/query/service/${enc}`, body, requestConfig)
|
const res = await http.post(`/query/service/${enc}`, body, requestConfig)
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 生成报告分享链接(与 H5 ShareReportButton:POST /query/generate_share_link) */
|
||||||
|
export async function postGenerateShareLink(body, requestConfig) {
|
||||||
|
const res = await http.post('/query/generate_share_link', body, requestConfig)
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,12 +18,23 @@ const maskedName = computed(() => {
|
|||||||
return name.length > 1 ? `${name[0]}${'*'.repeat(name.length - 1)}` : '*'
|
return name.length > 1 ? `${name[0]}${'*'.repeat(name.length - 1)}` : '*'
|
||||||
})
|
})
|
||||||
|
|
||||||
const status = computed(() => {
|
/** 接口实际为 { result: { status } },示例数据可能为顶层 { status } */
|
||||||
const s = Number(p.value.status)
|
function readStatus(payload) {
|
||||||
if ([0, -1, -2, -4].includes(s))
|
if (!payload || typeof payload !== 'object')
|
||||||
return s
|
return null
|
||||||
|
const direct = Number(payload.status)
|
||||||
|
if ([0, -1, -2, -4].includes(direct))
|
||||||
|
return direct
|
||||||
|
const nested = payload.result
|
||||||
|
if (nested && typeof nested === 'object') {
|
||||||
|
const s = Number(nested.status)
|
||||||
|
if ([0, -1, -2, -4].includes(s))
|
||||||
|
return s
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
})
|
}
|
||||||
|
|
||||||
|
const status = computed(() => readStatus(p.value))
|
||||||
|
|
||||||
const resultText = computed(() => {
|
const resultText = computed(() => {
|
||||||
const s = status.value
|
const s = status.value
|
||||||
|
|||||||
@@ -182,6 +182,16 @@
|
|||||||
"navigationBarTextStyle": "black",
|
"navigationBarTextStyle": "black",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/webview/index",
|
||||||
|
"type": "page",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "网页",
|
||||||
|
"navigationStyle": "default",
|
||||||
|
"navigationBarBackgroundColor": "#ffffff",
|
||||||
|
"navigationBarTextStyle": "black"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subPackages": [],
|
"subPackages": [],
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function readQueryParams(row: Record<string, unknown>): Record<string, unknown>
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pickVin(qp: Record<string, unknown>): string {
|
function pickVin(qp: Record<string, unknown>): string {
|
||||||
const keys = ['vin_code', 'vin', 'frame_no', 'VIN', '车架号']
|
const keys = ['vin_code', 'vin', 'frame_no', 'VIN', '车架号', 'plate_no', 'car_license', '车牌号']
|
||||||
for (const k of keys) {
|
for (const k of keys) {
|
||||||
const v = qp[k]
|
const v = qp[k]
|
||||||
if (typeof v === 'string' && v.trim())
|
if (typeof v === 'string' && v.trim())
|
||||||
@@ -47,7 +47,7 @@ function pickVin(qp: Record<string, unknown>): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pickModel(qp: Record<string, unknown>): string {
|
function pickModel(qp: Record<string, unknown>): string {
|
||||||
const keys = ['model', 'vehicle_model', 'car_model', '车型', 'name', 'car_name']
|
const keys = ['carplate_type', 'car_type', 'model', 'vehicle_model', 'car_model', '车型', 'car_name']
|
||||||
for (const k of keys) {
|
for (const k of keys) {
|
||||||
const v = qp[k]
|
const v = qp[k]
|
||||||
if (typeof v === 'string' && v.trim())
|
if (typeof v === 'string' && v.trim())
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { getQueryDetailByOrderId, getQueryDetailByOrderNo } from '@/api'
|
import { getQueryDetailByOrderId, getQueryDetailByOrderNo, postGenerateShareLink } from '@/api'
|
||||||
import VehicleReportShell from '@/components/report/VehicleReportShell.vue'
|
import VehicleReportShell from '@/components/report/VehicleReportShell.vue'
|
||||||
|
import { buildReportShareUrl } from '@/utils/h5BaseUrl'
|
||||||
import { normalizeVehicleQueryData } from '@/utils/vehicleReportNormalize'
|
import { normalizeVehicleQueryData } from '@/utils/vehicleReportNormalize'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
@@ -21,6 +22,7 @@ const errText = ref('')
|
|||||||
const productName = ref('')
|
const productName = ref('')
|
||||||
const queryParams = ref({})
|
const queryParams = ref({})
|
||||||
const rows = ref(normalizeVehicleQueryData([]))
|
const rows = ref(normalizeVehicleQueryData([]))
|
||||||
|
const shareLoading = ref(false)
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
orderNo.value = options?.orderNo || ''
|
orderNo.value = options?.orderNo || ''
|
||||||
@@ -58,6 +60,65 @@ async function load() {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openReportInWebview(url, title = '报告详情') {
|
||||||
|
if (!url)
|
||||||
|
return
|
||||||
|
// #ifdef H5
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.open(url, '_blank')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/webview/index?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyShareUrl(url) {
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: url,
|
||||||
|
success: () => uni.showToast({ title: '链接已复制', icon: 'none' }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleShareReport() {
|
||||||
|
if (shareLoading.value)
|
||||||
|
return
|
||||||
|
if (!orderNo.value && !orderId.value) {
|
||||||
|
uni.showToast({ title: '缺少订单信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
shareLoading.value = true
|
||||||
|
try {
|
||||||
|
const body = orderId.value
|
||||||
|
? { order_id: Number.parseInt(orderId.value, 10) }
|
||||||
|
: { order_no: orderNo.value }
|
||||||
|
const res = await postGenerateShareLink(body)
|
||||||
|
if (res?.code !== 200 || !res.data?.share_link) {
|
||||||
|
uni.showToast({ title: res?.msg || '生成分享链接失败', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareUrl = buildReportShareUrl(res.data.share_link)
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['在网页版查看完整报告', '复制分享链接'],
|
||||||
|
success: (tap) => {
|
||||||
|
if (tap.tapIndex === 0)
|
||||||
|
openReportInWebview(shareUrl)
|
||||||
|
else if (tap.tapIndex === 1)
|
||||||
|
copyShareUrl(shareUrl)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
uni.showToast({ title: '生成分享链接失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
shareLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -75,6 +136,17 @@ async function load() {
|
|||||||
:query-params="queryParams"
|
:query-params="queryParams"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
/>
|
/>
|
||||||
|
<view class="share-bar">
|
||||||
|
<button
|
||||||
|
class="share-btn"
|
||||||
|
:loading="shareLoading"
|
||||||
|
:disabled="shareLoading"
|
||||||
|
@click="handleShareReport"
|
||||||
|
>
|
||||||
|
分享报告
|
||||||
|
</button>
|
||||||
|
<text class="share-tip">完整报告可在网页版查看,避免小程序渲染异常</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -97,4 +169,35 @@ async function load() {
|
|||||||
padding: 24rpx 24rpx 48rpx;
|
padding: 24rpx 24rpx 48rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.share-bar {
|
||||||
|
margin-top: 32rpx;
|
||||||
|
padding: 0 8rpx 16rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
background: #1768ff;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-btn::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-tip {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #86909c;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
66
src/pages/webview/index.vue
Normal file
66
src/pages/webview/index.vue
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
definePage({
|
||||||
|
style: {
|
||||||
|
navigationBarTitleText: '网页',
|
||||||
|
navigationStyle: 'default',
|
||||||
|
navigationBarBackgroundColor: '#ffffff',
|
||||||
|
navigationBarTextStyle: 'black',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const pageUrl = ref('')
|
||||||
|
|
||||||
|
onLoad((options) => {
|
||||||
|
pageUrl.value = options?.url ? decodeURIComponent(options.url) : ''
|
||||||
|
const title = options?.title ? decodeURIComponent(options.title) : ''
|
||||||
|
if (title)
|
||||||
|
uni.setNavigationBarTitle({ title })
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="webview-page">
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<web-view v-if="pageUrl" :src="pageUrl" />
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifndef MP-WEIXIN -->
|
||||||
|
<view v-if="pageUrl" class="fallback">
|
||||||
|
<text class="fallback-tip">当前环境不支持内嵌网页,请复制链接到浏览器打开</text>
|
||||||
|
<text class="fallback-url">{{ pageUrl }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<view v-if="!pageUrl" class="empty">
|
||||||
|
链接无效
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.webview-page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty,
|
||||||
|
.fallback {
|
||||||
|
padding: 100rpx 32rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #86909c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fallback-tip {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fallback-url {
|
||||||
|
display: block;
|
||||||
|
word-break: break-all;
|
||||||
|
color: #1768ff;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -51,10 +51,13 @@ const maskedName = computed(() => {
|
|||||||
return name.length > 1 ? name[0] + '*'.repeat(name.length - 1) : '*';
|
return name.length > 1 ? name[0] + '*'.repeat(name.length - 1) : '*';
|
||||||
});
|
});
|
||||||
|
|
||||||
// status: 0 一致, -1 不一致, -2 非法姓名, -4 无记录
|
// status: 0 一致, -1 不一致, -2 非法姓名, -4 无记录(接口多为 result.status)
|
||||||
const status = computed(() => {
|
const status = computed(() => {
|
||||||
const s = props.data?.status;
|
const d = props.data;
|
||||||
if (s === 0 || s === -1 || s === -2 || s === -4) return s;
|
const direct = d?.status;
|
||||||
|
if (direct === 0 || direct === -1 || direct === -2 || direct === -4) return direct;
|
||||||
|
const nested = d?.result?.status;
|
||||||
|
if (nested === 0 || nested === -1 || nested === -2 || nested === -4) return nested;
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
18
src/utils/h5BaseUrl.js
Normal file
18
src/utils/h5BaseUrl.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/** H5 站点根地址,用于构造报告分享 / 网页版链接 */
|
||||||
|
export function resolveH5BaseUrl() {
|
||||||
|
const fromEnv = import.meta.env.VITE_H5_BASE_URL
|
||||||
|
if (fromEnv)
|
||||||
|
return String(fromEnv).replace(/\/$/, '')
|
||||||
|
|
||||||
|
const apiBase = import.meta.env.VITE_API_BASE_URL || ''
|
||||||
|
if (apiBase.includes('/api/v1'))
|
||||||
|
return apiBase.replace(/\/api\/v1\/?$/, '')
|
||||||
|
|
||||||
|
return 'https://www.tianyuancha.cn'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 与 tyc-webview-v2 ShareReportButton 一致:/report/share/:linkId */
|
||||||
|
export function buildReportShareUrl(linkId) {
|
||||||
|
const base = resolveH5BaseUrl()
|
||||||
|
return `${base}/report/share/${encodeURIComponent(linkId)}`
|
||||||
|
}
|
||||||
3
uni-pages.d.ts
vendored
3
uni-pages.d.ts
vendored
@@ -21,7 +21,8 @@ type _LocationUrl =
|
|||||||
"/pages/report/detail" |
|
"/pages/report/detail" |
|
||||||
"/pages/toolbox/category" |
|
"/pages/toolbox/category" |
|
||||||
"/pages/toolbox/index" |
|
"/pages/toolbox/index" |
|
||||||
"/pages/toolbox/query";
|
"/pages/toolbox/query" |
|
||||||
|
"/pages/webview/index";
|
||||||
|
|
||||||
interface NavigateToOptions {
|
interface NavigateToOptions {
|
||||||
url: _LocationUrl;
|
url: _LocationUrl;
|
||||||
|
|||||||
Reference in New Issue
Block a user