This commit is contained in:
Mrx
2026-06-08 12:49:54 +08:00
parent bb31ce2842
commit 0e5ce99594
10 changed files with 232 additions and 12 deletions

2
.env
View File

@@ -7,3 +7,5 @@
# VITE_API_BASE_URL=/api/v1
# VITE_API_BASE_URL=http://127.0.0.1:8888/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

View File

@@ -35,3 +35,9 @@ export async function postQueryService(productEn, body, requestConfig) {
const res = await http.post(`/query/service/${enc}`, body, requestConfig)
return res.data
}
/** 生成报告分享链接(与 H5 ShareReportButtonPOST /query/generate_share_link */
export async function postGenerateShareLink(body, requestConfig) {
const res = await http.post('/query/generate_share_link', body, requestConfig)
return res.data
}

View File

@@ -18,12 +18,23 @@ const maskedName = computed(() => {
return name.length > 1 ? `${name[0]}${'*'.repeat(name.length - 1)}` : '*'
})
const status = computed(() => {
const s = Number(p.value.status)
/** 接口实际为 { result: { status } },示例数据可能为顶层 { status } */
function readStatus(payload) {
if (!payload || typeof payload !== 'object')
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
})
}
const status = computed(() => readStatus(p.value))
const resultText = computed(() => {
const s = status.value

View File

@@ -182,6 +182,16 @@
"navigationBarTextStyle": "black",
"enablePullDownRefresh": false
}
},
{
"path": "pages/webview/index",
"type": "page",
"style": {
"navigationBarTitleText": "网页",
"navigationStyle": "default",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
],
"subPackages": [],

View File

@@ -35,7 +35,7 @@ function readQueryParams(row: Record<string, unknown>): Record<string, unknown>
}
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) {
const v = qp[k]
if (typeof v === 'string' && v.trim())
@@ -47,7 +47,7 @@ function pickVin(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) {
const v = qp[k]
if (typeof v === 'string' && v.trim())

View File

@@ -1,8 +1,9 @@
<script setup>
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
import { getQueryDetailByOrderId, getQueryDetailByOrderNo } from '@/api'
import { getQueryDetailByOrderId, getQueryDetailByOrderNo, postGenerateShareLink } from '@/api'
import VehicleReportShell from '@/components/report/VehicleReportShell.vue'
import { buildReportShareUrl } from '@/utils/h5BaseUrl'
import { normalizeVehicleQueryData } from '@/utils/vehicleReportNormalize'
definePage({
@@ -21,6 +22,7 @@ const errText = ref('')
const productName = ref('')
const queryParams = ref({})
const rows = ref(normalizeVehicleQueryData([]))
const shareLoading = ref(false)
onLoad((options) => {
orderNo.value = options?.orderNo || ''
@@ -58,6 +60,65 @@ async function load() {
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>
<template>
@@ -75,6 +136,17 @@ async function load() {
:query-params="queryParams"
: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>
</template>
@@ -97,4 +169,35 @@ async function load() {
padding: 24rpx 24rpx 48rpx;
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>

View 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>

View File

@@ -51,10 +51,13 @@ const maskedName = computed(() => {
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 s = props.data?.status;
if (s === 0 || s === -1 || s === -2 || s === -4) return s;
const d = props.data;
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;
});

18
src/utils/h5BaseUrl.js Normal file
View 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
View File

@@ -21,7 +21,8 @@ type _LocationUrl =
"/pages/report/detail" |
"/pages/toolbox/category" |
"/pages/toolbox/index" |
"/pages/toolbox/query";
"/pages/toolbox/query" |
"/pages/webview/index";
interface NavigateToOptions {
url: _LocationUrl;