320 lines
8.5 KiB
Plaintext
320 lines
8.5 KiB
Plaintext
<template>
|
||
<view class="container page-container">
|
||
<!-- 导航栏 -->
|
||
<custom-nav-bar :title="fileName" show-back>
|
||
<template v-slot:right>
|
||
<!-- #ifdef H5 || WEB -->
|
||
<text
|
||
class="iconfont icon-ShareAltOutlined font-48"
|
||
@click="handleShare"
|
||
></text>
|
||
<!-- #endif -->
|
||
</template>
|
||
</custom-nav-bar>
|
||
|
||
<view class="file-preview-content">
|
||
<!-- 加载中 -->
|
||
<!-- <template v-if="isLoading">
|
||
<view class="loading-box">
|
||
<image
|
||
class="icon-loading-image"
|
||
src="@/static/assets/icon_loading.svg"
|
||
mode="widthFix"
|
||
/>
|
||
</view>
|
||
</template> -->
|
||
|
||
<!-- Web-View 预览 -->
|
||
<template v-if="previewUrl">
|
||
<!-- #ifdef H5 || WEB -->
|
||
<iframe
|
||
class="w-full h-full"
|
||
frameborder="0"
|
||
:src="previewUrl"
|
||
></iframe>
|
||
<!-- #endif -->
|
||
<!-- #ifdef MP-WEIXIN -->
|
||
<web-view
|
||
class="w-full h-full"
|
||
:src="previewUrl"
|
||
id="webview"
|
||
></web-view>
|
||
<!-- #endif -->
|
||
</template>
|
||
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { onAddToFavorites } from "@dcloudio/uni-app";
|
||
import {
|
||
parseUrlInfo,
|
||
getFileName,
|
||
} from "@/subpackages/utils/parseUrlInfo.uts";
|
||
import { useI18n } from "@/utils/i18n";
|
||
|
||
const { t } = useI18n();
|
||
|
||
// 请求工具
|
||
import {
|
||
apiUserTicketCreate,
|
||
apiAgentConversationShare,
|
||
} from "@/servers/agentDev.uts";
|
||
import { SUCCESS_CODE } from "@/constants/codes.constants";
|
||
|
||
// 环境配置
|
||
import { API_BASE_URL } from "@/constants/config";
|
||
|
||
const previewUrl = ref<string>(""); // 预览地址
|
||
const fileName = ref<string>(""); // 文件名
|
||
const pageTitle = ref<string>(""); // 页面标题
|
||
const fileType = ref<string>(""); // 文件类型
|
||
|
||
const isLoading = ref<boolean>(true); // 是否加载中
|
||
const conversationId = ref<string>(null); // 会话ID
|
||
const fileProxyUrl = ref<string>(""); // 文件路径
|
||
const sk = ref<string>(""); // sk 微信小程序中需要 sk 查询参数
|
||
const isDev = process.env.NODE_ENV === "development";
|
||
|
||
let baseUrl = "";
|
||
// #ifdef H5 || WEB
|
||
baseUrl = isDev ? API_BASE_URL : window?.location?.origin;
|
||
// #endif
|
||
|
||
// #ifdef MP-WEIXIN
|
||
baseUrl = API_BASE_URL;
|
||
// #endif
|
||
|
||
/**
|
||
* 获取 shareKey
|
||
*/
|
||
const getConversationShareKey = async (
|
||
conversationId: string,
|
||
content: string = "",
|
||
): Promise<string> => {
|
||
const params = {
|
||
conversationId,
|
||
type: "CONVERSATION",
|
||
content,
|
||
};
|
||
|
||
const { data, code } = await apiAgentConversationShare(params);
|
||
|
||
if (code !== SUCCESS_CODE || !data?.shareKey) {
|
||
throw new Error(t("Mobile.FilePreview.getShareKeyFailed"));
|
||
}
|
||
|
||
return data.shareKey;
|
||
};
|
||
|
||
// 分享文件
|
||
const handleShare = async () => {
|
||
if (!baseUrl) {
|
||
uni.showToast({
|
||
title: t("Mobile.FilePreview.shareDomainMissing"),
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const shareKey = await getConversationShareKey(
|
||
conversationId.value,
|
||
fileProxyUrl.value || "",
|
||
);
|
||
|
||
const previewUrlValue = `${baseUrl}/static/file-preview.html?sk=${shareKey}`;
|
||
|
||
// 复制到剪切板
|
||
uni.setClipboardData({
|
||
data: previewUrlValue,
|
||
success: () => {
|
||
uni.showToast({
|
||
title: t("Mobile.FilePreview.shareSuccess"),
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
},
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: t("Mobile.FilePreview.shareFailed"),
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
},
|
||
});
|
||
} catch (error) {
|
||
console.error("分享失败:", error);
|
||
uni.showToast({
|
||
title: t("Mobile.FilePreview.shareFailed"),
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
}
|
||
};
|
||
|
||
// 获取 ticket
|
||
const getTicket = async () => {
|
||
try {
|
||
const { code, data } = await apiUserTicketCreate();
|
||
|
||
return code === SUCCESS_CODE && data ? data : "";
|
||
} catch (e) {
|
||
console.log(e);
|
||
return "";
|
||
}
|
||
};
|
||
|
||
// 设置 html 文件标题
|
||
const setHtmlTitle = (url: string) => {
|
||
uni.request({
|
||
url,
|
||
method: "GET",
|
||
success: (res) => {
|
||
const data = res.data;
|
||
// 使用正则表达式提取 <title>...</title> 中的内容
|
||
const match = data.match(/<title[^>]*>([^<]+)<\/title>/i);
|
||
if (match && match[1]) {
|
||
const title = match[1].trim();
|
||
// 赋值给 pageTitle
|
||
pageTitle.value = title;
|
||
// 更新导航栏标题
|
||
uni.setNavigationBarTitle({
|
||
title: title,
|
||
});
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error("获取 html 失败", err);
|
||
},
|
||
});
|
||
};
|
||
|
||
onLoad(async (options: { cId: number; fileProxyUrl: string }) => {
|
||
let { cId, fileProxyUrl: fileProxyUrlValue } = options;
|
||
if (!cId || !fileProxyUrlValue) {
|
||
uni.showToast({
|
||
title: t("Mobile.FilePreview.invalidParams"),
|
||
icon: "none",
|
||
});
|
||
isLoading.value = false;
|
||
return;
|
||
}
|
||
|
||
const { path, query } = parseUrlInfo(decodeURIComponent(fileProxyUrlValue));
|
||
|
||
fileProxyUrl.value = path;
|
||
|
||
// 得到文件名
|
||
const fileNameValue = getFileName(path);
|
||
conversationId.value = cId;
|
||
fileName.value = fileNameValue;
|
||
|
||
uni.setNavigationBarTitle({ title: fileNameValue });
|
||
|
||
// 得到文件类型
|
||
const fileTypeValue = fileNameValue.split(".").pop();
|
||
fileType.value = fileTypeValue;
|
||
|
||
try {
|
||
if (query?.sk) {
|
||
// 有 sk 参数,直接使用
|
||
sk.value = query.sk;
|
||
previewUrl.value = `${baseUrl}/static/file-preview.html?sk=${query.sk}`;
|
||
// 设置 html 文件标题
|
||
setHtmlTitle(baseUrl + fileProxyUrl.value + "?sk=" + query.sk);
|
||
} else {
|
||
// 没有 sk 参数,需要获取 shareKey 和 _ticket
|
||
// 1. 获取 shareKey 用于后续分享功能
|
||
let shareKey = await getConversationShareKey(cId, path);
|
||
sk.value = shareKey;
|
||
const fileUrl = encodeURIComponent(`${fileProxyUrl.value}`);
|
||
|
||
// #ifdef H5 || WEB
|
||
if (isDev) {
|
||
// _sk 用于下载功能,_ticket 用于当前页面访问(_ticket 只能消费一次,适合当前用户自己访问)
|
||
const _ticket = await getTicket();
|
||
previewUrl.value = `${baseUrl}/static/file-preview.html?fileUrl=${baseUrl + fileUrl}&_ticket=${_ticket}&_sk=${shareKey}`;
|
||
} else {
|
||
previewUrl.value = `${baseUrl}/static/file-preview.html?fileUrl=${baseUrl + fileUrl}&sk=${shareKey}`;
|
||
}
|
||
// #endif
|
||
|
||
// #ifdef MP-WEIXIN
|
||
// _sk 用于下载功能,_ticket 用于当前页面访问(_ticket 只能消费一次,适合当前用户自己访问)
|
||
const _ticket = await getTicket();
|
||
previewUrl.value = `${baseUrl}/static/file-preview.html?fileUrl=${baseUrl + fileUrl}&_ticket=${_ticket}&_sk=${shareKey}`;
|
||
// 设置 html 文件标题
|
||
setHtmlTitle(baseUrl + fileProxyUrl.value + "?sk=" + shareKey);
|
||
// #endif
|
||
}
|
||
} catch (error) {
|
||
console.error("构建预览URL失败:", error);
|
||
} finally {
|
||
isLoading.value = false;
|
||
}
|
||
});
|
||
|
||
// #ifdef MP-WEIXIN
|
||
// 转发给朋友
|
||
// 分享到首页,由首页自动跳转到文件预览页面,解决分享后无返回按钮问题
|
||
onShareAppMessage(() => {
|
||
return {
|
||
title: pageTitle.value || fileName.value || "",
|
||
path:
|
||
"/pages/index/index?cId=" +
|
||
conversationId.value +
|
||
"&fileProxyUrl=" +
|
||
encodeURIComponent(`${fileProxyUrl.value}?sk=${sk.value}`),
|
||
};
|
||
});
|
||
|
||
// 收藏
|
||
onAddToFavorites(() => {
|
||
return {
|
||
title: pageTitle.value || fileName.value || "",
|
||
path:
|
||
"/pages/index/index?cId=" +
|
||
conversationId.value +
|
||
"&fileProxyUrl=" +
|
||
encodeURIComponent(`${fileProxyUrl.value}?sk=${sk.value}`),
|
||
};
|
||
});
|
||
// #endif
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.container {
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.file-preview-content {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.loading-box {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
|
||
.icon-loading-image {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
margin-right: 10rpx;
|
||
-webkit-touch-callout: none !important;
|
||
-webkit-user-select: none !important;
|
||
user-select: none !important;
|
||
}
|
||
}
|
||
|
||
.text-gray-500 {
|
||
color: #999;
|
||
}
|
||
}
|
||
</style>
|