chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,319 @@
<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>