11.13早上提交

This commit is contained in:
zoujiandong 2025-11-13 08:41:08 +08:00
parent 4816054b96
commit 1e134f236c
67 changed files with 12734 additions and 7886 deletions

727
App.vue
View File

@ -1,613 +1,152 @@
<script >
import RootStore from "@xkit-yx/im-store-v2";
/** esm 版本 */
//@ts-ignore
// import { V2NIMConst, NIM } from './esmNim.js'
/** 常规版本*/
import NIM from "nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK";
import { V2NIMConst } from "nim-web-sdk-ng/dist/esm/nim";
import {
customRedirectTo,
customReLaunch,
customSwitchTab,
} from "@/utils/im/customNavigate";
import { getMsgContentTipByType } from "@/utils/im/msg";
import { STORAGE_KEY } from "@/utils/im/constants";
import { isWxApp } from "@/utils/im/index";
/** 国际化*/
import { setLanguage } from "@/utils/im/i18n";
import BASE_URL from "@/utils/config.js"
import my_api from '@/api/my_api.js'
import GlobalDialog from "@/components/GlobalDialog.vue"
// #ifdef APP-PLUS
/** 推送插件 */
const nimPushPlugin = uni.requireNativePlugin("NIMUniPlugin-PluginModule");
console.log("nimPushPlugin1111");
console.log(nimPushPlugin);
/** 音视频通话插件 */
// const nimCallKit = (uni.$UIKitCallKit =
// uni.requireNativePlugin("netease-CallKit"));
// #endif
const app = getApp()
let startByNotificationId = "";
export default {
globalData: {
plAd: true
},
onLaunch() {
// #ifdef APP-PLUS
/** 关闭启动画面,锁定竖屏 */
try {
plus.navigator.closeSplashscreen();
plus.screen.lockOrientation("portrait-primary");
} catch (error) {
}
//
nimPushPlugin.addOpenNotificationListener((res) => {
console.log("推送监听");
console.log("res",res);
if (typeof res === 'object') {
console.log("=====addOpenNotificationListener success:", JSON.stringify(res))
uni.redirectTo({
url: `/pages_app/patientMsg/patientMsg?from=push`,
success: () => {
app.globalData.plAd=false;
},
fail: (err) => {
console.log("跳转失败", err);
}
});
} else {
console.log("=====addOpenNotificationListener unexpected:", res)
}
});
// #endif
// uni-apppushApp.vue
// uni.getPushClientId({
// success: (res) => {
// let push_clientid = res.cid
// uni.setStorageSync("push_clientid", push_clientid)
// console.log('cid:',push_clientid)
// const systemInfo = uni.getSystemInfoSync();
// console.log("systemInfo:", systemInfo)
// let userInfo= uni.getStorageSync('userInfo')
// console.log("userinfo:", userInfo)
// if(!userInfo){
// return
// }
// if(userInfo.uuid){
// let client_type = ""
// if (systemInfo.platform === 'android') {
// client_type = "A"
// } else if (systemInfo.platform === 'ios') {
// client_type = "I"
// }
// console.log("client_type:", client_type)
// if(client_type){
// my_api.addUniappCid({
// user_uuid: userInfo.uuid,
// client_type:client_type,
// cid: push_clientid
// })
// }
// }
// },
// fail(err) {
// console.error("unipush: ", err)
// }
// })
// uni.onPushMessage((res)=>{
// console.log("", res)
// let app_isback = uni.getStorageSync("app_isback")
// console.log("app_isback", app_isback)
// uni.showModal({
// title: res.data.title,
// content: res.type,
// showCancel: false
// });
// if(res.type === 'click' ){
// }else{
// if(app_isback){
// uni.createPushMessage({
// title: res.data.title,
// content: res.data.content,
// payload: res.data.payload,
// delay: 1,
// success: (res) => {
// console.log("", res)
// }
// })
// }else{
// console.log('[App] emit global-dialog:show', res)
// uni.$emit('global-dialog:show', {
// title: res.data.title || '',
// content: res.data.content || '',
// confirmText: '',
// position: 'top',
// offsetTop: '160rpx'
// })
// }
// }
// })
/** 设置语言 此处为了方便demo切换语言将其存到本地实际需根据业务情况设置*/
setLanguage(
uni.getStorageSync("switchToEnglishFlag") == "en" ? "en" : "zh"
);
/** 已经登录了 不用走初始化逻辑*/
if (
uni?.$UIKitStore?.connectStore?.connectStatus ===
V2NIMConst.V2NIMConnectStatus.V2NIM_CONNECT_STATUS_CONNECTED
) {
return;
}
let storage_token='';
let storage_accid='';
if (process.env.UNI_PLATFORM == "h5") {
if (window.location.href.indexOf('dev') > -1) {
storage_accid=uni.getStorageSync('AUTH_YX_ACCID_App');
storage_token=uni.setStorageSync('AUTH_YX_TOKEN_App');
} else {
storage_accid=uni.getStorageSync('DEV_AUTH_YX_ACCID_App');
storage_token=uni.getStorageSync('DEV_AUTH_YX_TOKEN_App');
}
} else if(process.env.UNI_PLATFORM == "mp-weixin") {
const {
envVersion
} = uni.getAccountInfoSync().miniProgram;
if (envVersion == "release") {
storage_accid=uni.getStorageSync('AUTH_YX_ACCID_App');
storage_token=uni.setStorageSync('AUTH_YX_TOKEN_App');
} else {
storage_accid=uni.getStorageSync('DEV_AUTH_YX_ACCID_App');
storage_token=uni.getStorageSync('DEV_AUTH_YX_TOKEN_App');
}
}else{
storage_accid=uni.getStorageSync('DEV_AUTH_YX_ACCID_App');
storage_token=uni.getStorageSync('DEV_AUTH_YX_TOKEN_App');
globalData: {
plAd: true,
dotNumber:{
}
const account =storage_accid;
const token = storage_token;
if(!account || !token){
uni.redirectTo({
url: '/pages_app/login/login'
});
return;
}
console.log("account",account);
console.log("token",token);
console.log("account",typeof account);
console.log("token",typeof token);
const imOptions = {
appkey: "885dea390870814acf3ba8558c717572", // appkey
account: String(account),//"9ufkll2xo57km6224xe", // account
token:String( token)//"4918605da57e573cff93209df56f351d", // 请填写你的token
},
onLaunch(options) {
console.log('打印options');
console.log(options);
if(options && options.referrerInfo.extraData && options.referrerInfo.extraData.token){
uni.setStorageSync("DEV_AUTH_TOKEN_App", options.referrerInfo.extraData.token);
uni.setStorageSync("userInfo", options.referrerInfo.extraData.userInfo);
};
console.log("onLaunch");
if (imOptions) {
console.log("初始化im");
this.initNim(imOptions);
} else {
/** 未登录 跳转登录页 */
// customRedirectTo({
// url: isWxApp ? "/pages/index/index" : "/pages/Login/index",
// });
if(options && options.referrerInfo.extraData && options.referrerInfo.extraData.targetPath){
uni.navigateTo({
url: options.referrerInfo.extraData.targetPath
});
};
console.log("app传递登录信息onLaunch-------------------");
try {
// #ifdef APP
// uni.onNativeEventReceive((event, data) => {
// console.log("app-------------------");
// console.log("event");
// console.log(event);
// console.log("data");
// console.log(data);
// if (event.indexOf("loginInfo") > -1) {
// uni.setStorageSync("DEV_AUTH_TOKEN_App", data.token);
// uni.setStorageSync("userInfo", data.userInfo);
// console.log("token");
// console.log(uni.getStorageSync("DEV_AUTH_TOKEN_App"));
// }
// });
// #endif
} catch (error) {
console.log("app传递登录信息error-------------------");
console.log(error);
}
},
onShow() {
// #ifdef APP-PLUS
console.log("onShow");
uni?.$UIKitNIM?.V2NIMSettingService?.setAppBackground(false);
uni.setStorageSync("app_isback", false)
//
// nimPushPlugin.addOpenNotificationListener((res) => {
// console.log("");
// console.log("res",res);
// if (typeof res == "object" && res?.sessionId && res?.sessionType) {
// // id
// const imOptions = uni.getStorageSync(STORAGE_KEY);
// //
// const type = res?.sessionType;
// // ID
// startByNotificationId = `${imOptions.account}|${type}|${res?.sessionId}`;
// }
// });
// #endif
},
onHide() {
console.log("onHide");
uni.setStorageSync("app_isback", true)
// #ifdef APP-PLUS
uni?.$UIKitNIM?.V2NIMSettingService?.setAppBackground(true);
// #endif
// startByNotificationId
startByNotificationId = "";
},
components:{
GlobalDialog
},
methods: {
initNim(opts) {
console.log("initNim1111111111");
/** 保存登录信息 demo 层逻辑 具体根据您的业务调整*/
uni.setStorage({
key: STORAGE_KEY,
data: opts,
});
/** 是否开启云端会话此处为了方便demo切换云端/本地会话,将其存到本地,实际需根据业务情况设置)*/
const enableV2CloudConversation =
uni.getStorageSync("enableV2CloudConversation") === "on";
/** 初始化 nim sdk */
//@ts-ignore
const nim = (uni.$UIKitNIM = NIM.getInstance(
{
appkey: opts.appkey,
needReconnect: true,
debugLevel: "debug",
apiVersion: "v2",
enableV2CloudConversation: enableV2CloudConversation,
},
{
V2NIMLoginServiceConfig: {
/**
* 微信小程序需要使用单独的lbsUrls和linkUrl
*/
lbsUrls: isWxApp
? ["https://lbs.netease.im/lbs/wxwebconf.jsp"]
: ["https://lbs.netease.im/lbs/webconf.jsp"],
linkUrl: isWxApp ? "wlnimsc0.netease.im" : "weblink.netease.im",
/**
* 使用固定设备ID
*/
isFixedDeviceId: true,
},
}
));
/** 初始化 im store */
// @ts-ignore
const store = (uni.$UIKitStore = new RootStore(
// @ts-ignore
nim,
{
//
addFriendNeedVerify: true,
// p2p p2p false
p2pMsgReceiptVisible: true,
// false
teamMsgReceiptVisible: true,
// 线线
loginStateVisible: true,
//
teamAgreeMode:
V2NIMConst.V2NIMTeamAgreeMode.V2NIM_TEAM_AGREE_MODE_NO_AUTH,
// ,
// @ts-ignore
sendMsgBefore: async (options) => {
const pushContent = getMsgContentTipByType({
text: options.msg.text,
messageType: options.msg.messageType,
});
const yxAitMsg = options.serverExtension
? options.serverExtension.yxAitMsg
: { forcePushIDsList: "[]", needForcePush: false };
// at 线
// @ts-ignore
const { forcePushIDsList, needForcePush } = yxAitMsg
? // @ts-ignore
store.msgStore._formatExtAitToPushInfo(
yxAitMsg,
options.msg.text
)
: { forcePushIDsList: "[]", needForcePush: false };
const { conversationId } = options;
const conversationType =
nim.V2NIMConversationIdUtil.parseConversationType(conversationId);
const targetId =
nim.V2NIMConversationIdUtil.parseConversationTargetId(
conversationId
);
const PACKAGE_NAME = 'cn.shangyu.gdxzExpert';
let sessionId=conversationType == 1
? uni.$UIKitStore.userStore.myUserInfo.accountId
: targetId;
// 线
// https://doc.yunxin.163.com/messaging2/guide/zc4MTg5MDY?platform=client#%E7%AC%AC%E4%B8%80%E6%AD%A5%E4%B8%8A%E4%BC%A0%E6%8E%A8%E9%80%81%E8%AF%81%E4%B9%A6
const pushPayload = JSON.stringify({
//pushTitle: "", //
notify_effect: "2", //1appLauncher Activity2appActivityintent_uri3web_uri
intent_uri: `intent:#Intent;action=com.netease.nimlib.uniapp.push.NotificationClickActivity;component=${PACKAGE_NAME}/com.netease.nimlib.uniapp.push.NotificationClickActivity;launchFlags=0x04000000;i.sessionType=${conversationType};S.sessionId=${sessionId};end`,
hwField: {
click_action: {
//
type: 1, //1 2URL 3
// intentintenttype1intentactionschemeactivity
intent:
`intent:#Intent;action=com.netease.nimlib.uniapp.push.NotificationClickActivity;component=${PACKAGE_NAME}/com.netease.nimlib.uniapp.push.NotificationClickActivity;launchFlags=0x04000000;i.sessionType=${conversationType};S.sessionId=${sessionId};end`
},
androidConfig: {
category: "IM", // AndroidConfig.category
},
},
honorField: {
notification: {
// AndroidNotification
clickAction: {
//
type: 1, //1 2URL 3
//intentintenttype1intentaction
intent: `intent://com.honor.push/deeplink?#Intent;scheme=pushscheme;launchFlags=0x04000000;i.sessionType=${conversationType};S.sessionId=${sessionId};end`,
},
importance: "NORMAL", //AndroidLOW NORMAL
},
},
vivoField: {
skipType: "4", // 1APP 2 3 4:app1
skipContent: `intent:#Intent;action=com.netease.nimlib.uniapp.push.NotificationClickActivity;component=${PACKAGE_NAME}/com.netease.nimlib.uniapp.push.NotificationClickActivity;launchFlags=0x04000000;i.sessionType=${conversationType};S.sessionId=${sessionId};end`,
classification: "1", // 010
category: "IM", //
},
oppoField: {
channel_id: "", //ID
category: "IM", //
notify_level: 2, //1-2-+16-++++
click_action_type: "1", //00.1.action2.4.5.Intent scheme URL: "",
click_action_activity: "com.netease.nimlib.uniapp.push.NotificationClickActivity",
action_parameters: JSON.stringify({
sessionType: conversationType,
sessionId: sessionId,
}),
},
fcmFieldV1: {
message: {
android: {
priority: "",
data: {
sessionType: conversationType,
sessionId: sessionId,
},
notification: {
click_action: "",
},
},
},
},
// IOS apns
sessionId:sessionId,
sessionType: conversationType,
});
// @ts-ignore
const pushConfig = {
pushEnabled: true,
pushNickEnabled: true,
forcePush: needForcePush,
forcePushContent: pushContent,
forcePushAccountIds: forcePushIDsList,
pushPayload,
pushContent,
};
return { ...options, pushConfig };
},
},
"UniApp"
));
// #ifdef APP-PLUS
/** 注册推送 实际根据您在推送厂商申请的证书进行配置,具体参考文档 https://doc.yunxin.163.com/messaging2/guide/zc4MTg5MDY?platform=client#%E7%AC%AC%E4%B8%80%E6%AD%A5%E4%B8%8A%E4%BC%A0%E6%8E%A8%E9%80%81%E8%AF%81%E4%B9%A6
*/
nim.V2NIMSettingService.setOfflinePushConfig(nimPushPlugin, {
// miPush: {
// appId: "",
// appKey: "",
// certificateName: "",
// },
// hwPush: {
// appId: "",
// certificateName: "",
// },
miPush: {
appId: "2882303761517470793",
appKey: "5431747048793",
certificateName: "Expert_Mi_Push",
},
hwPush: {
appId: "10526652",
certificateName: "Expert_Hw_push_f",
},
oppoPush: {
appId: "3364888",
appKey: "75D5Vqsg63wok0S0w4SKog0w8",
certificateName: "Expert_Op_Push",
secret: "416a4f5eF822ddcB73B627Cc310636D7",
},
vivoPush: {
appId: "100034326",
appKey: "e789634c1f76c8f184b1def1101ce824",
certificateName: "Expert_Vi_Push",
secret: "416a4f5eF822ddcB73B627Cc310636D7",
},
fcmPush: {
certificateName: "FCM_PUSH_CERT",
},
mzPush: {
appId: "111",
appKey: "111",
certificateName: "MEIZU_PUSH_CERT",
},
apns: {
certificateName: "专家端Pro",
},
});
try {
// #ifdef APP
let main = plus.android.runtimeMainActivity();
//退quit
plus.runtime.quit = function(){
main.moveTaskToBack(false);
};
//toast 退 toast
plus.nativeUI.toast = (function(str){
if(str == '再按一次退出应用'){
main.moveTaskToBack(false);
}else{
uni.showToast({
title:str,
icon:'none',
})
}
});
// uni.onNativeEventReceive((event, data) => {
// console.log("apponshow-------------------");
// console.log("event", event);
// console.log("data", data);
// if (event == "loginInfo") {
// uni.setStorageSync("DEV_AUTH_YX_TOKEN_App", data.token);
// uni.setStorageSync("userInfo", data.userInfo);
// }else if(event == "showRedDot"){
// console.log("showRedDot");
// console.log(data);
// getApp().globalData.dotNumber=data;
// }
// });
// #endif
/** nim sdk 登录 */
nim.V2NIMLoginService.login(opts.account, opts.token).then(async () => {
console.log("登录成功");
nim.V2NIMLoginService.on('onKickedOffline', (res) => {
console.log("被踢下线", res);
});
// #ifdef APP-PLUS
/** 初始化音视频通话插件*/
// nimCallKit.initConfig(
// {
// appKey: opts.appkey, // appkey
// account: opts.account, // account
// token: opts.token, // token
// apnsCername: "",
// pkCername: "",
// },
// (ret) => {
// if (ret.code != 200) {
// // callkit init
// } else {
// nimCallKit.login(
// {
// account: opts.account,
// token: opts.token,
// },
// function (ret) {
// if (ret.code != 200) {
// //
// } else {
// //
// }
// }
// );
// }
// }
// );
// #endif
// ,
if (!startByNotificationId) {
// customSwitchTab({
// url: "/pages/Conversation/index",
// });
} else {
if (startByNotificationId) {
await uni.$UIKitStore.uiStore.selectConversation(
startByNotificationId
);
uni.navigateTo({
url: `/pages/Chat/index?conversationId=${startByNotificationId}`,
});
startByNotificationId = "";
}
}
}).catch((err) => {
console.log("登录失败", err);
});
},
logout() {
uni.removeStorageSync(STORAGE_KEY);
try {
// nimCallKit.logout({}, (ret) => {
// if (ret.code != 200) {
// console.log("退");
// } else {
// console.log("退");
// }
// });
} catch (error) {
console.log("音视频通话插件退出失败", error);
}
// 退
uni.$UIKitNIM.V2NIMLoginService.logout().then((res) => {
console.log("退出登录", res);
uni.$UIKitStore.destroy();
uni.clearStorageSync();
//
uni.reLaunch({
url: '/pages_app/login/login'
});
});
},
} catch (error) {}
},
onHide() {},
methods: {},
};
</script>
<template>
<GlobalDialog />
<GlobalDialog />
</template>
<style lang="scss">
/*每个页面公共css */
@import '@/uni_modules/uni-scss/index.scss';
/* #ifndef APP-NVUE */
@import '@/static/customicons.css';
//
page {
background-color: #f5f5f5;
}
.uni-navbar__header-btns-right{
min-width: 120rpx;
width:auto!important;
}
/* #endif */
.uni-nav-bar-text{
font-weight: bold;
font-size: 34rpx!important;
}
.example-info {
font-size: 14px;
color: #333;
padding: 10px;
}
.twoline{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.oneline{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.uni-navbar .uniui-left{
font-weight: bold;
font-size: 50rpx!important;
}
::-webkit-scrollbar { display: none; }
// .nav-right{
// margin-top: -20px;
// }
/*每个页面公共css */
@import "@/uni_modules/uni-scss/index.scss";
/* #ifndef APP-NVUE */
@import "@/static/customicons.css";
//
page {
background-color: #f5f5f5;
}
.uni-navbar__header-btns-right {
min-width: 120rpx;
width: auto !important;
}
/* #endif */
.uni-nav-bar-text {
font-weight: bold;
font-size: 36rpx !important;
}
.example-info {
font-size: 14px;
color: #333;
padding: 10px;
}
.twoline {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.oneline {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.uni-navbar .uniui-left {
font-weight: bold;
font-size: 50rpx !important;
}
::-webkit-scrollbar {
display: none;
}
.status_bar {
height: var(--status-bar-height);
background-color: #eeeeee;
}
.navbox{
position: fixed;
top: 0;
left: 0;
right: 0;
height:calc(var(--status-bar-height) + 44px);
z-index: 9999;
}
</style>

View File

@ -1,3 +1,3 @@
{
"prompt" : "template"
"prompt" : "none"
}

View File

@ -1,4 +1,5 @@
import {request} from '@/utils/request.js'
const api = {
expertWxLogin(jscode){
@ -607,7 +608,46 @@ const api = {
payGanDanFile(data){
return request('/expertPay/payGanDanFileOrder', data, 'post', false);
},
// discollection(data){
// return request('/expert/discollection', data, 'post', false);
// },
// collection(data){
// return request('/expert/collection', data, 'post', false);
// },
getTypeUuidByName(data){
return request('/expertAPI/getTypeUuidByName', data, 'post', false);
},
videoByTypeNew(data){
return request('/expertAPI/videoByTypeNew', data, 'post', false);
},
meetingHistoryList(data){
return request('/expertAPI/meetingHistoryList', data, 'post', false);
},
// 扫码登录相关API
// 确认扫码登录
confirmScanLogin(data){
return request('/expertAPI/confirmScanLogin', data, 'post', false);
},
// 取消扫码登录
cancelScanLogin(data){
return request('/expertAPI/cancelScanLogin', data, 'post', false);
},
// 获取扫码登录状态
getScanLoginStatus(data){
return request('/expertAPI/getScanLoginStatus', data, 'post', false);
},
gethonorDetail(data){
return request('/expertAPI/gethonorDetail', data, 'post', false);
},
checkVersion(data){
return request('/expertAPI/checkVersion', data, 'post', false);
},
getIsAccount(data){
return request('/expertAPI/getIsAccount', data, 'post', false);
},
}
export default api

View File

@ -112,7 +112,7 @@ const maskStyle = computed(() => position.value === 'top' ? { paddingTop: offset
right: 0;
bottom: 0;
width: 100%;
display: flex;
display: none;
align-items: center;
justify-content: center;
//

View File

@ -1,15 +1,16 @@
<template>
<!-- 顶部导航栏 -->
<uni-nav-bar
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
:left-icon="showLeft?'left':''"
:title="title"
@clickLeft="goBack"
fixed
color="#8B2316"
height="180rpx"
:border="false"
backgroundColor="#eeeeee"
/>
</view>
</template>
<script setup>
@ -20,6 +21,10 @@
type:Boolean,
default:true
},
noticeBack:{
type:Boolean,
default:false
},
title:{
type:String,
default:''
@ -33,10 +38,21 @@
url: '/pages/index/index'
});
}
});
});
};
</script>
<style scoped>
.navbox{
position: fixed;
top: 0;
left: 0;
right: 0;
height:(var(--status-bar-height)+44px);
z-index: 9999;
}
.status_bar{
height: var(--status-bar-height);
background-color: #eeeeee;
}
</style>

View File

@ -5,7 +5,7 @@
v-for="(item, index) in tabList"
:key="index"
@click="switchTab(index, item)"
:class="{ active: currentTab === index }"
:class="{ active: 2 === index }"
>
<!-- 图标容器 -->
<view class="icon-container">
@ -121,32 +121,56 @@
if (currentTab.value === index) return;
// tab
currentTab.value = index;
//
if (item.showRedDot) {
item.showRedDot = false;
//currentTab.value = index;
let msg="home"
if(index == 0){
msg="home"
}else if(index == 1){
msg="patientClass"
}else if(index == 2){
msg="live"
}else if(index == 3){
msg="education"
}else if(index == 4){
msg="mine"
}
try{
uni.sendNativeEvent('goTabbarPage', {
msg: msg
},ret => {
console.log(ret);
})
}catch(e){
console.log(e);
}
plus.runtime.quit()
//
// if (item.showRedDot) {
// item.showRedDot = false;
// }
//
uni.redirectTo({
url: item.pagePath,
fail: () => {
// 使navigateTo
uni.navigateTo({
url: item.pagePath,
fail: () => {
// uni.redirectTo({
// url: item.pagePath,
// fail: () => {
// // 使navigateTo
// uni.navigateTo({
// url: item.pagePath,
// fail: () => {
}
});
}
});
// }
// });
// }
// });
//
emit('tabChange', {
index,
item
});
// emit('tabChange', {
// index,
// item
// });
};
//

View File

@ -0,0 +1,64 @@
<template>
<view class="top-tip" v-if="visible">
<view class="title">{{ title }}</view>
<view class="content">
{{ content }}
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const visible = ref(false)
const props = defineProps({
title: {
type: String,
default: ''
},
content: {
type: String,
default: ''
}
})
const open = () => {
visible.value = true
}
const close = () => {
visible.value = false
}
defineExpose({
open,
close
})
</script>
<style scoped lang="scss">
.top-tip{
position: fixed;
top: 180rpx;
left: 40rpx;
right:40rpx;
padding: 20rpx;
border-radius: 16rpx;
background-color: rgba(0, 0, 0, 0.8);
z-index: 99999999999;
.title{
font-size: 32rpx;
color: #fff;
text-align: left;
}
.content{
font-size: 28rpx;
color: #fff;
text-align: left;
margin-top: 25rpx;
line-height: 1.5;
}
}
</style>

View File

@ -0,0 +1,273 @@
/**
* 本模块封装了AndroidiOS的应用权限判断打开应用权限设置界面以及位置系统服务是否开启
*/
var isIos
// #ifdef APP-PLUS
isIos = (plus.os.name == "iOS")
// #endif
// 判断推送权限是否开启
function judgeIosPermissionPush() {
var result = false;
var UIApplication = plus.ios.import("UIApplication");
var app = UIApplication.sharedApplication();
var enabledTypes = 0;
if (app.currentUserNotificationSettings) {
var settings = app.currentUserNotificationSettings();
enabledTypes = settings.plusGetAttribute("types");
console.log("enabledTypes1:" + enabledTypes);
if (enabledTypes == 0) {
console.log("推送权限没有开启");
} else {
result = true;
console.log("已经开启推送功能!")
}
plus.ios.deleteObject(settings);
} else {
enabledTypes = app.enabledRemoteNotificationTypes();
if (enabledTypes == 0) {
console.log("推送权限没有开启!");
} else {
result = true;
console.log("已经开启推送功能!")
}
console.log("enabledTypes2:" + enabledTypes);
}
plus.ios.deleteObject(app);
plus.ios.deleteObject(UIApplication);
return result;
}
// 判断定位权限是否开启
function judgeIosPermissionLocation() {
var result = false;
var cllocationManger = plus.ios.import("CLLocationManager");
var status = cllocationManger.authorizationStatus();
result = (status != 2)
console.log("定位权限开启:" + result);
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
/* var enable = cllocationManger.locationServicesEnabled();
var status = cllocationManger.authorizationStatus();
console.log("enable:" + enable);
console.log("status:" + status);
if (enable && status != 2) {
result = true;
console.log("手机定位服务已开启且已授予定位权限");
} else {
console.log("手机系统的定位没有打开或未给予定位权限");
} */
plus.ios.deleteObject(cllocationManger);
return result;
}
// 判断麦克风权限是否开启
function judgeIosPermissionRecord() {
var result = false;
var avaudiosession = plus.ios.import("AVAudioSession");
var avaudio = avaudiosession.sharedInstance();
var permissionStatus = avaudio.recordPermission();
console.log("permissionStatus:" + permissionStatus);
if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
console.log("麦克风权限没有开启");
} else {
result = true;
console.log("麦克风权限已经开启");
}
plus.ios.deleteObject(avaudiosession);
return result;
}
// 判断相机权限是否开启
function judgeIosPermissionCamera() {
var result = false;
var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
console.log("authStatus:" + authStatus);
if (authStatus == 3) {
result = true;
console.log("相机权限已经开启");
} else {
console.log("相机权限没有开启");
}
plus.ios.deleteObject(AVCaptureDevice);
return result;
}
// 判断相册权限是否开启
function judgeIosPermissionPhotoLibrary() {
var result = false;
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
var authStatus = PHPhotoLibrary.authorizationStatus();
console.log("authStatus:" + authStatus);
if (authStatus == 3) {
result = true;
console.log("相册权限已经开启");
} else {
console.log("相册权限没有开启");
}
plus.ios.deleteObject(PHPhotoLibrary);
return result;
}
// 判断通讯录权限是否开启
function judgeIosPermissionContact() {
var result = false;
var CNContactStore = plus.ios.import("CNContactStore");
var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
if (cnAuthStatus == 3) {
result = true;
console.log("通讯录权限已经开启");
} else {
console.log("通讯录权限没有开启");
}
plus.ios.deleteObject(CNContactStore);
return result;
}
// 判断日历权限是否开启
function judgeIosPermissionCalendar() {
var result = false;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
if (ekAuthStatus == 3) {
result = true;
console.log("日历权限已经开启");
} else {
console.log("日历权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
// 判断备忘录权限是否开启
function judgeIosPermissionMemo() {
var result = false;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
if (ekAuthStatus == 3) {
result = true;
console.log("备忘录权限已经开启");
} else {
console.log("备忘录权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
// Android权限查询
function requestAndroidPermission(permissionID) {
return new Promise((resolve, reject) => {
plus.android.requestPermissions(
[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
function(resultObj) {
var result = 0;
for (var i = 0; i < resultObj.granted.length; i++) {
var grantedPermission = resultObj.granted[i];
console.log('已获取的权限:' + grantedPermission);
result = 1
}
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
var deniedPresentPermission = resultObj.deniedPresent[i];
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
result = 0
}
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
var deniedAlwaysPermission = resultObj.deniedAlways[i];
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
result = -1
}
resolve(result);
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
// if (result != 1) {
// gotoAppPermissionSetting()
// }
},
function(error) {
console.log('申请权限错误:' + error.code + " = " + error.message);
resolve({
code: error.code,
message: error.message
});
}
);
});
}
// 使用一个方法,根据参数判断权限
function judgeIosPermission(permissionID) {
if (permissionID == "location") {
return judgeIosPermissionLocation()
} else if (permissionID == "camera") {
return judgeIosPermissionCamera()
} else if (permissionID == "photoLibrary") {
return judgeIosPermissionPhotoLibrary()
} else if (permissionID == "record") {
return judgeIosPermissionRecord()
} else if (permissionID == "push") {
return judgeIosPermissionPush()
} else if (permissionID == "contact") {
return judgeIosPermissionContact()
} else if (permissionID == "calendar") {
return judgeIosPermissionCalendar()
} else if (permissionID == "memo") {
return judgeIosPermissionMemo()
}
return false;
}
// 跳转到**应用**的权限页面
function gotoAppPermissionSetting() {
if (isIos) {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
// console.log(plus.device.vendor);
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
// 检查系统的设备服务是否开启
// var checkSystemEnableLocation = async function () {
function checkSystemEnableLocation() {
if (isIos) {
var result = false;
var cllocationManger = plus.ios.import("CLLocationManager");
var result = cllocationManger.locationServicesEnabled();
console.log("系统定位开启:" + result);
plus.ios.deleteObject(cllocationManger);
return result;
} else {
var context = plus.android.importClass("android.content.Context");
var locationManager = plus.android.importClass("android.location.LocationManager");
var main = plus.android.runtimeMainActivity();
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
console.log("系统定位开启:" + result);
return result
}
}
// module.exports = {
// judgeIosPermission: judgeIosPermission,
// requestAndroidPermission: requestAndroidPermission,
// checkSystemEnableLocation: checkSystemEnableLocation,
// gotoAppPermissionSetting: gotoAppPermissionSetting
// }
export default {judgeIosPermission,requestAndroidPermission,checkSystemEnableLocation,gotoAppPermissionSetting}

View File

@ -1,5 +1,9 @@
// ES6模块化引入
//import VConsole from 'vconsole'
import App from './App'
import uviewPlus, { setConfig } from 'uview-plus'
//const vConsole = new VConsole();
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'

View File

@ -7,6 +7,7 @@
"transformPx" : false,
"app-plus" : {
/* 5+App */
"runmode" : "liberate",
"usingComponents" : true,
"nvueCompiler" : "uni-app",
"nvueStyleCompiler" : "uni-app",
@ -16,23 +17,27 @@
"autoclose" : true,
"delay" : 0
},
"useragent": {
"value": "gdxz",
"concatenate": true
},
"compatible" : {
"ignoreVersion" : true
},
"useragent" : {
"value" : "gdxz",
"concatenate" : true
},
"modules" : {
"OAuth" : {},
"Payment" : {},
"Share" : {},
"Webview-x5" : {},
"UIWebview" : {},
"Push" : {},
"VideoPlayer" : {}
"VideoPlayer" : {},
"OAuth" : {},
"Barcode" : {},
"Camera" : {}
},
/* */
"distribute" : {
/* */
"android" : {
"hasTaskAffinity" : true,
/* android */
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
@ -63,8 +68,13 @@
"oauth" : {
"weixin" : {
"appid" : "wxbf3658f5e674667c",
"appsecret" : "c4505a04a9910c65efea8e11ffc93f92",
"UniversalLinks" : "https://doc.igandan.com/gdxzExpert/"
},
"univerify" : {},
"sina" : {
"appkey" : "2071931330",
"redirect_uri" : "http://sns.whalecloud.com/sina2/callback",
"UniversalLinks" : ""
}
},
"payment" : {
@ -79,6 +89,11 @@
"weixin" : {
"appid" : "wxbf3658f5e674667c",
"UniversalLinks" : "https://doc.igandan.com/gdxzExpert/"
},
"sina" : {
"appkey" : "2071931330",
"redirect_uri" : "http://sns.whalecloud.com/sina2/callback",
"UniversalLinks" : "https://doc.igandan.com/gdxzExpert/"
}
},
"speech" : {},
@ -86,7 +101,7 @@
},
"splashscreen" : {
"androidStyle" : "common",
"useOriginalMsgbox" : true,
"useOriginalMsgbox" : false,
"iosStyle" : "common"
},
"icons" : {

View File

@ -18,6 +18,8 @@
"mobx": "^6.6.1",
"nim-web-sdk-ng": "^10.9.50",
"pinyin": "^4.0.0",
"uview-plus": "^3.4.73"
"uniapp-video-player": "^1.3.0",
"uview-plus": "^3.4.73",
"vconsole": "^3.15.1"
}
}

2383
pages.json

File diff suppressed because it is too large Load Diff

View File

@ -167,9 +167,8 @@ $padding-small: 10px;
.page {
background-color: $gray-bg;
min-height: calc(100vh - 180rpx);
padding-bottom: 120rpx;
min-height: calc(100vh - var(--status-bar-height) - 44px);
margin-top:calc(var(--status-bar-height) + 44px);
}
//

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

31
pages/loading/loading.vue Normal file
View File

@ -0,0 +1,31 @@
<template>
<navBar :showLeft="false" :title="'加载中...'"></navBar>
<view class="loading-page"></view>
</template>
<script setup>
import { onMounted } from 'vue';
import { onLoad,onUnload,onHide,onShow } from '@dcloudio/uni-app';
import navBar from '@/components/navBar/navBar.vue';
onShow(() => {
uni.showLoading({
title: '加载中...',
mask: true
});
});
onUnload(() => {
uni.hideLoading();
});
onHide(() => {
uni.hideLoading();
});
</script>
<style scoped lang="scss">
.loading-page{
width: 100%;
height: 100%;
background: #fff;
}
</style>

View File

@ -6,14 +6,18 @@
<!-- 用户信息卡片 -->
<view class="user-card"
:style="{'background-image': `url(${myInfoBackGround || '/static/big_background_my.png'})`, 'background-size':'cover','background-repeat':'no-repeat'}">
<view class="user-info" @click="goToPage('myInfo')">
<image class="avatar" :src="avatar" mode="aspectFill"></image>
<view class="user-info">
<image class="avatar" :src="avatar" mode="aspectFill" @click="goToPage('myInfo')"></image>
<view class="user-info-content">
<text class="username">{{username}}</text>
<scroll-view class="honor_box" scroll-x="true" show-scrollbar="false">
<view class="honor_wrap">
<view class="honor_item" v-for="item in honor_list" :key="item.id">
<image class="honor_image" src="/static/wdsc.png" mode="aspectFill"></image>
<view class="honor_item" v-if="ranking > 0">
<image class="honor_image" :src="suifangImg" mode="aspectFill"></image>
<text class="honor_text">随访达人|排名{{ranking}}</text>
</view>
<view class="honor_item" v-for="item in honor_list" :key="item.id" @click="getHonorDetail(item.id)">
<image class="honor_image" :src="rongyu" mode="aspectFill"></image>
<text class="honor_text">{{ item.nick_name }}</text>
</view>
</view>
@ -100,9 +104,9 @@
<!-- 账户明细 -->
<view class="section">
<view class="section-title">账户明细</view>
<up-scroll-list indicatorActiveColor="#8B2316" indicatorWidth="40rpx" indicatorBarWidth="20rpx"
<up-scroll-list :indicator="isAccount" indicatorActiveColor="#8B2316" indicatorWidth="40rpx" indicatorBarWidth="20rpx"
class="account-grid">
<view class="account-item" @click="goToPage('myAccount')">
<view class="account-item" @click="goToPage('myAccount')" v-if="isAccount">
<up-image :src="wdzzImg" width="48rpx" height="48rpx"></up-image>
<text>我的账户</text>
</view>
@ -114,11 +118,11 @@
<up-image :src="wdflImg" width="48rpx" height="48rpx"></up-image>
<text>我的福利</text>
</view>
<view class="account-item" @click="goToPage('myFlower')">
<view class="account-item" @click="goToPage('myFlower')" v-if="isAccount">
<up-image :src="wdxhImg" width="48rpx" height="48rpx"></up-image>
<text>我的鲜花</text>
</view>
<view class="account-item" @click="goToPage('pptDetail')">
<view class="account-item" @click="goToPage('pptDetail')" v-if="isAccount">
<up-image :src="kjmxImg" width="48rpx" height="48rpx"></up-image>
<text>课件明细</text>
</view>
@ -148,8 +152,8 @@
<text>通知已{{notice_on?'开':'关'}}</text>
</view>
<view class="operation-item" @click="showVersion">
<up-image :src="versionImg" width="48rpx" height="48rpx"></up-image>
<text>V{{version}}</text>
<up-image :src="hasNewVersion?versionImgOn:versionImg" width="48rpx" height="48rpx"></up-image>
<text>{{hasNewVersion?'有新版本':'V'+version}}</text>
</view>
</view>
</view>
@ -174,7 +178,7 @@
<!-- 常用银行卡 -->
<view class="sectioncell bank-section">
<view class="bank-item" @click="goToPage('bankCard')">
<view class="bank-item" @click="goToPage('bankCard')" v-if="isAccount">
<view class="bank-icon">
<up-image :src="cyyhk" width="48rpx" height="48rpx"></up-image>
</view>
@ -193,10 +197,12 @@
<view class="settings-icon">
<up-image :src="settingImg" width="48rpx" height="48rpx"></up-image>
</view>
<view class="settings-content">
<view class="settings-content flex-row" >
<text class="settings-title">设置与帮助</text>
<text v-if="hasNewVersion && isIos" class="new-version">有新版本</text>
</view>
<view class="settings-arrow">
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
</view>
</view>
@ -233,6 +239,29 @@
</view>
</view>
</view>
</up-overlay>
<up-overlay :show="showHonor" >
<view class="honorwrap">
<view class="honorbox">
<image :src="docUrl+honnor_detail.img" class="honor_image"></image>
<uni-icons type="close" class="close_honor" size="24" color="#fff" @click="showHonor=false"></uni-icons>
<view class="content">
<view class="content_title">
{{honnor_detail.title}}
</view>
<view class="row">
<view class="row_item" v-for="item in honnor_detail.honorlist" :key="item.id">
{{ item.nick_name }}
</view>
</view>
<view class="content_content">
{{honnor_detail.note}}
</view>
</view>
</view>
</view>
</up-overlay>
</template>
@ -248,6 +277,11 @@
import {
onShow
} from "@dcloudio/uni-app";
const showHonor=ref(false);
import docUrl from "@/utils/docUrl.js"
import honnorExpert from "@/static/zixunexpert.png"
import honnorYingxiong from "@/static/yingxiongbanglog.png"
import rongyu from "@/static/yxb.png"
import version from '@/utils/version.js'
import hzshImg from "@/static/hzsh.png"
import hzfzImg from "@/static/hzfz.png"
@ -269,8 +303,9 @@
import ghsjhImg from "@/static/ghsjh.png"
import tzykImg from "@/static/xxtx.png"
import versionImg from "@/static/fxxbb.png"
import versionImgOn from "@/static/fxbb.png"
import jifenImg from "@/static/point_buy.png"
import suifangImg from "@/static/suifang.png"
import fulicard from "@/static/fulicard.png"
import fpgl from "@/static/fpgl.png"
import cyyhk from "@/static/cyhhk.png"
@ -279,6 +314,7 @@
import my_api from '@/api/my_api.js'
import navTo from "@/utils/navTo"
import linkUrl from "@/utils/docUrl"
//
const isLargeFont = ref(false);
const hasSign = ref(false)
@ -298,8 +334,25 @@
const totalPoints = ref(0)
const sign_in = ref(0)
const honor_list = ref([])
const ranking = ref(0)
const notice_on = ref(false)
const hasNewVersion = ref(false)
const checkVersion=()=>{
api.checkVersion().then(res=>{
if(res.code==80002){
hasNewVersion.value=true
}
})
}
const isAccount = ref(false);
const getIsAccount=()=>{
api.getIsAccount().then(res=>{
if(res.code==1){
isAccount.value=res.is_account==0?false:true;
}
})
}
const goNews=()=>{
let url=docUrl+signInfo.news.path;
// #ifdef H5
@ -376,6 +429,7 @@
totalPoints.value = data.total_points || 0
sign_in.value = data.sign_in || 0
honor_list.value = data.honor_list || []
ranking.value = data.ranking || 0
}
} catch (e) {
console.error('获取我的信息失败', e)
@ -629,7 +683,16 @@ const switchPushPermissions = () => {
showCancel: false
});
};
const honnor_detail=ref({});
const getHonorDetail=async(id)=>{
const res = await api.gethonorDetail({
id:id
})
if (res.code == 1 ) {
honnor_detail.value = res.data;
showHonor.value = true;
}
}
const onStatsClick = (key) => {
let url = '';
switch (key) {
@ -661,13 +724,23 @@ const switchPushPermissions = () => {
isBoundWechat.value = res.data
}
}
const isBoundWechat = ref(false)
const isBoundWechat = ref(false);
const isIos = ref(false);
onShow(() => {
console.log('我的页面显示');
getUserInfoFromStorage() // storage
fetchMyInfo() // API
getIsBoundWechat()
isNoticeOn()
isNoticeOn();
checkVersion();
getIsAccount();
let systemInfo = uni.getSystemInfoSync();
console.log(systemInfo)
if(systemInfo.osName == 'ios'){
isIos.value = true;
}else{
isIos.value = false;
}
});
</script>
@ -691,11 +764,91 @@ const switchPushPermissions = () => {
.page {
background-color: $gray-bg;
height: calc(100vh - 180rpx);
margin-top:calc(var(--status-bar-height) + 44px);
height: calc(100vh - var(--status-bar-height) - 44px);
overflow-y: scroll;
padding-bottom: 100rpx;
}
.flex-row{
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.new-version{
background:#8B2316;
color:#fff;
font-size: 24rpx;
margin-right: 10rpx;
padding: 8rpx 20rpx;
border-radius: 30rpx;
}
.honorwrap{
width:100%;
height:100vh;
display: flex;
align-items: center;
justify-content: center;
.honorbox{
width:80%;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
min-height:200rpx;
background:linear-gradient(to bottom, #54049b, #da4ee2);
border-radius: 20rpx;
padding: 20rpx;
.row{
display: grid;
grid-template-columns: repeat(2, 1fr); /* 创建两列,每列等宽 */
gap: 20px;
.row_item{
font-size: 26rpx;
color: #da4ee2;
margin-right: 20rpx;
text-align: center;
padding: 10rpx 20rpx;
background:#fff;
white-space: nowrap;
border-radius: 10rpx;
border:1rpx solid #da4ee2;
}
}
.close_honor{
position: absolute;
top:20rpx;
right:20rpx;
}
.honor_image{
width:269rpx;
height:213rpx;
position: absolute;
top:-76rpx;
left:50%;
transform: translateX(-50%);
}
.content{
margin-top:73rpx;
background:#fff;
border-radius: 5rpx;
padding: 20rpx;
.content_title{
font-weight: bold;
padding: 35rpx 0 20rpx;
text-align: center;
font-size: 36rpx;
color: #da4ee2;
}
.content_content{
font-size: 26rpx;
color: #333;
margin-top: 20rpx;
line-height: 1.5;
}
}
}
}
.signwrap{
display: flex;
align-items: center;
@ -825,6 +978,11 @@ const switchPushPermissions = () => {
.honor_text {
font-size: 24rpx;
color: $white;
display: flex;
align-items: center;
.bar_rank{
padding:0 6rpx;
}
}
}
}

View File

@ -10,12 +10,13 @@
<view class="tab-item" :class="{ active: activeTab === 0 }" @click="switchTab(0)">
患教文库
</view>
<view class="bar"></view>
<view class="tab-item" :class="{ active: activeTab === 1 }" @click="switchTab(1)">
患教视频
</view>
<view class="tab-item" :class="{ active: activeTab === 2 }" @click="switchTab(2)">
<!-- <view class="tab-item" :class="{ active: activeTab === 2 }" @click="switchTab(2)">
常见问题
</view>
</view> -->
</view>
<!-- 筛选栏 -->
@ -51,7 +52,7 @@
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.DES }}
{{ tag.NAME }}
</view>
</view>
@ -74,20 +75,20 @@
:lower-threshold="100"
>
<view class="article-item" v-for="(article, index) in articleList" :key="article.uuid || index" @click="goToDetail(article)">
<image class="article-image" :src="article.image" mode="aspectFill"></image>
<image class="article-image" :src="article.image"></image>
<view class="article-content">
<view class="article-title">{{ article.title }}</view>
<view class="article-meta">
<view class="date-tag" v-if="article.isToday">今日</view>
<view class="date" v-else>{{ article.date }}</view>
<view class="stats">
<view class="stat-item">
<view class="stat-item readnum">
<uni-icons type="eye" size="12" color="#999"></uni-icons>
<text>{{ article.views }}</text>
<text>{{ formatNumber(article.views) }}</text>
</view>
<view class="stat-item">
<uni-icons type="hand-up" size="13" color="#999"></uni-icons>
<text>{{ article.likes }}</text>
<text>{{ formatNumber(article.likes) }}</text>
</view>
</view>
</view>
@ -130,6 +131,7 @@
import docUrl from '@/utils/docUrl.js';
import navTo from '@/utils/navTo.js';
import navBar from "@/components/navBar/navBar.vue"
import formatNumber from '@/utils/formatNumber.js';
const type=ref(1)
//
@ -147,6 +149,7 @@
//
currentPage.value = 1;
hasMore.value = true;
articleList.value = [];
polularScienceArticleListByKeywordsNew(true);
};
const loadGuideTags = async () => {
@ -240,9 +243,18 @@
//
const formatDate = (date) => {
const today = new Date();
const year = date.getFullYear();
const currentYear = today.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${month}-${day}`;
//
if (year === currentYear) {
return `${month}-${day}`;
} else {
return `${year}-${month}-${day}`;
}
};
//
const filterTags = ref([]);
@ -270,7 +282,7 @@
//
if (article.path) {
uni.navigateTo({
url: `/pages_app/webview/webview?url=${encodeURIComponent(docUrl+article.path)}&title=${article.title}`
url: `/pages_app/webview/webview?url=${encodeURIComponent(docUrl+article.path)}&title=患教详情`
});
}
};
@ -319,7 +331,19 @@
};
const toggleTag = (index) => {
filterTags.value[index].selected = !filterTags.value[index].selected;
const tag = filterTags.value[index];
// 3
if (!tag.selected) {
const selectedCount = filterTags.value.filter(t => t.selected).length;
if (selectedCount >= 3) {
uni.showToast({
title: '最多选择3个标签',
icon: 'none'
});
return;
}
}
tag.selected = !tag.selected;
};
const resetFilter = () => {
@ -336,7 +360,7 @@
//
if (hasSelected) {
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.DES);
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.NAME);
console.log('选中的标签:', selectedTags);
keywords.value = selectedTags.join(',');
} else {
@ -346,7 +370,7 @@
//
currentPage.value = 1;
hasMore.value = true;
articleList.value = [];
showFilter.value = false;
polularScienceArticleListByKeywordsNew(true);
};
@ -369,10 +393,10 @@ $gray-dark: #666;
$text-color: #333;
//
$border-radius: 8px;
$border-radius-small: 6px;
$padding: 15px;
$padding-small: 10px;
$border-radius: 16rpx;
$border-radius-small: 12rpx;
$padding: 30rpx;
$padding-small: 20rpx;
.page {
background-color: $gray-bg;
@ -385,19 +409,25 @@ $padding-small: 10px;
.top{
width:100%;
position: fixed;
top:180rpx;
top:calc(var(--status-bar-height) + 44px);
}
//
.tabs {
background-color: $white;
display: flex;
border-bottom: 1px solid $gray-light;
align-items: center;
border-bottom: 2rpx solid $gray-light;
.bar{
width: 2rpx;
height: 98rpx;
background-color: $gray-light;
}
.tab-item {
flex: 1;
text-align: center;
padding: $padding 0;
font-size: 16px;
font-size: 32rpx;
color: $gray-dark;
position: relative;
@ -411,8 +441,8 @@ $padding-small: 10px;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 2px;
width: 60rpx;
height: 4rpx;
}
}
@ -425,25 +455,25 @@ $padding-small: 10px;
padding: $padding-small $padding;
display: flex;
align-items: center;
border-bottom: 1px solid $gray-light;
border-bottom: 2rpx solid $gray-light;
.search-box {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
gap: 10rpx;
.search-text {
font-size: 14px;
font-size: 28rpx;
color: $gray-medium;
}
}
.divider {
width: 1px;
height: 16px;
background-color: $gray-medium;
width: 2rpx;
height: 32rpx;
background-color: $gray-light;
margin: 0 $padding;
}
@ -452,8 +482,8 @@ $padding-small: 10px;
display: flex;
align-items: center;
justify-content: center;
gap: 3px;
font-size: 14px;
gap: 6rpx;
font-size: 28rpx;
color: #999;
transition: all 0.3s ease;
}
@ -466,8 +496,8 @@ $padding-small: 10px;
//
.article-list {
position: fixed;
height:calc(100vh - 450rpx);
top: 342rpx;
height:calc(100vh - var(--status-bar-height) - 44px - 188rpx);
top: calc(var(--status-bar-height) + 44px + 188rpx);
padding-bottom: 130rpx;
.article-item {
@ -476,11 +506,11 @@ $padding-small: 10px;
border-radius: $border-radius;
padding: $padding;
display: flex;
gap: 12px;
gap: 24rpx;
.article-image {
width: 80px;
height: 80px;
width: 260rpx;
height: 160rpx;
border-radius: $border-radius-small;
flex-shrink: 0;
}
@ -492,7 +522,7 @@ $padding-small: 10px;
justify-content: space-between;
.article-title {
font-size: 16px;
font-size: 32rpx;
color: $text-color;
line-height: 1.4;
margin-bottom: $padding-small;
@ -506,31 +536,36 @@ $padding-small: 10px;
.article-meta {
display: flex;
justify-content: space-between;
align-items: center;
.date-tag {
background-color: $primary-color;
color: $white;
font-size: 12px;
padding: 2px 6px;
border-radius: 3px;
font-size: 24rpx;
padding: 4rpx 12rpx;
border-radius: 6rpx;
}
.date {
font-size: 12px;
width: 125rpx;
font-size: 24rpx;
color: $gray-medium;
white-space: nowrap;
}
.stats {
display: flex;
gap: $padding;
margin-left: 50rpx;
.readnum{
width:100rpx;
}
.stat-item {
display: flex;
align-items: center;
gap: 3px;
font-size: 12px;
gap: 10rpx;
font-size: 24rpx;
color: $gray-medium;
}
}
@ -591,8 +626,8 @@ $padding-small: 10px;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
font-size: 12px;
gap: 4rpx;
font-size: 24rpx;
color: $gray-medium;
&.active {
@ -600,7 +635,7 @@ $padding-small: 10px;
}
text {
font-size: 10px;
font-size: 20rpx;
}
}
}
@ -619,7 +654,7 @@ $padding-small: 10px;
z-index: 9999;
.filter-content {
margin-top:329rpx;
margin-top:calc(var(--status-bar-height) + 44px + 188rpx);;
background-color: $white;
// border-radius: 20rpx 20rpx 0 0;
@ -638,9 +673,14 @@ $padding-small: 10px;
.tag-item {
background-color: #f8f8f8;
color: $gray-dark;
padding: 8rpx 24rpx;
padding: 8rpx 0rpx;
width:150rpx;
text-align:center;
border-radius: 30rpx;
font-size: 26rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border: 2rpx solid #efefef;
transition: all 0.3s ease;

File diff suppressed because it is too large Load Diff

View File

@ -218,7 +218,7 @@
uni.setStorageSync('userInfo', res.data);
}
const app = getApp()
app.initNim({appkey: "885dea390870814acf3ba8558c717572", account:result.data.YX_accid, token: result.data.YX_token })
//app.initNim({appkey: "885dea390870814acf3ba8558c717572", account:result.data.YX_accid, token: result.data.YX_token })
//
uni.redirectTo({
url: '/pages/index/index'

View File

@ -0,0 +1,582 @@
<template>
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="过往会议"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="nav-right">
<uni-icons type="search" size="24" color="#8B2316" @click="searchMeetHistroy"></uni-icons>
</view>
</template>
</uni-nav-bar>
</view>
<view class="page">
<!-- 筛选标签栏 -->
<view class="filter-bar">
<view class="filter-item" :class="{active:monthName}" @click="showTimePopup">
<text>{{ monthName?monthName:'会议年份' }}</text>
<up-image :src="monthName?selectOn:select" width="26rpx" height="26rpx"></up-image>
</view>
<view class="filter-divider"></view>
<view class="filter-item" :class="{active:typeName}" @click="showLocationPopup">
<text>{{ typeName?typeName:'会议月份' }}</text>
<up-image :src="typeName?selectOn:select" width="26rpx" height="26rpx"></up-image>
</view>
</view>
<!-- 可滚动内容区域 -->
<scroll-view
class="scroll-content"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="isRefreshing"
:refresher-threshold="100"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
:lower-threshold="100"
>
<!-- 会议列表 -->
<view class="meeting-list" v-if="meetingList.length > 0">
<view
class="meeting-item" @click="goDetail(meeting)"
v-for="(meeting, index) in meetingList"
:key="meeting.id || index"
>
<view class="meeting-content">
<text class="meeting-title">{{
meeting.title || meeting.name
}}</text>
<view class="meeting-info">
<view class="meeting-time">
<view class="timebox">
<up-image
:src="timeImg"
width="24rpx"
height="24rpx"
></up-image>
</view>
<view class="text"> {{ formatDate(meeting.begin_date) }}</view>
</view>
<view class="meeting-location">
<uni-icons type="location" size="14" color="#999"></uni-icons>
{{ meeting.location }}
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!isRefreshing">
<empty></empty>
</view>
<!-- 加载更多提示 -->
<view class="load-more" v-if="showLoadMore">
<view class="load-more-content" v-if="isLoadingMore">
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
<text class="load-more-text">加载中...</text>
</view>
<view
class="load-more-content"
v-else-if="hasMoreData && meetingList.length > 0"
>
<text class="load-more-text">上拉加载更多</text>
</view>
<view
class="load-more-content"
v-else-if="meetingList.length > 0 && !isLoadingMore"
>
<text class="load-more-text">没有更多数据了</text>
</view>
</view>
</scroll-view>
<!-- 时间选择弹窗 -->
<view class="time-popup" v-if="isTimePopupShow" @click="hideTimePopup">
<view class="popup-mask"></view>
<view class="time-popup-content" @click.stop>
<view class="time-list">
<view
class="time-item"
v-for="(month, index) in monthList"
:key="index"
:class="{ active: selectedMonth === month.value }"
@click="selectMonth(month)"
>
<text>{{ month.label }}</text>
</view>
</view>
</view>
</view>
<!-- 地区选择弹窗 -->
<view
class="time-popup"
v-if="isLocationPopupShow"
@click="hideLocationPopup"
>
<view class="popup-mask"></view>
<view class="time-popup-content" @click.stop>
<view class="time-list">
<view
class="time-item"
v-for="(location, index) in locationList"
:key="index"
:class="{ active: selectedType === location.code }"
@click="selectLocation(location)"
>
<text>{{ location.name }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { onShow,onLoad,onHide} from "@dcloudio/uni-app";
import api from "@/api/api.js";
import navBar from "@/components/navBar/navBar.vue";
import select from "@/static/triangle_normal.png";
import selectOn from "@/static/triangle_green_theme.png"
import dayjs from "dayjs";
import timeImg from "@/static/play_long.png"
import navTo from "@/utils/navTo";
import empty from '@/components/empty/empty.vue';
//
const isTimePopupShow = ref(false);
const isLocationPopupShow = ref(false);
const selectedMonth = ref("");
const monthName = ref("");
const selectedType = ref("");
const typeName = ref("");
//
const isRefreshing = ref(false);
const isLoadingMore = ref(false);
const hasMoreData = ref(true);
const showLoadMore = ref(true);
const currentPage = ref(1);
const pageSize = ref(10);
//
const meetingList = ref([]);
const goBack=()=>{
uni.navigateBack({
fail:()=>{
uni.redirectTo({
url:'/pages/index/index'
})
}
});
}
onShow(() => {
});
onHide(() => {
});
//
const monthList = ref([]);
const generateYearList = () => {
const years = [];
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
// 10
for (let i = 0; i < 10; i++) {
const year = currentYear - i;
years.push({
value: year.toString(),
label: `${year}`,
year: year,
});
}
monthList.value = years;
console.log("生成年份列表:", monthList.value);
};
//
const locationList = ref([]);
//
const generateMonthList = () => {
const months = [];
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
const currentMonth = currentDate.getMonth() + 1; // 1-12
//
if (selectedMonth.value && selectedMonth.value === currentYear.toString()) {
for (let i = 1; i <= currentMonth; i++) {
months.push({
code: i,
name: `${i}`,
});
}
} else {
// 12
for (let i = 1; i <= 12; i++) {
months.push({
code: i,
name: `${i}`,
});
}
}
locationList.value = months;
console.log("生成月份列表:", locationList.value);
};
//
onMounted(() => {
generateYearList();
generateMonthList();
});
const searchMeetHistroy=()=>{
navTo({
url:'/pages_app/meetHistroySearch/meetHistroySearch'
})
}
onLoad(() => {
currentPage.value = 1;
hasMoreData.value = true;
loadMeetingData();
});
//
const loadMeetingData = async (isRefresh = false) => {
if (isLoadingMore.value && !isRefresh) return;
if (isRefresh) {
isRefreshing.value = true;
} else {
isLoadingMore.value = true;
}
try {
const params = {
page: currentPage.value,
pageSize: pageSize.value,
year: selectedMonth.value,
title: "",
month: selectedType.value,
};
console.log("获取会议列表参数:", params);
const response = await api.meetingHistoryList(params);
if (response && response.code === 200) {
const { list, totalPage, pageNumber } = response.data || {};
if (isRefresh) {
meetingList.value = list || [];
currentPage.value = 1;
} else {
meetingList.value = [...meetingList.value, ...(list || [])];
}
hasMoreData.value = totalPage > pageNumber;
showLoadMore.value = meetingList.value.length > 0;
}
} catch (error) {
console.error("加载会议数据失败:", error);
} finally {
isRefreshing.value = false;
isLoadingMore.value = false;
}
};
//
const onRefresh = () => {
currentPage.value = 1;
hasMoreData.value = true;
loadMeetingData(true);
};
//
const onLoadMore = () => {
if (!hasMoreData.value || isLoadingMore.value) return;
currentPage.value++;
loadMeetingData();
};
//
const showTimePopup = () => {
isTimePopupShow.value = !isTimePopupShow.value;
};
//
const hideTimePopup = () => {
isTimePopupShow.value = false;
};
//
const selectMonth = (month) => {
selectedMonth.value = month.value;
monthName.value = month.label;
console.log("选择年份:", month.label);
//
generateMonthList();
//
selectedType.value = "";
//
loadMeetingData(true);
hideTimePopup();
};
//
const showLocationPopup = () => {
if (selectedMonth.value === "") {
uni.showToast({
title: "请先选择年份",
icon: "none",
});
return;
}
isLocationPopupShow.value = !isLocationPopupShow.value;
};
//
const hideLocationPopup = () => {
isLocationPopupShow.value = false;
};
//
const selectLocation = (location) => {
selectedType.value = location.code;
typeName.value = location.name;
console.log("选择月份:", location.name);
//
loadMeetingData(true);
hideLocationPopup();
};
//
const formatDate = (dateStr) => {
if (!dateStr) return "";
return dayjs(dateStr).format("YYYY.MM.DD");
};
const goDetail=(item)=>{
const encoded = encodeURIComponent(item.path);
let imgPath=encodeURIComponent('https://doc.igandan.com/app/html/img/2016/20160714132557.png');
uni.navigateTo({
url: `/pages_app/webview/webview?url=${encoded}&sharetitle=${item.title}&bg=1&type=live_old&imgPath=${imgPath}&share=1&title=会议详情`
});
}
</script>
<style scoped lang="scss">
$white: #fff;
$gray: #999;
$gray-dark: #666;
$gray-light: #eee;
$border-color: #e0e0e0;
$theme-color: #8b2316;
// .nav-right{
// margin-top: 50rpx;
// }
.page {
background-color: #f5f5f5;
height: 100vh;
display: flex;
flex-direction: column;
}
//
.filter-bar {
position: fixed;
top: calc(var(--status-bar-height) + 44px);
width: 100%;
z-index: 2;
background-color: $white;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 30rpx;
border-bottom: 2rpx solid $gray-light;
.filter-item {
display: flex;
align-items: center;
justify-content: center;
padding: 0 20rpx;
font-size: 28rpx;
color:$gray-dark;
text {
margin-right: 10rpx;
}
&.active {
color: #8B2316;
}
}
.filter-divider {
width: 2rpx;
height: 32rpx;
background-color: $border-color;
margin: 0 60rpx;
}
}
//
.scroll-content {
position: fixed;
top: calc(var(--status-bar-height) + 44px + 88rpx);
left: 0;
right: 0;
bottom: 0;
background-color: $white;
}
//
.meeting-list {
padding: 0 30rpx;
}
.meeting-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.meeting-content {
.meeting-title {
font-size: 32rpx;
color: #333;
line-height: 1.4;
margin-bottom: 16rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.meeting-info {
display: flex;
justify-content: space-between;
gap: 8rpx;
.meeting-time,
.meeting-location {
.timebox{
margin-top: -4rpx;
margin-right: 8rpx;
}
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
}
}
}
//
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
.empty-icon {
margin-bottom: 20rpx;
}
.empty-text {
color: #999;
font-size: 28rpx;
}
}
//
.load-more {
padding: 30rpx 0;
display: flex;
justify-content: center;
.load-more-content {
display: flex;
align-items: center;
gap: 10rpx;
.load-more-text {
font-size: 24rpx;
color: #999;
}
}
}
//
.time-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
display: flex;
.popup-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.time-popup-content {
margin-top: calc(var(--status-bar-height) + 44px + 90rpx);;
position: relative;
background-color: $white;
width: 100%;
height: calc(100vh - calc(var(--status-bar-height) - 44px - 88rpx));
.time-list {
padding: 0;
height: 100%;
overflow-y: auto;
.time-item {
padding: 25rpx 60rpx;
font-size: 32rpx;
color: #666;
border-bottom: 2rpx solid #f0f0f0;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
&:hover {
background-color: #f8f8f8;
}
&.active {
background-color: #f8f8f8;
color: $theme-color;
}
}
}
}
}
</style>

View File

@ -0,0 +1,493 @@
<template>
<view class="page">
<!-- 头部搜索栏 -->
<view class="navbox">
<view class="status_bar"></view>
<view class="search-header ">
<view class="back-icon" @click="goBack">
<uni-icons type="left" size="30" color="#8B2316"></uni-icons>
</view>
<view class="search-input-wrapper">
<view class="search-icon">
<uni-icons type="search" size="20" color="#999"></uni-icons>
</view>
<input
class="search-input"
placeholder="搜索会议关键字"
v-model="searchKeyword"
@input="onSearchInput"
@confirm="onSearchConfirm"
confirm-type="search"
/>
<view class="clear-icon" v-if="searchKeyword" @click="clearSearch">
<uni-icons type="clear" size="20" color="#999"></uni-icons>
</view>
</view>
<view class="cancel-btn" @click="goSearch">
<text class="cancel-text">搜索</text>
</view>
</view>
</view>
<!-- 可滚动内容区域 -->
<scroll-view
class="scroll-content"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="isRefreshing"
:refresher-threshold="100"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
:lower-threshold="100"
>
<!-- 会议列表 -->
<view class="meeting-list" v-if="meetingList.length > 0">
<view
class="meeting-item" @click="goDetail(meeting)"
v-for="(meeting, index) in meetingList"
:key="meeting.id || index"
>
<view class="meeting-content">
<text class="meeting-title">{{
meeting.title || meeting.name
}}</text>
<view class="meeting-info">
<view class="meeting-time">
<view class="timebox">
<up-image
:src="timeImg"
width="24rpx"
height="24rpx"
></up-image>
</view>
<view class="text" >{{ formatDate(meeting.begin_date,'YYYY.MM.DD')}}</view>
<view class="text" v-if="formatDate(meeting.end_date,'YYYY.MM.DD')!=formatDate(meeting.begin_date,'YYYY.MM.DD')">{{ '-'+formatDate(meeting.end_date,'YYYY.MM.DD')}}</view>
</view>
<view class="meeting-location">
<uni-icons type="location" size="14" color="#999"></uni-icons>
{{ meeting.location }}
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!isRefreshing">
<empty></empty>
</view>
<!-- 加载更多提示 -->
<view class="load-more" v-if="showLoadMore">
<view class="load-more-content" v-if="isLoadingMore">
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
<text class="load-more-text">加载中...</text>
</view>
<view
class="load-more-content"
v-else-if="hasMoreData && meetingList.length > 0"
>
<text class="load-more-text">上拉加载更多</text>
</view>
<view
class="load-more-content"
v-else-if="meetingList.length > 0 && !isLoadingMore"
>
<text class="load-more-text">没有更多数据了</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
import api from "@/api/api.js";
import navBar from "@/components/navBar/navBar.vue";
import select from "@/static/triangle_normal.png";
import dayjs from "dayjs";
import timeImg from "@/static/play_long.png"
import empty from "@/components/empty/empty.vue";
const searchKeyword = ref('');
//
const isRefreshing = ref(false);
const isLoadingMore = ref(false);
const hasMoreData = ref(true);
const showLoadMore = ref(true);
const currentPage = ref(1);
const pageSize = ref(10);
//
const meetingList = ref([]);
const goSearch=()=>{
if(!searchKeyword.value){
uni.showToast({
title: '请输入搜索内容',
icon: 'none'
});
return;
}
currentPage.value = 1;
hasMoreData.value = true;
meetingList.value = [];
loadMeetingData();
}
const goBack=()=>{
uni.navigateBack({
fail:()=>{
uni.redirectTo({
url:'/pages/index/index'
})
}
});
}
//
const loadMeetingData = async (isRefresh = false) => {
if (isLoadingMore.value && !isRefresh) return;
if (isRefresh) {
isRefreshing.value = true;
} else {
isLoadingMore.value = true;
}
try {
const params = {
page: currentPage.value,
pageSize: pageSize.value,
year:'',
title: searchKeyword.value,
month: '',
};
console.log("获取会议列表参数:", params);
const response = await api.meetingHistoryList(params);
if (response && response.code === 200) {
const { list, totalPage, pageNumber } = response.data || {};
if (isRefresh) {
meetingList.value = list || [];
currentPage.value = 1;
} else {
meetingList.value = [...meetingList.value, ...(list || [])];
}
console.log("会议列表数据:", meetingList.value);
hasMoreData.value = totalPage > pageNumber;
showLoadMore.value = meetingList.value.length > 0;
}
} catch (error) {
console.error("加载会议数据失败:", error);
} finally {
isRefreshing.value = false;
isLoadingMore.value = false;
}
};
//
const onRefresh = () => {
currentPage.value = 1;
hasMoreData.value = true;
loadMeetingData(true);
};
//
const onLoadMore = () => {
if (!hasMoreData.value || isLoadingMore.value) return;
currentPage.value++;
loadMeetingData();
};
//
const showTimePopup = () => {
isTimePopupShow.value = !isTimePopupShow.value;
};
//
const hideTimePopup = () => {
isTimePopupShow.value = false;
};
//
const selectMonth = (month) => {
selectedMonth.value = month.value;
console.log("选择年份:", month.label);
//
generateMonthList();
//
selectedType.value = "";
//
loadMeetingData(true);
hideTimePopup();
};
//
const showLocationPopup = () => {
if (selectedMonth.value === "") {
uni.showToast({
title: "请先选择年份",
icon: "none",
});
return;
}
isLocationPopupShow.value = !isLocationPopupShow.value;
};
//
const hideLocationPopup = () => {
isLocationPopupShow.value = false;
};
//
const selectLocation = (location) => {
selectedType.value = location.code;
console.log("选择月份:", location.name);
//
loadMeetingData(true);
hideLocationPopup();
};
//
const formatDate = (dateStr) => {
if (!dateStr) return "";
return dayjs(dateStr).format("YYYY.MM.DD");
};
const goDetail=(item)=>{
const encoded = encodeURIComponent(item.path);
let imgPath=encodeURIComponent('https://doc.igandan.com/app/html/img/2016/20160714132557.png');
uni.navigateTo({
url: `/pages_app/webview/webview?url=${encoded}&sharetitle=${item.title}&bg=1&type=live_yugao&imgPath=${imgPath}&share=1&title=会议详情`
});
}
</script>
<style scoped lang="scss">
$white: #fff;
$gray: #999;
$gray-dark: #666;
$gray-light: #eee;
$border-color: #e0e0e0;
$theme-color: #8b2316;
.page {
background-color: #f5f5f5;
height: 100vh;
display: flex;
flex-direction: column;
}
//
.search-header {
background-color: #eeeeee;
display: flex;
height:44px;
align-items: center;
padding:0 30rpx;
gap: 20rpx;
.search-input-wrapper {
flex: 1;
display: flex;
align-items: center;
background-color: #fff;
border-radius: 20rpx;
padding: 0 20rpx;
height:70rpx;
.search-icon {
margin-right: 15rpx;
}
.search-input {
flex: 1;
height: 80rpx;
font-size: 28rpx;
color: #333;
}
.clear-icon {
margin-left: 15rpx;
padding: 10rpx;
}
}
.cancel-btn {
padding: 10rpx 20rpx;
background:#8B2316;
border-radius: 10rpx;
.cancel-text {
font-size: 28rpx;
color: #fff;
}
}
}
//
.scroll-content {
position: fixed;
top: calc(var(--status-bar-height) + 44px);
left: 0;
right: 0;
bottom: 0;
background-color: $white;
}
//
.meeting-list {
padding: 0 30rpx;
}
.meeting-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.meeting-content {
.meeting-title {
font-size: 32rpx;
color: #333;
line-height: 1.4;
margin-bottom: 16rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.meeting-info {
display: flex;
justify-content: space-between;
gap: 8rpx;
.meeting-time,
.meeting-location {
.timebox{
margin-top: -4rpx;
margin-right: 8rpx;
}
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
}
}
}
//
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
.empty-icon {
margin-bottom: 20rpx;
}
.empty-text {
color: #999;
font-size: 28rpx;
}
}
//
.load-more {
padding: 30rpx 0;
display: flex;
justify-content: center;
.load-more-content {
display: flex;
align-items: center;
gap: 10rpx;
.load-more-text {
font-size: 24rpx;
color: #999;
}
}
}
//
.time-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
display: flex;
.popup-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.time-popup-content {
margin-top: 271rpx;
position: relative;
background-color: $white;
width: 100%;
height: calc(100vh - 275rpx);
.time-list {
padding: 0;
height: 100%;
overflow-y: auto;
.time-item {
padding: 25rpx 60rpx;
font-size: 32rpx;
color: #666;
border-bottom: 2rpx solid #f0f0f0;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
&:hover {
background-color: #f8f8f8;
}
&.active {
background-color: #f8f8f8;
color: $theme-color;
}
}
}
}
}
</style>

View File

@ -1,13 +1,15 @@
<template>
<view class="my-application-page">
<!-- 顶部导航栏 -->
<uni-nav-bar
<view class="navBox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="我的应用"
@clickLeft="goBack"
fixed
color="#8B2316"
height="180rpx"
:border="false"
backgroundColor="#eeeeee"
>
@ -17,6 +19,7 @@
}}</text>
</template>
</uni-nav-bar>
</view>
<scroll-view class="content" scroll-y>
<!-- 我的应用部分 -->
@ -102,6 +105,14 @@
//
const addUserIcon = () => {
if(myApps.value.length <3){
uni.showToast({
title: '至少添加3个应用',
icon: 'none',
duration: 1500
});
return false;
}
api.addUserIcon({
idStr:myApps.value.map(item => item.id).join(',')
}).then(res => {
@ -190,7 +201,7 @@
navTo({
url:url
})
}else if(app.name=="肝新闻"){
}else if(app.name=="肝新闻"){
url='/pages_app/news/news'
navTo({
url:url

View File

@ -1,15 +1,6 @@
<template>
<uni-nav-bar
left-icon="left"
title="肝胆新闻"
@clickLeft="goBack"
fixed
color="#8B2316"
height="180rpx"
:border="false"
backgroundColor="#eeeeee"
></uni-nav-bar>
<navBar title="肝胆新闻" />
<view class="news-page">
<!-- Fixed Banner Swiper -->
<view class="banner-container">
@ -143,7 +134,8 @@
import { onShow } from "@dcloudio/uni-app";
import api from '@/api/api.js';
import docUrl from "@/utils/docUrl.js";
import navTo from '../../utils/navTo';
import navTo from '../../utils/navTo';
import navBar from '@/components/navBar/navBar.vue';
onMounted(() => {
loadNewsBanner();
@ -270,7 +262,7 @@ import navTo from '../../utils/navTo';
/* Fixed Banner Container */
.banner-container {
position: fixed;
top: 180rpx; /* uni-nav-bar height */
top:calc(var(--status-bar-height) + 44px);
left: 0;
right: 0;
z-index: 50;

View File

@ -1,10 +1,9 @@
<template>
<uni-nav-bar left-icon="left"
@clickLeft="goBack" title="患教视频" fixed color="#8B2316" height="180rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
<navBar title="患教视频" />
<view class="video-page">
<!-- Header -->
<!-- Fixed Filter Tabs -->
<view class="filter-tabs-container">
<view class="filter-tabs">
@ -67,7 +66,7 @@
<text class="author">{{video.public_name}}</text>
<view class="stats">
<uni-icons type="eye" size="14" color="#999"></uni-icons>
<text class="view-count">{{video.readnum}}</text>
<text class="view-count">{{formatNumber(video.readnum)}}</text>
</view>
</view>
@ -153,6 +152,8 @@
import docUrl from '@/utils/docUrl';
import navTo from '@/utils/navTo';
import empty from '@/components/empty/empty.vue';
import formatNumber from '@/utils/formatNumber.js';
import navBar from '@/components/navBar/navBar.vue';
const isAllActive=ref(false)
//
const videoList = ref([]);
@ -515,9 +516,9 @@
.scroll-view {
position: fixed;
bottom: 0rpx;
height: calc(100vh - 180rpx); /* 固定高度,减去导航栏高度 */
height: calc(100vh - var(--status-bar-height) - 44px); /* 固定高度,减去导航栏高度 */
background-color: #f5f5f5;
top: 172rpx; /* 为导航栏留出空间 */
top: calc(var(--status-bar-height) + 65px); /* 为导航栏留出空间 */
}
@ -569,7 +570,7 @@
/* Fixed Filter Tabs Container */
.filter-tabs-container {
position: fixed;
top: 180rpx; /* uni-nav-bar height */
top: calc(var(--status-bar-height) + 44px); /* uni-nav-bar height */
left: 0;
right: 0;
z-index: 40;

View File

@ -1,16 +1,7 @@
<template>
<view class="person-page">
<!-- 顶部导航栏 -->
<uni-nav-bar
left-icon="left"
title="个人资料"
@clickLeft="goBack"
fixed
color="#8B2316"
height="180rpx"
:border="false"
backgroundColor="#eeeeee"
></uni-nav-bar>
<navBar title="个人资料" />
<scroll-view class="content" scroll-y>
<!-- 基本资料分组 -->
@ -267,6 +258,7 @@
import areaList from '@/utils/areaList.js';
import docUrl from '@/utils/docUrl';
import navTo from '@/utils/navTo';
import navBar from '@/components/navBar/navBar.vue';
import QfImageCropper from '@/components/qf-image-cropper/qf-image-cropper.vue';
const form = ref({
//
@ -777,7 +769,7 @@ const confirmSpecialties = () => {
}
.content {
position: fixed;
top: 180rpx;
top: calc(var(--status-bar-height) + 44px);
bottom: 0;
left: 0;
right: 0;

View File

@ -90,7 +90,7 @@
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.DES}}
{{ tag.NAME}}
</view>
</view>
@ -391,9 +391,9 @@
console.log('选中的筛选标签:', selectedTags);
for (var i = 0; i < selectedTags.length; i++) {
if(keywords.value){
keywords.value+=","+selectedTags[i].DES
keywords.value+=","+selectedTags[i].NAME
}else{
keywords.value=selectedTags[i].DES
keywords.value=selectedTags[i].NAME
}
}
isFilterActive.value =true;

View File

@ -217,7 +217,7 @@ const goBack = () => {
.download-bar {
position: fixed;
top: calc(var(--status-bar-height) + 44px);
top:220rpx;
z-index: 89;
width: 100%;
background-color: #6f6f6f; //

View File

@ -255,7 +255,7 @@
icon: 'none'
});
const app = getApp()
app.initNim({appkey: "885dea390870814acf3ba8558c717572", account:result.YX_accid, token: result.YX_token })
//app.initNim({appkey: "885dea390870814acf3ba8558c717572", account:result.YX_accid, token: result.YX_token })
console.log(3)
uni.redirectTo({
url:'/pages/index/index'
@ -330,7 +330,7 @@
uni.setStorageSync('userInfo', res.data);
}
const app = getApp()
app.initNim({ account:result.data.YX_accid, token: result.data.YX_token })
//app.initNim({ account:result.data.YX_accid, token: result.data.YX_token })
//
uni.redirectTo({
url: '/pages/index/index'

View File

@ -0,0 +1,635 @@
<template>
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar left-icon="left"
@clickLeft="goBack" :title="name" color="#8B2316" :border="false" backgroundColor="#eeeeee">
<template #right>
<view class="nav-right" @click="goSelect">
精选
</view>
</template>
</uni-nav-bar>
</view>
<view class="video-page">
<!-- Header -->
<!-- Main Content -->
<scroll-view
class="scroll-view"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
lower-threshold="100"
>
<!-- Video List -->
<view class="video-list">
<view
class="video-item"
v-for="(video, index) in videoList"
:key="video.id || video.uuid"
@click="playVideo(video)"
>
<view class="video-thumbnail">
<image :src="docUrl + video.imgpath" mode="aspectFill"></image>
</view>
<view class="video-info">
<view class="video-title">{{video.title || video.name}}</view>
<view class="video-meta">
<text class="author">{{video.public_name}}</text>
<view class="stats">
<uni-icons type="eye" size="14" color="#999"></uni-icons>
<text class="view-count">{{video.readnum}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- Loading More -->
<uni-load-more
v-if="videoList.length > 0"
:status="loadMoreStatus"
:content-text="{
contentdown: '上拉加载更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
}"
></uni-load-more>
<!-- Empty State -->
<view class="empty-state" v-if="videoList.length === 0 && !loading">
<empty></empty>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted, computed,reactive } from 'vue';
import { onShow,onLoad } from "@dcloudio/uni-app";
import api from '@/api/api.js';
import allImg from "@/static/video_all.png"
import allOnImg from "@/static/video_select.png"
import selectImg from "@/static/all_video.png"
import selectOnImg from "@/static/select_video.png"
import filter from "@/static/cb_screen_no.png"
import filterOn from "@/static/cb_screen_yes.png"
import upImg from "@/static/cb_up.png"
import downImg from "@/static/cb_down.png"
import docUrl from '@/utils/docUrl';
import navTo from '@/utils/navTo';
import empty from '@/components/empty/empty.vue';
const isAllActive=ref(false)
//
const videoList = ref([]);
const activeTab = ref(1); // ""
const loading = ref(false);
const refreshing = ref(false);
const loadMoreStatus = ref('more'); // more, loading, noMore
const currentPage = ref(1);
const pageSize = ref(10);
const hasMoreData = ref(true);
const filteredContent=ref([])
const sort =ref(2);
const keywords=ref('');
const typeUuid=ref('');
const typeName=ref('乙肝')
const isFilterActive=ref(false)
const showFilter = ref(false);
const yearList=ref([]);
const selectYearContent=reactive({});
const name=ref('');
//
const filterTags = ref([]);
onLoad((options)=>{
if(options.name){
name.value=decodeURIComponent(options.name);
}
getVideoType();
})
const goSelect=()=>{
navTo({
url: `/pages_app/replayText/replayText?name=${name.value}`
});
};
const getVideoType=async () => {
const response = await api.getTypeUuidByName({
name: name.value,
});
if(response.code === 200 && response.data){
typeUuid.value = response.data;
loadVideoData();
}
}
//
//
const loadVideoData = async (isRefresh = false) => {
if (loading.value && !isRefresh) return;
loading.value = true;
try {
const response = await api.videoByTypeNew({
page: currentPage.value,
pageSize: pageSize.value,
typeUuid:typeUuid.value,
});
console.log('视频列表API原始响应:', response);
//
let videoData = {};
if (response && response.data) {
if (response.data.code === 200) {
videoData = response.data.data || {};
} else if (response.code === 200) {
videoData = response.data || {};
}
} else if (response && response.code === 200) {
videoData = response.data || {};
}
console.log('解析后的视频数据:', videoData);
if (videoData && videoData.list) {
const { list, totalPage,pageNumber } = videoData;
console.log('视频列表数据:', list);
if (isRefresh) {
videoList.value = list || [];
currentPage.value = 1;
} else {
videoList.value = [...videoList.value, ...(list || [])];
}
hasMoreData.value = totalPage>pageNumber;
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
} else {
throw new Error(response?.message || '获取数据失败');
}
} catch (error) {
console.error('加载视频失败:', error);
} finally {
loading.value = false;
refreshing.value = false;
}
};
//
const onRefresh = () => {
refreshing.value = true;
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData(true);
};
//
const onLoadMore = () => {
if (!hasMoreData.value || loading.value) return;
loadMoreStatus.value = 'loading';
currentPage.value++;
loadVideoData();
};
//
const playVideo = (video) => {
console.log(video);
const videoId = video.id || video.uuid;
navTo({
url: `/pages_app/videoDetail/videoDetail?id=${videoId}`
});
};
//
const goBack = () => {
uni.navigateBack({
fail() {
uni.redirectTo({
url: '/pages/index/index'
});
}
});
}
</script>
<style scoped lang="scss">
$primary-color: #ff6b6b;
$theme-color: #8B2316;
$white: #fff;
$gray-bg: #f5f5f5;
$gray-light: #eee;
$gray-medium: #999;
$gray-dark: #666;
$text-color: #333;
//
$border-radius: 8px;
$border-radius-small: 6px;
$padding: 15px;
$padding-small: 10px;
.nav-right {
// margin-top:50rpx;
font-size: 32rpx;
color: #8b2316;
font-weight: 500;
}
.video-page {
background-color: #f5f5f5;
height: 100vh;
display: flex;
overflow: hidden;
flex-direction: column;
}
/* Header Styles */
.header {
background-color: #fff;
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
position: relative;
z-index: 100;
}
.header-left, .header-right {
display: flex;
align-items: center;
}
.header-title {
font-size: 36rpx;
font-weight: 600;
color: #8B4513;
}
/* Scroll View */
.scroll-view {
position: fixed;
bottom: 0rpx;
height:calc(100vh - var(--status-bar-height) - 44px);
background-color: #f5f5f5;
top: calc(var(--status-bar-height) + 44px); /* 为导航栏留出空间 */
}
/* Filter Tabs */
.filter-tabs {
background-color: #fff;
display: flex;
justify-content: space-between;
padding: 30rpx 30rpx;
}
.tab-item {
display: flex;
align-items: center;
margin-right: 60rpx;
padding: 10rpx 0;
}
.tab-item:last-child{
margin-right: 0;
}
.right{
display: flex;
}
.tab-item.active .tab-text {
color: #E74C3C;
font-weight: 600;
}
.tab-text {
margin: 0 8rpx;
font-size: 28rpx;
color: #666;
}
.tab-item:first-child .tab-text{
max-width:295rpx;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tab-item .tab-text.active {
color: #8B2316;
}
/* Fixed Filter Tabs Container */
.filter-tabs-container {
position: fixed;
top: 180rpx; /* uni-nav-bar height */
left: 0;
right: 0;
z-index: 40;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.doctor-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 20rpx;
overflow: hidden;
}
.doctor-avatar image {
width: 100%;
height: 100%;
}
/* Video List */
.video-list {
padding: 0 20rpx;
margin-top: 20rpx; /* 为固定的Filter Tabs留出空间 */
display: grid;
grid-template-columns: repeat(2, 1fr); /* 每行两个 */
gap: 16rpx; /* 卡片间距 */
}
.video-item {
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 0;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
width: 100%;
}
.video-thumbnail {
position: relative;
height: 200rpx;
}
.video-thumbnail image {
width: 100%;
height: 100%;
}
.video-duration {
margin-top: 8rpx;
}
.video-duration text {
font-size: 22rpx;
color: #999;
}
.video-info {
padding: 24rpx;
}
.video-title {
font-size: 28rpx;
font-weight: 400;
color: #333;
line-height: 1.4;
margin-bottom: 16rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
height: 2.8em; /* 固定2行高度1.4 * 2 = 2.8em */
}
.video-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.author {
font-size: 24rpx;
color: #999;
}
.stats {
display: flex;
align-items: center;
}
.view-count {
font-size: 24rpx;
color: #999;
margin-left: 8rpx;
}
/* Empty State */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-state image {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
opacity: 0.5;
}
.empty-state text {
color: #999;
font-size: 28rpx;
}
.newbox{
margin-top: -12rpx;
}
/* 全部视频弹窗样式 */
.all-video-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
display: flex;
align-items: flex-end;
}
.popup-content {
width: 100%;
background-color: #fff;
height:calc(100vh - 272rpx);
overflow-y: scroll;
}
.popup-header {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fff;
}
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.popup-body {
height:100%;
}
.category-item {
padding: 30rpx;
font-size: 28rpx;
color: #333;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fff;
transition: background-color 0.2s ease;
}
.category-item:last-child {
border-bottom: none;
}
.category-item.active {
background-color: #f8f8f8;
color: #8B2316;
}
.category-item:active {
background-color: #e8f4fd;
}
.empty-content {
display: flex;
justify-content: center;
align-items: center;
height: 200rpx;
color: #999;
font-size: 28rpx;
}
//
.filter-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
// background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
z-index: 9999;
.filter-content {
margin-top:272rpx;
overflow-y:scroll;
background-color: $white;
// border-radius: 20rpx 20rpx 0 0;
padding: 20rpx 30rpx 60rpx;
width: 100%;
height: calc(100vh - 272rpx);
.filter-tags {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
margin-bottom: 60rpx;
max-height: 65vh;
overflow-y: auto;
.tag-item {
background-color: #f8f8f8;
color: $gray-dark;
padding: 8rpx 0; /* 去掉左右padding仅保留上下 */
border-radius: 30rpx;
font-size: 26rpx;
border: 2rpx solid #efefef;
transition: all 0.3s ease;
width: 152rpx; /* 固定宽度 */
text-align: center; /* 文本居中 */
white-space: nowrap; /* 单行显示 */
overflow: hidden; /* 隐藏溢出 */
text-overflow: ellipsis; /* 超出显示省略号 */
&.active {
background-color: #fff;
color: $theme-color;
border-color: $theme-color;
}
}
}
.filter-buttons {
display: flex;
gap: 20rpx;
position: fixed;
bottom: 30rpx;
left: 30rpx;
right: 30rpx;
background-color: #fff; /* 背景色为白色 */
.btn-reset,
.btn-confirm {
flex: 1;
height: 70rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 500;
}
.btn-reset {
background-color: $white;
color: $theme-color;
border: 2rpx solid $theme-color;
}
.btn-confirm {
border: 2rpx solid $theme-color;
background-color: $theme-color;
color: $white;
}
}
}
}
</style>

View File

@ -0,0 +1,272 @@
<template>
<view class="meeting-page">
<!-- 导航栏 -->
<navBar :title="name" />
<!-- 主内容区域 -->
<scroll-view
class="content-scroll"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
lower-threshold="100"
>
<view class="meeting-list">
<view
class="meeting-item"
v-for="(meeting, index) in meetingList"
:key="meeting.id"
@click="playVideo(meeting)"
>
<view class="meeting-content">
{{ meeting.name }}
</view>
<view class="meeting-arrow">
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
</view>
<!-- 加载更多状态 -->
<uni-load-more
v-if="meetingList.length > 0"
:status="loadMoreStatus"
:content-text="{
contentdown: '上拉加载更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
}"
></uni-load-more>
<!-- 空状态 -->
<view class="empty-state" v-if="meetingList.length === 0 && !loading">
<view class="empty-content">
<text class="empty-text">暂无数据</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { onShow,onLoad } from "@dcloudio/uni-app";
import navBar from '@/components/navBar/navBar.vue';
import api from '@/api/api.js';
import navTo from '@/utils/navTo';
//
const typeUuid=ref('');
const typeName=ref('');
const currentPage=ref(1);
const pageSize=ref(10);
const hasMoreData=ref(true);
const loadMoreStatus=ref('more');
const loading=ref(false);
const refreshing=ref(false);
const meetingList=ref([]);
//
onMounted(() => {
//
});
const name=ref('');
onLoad((options)=>{
if(options.name){
name.value=decodeURIComponent(options.name);
}
getVideoType();
});
const getVideoType=async () => {
const response = await api.getTypeUuidByName({
name: name.value,
});
if(response.code === 200 && response.data){
typeUuid.value = response.data;
loadVideoData();
}
}
//
const goBack = () => {
uni.navigateBack({
fail() {
uni.redirectTo({
url: '/pages/index/index'
});
}
});
};
const loadVideoData = async (isRefresh = false) => {
if (loading.value && !isRefresh) return;
loading.value = true;
try {
const response = await api.videoByTypeNew({
page: currentPage.value,
pageSize: pageSize.value,
typeUuid:typeUuid.value,
});
console.log('视频列表API原始响应:', response);
//
let videoData = {};
if (response && response.data) {
if (response.data.code === 200) {
videoData = response.data.data || {};
} else if (response.code === 200) {
videoData = response.data || {};
}
} else if (response && response.code === 200) {
videoData = response.data || {};
}
console.log('解析后的视频数据:', videoData);
if (videoData && videoData.list) {
const { list, totalPage,pageNumber } = videoData;
console.log('视频列表数据:', list);
if (isRefresh) {
meetingList.value = list || [];
currentPage.value = 1;
} else {
meetingList.value = [...meetingList.value, ...(list || [])];
}
hasMoreData.value = totalPage>pageNumber;
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
} else {
throw new Error(response?.message || '获取数据失败');
}
} catch (error) {
console.error('加载视频失败:', error);
} finally {
loading.value = false;
refreshing.value = false;
}
};
//
const onRefresh = () => {
refreshing.value = true;
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData(true);
};
//
const onLoadMore = () => {
if (!hasMoreData.value || loading.value) return;
loadMoreStatus.value = 'loading';
currentPage.value++;
loadVideoData();
};
//
//
const playVideo = (video) => {
const videoId = video.id || video.uuid;
navTo({
url: `/pages_app/videoDetail/videoDetail?id=${videoId}`
});
};
</script>
<style scoped lang="scss">
$theme-color: #8B2316;
$text-color: #333;
$text-gray: #999;
$bg-color: #f5f5f5;
$white: #fff;
.meeting-page {
background-color: $bg-color;
height: 100vh;
display: flex;
flex-direction: column;
}
/* 内容滚动区域 */
.content-scroll {
flex: 1;
height: calc(100vh - var(--status-bar-height) - 44px);
background-color: $white;
margin-top: calc(var(--status-bar-height) + 44px);
}
/* 会议列表 */
.meeting-list {
background-color: $white;
}
/* 会议项 */
.meeting-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: $white;
transition: background-color 0.2s ease;
}
.meeting-item:active {
background-color: #f8f8f8;
}
/* 会议内容 */
.meeting-content {
flex: 1;
margin-right: 20rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 30rpx;
color: $text-color;
}
.meeting-title {
font-size: 30rpx;
color: $text-color;
line-height: 1.4;
}
/* 箭头图标 */
.meeting-arrow {
display: flex;
align-items: center;
justify-content: center;
width: 40rpx;
height: 40rpx;
}
/* 空状态样式 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.empty-text {
color: #999;
font-size: 28rpx;
}
</style>

View File

@ -0,0 +1,350 @@
<template>
<view class="scan-login-container">
<navBar title="扫码登录" />
<!-- 主要内容区域 -->
<view class="main-content">
<!-- 连接图标区域 -->
<view class="connection-area">
<up-image :src="loginImg" width="420rpx" height="187rpx"></up-image>
</view>
<!-- 提示文字 -->
<view class="tips-text">
<text class="main-tip">确认电脑登录</text>
<text class="sub-tip">为确保账号安全,请确认是您本人操作</text>
</view>
<!-- 按钮区域 -->
<view class="button-area">
<button class="confirm-btn" @click="toggleLogin(true)">确认登录</button>
<button class="cancel-btn" @click="toggleLogin(false)">取消登录</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { onShow, onLoad } from "@dcloudio/uni-app";
import navBar from '@/components/navBar/navBar.vue';
import api from '@/api/api.js';
import loginImg from '@/static/pcquer.png';
import navTo from '@/utils/navTo';
import { encryptAesEcb } from '@/utils/aesEcb.js';
import OTHER_HOST from '@/utils/otherHost.js';
//
const loading = ref(false);
const code = ref('');
//
onLoad((options) => {
console.log('扫码登录页面加载完成', options);
if (options.code) {
code.value = options.code;
}
});
const toggleLogin = (status) => {
let userInfo=uni.getStorageSync('userInfo');
let str='';
if(status){
str='watchliveTo}'+code.value+'}'+userInfo.uuid+','+userInfo.realName+','+userInfo.photo;
}else{
str='watchliveCancel}'+code.value+'}'+userInfo.uuid+','+userInfo.realName+','+userInfo.photo;
}
let message=encryptAesEcb(str,'deoep09_klodLdAo');
uni.request({
url: OTHER_HOST+'/watchlive/sendWebsocketMsg',
method: 'POST',
data: {
message: message,
},
header: {
'Content-Type':'application/x-www-form-urlencoded'//
},
success: (res) => {
console.log(res);
if(res.data.code==1){
uni.navigateBack();
if(status){
uni.showToast({
title: '操作成功',
icon: 'none'
});
}
}else{
uni.showToast({
title: res.data.message,
icon: 'none'
});
}
},
});
}
//
const confirmLogin = async () => {
if (loading.value) return;
try {
loading.value = true;
// API
const result = await api.confirmScanLogin({
qrCode: loginData.qrCode,
sessionId: loginData.sessionId
});
if (result.code === 200) {
uni.showToast({
title: '登录成功',
icon: 'success'
});
//
setTimeout(() => {
uni.switchTab({
url: '/pages/index/index'
});
}, 1500);
} else {
uni.showToast({
title: result.message || '登录失败',
icon: 'none'
});
}
} catch (error) {
console.error('确认登录失败:', error);
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
});
} finally {
loading.value = false;
}
};
//
const cancelLogin = () => {
uni.showModal({
title: '提示',
content: '确定要取消登录吗?',
success: (res) => {
if (res.confirm) {
uni.navigateBack({
fail() {
uni.switchTab({
url: '/pages/index/index'
});
}
});
}
}
});
};
</script>
<style scoped>
.scan-login-container {
min-height: 100vh;
background-color: #ffffff;
}
.main-content {
padding: 80rpx 60rpx;
display: flex;
flex-direction: column;
align-items: center;
}
/* 连接图标区域 */
.connection-area {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 80rpx;
position: relative;
}
/* 手机图标 */
.phone-icon {
width: 120rpx;
height: 200rpx;
background-color: #f5f5f5;
border-radius: 20rpx;
position: relative;
border: 2rpx solid #e0e0e0;
}
.phone-screen {
width: 100rpx;
height: 140rpx;
background-color: #ffffff;
border-radius: 10rpx;
position: absolute;
top: 20rpx;
left: 10rpx;
}
.phone-home-button {
width: 20rpx;
height: 20rpx;
background-color: #d0d0d0;
border-radius: 50%;
position: absolute;
bottom: 20rpx;
left: 50rpx;
}
/* 连接信号 */
.connection-signals {
margin: 0 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.signal-line {
width: 4rpx;
background-color: #d0d0d0;
margin: 4rpx 0;
border-radius: 2rpx;
}
.signal-1 {
height: 20rpx;
}
.signal-2 {
height: 30rpx;
}
.signal-3 {
height: 40rpx;
}
/* PC图标 */
.pc-icon {
width: 140rpx;
height: 100rpx;
position: relative;
}
.pc-screen {
width: 140rpx;
height: 80rpx;
background-color: #f5f5f5;
border: 2rpx solid #e0e0e0;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.pc-text {
font-size: 24rpx;
color: #666666;
font-weight: bold;
}
.pc-stand {
width: 60rpx;
height: 20rpx;
background-color: #e0e0e0;
border-radius: 0 0 8rpx 8rpx;
position: absolute;
bottom: -20rpx;
left: 50%;
transform: translateX(-50%);
}
/* 提示文字 */
.tips-text {
text-align: center;
margin-bottom: 100rpx;
}
.main-tip {
display: block;
font-size: 32rpx;
color: #333333;
font-weight: 500;
margin-bottom: 20rpx;
}
.sub-tip {
display: block;
font-size: 28rpx;
color: #666666;
line-height: 1.5;
}
/* 按钮区域 */
.button-area {
width: 100%;
display: flex;
flex-direction: column;
gap: 30rpx;
}
.confirm-btn {
width: 100%;
height: 88rpx;
background-color: #8B2316;
color: #ffffff;
border: none;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.confirm-btn:active {
background-color: #6B1A0F;
transform: scale(0.98);
}
.cancel-btn {
width: 100%;
height: 88rpx;
background-color: #ffffff;
color: #666666;
border: 2rpx solid #e0e0e0;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.cancel-btn:active {
background-color: #f5f5f5;
transform: scale(0.98);
}
/* 加载状态 */
.confirm-btn[disabled] {
background-color: #cccccc;
color: #999999;
}
/* 响应式适配 */
@media screen and (max-width: 750rpx) {
.main-content {
padding: 60rpx 40rpx;
}
.connection-area {
margin-bottom: 60rpx;
}
.tips-text {
margin-bottom: 80rpx;
}
}
</style>

View File

@ -222,7 +222,7 @@
background-color: #eeeeee;
display: flex;
height:140rpx;
height:180rpx;
align-items: center;
padding:0 30rpx;
gap: 20rpx;

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
<template>
<uni-nav-bar left-icon="left"
@clickLeft="goBack" title="肝胆视频" fixed color="#8B2316" height="180rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
<navBar title="肝胆视频" />
<view class="video-page">
<!-- Header -->
@ -64,6 +63,7 @@
<up-image :src="sort==2?upImg:downImg" width="20rpx" height="26rpx" ></up-image>
</view>
</view>
<view class="bar"></view>
<view
class="tab-item"
@click="showFilterPopup"
@ -105,7 +105,7 @@
<text class="author">{{video.public_name}}</text>
<view class="stats">
<uni-icons type="eye" size="14" color="#999"></uni-icons>
<text class="view-count">{{video.readnum}}</text>
<text class="view-count">{{formatNumber(video.readnum)}}</text>
</view>
</view>
@ -147,7 +147,7 @@
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.DES }}
{{ tag.NAME }}
</view>
</view>
@ -219,6 +219,8 @@
import videoImg from "@/static/patient_video.png"
import docUrl from '@/utils/docUrl';
import navTo from '@/utils/navTo';
import formatNumber from '@/utils/formatNumber.js';
import navBar from '@/components/navBar/navBar.vue';
const isAllActive=ref(false)
//
const videoList = ref([]);
@ -302,7 +304,7 @@
} catch (error) {
console.error('加载轮播数据失败:', error);
// 使
bannerList.value = getMockBannerData();
}
};
@ -333,7 +335,8 @@
console.log('类型API原始响应:', response);
if(response.code==200){
yearList.value=response.data;
filteredContent.value=(response.data[0]).list;
let all=[{uuid:'',name:'全部视频'}];
filteredContent.value=all.concat((response.data[0]).list);
console.log(33)
let arr=response.data.map(item=>{
return {
@ -612,33 +615,7 @@
}
};
// Banner
const getMockBannerData = () => {
return [
{
id: 'banner_1',
title: '《2025年版慢加急性肝衰竭指南》解读',
subtitle: '专业医学视频教育平台',
thumbnail: '/static/big_background_my.png',
doctorAvatar: '/static/doctor-avatar-1.png'
},
{
id: 'banner_2',
title: '自身免疫性肝病专栏|免疫治疗的双刃剑',
subtitle: '专业医学视频教育平台',
thumbnail: '/static/big_background_my.png',
doctorAvatar: '/static/doctor-avatar-2.png'
},
{
id: 'banner_3',
title: '徐医感染:硬化出血发热路,关关难过关关过',
subtitle: '专业医学视频教育平台',
thumbnail: '/static/big_background_my.png',
doctorAvatar: '/static/doctor-avatar-3.png'
}
];
};
const onSwiperChange = (e) => {
currentBannerIndex.value = e.detail.current;
};
@ -663,19 +640,18 @@
selectedCategory.value = categoryValue;
console.log(yearList.value)
console.log('选择分类:', categoryValue);
let all=[{uuid:'',name:'全部视频'}];
for (var i = 0; i < yearList.value.length; i++) {
if(categoryValue==yearList.value[i].name){
console.log(yearList.value[i]);
filteredContent.value=yearList.value[i].list;
filteredContent.value=categoryValue=='全部'?all.concat(yearList.value[i].list):yearList.value[i].list;
console.log(filteredContent.value)
break;
}
}
};
const selectContent = (content) => {
//
console.log('选中内容:', content);
@ -684,10 +660,10 @@
typeUuid.value=content.uuid;
typeName.value=content.name;
isAllActive.value=true;
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData(true);
};
//
const showFilterPopup = () => {
showFilter.value = true;
@ -720,9 +696,9 @@
console.log('选中的筛选标签:', selectedTags);
for (var i = 0; i < selectedTags.length; i++) {
if(keywords.value){
keywords.value+=","+selectedTags[i].DES
keywords.value+=","+selectedTags[i].NAME
}else{
keywords.value=selectedTags[i].DES
keywords.value=selectedTags[i].NAME
}
@ -849,10 +825,10 @@
.scroll-view {
position: fixed;
bottom:0rpx;
height: calc(100vh - 658rpx); /* 固定高度减去固定的banner和Filter Tabs空间 */
height: calc(100vh - var(--status-bar-height) - 54px - 400rpx); /* 固定高度减去固定的banner和Filter Tabs空间 */
background-color: #f5f5f5;
margin-bottom: 200rpx;
top: 614rpx; /* 为固定的banner和Filter Tabs留出空间 */
top: calc(var(--status-bar-height) + 54px + 400rpx); /* 为固定的banner和Filter Tabs留出空间 */
}
/* Banner Styles */
@ -861,7 +837,12 @@
height: 400rpx;
margin-bottom: 20rpx;
}
.bar{
width:2rpx;
margin:0 26rpx 0 30rpx;
height: 32rpx;
background-color: #666;
}
.banner-image {
width: 100%;
height: 100%;
@ -894,7 +875,7 @@
.tab-item {
display: flex;
align-items: center;
margin-right: 60rpx;
padding: 10rpx 0;
}
@ -903,6 +884,8 @@
}
.right{
display: flex;
align-items: center;
justify-content: space-between;
}
.tab-item.active .tab-text {
@ -929,7 +912,7 @@
/* Fixed Banner Container */
.banner-container {
position: fixed;
top: 180rpx; /* uni-nav-bar height */
top: calc(var(--status-bar-height) + 44px); /* uni-nav-bar height */
left: 0;
right: 0;
z-index: 50;
@ -967,7 +950,7 @@
/* Fixed Filter Tabs Container */
.filter-tabs-container {
position: fixed;
top: 580rpx; /* banner-container top + banner height */
top: calc(var(--status-bar-height) + 44px + 400rpx); /* banner-container top + banner height */
left: 0;
right: 0;
z-index: 40;
@ -1154,7 +1137,7 @@
.popup-content {
width: 100%;
margin-top: 650rpx;
margin-top: calc(var(--status-bar-height) + 44px + 400rpx + 117rpx);
height: 80vh;
background-color: #fff;
@ -1276,7 +1259,7 @@
z-index: 9999;
.filter-content {
margin-top:660rpx;
margin-top:calc(var(--status-bar-height) + 44px + 400rpx + 117rpx);
overflow-y:scroll;
background-color: $white;
// border-radius: 20rpx 20rpx 0 0;

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,142 @@
<template>
<view class="container">
<navBar :title="title" :showLeft="true"></navBar>
<view class="container" :style="{ backgroundColor: backgroundColor }">
<!-- 分享弹窗 -->
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
:title="title"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eeeeee"
>
<template #right>
<view class="nav-actions">
<view class="collect-img" @click="shareToggle" v-if="canShare">
<image class="share-img-icon" :src="shareIcon" mode="aspectFill" />
</view>
</view>
</template>
</uni-nav-bar>
</view>
</view>
<uni-popup ref="shareRef" type="bottom" safeArea backgroundColor="#fff">
<view class="share-popup">
<view class="share-title">分享到</view>
<view class="share-content">
<view class="share-item" @click="shareToWechat">
<view class="share-icon wechat-icon">
<image class="share-img" :src="wxImg" mode="aspectFill" />
</view>
<text class="share-text">微信</text>
</view>
<view class="share-item" @click="shareToMoments">
<view class="share-icon moments-icon">
<image class="share-img" :src="friendImg" mode="aspectFill" />
</view>
<text class="share-text">朋友圈</text>
</view>
<view class="share-item" @click="shareToWeibo">
<view class="share-icon weibo-icon">
<image class="share-img" :src="sinaImg" mode="aspectFill" />
</view>
<text class="share-text">新浪微博</text>
</view>
</view>
<view class="share-cancel" @click="closeShare">
<text>取消</text>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { onLoad,onHide,onShow,onUnload} from "@dcloudio/uni-app";
import navBar from "@/components/navBar/navBar.vue";
import shareIcon from "@/static/icon_share.png";
import sinaImg from "@/static/share_sina.png";
import wxImg from "@/static/share_weixin.png";
import friendImg from "@/static/share_wxc.png";
import logoImg from "@/static/weiboShare.png";
const title = ref("新闻详情");
const safeUrl = ref("");
const backgroundColor = ref("");
const hasBg = ref(false);
const canShare = ref(false);
const summary = ref("");
const shareLink = ref("");
const shareImg=ref("");
const shareTitle = ref("");
const goBack = () => {
uni.navigateBack({
fail() {
uni.redirectTo({
url: '/pages/index/index'
});
}
});
};
onShow(() => {
uni.hideLoading();
// console.log('show');
// plus.screen.lockOrientation('portrait-primary');
});
onUnload(() => {
// console.log('onUnload');
// plus.screen.lockOrientation('portrait-primary');
});
onHide(() => {
// console.log('onHide');
// plus.screen.lockOrientation('landscape-primary');
});
onLoad((query) => {
// url
if(query.title){
title.value = query.title;
}
if(query.share == 1){
canShare.value = true;
}
if(query.bg == 1){
hasBg.value = true;
backgroundColor.value = "linear-gradient(to bottom,'#fff' , '#398775')";
}
var height = 0; //
uni.getSystemInfo({
success: (sysinfo) => {
height = sysinfo.windowHeight; //
},
complete: () => {}
});
const raw = query && (query.url || "");
try {
safeUrl.value = decodeURIComponent(raw);
title.value = query.title;
shareImg.value = decodeURIComponent(query.imgPath);
if(query.type == "live_yugao"){
shareTitle.value = query.sharetitle;
summary.value = "分享一篇来自“肝胆相照”的会议预告:"+shareTitle.value;
}else if(query.type == "live_old"){
shareTitle.value = query.sharetitle; shareTitle.value = query.sharetitle;
summary.value = "分享一篇来自“肝胆相照”的历史会议:"+shareTitle.value;
}else if(query.type == "live"){
shareTitle.value = '肝胆相照直播:'+query.shareTitle;
summary.value = shareTitle.value;
}
// #ifdef APP-PLUS
let wv = plus.webview.create("", "custom-webview", {
top: "140rpx",
bottom:"20rpx",
bottom:"0rpx",
});
wv.loadURL( safeUrl.value);
@ -32,30 +148,720 @@ onLoad((query) => {
currentWebview.append(wv);
setTimeout(function () {
wv = currentWebview.children()[0];
uni.getSystemInfo({
//
success: (sysinfo) => {
let height = sysinfo.windowHeight; // ---(-50)
wv.setStyle({ top: 70,height: height- 70});
},
complete: () => {}
});
wv.setStyle({ top: 80,height: height- 80,bottom:0,scalable:true, videoFullscreen: 'landscape',zIndex:-1});
}, 300); //
wv.onloaded = (e) => {
wv.show();
};
uni.onWindowResize((res) => {
if (res.size.windowHeight < height) {
console.log(res.size.windowHeight);
console.log(height)
console.log('键盘高度',res.height);
setTimeout(function() {
wv.setStyle({ //web-viewpx
top:80,
videoFullscreen: 'landscape',
height: height-res.size.windowHeight+100,
scalable: true, //webview
})
},0);
//
//
} else {
setTimeout(function() {
wv.setStyle({ //web-viewpx
top:80,
height:height-80,
videoFullscreen: 'landscape',
scalable: true, //webview
})
},0);
}
})
//
createNativePopup();
// #endif
} catch (e) {
safeUrl.value = raw;
}
});
const shareRef = ref(null);
shareRef.value?.open();
// APP
const shareToggle = () => {
showNativePopup();
// console.log(shareRef.value);
// shareRef.value?.open();
// uni.showActionSheet({
// title:"",
// itemList: ['', '', ''],
// success: function (res) {
// if(res.tapIndex==0){
// shareToWechat()
// }else if(res.tapIndex==1){
// shareToMoments()
// }else{
// shareToWeibo()
// }
// },
// })
};
//
const closeShare = () => {
shareRef.value.close();
};
// ========= plus.nativeObj.view============
const nativeMaskView = ref(null);
const nativePopupView = ref(null);
const popupShowing = ref(false);
// 使
const popupInfo = ref({
screenW: 0,
screenH: 0,
panelH: 0,
cancelH: 0,
contentTop: 0,
iconSize: 0,
textSize: 0
});
function createNativePopup() {
// #ifdef APP-PLUS
if (nativeMaskView.value || nativePopupView.value) return;
const screenW = plus.screen.resolutionWidth;
const screenH = plus.screen.resolutionHeight;
const panelH = uni.upx2px(520); // ++
const radius = uni.upx2px(20);
//
const iconSize = 50; // 80px
const titleH = uni.upx2px(80);
const cancelH = uni.upx2px(100);
const contentTop = uni.upx2px(90) + titleH;
const textSize = uni.upx2px(24);
popupInfo.value = {
screenW,
screenH,
panelH,
cancelH,
contentTop,
iconSize,
textSize
};
//
nativeMaskView.value = new plus.nativeObj.View('native-share-mask', {
left: '0px',
top: '0px',
width: screenW + 'px',
height: screenH + 'px',
zindex: 99998,
touchable: true,
interceptTouchEvent: true
}, [
{ tag: 'rect', id: 'mask', position: { left: '0px', top: '0px', width: screenW + 'px', height: screenH + 'px' }, color: 'rgba(0,0,0,0.5)' }
]);
//
nativePopupView.value = new plus.nativeObj.View('native-share-panel', {
left: '0px',
top: (screenH - panelH) + 'px',
width: screenW + 'px',
height: panelH + 'px',
zindex: 99999,
touchable: true,
interceptTouchEvent: true
}, []);
// +
nativePopupView.value.drawRect({
color: '#FFFFFF',
radius: radius,
rectStyles: {}
}, {
left: uni.upx2px(0) + 'px',
top: uni.upx2px(0) + 'px',
width: screenW + 'px',
height: panelH + 'px'
});
//
nativePopupView.value.drawText('分享到', {
left: '0px',
top: uni.upx2px(20) + 'px',
width: screenW + 'px',
height: titleH + 'px'
}, {
size: uni.upx2px(32) + 'px',
color: '#333333',
align: 'center',
verticalAlign: 'middle',
weight: '500'
});
// 使
const itemW = screenW / 3;
const iconTop = contentTop;
const textTop = iconTop + iconSize + uni.upx2px(16);
console.log('图标布局信息:', {
screenW,
itemW,
iconSize,
iconTop,
contentTop
});
// 使
const iconFiles = [
'share_weixin.png', //
'share_wxc.png', //
'share_sina.png' //
];
const items = [
{ id: 'it-wechat', text: '微信' },
{ id: 'it-moments', text: '朋友圈' },
{ id: 'it-weibo', text: '微博' }
];
items.forEach((it, idx) => {
const left = idx * itemW;
//
const iconLeft = Math.round(left + (itemW - iconSize) / 2);
const iconLeftPx = iconLeft;
// 使 top
const iconTopPx = Math.round(iconTop);
const iconSizePx = iconSize;
console.log(`图标 ${idx} (${it.text}): left=${left}, itemW=${itemW.toFixed(2)}, iconLeft=${iconLeftPx}, iconTop=${iconTopPx}, iconSize=${iconSizePx}`);
//
let iconPath = null;
try {
// 使
const iconFile = iconFiles[idx];
iconPath = plus.io.convertLocalFileSystemURL('_www/static/' + iconFile);
console.log(`图标 ${it.text} 路径: ${iconPath}`);
} catch (e) {
console.log('构建图标路径失败:', e);
iconPath = null;
}
//
if (iconPath) {
try {
console.log(`绘制图标 ${it.text}: 位置=(${iconLeftPx}, ${iconTopPx}), 大小=${iconSizePx}x${iconSizePx}`);
// 使 drawBitmap
//
// 1: drawBitmap(src, position, options)
try {
nativePopupView.value.drawBitmap(iconPath, {}, {
left: iconLeftPx + 'px',
top: iconTopPx + 'px',
width: iconSizePx + 'px',
height: iconSizePx + 'px'
});
} catch (e1) {
// 12: drawBitmap(src, position)
try {
nativePopupView.value.drawBitmap(iconPath, {
left: iconLeftPx + 'px',
top: iconTopPx + 'px',
width: iconSizePx + 'px',
height: iconSizePx + 'px'
});
} catch (e2) {
console.log('drawBitmap 两种格式都失败:', e1, e2);
throw e2;
}
}
} catch (e) {
console.log('绘制图标失败,使用圆形色块代替:', e);
// 使
const bgColor = idx === 2 ? '#E6162D' : '#07C160';
nativePopupView.value.drawRect({
color: bgColor,
radius: Math.round(iconSize / 2)
}, {
left: Math.round(iconLeft) + 'px',
top: Math.round(iconTop) + 'px',
width: Math.round(iconSize) + 'px',
height: Math.round(iconSize) + 'px'
});
}
} else {
// 使
const bgColor = idx === 2 ? '#E6162D' : '#07C160';
nativePopupView.value.drawRect({
color: bgColor,
radius: Math.round(iconSize / 2)
}, {
left: Math.round(iconLeft) + 'px',
top: Math.round(iconTop) + 'px',
width: Math.round(iconSize) + 'px',
height: Math.round(iconSize) + 'px'
});
}
//
nativePopupView.value.drawText(it.text, {
left: left + 'px',
top: textTop + 'px',
width: itemW + 'px',
height: textSize + uni.upx2px(20) + 'px'
}, {
size: textSize + 'px',
color: '#666666',
align: 'center',
verticalAlign: 'top'
});
});
// 线
const cancelTop = panelH - cancelH;
nativePopupView.value.drawRect({ color: '#F0F0F0' }, {
left: '0px',
top: (cancelTop - 1) + 'px',
width: screenW + 'px',
height: '1px'
});
nativePopupView.value.drawText('取消', {
left: '0px',
top: cancelTop + 'px',
width: screenW + 'px',
height: cancelH + 'px'
}, {
size: uni.upx2px(32) + 'px',
color: '#333333',
align: 'center',
verticalAlign: 'middle'
});
//
const handleMaskClick = () => {
console.log('遮罩被点击');
closeNativePopup();
};
const handlePanelClick = (e) => {
console.log('面板被点击', e);
const info = popupInfo.value;
//
let x = 0;
let y = 0;
//
if (e.clientX !== undefined && e.clientY !== undefined) {
x = e.clientX;
y = e.clientY;
} else if (e.pageX !== undefined && e.pageY !== undefined) {
x = e.pageX;
y = e.pageY;
} else if (e.touches && e.touches.length > 0) {
x = e.touches[0].clientX || e.touches[0].pageX || 0;
y = e.touches[0].clientY || e.touches[0].pageY || 0;
} else if (e.changedTouches && e.changedTouches.length > 0) {
x = e.changedTouches[0].clientX || e.changedTouches[0].pageX || 0;
y = e.changedTouches[0].clientY || e.changedTouches[0].pageY || 0;
}
console.log('原始坐标:', x, y);
console.log('屏幕信息:', info);
if (!x || !y) {
console.log('坐标无效');
return;
}
//
// y panelH
// y screenH - panelH
let relativeY = y;
let relativeX = x;
if (y < info.panelH) {
// 使
relativeY = y;
relativeX = x;
console.log('使用相对于视图的坐标:', relativeX, relativeY);
} else if (y >= (info.screenH - info.panelH)) {
//
relativeY = y - (info.screenH - info.panelH);
relativeX = x;
console.log('使用相对于屏幕的坐标,转换为相对视图:', relativeX, relativeY);
} else {
// 使
console.log('坐标类型不确定,尝试直接使用');
}
// -
const cancelTop = info.panelH - info.cancelH;
console.log('取消区域判断: relativeY =', relativeY, ', cancelTop =', cancelTop);
if (relativeY >= cancelTop) {
console.log('点击了取消按钮');
closeNativePopup();
return;
}
// +-
const shareAreaTop = info.contentTop;
const shareAreaBottom = shareAreaTop + info.iconSize + info.textSize + uni.upx2px(20);
console.log('分享区域: relativeY =', relativeY, ', 范围 =', shareAreaTop, '到', shareAreaBottom);
if (relativeY >= shareAreaTop && relativeY <= shareAreaBottom) {
// 0:, 1:, 2:
const itemWidth = info.screenW / 3;
const idx = Math.min(2, Math.max(0, Math.floor(relativeX / itemWidth)));
console.log('点击了分享项:', idx, ', x =', relativeX, ', itemWidth =', itemWidth);
if (idx === 0) {
console.log('执行微信分享');
shareToWechat();
closeNativePopup();
} else if (idx === 1) {
console.log('执行朋友圈分享');
shareToMoments();
closeNativePopup();
} else if (idx === 2) {
console.log('执行微博分享');
shareToWeibo();
closeNativePopup();
}
} else {
console.log('点击位置不在分享区域内, relativeY =', relativeY, ', 需要范围:', shareAreaTop, '到', shareAreaBottom);
}
};
// click touchstart
nativeMaskView.value.addEventListener('click', handleMaskClick, false);
nativeMaskView.value.addEventListener('touchstart', handleMaskClick, false);
nativePopupView.value.addEventListener('click', handlePanelClick, false);
nativePopupView.value.addEventListener('touchstart', handlePanelClick, false);
nativePopupView.value.addEventListener('touchend', handlePanelClick, false);
// #endif
}
function showNativePopup() {
// #ifdef APP-PLUS
if (popupShowing.value) return;
if (!nativeMaskView.value || !nativePopupView.value) {
createNativePopup();
}
nativeMaskView.value && nativeMaskView.value.show();
nativePopupView.value && nativePopupView.value.show();
popupShowing.value = true;
// #endif
}
function closeNativePopup() {
// #ifdef APP-PLUS
if (!popupShowing.value) return;
nativeMaskView.value && nativeMaskView.value.hide();
nativePopupView.value && nativePopupView.value.hide();
popupShowing.value = false;
// #endif
}
//
const shareToWechat = () => {
// #ifdef APP-PLUS
// 使
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 0,
title: shareTitle.value,
summary:summary.value,
href: safeUrl.value,
imageUrl: shareImg.value,
success: function (res) {
console.log("success:" + JSON.stringify(res));
},
fail: function (err) {
console.log("fail:" + JSON.stringify(err));
},
});
// #endif
// #ifdef H5
// H5
if (navigator.share) {
navigator
.share({
title: "肝胆相照APP",
text: "专业的医疗健康平台,快来下载体验吧!",
url: shareLink.value,
})
.then(() => {
uni.showToast({
title: "分享成功",
icon: "success",
});
})
.catch(() => {
uni.showToast({
title: "分享失败",
icon: "none",
});
});
} else {
//
uni.setClipboardData({
data:
"肝胆相照APP - 专业的医疗健康平台,快来下载体验吧!\n下载链接" +
shareLink.value,
success: () => {
uni.showToast({
title: "已复制到剪贴板",
icon: "success",
});
},
});
}
// #endif
// #ifdef MP-WEIXIN
//
uni.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"],
});
// #endif
closeShare();
};
// /
onHide(() => {
// #ifdef APP-PLUS
closeNativePopup();
// #endif
});
onUnload(() => {
// #ifdef APP-PLUS
try{
nativeMaskView.value && nativeMaskView.value.close && nativeMaskView.value.close();
nativePopupView.value && nativePopupView.value.close && nativePopupView.value.close();
}catch(e){}
nativeMaskView.value = null;
nativePopupView.value = null;
popupShowing.value = false;
// #endif
});
//
const shareToMoments = () => {
// #ifdef APP-PLUS
uni.share({
provider: "weixin",
scene: "WXSceneTimeline",
type: 0,
title: shareTitle.value,
summary:summary.value,
href: safeUrl.value,
imageUrl: shareImg.value,
success: function (res) {
console.log("success:" + JSON.stringify(res));
},
fail: function (err) {
console.log("fail:" + JSON.stringify(err));
},
});
// #endif
// #ifdef H5
//
uni.setClipboardData({
data: "肝胆相照APP - 专业的医疗健康平台,快来下载体验吧!\n下载链接https://www.igandan.com",
success: () => {
uni.showToast({
title: "已复制到剪贴板,可分享到朋友圈",
icon: "success",
});
},
});
// #endif
// #ifdef MP-WEIXIN
uni.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"],
});
// #endif
closeShare();
};
//
const shareToWeibo = () => {
// #ifdef APP-PLUS
uni.share({
provider: "sinaweibo",
type: 0,
title: shareTitle.value ,
summary:summary.value,
href: safeUrl.value,
imageUrl: logoImg,
success: function (res) {
console.log("分享成功");
},
fail: function (err) {
console.log("fail:" + JSON.stringify(err));
},
});
// plus.share.sendWithSystem({
// type: 'text',
// content: 'APP - \nhttps://www.igandan.com'
// }, () => {
// uni.showToast({
// title: '',
// icon: 'success'
// })
// }, (err) => {
// console.log('', err)
// uni.showToast({
// title: '',
// icon: 'none'
// })
// })
// #endif
// #ifdef H5
//
uni.setClipboardData({
data: "肝胆相照APP - 专业的医疗健康平台,快来下载体验吧!\n下载链接https://www.igandan.com",
success: () => {
uni.showToast({
title: "已复制到剪贴板,可分享到微博",
icon: "success",
});
},
});
// #endif
closeShare();
};
</script>
<style>
.mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 10000000;
}
/* 分享弹窗样式 */
.share-popup {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
padding: 40rpx 0 0;
}
.share-img {
width: 100rpx;
height: 100rpx;
}
.share-title {
text-align: center;
font-size: 32rpx;
color: #333;
margin-bottom: 40rpx;
font-weight: 500;
}
.share-content {
display: flex;
justify-content: space-around;
padding: 0 40rpx 40rpx;
}
.share-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.share-icon {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.wechat-icon {
background-color: #07c160;
}
.moments-icon {
background-color: #07c160;
}
.weibo-icon {
background-color: #e6162d;
}
.qq-icon {
background-color: #12b7f5;
}
.share-icon-text {
font-size: 50rpx;
color: #fff;
font-weight: bold;
}
.share-text {
font-size: 24rpx;
color: #666;
}
.share-cancel {
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
border-top: 1rpx solid #f0f0f0;
font-size: 32rpx;
color: #333;
}
.share-cancel:active {
background-color: #f5f5f5;
}
.nav-actions .share-img-icon {
width: 40rpx;
height: 40rpx;
}
.nav-actions .collect-img-icon {
width: 40rpx;
height: 40rpx;
}
.nav-actions {
display: flex;
align-items: center;
}
.container {
height: 100vh;
position: relative;
z-index:0;
overflow: scroll;
}
.tip {

View File

@ -232,7 +232,7 @@
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.DES }}
{{ tag.NAME }}
</view>
</view>
@ -313,9 +313,9 @@
console.log('选中的筛选标签:', selectedTags);
for (var i = 0; i < selectedTags.length; i++) {
if(keywords.value){
keywords.value+=","+selectedTags[i].DES
keywords.value+=","+selectedTags[i].NAME
}else{
keywords.value=selectedTags[i].DES
keywords.value=selectedTags[i].NAME
}
navTo({

File diff suppressed because it is too large Load Diff

View File

@ -35,7 +35,7 @@
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.DES }}
{{ tag.NAME }}
</view>
</view>
@ -316,7 +316,7 @@
//
if (hasSelected) {
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.DES);
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.NAME);
console.log('选中的标签:', selectedTags);
keywords.value = selectedTags.join(',');
} else {

View File

@ -56,7 +56,7 @@
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.DES }}
{{ tag.NAME }}
</view>
</view>
@ -356,7 +356,7 @@
//
if (hasSelected) {
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.DES);
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.NAME);
console.log('选中的标签:', selectedTags);
keywords.value = selectedTags.join(',');
} else {

BIN
static/fxbb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
static/huanhuan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

BIN
static/ic_about_app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
static/icon_share.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
static/meetHis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/pcquer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
static/share_sina.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
static/share_weixin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
static/share_wxc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
static/suifang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
static/weiboShare.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
static/yingxiongbanglog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
static/yxb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/zixunexpert.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -43,6 +43,22 @@

View File

@ -0,0 +1,9 @@
## 1.0.32023-02-22
本插件将于2023/11/30停止提供服务
## 1.0.22022-07-19
- 【添加】 兼容大部分小程序
- 【修复】 微信小程序中直接打开问题
## 1.0.12022-07-02
修复在 ios 下背景出现大量标题,主题,创作者等等数据
## 1.0.02022-05-25
版本上线

View File

@ -0,0 +1,208 @@
<template>
<view class="preview" v-if="value && isMp === false">
<view class="right" :class="previewType"></view>
<view class="left" :class="previewType"></view>
<iframe :src="previewUrl" width="100%" frameborder="0" id="if"></iframe>
<view class="error" v-if="isError">传入文件格式不正确</view>
<view class="loading">预览模块加载中...</view>
<view class="close" @click="closePre()">关闭</view>
</view>
</template>
<script>
export default {
model: {
event: 'change'
},
props: {
fileUrl: {
type: String,
default: ''
},
value: {
type: Boolean
}
},
data() {
return {
previewUrl: ``,
isError: false,
isMp: false
}
},
watch: {
fileUrl: {
handler(newValue) {
if (!newValue) return
//#ifdef MP
this.isMp = true
uni.downloadFile({
url: newValue,
success: res => {
const filePath = res.tempFilePath
uni.openDocument({
filePath: filePath,
success: res => {
console.log('打开文档成功')
}
})
}
})
//#endif
//#ifndef MP
let exit = newValue.split('.')
if (!exit.length) return (this.isError = true)
exit = exit.pop()
const arr = ['pptx', 'ppt', 'docx', 'doc', 'xlsx', 'xls', 'pdf']
if (arr.indexOf(exit) === -1) return (this.isError = true)
this.isError = false
if (exit === 'pdf') return (this.previewUrl = this.pasePdfUrl(newValue))
this.previewUrl = this.paseOfficeUrl(newValue)
//#endif
},
immediate: true
}
},
methods: {
closePre() {
this.$emit('change', false)
},
paseOfficeUrl(url) {
url = encodeURIComponent(url)
return `https://view.officeapps.live.com/op/embed.aspx?src=${url}&amp;wdPrint=0&amp;wdEmbedCode=0&amp;wdStartOn=1`
},
pasePdfUrl(url) {
//url = encodeURIComponent(url)
return `https://previewpdf.mumudev.top/?file=${url}`
}
},
computed: {
previewType() {
let exit = this.fileUrl.split('.')
if (!exit.length) return console.log('传入文件格式不正确')
exit = exit.pop()
switch (exit) {
case 'pptx':
case 'ppt':
return 'ppt'
case 'docx':
case 'doc':
return 'word'
case 'xlsx':
case 'xls':
return 'excel'
case 'pdf':
return ''
default:
console.log('传入文件格式不正确')
}
}
}
}
</script>
<style lang="scss" scoped>
.preview {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
z-index: 100;
.right,
.left {
position: absolute;
z-index: 99999;
}
.ppt {
height: 20px;
width: 80px;
background-color: #444444;
&.right {
bottom: 2px;
right: 4px;
}
&.left {
left: 4px;
bottom: 2px;
width: 50px;
}
}
.word {
position: absolute;
width: 80px;
height: 17px;
background-color: #ffffff;
&.right {
bottom: 2px;
right: 2px;
}
&.left {
left: 2px;
bottom: 2px;
width: 40px;
}
}
.excel {
position: absolute;
width: 90px;
height: 23px;
background-image: linear-gradient(#3f4244, #36383a);
&.right {
bottom: 2px;
right: 2px;
}
&.left {
left: 2px;
bottom: 2px;
}
}
.close {
position: absolute;
top: 50px;
right: 10px;
background-color: #ccc;
color: #fff;
padding: 5px 10px;
border-radius: 5px;
font-size: 12px;
z-index: 102;
}
.error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ccc;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ccc;
}
#if {
height: 100%;
position: relative;
z-index: 101;
}
}
</style>

View File

@ -0,0 +1,85 @@
{
"id": "mumu-previewOffce",
"displayName": "预览offce文件。可以打开 PDF WORD PPT EXCEL 文件",
"version": "1.0.3",
"description": "在程序中直接打开 offce 文件进行预览。可以打开 PDF WORD PPT EXCEL 文件",
"keywords": [
"PDF",
"WORD",
"PPT",
"EXCEL",
"offce"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "n",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "u",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,85 @@
# 插件介绍
** 预览的文件地址必须是可以通过互联网访问的!!! **
** 预览的文件地址必须是可以直接复制在浏览器地址中访问的!!! **
## 本插件将于2023/11/30停止提供服务
文档预览工具新版本已上线
对比当前版本支持了手机端双手缩放操作office文件不在依赖微软的解析接口支持服务端完全私有化部署(内网部署)UI界面有单独的电脑端、ios端、安卓端。
新版地址:[h5office。预览office文件预览文档打开PDF WORD PPT EXCEL 文件 - DCloud 插件市场](https://ext.dcloud.net.cn/plugin?id=10895)
## 插件原理
> pdf 文件预览是通过 `pdf.js` 开源库搭建了一个pdf预览的网站。前端只需要使用 iframe 加载这个网站即可。[pdf.js 官网](http://mozilla.github.io/pdf.js/api/draft/index.html)
>
> offce 文件的预览是通过微软offce在线接口进行解析的。offce在线地址https://view.officeapps.live.com/op/embed.aspx?src=‘你的文件网络地址’
>
> 在微信小程序中是通过小程序中的API进行预览的。[小程序文档](https://developers.weixin.qq.com/miniprogram/dev/api/file/wx.openDocument.html)
## 使用环境
** 不支持nvue 。小程序中使用官方提供的api。h5与其他环境是通过上面介绍的插件原理加载。**
## 插件使用
**插件已支持 uni_modules 支持组件easycom以下代码演示的是普通使用**
``` html
<!-- HTML -->
<mumu-previewOffce :fileUrl='fileUrl' v-model='showPreview'></mumu-previewOffce>
<button @click='showPreview = true'></button>
```
``` js
import MumuPreviewOffce from '@/uni_modules/mumu-previewOffce/components/mumu-previewOffce/mumu-previewOffce.vue'
export default {
components: {
MumuPreviewOffce
},
data() {
return {
showPreview: false,
fileUrl: 'https://h5plugin.mumudev.top/public/previewOffce/333.docx'
}
},
methods: {
},
}
```
## 相关 API
### 可传属性Props
| 参数 | 说明 | 类型 | 默认值 | 可选 |
| ------- | ------------------------ | ------- | ------ | ------------ |
| v-model | 双向绑定,显示或隐藏组件 | Boolean | false | false / true |
| fileUrl | 预览文件的网络地址 | String | - | - |
## 打开本地预览
本地预览功能还在开发中...
开发思路是:
> 选择打开本地文件,上传到服务器。获取到服务器中的文件地址,传递给当前组件展示。
没有办法直接在本地打开,所有采取这种方案。有条件的同学可以自己开发。我也会尽快把这个功能做出来。
## 案例演示
![](https://h5plugin.mumudev.top/public/previewOffce/qrcode.png)
## 支持作者
![支持作者](https://student.mumudev.top/wxMP.jpg)

70
utils/aesEcb.js Normal file
View File

@ -0,0 +1,70 @@
import crypto from "crypto-js";
//解密
const decryptAesEcb = (sSrc, sKey) => {
try {
if (!sKey) {
console.error("Key为空null");
return null;
}
// 1. 将密钥转为 WordArraycrypto 内部格式)
const key = crypto.enc.Utf8.parse(sKey);
// 2. Base64 解码密文,得到 WordArray
const encryptedBytes = crypto.enc.Base64.parse(sSrc);
// 3. 使用 AES/ECB/PKCS7 解密
const decrypted = crypto.AES.decrypt(
{ ciphertext: encryptedBytes }, // 加密数据
key, // 密钥
{
mode: crypto.mode.ECB, // ECB 模式
padding: crypto.pad.Pkcs7, // PKCS7等同于 Java 的 PKCS5Padding
}
);
// 4. 将解密结果转为 UTF-8 字符串
const decryptedText = decrypted.toString(crypto.enc.Utf8);
return decryptedText; // 如果解密失败,可能返回空字符串或非法字符
} catch (error) {
console.error("解密失败:", error);
return null;
}
};
//加密
const encryptAesEcb = (sSrc, sKey) => {
try {
if (!sKey) {
console.error("Key为空null");
return null;
}
// 1. 将密钥转为 WordArraycrypto 内部格式)
const key = crypto.enc.Utf8.parse(sKey);
// 2. 将明文转为 WordArray
const srcBytes = crypto.enc.Utf8.parse(sSrc);
// 3. 使用 AES/ECB/PKCS7 加密
const encrypted = crypto.AES.encrypt(
srcBytes, // 明文数据
key, // 密钥
{
mode: crypto.mode.ECB, // ECB 模式
padding: crypto.pad.Pkcs7, // PKCS7 填充
}
);
// 4. 将加密结果转为 Base64 字符串
// crypto-js 的 encrypt 方法返回的对象中ciphertext 是 WordArray 格式
const encryptedBase64 = encrypted.ciphertext.toString(crypto.enc.Base64);
return encryptedBase64;
} catch (error) {
console.error("加密失败:", error);
return null;
}
};
export { decryptAesEcb, encryptAesEcb };

View File

@ -14,5 +14,6 @@ if(process.env.UNI_PLATFORM =="h5"){
}
}else{
BASE_URL='https://dev-app.igandan.com/app'
//BASE_URL='https://app.igandan.com/app'
}
export default BASE_URL

View File

@ -14,5 +14,6 @@ if(process.env.UNI_PLATFORM =="h5"){
}
}else{
DOC_URL='https://dev-doc.igandan.com/app/'
//DOC_URL='https://doc.igandan.com/app/'
}
export default DOC_URL

9
utils/formatNumber.js Normal file
View File

@ -0,0 +1,9 @@
const formatNumber = (number) => {
if(number < 10000){
return number;
}else{
return (number / 10000).toFixed(1) + 'w';
}
}
export default formatNumber;

13
utils/otherHost.js Normal file
View File

@ -0,0 +1,13 @@
let OTHER_HOST=''
if(process.env.UNI_PLATFORM =="mp-weixin"){
const { envVersion } = uni.getAccountInfoSync().miniProgram;
if (envVersion == "release") {
OTHER_HOST='https://wx.igandan.com'
}else{
OTHER_HOST='https://dev-wx.igandan.com'
}
}else{
OTHER_HOST='https://dev-wx.igandan.com'
//OTHER_HOST='https://wx.igandan.com'
}
export default OTHER_HOST

35
utils/permission.js Normal file
View File

@ -0,0 +1,35 @@
import permision from "@/js_sdk/wa-permission/permission.js"
const getCameraPermission = async(callback) => {
const systemInfo = uni.getSystemInfoSync();
// 判断平台
if (systemInfo.platform === 'android') {
let result = await permision.requestAndroidPermission('android.permission.CAMERA');
if(result == 1){
callback();
}else{
openModal('请授权相机权限');
}
} else if (systemInfo.platform === 'ios') {
let iosResult = await permision.judgeIosPermission("camera")
if(iosResult){
callback();
}else{
openModal('请授权相机权限');
}
}
}
const openModal=(content)=>{
uni.showModal({
title: '提示',
content:content,
showCancel: true,
success: (res) => {
if(res.confirm){
permision.gotoAppPermissionSetting();
}
}
});
}
export { getCameraPermission };

View File

@ -137,8 +137,10 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
n(res)
} else {
if(url.indexOf('addOutPatientA')!=-1){
if(url.indexOf('addOutPatientA')!=-1 || url.indexOf('checkVersion')!=-1){
e(res.data)
}else if(res.data.code==35002){
n(res)
}else{
uni.showToast({
title: res.data.message,