This commit is contained in:
ywyonui 2025-07-27 23:37:28 +08:00
commit 5ab1697d2b
12 changed files with 1627 additions and 220 deletions

View File

@ -163,9 +163,13 @@ export const typesFindTreeApi = async () => {
export const resourcesFindPageApi = async (params: any) => { export const resourcesFindPageApi = async (params: any) => {
return await get("/api/resources/findPage", params); return await get("/api/resources/findPage", params);
}; };
// 获取资源分页
export const resourcesSaveApi = async (params: any) => {
return await post("/api/resources/save", params);
};
export const resourcesAddNumByTypeApi = async (params: any) => { export const resourcesAddNumByTypeApi = async (params: any) => {
return await post("/api/resources/addNumByType", params); return await post("/api/resources/addNumByType", params);
}; };
// 获取工作量 // 获取工作量

View File

@ -1,7 +1,7 @@
const ip: string = "127.0.0.1:8897"; const ip: string = "127.0.0.1:8897";
// const ip: string = "yufangzc.com"; // const ip: string = "yufangzc.com";
// const ip: string = "yufangzc.com"; // const ip: string = "lzcxsx.cn";
const fwqip: string = "yufangzc.com"; const fwqip: string = "lzcxsx.cn";
//打包服务器接口代理标识 //打包服务器接口代理标识
const SERVERAGENT: string = "/jsd-api"; const SERVERAGENT: string = "/jsd-api";
//本地代理url地址,配置了就启动代理,没配置就不启动代理 //本地代理url地址,配置了就启动代理,没配置就不启动代理
@ -13,7 +13,9 @@ export const BASE_URL: string =
export const BASE_WS_URL: string = `wss://${ip}`; export const BASE_WS_URL: string = `wss://${ip}`;
//图片地址 //图片地址
// export const BASE_IMAGE_URL: string = process.env.NODE_ENV == "development" ? `https://${ip}` : `https://${fwqip}`; // export const BASE_IMAGE_URL: string = process.env.NODE_ENV == "development" ? `https://${ip}` : `https://${fwqip}`;
export const BASE_IMAGE_URL = `http://${fwqip}`; export const BASE_IMAGE_URL = `https://${fwqip}`;
// kkFileView预览服务地址
export const KK_FILE_VIEW_URL: string = `http://${fwqip}:8891`;
//存token的key //存token的key
export const AUTH_KEY: string = "satoken"; export const AUTH_KEY: string = "satoken";
//token过期返回状态码 //token过期返回状态码

View File

@ -51,6 +51,24 @@
"backgroundColor": "#ffffff" "backgroundColor": "#ffffff"
} }
}, },
{
"path": "pages/system/web-viewer/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "文件预览",
"enablePullDownRefresh": false,
"backgroundColor": "#f5f5f5"
}
},
{
"path": "pages/system/video-player/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "视频播放",
"enablePullDownRefresh": false,
"backgroundColor": "#000000"
}
},
{ {
"path": "pages/system/updatePopup/updatePopup", "path": "pages/system/updatePopup/updatePopup",
"style": { "style": {

View File

@ -0,0 +1,159 @@
<template>
<view class="video-player-page">
<view class="player-header">
<view class="header-left">
<u-icon name="arrow-left" size="20" @click="goBack"></u-icon>
<text class="header-title">{{ videoTitle }}</text>
</view>
</view>
<view class="video-container">
<video
:src="videoUrl"
:poster="posterUrl"
:title="videoTitle"
controls
show-center-play-btn
show-play-btn
show-fullscreen-btn
show-progress
enable-progress-gesture
@error="handleVideoError"
@fullscreenchange="handleFullscreenChange"
@play="handleVideoPlay"
@pause="handleVideoPause"
@ended="handleVideoEnded"
class="video-player"
></video>
</view>
<view class="video-info">
<view class="info-title">{{ videoTitle }}</view>
<view class="info-desc" v-if="videoDesc">{{ videoDesc }}</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const videoUrl = ref('');
const videoTitle = ref('视频播放');
const videoDesc = ref('');
const posterUrl = ref('');
onLoad((options) => {
if (options.url) {
videoUrl.value = decodeURIComponent(options.url);
}
if (options.title) {
videoTitle.value = decodeURIComponent(options.title);
}
if (options.desc) {
videoDesc.value = decodeURIComponent(options.desc);
}
if (options.poster) {
posterUrl.value = decodeURIComponent(options.poster);
}
console.log('视频播放URL:', videoUrl.value);
});
const goBack = () => {
uni.navigateBack();
};
const handleVideoError = (e) => {
console.error('视频播放错误:', e);
uni.showToast({
title: '视频播放失败,请检查网络连接',
icon: 'none',
duration: 3000
});
};
const handleFullscreenChange = (e) => {
console.log('全屏状态变化:', e.detail.fullScreen);
};
const handleVideoPlay = () => {
console.log('视频开始播放');
};
const handleVideoPause = () => {
console.log('视频暂停');
};
const handleVideoEnded = () => {
console.log('视频播放结束');
};
</script>
<style scoped>
.video-player-page {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background-color: #000;
}
.player-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
background-color: rgba(0, 0, 0, 0.8);
flex-shrink: 0;
z-index: 10;
}
.header-left {
display: flex;
align-items: center;
gap: 20rpx;
}
.header-title {
font-size: 32rpx;
font-weight: bold;
color: #ffffff;
max-width: 400rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.video-container {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: #000;
}
.video-player {
width: 100%;
height: 100%;
}
.video-info {
padding: 30rpx;
background-color: #ffffff;
flex-shrink: 0;
}
.info-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.info-desc {
font-size: 28rpx;
color: #666;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,111 @@
<template>
<view class="web-viewer">
<view class="viewer-header">
<view class="header-left">
<u-icon name="arrow-left" size="20" @click="goBack"></u-icon>
<text class="header-title">{{ pageTitle }}</text>
</view>
<view class="header-right">
<u-button size="mini" @click="refreshPage">刷新</u-button>
</view>
</view>
<web-view
:src="previewUrl"
@message="handleMessage"
@error="handleError"
class="viewer-content"
></web-view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const previewUrl = ref('');
const pageTitle = ref('文件预览');
onLoad((options) => {
if (options.url) {
previewUrl.value = decodeURIComponent(options.url);
}
if (options.title) {
pageTitle.value = decodeURIComponent(options.title);
}
console.log('Web-View预览URL:', previewUrl.value);
});
const goBack = () => {
uni.navigateBack();
};
const refreshPage = () => {
//
const currentUrl = previewUrl.value;
previewUrl.value = '';
setTimeout(() => {
previewUrl.value = currentUrl;
}, 100);
};
const handleMessage = (e) => {
console.log('Web-view消息:', e.detail);
};
const handleError = (e) => {
console.error('Web-view错误:', e.detail);
uni.showToast({
title: '预览加载失败,请检查网络连接',
icon: 'none',
duration: 3000
});
};
</script>
<style scoped>
.web-viewer {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background-color: #f5f5f5;
}
.viewer-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
background-color: #ffffff;
border-bottom: 1rpx solid #e0e0e0;
flex-shrink: 0;
}
.header-left {
display: flex;
align-items: center;
gap: 20rpx;
}
.header-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
max-width: 400rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.header-right {
display: flex;
gap: 20rpx;
}
.viewer-content {
flex: 1;
width: 100%;
}
</style>

View File

@ -27,20 +27,20 @@
>范围: {{ data.njmc + data.bjmc }}</text >范围: {{ data.njmc + data.bjmc }}</text
> >
<view class="footer-actions"> <view class="footer-actions">
<u-icon <image
name="list" src="@/static/base/details.png"
size="22" mode="aspectFit"
color="#409EFF" class="footer-action-icon"
style="width:22px;height:22px;"
@click="goToFeedback(data.id)" @click="goToFeedback(data.id)"
class="footer-action-icon"
/> />
<u-icon <image
v-if="data.jlStatus === 'A'" v-if="data.jlStatus === 'A'"
name="arrow-right" src="@/static/base/push.png"
size="22" mode="aspectFit"
color="#FFA726"
@click="goToPush(data.id)"
class="footer-action-icon" class="footer-action-icon"
style="width:22px;height:22px;"
@click="goToPush(data.id)"
/> />
</view> </view>

View File

@ -111,6 +111,22 @@ onMounted(async () => {
const res = await typesFindTreeApi(); const res = await typesFindTreeApi();
typeTree.value = res.result || []; typeTree.value = res.result || [];
console.log('教学资源页面加载完成', res); console.log('教学资源页面加载完成', res);
//
if (typeTree.value.length > 0) {
//
const chineseSubject = typeTree.value.find(item =>
item.title && (item.title.includes('语文') || item.title.includes('语文'))
);
if (chineseSubject) {
//
selectType(chineseSubject);
} else {
//
selectType(typeTree.value[0]);
}
}
}); });
</script> </script>

File diff suppressed because it is too large Load Diff

View File

@ -115,7 +115,7 @@
<text class="card-title">二维码信息</text> <text class="card-title">二维码信息</text>
<view class="info-item"> <view class="info-item">
<text class="info-label">二维码有效期</text> <text class="info-label">二维码有效期</text>
<text class="info-value">60</text> <text class="info-value">{{ qrExpireTime }}</text>
</view> </view>
<view class="info-item"> <view class="info-item">
<text class="info-label">当前时间</text> <text class="info-label">当前时间</text>
@ -126,6 +126,38 @@
<button @click="goBack" class="back-btn">返回</button> <button @click="goBack" class="back-btn">返回</button>
</view> </view>
</view> </view>
<!-- 重复签到阶段 -->
<view v-else-if="currentStep === 'alreadySigned'" class="already-signed-step">
<view class="already-signed-content">
<u-icon name="checkmark-circle" size="80" color="#67C23A" />
<text class="already-signed-title">您已签到成功</text>
<text class="already-signed-subtitle">您已经完成本次签到请勿重复签到</text>
<text class="already-signed-tip">如需重新签到请联系会议组织者</text>
<view class="meeting-info-card">
<text class="card-title">会议信息</text>
<view class="info-item">
<text class="info-label">会议名称</text>
<text class="info-value">{{ meetingInfo?.qdmc || '未设置' }}</text>
</view>
<view class="info-item">
<text class="info-label">会议时间</text>
<text class="info-value">{{ formatTime(meetingInfo?.qdkstime) }} - {{ formatTime(meetingInfo?.qdjstime) }}</text>
</view>
<view class="info-item">
<text class="info-label">会议地点</text>
<text class="info-value">{{ meetingInfo?.qdwz || '未设置' }}</text>
</view>
<view class="info-item">
<text class="info-label">签到时间</text>
<text class="info-value">{{ formatTime(qdzxRecord?.qdwctime) }}</text>
</view>
</view>
<button @click="goBack" class="back-btn">返回</button>
</view>
</view>
</view> </view>
<!-- 签名组件 --> <!-- 签名组件 -->
@ -139,11 +171,20 @@ import { qdFindByIdApi, qdzxSignInApi, qdzxFindByQdParamsApi, qdzxFindByQdAndJsA
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
// //
const options = ref<any>({});
//
onLoad((params) => {
options.value = params;
});
//
const qdId = ref(''); const qdId = ref('');
const qdzxId = ref(''); const qdzxId = ref('');
const qrExpireTime = ref(60); // 60
// //
const currentStep = ref<'sign' | 'confirm' | 'success' | 'notInList' | 'timeExpired' | 'qrExpired'>('confirm'); const currentStep = ref<'sign' | 'confirm' | 'success' | 'notInList' | 'timeExpired' | 'qrExpired' | 'alreadySigned'>('confirm');
// //
const userInfo = ref<any>(null); const userInfo = ref<any>(null);
@ -163,21 +204,26 @@ const showSignature = ref<boolean>(false);
// store // store
const userStore = useUserStore(); const userStore = useUserStore();
onLoad(async (options) => { //
if (options?.qdId) { onMounted(async () => {
qdId.value = options.qdId; // ID
if (options.value?.qdId) {
qdId.value = options.value.qdId;
} else {
uni.showToast({ title: '参数错误', icon: 'none' });
return;
} }
if (options?.qdzxId) {
qdzxId.value = options.qdzxId; //
if (options.value?.rqgqtime) {
qrExpireTime.value = parseInt(options.value.rqgqtime) || 60;
} }
// //
const jsData = userStore.getJs; const jsData = userStore.getJs;
if (jsData && Object.keys(jsData).length > 0) { if (jsData && Object.keys(jsData).length > 0) {
userInfo.value = jsData; userInfo.value = jsData;
console.log('获取到教师信息:', jsData);
} else { } else {
console.error('未找到教师信息,请先登录');
uni.showToast({ title: '请先登录', icon: 'none' }); uni.showToast({ title: '请先登录', icon: 'none' });
// //
setTimeout(() => { setTimeout(() => {
@ -189,21 +235,13 @@ onLoad(async (options) => {
} }
// //
if (options?.timestamp) { if (options.value?.timestamp) {
const qrTimestamp = parseInt(options.timestamp); const qrTimestamp = parseInt(options.value.timestamp);
const currentTimestamp = Date.now(); const currentTimestamp = Date.now();
const timeDiff = currentTimestamp - qrTimestamp; const timeDiff = currentTimestamp - qrTimestamp;
const maxValidTime = 60 * 1000; // 60 const maxValidTime = qrExpireTime.value * 1000; //
console.log('二维码时间戳验证:', {
qrTimestamp,
currentTimestamp,
timeDiff,
maxValidTime
});
if (timeDiff > maxValidTime) { if (timeDiff > maxValidTime) {
console.error('二维码已过期');
currentStep.value = 'qrExpired'; currentStep.value = 'qrExpired';
return; return;
} }
@ -220,23 +258,24 @@ const checkTeacherSignInPermission = async () => {
qdId: qdId.value, qdId: qdId.value,
jsId: userInfo.value.id jsId: userInfo.value.id
}); });
if (result && result.resultCode === 1 && result.result) { if (result && result.resultCode === 1 && result.result) {
// //
qdzxRecord.value = result.result; qdzxRecord.value = result.result;
qdzxId.value = result.result.id; qdzxId.value = result.result.id;
console.log('找到教师签到记录:', result.result);
// //
await loadMeetingInfo(); await loadMeetingInfo();
} else { } else if (result && result.resultCode === 0) {
// "" //
currentStep.value = 'notInList'; currentStep.value = 'notInList';
await loadMeetingInfo(); // await loadMeetingInfo(); //
} else {
//
uni.showToast({ title: '查询签到权限失败', icon: 'none' });
} }
} catch (error) { } catch (error) {
console.error('查询教师签到权限失败:', error); uni.showToast({ title: '网络异常,请重试', icon: 'none' });
uni.showToast({ title: '查询签到权限失败', icon: 'none' });
} }
}; };
@ -259,6 +298,13 @@ const loadMeetingInfo = async () => {
// qdzxId // qdzxId
if (qdzxId.value) { if (qdzxId.value) {
//
if (qdzxRecord.value?.qdStatus === '1') {
//
currentStep.value = 'alreadySigned';
return;
}
// //
if (meetingInfo.value?.mdqz === '1') { if (meetingInfo.value?.mdqz === '1') {
// //
@ -270,7 +316,6 @@ const loadMeetingInfo = async () => {
} }
} }
} catch (error) { } catch (error) {
console.error('加载会议信息失败:', error);
uni.showToast({ title: '加载会议信息失败', icon: 'none' }); uni.showToast({ title: '加载会议信息失败', icon: 'none' });
} }
}; };
@ -292,7 +337,6 @@ const handleSignature = async () => {
currentStep.value = 'confirm'; currentStep.value = 'confirm';
} catch (error) { } catch (error) {
showSignature.value = false; showSignature.value = false;
console.error('签名失败:', error);
uni.showToast({ title: '签名失败', icon: 'none' }); uni.showToast({ title: '签名失败', icon: 'none' });
} }
}; };
@ -310,24 +354,15 @@ const submitSignIn = async () => {
params.signatureImage = sign_file.value; params.signatureImage = sign_file.value;
} }
console.log('提交签到参数:', JSON.stringify(params, null, 2));
console.log('参数类型检查:', {
qdzxId: typeof params.qdzxId,
signInTime: typeof params.signInTime,
signatureImage: typeof params.signatureImage
});
const result = await qdzxSignInApi(params); const result = await qdzxSignInApi(params);
console.log('签到结果:', result);
if (result && result.resultCode === 1) { if (result && result.resultCode === 1) {
currentStep.value = 'success'; currentStep.value = 'success';
} else { } else {
const errorMsg = result?.msg || result?.message || '签到失败'; const errorMsg = result?.message || '签到失败';
uni.showToast({ title: errorMsg, icon: 'none' }); uni.showToast({ title: errorMsg, icon: 'none' });
} }
} catch (error) { } catch (error) {
console.error('签到失败:', error);
uni.showToast({ title: '网络异常,请重试', icon: 'none' }); uni.showToast({ title: '网络异常,请重试', icon: 'none' });
} }
}; };
@ -627,4 +662,35 @@ const goBack = () => {
} }
} }
} }
/* 重复签到阶段 */
.already-signed-step {
background: white;
border-radius: 16px;
padding: 40px 24px;
margin-top: 40px;
text-align: center;
}
.already-signed-content {
.already-signed-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin: 16px 0 8px;
}
.already-signed-subtitle {
font-size: 14px;
color: #666;
margin-bottom: 16px;
}
.already-signed-tip {
font-size: 14px;
color: #999;
margin-bottom: 24px;
}
}
</style> </style>

View File

@ -57,7 +57,7 @@
<text class="tips-title">使用说明</text> <text class="tips-title">使用说明</text>
</view> </view>
<view class="tips-content"> <view class="tips-content">
<text class="tip-item">1. 二维码有效期为60请及时扫描</text> <text class="tip-item">1. 二维码有效期为{{ qrExpireTime }}请及时扫描</text>
<text class="tip-item">2. 使用微信扫一扫功能扫描二维码</text> <text class="tip-item">2. 使用微信扫一扫功能扫描二维码</text>
<text class="tip-item">3. 扫描后将跳转到签到确认页面</text> <text class="tip-item">3. 扫描后将跳转到签到确认页面</text>
<text class="tip-item">4. 请确保网络连接正常</text> <text class="tip-item">4. 请确保网络连接正常</text>
@ -74,6 +74,7 @@ import { qdFindByIdApi, generateQRCodeApi } from '@/api/base/server';
// //
const qdId = ref(''); const qdId = ref('');
const qrExpireTime = ref(60); // 60
// //
const meetingInfo = ref<any>(null); const meetingInfo = ref<any>(null);
@ -88,6 +89,10 @@ onLoad((options) => {
if (options?.qdId) { if (options?.qdId) {
qdId.value = options.qdId; qdId.value = options.qdId;
} }
//
startTimer();
loadMeetingInfo(); loadMeetingInfo();
}); });
@ -118,14 +123,24 @@ const initQRCode = async () => {
if (result && result.resultCode === 1) { if (result && result.resultCode === 1) {
// //
const qrData = result.result || ''; const qrData = result.result || '';
console.log('二维码数据:', qrData);
// rqgqtime
const urlParams = new URLSearchParams(qrData.split('?')[1] || '');
const rqgqtime = urlParams.get('rqgqtime');
if (rqgqtime) {
const expireTime = parseInt(rqgqtime) || 60;
qrExpireTime.value = expireTime;
qrTimer.value = expireTime;
//
startTimer();
}
generateQRCodeImage(qrData); generateQRCodeImage(qrData);
} else { } else {
throw new Error('生成二维码失败'); throw new Error('生成二维码失败');
} }
// // onLoad
startTimer();
} catch (error) { } catch (error) {
console.error('生成二维码失败:', error); console.error('生成二维码失败:', error);
uni.showToast({ title: '生成二维码失败', icon: 'none' }); uni.showToast({ title: '生成二维码失败', icon: 'none' });
@ -171,7 +186,6 @@ const generateQRCodeImage = (qrData: string) => {
qrCanvas.drawImage(url, 0, 0, qrSize.value, qrSize.value); qrCanvas.drawImage(url, 0, 0, qrSize.value, qrSize.value);
qrCanvas.draw(); qrCanvas.draw();
console.log('二维码生成成功');
}); });
}).catch((error) => { }).catch((error) => {
console.error('加载qrcode库失败:', error); console.error('加载qrcode库失败:', error);
@ -186,8 +200,6 @@ const generateQRCodeImage = (qrData: string) => {
qrCanvas.draw(); qrCanvas.draw();
}); });
console.log('二维码数据:', qrData);
} catch (error) { } catch (error) {
console.error('生成二维码异常:', error); console.error('生成二维码异常:', error);
// //
@ -204,7 +216,7 @@ const generateQRCodeImage = (qrData: string) => {
// //
const startTimer = () => { const startTimer = () => {
qrTimer.value = 60; qrTimer.value = qrExpireTime.value;
if (qrTimerInterval) { if (qrTimerInterval) {
clearInterval(qrTimerInterval); clearInterval(qrTimerInterval);
} }
@ -224,6 +236,8 @@ const startTimer = () => {
const refreshQRCode = async () => { const refreshQRCode = async () => {
try { try {
await initQRCode(); await initQRCode();
//
startTimer();
uni.showToast({ title: '二维码已刷新', icon: 'success' }); uni.showToast({ title: '二维码已刷新', icon: 'success' });
} catch (error) { } catch (error) {
uni.showToast({ title: '刷新失败', icon: 'none' }); uni.showToast({ title: '刷新失败', icon: 'none' });

419
src/utils/filePreview.ts Normal file
View File

@ -0,0 +1,419 @@
/**
*
*
*/
import { KK_FILE_VIEW_URL } from '@/config';
// 文件类型判断
export const isVideo = (fileType: string): boolean => {
const videoTypes = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv', '3gp', 'm4v'];
return videoTypes.includes(fileType.toLowerCase());
};
export const isImage = (fileType: string): boolean => {
const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
return imageTypes.includes(fileType.toLowerCase());
};
// 检查是否是压缩包文件
export const isCompressedFile = (fileName: string): boolean => {
const compressedExtensions = ['zip', 'rar', '7z', 'tar', 'gz', 'bz2'];
const extension = fileName.split('.').pop()?.toLowerCase();
return compressedExtensions.includes(extension || '');
};
// 检查文件是否可以预览
export const canPreview = (fileName: string): boolean => {
const previewableExtensions = [
'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
'txt', 'rtf', 'odt', 'ods', 'odp',
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp',
'mp4', 'avi', 'mov', 'wmv', 'flv', 'webm',
'mp3', 'wav', 'ogg', 'aac', 'm4a'
];
const extension = fileName.split('.').pop()?.toLowerCase();
return previewableExtensions.includes(extension || '');
};
// 获取文件图标
export const getFileIcon = (fileName: string): string => {
const extension = fileName.split('.').pop()?.toLowerCase();
// 压缩包文件显示下载图标
if (isCompressedFile(fileName)) {
return '📥';
}
switch (extension) {
case 'pdf':
return '📄';
case 'doc':
case 'docx':
return '📝';
case 'xls':
case 'xlsx':
return '📊';
case 'ppt':
case 'pptx':
return '📽️';
case 'txt':
return '📃';
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
case 'bmp':
case 'webp':
return '🖼️';
case 'mp4':
case 'avi':
case 'mov':
case 'wmv':
case 'flv':
case 'webm':
return '🎥';
case 'mp3':
case 'wav':
case 'ogg':
case 'aac':
case 'm4a':
return '🎵';
default:
return '📄';
}
};
// 获取文件类型名称
export const getFileTypeName = (fileName: string): string => {
const extension = fileName.split('.').pop()?.toLowerCase();
// 压缩包文件
if (isCompressedFile(fileName)) {
switch (extension) {
case 'zip':
return 'ZIP压缩包';
case 'rar':
return 'RAR压缩包';
case '7z':
return '7Z压缩包';
case 'tar':
return 'TAR压缩包';
case 'gz':
return 'GZ压缩包';
case 'bz2':
return 'BZ2压缩包';
default:
return '压缩包';
}
}
switch (extension) {
case 'pdf':
return 'PDF文档';
case 'doc':
case 'docx':
return 'Word文档';
case 'xls':
case 'xlsx':
return 'Excel表格';
case 'ppt':
case 'pptx':
return 'PowerPoint演示';
case 'txt':
return '文本文件';
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
case 'bmp':
case 'webp':
return '图片文件';
case 'mp4':
case 'avi':
case 'mov':
case 'wmv':
case 'flv':
case 'webm':
return '视频文件';
case 'mp3':
case 'wav':
case 'ogg':
case 'aac':
case 'm4a':
return '音频文件';
default:
return '未知文件';
}
};
// 跨平台文件预览
export const previewFile = (fileUrl: string, fileName: string, fileType: string) => {
return new Promise((resolve, reject) => {
const systemInfo = uni.getSystemInfoSync();
const { platform } = systemInfo;
const type = fileType.toLowerCase();
console.log('=== 文件预览调试信息 ===');
console.log('平台:', platform);
console.log('系统信息:', systemInfo);
console.log('原始文件URL:', fileUrl);
console.log('文件名:', fileName);
console.log('文件类型:', fileType);
// 检查是否是压缩包文件,如果是则直接下载
if (isCompressedFile(fileName)) {
console.log('=== 检测到压缩包文件,直接下载 ===');
uni.showToast({ title: '压缩包文件不支持预览,将为您下载', icon: 'none' });
downloadFile(fileUrl, fileName).then(resolve).catch(resolve);
return;
}
// 检查是否支持预览的文件格式
const canPreviewType = canPreview(fileName);
if (!canPreviewType) {
// 不支持预览的文件格式,直接下载
console.log('不支持预览的文件格式,执行下载');
uni.showToast({ title: '该文件格式暂不支持预览,将为您下载', icon: 'none' });
downloadFile(fileUrl, fileName).then(resolve).catch(resolve);
return;
}
// 检查URL是否已经是kkFileView生成的压缩包内文件URL
const isCompressedFileUrl = fileUrl.includes('kkCompressfileKey') && fileUrl.includes('kkCompressfilepath');
console.log('=== 压缩包内文件检测 ===');
console.log('URL包含kkCompressfileKey:', fileUrl.includes('kkCompressfileKey'));
console.log('URL包含kkCompressfilepath:', fileUrl.includes('kkCompressfilepath'));
console.log('是否为压缩包内文件URL:', isCompressedFileUrl);
if (isCompressedFileUrl) {
console.log('=== 压缩包内文件处理 ===');
console.log('检测到压缩包内文件URL直接使用');
console.log('原始压缩包内文件URL:', fileUrl);
const previewUrl = fileUrl; // 直接使用这个URL不重新编码
console.log('最终预览URL (压缩包内文件):', previewUrl);
const needLandscape = ['ppt', 'pptx'].includes(type);
console.log('是否需要横屏显示:', needLandscape);
const targetPageUrl = `/pages/system/web-viewer/index?url=${encodeURIComponent(previewUrl)}&title=${encodeURIComponent(fileName)}&landscape=${needLandscape ? '1' : '0'}`;
console.log('跳转目标页面URL:', targetPageUrl);
uni.navigateTo({
url: targetPageUrl,
success: () => {
console.log('跳转到压缩包内文件预览页面成功');
resolve(true);
},
fail: (err) => {
console.error('跳转到压缩包内文件预览页面失败:', err);
console.log('错误详情:', JSON.stringify(err));
uni.showToast({ title: '预览加载失败,尝试下载', icon: 'none' });
setTimeout(() => {
downloadFile(fileUrl, fileName).then(resolve).catch(resolve);
}, 1000);
}
});
return; // 退出函数
}
console.log('=== 普通文件处理 ===');
// 处理普通文件预览
const finalFileUrl = fileUrl;
console.log('DEBUG: 最终用于编码的URL (finalFileUrl):', finalFileUrl);
// 使用kkFileView进行预览
const encodedUrl = btoa(finalFileUrl);
console.log('使用kkFileView预览Base64编码后的URL:', encodedUrl);
const previewUrl = `http://yufangzc.com:8891/onlinePreview?url=${encodeURIComponent(encodedUrl)}`;
console.log('最终预览URL (普通文件):', previewUrl);
const needLandscape = ['ppt', 'pptx'].includes(type);
console.log('是否需要横屏显示:', needLandscape);
const targetPageUrl = `/pages/system/web-viewer/index?url=${encodeURIComponent(previewUrl)}&title=${encodeURIComponent(fileName)}&landscape=${needLandscape ? '1' : '0'}`;
console.log('跳转目标页面URL:', targetPageUrl);
uni.navigateTo({
url: targetPageUrl,
success: () => {
console.log('跳转到预览页面成功');
resolve(true);
},
fail: (err) => {
console.error('跳转到预览页面失败:', err);
console.log('错误详情:', JSON.stringify(err));
uni.showToast({ title: '预览加载失败,尝试下载', icon: 'none' });
setTimeout(() => {
downloadFile(fileUrl, fileName).then(resolve).catch(resolve);
}, 1000);
}
});
});
};
// 下载文件
export const downloadFile = (fileUrl: string, fileName: string) => {
const { platform } = uni.getSystemInfoSync();
if (platform === 'web') {
return downloadForWeb(fileUrl, fileName);
} else {
return downloadForNative(fileUrl, fileName);
}
};
// H5平台下载
const downloadForWeb = (url: string, filename: string) => {
return new Promise((resolve, reject) => {
fetch(url)
.then((response) => response.blob())
.then((blob) => {
const downloadUrl = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = downloadUrl;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(downloadUrl);
uni.showToast({ title: "开始下载", icon: "success" });
resolve(true);
})
.catch((err) => {
console.error("H5下载失败:", err);
uni.showToast({ title: "下载失败", icon: "none" });
reject(err);
});
});
};
// 原生平台下载
const downloadForNative = (url: string, filename: string) => {
return new Promise((resolve, reject) => {
uni.showLoading({ title: "下载中..." });
uni.downloadFile({
url: url,
success: async (res) => {
if (res.statusCode === 200) {
try {
const saved = await saveFile(res.tempFilePath, filename);
if (saved) {
// 尝试打开文件
uni.openDocument({
filePath: saved,
success: () => {
uni.showToast({ title: "文件已打开", icon: "success" });
resolve(true);
},
fail: (err) => {
console.warn('文件打开失败:', err);
uni.showToast({ title: "文件已下载,但无法打开", icon: "none" });
resolve(true); // 下载成功,即使打开失败也算成功
}
});
} else {
reject(new Error('保存失败'));
}
} catch (error) {
console.error('处理下载文件时出错:', error);
reject(error);
}
} else {
uni.showToast({ title: "下载失败", icon: "none" });
reject(new Error('下载失败'));
}
},
fail: (err) => {
console.error("下载失败:", err);
uni.showToast({ title: "下载失败", icon: "none" });
reject(err);
},
complete: () => uni.hideLoading()
});
});
};
// 保存文件
const saveFile = (tempPath: string, filename: string): Promise<string | null> => {
return new Promise((resolve) => {
try {
// 检查是否支持文件系统管理器
if (typeof uni.getFileSystemManager === 'function') {
const fs = uni.getFileSystemManager();
const appPath = `${filename}`;
fs.rename({
oldPath: tempPath,
newPath: appPath,
success: () => resolve(appPath),
fail: () => {
console.warn('文件重命名失败,使用临时路径');
resolve(tempPath);
}
});
} else {
// 不支持文件系统管理器时,直接使用临时路径
console.warn('当前平台不支持文件系统管理器,使用临时路径');
resolve(tempPath);
}
} catch (error) {
console.error('保存文件时出错:', error);
uni.showToast({ title: "保存失败", icon: "none" });
resolve(null);
}
});
};
// 视频预览
export const previewVideo = (videoUrl: string, videoTitle: string) => {
console.log('=== 视频预览调试信息 ===');
console.log('视频URL:', videoUrl);
console.log('视频标题:', videoTitle);
return new Promise((resolve) => {
const targetUrl = `/pages/system/video-player/index?url=${encodeURIComponent(videoUrl)}&title=${encodeURIComponent(videoTitle)}`;
console.log('跳转目标URL:', targetUrl);
uni.navigateTo({
url: targetUrl,
success: () => {
console.log('跳转到视频播放页面成功');
resolve(true);
},
fail: (err) => {
console.error('跳转到视频播放页面失败:', err);
console.log('错误详情:', JSON.stringify(err));
// 跳转失败时尝试使用系统播放器
uni.showToast({
title: '跳转失败,尝试使用系统播放器',
icon: 'none',
duration: 2000
});
// 延迟后尝试下载视频
setTimeout(() => {
downloadFile(videoUrl, videoTitle + '.mp4').then(resolve).catch(resolve);
}, 2000);
}
});
});
};
// 图片预览
export const previewImage = (imageUrl: string) => {
return new Promise((resolve) => {
uni.previewImage({
urls: [imageUrl],
current: imageUrl,
success: resolve,
fail: resolve
});
});
};

View File

@ -89,7 +89,7 @@ export function get<T = any>(
options options
) )
).then((res) => { ).then((res) => {
if (res.resultCode == 1) { if (res.resultCode == 1 || res.resultCode == 0) {
resolve(res); resolve(res);
} else { } else {
if (res.resultCode) { if (res.resultCode) {