Compare commits

..
40 Commits
Author SHA1 Message Date
xiaoxiao 3013b441a6 1.2.0部分代码 2025-09-09 13:27:34 +08:00
xiaoxiao e89a5f15ce 1.2.0部分代码 2025-08-27 16:11:20 +08:00
XiuYun CHEN ca7d1dd379 环境切换 2025-08-19 15:48:01 +08:00
XiuYun CHEN 81aba7fdf6 bug修改 2025-08-19 09:09:46 +08:00
xiaoxiao 662c0ba507 状态栏修改 2025-08-18 16:28:56 +08:00
XiuYun CHEN ccd4cdf328 选择多张图片 2025-08-18 16:33:10 +08:00
XiuYun CHEN 110512d9cb 选择多张图片 2025-08-18 16:21:56 +08:00
XiuYun CHEN f1032ddef1 bug优化 2025-08-15 17:35:06 +08:00
xiaoxiao 7035dbcdfb 聊天输入框高度自适应 2025-08-15 17:17:37 +08:00
xiaoxiao bf8f2c90e6 聊天输入框高度自适应 2025-08-15 17:16:19 +08:00
XiuYun CHEN b63d268712 关闭网络请求的log 2025-08-15 11:09:46 +08:00
XiuYun CHEN 9e8c96ccd3 bug修改 2025-08-15 11:02:03 +08:00
xiaoxiao 8ce86ba34b 图片上传 2025-08-15 10:36:32 +08:00
xiaoxiao 75f032f853 bug修复 2025-08-15 10:30:15 +08:00
xiaoxiao e73e2d541f cookie添加 2025-08-14 14:36:34 +08:00
xiaoxiao 38af6d3fd8 上传图片 2025-08-14 13:02:39 +08:00
xiaoxiao 8ab35786fb 上传图片 2025-08-14 11:56:16 +08:00
XiuYun CHEN b7a08cc1b3 bug修改 2025-08-13 14:27:37 +08:00
xiaoxiao f5a31e1ffa 聊天气泡背景图 2025-08-12 13:30:54 +08:00
XiuYun CHEN 5212bfee2c bug修改 2025-08-11 15:01:18 +08:00
xiaoxiao 2ae59301fd 消息撤回显示 2025-08-11 13:50:46 +08:00
xiaoxiao 9d235c72f8 消息撤回显示 2025-08-11 13:48:52 +08:00
xiaoxiao 33c3fbea68 消息撤回显示+扫一扫按钮文字 2025-08-11 13:21:54 +08:00
xiaoxiao 5ea18f1468 cookie 2025-08-08 17:03:39 +08:00
xiaoxiao bf3a456c8f cookie 2025-08-08 16:57:26 +08:00
xiaoxiao ebeea4da75 cookie 2025-08-08 16:33:07 +08:00
xiaoxiao 691af9e35c cookie 2025-08-08 16:32:59 +08:00
xiaoxiao 58931fcf2e 患者分组代码优化,解决大数据问题 2025-08-08 10:17:47 +08:00
xiaoxiao 8d0f7452b5 患者分组代码提交 2025-08-07 16:07:26 +08:00
xiaoxiao 43663a9656 患者分组代码提交 2025-08-07 14:13:28 +08:00
XiuYun CHEN e590efbe68 我的患者列表 2025-08-07 14:13:57 +08:00
XiuYun CHEN 69dd55bf27 我的患者列表 2025-08-06 15:38:11 +08:00
XiuYun CHEN 8cb36953d9 群发消息显示 2025-08-06 11:08:48 +08:00
xiaoxiao c016bc889f 群发bug修复 2025-08-06 09:54:45 +08:00
xiaoxiao 9f37131022 bug修复 2025-08-05 17:01:39 +08:00
xiaoxiao 68c33cf3a5 bug修复 2025-08-05 16:59:58 +08:00
XiuYun CHEN 76b7785d84 角标,小红点,群发消息 2025-08-05 17:02:15 +08:00
XiuYun CHEN 9355959b36 登录和接口请求 2025-08-05 10:52:22 +08:00
XiuYun CHEN ef0b32cfe4 Merge branch 'master' of https://gitea.igandanyiyuan.com/gdxz/harmony
# Conflicts:
#	features/register/src/main/ets/view/LoginComp.ets
2025-08-05 09:58:30 +08:00
XiuYun CHEN ad584a9860 登录 2025-08-05 09:56:53 +08:00
195 changed files with 10280 additions and 773 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
"app": { "app": {
"bundleName": "cn.shangyu.gdxzExports", "bundleName": "cn.shangyu.gdxzExports",
"vendor": "example", "vendor": "example",
"versionCode": 1000000, "versionCode": 1000003,
"versionName": "1.0.0", "versionName": "1.1.0",
"icon": "$media:layered_image", "icon": "$media:layered_image",
"label": "$string:app_name" "label": "$string:app_name"
} }
+35 -11
View File
@@ -26,12 +26,11 @@ import { KitLogger } from './logger/AppLogger';
import { LoggerKitImpl } from './logger/LoggerKitImpl'; import { LoggerKitImpl } from './logger/LoggerKitImpl';
import { ChatRepo } from './repo/ChatRepo'; import { ChatRepo } from './repo/ChatRepo';
import { saveLocalRevokeMessageFormOther } from './utils/MessageUtils'; import { saveLocalRevokeMessageFormOther } from './utils/MessageUtils';
import { router } from '@kit.ArkUI'; import { LengthMetrics, router } from '@kit.ArkUI';
import { HMRouterMgr } from '@hadss/hmrouter'; import { HMRouterMgr } from '@hadss/hmrouter';
import { NotificationUtil } from '@itcast/basic'; import { ChangeUtil, NotificationManagementUtil, NotificationUtil } from '@itcast/basic';
import { BusinessError } from '@kit.BasicServicesKit'; import { BusinessError } from '@kit.BasicServicesKit';
import { NotificationBasicOptions } from '@itcast/basic/src/main/ets/pushnotification/NotificationOptions'; import { NotificationBasicOptions } from '@itcast/basic/src/main/ets/pushnotification/NotificationOptions';
export const currentConversationChanged: string = 'CurrentConversationChanged' export const currentConversationChanged: string = 'CurrentConversationChanged'
export class ChatKitClient { export class ChatKitClient {
@@ -58,6 +57,7 @@ export class ChatKitClient {
appKey: appKey, appKey: appKey,
imVersion: IM_SDK_VERSION, imVersion: IM_SDK_VERSION,
}) })
} }
/** /**
@@ -173,15 +173,36 @@ export class ChatKitClient {
return return
} }
console.info("netease ChatKitClient initListener "); console.info("netease ChatKitClient initListener ");
ChatKitClient.nim.loginService.on("onKickedOffline", (detail: V2NIMKickedOfflineDetail) => { ChatKitClient.nim.loginService.on("onKickedOffline", async (detail: V2NIMKickedOfflineDetail) => {
// const detail = ChatKitClient.nim.loginService.getKickedOfflineDetail() // const detail = ChatKitClient.nim.loginService.getKickedOfflineDetail()
console.log('Response onKickedOffline'+detail.clientType+' 22 '+detail.customClientType) // console.log('Response onKickedOffline'+detail.clientType+' 22 '+detail.customClientType)
if(HMRouterMgr.getCurrentPathStack()!=null)
{
HMRouterMgr.removeAll()
}
let phone="ios";
switch(detail.clientType)
{
case 1:
phone="android";
break;
case 2:
phone="ios";
break;
case 16:
phone="web";
break;
default:
phone="其他设备";
break;
HMRouterMgr.removeAll() }
router.replaceUrl({ await ChatKitClient.nim.loginService.logout()
url: 'pages/LoginPage/LoginPage', // 目标url ChangeUtil.Logout(phone)
},router.RouterMode.Single) // router.replaceUrl({
router.clear() // url: 'pages/LoginPage/LoginPage', // 目标url
// },router.RouterMode.Single)
// router.clear()
}) })
// 数据同步监听 // 数据同步监听
ChatKitClient.nim.conversationService?.on('onSyncFinished', ChatKitClient.onSyncFinishedFun) ChatKitClient.nim.conversationService?.on('onSyncFinished', ChatKitClient.onSyncFinishedFun)
@@ -189,7 +210,8 @@ export class ChatKitClient {
// 消息撤回监听,消息撤回后,会收到通知 // 消息撤回监听,消息撤回后,会收到通知
ChatRepo.onRevokeMessage(ChatKitClient.onRevokeFun) ChatRepo.onRevokeMessage(ChatKitClient.onRevokeFun)
ChatKitClient.hasInitListener = true ChatKitClient.hasInitListener = true
let unreadCount=ChatKitClient.nim.localConversationService?ChatKitClient.nim.localConversationService.getTotalUnreadCount():-1
NotificationManagementUtil.setBadgeNumber(unreadCount)
// ChatKitClient.nim.messageService?.on("onReceiveMessages", (messages: V2NIMMessage[]) => { // ChatKitClient.nim.messageService?.on("onReceiveMessages", (messages: V2NIMMessage[]) => {
// for (const message of messages) { // for (const message of messages) {
// let basicOptions: NotificationBasicOptions = { // let basicOptions: NotificationBasicOptions = {
@@ -223,4 +245,6 @@ export class ChatKitClient {
} }
}) })
} }
} }
+35 -31
View File
@@ -12,42 +12,46 @@ import { ChatRepo } from '../repo/ChatRepo';
// 保存撤回消息到本地 // 保存撤回消息到本地
export function saveLocalRevokeMessage(conversationId: string, msg: V2NIMMessage, edit: boolean) { export function saveLocalRevokeMessage(conversationId: string, msg: V2NIMMessage, edit: boolean) {
// let currentTime = systemDateTime.getTime() let currentTime = systemDateTime.getTime()
// let revokeText = getContext().resourceManager.getStringByNameSync('chat_msg_undo_tips'); // let revokeText = getContext().resourceManager.getStringByNameSync('chat_msg_undo_tips');
// let revokeMsg = ChatKitClient.nim.messageCreator.createTextMessage(revokeText) let revokeText = '你撤回一条消息';
// revokeMsg.serverExtension = msg.serverExtension let revokeMsg = ChatKitClient.nim.messageCreator.createTextMessage(revokeText)
// revokeMsg.threadReply = msg.threadReply revokeMsg.serverExtension = msg.serverExtension
// let localExtension = { revokeMsg.threadReply = msg.threadReply
// revoke_message_local: true, revokeMsg.messageType = 10
// revoke_message_local_time: currentTime, let localExtension = {
// revoke_message_client_id: msg.messageClientId, revoke_message_local: true,
// revoke_message_local_edit: edit, revoke_message_local_time: currentTime,
// revoke_message_local_content: msg.text ?? '' revoke_message_client_id: msg.messageClientId,
// revoke_message_local_edit: edit,
// } as RevokeMessageExtension revoke_message_local_content: msg.text ?? ''
//
// revokeMsg.localExtension = JSON.stringify(localExtension) } as RevokeMessageExtension
// let createTime = msg.createTime + 10
// console.debug('netease saveLocalRevokeMessage:', revokeMsg.localExtension) revokeMsg.localExtension = JSON.stringify(localExtension)
// ChatRepo.saveLocalMessage(revokeMsg, conversationId, ChatKitClient.getLoginUserId(), createTime) let createTime = msg.createTime + 10
console.debug('netease saveLocalRevokeMessage:', revokeMsg.localExtension)
ChatRepo.saveLocalMessage(revokeMsg, conversationId, ChatKitClient.getLoginUserId(), createTime)
} }
// 保存他人撤回消息到本地 // 保存他人撤回消息到本地
export function saveLocalRevokeMessageFormOther(conversationId: string, msgNotify: V2NIMMessageRevokeNotification, export function saveLocalRevokeMessageFormOther(conversationId: string, msgNotify: V2NIMMessageRevokeNotification,
edit: boolean) { edit: boolean) {
// let currentTime = systemDateTime.getTime() let currentTime = systemDateTime.getTime()
// let revokeText = getContext().resourceManager.getStringByNameSync('chat_msg_undo_tips'); // let revokeText = getContext().resourceManager.getStringByNameSync('chat_msg_undo_tips');
// let revokeMsg = ChatKitClient.nim.messageCreator.createTextMessage(revokeText) let revokeText = '对方撤回一条消息';
// let localExtension = { let revokeMsg = ChatKitClient.nim.messageCreator.createTextMessage(revokeText)
// revoke_message_local: false, revokeMsg.messageType = 10
// revoke_message_local_time: currentTime, let localExtension = {
// revoke_message_client_id: msgNotify.messageRefer.messageClientId, revoke_message_local: false,
// revoke_message_local_edit: edit, revoke_message_local_time: currentTime,
// revoke_message_local_content: '' revoke_message_client_id: msgNotify.messageRefer.messageClientId,
// } as RevokeMessageExtension revoke_message_local_edit: edit,
// revoke_message_local_content: ''
// revokeMsg.localExtension = JSON.stringify(localExtension) } as RevokeMessageExtension
// console.debug('netease saveLocalRevokeMessageFormOther:', revokeMsg.localExtension)
// let createTime = msgNotify.messageRefer.createTime + 10; revokeMsg.localExtension = JSON.stringify(localExtension)
// ChatRepo.saveLocalMessage(revokeMsg, conversationId, msgNotify.revokeAccountId, createTime) console.debug('netease saveLocalRevokeMessageFormOther:', revokeMsg.localExtension)
let createTime = msgNotify.messageRefer.createTime + 10;
ChatRepo.saveLocalMessage(revokeMsg, conversationId, msgNotify.revokeAccountId, createTime)
} }
+53 -17
View File
@@ -23,6 +23,7 @@ import {
NECommonUtils, NECommonUtils,
NetworkBrokenBuilder, NetworkBrokenBuilder,
PermissionsUtils, PermissionsUtils,
StringIsEmpty,
UserUtils, UserUtils,
VideoViewerDialog VideoViewerDialog
} from '@nimkit/common'; } from '@nimkit/common';
@@ -75,9 +76,9 @@ import { TextMessageDetailDialog } from '../view/TextMessageDetailDialog';
import { TeachDialog } from '../view/TeachDialog'; import { TeachDialog } from '../view/TeachDialog';
import { NewWaDialog } from '../view/NewWaDialog'; import { NewWaDialog } from '../view/NewWaDialog';
import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet' import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet'
import { hdHttp, HdResponse,BasicConstant, authStore, preferenceStore, ChatParam} from '@itcast/basic' import { hdHttp, HdResponse,BasicConstant, authStore, preferenceStore, ChatParam,
customAttachment} from '@itcast/basic'
import { HashMap } from '@kit.ArkTS'; import { HashMap } from '@kit.ArkTS';
import { customAttachment } from '../view/MessageComponentBuilder'
import { HMRouter, HMRouterMgr, HMRouterPathCallback, HMRouterPathInfo } from '@hadss/hmrouter'; import { HMRouter, HMRouterMgr, HMRouterPathCallback, HMRouterPathInfo } from '@hadss/hmrouter';
@HMRouter({ @HMRouter({
pageUrl: 'ChatP2PPage' pageUrl: 'ChatP2PPage'
@@ -117,6 +118,11 @@ export struct ChatP2PPage {
navBarHeight: number = 80; navBarHeight: number = 80;
// 底部输入框以及固定按钮操作栏高度 // 底部输入框以及固定按钮操作栏高度
@Local bottomHeight: number = 65; @Local bottomHeight: number = 65;
@Monitor("bottomHeight")
onbottomHeightChange(monitor: IMonitor) {
// console.info(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
this.computeScrollHeight()
}
@Local bottomMargin: number = 100; @Local bottomMargin: number = 100;
// bottomMargin: number = 10; // bottomMargin: number = 10;
bottomWithReplyHeight: number = 135; bottomWithReplyHeight: number = 135;
@@ -450,7 +456,13 @@ export struct ChatP2PPage {
let json = JSON.parse(res+'') as Record<string, object>; let json = JSON.parse(res+'') as Record<string, object>;
let datas=json.data as Record<string, string>; let datas=json.data as Record<string, string>;
this.patientUuid=datas.uuid this.patientUuid=datas.uuid
let tmpname= this.showName
this.showName=await UserUtils.getAvatarName(this.patientUuid,String(this.chatUserInfo.conversationName)) this.showName=await UserUtils.getAvatarName(this.patientUuid,String(this.chatUserInfo.conversationName))
if(StringIsEmpty(this.showName))
{
this.showName=tmpname
}
}).catch((err: BusinessError) => { }).catch((err: BusinessError) => {
}) })
@@ -840,8 +852,6 @@ export struct ChatP2PPage {
Column() Column()
.height(this.bottomHeight) .height(this.bottomHeight)
.backgroundColor(Color.Transparent)
.backgroundColor($r('app.color.chat_input_background'))
.id("chat_input") .id("chat_input")
.alignRules({ .alignRules({
left: { anchor: "__container__", align: HorizontalAlign.Start }, left: { anchor: "__container__", align: HorizontalAlign.Start },
@@ -861,14 +871,21 @@ export struct ChatP2PPage {
}, },
onDidClickImage: () => { onDidClickImage: () => {
this.showOperationView = false this.showOperationView = false
MediaUtils.showImageVideoPicker().then((result) => { MediaUtils.showImageVideoPicker().then((results) => {
if (result.errorMsg == null && result.uri) { if(results!=null&&results.length>0)
if (result.type === photoAccessHelper.PhotoType.IMAGE) { {
this.chatViewModel.sendImageMessage(result.uri); for (const result of results) {
} else if (result.type === photoAccessHelper.PhotoType.VIDEO) { if (result.errorMsg == null && result.uri) {
this.chatViewModel.sendVideoMessage(result.uri, result.duration, result.width, result.height); if (result.type === photoAccessHelper.PhotoType.IMAGE) {
this.chatViewModel.sendImageMessage(result.uri);
} else if (result.type === photoAccessHelper.PhotoType.VIDEO) {
this.chatViewModel.sendVideoMessage(result.uri, result.duration, result.width, result.height);
}
}
} }
} }
}) })
}, },
onDidClickAudio: () => { onDidClickAudio: () => {
@@ -926,6 +943,10 @@ export struct ChatP2PPage {
this.chatViewModel.sendTextMessage(text, this.replyMsg) this.chatViewModel.sendTextMessage(text, this.replyMsg)
this.clearInput(true) this.clearInput(true)
}, },
onChangeInputHeight:(height:number)=> {
this.bottomHeight = height+25
// this.computeScrollHeight()
},
inputStyle: this.inputStyle inputStyle: this.inputStyle
}).alignRules({ }).alignRules({
left: { anchor: "chat_input", align: HorizontalAlign.Start }, left: { anchor: "chat_input", align: HorizontalAlign.Start },
@@ -935,6 +956,7 @@ export struct ChatP2PPage {
}) })
.backgroundColor($r('app.color.chat_input_background')) .backgroundColor($r('app.color.chat_input_background'))
} else if (this.showMultiSelect) { } else if (this.showMultiSelect) {
ChatMultiSelectView({ ChatMultiSelectView({
isEnable: this.multiSelectCount > 0, isEnable: this.multiSelectCount > 0,
@@ -1003,14 +1025,27 @@ export struct ChatP2PPage {
}) })
} else if (data.type == NEChatMoreOperationType.Image) { } else if (data.type == NEChatMoreOperationType.Image) {
this.showOperationView = false this.showOperationView = false
MediaUtils.showImageVideoPicker().then((result) => { MediaUtils.showImageVideoPicker().then((results) => {
if (result.errorMsg == null && result.uri) { if(results!=null&&results.length>0)
if (result.type === photoAccessHelper.PhotoType.IMAGE) { {
this.chatViewModel.sendImageMessage(result.uri); for (const result of results) {
} else if (result.type === photoAccessHelper.PhotoType.VIDEO) { if (result.errorMsg == null && result.uri) {
this.chatViewModel.sendVideoMessage(result.uri, result.duration, result.width, result.height); if (result.type === photoAccessHelper.PhotoType.IMAGE) {
this.chatViewModel.sendImageMessage(result.uri);
} else if (result.type === photoAccessHelper.PhotoType.VIDEO) {
this.chatViewModel.sendVideoMessage(result.uri, result.duration, result.width, result.height);
}
}
} }
} }
// if (result.errorMsg == null && result.uri) {
// if (result.type === photoAccessHelper.PhotoType.IMAGE) {
// this.chatViewModel.sendImageMessage(result.uri);
// } else if (result.type === photoAccessHelper.PhotoType.VIDEO) {
// this.chatViewModel.sendVideoMessage(result.uri, result.duration, result.width, result.height);
// }
// }
}) })
} }
@@ -1167,6 +1202,7 @@ export struct ChatP2PPage {
.expandSafeArea([SafeAreaType.KEYBOARD]) .expandSafeArea([SafeAreaType.KEYBOARD])
.zIndex(1) .zIndex(1)
} }
.backgroundColor('#f4f4f4')
// } // }
@@ -1235,7 +1271,7 @@ export struct ChatP2PPage {
this.controller.deleteSpans() this.controller.deleteSpans()
} }
this.replyMsg = undefined this.replyMsg = undefined
this.bottomHeight = 105 // this.bottomHeight = 105
this.computeScrollHeight() this.computeScrollHeight()
} }
+12 -9
View File
@@ -778,15 +778,15 @@ export struct ChatTeamPage {
}, },
onDidClickImage: () => { onDidClickImage: () => {
this.showOperationView = false this.showOperationView = false
MediaUtils.showImageVideoPicker().then((result) => { // MediaUtils.showImageVideoPicker().then((result) => {
if (result.errorMsg == null && result.uri) { // if (result.errorMsg == null && result.uri) {
if (result.type === photoAccessHelper.PhotoType.IMAGE) { // if (result.type === photoAccessHelper.PhotoType.IMAGE) {
this.chatViewModel.sendImageMessage(result.uri); // this.chatViewModel.sendImageMessage(result.uri);
} else if (result.type === photoAccessHelper.PhotoType.VIDEO) { // } else if (result.type === photoAccessHelper.PhotoType.VIDEO) {
this.chatViewModel.sendVideoMessage(result.uri, result.duration, result.width, result.height); // this.chatViewModel.sendVideoMessage(result.uri, result.duration, result.width, result.height);
} // }
} // }
}) // })
}, },
onDidClickAudio: () => { onDidClickAudio: () => {
let context = getContext(this) as common.UIAbilityContext let context = getContext(this) as common.UIAbilityContext
@@ -851,6 +851,9 @@ export struct ChatTeamPage {
this.aitManager.cleanAit() this.aitManager.cleanAit()
this.builderSpans.splice(0, this.builderSpans.length) this.builderSpans.splice(0, this.builderSpans.length)
}, },
onChangeInputHeight:(height:number)=>{
this.bottomHeight = height
},
inputStyle: this.inputStyle, inputStyle: this.inputStyle,
mute: this.chatViewModel.isMuteModel, mute: this.chatViewModel.isMuteModel,
teamId: this.chatTeamInfo.team?.teamId, teamId: this.chatTeamInfo.team?.teamId,
+27 -1
View File
@@ -47,6 +47,8 @@ export struct NEChatInputView {
onDidClickMore?: () => void; onDidClickMore?: () => void;
@Param @Require @Param @Require
onDidClickCloseReply?: () => void; onDidClickCloseReply?: () => void;
@Param @Require
onChangeInputHeight?: (height:number) => void;
@Param inputStyle: InputStyleType = InputStyleType.None; @Param inputStyle: InputStyleType = InputStyleType.None;
@Param @Require @Param @Require
controller?: RichEditorController; controller?: RichEditorController;
@@ -91,6 +93,24 @@ export struct NEChatInputView {
width: '100%', width: '100%',
customStyle: true customStyle: true
}) })
@Local editorHeight: number = 40; // 新增:动态高度状态
/**
* 计算编辑器高度
*/
calculateEditorHeight(): number {
const baseHeight = 40;
const maxHeight = 100;
const lineHeight = 20; // 每行大约20vp高度
if (this.inputContentLength === 0) {
return baseHeight;
}
// 根据内容长度估算行数,假设每行约4420个字符
const estimatedLines = Math.ceil(this.inputContentLength / 26);
const calculatedHeight = baseHeight + (estimatedLines - 1) * lineHeight;
// 限制在最小和最大高度之间
return Math.max(baseHeight, Math.min(calculatedHeight, maxHeight));
}
aboutToAppear(): void { aboutToAppear(): void {
this.initData(); this.initData();
@@ -328,6 +348,7 @@ export struct NEChatInputView {
RichEditor({ controller: this.controller }) RichEditor({ controller: this.controller })
.backgroundColor(this.mute ? $r('app.color.color_chat_mute_bg') : $r('app.color.color_chat_page_bg')) .backgroundColor(this.mute ? $r('app.color.color_chat_mute_bg') : $r('app.color.color_chat_page_bg'))
.height(40) .height(40)
.height(this.editorHeight) // 修改:使用动态高度
.layoutWeight(1) .layoutWeight(1)
.placeholder(this.mute ? $r('app.string.chat_team_all_mute') : .placeholder(this.mute ? $r('app.string.chat_team_all_mute') :
$r('app.string.chat_send_tips')) $r('app.string.chat_send_tips'))
@@ -343,6 +364,10 @@ export struct NEChatInputView {
this.inputContentLength = spans this.inputContentLength = spans
.map(span => (span as RichEditorTextSpanResult).value?.length ?? 0) .map(span => (span as RichEditorTextSpanResult).value?.length ?? 0)
.reduce((a, b) => a + b, 0); .reduce((a, b) => a + b, 0);
// 新增:动态计算并更新编辑器高度
this.editorHeight = this.calculateEditorHeight();
this.onChangeInputHeight?.(this.editorHeight)
}) })
.onSelectionChange((range: RichEditorRange) => { .onSelectionChange((range: RichEditorRange) => {
console.debug(`Response ChatInputView onSelectionChange ${range.start} ~ ${range.end}`) console.debug(`Response ChatInputView onSelectionChange ${range.start} ~ ${range.end}`)
@@ -429,7 +454,8 @@ export struct NEChatInputView {
} }
} }
.width('100%') .width('100%')
.height(60) // .height(60)
.height(this.editorHeight+20)
.id("action_button") .id("action_button")
}.padding({ left: 8, right: 8 }) }.padding({ left: 8, right: 8 })
@@ -196,28 +196,37 @@ export struct SenderMessageComponent {
} }
} }
} }
messageContent({ Row() {
message: this.message, messageContent({
onMessageClick: this.onMessageClick, message: this.message,
chatInfo: this.chatUserInfo onMessageClick: this.onMessageClick,
}) chatInfo: this.chatUserInfo
.backgroundColor($r('app.color.color_chat_send_message_bg'))
.borderRadius(6)
// .backgroundImage($r('app.media.green_bg'))
// .backgroundImageSize({ width: '100%', height: '100%' }) // 背景覆盖整个组件区域
// .backgroundImageResizable({
// slice: { top: 100, left: 50, bottom: 2, right: 60 } // 圆角区域不拉伸
//
// })
.onClick((event: ClickEvent) => {
if (this.showRadio) {
this.radioOn = !this.radioOn
}
this.onMessageClick?.onItemClick?.(event, this.message)
}) })
.margin({ top: 4, right: 6 }) .backgroundImage(this.message.getMessageType() !== 100?$r('app.media.im_my_message_back_icon'):$r('app.media.im_my_message_white_back_icon'))
.flexShrink(1) .backgroundImageSize({ width: '100%', height: '100%' })
.id("msgContainer") .backgroundImageResizable({
slice:{
top:24,right:36,bottom:24,left:24
}
})
.onClick((event: ClickEvent) => {
if (this.showRadio) {
this.radioOn = !this.radioOn
}
this.onMessageClick?.onItemClick?.(event, this.message)
})
.margin({top:2})
.flexShrink(1)
.id("msgContainer")
Image(this.message.getMessageType() !== 100?$r('app.media.im_my_message_jiantou_icon'):$r('app.media.im_my_message_white_jiantou_icon'))
.width(7)
.height(14)
.alignSelf(ItemAlign.Start)
.margin({top:17,left:-1})
.visibility(this.message.getMessageType() !== 1?Visibility.Visible:Visibility.Hidden)
}
} }
.justifyContent(FlexAlign.End) .justifyContent(FlexAlign.End)
.alignItems(VerticalAlign.Bottom) .alignItems(VerticalAlign.Bottom)
@@ -261,13 +270,15 @@ export struct SenderMessageComponent {
}) })
} }
.width('100%') .width('100%')
.backgroundColor(this.message.isPinMsg ? $r('app.color.color_chat_pin_bg') : // .backgroundColor(this.message.isPinMsg ? $r('app.color.color_chat_pin_bg') :
$r('app.color.color_chat_page_bg')) // $r('app.color.color_chat_page_bg'))
.backgroundColor('#f4f4f4')
.justifyContent(FlexAlign.End) .justifyContent(FlexAlign.End)
.alignItems(VerticalAlign.Top) .alignItems(VerticalAlign.Top)
} }
} }
.width('100%').margin({ top: 6, bottom: 6 }) .width('100%').margin({ top: 6, bottom: 6 })
.backgroundColor('#f4f4f4')
} }
} }
@@ -351,21 +362,36 @@ export struct ReceiveMessageComponent {
} }
Column() { Column() {
messageContent({ Row() {
message: this.message, Image($r('app.media.im_from_message_white_jiantou_icon'))
onMessageClick: this.onMessageClick, .width(7)
chatInfo: this.chatUserInfo .height(14)
}) .alignSelf(ItemAlign.Start)
.backgroundColor($r('app.color.color_chat_receive_message_bg')) .margin({top:10})
.borderRadius(6) .visibility(this.message.getMessageType() !== 1?Visibility.Visible:Visibility.Hidden)
.id("msgContainer")
.alignRules(this.AlignLeft) messageContent({
.onClick((event: ClickEvent) => { message: this.message,
if (this.showRadio) { onMessageClick: this.onMessageClick,
this.radioOn = !this.radioOn chatInfo: this.chatUserInfo
}
this.onMessageClick?.onItemClick?.(event, this.message)
}) })
.backgroundImage($r('app.media.im_my_message_white_back_icon'))
.backgroundImageSize({ width: '100%', height: '100%' })
.backgroundImageResizable({
slice:{
top:24,right:36,bottom:24,left:24
}
})
.id("msgContainer")
.alignRules(this.AlignLeft)
.margin({top:-2})
.onClick((event: ClickEvent) => {
if (this.showRadio) {
this.radioOn = !this.radioOn
}
this.onMessageClick?.onItemClick?.(event, this.message)
})
}
} }
.width('100%') .width('100%')
//.height(this.message?.getMessageHeight(this.getUIContext())) //.height(this.message?.getMessageHeight(this.getUIContext()))
@@ -390,11 +416,13 @@ export struct ReceiveMessageComponent {
} }
.width('100%') .width('100%')
.alignItems(VerticalAlign.Top) .alignItems(VerticalAlign.Top)
.backgroundColor(this.message?.isPinMsg ? $r('app.color.color_chat_pin_bg') : // .backgroundColor(this.message?.isPinMsg ? $r('app.color.color_chat_pin_bg') :
$r('app.color.color_chat_page_bg')) // $r('app.color.color_chat_page_bg'))
.backgroundColor('#f4f4f4')
.id('itemContainer') .id('itemContainer')
} }
}.width('100%').padding({ top: 6, bottom: 6 }) }.width('100%').padding({ top: 6, bottom: 6 })
.backgroundColor('#f4f4f4')
} }
} }
@@ -34,10 +34,11 @@ import { ChatAitNode } from '../model/ChatAitNode'
import { MergedMessageAttachment } from '@nimkit/chatkit' import { MergedMessageAttachment } from '@nimkit/chatkit'
import { ChatInfo } from '../model/ChatInfo' import { ChatInfo } from '../model/ChatInfo'
import { Markdown } from '@nimkit/markdown' import { Markdown } from '@nimkit/markdown'
import { BasicConstant,DataWebModel } from '@itcast/basic' import { BasicConstant, customAttachment,DataWebModel } from '@itcast/basic'
import { router } from '@kit.ArkUI' import { router } from '@kit.ArkUI'
import { videoTools } from 'home' import { videoTools } from 'home'
import { HMRouterMgr } from '@hadss/hmrouter' import { HMRouterMgr } from '@hadss/hmrouter'
import { StringIsEmpty } from '@nimkit/common'
@ComponentV2 @ComponentV2
export struct messageContent { export struct messageContent {
@@ -405,8 +406,8 @@ export struct imageBuilder {
Column() { Column() {
Image(this.thumbUrl ?? this.url) Image(this.thumbUrl ?? this.url)
.objectFit(ImageFit.Cover) .objectFit(ImageFit.Cover)
.width(getImageWidth(this.message)) .width(getImageWidth(this.message)*0.7)
.height(getImageHeight(this.message)) .height(getImageHeight(this.message)*0.7)
.borderRadius(8) .borderRadius(8)
.gesture(LongPressGesture().onAction((event: GestureEvent) => { .gesture(LongPressGesture().onAction((event: GestureEvent) => {
this.onMessageClick?.onItemLongClick?.(event, this.message) this.onMessageClick?.onItemLongClick?.(event, this.message)
@@ -446,7 +447,7 @@ export struct videoBuilder {
build() { build() {
Stack() { Stack() {
Image(this.videoMessageThumbnail ?? getVideoMessageUrl(this.message)).objectFit(ImageFit.Auto) Image(this.videoMessageThumbnail ?? getVideoMessageUrl(this.message)).objectFit(ImageFit.Auto)
.width(getImageWidth(this.message)).height(getImageHeight(this.message)) .width(getImageWidth(this.message)*0.7).height(getImageHeight(this.message)*0.7)
.borderRadius(8) .borderRadius(8)
Image($r('app.media.ic_chat_message_video')).objectFit(ImageFit.Auto) Image($r('app.media.ic_chat_message_video')).objectFit(ImageFit.Auto)
.width(60).height(60) .width(60).height(60)
@@ -836,7 +837,7 @@ export struct customBuilder {
{ {
this.content=custom.gdxz_content this.content=custom.gdxz_content
} }
if(custom.gdxz_title!=null&&custom.gdxz_title.includes("互联网医院")) if(custom.gdxz_title!=null&&custom.gdxz_title.includes("互联网医院")||!StringIsEmpty(custom.gdxz_img))
{ {
this.photoUrl=custom.gdxz_img this.photoUrl=custom.gdxz_img
@@ -969,14 +970,3 @@ function ToWeb(u:string,t:string)
}) })
} }
export interface customAttachment
{
gdxz_title:string;//标题
gdxz_url:string;//内容url
gdxz_content:string;//内容
gdxz_id:string;//id
gdxz_img:string;//照片
gdxz_type:string;//类型
gdxz_ext_data:string;//备用字段,json字符串
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

@@ -55,15 +55,16 @@ export function NavigationBackBuilder(params?: NavigationBackBuilderParams) {
Image($r('app.media.top_back')) Image($r('app.media.top_back'))
.margin({ left: 20 }) .margin({ left: 20 })
.width(65) .width(65)
.height(24) .height(50)
.padding({ .padding({
right:41 right:41,
top: 13,
bottom: 13
}) })
// .padding({ // .padding({
// left: 8, // left: 8,
// right: 8, // right: 8,
// top: 8, //
// bottom: 8
// }) // })
.onClick(params?.leftButtonAction) .onClick(params?.leftButtonAction)
} }
@@ -110,13 +111,13 @@ export function NavigationBackBuilder(params?: NavigationBackBuilderParams) {
Image(params?.rightButtonIcon) Image(params?.rightButtonIcon)
// .width(36) // .width(36)
.width(65) .width(65)
.height(4) .height(50)
.objectFit(ImageFit.Contain) .objectFit(ImageFit.Contain)
.padding({ .padding({
left: 47, left: 47,
// right: 3, // right: 3,
// top: 3, top: 23,
// bottom: 3 bottom: 23
}) })
.margin({ right: 20 }) .margin({ right: 20 })
.onClick(params?.rightButtonAction) .onClick(params?.rightButtonAction)
+34 -2
View File
@@ -57,6 +57,38 @@ export class MediaUtils {
}); });
return result; return result;
} }
static async showMediaPickerMore(mimeType: photoAccessHelper.PhotoViewMIMETypes): Promise<SelectResult[]> {
console.log("net ease show photo picker");
const result: SelectResult[] = await new Promise((resolve: Function) => {
const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 过滤选择媒体文件类型为IMAGE
photoSelectOptions.MIMEType = mimeType;
// 选择媒体文件的最大数目
photoSelectOptions.maxSelectNumber = 5;
const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
photoViewPicker.select(photoSelectOptions)
.then(async (photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
const context = getContext(photoSelectOptions)
const uri = photoSelectResult.photoUris;
let selectResult:SelectResult[]=[]
// const uri = photoSelectResult.photoUris[0];
for (const indexUri of uri) {
selectResult.push(await MediaUtils.getMediaInfo(context, indexUri))
}
// const selectResult = MediaUtils.getMediaInfo(context, uri)
resolve(selectResult);
})
.catch((err: BusinessError) => {
console.error(`net ease Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
const selectResult = new SelectResult();
selectResult.errorMsg =
`net ease Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`;
resolve(selectResult);
})
});
return result;
}
// 显示相册(图片)选择器 // 显示相册(图片)选择器
static async showImagePicker(): Promise<SelectResult> { static async showImagePicker(): Promise<SelectResult> {
@@ -73,9 +105,9 @@ export class MediaUtils {
} }
// 显示相册(图片+视频)选择器 // 显示相册(图片+视频)选择器
static async showImageVideoPicker(): Promise<SelectResult> { static async showImageVideoPicker(): Promise<SelectResult[]> {
console.log("net ease show photo picker"); console.log("net ease show photo picker");
const result: SelectResult = await MediaUtils.showMediaPicker(photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE) const result: SelectResult[] = await MediaUtils.showMediaPickerMore(photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE)
return result; return result;
} }
+25 -1
View File
@@ -101,4 +101,28 @@ export { HdTwoNav } from './src/main/ets/components/HdTwoNav'
export { NotificationUtil } from './src/main/ets/utils/NotificationUtil' export { NotificationUtil } from './src/main/ets/utils/NotificationUtil'
export { ScanUtil } from './src/main/ets/utils/ScanUtil' export { ScanUtil } from './src/main/ets/utils/ScanUtil'
export { NotificationManagementUtil } from './src/main/ets/utils/NotificationManagementUtil'
export { customAttachment } from './src/main/ets/models/customAttachment'
export { ImageUtil } from './src/main/ets/utils/ImageUtil'
export { compressedImage,saveImageToGallery,CompressedImageInfo } from './src/main/ets/utils/ImageCompressUtils'
export { ScreeningView } from './src/main/ets/Views/ScreeningView'
export { TagListModel, TagList} from './src/main/ets/models/TagListModel'
export { AppUtil } from './src/main/ets/utils/AppUtil'
export { OnWXResp,WXApi,WXEventHandler } from './src/main/ets/models/WXApiWrap'
// export { DBManager,FileDownloadManager } from './src/main/ets/utils/FileManager'
export { FileManager } from './src/main/ets/utils/FileManager'
export { FileInfo,DownloadCallback,PreviewConfig } from './src/main/ets/models/FileModel'
export { FileDownloadManager } from './src/main/ets/utils/FileDownloadManager'
@@ -1,3 +1,5 @@
import { ChangeUtil } from "../../../../Index";
import { promptAction } from "@kit.ArkUI";
@CustomDialog @CustomDialog
export struct DefaultHintProWindows { export struct DefaultHintProWindows {
@@ -12,16 +14,23 @@ export struct DefaultHintProWindows {
@Prop cancleColor:ResourceStr = '#FFFFFF'; @Prop cancleColor:ResourceStr = '#FFFFFF';
@Prop confirmColor:ResourceStr = '#FFFFFF'; @Prop confirmColor:ResourceStr = '#FFFFFF';
controller: CustomDialogController; controller: CustomDialogController;
@State inputContent:string = ''
// 添加回调函数属性
private selectedButton: (index: number) => void = () => {}; private selectedButton: (index: number) => void = () => {};
private selectedButtonAndContent: (index: number,content:string) => void = () => {};
// 修改构造函数 // 修改构造函数
constructor(controller: CustomDialogController, selectedButton: (index:number) => void) { constructor(controller: CustomDialogController, selectedButton: (index:number) => void) {
super(); super();
this.controller = controller; this.controller = controller;
this.selectedButton = selectedButton; this.selectedButton = selectedButton;
} }
constructor(controller:CustomDialogController,selectedButtonAndContent: (index: number,content:string) => void) {
super();
this.controller = controller
this.selectedButtonAndContent = selectedButtonAndContent
}
build() { build() {
Row(){ Row(){
@@ -32,10 +41,21 @@ export struct DefaultHintProWindows {
.textAlign(TextAlign.Center) .textAlign(TextAlign.Center)
.margin({ top: 20 }) .margin({ top: 20 })
Text(this.message) if (!ChangeUtil.stringIsUndefinedAndNull(this.message)) {
.fontSize(this.messageFont) Text(this.message)
.textAlign(TextAlign.Center) .fontSize(this.messageFont)
.margin({ top: 10 }) .textAlign(TextAlign.Center)
.margin({ top: 10 })
.padding({left:10,right:10})
} else {
TextInput({placeholder:'请输入您的登录密码'})
.margin(10)
.backgroundColor('#f4f4f4')
.borderRadius(5)
.onChange((value:string)=>{
this.inputContent = value
})
}
Row({ space: 20 }) { Row({ space: 20 }) {
Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) { Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) {
@@ -45,7 +65,11 @@ export struct DefaultHintProWindows {
} }
.width('45%').height(30).backgroundColor(this.cancleColor) .width('45%').height(30).backgroundColor(this.cancleColor)
.onClick(() => { .onClick(() => {
this.selectedButton(0) if (ChangeUtil.stringIsUndefinedAndNull(this.message)) {
this.selectedButtonAndContent(0,'')
} else {
this.selectedButton(0)
}
}) })
.visibility(this.cancleTitle?Visibility.Visible:Visibility.None) .visibility(this.cancleTitle?Visibility.Visible:Visibility.None)
@@ -55,7 +79,15 @@ export struct DefaultHintProWindows {
.fontColor(this.confirmTitleColor) .fontColor(this.confirmTitleColor)
}.width('45%').height(30).backgroundColor(this.confirmColor) }.width('45%').height(30).backgroundColor(this.confirmColor)
.onClick(() => { .onClick(() => {
this.selectedButton(1) if (ChangeUtil.stringIsUndefinedAndNull(this.message)) {
if (ChangeUtil.stringIsUndefinedAndNull(this.inputContent)) {
promptAction.showToast({ message: '请输入您的登录密码', duration: 1000 })
return
}
this.selectedButtonAndContent(1,this.inputContent)
} else {
this.selectedButton(1)
}
}) })
}.margin({ top: 20, bottom: 20 }) }.margin({ top: 20, bottom: 20 })
} }
@@ -7,7 +7,7 @@ export struct PerfactInputSheet {
@Prop inputTitle:string = ''; @Prop inputTitle:string = '';
@Prop inputPlaceholder:string = '' @Prop inputPlaceholder:string = ''
@State inputText:string = '' @State inputText:string = ''
@State style:string = '0'//默认类型为注册时候的弹窗//1为普通确认取消输入框//2未普通弹框 @State style:string = '0'//默认类型为注册时候的弹窗//1为普通确认取消输入框//2未普通弹框//3为只有一个确定按钮的弹窗
// 添加回调函数属性 // 添加回调函数属性
@State okColor:ResourceStr='#000000' @State okColor:ResourceStr='#000000'
@State cancelColor:ResourceStr='#333333' @State cancelColor:ResourceStr='#333333'
@@ -195,6 +195,63 @@ export struct PerfactInputSheet {
}.margin({ top: 10, bottom: 10 }).width('100%')
}
.width('80%').backgroundColor(Color.White)
}.borderRadius(24)
}
else if(this.style=='3')
{
Row(){
Column() {
Text(this.inputTitle)
.fontSize(18)
.padding(20)
.width('100%')
.textAlign(TextAlign.Center)
.fontColor('#444444')
Row(){
Text(this.inputPlaceholder)
.fontColor('#444444')
.backgroundColor(Color.White)
.padding({left:10,right:10,bottom:20})
}
.backgroundColor(Color.White)
Text('').height(1).width('100%')
.backgroundColor($r('app.color.home_gray')).margin({top:10})
Row() {
// Text(this.cancelText)
// .fontSize(15)
// .fontColor(this.cancelColor)
// .textAlign(TextAlign.Center)
//
// .height(30).layoutWeight(1)
// .onClick(() => {
// if(this.needcancelCallBack)
// {
// this.inputCallBack(this.inputText, 'needcancelCallBack');
// }
// this.controller.close()
// })
// Text('').height(30).width(1)
// .backgroundColor($r('app.color.home_gray'))
Text(this.okText)
.textAlign(TextAlign.Center)
.fontColor(this.okColor)
.fontSize(15)
.onClick(() => {
this.controller.close()
this.inputCallBack(this.inputText, this.inputTitle);
})
.height(30).layoutWeight(1)
}.margin({ top: 10, bottom: 10 }).width('100%') }.margin({ top: 10, bottom: 10 }).width('100%')
} }
.width('80%').backgroundColor(Color.White) .width('80%').backgroundColor(Color.White)
@@ -0,0 +1,107 @@
import { TagList, TagListModel } from "../models/TagListModel";
import { promptAction } from "@kit.ArkUI";
import { HashMap } from "@kit.ArkTS";
import { BusinessError } from "@kit.BasicServicesKit";
import { hdHttp, HdResponse } from "../utils/request";
import { BasicConstant } from "../constants/BasicConstant";
@Component
export struct ScreeningView {
@State tags:TagList[] = []
@Prop selectedArray:TagList[] = []
@Prop type:string = '0'
private isSelectedItem?: (value: TagList[]) => void;
aboutToAppear(): void {
let hashMap: HashMap<string, string> = new HashMap();
hdHttp.post<string>(BasicConstant.tagList,{
type:this.type
} as TagModel).then(async (res: HdResponse<string>) => {
let json:TagListModel = JSON.parse(res+'') as TagListModel;
if (json.code == '1') {
this.tags = json.data
}
}).catch((err: BusinessError) => {
console.error('Response tagList'+err)
})
}
build() {
Column() {
Grid() {
ForEach(this.tags, (item: TagList, index: number) => {
GridItem() {
Text(item.NAME)
.height(25)
.width('100%')
.padding({left:5,right:5})
.fontWeight(FontWeight.Regular)
.fontSize(13)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontColor(this.selectedArray.some(selected=>selected.ID === item.ID)?$r('app.color.top_title'): $r('app.color.999999'))
// .fontColor(this.selectedArray.indexOf(item)!=-1 ? $r('app.color.top_title'): $r('app.color.999999'))
.textAlign(TextAlign.Center)
.border({ width: 1, color:this.selectedArray.some(selected=>selected.ID === item.ID)? $r('app.color.top_title'): '#999999' })
.borderRadius(30)
.onClick(() => {
const targetIndex = this.selectedArray.findIndex(
selected => selected.ID === item.ID
);
if(targetIndex !== -1) {
const newSelected = [...this.selectedArray];
newSelected.splice(targetIndex, 1);
this.selectedArray = newSelected;
} else {
if (this.selectedArray.length >= 3) {
promptAction.showToast({ message: '最多选择三个标签!', duration: 1000 });
return;
}
this.selectedArray.push(item)
}
})
}
})
}
.padding(10)
.columnsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(10)
.backgroundColor(Color.White)
.height('100%')
.width('100%')
.layoutWeight(1)
Row() {
Text('重置').height(40)
.textAlign(TextAlign.Center)
.fontColor($r('app.color.top_title') )
.border({ width: 1, color: $r('app.color.top_title') })
.borderRadius(5)
.layoutWeight(1)
.margin({left:15})
.onClick(()=>{
this.selectedArray= [];
})
Text('确定')
.height(40)
.fontColor(Color.White).textAlign(TextAlign.Center)
.border({ width: 1, color: $r('app.color.top_title') })
.borderRadius(5)
.layoutWeight(1)
.backgroundColor($r('app.color.top_title'))
.margin({left:10,right:15})
.onClick(()=>{
this.isSelectedItem?.(this.selectedArray)
})
}.width('100%')
.height(60)
.backgroundColor(Color.White)
}
.width('100%')
.height('100%')
}
}
interface TagModel {
type:string
}
@@ -60,6 +60,7 @@ export struct HdGrid {
.fontColor(Color.Gray) .fontColor(Color.Gray)
.fontSize($r('app.float.hd_list_load_font')) .fontSize($r('app.float.hd_list_load_font'))
} }
.visibility(Visibility.None)
.height($r('app.float.hd_list_load_height')) .height($r('app.float.hd_list_load_height'))
.width('100%') .width('100%')
.justifyContent(FlexAlign.Center) .justifyContent(FlexAlign.Center)
@@ -22,8 +22,12 @@ export struct HdNav {
@Prop @Prop
rightIcon: ResourceStr = $r('sys.media.ohos_ic_public_more') rightIcon: ResourceStr = $r('sys.media.ohos_ic_public_more')
@Prop @Prop
twoRightIcon: ResourceStr = $r('sys.media.ohos_ic_public_more')
@Prop
showRightIcon: boolean = true showRightIcon: boolean = true
@Prop @Prop
showTwoRightItem: boolean = false
@Prop
showLeftIcon: boolean = true showLeftIcon: boolean = true
@Prop @Prop
showRightText: boolean = false showRightText: boolean = false
@@ -41,6 +45,8 @@ export struct HdNav {
isLeftAction: boolean = false isLeftAction: boolean = false
// 添加右侧点击函数 // 添加右侧点击函数
private rightItemAction:()=> void = () => {}; private rightItemAction:()=> void = () => {};
// 添加右侧第二个按钮的点击函数
private twoRightItemAction:()=> void = () => {};
// 添加左侧点击函数 // 添加左侧点击函数
private leftItemAction:()=> void = () => {}; private leftItemAction:()=> void = () => {};
@@ -74,20 +80,29 @@ export struct HdNav {
this.titleBuilder() this.titleBuilder()
} }
} }
.margin({left:this.showTwoRightItem?20:0})
.height(56) .height(56)
.width(150) .width(150)
.layoutWeight(1) .layoutWeight(1)
if (this.showRightIcon) { if (this.showRightIcon) {
Row() Row() {
{
Image(this.rightIcon) Image(this.rightIcon)
.size({ width: 24, height: 24 }) .size({ width: 24, height: 24 })
.objectFit(ImageFit.Contain) .objectFit(ImageFit.Contain)
.bindMenu(this.menuBuilder) .bindMenu(this.menuBuilder)
.onClick(()=>this.rightItemAction()) .onClick(()=>this.rightItemAction())
if (this.showTwoRightItem) {
Image(this.twoRightIcon)
.margin({left:15})
.size({ width: 24, height: 24 })
.objectFit(ImageFit.Contain)
.bindMenu(this.menuBuilder)
.onClick(()=>this.twoRightItemAction())
}
} }
.size({ width: 50, height: 50 }).justifyContent(FlexAlign.End) .size(this.showTwoRightItem?{width:70,height:50}:{ width: 50, height: 50 })
.justifyContent(FlexAlign.End)
} else if (this.showRightText) { } else if (this.showRightText) {
Text(this.rightText) Text(this.rightText)
@@ -13,7 +13,8 @@ export class BasicConstant {
static readonly urlExpert = "https://dev-app.igandan.com/app/expert/" static readonly urlExpert = "https://dev-app.igandan.com/app/expert/"
static readonly wxUrl = "https://dev-wx.igandan.com/"; static readonly wxUrl = "https://dev-wx.igandan.com/";
static readonly polvId = "11";//保利威视学员id static readonly polvId = "11";//保利威视学员id
static readonly urlApp="https://dev-app.igandan.com/app/"
static readonly expertPay = "https://dev-app.igandan.com/app/expertPay/"
//正式环境 //正式环境
// static readonly urlExpertAPI = "https://app.igandan.com/app/expertAPI/"; // static readonly urlExpertAPI = "https://app.igandan.com/app/expertAPI/";
// static readonly urlExpertApp = "http://app.igandan.com/app/expertApp/" // static readonly urlExpertApp = "http://app.igandan.com/app/expertApp/"
@@ -22,10 +23,14 @@ export class BasicConstant {
// static readonly urlExpert = "http://app.igandan.com/app/expert/" // static readonly urlExpert = "http://app.igandan.com/app/expert/"
// static readonly wxUrl = "https://wx.igandan.com/";// 微信服务器地址 // static readonly wxUrl = "https://wx.igandan.com/";// 微信服务器地址
// static readonly polvId = "21";//保利威视学员id // static readonly polvId = "21";//保利威视学员id
// static readonly urlApp="http://app.igandan.com/app/"
// static readonly expertPay = "https://app.igandan.com/app/expertPay/"
static readonly getSystemTimeStamp = BasicConstant.urlImage+'manager/getSystemTimeStamp' static readonly getSystemTime = BasicConstant.urlApp + 'manager/' + 'getSystemTime'
static readonly getSystemTimeStamp = BasicConstant.urlApp+'manager/getSystemTimeStamp'
static readonly addBonusPoints = BasicConstant.urlExpertApp+'addBonusPoints' static readonly addBonusPoints = BasicConstant.urlExpertApp+'addBonusPoints'
static readonly indexV2 = BasicConstant.urlExpertAPI+'indexV2';//首页轮播 static readonly indexV2 = BasicConstant.urlExpertAPI+'indexV2';//首页轮播
static readonly getAppActivity = BasicConstant.urlExpertAPI+'getAppActivity'
static readonly applyList = BasicConstant.urlExpert+'applyList' static readonly applyList = BasicConstant.urlExpert+'applyList'
static readonly groupList = BasicConstant.urlExpertApp+'groupList' static readonly groupList = BasicConstant.urlExpertApp+'groupList'
static readonly isMaiLanExpert = BasicConstant.urlExpertAPI+'isMaiLanExpert' static readonly isMaiLanExpert = BasicConstant.urlExpertAPI+'isMaiLanExpert'
@@ -70,8 +75,33 @@ export class BasicConstant {
static readonly deleteComment = BasicConstant.urlExpertApp+'deleteComment';//删除评论 static readonly deleteComment = BasicConstant.urlExpertApp+'deleteComment';//删除评论
static readonly meetingHistoryList = BasicConstant.urlExpertAPI + "meetingHistoryList"; static readonly meetingHistoryList = BasicConstant.urlExpertAPI + "meetingHistoryList";
static readonly videoRoll = BasicConstant.urlExpertAPI + "videoRoll"; static readonly videoRoll = BasicConstant.urlExpertAPI + "videoRoll";
static readonly newsRollNew = BasicConstant.urlExpert + 'newsRollNew'
static readonly newsTagList = BasicConstant.urlExpert + 'newsTagList'
static readonly newsListNew = BasicConstant.urlExpert + 'newsListNew'
static readonly defaultNewsListNew = BasicConstant.urlExpert + 'defaultNewsListNew'
static readonly expertVideoTypeList = BasicConstant.urlExpertAPI + "expertVideoTypeList"; static readonly expertVideoTypeList = BasicConstant.urlExpertAPI + "expertVideoTypeList";
static readonly patientVideoNew = BasicConstant.urlExpertApp + 'patientVideoNew';
static readonly videoByKeyWordsNew = BasicConstant.urlExpertApp + "videoByKeyWordsNew"; static readonly videoByKeyWordsNew = BasicConstant.urlExpertApp + "videoByKeyWordsNew";
static readonly patientVideoByKeyWordsNew = BasicConstant.urlExpertApp + 'patientVideoByKeyWordsNew'
static readonly feedBack = BasicConstant.urlExpert+'feedBack'
static readonly ganDanFileByKeyWords = BasicConstant.urlExpertAPI+'ganDanFileByKeyWords'//肝胆课件
static readonly getKePuCollection = BasicConstant.urlExpert+'getKePuCollection'
static readonly discollection = BasicConstant.urlExpert + 'discollection'
static readonly collection = BasicConstant.urlExpert + 'collection'
static readonly disagree = BasicConstant.urlExpert + 'disagree'
static readonly agree = BasicConstant.urlExpert + 'agree'
static readonly ganDanFileDetials = BasicConstant.urlExpertAPI + 'ganDanFileDetials'
static readonly newsDetial = BasicConstant.urlExpert + 'newsDetial'
static readonly createGanDanFileOrder = BasicConstant.expertPay + 'createGanDanFileOrder'
static readonly getBalance = BasicConstant.expertPay + 'getBalance'
static readonly payGanDanFileOrder = BasicConstant.expertPay + 'payGanDanFileOrder'
static readonly getOrderStatus = BasicConstant.expertPay + 'getOrderStatus'
static readonly useWelfareNum = BasicConstant.urlExpertAPI + 'useWelfareNum'
static readonly allXinyiList = BasicConstant.expertPay + 'allXinyiList'
static readonly xinyiPrice = BasicConstant.expertPay + 'xinyiPrice'
static readonly createXinYiOrder = BasicConstant.expertPay + 'createXinYiOrder'
static readonly payXinYiOrder = BasicConstant.expertPay + 'payXinYiOrder'
static readonly getFlowerList = BasicConstant.expertPay + 'getFlowerList'
static readonly tagList = BasicConstant.urlExpertApp + "tagList"; static readonly tagList = BasicConstant.urlExpertApp + "tagList";
static readonly meetingListBySearch = BasicConstant.urlExpertAPI + "meetingListBySearch"; static readonly meetingListBySearch = BasicConstant.urlExpertAPI + "meetingListBySearch";
static readonly videoBySearchNew = BasicConstant.urlExpertApp+'videoBySearchNew';//搜索肝胆视频列表 static readonly videoBySearchNew = BasicConstant.urlExpertApp+'videoBySearchNew';//搜索肝胆视频列表
@@ -118,6 +148,7 @@ export class BasicConstant {
static readonly addWorkPlace= BasicConstant.urlExpertApp+"addWorkPlace";//执业地点-增加 static readonly addWorkPlace= BasicConstant.urlExpertApp+"addWorkPlace";//执业地点-增加
static readonly editWorkPlace= BasicConstant.urlExpertApp+"editWorkPlace";//执业地点-修改 static readonly editWorkPlace= BasicConstant.urlExpertApp+"editWorkPlace";//执业地点-修改
static readonly addInterrogationAnswer= BasicConstant.urlExpertAPI+"addInterrogationAnswer";// 一问多答 回答 static readonly addInterrogationAnswer= BasicConstant.urlExpertAPI+"addInterrogationAnswer";// 一问多答 回答
static readonly checkUser = BasicConstant.urlExpertAPI+'checkUser'
static readonly province=['全国','北京市','天津市','河北省','山西省' static readonly province=['全国','北京市','天津市','河北省','山西省'
,'内蒙古自治区','辽宁省','吉林省','黑龙江省','上海市','江苏省','浙江省' ,'内蒙古自治区','辽宁省','吉林省','黑龙江省','上海市','江苏省','浙江省'
,'安徽省','福建省','江西省','山东省','河南省','湖北省','湖南省','广东省', ,'安徽省','福建省','江西省','山东省','河南省','湖北省','湖南省','广东省',
@@ -128,6 +159,8 @@ export class BasicConstant {
static readonly notification_back_refreshData = 250529;//返回上页通知刷新数据 static readonly notification_back_refreshData = 250529;//返回上页通知刷新数据
//首页tabContent切换事件通知 //首页tabContent切换事件通知
static readonly notification_home_tab_change = 25060413; static readonly notification_home_tab_change = 25060413;
//课件支付成功返回上一页下载科技
static readonly notification_kejian_pay_success = 202509041128;
static readonly YX_accid='YX_accid'//云信 static readonly YX_accid='YX_accid'//云信
@@ -145,5 +178,6 @@ export class BasicConstant {
static readonly consult = "consult"; static readonly consult = "consult";
static readonly ExpertAesKey = 'deoep09_klodLdAo' static readonly ExpertAesKey = 'deoep09_klodLdAo'
static readonly EXPERT_HCP_TOKEN = 'hcp_token'
} }
@@ -0,0 +1,2 @@
export const APP_ID = "wxbf3658f5e674667c"
export const APP_SECRET = "c4505a04a9910c65efea8e11ffc93f92"
@@ -0,0 +1,51 @@
// 文件信息接口
export interface FileInfo {
fileId: string;
fileName: string;
fileType: string;
fileSize: number;
fileUrl: string;
filePath?: string;
downloadStatus: 'pending' | 'downloading' | 'downloaded' | 'failed' | 'cancelled';
downloadProgress: number;
createTime: number;
updateTime: number;
}
// 下载进度回调
export interface DownloadCallback {
onProgress?: (progress: number) => void;
onSuccess?: (filePath: string) => void;
onFailed?: (error: string) => void;
onCancelled?: () => void;
}
// 文件预览配置
export interface PreviewConfig {
enableCache?: boolean;
maxCacheSize?: number;
supportedTypes?: string[];
}
// 定义下载状态枚举
export enum DownloadStatus {
PENDING = 'pending', // 等待中
DOWNLOADING = 'downloading', // 下载中
PAUSED = 'paused', // 已暂停
COMPLETED = 'completed', // 已完成
FAILED = 'failed', // 失败
}
// 定义文件项接口
export interface DownloadFileItem {
id: string; // 文件唯一标识
name: string; // 文件名
url: string; // 下载地址
localPath: string; // 本地存储路径
size: number; // 文件大小(字节)
downloadedSize: number; // 已下载大小(字节)
status: DownloadStatus; // 下载状态
createTime: number; // 创建时间
finishTime?: number; // 完成时间
error?: string; // 错误信息(失败时)
}
@@ -52,7 +52,9 @@ export interface Data{
state:number; state:number;
realName:string; realName:string;
specialy:Array<object>; specialy:Array<object>;
special:SpecialDisease[];
YX_accid:string;
YX_token:string
} }
export interface BaseBean{ export interface BaseBean{
@@ -0,0 +1,44 @@
import * as wxopensdk from '@tencent/wechat_open_sdk';
import { APP_ID } from '../constants/Constants';
export type OnWXReq = (req: wxopensdk.BaseReq) => void
export type OnWXResp = (resp: wxopensdk.BaseResp) => void
const kTag = "WXApiEventHandlerImpl"
class WXApiEventHandlerImpl implements wxopensdk.WXApiEventHandler {
private onReqCallbacks: Map<OnWXReq, OnWXReq> = new Map
private onRespCallbacks: Map<OnWXResp, OnWXResp> = new Map
registerOnWXReqCallback(on: OnWXReq) {
this.onReqCallbacks.set(on, on)
}
unregisterOnWXReqCallback(on: OnWXReq) {
this.onReqCallbacks.delete(on)
}
registerOnWXRespCallback(on: OnWXResp) {
this.onRespCallbacks.set(on, on)
}
unregisterOnWXRespCallback(on: OnWXResp) {
this.onRespCallbacks.delete(on)
}
onReq(req: wxopensdk.BaseReq): void {
wxopensdk.Log.i(kTag, "onReq:%s", JSON.stringify(req))
this.onReqCallbacks.forEach((on) => {
on(req)
})
}
onResp(resp: wxopensdk.BaseResp): void {
wxopensdk.Log.i(kTag, "onResp:%s", JSON.stringify(resp))
this.onRespCallbacks.forEach((on) => {
on(resp)
})
}
}
export const WXApi = wxopensdk.WXAPIFactory.createWXAPI(APP_ID)
export const WXEventHandler = new WXApiEventHandlerImpl
@@ -0,0 +1,11 @@
export interface customAttachment
{
gdxz_title:string;//标题
gdxz_url:string;//内容url
gdxz_content:string;//内容
gdxz_id:string;//id
gdxz_img:string;//照片
gdxz_type:string;//类型
gdxz_ext_data:string;//备用字段,json字符串
}
@@ -111,7 +111,7 @@ export class AESEncryptionDecryption {
globalResult = AESEncryptionDecryption.uint8ArrayToString(result.data); globalResult = AESEncryptionDecryption.uint8ArrayToString(result.data);
console.info('解密后的明文:' + globalResult); console.info('解密后的明文:' + globalResult);
} catch (err) { } catch (err) {
console.info(err.message); console.info('解密失败:',err.message);
} }
} }
@@ -3,6 +3,7 @@ import { BusinessError } from '@ohos.base'
import { KeyboardAvoidMode, window } from '@kit.ArkUI' import { KeyboardAvoidMode, window } from '@kit.ArkUI'
import { resourceManager } from '@kit.LocalizationKit' import { resourceManager } from '@kit.LocalizationKit'
import { common } from '@kit.AbilityKit' import { common } from '@kit.AbilityKit'
import display from '@ohos.display'
export class AppUtil { export class AppUtil {
@@ -236,6 +237,22 @@ export class AppUtil {
} }
/**
* display width
* @returns width in px
*/
static getDisplayWindowWidth(): Pixels {
return px(display.getDefaultDisplaySync().width)
}
/**
* display height
* @returns height in px
*/
static getDisplayWindowHeight(): Pixels {
return px(display.getDefaultDisplaySync().height)
}
/** /**
* 设置沉浸式状态栏 * 设置沉浸式状态栏
* @param isLayoutFullScreen 窗口的布局是否为沉浸式布局(该沉浸式布局状态栏、导航栏仍然显示)。true表示沉浸式布局;false表示非沉浸式布局。 * @param isLayoutFullScreen 窗口的布局是否为沉浸式布局(该沉浸式布局状态栏、导航栏仍然显示)。true表示沉浸式布局;false表示非沉浸式布局。
@@ -342,6 +359,29 @@ export class AppUtil {
AppUtil.getContext().terminateSelf(); AppUtil.getContext().terminateSelf();
AppUtil.getContext().getApplicationContext().killAllProcesses(); AppUtil.getContext().getApplicationContext().killAllProcesses();
} }
}
export function px(value: number) {
return new Pixels(value)
}
export class Pixels {
value: number;
constructor(value: number) {
this.value = value;
}
get px() {
return this.value
}
get vp() {
return px2vp(this.value)
}
toString() {
return `${this.value}px`
}
} }
@@ -10,7 +10,9 @@ import { i18n } from '@kit.LocalizationKit';
import { connection } from '@kit.NetworkKit'; import { connection } from '@kit.NetworkKit';
import http from '@ohos.net.http' import http from '@ohos.net.http'
import {BasicConstant} from '../constants/BasicConstant' import {BasicConstant} from '../constants/BasicConstant'
import { componentUtils } from '@kit.ArkUI'; import { componentUtils, router } from '@kit.ArkUI';
import { authStore } from './auth';
import { preferenceStore } from './PreferenceStore';
export class ChangeUtil { export class ChangeUtil {
/** /**
@@ -312,4 +314,32 @@ export class ChangeUtil {
const height = componentInfo.size.height//3.38 const height = componentInfo.size.height//3.38
return height return height
} }
static formatPrice(priceStr: string): string {
let priceInFen = parseFloat(priceStr);
let priceInYuan = priceInFen / 100;
return `${priceInYuan.toFixed(2)}`;
}
static formatPrice2(priceStr: string,discount: string): string {
let priceInFen = parseFloat(priceStr);
let priceInYuan = priceInFen / 100 * parseFloat(discount);
return `${priceInYuan.toFixed(2)}`;
}
static Logout(phone:string)
{
authStore.delUser();
preferenceStore.clear()
preferenceStore.setItemBoolean('isLogin',false)
preferenceStore.setItemBoolean('isFirstRun',false)
// router.back()
router.replaceUrl({
url: 'pages/LoginPage/LoginPage', // 目标url
params: {
phone:phone
}
},router.RouterMode.Single)
router.clear()
}
} }
@@ -0,0 +1,366 @@
import fileIO from '@ohos.fileio';
import request from '@ohos.request';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
/**
* 文件下载状态枚举
*/
export enum DownloadStatus {
DOWNLOADING = 'downloading',
COMPLETED = 'completed',
FAILED = 'failed'
}
/**
* 文件信息接口
*/
export interface FileInfo {
id: string;
name: string;
url: string;
format: string;
size: number;
status: DownloadStatus;
localPath: string;
downloadTime: number;
}
/**
* 文件下载管理类
*/
export class FileDownloadManager {
private static instance: FileDownloadManager;
private downloadQueue: Map<string, FileInfo> = new Map();
private downloadTasks: Map<string, request.DownloadTask> = new Map();
private context: common.UIAbilityContext;
private downloadDir: string = '';
private constructor(context: common.UIAbilityContext) {
this.context = context;
this.initDownloadDirectory();
}
/**
* 获取单例实例
*/
public static getInstance(context: common.UIAbilityContext): FileDownloadManager {
if (!FileDownloadManager.instance) {
FileDownloadManager.instance = new FileDownloadManager(context);
}
return FileDownloadManager.instance;
}
/**
* 初始化下载目录
*/
private async initDownloadDirectory(): Promise<void> {
try {
const filesDir = this.context.filesDir;
this.downloadDir = `${filesDir}/downloads`;
// 检查并创建下载目录
try {
await fileIO.access(this.downloadDir);
} catch {
await fileIO.mkdir(this.downloadDir);
}
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '初始化下载目录失败: %{public}s', String(error));
}
}
/**
* 开始下载文件
*/
public async downloadFile(url: string,fileId:string, fileName: string, fileType:string): Promise<string> {
try {
// 从URL中提取文件格式
const format = this.extractFileFormat(url);
// 生成文件名
const finalFileName = `${fileName}/${fileId}.${fileType}`;
// 创建文件信息
const fileInfo: FileInfo = {
id: fileId,
name: finalFileName,
url: url,
format: format,
size: 0,
status: DownloadStatus.DOWNLOADING,
localPath: `${this.downloadDir}/${finalFileName}`,
downloadTime: Date.now()
};
// 添加到下载队列
this.downloadQueue.set(fileId, fileInfo);
// 开始下载
await this.startDownload(fileInfo);
return fileId;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '下载文件失败: %{public}s', String(error));
throw new Error(String(error));
}
}
/**
* 开始下载任务
*/
private async startDownload(fileInfo: FileInfo): Promise<void> {
try {
const config: request.DownloadConfig = {
url: fileInfo.url,
title: fileInfo.name,
description: '文件下载中...',
filePath: fileInfo.localPath,
header: {
'User-Agent': 'HarmonyOS File Downloader'
}
};
const downloadTask = await request.downloadFile(this.context, config);
this.downloadTasks.set(fileInfo.id, downloadTask);
// 监听下载进度
downloadTask.on('progress', (receivedSize: number, totalSize: number) => {
fileInfo.size = totalSize;
this.updateFileInfo(fileInfo);
});
// 监听下载完成
downloadTask.on('complete', () => {
fileInfo.status = DownloadStatus.COMPLETED;
this.updateFileInfo(fileInfo);
this.downloadTasks.delete(fileInfo.id);
});
// 监听下载失败
downloadTask.on('fail', (err: number) => {
fileInfo.status = DownloadStatus.FAILED;
this.updateFileInfo(fileInfo);
this.downloadTasks.delete(fileInfo.id);
hilog.error(0x0000, 'FileDownloadManager', '下载失败: %{public}d', err);
});
} catch (error) {
fileInfo.status = DownloadStatus.FAILED;
this.updateFileInfo(fileInfo);
throw new Error(String(error));
}
}
/**
* 更新文件信息
*/
private updateFileInfo(fileInfo: FileInfo): void {
this.downloadQueue.set(fileInfo.id, fileInfo);
}
/**
* 从URL提取文件格式
*/
private extractFileFormat(url: string): string {
const supportedFormats = ['docx', 'txt', 'ppt', 'pdf', 'doc', 'pptx', 'xls', 'xlsx'];
const urlParts = url.split('.');
const format = urlParts[urlParts.length - 1].toLowerCase();
return supportedFormats.includes(format) ? format : 'bin';
}
/**
* 获取下载状态
*/
public getDownloadStatus(fileId: string): DownloadStatus | null {
const fileInfo = this.downloadQueue.get(fileId);
return fileInfo ? fileInfo.status : null;
}
/**
* 获取文件信息
*/
public getFileInfo(fileId: string): FileInfo | null {
return this.downloadQueue.get(fileId) || null;
}
/**
* 获取所有文件信息
*/
public getAllFiles(): FileInfo[] {
return Array.from(this.downloadQueue.values());
}
/**
* 获取指定状态的文件
*/
public getFilesByStatus(status: DownloadStatus): FileInfo[] {
return Array.from(this.downloadQueue.values()).filter(file => file.status === status);
}
/**
* 查看单个文件
*/
public async viewFile(fileId: string): Promise<boolean> {
try {
const fileInfo = this.downloadQueue.get(fileId);
if (!fileInfo || fileInfo.status !== DownloadStatus.COMPLETED) {
throw new Error('文件不存在或下载未完成');
}
// 检查文件是否存在
try {
await fileIO.access(fileInfo.localPath);
} catch {
throw new Error('文件不存在');
}
// 获取文件信息
const stat = await fileIO.stat(fileInfo.localPath);
hilog.info(0x0000, 'FileDownloadManager', '文件信息: %{public}s', JSON.stringify(stat));
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '查看文件失败: %{public}s', String(error));
return false;
}
}
/**
* 查看所有文件
*/
public async viewAllFiles(): Promise<FileInfo[]> {
try {
const allFiles = this.getAllFiles();
const completedFiles: FileInfo[] = [];
for (const file of allFiles) {
if (file.status === DownloadStatus.COMPLETED) {
try {
await fileIO.access(file.localPath);
completedFiles.push(file);
} catch {
// 文件不存在,跳过
}
}
}
return completedFiles;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '查看所有文件失败: %{public}s', String(error));
return [];
}
}
/**
* 删除单个文件
*/
public async deleteFile(fileId: string): Promise<boolean> {
try {
const fileInfo = this.downloadQueue.get(fileId);
if (!fileInfo) {
throw new Error('文件不存在');
}
// 取消下载任务
const downloadTask = this.downloadTasks.get(fileId);
if (downloadTask) {
downloadTask.off('progress');
downloadTask.off('complete');
downloadTask.off('fail');
this.downloadTasks.delete(fileId);
}
// 删除本地文件
if (fileInfo.status === DownloadStatus.COMPLETED) {
try {
await fileIO.unlink(fileInfo.localPath);
} catch {
// 文件可能不存在,忽略错误
}
}
// 从队列中移除
this.downloadQueue.delete(fileId);
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '删除文件失败: %{public}s', String(error));
return false;
}
}
/**
* 删除所有文件
*/
public async deleteAllFiles(): Promise<boolean> {
try {
const allFiles = Array.from(this.downloadQueue.keys());
for (const fileId of allFiles) {
await this.deleteFile(fileId);
}
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '删除所有文件失败: %{public}s', String(error));
return false;
}
}
/**
* 暂停下载
*/
public pauseDownload(fileId: string): boolean {
const downloadTask = this.downloadTasks.get(fileId);
if (downloadTask) {
downloadTask.off('progress');
downloadTask.off('complete');
downloadTask.off('fail');
this.downloadTasks.delete(fileId);
return true;
}
return false;
}
/**
* 恢复下载
*/
public async resumeDownload(fileId: string): Promise<boolean> {
try {
const fileInfo = this.downloadQueue.get(fileId);
if (!fileInfo || fileInfo.status !== DownloadStatus.DOWNLOADING) {
return false;
}
await this.startDownload(fileInfo);
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '恢复下载失败: %{public}s', String(error));
return false;
}
}
/**
* 清理下载目录
*/
// public async cleanDownloadDirectory(): Promise<boolean> {
// try {
// // 使用fileIO.listDir替代readdir
// const files = await fileIO.listDir(this.downloadDir);
// for (const file of files) {
// const filePath = `${this.downloadDir}/${file}`;
// try {
// await fileIO.unlink(filePath);
// } catch {
// // 忽略删除失败的文件
// }
// }
// return true;
// } catch (error) {
// hilog.error(0x0000, 'FileDownloadManager', '清理下载目录失败: %{public}s', String(error));
// return false;
// }
// }
}
@@ -0,0 +1,799 @@
import { http } from '@kit.NetworkKit';
import { promptAction, router } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import { fileIo, fileUri } from '@kit.CoreFileKit';
import { FileInfo,DownloadCallback } from '../models/FileModel'
import preferences from '@ohos.data.preferences';
import { Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { filePreview } from '@kit.PreviewKit';
import request from '@ohos.request';
// 下载任务接口
export interface DownloadTask {
httpRequest: http.HttpRequest;
filePath: string;
}
// 文件统计信息接口
export interface FileStat {
size: number;
ctime: number;
mtime: number;
}
/**
* 文件管理类 - 提供下载、查询、预览等功能
*/
export class FileManager {
private context: common.Context;
private downloadTasks: Map<string, DownloadTask> = new Map();
private downloadCallbacks: Map<string, DownloadCallback> = new Map();
private fileCache: Map<string, FileInfo> = new Map();
private downloadDir: string;
private isDownloading: boolean = false;
private preferencesHelper: preferences.Preferences | null = null;
private readonly STORE_NAME = 'file_manager_store';
private readonly FILE_INFO_KEY = 'file_info_cache';
constructor(context: common.Context) {
this.context = context;
this.downloadDir = `${this.context.filesDir}/downloads`;
this.initDownloadDirectory();
this.initPreferences();
this.loadFileInfoFromStorage();
}
/**
* 初始化偏好设置存储
*/
private async initPreferences(): Promise<void> {
try {
this.preferencesHelper = await preferences.getPreferences(this.context, this.STORE_NAME);
} catch (error) {
console.error('初始化偏好设置失败:', error);
}
}
/**
* 从存储加载文件信息
*/
private async loadFileInfoFromStorage(): Promise<void> {
try {
if (this.preferencesHelper) {
const fileInfoJson = await this.preferencesHelper.get(this.FILE_INFO_KEY, '[]');
const fileInfoArray: FileInfo[] = JSON.parse(fileInfoJson as string);
// 恢复缓存
for (const fileInfo of fileInfoArray) {
this.fileCache.set(fileInfo.fileId, fileInfo);
}
console.info(`从存储加载了 ${fileInfoArray.length} 个文件信息`);
}
} catch (error) {
console.error('加载文件信息失败:', error);
}
}
/**
* 保存文件信息到存储
*/
private async saveFileInfoToStorage(): Promise<void> {
try {
if (this.preferencesHelper) {
const fileInfoArray = Array.from(this.fileCache.values());
const fileInfoJson = JSON.stringify(fileInfoArray);
await this.preferencesHelper.put(this.FILE_INFO_KEY, fileInfoJson);
await this.preferencesHelper.flush();
}
} catch (error) {
console.error('保存文件信息失败:', error);
}
}
/**
* 初始化下载目录
*/
private async initDownloadDirectory(): Promise<void> {
try {
if (!fileIo.accessSync(this.downloadDir)) {
fileIo.mkdirSync(this.downloadDir);
}
} catch (error) {
console.error('创建下载目录失败:', error);
}
}
/**
* 下载文件
* @param fileInfo 文件信息
* @param callback 下载回调
* @returns Promise<boolean>
*/
async downloadFile(fileInfo: FileInfo, callback?: DownloadCallback): Promise<boolean> {
try {
// 检查是否正在下载
if (this.downloadTasks.has(fileInfo.fileId)) {
promptAction.showToast({ message: '文件正在下载中,请勿重复操作', duration: 2000 });
return false;
}
// 检查文件是否已下载
if (await this.isFileDownloaded(fileInfo.fileId)) {
promptAction.showToast({ message: '文件已下载', duration: 2000 });
return true;
}
// 设置下载状态
this.isDownloading = true;
fileInfo.downloadStatus = 'downloading';
fileInfo.downloadProgress = 0;
// 保存回调
if (callback) {
this.downloadCallbacks.set(fileInfo.fileId, callback);
}
// 创建下载任务
const downloadTask = await this.createDownloadTask(fileInfo);
this.downloadTasks.set(fileInfo.fileId, downloadTask);
// 开始下载
const success = await this.startDownload(downloadTask, fileInfo);
if (success) {
fileInfo.downloadStatus = 'downloaded';
fileInfo.downloadProgress = 100;
this.onDownloadSuccess(fileInfo);
} else {
fileInfo.downloadStatus = 'failed';
this.onDownloadFailed(fileInfo, '下载失败');
}
return success;
} catch (error) {
console.error('下载文件失败:', error);
fileInfo.downloadStatus = 'failed';
this.onDownloadFailed(fileInfo, '下载失败');
return false;
} finally {
this.isDownloading = false;
this.downloadTasks.delete(fileInfo.fileId);
}
}
/**
* 创建下载任务
*/
private async createDownloadTask(fileInfo: FileInfo): Promise<DownloadTask> {
try {
// 创建文件名目录
const fileNameDir = `${fileInfo.fileName}`;
const fileNameDirPath = `${this.downloadDir}/${fileNameDir}`;
// 确保目录存在
if (!fileIo.accessSync(fileNameDirPath)) {
fileIo.mkdirSync(fileNameDirPath);
}
// 设置文件保存路径 - 格式:文件名/文件ID.文件格式
const fileName = `${fileInfo.fileId}.${fileInfo.fileType}`;
const filePath = `${fileNameDirPath}/${fileName}`;
fileInfo.filePath = filePath;
// 创建HTTP请求
const httpRequest = http.createHttp();
const task: DownloadTask = {
httpRequest: httpRequest,
filePath: filePath
};
return task;
} catch (error) {
console.error('创建下载任务失败:', error);
throw new Error('创建下载任务失败');
}
}
/**
* 开始下载
*/
private async startDownload(downloadTask: DownloadTask, fileInfo: FileInfo): Promise<boolean> {
//
// let contexta = getContext(this) as common.UIAbilityContext;
// request.downloadFile(contexta.getApplicationContext(), {
// url: fileInfo.fileUrl,
// filePath: downloadTask.filePath,
// enableMetered:true
// }).then((downloadTask: request.DownloadTask) => {
// let progresCallback = (receivedSize: number, totalSize: number) => {
// console.info("downloadddd1 receivedSize:" + receivedSize + " totalSize:" + totalSize);
// };
// let pauseCallback = () => {
// console.info('Downloadddd task pause.');
// };
// //开启进度回调
// downloadTask.on('progress', progresCallback);
// //开启暂停回调
// downloadTask.on('pause', pauseCallback);
// //开启下载完成回调
// downloadTask.on('complete', async () => {
// const taskInfo = await downloadTask.getTaskInfo();
// console.info('downloaddddTask1 complete:'+`status: ${taskInfo.status}`);
// // downloadTask.getTaskInfo();
//
// })
// return true
// }).catch((err: BusinessError) => {
// console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
// return false;
// })
try {
const httpRequest = downloadTask.httpRequest;
const filePath = downloadTask.filePath;
// 配置下载选项 - 参考iOS代码的请求头设置
const options: http.HttpRequestOptions = {
method: http.RequestMethod.GET,
header: {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HarmonyOS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
'Accept-Language': 'zh-CN,zh;q=0.9',
},
expectDataType: http.HttpDataType.ARRAY_BUFFER,
usingCache: false,
connectTimeout: 60000,
readTimeout: 300000
};
console.info(`开始下载文件: ${fileInfo.fileUrl}`);
console.info(`目标路径: ${filePath}`);
// 发起下载请求
const response = await httpRequest.request(fileInfo.fileUrl, options);
console.info(`响应状态码: ${response.responseCode}`);
console.info(`响应头: ${JSON.stringify(response.header)}`);
if (response.responseCode === 200) {
try {
// 验证响应数据
if (!response.result) {
console.error('下载响应数据为空');
return false;
}
// 检查数据类型和大小
if (!(response.result instanceof ArrayBuffer)) {
console.error('响应数据不是ArrayBuffer类型:', typeof response.result);
return false;
}
const dataSize = response.result.byteLength;
console.info(`下载数据大小: ${dataSize} 字节`);
if (dataSize <= 10) {
console.error('文件有误:',response.result)
return false
}
// 确保目标目录存在
const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
if (!fileIo.accessSync(dirPath)) {
fileIo.mkdirSync(dirPath);
}
// 将下载的数据写入文件
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
try {
// 写入文件数据
const bytesWritten = fileIo.writeSync(file.fd, response.result);
console.info(`写入文件成功,写入字节数: ${bytesWritten}`);
// 验证写入的字节数
if (bytesWritten !== dataSize) {
console.error(`写入字节数不匹配: 期望 ${dataSize}, 实际 ${bytesWritten}`);
fileIo.closeSync(file);
fileIo.unlinkSync(filePath); // 删除损坏的文件
return false;
}
// 同步文件到磁盘
fileIo.fsyncSync(file.fd);
// 关闭文件
fileIo.closeSync(file);
// 验证文件是否创建成功
if (!fileIo.accessSync(filePath)) {
console.error('文件创建失败');
return false;
}
// 获取文件统计信息
const stat = fileIo.statSync(filePath);
console.info(`文件创建成功: ${filePath}, 大小: ${stat.size} 字节`);
// 验证文件大小
if (stat.size !== dataSize) {
console.error(`文件大小不匹配: 期望 ${dataSize}, 实际 ${stat.size}`);
fileIo.unlinkSync(filePath);
return false;
}
// 更新文件信息
fileInfo.fileSize = stat.size;
await this.saveFileInfo(fileInfo);
return true;
} catch (writeError) {
console.error('写入文件失败:', writeError);
fileIo.closeSync(file);
// 清理可能创建的文件
try {
if (fileIo.accessSync(filePath)) {
fileIo.unlinkSync(filePath);
}
} catch (cleanupError) {
console.error('清理文件失败:', cleanupError);
}
return false;
}
} catch (writeError) {
console.error('文件写入过程失败:', writeError);
return false;
}
} else {
console.error(`下载失败,状态码: ${response.responseCode}`);
return false;
}
} catch (error) {
console.error('下载执行失败:', error);
return false;
} finally {
// 销毁HTTP请求
downloadTask.httpRequest.destroy();
}
}
/**
* 取消下载
*/
async cancelDownload(fileId: string): Promise<boolean> {
try {
const task = this.downloadTasks.get(fileId);
if (task) {
// 销毁HTTP请求
task.httpRequest.destroy();
this.downloadTasks.delete(fileId);
// 更新状态
const fileInfo = this.fileCache.get(fileId);
if (fileInfo) {
fileInfo.downloadStatus = 'cancelled';
fileInfo.downloadProgress = 0;
this.onDownloadCancelled(fileInfo);
}
return true;
}
return false;
} catch (error) {
console.error('取消下载失败:', error);
return false;
}
}
/**
* 查询已下载文件
*/
async getDownloadedFiles(): Promise<FileInfo[]> {
try {
const files: FileInfo[] = [];
// 从缓存获取
for (const fileInfo of this.fileCache.values()) {
if (fileInfo.downloadStatus === 'downloaded') {
files.push(fileInfo);
}
}
// 从本地目录扫描
const localFiles = await this.scanLocalFiles();
files.push(...localFiles);
// 按时间排序
return files.sort((a, b) => b.updateTime - a.updateTime);
} catch (error) {
console.error('查询下载文件失败:', error);
return [];
}
}
/**
* 扫描本地文件
*/
private async scanLocalFiles(): Promise<FileInfo[]> {
try {
const files: FileInfo[] = [];
if (!fileIo.accessSync(this.downloadDir)) {
return files;
}
const entryList = fileIo.listFileSync(this.downloadDir);
for (let i = 0; i < entryList.length; i++) {
const entryName = entryList[i];
const entryPath = `${this.downloadDir}/${entryName}`;
// 优先按目录处理(当前下载结构是 按文件名建目录,目录下放置 文件ID.扩展名)
let childNames: string[] | null = null;
try {
childNames = fileIo.listFileSync(entryPath);
} catch (_e) {
childNames = null;
}
if (childNames && childNames.length > 0) {
// 目录:遍历子文件
for (let j = 0; j < childNames.length; j++) {
const childName = childNames[j];
const childPath = `${entryPath}/${childName}`;
try {
const childStat = fileIo.statSync(childPath);
const fileInfo = this.parseFileName(childName, childPath, childStat);
if (fileInfo) {
files.push(fileInfo);
}
} catch (childErr) {
console.error('读取子文件失败:', childErr);
}
}
} else {
// 非目录项(兼容旧结构,直接作为文件解析)
try {
const stat = fileIo.statSync(entryPath);
const fileInfo = this.parseFileName(entryName, entryPath, stat);
if (fileInfo) {
files.push(fileInfo);
}
} catch (fileErr) {
console.error('读取文件失败:', fileErr);
}
}
}
return files;
} catch (error) {
console.error('扫描本地文件失败:', error);
return [];
}
}
/**
* 解析文件名
*/
private parseFileName(fileName: string, filePath: string, stat: fileIo.Stat): FileInfo | null {
try {
// 新的文件名格式: 文件名/文件ID.扩展名
const pathParts = filePath.split('/');
if (pathParts.length < 2) return null;
const fileNameDir = pathParts[pathParts.length - 2]; // 文件名目录
const fileNameWithExt = pathParts[pathParts.length - 1]; // 文件ID.扩展名
const fileIdParts = fileNameWithExt.split('.');
if (fileIdParts.length < 2) return null;
const fileId = fileIdParts[0];
const extension = fileIdParts[1];
return {
fileId,
fileName: fileNameDir, // 使用目录名作为文件名
fileType: extension,
fileSize: stat.size,
fileUrl: '',
filePath: filePath,
downloadStatus: 'downloaded',
downloadProgress: 100,
createTime: stat.ctime,
updateTime: stat.mtime
};
} catch (error) {
console.error('解析文件名失败:', error);
return null;
}
}
/**
* 检查文件是否已下载
*/
private async isFileDownloaded(fileId: string): Promise<boolean> {
try {
// 检查缓存
const cachedFile = this.fileCache.get(fileId);
if (cachedFile && cachedFile.downloadStatus === 'downloaded') {
return true;
}
// 检查本地文件
const localFiles = await this.scanLocalFiles();
for (let i = 0; i < localFiles.length; i++) {
if (localFiles[i].fileId === fileId) {
return true;
}
}
return false;
} catch (error) {
console.error('检查文件下载状态失败:', error);
return false;
}
}
/**
* 预览文件 - 使用系统原生方法
*/
async previewFile(identifier: string) {
try {
// 1) 优先按 uuid 在缓存中查找
let target: FileInfo | null = null;
const cachedById = this.fileCache.get(identifier);
if (cachedById) {
target = cachedById;
}
// 2) 若未命中,再按文件名在缓存中查找(取最近更新)
if (!target) {
let latest: FileInfo | null = null;
for (const info of this.fileCache.values()) {
if (info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
// 3) 若缓存仍未命中,扫描本地目录
if (!target) {
const localFiles = await this.scanLocalFiles();
let latest: FileInfo | null = null;
for (const info of localFiles) {
if (info.fileId === identifier || info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
if (!target || !target.filePath) {
promptAction.showToast({ message: '未找到对应文件', duration: 2000 });
return;
}
const filePath = target.filePath;
const fileType = target.fileType || '';
// 文件是否存在
if (!fileIo.accessSync(filePath)) {
promptAction.showToast({ message: '文件不存在', duration: 2000 });
return;
}
let uri = fileUri.getUriFromPath(filePath);
filePreview.canPreview(this.context, uri).then((result) => { // 传入支持的文件类型且项目存在时会返回true
console.info(`Succeeded in obtaining the result of whether it can be previewed. result = ${result}`);
}).catch((err: BusinessError) => {
console.error(`Failed to obtain the result of whether it can be previewed, err.code = ${err.code}, err.message = ${err.message}`);
});
let fileInfo: filePreview.PreviewInfo = {
title: target.fileName,
uri: uri,
mimeType: this.getMimeType(fileType)
};
let files: Array<filePreview.PreviewInfo> = new Array();
files.push(fileInfo);
filePreview.openPreview(this.context, files, 0).then(() => {
console.info('Succeeded in opening preview');
}).catch((err: BusinessError) => {
console.error(`Failed to open preview, err.code = ${err.code}, err.message = ${err.message}`);
});
} catch (error) {
console.error('打开文件异常:', error);
}
}
/**
* 获取MIME类型
*/
private getMimeType(fileType: string): string {
const mimeTypes: Record<string, string> = {
'pdf': 'application/pdf',
'doc': 'application/msword',
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'ppt': 'application/vnd.ms-powerpoint',
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'xls': 'application/vnd.ms-excel',
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'txt': 'text/plain',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'bmp': 'image/bmp',
'mp4': 'video/mp4',
'avi': 'video/x-msvideo',
'mov': 'video/quicktime',
'wmv': 'video/x-ms-wmv'
};
return mimeTypes[fileType.toLowerCase()] || '*/*';
}
/**
* 删除文件
*/
async deleteFile(identifier: string): Promise<boolean> {
try {
// 1) 优先按 fileId 在缓存中查找
let target: FileInfo | null = null;
const cachedById = this.fileCache.get(identifier);
if (cachedById) {
target = cachedById;
}
// 2) 若未命中,再按文件名在缓存中查找(取最近更新)
if (!target) {
let latest: FileInfo | null = null;
for (const info of this.fileCache.values()) {
if (info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
// 3) 若缓存仍未命中,扫描本地目录
if (!target) {
const localFiles = await this.scanLocalFiles();
let latest: FileInfo | null = null;
for (const info of localFiles) {
if (info.fileId === identifier || info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
if (!target || !target.filePath) {
console.error('未找到要删除的文件:', identifier);
return false;
}
// 删除本地文件
if (fileIo.accessSync(target.filePath)) {
fileIo.unlinkSync(target.filePath);
console.info(`成功删除文件: ${target.filePath}`);
// 删除文件后,检查并删除空的文件夹
await this.cleanupEmptyDirectory(target.filePath);
}
// 从缓存中移除
this.fileCache.delete(target.fileId);
// 更新持久化存储
await this.saveFileInfoToStorage();
return true;
} catch (error) {
console.error('删除文件失败:', error);
return false;
}
}
/**
* 清理空文件夹
*/
private async cleanupEmptyDirectory(filePath: string): Promise<void> {
try {
// 获取文件所在目录
const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
// 检查目录是否存在
if (!fileIo.accessSync(dirPath)) {
return;
}
// 检查目录是否为空
const files = fileIo.listFileSync(dirPath);
if (files.length === 0) {
// 目录为空,删除目录
fileIo.rmdirSync(dirPath);
console.info(`成功删除空文件夹: ${dirPath}`);
// 递归检查父目录是否也为空(但不要删除下载根目录)
const parentDir = dirPath.substring(0, dirPath.lastIndexOf('/'));
if (parentDir !== this.downloadDir && fileIo.accessSync(parentDir)) {
const parentFiles = fileIo.listFileSync(parentDir);
if (parentFiles.length === 0) {
fileIo.rmdirSync(parentDir);
console.info(`成功删除空父文件夹: ${parentDir}`);
}
}
}
} catch (error) {
console.error('清理空文件夹失败:', error);
}
}
/**
* 获取下载状态
*/
isDownloadingFile(): boolean {
return this.isDownloading;
}
/**
* 保存文件信息
*/
private async saveFileInfo(fileInfo: FileInfo): Promise<void> {
try {
this.fileCache.set(fileInfo.fileId, fileInfo);
// 保存到持久化存储
await this.saveFileInfoToStorage();
} catch (error) {
console.error('保存文件信息失败:', error);
}
}
/**
* 下载成功回调
*/
private onDownloadSuccess(fileInfo: FileInfo): void {
const callback = this.downloadCallbacks.get(fileInfo.fileId);
if (callback && callback.onSuccess) {
callback.onSuccess(fileInfo.filePath || '');
}
this.downloadCallbacks.delete(fileInfo.fileId);
promptAction.showToast({ message: '下载完成', duration: 2000 });
}
/**
* 下载失败回调
*/
private onDownloadFailed(fileInfo: FileInfo, error: string): void {
const callback = this.downloadCallbacks.get(fileInfo.fileId);
if (callback && callback.onFailed) {
callback.onFailed(error);
}
this.downloadCallbacks.delete(fileInfo.fileId);
promptAction.showToast({ message: error, duration: 2000 });
}
/**
* 下载取消回调
*/
private onDownloadCancelled(fileInfo: FileInfo): void {
const callback = this.downloadCallbacks.get(fileInfo.fileId);
if (callback && callback.onCancelled) {
callback.onCancelled();
}
this.downloadCallbacks.delete(fileInfo.fileId);
promptAction.showToast({ message: '下载已取消', duration: 2000 });
}
}
@@ -0,0 +1,346 @@
// 导入必要的鸿蒙模块
import http from '@ohos.net.http';
import fs from '@ohos.file.fs';
import relationalStore from '@ohos.data.relationalStore';
import common from '@ohos.app.ability.common';
import security from '@ohos.security.cryptoFramework';
import util from '@ohos.util';
import { BusinessError } from '@ohos.base';
import promptAction from '@ohos.promptAction';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { authStore } from './auth';
import { AESEncryptionDecryption } from './AESEncryptionDecryption';
import { BasicConstant } from '../constants/BasicConstant';
import { rcp } from '@kit.RemoteCommunicationKit';
import fileIo from '@ohos.file.fs';
// 1. 定义数据模型和数据库结构
// 文件信息实体
class WPSFile {
fileId: string = ''; // 对应fileID
fileTitle: string = ''; // 对应fileTitle
fileType: string = ''; // 对应fileType
filePath: string = ''; // 本地存储路径
downloadType: string = ''; // 下载状态: 'downloading', 'downloadFalse', 'downloaded'
md5?: string = ''; // 文件MD5值
}
// 数据库管理器
export class DBManager {
private rdbStore: relationalStore.RdbStore | null = null;
private readonly TABLE_NAME: string = 'WPSFILELIST';
private readonly STORE_CONFIG: relationalStore.StoreConfig = {
name: 'WPSFileDB.db',
securityLevel: relationalStore.SecurityLevel.S1,
};
private context: common.Context;
constructor(context: common.Context) {
this.context = context;
}
// 初始化数据库
async initializeDB(): Promise<void> {
try {
this.rdbStore = await relationalStore.getRdbStore(this.context, this.STORE_CONFIG);
// 创建表
const sql = `
CREATE TABLE IF NOT EXISTS ${this.TABLE_NAME} (
fileId TEXT PRIMARY KEY,
fileTitle TEXT NOT NULL,
fileType TEXT NOT NULL,
filePath TEXT,
downloadType TEXT NOT NULL,
md5 TEXT
)`;
await this.rdbStore.executeSql(sql);
console.log('数据库初始化成功');
} catch (err) {
console.error(`数据库初始化失败: ${err.code}, ${err.message}`);
}
}
// 插入文件记录
async insertFile(file: WPSFile): Promise<boolean> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
const valueBucket: relationalStore.ValuesBucket = {
'fileId': file.fileId,
'fileTitle': file.fileTitle,
'fileType': file.fileType,
'filePath': file.filePath,
'downloadType': file.downloadType,
'md5': file.md5 || ''
};
await this.rdbStore?.insert(this.TABLE_NAME, valueBucket);
console.log('文件记录插入成功');
return true;
} catch (err) {
console.error(`插入文件记录失败: ${err.code}, ${err.message}`);
return false;
}
}
// 更新下载状态
async updateDownloadStatus(fileId: string, status: string): Promise<boolean> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
const valueBucket: relationalStore.ValuesBucket = {
'downloadType': status
};
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
predicates.equalTo('fileId', fileId);
await this.rdbStore?.update(valueBucket, predicates);
console.log('下载状态更新成功');
return true;
} catch (err) {
console.error(`更新下载状态失败: ${err.code}, ${err.message}`);
return false;
}
}
// 删除文件记录
async deleteFile(fileId: string): Promise<boolean> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
predicates.equalTo('fileId', fileId);
await this.rdbStore?.delete(predicates);
console.log('文件记录删除成功');
return true;
} catch (err) {
console.error(`删除文件记录失败: ${err.code}, ${err.message}`);
return false;
}
}
// 根据ID查询文件
async getFileById(fileId: string): Promise<WPSFile | null> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
predicates.equalTo('fileId', fileId);
let resultSet = await this.rdbStore?.query(predicates, ['fileId', 'fileTitle', 'fileType', 'filePath', 'downloadType', 'md5']);
if (resultSet && resultSet.rowCount > 0) {
await resultSet.goToFirstRow();
let file = new WPSFile();
file.fileId = resultSet.getString(resultSet.getColumnIndex('fileId')) || '';
file.fileTitle = resultSet.getString(resultSet.getColumnIndex('fileTitle')) || '';
file.fileType = resultSet.getString(resultSet.getColumnIndex('fileType')) || '';
file.filePath = resultSet.getString(resultSet.getColumnIndex('filePath')) || '';
file.downloadType = resultSet.getString(resultSet.getColumnIndex('downloadType')) || '';
file.md5 = resultSet.getString(resultSet.getColumnIndex('md5')) || '';
resultSet.close();
return file;
}
return null;
} catch (err) {
console.error(`查询文件失败: ${err.code}, ${err.message}`);
return null;
}
}
}
// 1. 计算文件 MD5 (使用路径创建流)
async function calculateFileMD5(filePath: string): Promise<string> {
let stream: fs.Stream = fs.createStreamSync(filePath, 'r');
let md5AlgName: string = "MD5";
let hash: cryptoFramework.Md = cryptoFramework.createMd(md5AlgName);
let dataBuff: ArrayBuffer = new ArrayBuffer(4096);
let readCount: number = stream.readSync(dataBuff);
while (readCount > 0) {
let messageData: Uint8Array = new Uint8Array(dataBuff.slice(0, readCount));
let updateMessageBlob: cryptoFramework.DataBlob = { data: messageData };
hash.updateSync(updateMessageBlob);
readCount = stream.readSync(dataBuff);
}
stream.closeSync();
let md5Result: cryptoFramework.DataBlob = hash.digestSync();
let md5Hex: string = Array.from(md5Result.data).map(byte => byte.toString(16).padStart(2, '0')).join('');
return md5Hex;
}
// 2. 文件下载管理器
export class FileManagerCopy {
private httpRequest: http.HttpRequest = http.createHttp();
private context: common.Context;
private dbManager: DBManager;
constructor(context: common.Context) {
this.context = context;
this.dbManager = new DBManager(context);
}
private generateRandomString(): string {
// 生成1到10之间的随机整数作为字符串长度[6,8](@ref)
const minLength: number = 1;
const maxLength: number = 10;
const targetLength: number = Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength;
// 初始化一个空数组用于存储字符
let charArray: string[] = [];
// 循环生成指定数量的随机大写字母[6](@ref)
for (let i = 0; i < targetLength; i++) {
// 生成65('A')到90('Z')之间的随机数[6](@ref)
const randomCharCode: number = Math.floor(Math.random() * 26) + 65;
// 将Unicode编码转换为字符并添加到数组
charArray.push(String.fromCharCode(randomCharCode));
}
return charArray.join('');
}
async downloadFile(wpsUuid: string, wpsTitle: string, wpsType: string, fileMd5: string, order_id:string , orderStatus: number, timestamp: string) {
// 检查是否正在下载
let existingFile = await this.dbManager.getFileById(wpsUuid);
if (existingFile && existingFile.downloadType === 'downloading') {
promptAction.showToast({ message: '正在下载,请勿重复点击', duration: 2000 });
// return false;
}
let daijiami:string = ''
if (orderStatus == 1) {//免费
daijiami = `${wpsUuid}|${order_id}|${authStore.getUser().uuid}|${timestamp}`
}
// else if (orderStatus == 8) {//复制iOS代码逻辑,有这样的内容
// daijiami = `${wpsUuid}|${orderStatus}|${authStore.getUser().uuid}|${timestamp}`
// }
else {
daijiami = `${wpsUuid}|${order_id}|${authStore.getUser().uuid}|${timestamp}`
}
// f6f25104fa0345b38074710c9356948b|USEWELFARENUM|1CBMDQbuOX3xbxAcxE5|2025-09-01 11:02:42
//ios-//f6f25104fa0345b38074710c9356948b|USEWELFARENUM|GA5LeMOXChsKxMrqFnL|2025-08-29 17:25:54
const scanData = await AESEncryptionDecryption.aesEncrypt(daijiami,BasicConstant.ExpertAesKey)
//hGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS/916uiWz8iNWCZMYLa4iKXw3
const encodedString = encodeURIComponent(scanData)
// hGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F916uiWz8iNWCZMYLa4iKXw3
//ios-//hGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv
let pinString = 'X'+encodedString
//XhGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F90X6zMHwPph2jDuPjTMzXqP
//ios-//XhGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv
const targetLength: number = Math.floor(Math.random() * (10 - 1 + 1)) + 1;
let downloadUrl = `${BasicConstant.urlExpertApp}downloadGanDanFile?&gdf=${pinString}&a=${targetLength}`
//https://dev-app.igandan.com/app/expertApp/downloadGanDanFile?&gdf=XhGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F90X6zMHwPph2jDuPjTMzXqP&a=10
//ios-//https://dev-app.igandan.com/app/expertApp/downloadGanDanFile?&gdf=XhGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv&a=1
// 创建下载记录
let wpsFile = new WPSFile();
wpsFile.fileId = wpsUuid;
wpsFile.fileTitle = wpsTitle;
wpsFile.fileType = wpsType;
wpsFile.downloadType = 'downloading';
wpsFile.md5 = fileMd5;
//下载文件保存的文件夹路径,仅为示例,请按需求进行替换。
const DOWNLOAD_TO_PATH = `/data/storage/el2/base/haps/entry/files`;
// 创建了一个安全配置对象,其中remoteValidation设置为'skip',表示将跳过远程验证。
const securityConfig: rcp.SecurityConfiguration = {
remoteValidation: 'skip'
}
// 创建了一个下载配置对象,其中kind设置为'folder',表示下载的目标是文件夹,path设置为之前定义的DOWNLOAD_TO_PATH。
let downloadToFile: rcp.DownloadToFile = {
kind: 'folder',
path: DOWNLOAD_TO_PATH
}
// 创建一个HTTP会话,其中请求配置包括传输超时设置和安全配置(配置可自定义)
const session = rcp.createSession({
requestConfiguration: {
transfer: { timeout: { connectMs: 6000, transferMs: 6000, inactivityMs: 6000 } },
security: securityConfig
}
})
// 检查目标路径是否存在
if (fileIo.accessSync(DOWNLOAD_TO_PATH)) {
fileIo.rmdirSync(DOWNLOAD_TO_PATH);
}
// 发起请求,执行下载操作
session.downloadToFile(downloadUrl, downloadToFile)
.then((response: rcp.Response) => {
console.info(`Successfully received the response, statusCode: ${JSON.stringify(response.statusCode)}`);
}).catch((err: BusinessError) => {
console.error(`Failed, the error message is ${JSON.stringify(err)}`)
})
// // 文件保存路径
// let filesDir = this.context.filesDir;
// wpsFile.filePath = `${filesDir}/${wpsUuid}.${wpsType}`;
//
// // 插入数据库记录
// let insertSuccess = await this.dbManager.insertFile(wpsFile);
// if (!insertSuccess) {
// promptAction.showToast({ message: '下载初始化失败', duration: 2000 });
// return false;
// }
//
// promptAction.showToast({ message: '开始下载,请勿重复点击', duration: 2000 });
//
// try {
// // 配置下载选项
// let options: http.HttpRequestOptions = {
// method: http.RequestMethod.GET,
// header: {
// 'Content-Type': 'application/octet-stream'
// },
// expectDataType: http.HttpDataType.ARRAY_BUFFER,
// usingCache: false,
// };
//
// // 执行下载请求
// let response = await this.httpRequest.request(downloadUrl, options);
// if (response.responseCode === 200) {
// // 保存文件到本地
// let file = fs.openSync(wpsFile.filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// fs.writeSync(file.fd, response.result as ArrayBuffer);
// fs.closeSync(file);
//
// // 验证MD5
// let localMd5 = await calculateFileMD5(wpsFile.filePath)
// if (localMd5 === fileMd5) {
// // 更新数据库状态为下载成功
// await this.dbManager.updateDownloadStatus(wpsUuid, 'downloaded');
// promptAction.showToast({ message: '下载完成', duration: 2000 });
// return true;
// } else {
// // MD5不匹配,删除文件
// fs.unlinkSync(wpsFile.filePath);
// await this.dbManager.deleteFile(wpsUuid);
// promptAction.showToast({ message: '下载失败,文件校验错误', duration: 2000 });
// return false;
// }
// } else {
// // 下载失败
// await this.dbManager.deleteFile(wpsUuid);
// promptAction.showToast({ message: '下载失败,请重试', duration: 2000 });
// return false;
// }
// } catch (err) {
// // 异常处理
// await this.dbManager.deleteFile(wpsUuid);
// console.error(`下载失败: ${err.code}, ${err.message}`);
// promptAction.showToast({ message: '下载失败,请重试', duration: 2000 });
// return false;
// }
}
}
@@ -0,0 +1,239 @@
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { image } from '@kit.ImageKit';
import { fileIo } from '@kit.CoreFileKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { promptAction } from '@kit.ArkUI';
const TAG: string = 'ImageCompressInfo';
// let context = getContext(this);
// let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
let compressedImageData: ArrayBuffer
//压缩图片类
export class CompressedImageInfo {
public imageUri: string = ''; // 压缩后图片保存位置的uri
public imageByteLength: number = 0; // 压缩后图片字节长度
}
/**
* 图片压缩,保存
* @param sourcePixelMap:原始待压缩图片的PixelMap对象
* @param maxCompressedImageSize:指定图片的压缩目标大小,单位kb
* @returns compressedImageInfo:返回最终压缩后的图片信息
*/
export async function compressedImage(sourcePixelMap: PixelMap,
maxCompressedImageSize: number): Promise<CompressedImageInfo> {
// 创建图像编码ImagePacker对象
const IMAGE_PACKER_API = image.createImagePacker();
const IMAGE_QUALITY = 0;
const PACK_OPTS: image.PackingOption = { format: 'image/jpeg', quality: IMAGE_QUALITY };
// 通过PixelMap进行编码。compressedImageData为打包获取到的图片文件流。
compressedImageData = await IMAGE_PACKER_API.packToData(sourcePixelMap, PACK_OPTS);
// 压缩目标图像字节长度
const MAX_COMPRESS_IMAGE_BYTE = maxCompressedImageSize * 1024;
// 图片压缩。先判断设置图片质量参数quality为0时,packing能压缩到的图片最小字节大小是否满足指定的图片压缩大小。
// 如果满足,则使用packToData方式二分查找最接近指定图片压缩目标大小的quality来压缩图片。
// 如果不满足,则使用scale对图片先进行缩放,采用while循环每次递减0.2倍缩放图片。
// 再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据。
if (MAX_COMPRESS_IMAGE_BYTE > compressedImageData.byteLength) {
// 使用packing二分压缩获取图片文件流
compressedImageData =
await packingImage(compressedImageData, sourcePixelMap, IMAGE_QUALITY, MAX_COMPRESS_IMAGE_BYTE);
} else {
// 使用scale对图片先进行缩放,采用while循环每次递减0.2倍缩放图片。
// 再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据。
let imageScale = 1;
const REDUCE_SCALE = 0.2;
// 判断压缩后的图片大小是否大于指定图片的压缩目标大小,如果大于,继续降低缩放倍数压缩。
while (compressedImageData.byteLength > MAX_COMPRESS_IMAGE_BYTE) {
if (imageScale > 0) {
// 性能知识点: 由于scale会直接修改图片PixelMap数据,所以不适用二分查找scale缩放倍数。
// 这里采用循环递减0.2倍缩放图片,来查找确定最适合的缩放倍数。
// 如果对图片压缩质量要求不高,建议调高每次递减的缩放倍数reduceScale,减少循环,提升scale压缩性能。
imageScale = imageScale - REDUCE_SCALE;
await sourcePixelMap.scale(imageScale, imageScale);
compressedImageData = await packing(sourcePixelMap, IMAGE_QUALITY);
} else {
// imageScale缩放小于等于0时,没有意义,结束压缩。这里不考虑图片缩放倍数小于reduceScale的情况。
break;
}
}
}
// 保存图片,返回压缩后的图片信息。
const COMPRESS_IMAGE_INFO: CompressedImageInfo = await saveImageToSandboxPath(compressedImageData);
return COMPRESS_IMAGE_INFO;
}
/**
* packing压缩
* @param sourcePixelMap:原始待压缩图片的PixelMap
* @param imageQuality:图片质量参数
* @returns data:返回压缩后的图片数据
*/
export async function packing(sourcePixelMap: image.PixelMap, imageQuality: number): Promise<ArrayBuffer> {
const IMAGE_PACKER_API = image.createImagePacker();
const PACK_OPTS: image.PackingOption = { format: 'image/jpeg', quality: imageQuality };
const DATA: ArrayBuffer = await IMAGE_PACKER_API.packToData(sourcePixelMap, PACK_OPTS);
return DATA;
}
/**
* packing二分方式循环压缩
* @param compressedImageData:图片压缩的ArrayBuffer
* @param sourcePixelMap:原始待压缩图片的PixelMap
* @param imageQuality:图片质量参数
* @param maxCompressedImageByte:压缩目标图像字节长度
* @returns compressedImageData:返回二分packing压缩后的图片数据
*/
export async function packingImage(compressedImageData: ArrayBuffer, sourcePixelMap: image.PixelMap,
imageQuality: number,
maxCompressedImageByte: number): Promise<ArrayBuffer> {
// 图片质量参数范围为0-100,这里以10为最小二分单位创建用于packing二分图片质量参数的数组。
const PACKING_ARRAY: number[] = [];
const DICHOTOMY_ACCURACY = 10;
// 性能知识点: 如果对图片压缩质量要求不高,建议调高最小二分单位dichotomyAccuracy,减少循环,提升packing压缩性能。
for (let i = 0; i <= 100; i += DICHOTOMY_ACCURACY) {
PACKING_ARRAY.push(i);
}
let left = 0;
let right = PACKING_ARRAY.length - 1;
// 二分压缩图片
while (left <= right) {
const MID = Math.floor((left + right) / 2);
imageQuality = PACKING_ARRAY[MID];
// 根据传入的图片质量参数进行packing压缩,返回压缩后的图片文件流数据。
compressedImageData = await packing(sourcePixelMap, imageQuality);
// 判断查找一个尽可能接近但不超过压缩目标的压缩大小
if (compressedImageData.byteLength <= maxCompressedImageByte) {
left = MID + 1;
if (MID === PACKING_ARRAY.length - 1) {
break;
}
// 获取下一次二分的图片质量参数(mid+1)压缩的图片文件流数据
compressedImageData = await packing(sourcePixelMap, PACKING_ARRAY[MID + 1]);
// 判断用下一次图片质量参数(mid+1)压缩的图片大小是否大于指定图片的压缩目标大小。
// 如果大于,说明当前图片质量参数(mid)压缩出来的图片大小最接近指定图片的压缩目标大小。传入当前图片质量参数mid,得到最终目标图片压缩数据。
if (compressedImageData.byteLength > maxCompressedImageByte) {
compressedImageData = await packing(sourcePixelMap, PACKING_ARRAY[MID]);
break;
}
} else {
// 目标值不在当前范围的右半部分,将搜索范围的右边界向左移动,以缩小搜索范围并继续在下一次迭代中查找左半部分。
right = MID - 1;
}
}
return compressedImageData;
}
/**
* 图片保存至沙箱路径
* @param compressedImageData:压缩后的图片数据
* @returns compressedImageInfo:返回压缩后的图片信息
*/
async function saveImageToSandboxPath(compressedImageData: ArrayBuffer): Promise<CompressedImageInfo> {
const CONTEXT: Context = getContext();
// 定义要保存的压缩图片uri。afterCompressiona.jpeg表示压缩后的图片。
const COMPRESSED_IMAGE_URI: string = CONTEXT.filesDir + '/' + 'afterCompressiona.jpeg';
try {
const res = fileIo.accessSync(COMPRESSED_IMAGE_URI);
if (res) {
// 如果图片afterCompressiona.jpeg已存在,则删除
fileIo.unlinkSync(COMPRESSED_IMAGE_URI);
}
} catch (err) {
hilog.error(0X0000, TAG, `AccessSync failed with error message: ${err.message}, error code: ${err.code}`);
}
// 获取最终图片压缩数据compressedImageData,保存图片。
// 压缩图片数据写入文件
const FILE: fileIo.File = fileIo.openSync(COMPRESSED_IMAGE_URI, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
try {
fileIo.writeSync(FILE.fd, compressedImageData);
} catch (err) {
let error = err as BusinessError;
throw new Error(`Image saving failed: ${error.message}`);
} finally {
if (FILE) {
fileIo.closeSync(FILE);
}
}
// 获取压缩图片信息
let compressedImageInfo: CompressedImageInfo = new CompressedImageInfo();
compressedImageInfo.imageUri = COMPRESSED_IMAGE_URI;
compressedImageInfo.imageByteLength = compressedImageData.byteLength;
return compressedImageInfo;
}
//图片保存至图库
export async function saveImageToGallery() {
// try {
// const CONTEXT: Context = getContext();
// // 定义要保存的压缩图片uri。afterCompressiona.jpeg表示压缩后的图片。
// const COMPRESS_IMAGE_URI: string = CONTEXT.filesDir + '/' + 'afterCompressiona.jpeg';
// let srcFileUris: Array<string> = [
// COMPRESS_IMAGE_URI
// ];
// let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
// {
// title: 'test', // 可选。
// fileNameExtension: 'jpg',
// photoType: photoAccessHelper.PhotoType.IMAGE,
// subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选。
// }
// ];
// // 获取最终图片压缩数据compressedImageData,保存图片。
// // 压缩图片数据写入文件
// // 基于弹窗授权的方式获取媒体库的目标uri。
// let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
// // 将来源于应用沙箱的照片内容写入媒体库的目标uri。
// let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);
// let srcFile: fileIo.File = await fileIo.open(COMPRESS_IMAGE_URI, fileIo.OpenMode.READ_ONLY);
// const FILE: fileIo.File =
// fileIo.openSync(COMPRESS_IMAGE_URI, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
// try {
// fileIo.writeSync(FILE.fd, compressedImageData);
// await fileIo.copyFile(srcFile.fd, desFile.fd);
// } catch (err) {
// let error = err as BusinessError;
// throw new Error(`Image saving failed: ${error.message}`);
// } finally {
// promptAction.showToast({
// message: '保存成功',
// textColor: '#0A59F7',
// })
// if (FILE) {
// fileIo.closeSync(FILE);
// }
// if (srcFile) {
// fileIo.closeSync(srcFile);
// }
// if (desFile) {
// fileIo.closeSync(desFile);
// }
// }
// } catch (err) {
// hilog.error(0X0000, TAG, `failed to save image to gallery, errCode is: ${err.code}, ${err.message}`);
// }
}
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { notificationManager } from '@kit.NotificationKit';
export class NotificationManagementUtil {
static async setBadgeNumber(num: number) {
if(num<0)
{
return
}
await notificationManager.setBadgeNumber(num).then(() => {
});
}
}
+49 -17
View File
@@ -11,6 +11,8 @@ import { bundleManager} from '@kit.AbilityKit';
import { deviceInfo } from '@kit.BasicServicesKit'; import { deviceInfo } from '@kit.BasicServicesKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit'; import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { rcp } from '@kit.RemoteCommunicationKit'; import { rcp } from '@kit.RemoteCommunicationKit';
import { BasicConstant } from '../../../../Index';
import { connection } from '@kit.NetworkKit';
interface HdRequestOptions { interface HdRequestOptions {
baseURL?: string baseURL?: string
@@ -25,8 +27,14 @@ export interface HdResponse<T> {
} }
export interface TimestampBean { export interface TimestampBean {
timestamp:string timestamp:string
}
export enum NetworkStatus {
type_default = 0,//无网络
type_wifi = 1,//wifi
type_traffic = 2,//蜂窝网络类型(2G/3G/4G/5G)
type_unknown = 3,//未知网络
type_error = -1,//失败
} }
class HdHttp { class HdHttp {
@@ -60,12 +68,12 @@ class HdHttp {
return httpInstance.request(fullUrl, options).then((res) => { return httpInstance.request(fullUrl, options).then((res) => {
logger.info('Response param' + JSON.stringify(extraDatas)) // logger.info('Response param' + JSON.stringify(extraDatas))
logger.info('Response fullUrl:' + fullUrl + res.result); // logger.info('Response fullUrl:' + fullUrl + res.result);
const result = res.result as HdResponse<T> const result = res.result as HdResponse<T>
return result return result
}).catch((err: BusinessError) => { }).catch((err: BusinessError) => {
logger.info(fullUrl + `Response succeeded: ${err}`); // logger.info(fullUrl + `Response succeeded: ${err}`);
promptAction.showToast({ message: err.message || '网络错误' }) promptAction.showToast({ message: err.message || '网络错误' })
return Promise.reject(err) return Promise.reject(err)
}).finally(() => { }).finally(() => {
@@ -109,12 +117,12 @@ class HdHttp {
let fullUrl = this.baseURL + path let fullUrl = this.baseURL + path
return httpInstance.request(fullUrl, options).then((res) => { return httpInstance.request(fullUrl, options).then((res) => {
logger.info('Response param' + JSON.stringify(extraDatas)) // logger.info('Response param' + JSON.stringify(extraDatas))
logger.info('Response fullUrl:' + fullUrl + res.result); // logger.info('Response fullUrl:' + fullUrl + res.result);
const result = res.result as HdResponse<T> const result = res.result as HdResponse<T>
return result return result
}).catch((err: BusinessError) => { }).catch((err: BusinessError) => {
logger.info(fullUrl + `Response succeeded: ${err}`); // logger.info(fullUrl + `Response succeeded: ${err}`);
promptAction.showToast({ message: err.message || '网络错误' }) promptAction.showToast({ message: err.message || '网络错误' })
return Promise.reject(err) return Promise.reject(err)
}).finally(() => { }).finally(() => {
@@ -149,11 +157,11 @@ class HdHttp {
} }
return httpInstance.request(fullUrl, options).then((res) => { return httpInstance.request(fullUrl, options).then((res) => {
logger.info('Response fullUrl:' + fullUrl + res.result); // logger.info('Response fullUrl:' + fullUrl + res.result);
const result = res.result as HdResponse<T> const result = res.result as HdResponse<T>
return result return result
}).catch((err: BusinessError) => { }).catch((err: BusinessError) => {
logger.info(fullUrl + `Response succeeded: ${err}`); // logger.info(fullUrl + `Response succeeded: ${err}`);
promptAction.showToast({ message: err.message || '网络错误' }) promptAction.showToast({ message: err.message || '网络错误' })
return Promise.reject(err) return Promise.reject(err)
}).finally(() => { }).finally(() => {
@@ -188,7 +196,7 @@ class HdHttp {
httpReq<T>(url: string, datas: HashMap<string, string>): Promise<HdResponse<T>> { httpReq<T>(url: string, datas: HashMap<string, string>): Promise<HdResponse<T>> {
// 创建httpRequest对象。 // 创建httpRequest对象。
let httpRequest = http.createHttp(); let httpRequest = http.createHttp();
let url1 = "https://dev-app.igandan.com/app/manager/getSystemTimeStamp"; let url1 = BasicConstant.getSystemTimeStamp;
let promise = httpRequest.request( let promise = httpRequest.request(
// 请求url地址 // 请求url地址
url1, url1,
@@ -207,7 +215,7 @@ class HdHttp {
// 处理响应结果。 // 处理响应结果。
return promise.then(async (data) => { return promise.then(async (data) => {
if (data.responseCode === http.ResponseCode.OK) { if (data.responseCode === http.ResponseCode.OK) {
logger.info('Response httpReq:' + data.result); // logger.info('Response httpReq:' + data.result);
let json: TimestampBean = JSON.parse(data.result.toString()) as TimestampBean; let json: TimestampBean = JSON.parse(data.result.toString()) as TimestampBean;
let tp = json.timestamp; let tp = json.timestamp;
datas.set("user_uuid", authStore.getUser().uuid ? authStore.getUser().uuid : ''); datas.set("user_uuid", authStore.getUser().uuid ? authStore.getUser().uuid : '');
@@ -220,7 +228,7 @@ class HdHttp {
} }
} }
).catch((err: BusinessError) => { ).catch((err: BusinessError) => {
logger.info('Response httpReq error:' + JSON.stringify(err)); // logger.info('Response httpReq error:' + JSON.stringify(err));
return Promise.reject(err); return Promise.reject(err);
}).finally(() => { }).finally(() => {
httpRequest.destroy() httpRequest.destroy()
@@ -230,7 +238,7 @@ class HdHttp {
httpReqObject<T>(url: string, datas: HashMap<string, Object>): Promise<HdResponse<T>> { httpReqObject<T>(url: string, datas: HashMap<string, Object>): Promise<HdResponse<T>> {
// 创建httpRequest对象。 // 创建httpRequest对象。
let httpRequest = http.createHttp(); let httpRequest = http.createHttp();
let url1 = "https://dev-app.igandan.com/app/manager/getSystemTimeStamp"; let url1 = BasicConstant.getSystemTimeStamp;
let promise = httpRequest.request( let promise = httpRequest.request(
// 请求url地址 // 请求url地址
url1, url1,
@@ -249,7 +257,7 @@ class HdHttp {
// 处理响应结果。 // 处理响应结果。
return promise.then(async (data) => { return promise.then(async (data) => {
if (data.responseCode === http.ResponseCode.OK) { if (data.responseCode === http.ResponseCode.OK) {
logger.info('Response httpReq:' + data.result); // logger.info('Response httpReq:' + data.result);
let json: TimestampBean = JSON.parse(data.result.toString()) as TimestampBean; let json: TimestampBean = JSON.parse(data.result.toString()) as TimestampBean;
let tp = json.timestamp; let tp = json.timestamp;
datas.set("user_uuid", authStore.getUser().uuid ? authStore.getUser().uuid : ''); datas.set("user_uuid", authStore.getUser().uuid ? authStore.getUser().uuid : '');
@@ -262,7 +270,7 @@ class HdHttp {
} }
} }
).catch((err: BusinessError) => { ).catch((err: BusinessError) => {
logger.info('Response httpReq error:' + JSON.stringify(err)); // logger.info('Response httpReq error:' + JSON.stringify(err));
return Promise.reject(err); return Promise.reject(err);
}).finally(() => { }).finally(() => {
httpRequest.destroy() httpRequest.destroy()
@@ -289,12 +297,12 @@ class HdHttp {
}); });
// 处理响应结果。 // 处理响应结果。
return promise.then((data) => { return promise.then((data) => {
logger.info('Response httpReqSimply:' + JSON.stringify(data)); // logger.info('Response httpReqSimply:' + JSON.stringify(data));
const result = data.result as HdResponse<T> const result = data.result as HdResponse<T>
return result return result
} }
).catch((err: BusinessError) => { ).catch((err: BusinessError) => {
logger.info('Response httpReq error:' + JSON.stringify(err)); // logger.info('Response httpReq error:' + JSON.stringify(err));
return Promise.reject(err); return Promise.reject(err);
}).finally(() => { }).finally(() => {
httpRequest.destroy() httpRequest.destroy()
@@ -430,6 +438,30 @@ class HdHttp {
return result === 0 ? (a < b ? -1 : 1) : result; return result === 0 ? (a < b ? -1 : 1) : result;
}); });
} }
getNetworkType():number {
try {
let netHandle = connection.getDefaultNetSync()
if (!netHandle || netHandle.netId === 0) {
console.info('当前网络状态:无网络')
return NetworkStatus.type_default;
}
let netCap = connection.getNetCapabilitiesSync(netHandle);
if (netCap.bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
console.info('当前网络状态:wifi')
return NetworkStatus.type_wifi;
} else if (netCap.bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
console.info('当前网络状态:2G/3G/4G/5G')
return NetworkStatus.type_traffic;
} else {
console.info('当前网络状态:未知网络')
return NetworkStatus.type_unknown;
}
} catch (error) {
console.error("Get net type failed, error: " + JSON.stringify(error));
return NetworkStatus.type_error;
}
}
} }
interface DataBlob { interface DataBlob {
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -208,7 +208,8 @@ export struct ConversationViewItem {
return $r('app.string.msg_type_rtc_video') return $r('app.string.msg_type_rtc_video')
} }
} }
case V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM:
return $r('app.string.tipMessageType')
} }
return item.lastMessage?.text ?? $r('app.string.chatMessageNonsupportType') return item.lastMessage?.text ?? $r('app.string.chatMessageNonsupportType')
+3
View File
@@ -0,0 +1,3 @@
-keep-file-name
-keep-property-name
-keep-global-name
+3
View File
@@ -0,0 +1,3 @@
-keep-file-name
-keep-property-name
-keep-global-name
+3 -1
View File
@@ -30,4 +30,6 @@ export { VideoSelected } from './src/main/ets/pages/VideoSelected'
export { videoTools } from './src/main/ets/polyv/VideoUtil' export { videoTools } from './src/main/ets/polyv/VideoUtil'
export { CustomScanResultComp }from './src/main/ets/components/CustomScanResultComp' export { CustomScanResultComp }from './src/main/ets/components/CustomScanResultComp'
export { EducationVideo } from './src/main/ets/pages/EducationVideo'
+2 -1
View File
@@ -10,6 +10,7 @@
"@polyvharmony/media-player-sdk": "2.5.0", "@polyvharmony/media-player-sdk": "2.5.0",
"@polyvharmony/media-player-sdk-addon-cache-down": "2.5.0", "@polyvharmony/media-player-sdk-addon-cache-down": "2.5.0",
"scene_single_video": "file:../../scene_single_video", "scene_single_video": "file:../../scene_single_video",
"media-player-common": "file:../../polyv" "media-player-common": "file:../../polyv",
"@nimkit/chatkit": "file:../../chatkit"
} }
} }
@@ -1,15 +1,89 @@
import { HdNav, ScanUtil } from '@itcast/basic' import { AESEncryptionDecryption, authStore, BasicConstant, HdNav, ScanUtil } from '@itcast/basic'
import { promptAction } from '@kit.ArkUI' import { promptAction, router } from '@kit.ArkUI'
import { http } from '@kit.NetworkKit'
@Component @Component
export struct CustomScanResultComp { export struct CustomScanResultComp {
@State params:Record<string,string> = router.getParams() as Record<string,string>
aboutToAppear(): void {
}
build() { build() {
Column() { Column() {
HdNav({isLeftAction:false,title:'扫描二维码',showRightIcon:false,showRightText:false}) HdNav({isLeftAction:false,title:'扫描登录',showRightIcon:false,showRightText:false})
Image($r('app.media.scan_success_icon'))
.objectFit(ImageFit.Contain)
.width(210)
.height(74)
.margin({top:70})
Text('确认电脑登录\n为确保账号安全,请确认是您本人操作')
.textAlign(TextAlign.Center)
.fontSize(16)
.fontColor('rgba(102,102,102,1)')
.margin({top:50})
Text('确认登录')
.textAlign(TextAlign.Center)
.fontSize(18)
.fontColor(Color.White)
.width('90%')
.height(40)
.backgroundColor($r('app.color.main_color'))
.borderRadius(3)
.margin({left:15,right:15,top:100})
.onClick(()=>isAuthorization(this.params.id,true))
Text('取消登录')
.textAlign(TextAlign.Center)
.fontSize(18)
.fontColor('rgba(153,153,153,1)')
.borderWidth(1)
.borderColor('rgba(153,153,153,1)')
.width('90%')
.height(40)
.backgroundColor(Color.White)
.borderRadius(3)
.margin({left:15,right:15,top:20})
.onClick(()=>isAuthorization(this.params.id,false))
} }
} }
} }
async function isAuthorization(str:string,type:boolean) {
let scanString = ''
if (type) {
scanString = `watchliveTo}${str}}${authStore.getUser().uuid},${authStore.getUser().realName},${authStore.getUser().photo}`
} else {
scanString = `watchliveCancel}${str}}${authStore.getUser().uuid},${authStore.getUser().realName},${authStore.getUser().photo}`
}
const scanData = await AESEncryptionDecryption.aesEncrypt(scanString,BasicConstant.ExpertAesKey)
const encodedString = encodeURIComponent(scanData)
let httpRequest = http.createHttp();
let data = "message="+encodedString;
httpRequest.request(
BasicConstant.sendWebsocketMsg,
{
method: http.RequestMethod.POST,
header: { 'Content-Type': 'application/x-www-form-urlencoded' },
extraData: data,
connectTimeout: 60000,
readTimeout: 60000,
}, (err, data) => {
if (!err) {
console.info('Result:' + JSON.stringify(data.result));
let json:Record<string,string> = JSON.parse(data.result+'') as Record<string,string>
if (type) {
promptAction.showToast({ message: String(json.message), duration: 1000 })
}
if(json.code == '1') {
router.back()
} else {
console.error('获取患者信息失败:'+json.message)
}
} else {
console.info('error:' + JSON.stringify(err))
httpRequest.off('headersReceive')
httpRequest.destroy()
}
})
}
@@ -2,7 +2,9 @@ import { FlipperOptions,FlipperView,FlipperOptionsBuilder } from '@hshare/hshare
import { promptAction } from '@kit.ArkUI'; import { promptAction } from '@kit.ArkUI';
import { router } from '@kit.ArkUI' import { router } from '@kit.ArkUI'
import { meetingModel } from '../model/HomeModel' import { meetingModel } from '../model/HomeModel'
import { TimestampUtil } from '@itcast/basic' import { AESEncryptionDecryption, authStore, Base64Util, BasicConstant,
DataWebModels,
TimestampUtil } from '@itcast/basic'
@Component @Component
export struct FlipperComp { export struct FlipperComp {
@@ -10,13 +12,18 @@ export struct FlipperComp {
@State processedData: meetingModel[] = []; @State processedData: meetingModel[] = [];
options: FlipperOptions = FlipperOptionsBuilder.getInstance() options: FlipperOptions = FlipperOptionsBuilder.getInstance()
.setHeight(56)//View高度 .setHeight(56)
.setInterval(3000)//上下滚动间隔,单位为毫秒 .setInterval(3000)
.setAnimateParam(500)//动画持续时间,单位为毫秒 .setAnimateParam(500)
.setOnItemClicked((item: meetingModel, index: number) => { .setOnItemClicked((item: meetingModel, index: number) => {
let paramsInfo: DataWebModels = {
url:item.liveurl ,
title:'肝胆直播',
type:'live'
};
router.pushUrl({ router.pushUrl({
url: 'pages/WebView/WebPage', // 目标url url: 'pages/WebView/LivebroadcastPages', // 目标url
params: {url:item.liveurl,title:item.title} params: paramsInfo // 添加params属性,传递自定义参数
}) })
}) })
.setOnItemScrolled((item: meetingModel, index: number) => { .setOnItemScrolled((item: meetingModel, index: number) => {
@@ -56,13 +63,13 @@ export struct FlipperComp {
Column() { Column() {
Row({space:2}){ Row({space:2}){
Image($r('app.media.meeting_live_icon')).width(30).height(13) Image($r('app.media.meeting_live_icon')).width(30).height(13)
if (item.state === '1') { if (item.state == '1') {
if (TimestampUtil.isToday(item.begin_date_timestamp)) { if (TimestampUtil.isToday(item.begin_date_timestamp)) {
Image($r('app.media.meeting_begin_play_icon')).width(16).height(16) Image($r('app.media.meeting_begin_play_icon')).width(16).height(16)
} else { } else {
Image($r('app.media.meeting_noPlay_icon')).width(16).height(16) Image($r('app.media.meeting_noPlay_icon')).width(16).height(16)
} }
} else if (item.state === '0' || item.state === '3') { } else if (item.state == '0' || item.state == '3') {
if (TimestampUtil.isToday(item.begin_date_timestamp)) { if (TimestampUtil.isToday(item.begin_date_timestamp)) {
Image($r('app.media.meeting_begin_play_icon')).width(16).height(16) Image($r('app.media.meeting_begin_play_icon')).width(16).height(16)
} else { } else {
@@ -74,20 +81,20 @@ export struct FlipperComp {
} }
Stack({alignContent:Alignment.Center}){ Stack({alignContent:Alignment.Center}){
Image($r('app.media.meeting_timeBack_icon')).width(74).height(15).margin({top:-3}) Image($r('app.media.meeting_timeBack_icon')).width(74).height(15).margin({top:-3})
if (item.state === '1') { if (item.state == '1') {
if (TimestampUtil.isToday(item.begin_date_timestamp)) { if (TimestampUtil.isToday(item.begin_date_timestamp)) {
Text('开播'+TimestampUtil.format(item.begin_date_timestamp,'HH:mm')) Text('开播'+TimestampUtil.format(item.begin_date_timestamp,'HH:mm'))
.fontSize(12).fontColor(Color.White) .fontSize(12).fontColor(Color.White)
} else { } else {
Text(TimestampUtil.format(item.begin_date_timestamp,'MM月dd日')) Text(TimestampUtil.format(item.begin_date_timestamp,'MM月DD日'))
.fontSize(12).fontColor(Color.White) .fontSize(12).fontColor(Color.White)
} }
} else if (item.state === '0' || item.state === '3') { } else if (item.state == '0' || item.state == '3') {
if (TimestampUtil.isToday(item.begin_date_timestamp)) { if (TimestampUtil.isToday(item.begin_date_timestamp)) {
Text('开播'+TimestampUtil.format(item.begin_date_timestamp,'HH:mm')) Text('开播'+TimestampUtil.format(item.begin_date_timestamp,'HH:mm'))
.fontSize(12).fontColor(Color.White) .fontSize(12).fontColor(Color.White)
} else { } else {
Text(TimestampUtil.format(item.begin_date_timestamp,'MM月dd日')) Text(TimestampUtil.format(item.begin_date_timestamp,'MM月DD日'))
.fontSize(12).fontColor(Color.White) .fontSize(12).fontColor(Color.White)
} }
} else { } else {
@@ -101,7 +108,10 @@ export struct FlipperComp {
Text(item.title) Text(item.title)
.fontSize(14) .fontSize(14)
.fontColor('#333333') .fontColor('#333333')
.margin({left:9}) .width('70%')
.margin({left:9,right:10})
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}.margin({top:10}).width('100%').height(46) }.margin({top:10}).width('100%').height(46)
} }
} }
@@ -0,0 +1,179 @@
import { gandanfileModel, guideModel } from "../model/HomeModel";
@Component
export struct HomeGanDanFileComp {
@Prop gandanFileList:gandanfileModel[]
@Prop guideList:guideModel[]
@State currentIndex: number = 0
private tabsController: TabsController = new TabsController()
build() {
Column() {
this.headerView()
this.tabContent()
}
.width('100%')
.backgroundColor('#F5F5F5')
}
@Builder
headerView() {
Stack() {
Row() {
Image($r('app.media.home_guideAndShare_icon'))
.width('70%')
.height(50)
.objectFit(ImageFit.Cover)
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.End)
Row() {
Row() {
Column() {
Text('实用指南')
.fontSize(this.currentIndex === 0 ? 17 :14)
.fontColor(this.currentIndex === 0 ? '#000000' : '#666666')
.fontWeight(this.currentIndex === 0 ? FontWeight.Bold : FontWeight.Normal)
Blank()
.width(32)
.height(2)
.margin({top:5})
.backgroundColor($r('app.color.main_color'))
.visibility(this.currentIndex == 0? Visibility.Visible: Visibility.Hidden)
}
.width(80)
.height(50)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentIndex = 0
this.tabsController.changeIndex(0)
})
Column() {
Text('课件分享')
.fontSize(this.currentIndex === 1 ? 17 : 14)
.fontColor(this.currentIndex === 1 ? '#000000' : '#666666')
.fontWeight(this.currentIndex === 1 ? FontWeight.Bold : FontWeight.Normal)
Blank()
.width(32)
.height(2)
.margin({top:5})
.backgroundColor($r('app.color.main_color'))
.visibility(this.currentIndex == 1? Visibility.Visible: Visibility.Hidden)
}
.width(80)
.height(50)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentIndex = 1
this.tabsController.changeIndex(1)
})
}
.width('80%')
.justifyContent(FlexAlign.Start)
.margin({ left: 15 })
Row() {
Text('更多')
.fontSize(15)
.fontColor('#999999')
Image($r('app.media.arrow_right'))
.width(10)
.height(15)
.margin({ left: 5 })
}
.width(100)
.height('100%')
.padding({right:10})
}
.width('100%')
.height(60)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.height(50)
.backgroundColor(Color.White)
}
@Builder
tabContent() {
Tabs({ index: this.currentIndex, controller: this.tabsController }) {
TabContent() {
this.BuildGuideList()
}
TabContent() {
this.BuildShareList()
}
}
.onChange((index: number) => {
this.currentIndex = index
})
.width('100%')
.barWidth(0)
.animationDuration(0)
.backgroundColor(Color.White)
}
@Builder
BuildGuideList() {
Column() {
this.BuildListItem(
'一学就会的keynote教程番外篇】keynote 线下实训你们要的带练来了',
'查看'
)
this.BuildListItem(
'胆源性肝脏疾病一专题研讨会成功举行',
'查看'
)
}
.width('100%')
}
// 构建课件分享列表
@Builder
BuildShareList() {
Column() {
this.BuildListItem(
'肝病学新领域:肝硬化合并慢性肾脏病 | 深度综述大的并慢性肾脏病 | 深度综述并慢并慢性...',
'↓'
)
this.BuildListItem(
'【一学就会的keynote教程番外篇】keynote 线下实训你们要的带练来了',
'↓'
)
this.BuildListItem(
'段钟平教授:终末期肝病营养治疗的价值及研究进展',
'↓'
)
}
.width('100%')
}
// 构建列表项
@Builder
BuildListItem(title: string, action: string) {
Row() {
Text(title)
.fontSize(14)
.fontColor('#000000')
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(2)
.layoutWeight(1)
Text(action)
.fontSize(14)
.fontColor('#FF0000')
.margin({ left: 10 })
}
.width('95%')
.padding(10)
.backgroundColor('#f4f4f4')
.borderRadius(8)
.margin({ left: 10, bottom: 10, right:10 })
}
}
@@ -1,6 +1,8 @@
import { iconsModel } from '../model/HomeModel' import { HomeModel, iconsModel } from '../model/HomeModel'
import { patientDbManager, PatientEntity } from '@itcast/basic'; import {
import { promptAction, router } from '@kit.ArkUI'; applyListCallBacl,
authStore, BasicConstant, hdHttp, HdResponse, patientDbManager, PatientEntity } from '@itcast/basic';
import { promptAction, router, UIObserver, uiObserver } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit'; import { BusinessError } from '@kit.BasicServicesKit';
import { pushService } from '@kit.PushKit'; import { pushService } from '@kit.PushKit';
@@ -9,18 +11,36 @@ import { pushService } from '@kit.PushKit';
// name:string; // name:string;
// isRed:boolean; // isRed:boolean;
// } // }
import { ChatKitClient, LocalConversationRepo } from '@nimkit/chatkit';
import { HashMap } from '@kit.ArkTS';
@Component @Component
export struct HomeIconComp { export struct HomeIconComp {
@Prop iconList: iconsModel[]; @Prop iconList: iconsModel[];
@State patientIcon: string = ''; @State patientIcon: string = '';
@State patientName: string = '我的患者'; @State patientName: string = '我的患者';
@State patientRed: boolean = false;
@State videoIcon: string = ''; @State videoIcon: string = '';
@State videoName: string = '肝胆视频'; @State videoName: string = '肝胆视频';
@State zixunIcon: string = ''; @State zixunIcon: string = '';
@State consultation: string = '公益咨询'; @State consultation: string = '公益咨询';
@State consultationRed: boolean = false;
@State iconListtmp:iconsModel[]=[];
listener: (info: uiObserver.RouterPageInfo) => void = (info: uiObserver.RouterPageInfo) => {
let routerInfo: uiObserver.RouterPageInfo | undefined = this.queryRouterPageInfo();
if (info.pageId == routerInfo?.pageId) {
if (info.state == uiObserver.RouterPageState.ON_PAGE_SHOW) {
this.getRedCount()
} else if (info.state == uiObserver.RouterPageState.ON_PAGE_HIDE) {
}
}
}
aboutToAppear(): void { aboutToAppear(): void {
let uiObserver: UIObserver = this.getUIContext().getUIObserver();
uiObserver.on('routerPageUpdate', this.listener);
for (const icons of this.iconList) { for (const icons of this.iconList) {
if (icons.name === '我的患者') { if (icons.name === '我的患者') {
this.patientIcon = icons.img; this.patientIcon = icons.img;
@@ -30,19 +50,93 @@ export struct HomeIconComp {
this.zixunIcon = icons.img this.zixunIcon = icons.img
} }
} }
this.initList()
// for (let index = 0; index < this.iconList!.length; index++) {
// const iconModel = this.iconList![index] as iconsModel ;
// if (index == 0) {
// iconModel.isRed = true;
// }
// }
this.getRedCount()
}
for (let index = 0; index < this.iconList!.length; index++) { aboutToDisappear(): void {
const iconModel = this.iconList![index] as iconsModel ; let uiObserver: UIObserver = this.getUIContext().getUIObserver();
if (index == 0) { uiObserver.off('routerPageUpdate', this.listener);
iconModel.isRed = true; }
async getRedCount()
{
let unreadCount = LocalConversationRepo.getTotalUnreadCount() ?? 0
let consultcount=0
const result = await LocalConversationRepo.getConversationList(0,100)
if(result!=null&&result.conversationList!=null)
{
for (const conversation of result.conversationList) {
const uuid = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(conversation.conversationId);
// 假设 selectUserStyleWithModel 返回 "1" 表示 type=1
const style = await patientDbManager.getPatientTypeByUuid(uuid);
if (style != 1) {
if(conversation.unreadCount>0)
{
consultcount++
}
}
} }
} }
unreadCount=unreadCount-consultcount
if(unreadCount>0) {
this.patientRed=true
} else {
this.patientRed=false
hdHttp.post<string>(BasicConstant.applyList,{
expertUuid: authStore.getUser().uuid,
} as updateExtraData).then(async (res: HdResponse<string>) => {
let json:applyListCallBacl = JSON.parse(res+'') as applyListCallBacl;
if(json.code == 1) {
if(json.data!=null&&json.data.length>0) {
this.patientRed=true
}
}
}).catch((err: BusinessError) => {
})
}
if(consultcount>0) {
this.consultationRed=true
}
this.initList()
const hashMap: HashMap<string, string> = new HashMap();
hdHttp.httpReq<string>(BasicConstant.indexV2,hashMap).then(async (res: HdResponse<string>) => {
let json:HomeModel = JSON.parse(res+'') as HomeModel;
if(json.data!=null&&json.data.consult_list!=null&&json.data.consult_list.list!=null) {
consultcount=consultcount>0?consultcount:json.data.consult_list.list.length
if(consultcount>0) {
this.consultationRed=true
} else {
this.consultationRed=false
}
this.initList()
}
}).catch((err: BusinessError) => {
})
}
initList() {
const targetNews:iconsModel | undefined = this.iconList.find((model: iconsModel) => model.name === '肝胆新闻');
const targekejian:iconsModel | undefined = this.iconList.find((model: iconsModel) => model.name === '肝胆课件');
this.iconListtmp=[...[{ 'img': this.patientIcon, 'name': this.patientName,'isRed':this.patientRed } as iconsModel,
{ 'img': this.videoIcon, 'name': this.videoName} as iconsModel,
{'img':this.zixunIcon,'name':this.consultation,'isRed':this.consultationRed} as iconsModel],
{'img':targekejian == undefined?$r('app.media.home_kejian_icon'):targekejian?.img,'name':'肝胆课件','isRed':false} as iconsModel,
{'img':targetNews == undefined?$r('app.media.home_news_icon'):targetNews?.img,'name':'肝胆新闻','isRed':false} as iconsModel]
} }
build() { build() {
Row() { Row() {
Grid() { Grid() {
ForEach([{ 'img': this.patientIcon, 'name': this.patientName },{ 'img': this.videoIcon, 'name': this.videoName},{'img':this.zixunIcon,'name':this.consultation}], (item: iconsModel) => {//this.iconList ForEach(this.iconListtmp, (item: iconsModel) => {//this.iconList
GridItem(){ GridItem(){
Stack() { Stack() {
Column() { Column() {
@@ -69,11 +163,22 @@ export struct HomeIconComp {
url:'pages/VideoPage/VideoGandanPage', url:'pages/VideoPage/VideoGandanPage',
params:{"page":"首页"} params:{"page":"首页"}
}) })
} else if (item.name == '肝胆新闻') {
router.pushUrl({
url:'pages/News/GandanNewsPages'
})
} else if (item.name == '肝胆课件') {
router.pushUrl({
url:'pages/Courseware/CoursewarePage'
})
} }
}) })
}) })
}.width('100%').backgroundColor(Color.White) }.width('100%').backgroundColor(Color.White)
} }
} }
} }
export interface updateExtraData {
expertUuid: string
}
@@ -6,6 +6,7 @@ import { router } from '@kit.ArkUI'
export struct HomeSwiperComp { export struct HomeSwiperComp {
@Prop newslist: newsModel[]; @Prop newslist: newsModel[];
@Prop expertData:expertDetailModel; @Prop expertData:expertDetailModel;
@State isSuccessImage:boolean = false
build() { build() {
Column() { Column() {
@@ -13,17 +14,21 @@ export struct HomeSwiperComp {
ForEach(this.newslist, (item: newsModel,index:number) => { ForEach(this.newslist, (item: newsModel,index:number) => {
Stack({alignContent:Alignment.Center}) { Stack({alignContent:Alignment.Center}) {
Image(item.headImg) Image(item.headImg)
.alt($r('app.media.home_top_scroll_default'))
.objectFit(ImageFit.Fill)// 图片填充模式 .objectFit(ImageFit.Fill)// 图片填充模式
.onComplete(() => {
this.isSuccessImage = true
})
.width('100%').height('100%') .width('100%').height('100%')
if (index == 0) { if (index == 0) {
Column({space:5}){ Column({space:5}){
Text(this.expertData.realName+'专家工作室') Text(this.expertData.realName+'专家工作室')
.fontSize(19) .fontSize(19)
.fontColor(Color.White) .fontColor(this.isSuccessImage?Color.White:Color.Black)
.margin({left:20,top:60}) .margin({left:20,top:60})
Text(this.expertData.hospitalName) Text(this.expertData.hospitalName)
.fontSize(16) .fontSize(16)
.fontColor(Color.White) .fontColor(this.isSuccessImage?Color.White:Color.Black)
.margin({left:20}) .margin({left:20})
}.width('100%').alignItems(HorizontalAlign.Start) }.width('100%').alignItems(HorizontalAlign.Start)
} }
@@ -33,7 +38,7 @@ export struct HomeSwiperComp {
} else { } else {
router.pushUrl({ router.pushUrl({
url: 'pages/WebView/WebPage', // 目标url url: 'pages/WebView/WebPage', // 目标url
params: {url:item.path,title:item.title} params: {url:item.path,title:'新闻详情'}//item.title
}) })
} }
}) })
@@ -12,6 +12,7 @@ export struct ListCompGandan {
@State isEmptyViewVisible: boolean = false; // 控制显隐的状态变量 @State isEmptyViewVisible: boolean = false; // 控制显隐的状态变量
@Prop@Watch('onUpdate') sort:string='2' @Prop@Watch('onUpdate') sort:string='2'
@Prop pageType:string = ''
// @Consume@Watch('gotoTop') // @Consume@Watch('gotoTop')
// toTop:boolean; // toTop:boolean;
@Prop@Watch('onUpdate') @Prop@Watch('onUpdate')
@@ -67,7 +68,7 @@ export struct ListCompGandan {
this.hashMap.set('page', this.page+"") this.hashMap.set('page', this.page+"")
this.hashMap.set("sort", this.sort); this.hashMap.set("sort", this.sort);
this.hashMap.set('typeUuid', this.type_uuid) this.hashMap.set('typeUuid', this.type_uuid)
hdHttp.httpReq<string>(BasicConstant.videoByKeyWordsNew,this.hashMap).then(async (res: HdResponse<string>) => { hdHttp.httpReq<string>(this.pageType=='患教视频'?BasicConstant.patientVideoByKeyWordsNew:BasicConstant.videoByKeyWordsNew,this.hashMap).then(async (res: HdResponse<string>) => {
logger.info('Response videoByTypeNew'+res); logger.info('Response videoByTypeNew'+res);
let json:VideoMoreModel = JSON.parse(res+'') as VideoMoreModel; let json:VideoMoreModel = JSON.parse(res+'') as VideoMoreModel;
this.dialog.close() this.dialog.close()
@@ -19,10 +19,13 @@ import { BusinessError } from '@kit.BasicServicesKit';
import { VideoTypeModel,TypeList,VideoType } from '../model/VideoTypeModel' import { VideoTypeModel,TypeList,VideoType } from '../model/VideoTypeModel'
import { ArrayList } from '@kit.ArkTS'; import { ArrayList } from '@kit.ArkTS';
import { ListCompGandan } from '../components/ListCompGandan' import { ListCompGandan } from '../components/ListCompGandan'
import { TagListModel,TagList } from '../model/TagListModel' import { TagListModel,TagList } from '@itcast/basic/src/main/ets/models/TagListModel'
import { promptAction } from '@kit.ArkUI' import { promptAction } from '@kit.ArkUI'
import { it } from '@ohos/hypium';
@Component @Component
export struct SecondaryLink { export struct SecondaryLink {
@Prop pageType:string = ''
@State currentTagIndex: number = 0; // 一级列表焦点索引值 @State currentTagIndex: number = 0; // 一级列表焦点索引值
@State currentTagIndex2: number = -1; // 2级列表焦点索引值 @State currentTagIndex2: number = -1; // 2级列表焦点索引值
@State private tagLists:VideoType[] = []; // 一级列表数据 @State private tagLists:VideoType[] = []; // 一级列表数据
@@ -112,7 +115,7 @@ export struct SecondaryLink {
Row().height(10).width('100%') .backgroundColor('#efefef') Row().height(10).width('100%') .backgroundColor('#efefef')
Stack({alignContent:Alignment.Top}) Stack({alignContent:Alignment.Top})
{ {
ListCompGandan({ sort:this.sort,type_uuid:this.type_uuid,keywords:this.keywords}).padding({left:10,right:10}) ListCompGandan({ pageType:this.pageType,sort:this.sort,type_uuid:this.type_uuid,keywords:this.keywords}).padding({left:10,right:10})
if(this.isOpenSelect1) if(this.isOpenSelect1)
{ {
Column() Column()
@@ -126,6 +129,8 @@ export struct SecondaryLink {
.padding({left:5,right:5}) .padding({left:5,right:5})
.fontWeight(FontWeight.Regular) .fontWeight(FontWeight.Regular)
.fontSize(13) .fontSize(13)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontColor(this.tagPosition.indexOf(item)!=-1 ? $r('app.color.top_title'): $r('app.color.848284')) .fontColor(this.tagPosition.indexOf(item)!=-1 ? $r('app.color.top_title'): $r('app.color.848284'))
.textAlign(TextAlign.Center) .textAlign(TextAlign.Center)
.border({ width: 1, color:this.tagPosition.indexOf(item)!=-1 ? $r('app.color.top_title'): '#999999' }) .border({ width: 1, color:this.tagPosition.indexOf(item)!=-1 ? $r('app.color.top_title'): '#999999' })
@@ -213,76 +218,110 @@ export struct SecondaryLink {
} }
if(this.isOpenSelect) if(this.isOpenSelect)
{ {
Row() { if (this.pageType == '患教视频') {
// TODO:知识点: 2.一级列表绑定Scroller对象 Row() {
List() { List() {
// 性能知识点:此处为横向列表,列表项确定且数量较少,使用了ForEach,在列表项多的情况下,推荐使用LazyForeEach ForEach(this.tagLists, (item: VideoType, index: number) => {
ForEach(this.tagLists, (item: VideoType, index: number) => { ListItem() {
ListItem() { Column() {
Text(item.name) Text(item.name)
.width('100%') .width('100%')
.padding(10) .padding(10)
.fontWeight(FontWeight.Regular) .fontWeight(FontWeight.Regular)
.fontSize(15) .fontSize(15)
.fontColor(this.currentTagIndex === index ? $r('app.color.top_title'): $r('app.color.848284')) .fontColor(this.currentTagIndex === index ? $r('app.color.top_title') : Color.Gray)
.textAlign(TextAlign.Center) .onClick(() => {
.backgroundColor(this.currentTagIndex === index ? $r('sys.color.ohos_id_color_background') : '') this.currentTagIndex = index;
this.videoTitle = item.name
.onClick(() => { this.type_uuid = item.uuid
this.currentTagIndex = index; this.changeSelectRescourse()
this.contentData=this.arrayList[index]; })
Blank()
}) .width('95%')
} .height(0.5)
}) .backgroundColor(Color.Gray)
} .margin({left:10,right:10})
.backgroundColor('#efefef') }
.width('27%')
.height('100%')
// 二级列表
List() {
if(this.currentTagIndex==0)
{
ListItem() {
Row() {
Text('全部视频')
.fontSize(15)
.padding(10)
.fontColor(this.currentTagIndex2 === -2 ? $r('app.color.top_title'): $r('app.color.848284'))
} }
}.onClick(()=>{
this.videoTitle='全部视频'
this.currentTagIndex2=-2
this.changeSelectRescourse()
this.type_uuid=''
}) })
} }
ForEach(this.contentData, (item: TypeList,index: number) => { .backgroundColor(Color.White)
ListItem() { .width('100%')
Row() { .height('100%')
}.width('100%').alignItems(VerticalAlign.Top)
} else {
Row() {
// TODO:知识点: 2.一级列表绑定Scroller对象
List() {
// 性能知识点:此处为横向列表,列表项确定且数量较少,使用了ForEach,在列表项多的情况下,推荐使用LazyForeEach
ForEach(this.tagLists, (item: VideoType, index: number) => {
ListItem() {
Text(item.name) Text(item.name)
.fontSize(15) .width('100%')
.padding(10) .padding(10)
.fontColor(this.currentTagIndex2 === index&& this.videoTitle==item.name ? $r('app.color.top_title'): $r('app.color.848284')) .fontWeight(FontWeight.Regular)
.fontSize(15)
.fontColor(this.currentTagIndex === index ? $r('app.color.top_title') : $r('app.color.848284'))
.textAlign(TextAlign.Center)
.backgroundColor(this.currentTagIndex === index ? $r('sys.color.ohos_id_color_background') : '')
.onClick(() => {
this.currentTagIndex = index;
this.contentData = this.arrayList[index];
})
} }
}.onClick(()=>{
this.currentTagIndex2=index
this.videoTitle=item.name
this.type_uuid=item.uuid
this.changeSelectRescourse()
}) })
}
.backgroundColor('#efefef')
.width('27%')
.height('100%')
// 二级列表
List() {
if (this.currentTagIndex == 0) {
ListItem() {
Row() {
Text('全部视频')
.fontSize(15)
.padding(10)
.fontColor(this.currentTagIndex2 === -2 ? $r('app.color.top_title') : $r('app.color.848284'))
}
}.onClick(() => {
this.videoTitle = '全部视频'
this.currentTagIndex2 = -2
this.changeSelectRescourse()
this.type_uuid = ''
})
}
ForEach(this.contentData, (item: TypeList, index: number) => {
ListItem() {
Row() {
Text(item.name)
.fontSize(15)
.padding(10)
.fontColor(this.currentTagIndex2 === index && this.videoTitle == item.name ?
$r('app.color.top_title') : $r('app.color.848284'))
}
}.onClick(() => {
this.currentTagIndex2 = index
this.videoTitle = item.name
this.type_uuid = item.uuid
this.changeSelectRescourse()
})
})
}
.backgroundColor(Color.White)
.layoutWeight(1)
.divider({
strokeWidth: 1,
color: $r('app.color.common_gray_border'),
startMargin: 10,
endMargin: 10
}) })
} .height('100%')
.backgroundColor(Color.White) }.width('100%').alignItems(VerticalAlign.Top)
.layoutWeight(1) }
.divider({
strokeWidth: 1,
color: $r('app.color.common_gray_border'),
startMargin:10,
endMargin:10
})
.height('100%')
}.width('100%').alignItems(VerticalAlign.Top)
} }
} }
@@ -290,81 +329,117 @@ export struct SecondaryLink {
} }
} }
changeSelectRescourse() changeSelectRescourse() {
{
this.videoImg=$r('app.media.video_select') this.videoImg=$r('app.media.video_select')
this.videoImgt=$r('app.media.select_video') this.videoImgt=$r('app.media.select_video')
this.videoText=$r('app.color.top_title') this.videoText=$r('app.color.top_title')
this.isOpenSelect=false this.isOpenSelect=false
} }
initList() initList() {
{
let hashMap: HashMap<string, string> = new HashMap(); let hashMap: HashMap<string, string> = new HashMap();
hdHttp.httpReq<string>(BasicConstant.expertVideoTypeList,hashMap).then(async (res: HdResponse<string>) => { hdHttp.httpReq<string>(this.pageType=='患教视频'?BasicConstant.patientVideoNew:BasicConstant.expertVideoTypeList,hashMap).then(async (res: HdResponse<string>) => {
let json:VideoTypeModel = JSON.parse(res+'') as VideoTypeModel; let json:VideoTypeModel = JSON.parse(res+'') as VideoTypeModel;
this.tagLists=json.data if (this.pageType=='患教视频') {
for (let i = 0;i<this.tagLists.length;i++){ this.tagLists.push({name:'全部视频',uuid:''} as VideoType)
let datatagname=json.data[i].list; for (let j = 0; j < json.data.length; j++) {
let k:number[]=[]; let s: string;
for(let j=0;j<datatagname.length;j++) if ("z-BMS专家端" == (json.data[j].name)) {
{
let s:string;
if ("z-BMS专家端"==(datatagname[j].name)){
k.push(j);
} }
let title:string = datatagname[j].name let title: string = json.data[j].name
let digit:boolean = ChangeUtil.isFirstDigit(title.charAt(0));//判断首位是否是数字 let itemUuid:string = json.data[j].uuid
let asciiAlpha :boolean= ChangeUtil.isLetter(title.charAt(0));//字母 let digit: boolean = ChangeUtil.isFirstDigit(title.charAt(0)); //判断首位是否是数字
if(digit||asciiAlpha){ let asciiAlpha: boolean = ChangeUtil.isLetter(title.charAt(0)); //字母
let substring:string = ''; if (digit || asciiAlpha) {
if (title.length>=4){ let substring: string = '';
substring = title.substring(0, 4);//取出来前4位 if (title.length >= 4) {
}else if(title.length>=3){ substring = title.substring(0, 4); //取出来前4位
substring = title.substring(0, 3);//取出来前3位 } else if (title.length >= 3) {
}else if (title.length>=2){ substring = title.substring(0, 3); //取出来前3位
substring = title.substring(0, 2);//取出来前2位 } else if (title.length >= 2) {
substring = title.substring(0, 2); //取出来前2位
} }
if (substring.includes("-")){//含有- if (substring.includes("---")) { //含有-
let split: string[] = substring.split("-"); let split: string[] = substring.split("---");
if (split.length>1){ if (split.length > 1) {
s = split[split.length - 1] + title.substring(4, title.length); s = split[split.length - 1] + title.substring(4, title.length);
}else { } else {
s = title.substring(4, title.length); s = title.substring(4, title.length);
} }
datatagname[j].name=s; this.tagLists.push({name:s,uuid:itemUuid} as VideoType)
} else if (substring.includes("--")) {
let split: string[] = substring.split("--");
if (split.length > 1) {
s = split[split.length - 1] + title.substring(4, title.length);
} else {
s = title.substring(4, title.length);
}
this.tagLists.push({name:s,uuid:itemUuid} as VideoType)
} else if (substring.includes("-")) {
let split: string[] = substring.split("-");
if (split.length > 1) {
s = split[split.length - 1] + title.substring(4, title.length);
} else {
s = title.substring(4, title.length);
}
this.tagLists.push({name:s,uuid:itemUuid} as VideoType)
} else {
this.tagLists.push({name:title,uuid:itemUuid} as VideoType)
} }
} else {
this.tagLists.push({name:title,uuid:itemUuid} as VideoType)
} }
} }
const indexesToRemove = new Set(k); // 使用 Set 提高查找效率 } else {
const newArr = datatagname.filter((_, index) => !indexesToRemove.has(index)); this.tagLists = json.data
this.arrayList.add(newArr) for (let i = 0; i < this.tagLists.length; i++) {
k=[] let datatagname = json.data[i].list;
let k: number[] = [];
for (let j = 0; j < datatagname.length; j++) {
let s: string;
if ("z-BMS专家端" == (datatagname[j].name)) {
k.push(j);
}
let title: string = datatagname[j].name
let digit: boolean = ChangeUtil.isFirstDigit(title.charAt(0)); //判断首位是否是数字
let asciiAlpha: boolean = ChangeUtil.isLetter(title.charAt(0)); //字母
if (digit || asciiAlpha) {
let substring: string = '';
if (title.length >= 4) {
substring = title.substring(0, 4); //取出来前4位
} else if (title.length >= 3) {
substring = title.substring(0, 3); //取出来前3位
} else if (title.length >= 2) {
substring = title.substring(0, 2); //取出来前2位
}
if (substring.includes("-")) {
let split: string[] = substring.split("-");
if (split.length > 1) {
s = split[split.length - 1] + title.substring(4, title.length);
} else {
s = title.substring(4, title.length);
}
datatagname[j].name = s;
}
}
}
const indexesToRemove = new Set(k); // 使用 Set 提高查找效率
const newArr = datatagname.filter((_, index) => !indexesToRemove.has(index));
this.arrayList.add(newArr)
k = []
}
} }
this.contentData=this.arrayList[0]; this.contentData=this.arrayList[0];
}).catch((err: BusinessError) => {
}) })
let hashMap1: HashMap<string, string> = new HashMap();
hashMap1.set("type", "2")
hdHttp.post<string>(BasicConstant.tagList,{ hdHttp.post<string>(BasicConstant.tagList,{
type:'2' type:this.pageType=='患教视频'?'5':'2'
} as TagModel).then(async (res: HdResponse<string>) => { } as TagModel).then(async (res: HdResponse<string>) => {
let json:TagListModel = JSON.parse(res+'') as TagListModel; let json:TagListModel = JSON.parse(res+'') as TagListModel;
this.tags=json.data this.tags=json.data
}).catch((err: BusinessError) => {
}) })
} }
} }
interface TagModel interface TagModel {
{
type:string type:string
} }
@@ -54,8 +54,8 @@ export struct SpeciallyEStandingComp {
.height(45).width('95%') .height(45).width('95%')
.onClick(()=>{ .onClick(()=>{
router.pushUrl({ router.pushUrl({
url: 'pages/WebView/WebPage', // 目标url url: 'pages/WebView/WebPage',
params: {url:item.url,title:item.name} params: {url:item.url,title:'专题E站'}//item.name
}) })
}) })
}) })
@@ -5,7 +5,7 @@ export interface VideoTypeModel {
} }
export interface VideoType{ export interface VideoType{
uuid:string;
name:string; name:string;
list:TypeList[]; list:TypeList[];
} }
@@ -0,0 +1,26 @@
import { SecondaryLink } from '../components/SecondaryLink';
import router from '@ohos.router';
import { HdNav } from '@itcast/basic'
@Component
export struct EducationVideo {
@State isShowNaviLeft:boolean = false
@State rightBottom:string = ''
@State rightBottomPath:string = ''
@State rightBottomTitle:string = ''
build() {
Column() {
HdNav({ title: '患教视频', showRightIcon: true, showLeftIcon:true,showRightText:false,rightIcon:$r('app.media.selected_hospital_ws'),rightItemAction:()=>{
router.pushUrl({
url:'pages/SearchPage/VideoSearchPage',
params:{'pageName':'视频'}
})
}})
SecondaryLink({pageType:'患教视频'}).layoutWeight(1)
}.width('100%')
.height('100%')
.backgroundColor($r('app.color.top_bg'))
}
}
+43 -1
View File
@@ -6,10 +6,11 @@ import { HomeReplayVideoComp } from '../components/HomeReplayVideoComp'
import { getDisplayWindowWidth } from 'media-player-common' import { getDisplayWindowWidth } from 'media-player-common'
import { BusinessError } from '@kit.BasicServicesKit'; import { BusinessError } from '@kit.BasicServicesKit';
import HashMap from '@ohos.util.HashMap'; import HashMap from '@ohos.util.HashMap';
import { BasicConstant,hdHttp, HdResponse ,logger,HdHomeNav} from '@itcast/basic/Index' import { BasicConstant,hdHttp, HdResponse ,logger,HdHomeNav, ChangeUtil} from '@itcast/basic/Index'
import { HomeModel,dataModel, newsModel,iconsModel } from '../model/HomeModel'; import { HomeModel,dataModel, newsModel,iconsModel } from '../model/HomeModel';
import { DefaultHintProWindows,SignPopWindow,HdLoadingDialog } from '@itcast/basic' import { DefaultHintProWindows,SignPopWindow,HdLoadingDialog } from '@itcast/basic'
import { promptAction, router } from '@kit.ArkUI'; import { promptAction, router } from '@kit.ArkUI';
import { HomeGanDanFileComp } from '../components/HomeGanDanFileComp'
@Entry @Entry
@Component @Component
@@ -21,6 +22,9 @@ export struct HomePage {
// toTop:boolean; // toTop:boolean;
@State hintMessage:string = ''; @State hintMessage:string = '';
@State signData:Record<string,string> = {}; @State signData:Record<string,string> = {};
@State rightBottom:string = ''
@State rightBottomPath:string = ''
@State rightBottomTitle:string = ''
scroller:Scroller = new Scroller() scroller:Scroller = new Scroller()
@@ -64,10 +68,12 @@ export struct HomePage {
gotoTop() { gotoTop() {
this.scroller.scrollToIndex(0); this.scroller.scrollToIndex(0);
this.initData() this.initData()
this.getAppActivity()
} }
aboutToAppear(): void { aboutToAppear(): void {
this.initData() this.initData()
this.getAppActivity()
} }
initData() { initData() {
@@ -112,6 +118,25 @@ export struct HomePage {
}) })
} }
getAppActivity() {
const hashMap: HashMap<string, string> = new HashMap()
this.dialog.open()
hashMap.clear()
hashMap.set('id','6')
hdHttp.httpReq<string>(BasicConstant.getAppActivity,hashMap).then(async (res: HdResponse<string>) => {
logger.info('Response addBonusPoints'+res)
this.dialog.close()
let json:Record<string,string | Record<string,string>> = JSON.parse(res+'') as Record<string,string | Record<string,string>>
if (json.code == '200') {
this.rightBottom = json.data['img']
this.rightBottomPath = json.data['url_path']
this.rightBottomTitle = json.data['name']
}
}).catch((err: BusinessError) => {
this.dialog.close()
})
}
build() { build() {
Stack(){ Stack(){
Scroll(this.scroller) { Scroll(this.scroller) {
@@ -134,6 +159,9 @@ export struct HomePage {
if (this.homeData.video_list && this.homeData.video_list.length > 0) { if (this.homeData.video_list && this.homeData.video_list.length > 0) {
HomeReplayVideoComp({ videoList: this.homeData.video_list }) HomeReplayVideoComp({ videoList: this.homeData.video_list })
} }
if (this.homeData.gandanfile_list && this.homeData.gandanfile_list.length > 0) {
HomeGanDanFileComp({ gandanFileList:this.homeData.gandanfile_list,guideList:this.homeData.guide_ist })
}
} }
} }
.edgeEffect(EdgeEffect.Spring) .edgeEffect(EdgeEffect.Spring)
@@ -164,10 +192,24 @@ export struct HomePage {
}) })
} }
}) })
Image(BasicConstant.urlImage+this.rightBottom)
.width(76)
.height(40)
.position({ x: '100%', y: '100%' })
.translate({ x: -76, y: -76 })
.visibility(ChangeUtil.stringIsUndefinedAndNull(this.rightBottom)?Visibility.None:Visibility.Visible)
.onClick(()=>{
router.pushUrl({
url: 'pages/WebView/WebPage',
params: {'title':this.rightBottomTitle,'url':this.rightBottomPath}
})
})
} }
.alignContent(Alignment.Top) .alignContent(Alignment.Top)
.backgroundColor('#f4f4f4') .backgroundColor('#f4f4f4')
.width('100%') .width('100%')
.height('100%') .height('100%')
} }
} }
+20 -9
View File
@@ -1,10 +1,11 @@
import { HdNav } from '@itcast/basic' import { HdNav } from '@itcast/basic'
import { ListCompBack } from '../components/ListCompBack' import { ListCompBack } from '../components/ListCompBack'
import { router } from '@kit.ArkUI';
@Entry @Entry
@Component @Component
export struct PlayBack { export struct PlayBack {
@State params:Record<string, string> = router.getParams() as Record<string, string>
@State notselectImg: ResourceStr = $r('app.media.triangle_normal'); @State notselectImg: ResourceStr = $r('app.media.triangle_normal');
@State selectImg: ResourceStr = $r('app.media.triangle_green_theme'); @State selectImg: ResourceStr = $r('app.media.triangle_green_theme');
@State yearWords:Array<string> =[] @State yearWords:Array<string> =[]
@@ -16,22 +17,32 @@ export struct PlayBack {
@State timeText:string='会议时间'; @State timeText:string='会议时间';
@State typeText:string='会议类别'; @State typeText:string='会议类别';
@State type:string=''; @State type:string='';
onPageShow(): void { @State isShowNaviRight:boolean = false
console.log('VideoPage onPageShow!');
}
onPageHide(): void {
console.log('VideoPage onPageHide!');
}
aboutToAppear(): void { aboutToAppear(): void {
this.getSelect() this.getSelect()
if (this.params) {
if (this.params["pageName"] == '肝胆视频') {
this.isShowNaviRight = true
}
}
} }
build() { build() {
Column() Column()
{ {
HdNav({ title: '肝胆回放', showRightIcon: false, showLeftIcon: true}) if (this.isShowNaviRight) {
HdNav({ title: '肝胆视频', showRightIcon: true, showLeftIcon:true,showRightText:false,showTwoRightItem:true,twoRightIcon:$r('app.media.video_gandan_navigation2'),rightIcon:$r('app.media.selected_hospital_ws'),rightItemAction:()=>{
router.pushUrl({
url:'pages/SearchPage/VideoSearchPage',
params:{'pageName':'视频'}
})
},twoRightItemAction:()=>{
router.back()
}})
} else {
HdNav({ title: '肝胆回放', showRightIcon: false, showLeftIcon: true})
}
Row() { Row() {
Row() { Row() {
@@ -1,14 +1,18 @@
import { SecondaryLink } from '../components/SecondaryLink'; import { SecondaryLink } from '../components/SecondaryLink';
import router from '@ohos.router'; import router from '@ohos.router';
import { BasicConstant, HdNav } from '@itcast/basic' import { BasicConstant, ChangeUtil, hdHttp, HdNav, HdResponse } from '@itcast/basic'
import { SwiperComp } from '../components/SwiperComp' import { SwiperComp } from '../components/SwiperComp'
import { emitter } from '@kit.BasicServicesKit'; import { BusinessError, emitter } from '@kit.BasicServicesKit';
import { getDisplayWindowWidth } from 'media-player-common' import { getDisplayWindowWidth } from 'media-player-common'
import { HashMap } from '@kit.ArkTS';
@Component @Component
export struct VideoGandan { export struct VideoGandan {
@State params:Record<string, string> = router.getParams() as Record<string, string> @State params:Record<string, string> = router.getParams() as Record<string, string>
@State isShowNaviLeft:boolean = false @State isShowNaviLeft:boolean = false
@State rightBottom:string = ''
@State rightBottomPath:string = ''
@State rightBottomTitle:string = ''
aboutToAppear(): void { aboutToAppear(): void {
if (this.params) { if (this.params) {
@@ -16,19 +20,85 @@ export struct VideoGandan {
this.isShowNaviLeft = true this.isShowNaviLeft = true
} }
} }
this.getAppActivity()
}
getAppActivity() {
const hashMap: HashMap<string, string> = new HashMap()
hashMap.clear()
hashMap.set('id','6')
hdHttp.httpReq<string>(BasicConstant.getAppActivity,hashMap).then(async (res: HdResponse<string>) => {
let json:Record<string,string | Record<string,string>> = JSON.parse(res+'') as Record<string,string | Record<string,string>>
if (json.code == '200') {
this.rightBottom = json.data['img']
this.rightBottomPath = json.data['url_path']
this.rightBottomTitle = json.data['name']
}
}).catch((err: BusinessError) => {
})
} }
build() { build() {
Column() { Column() {
HdNav({ title: '肝胆视频', showRightIcon: true, showLeftIcon: this.isShowNaviLeft,showRightText:false,rightIcon:$r('app.media.selected_hospital_ws'),rightItemAction:()=>{ HdNav({ title: '肝胆视频', showRightIcon: true, showLeftIcon: this.isShowNaviLeft,showRightText:false,showTwoRightItem:true,twoRightIcon:$r('app.media.video_gandan_navigation_change'),rightIcon:$r('app.media.selected_hospital_ws'),rightItemAction:()=>{
router.pushUrl({ router.pushUrl({
url:'pages/SearchPage/VideoSearchPage', url:'pages/SearchPage/VideoSearchPage',
params:{'pageName':'视频'} params:{'pageName':'视频'}
}) })
},twoRightItemAction:()=>{
router.pushUrl({
url:'pages/VideoPage/PlayBackPage',
params:{'pageName':'肝胆视频'}
})
}}) }})
SwiperComp().height(getDisplayWindowWidth().vp / 16 * 9) SwiperComp().height(getDisplayWindowWidth().vp / 16 * 9)
SecondaryLink().layoutWeight(1) SecondaryLink({pageType:'肝胆视频'})
.margin({bottom:55})
.layoutWeight(1)
Row(){
Image($r('app.media.video_gandan_bottom_icon'))
.size({width:22,height:22})
Text('患教视频')
.fontSize(16)
.fontColor(Color.White)
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height(53)
.position({y:'94%'})
.backgroundColor('rgb(63,199,193)')
.onClick(() => {
router.pushUrl({
url: 'pages/VideoPage/EducationVideoPage'
})
})
Image($r('app.media.send_follow_icon'))
.width(48)
.height(48)
.position({ x: '100%', y: '100%' })
.translate({ x: -48, y: -176 })
.visibility(ChangeUtil.stringIsUndefinedAndNull(this.rightBottom)?Visibility.None:Visibility.Visible)
.onClick(()=>{
router.pushUrl({
url: 'pages/Pay/SendFollowPage'
})
})
Image(BasicConstant.urlImage+this.rightBottom)
.width(76)
.height(40)
.position({ x: '100%', y: '100%' })
.translate({ x: -76, y: -126 })
.visibility(ChangeUtil.stringIsUndefinedAndNull(this.rightBottom)?Visibility.None:Visibility.Visible)
.onClick(()=>{
router.pushUrl({
url: 'pages/WebView/WebPage',
params: {'title':this.rightBottomTitle,'url':this.rightBottomPath}
})
})
}.width('100%') }.width('100%')
.height('100%') .height('100%')
.backgroundColor($r('app.color.top_bg')) .backgroundColor($r('app.color.top_bg'))
+29 -48
View File
@@ -7,11 +7,12 @@ import cryptoFramework from '@ohos.security.cryptoFramework';
import util from '@ohos.util'; import util from '@ohos.util';
import { BusinessError } from '@kit.BasicServicesKit'; import { BusinessError } from '@kit.BasicServicesKit';
import { rcp } from '@kit.RemoteCommunicationKit'; import { rcp } from '@kit.RemoteCommunicationKit';
import { http } from '@kit.NetworkKit';
@Entry @Entry
@Component @Component
export struct VideoPage { export struct VideoPage {
@State params:Record<string, string> = router.getParams() as Record<string, string>
@State notselectImg: ResourceStr = $r('app.media.triangle_normal'); @State notselectImg: ResourceStr = $r('app.media.triangle_normal');
@State selectImg: ResourceStr = $r('app.media.triangle_green_theme'); @State selectImg: ResourceStr = $r('app.media.triangle_green_theme');
@State monthWords:Array<string> =[] @State monthWords:Array<string> =[]
@@ -41,7 +42,7 @@ export struct VideoPage {
{ {
Column() Column()
{ {
HdNav({ title: '肝胆会议', showRightIcon: false, showLeftIcon: false,showRightText:true,rightText:'扫一扫',rightTextColor:$r('app.color.main_color'),rightItemAction:()=>{ HdNav({ title: '肝胆会议',showLeftIcon:this.params?true:false, showRightIcon: false,showRightText:true,rightText:'扫一扫',rightTextColor:$r('app.color.main_color'),rightItemAction:()=>{
if (ScanUtil.canIUseScan()) { if (ScanUtil.canIUseScan()) {
ScanUtil.startScanForResult().then((scanResult) => { ScanUtil.startScanForResult().then((scanResult) => {
decryptString(scanResult.originalValue) decryptString(scanResult.originalValue)
@@ -98,7 +99,7 @@ export struct VideoPage {
.layoutWeight(1) .layoutWeight(1)
.justifyContent(FlexAlign.Center) .justifyContent(FlexAlign.Center)
.onClick(() => { .onClick(() => {
router.pushUrl({url:'pages/VideoPage/PlayBackPage'}) router.pushUrl({url:'pages/VideoPage/PlayBackPage', params:{'pageName':'肝胆会议'}})
}) })
@@ -265,57 +266,37 @@ async function decryptString(str:string) {
console.error("解密失败:", error) console.error("解密失败:", error)
} }
} }
//liveScanSuccess}8c30fb02189a44ed95d856f39a1fe792}1CBMDQbuOX3xbxAcxE5,Ios测试,expert/2025-07-09/7a4e8aa78bcb495b87ede343b7763ef9.jpg
//liveScanSuccess}8c30fb02189a44ed95d856f39a1fe792}1CBMDQbuOX3xbxAcxE5,Ios测试,expert/2025-07-09/7a4e8aa78bcb495b87ede343b7763ef9.jpg
//liveScanSuccess}8c30fb02189a44ed95d856f39a1fe792}1CBMDQbuOX3xbxAcxE5,Ios测试,expert/2025-07-09/7a4e8aa78bcb495b87ede343b7763ef9.jpg
// PfLvaZPedZHfbA0uwxOiOF76Du652Bsm89X1x1BlPoADdXYM1Tvg4PHHXXhK7BP1XSBDBc5aQbgw9+K9+Vr4HZfYQ9BKFtpz1bDPF+FiIsb+P6hXUwIYu5dy9EJjZUZBFJr8GQibSH3XwyqjqmsHAW3AVOWYZY6qvNenjoGjmjR0Msts21E+E31RBCK3Kvaj
// PfLvaZPedZHfbA0uwxOiOF76Du652Bsm89X1x1BlPoADdXYM1Tvg4PHHXXhK7BP1XSBDBc5aQbgw9+K9+Vr4HZfYQ9BKFtpz1bDPF+FiIsb+P6hXUwIYu5dy9EJjZUZBFJr8GQibSH3XwyqjqmsHAW3AVOWYZY6qvNenjoGjmjR0Msts21E+E31RBCK3Kvaj
// PfLvaZPedZHfbA0uwxOiOF76Du652Bsm89X1x1BlPoADdXYM1Tvg4PHHXXhK7BP1XSBDBc5aQbgw9%2BK9%2BVr4HZfYQ9BKFtpz1bDPF%2BFiIsb%2BP6hXUwIYu5dy9EJjZUZBFJr8GQibSH3XwyqjqmsHAW3AVOWYZY6qvNenjoGjmjR0Msts21E%2BE31RBCK3Kvaj
// B2e%2B96mEodDQ5EIsmcpfh6cJbH7WhjSkXrIz8JtGf2gREqwSckolodE2%2FdNTTssxvwLTAltHlphKiSYWT91IpBUkt30diwj0VF1elbS193JwyOF4AU%2FhJ%2FRYQtxUHrB%2BlRo65QEhktb7z79lsRuJwUKISPEbTkrCVzRx9QDGcihdsOe44ABgLH%2BmyFhgEsMC
// B2e%2B96mEodDQ5EIsmcpfh6cJbH7WhjSkXrIz8JtGf2gREqwSckolodE2%2FdNTTssxvwLTAltHlphKiSYWT91IpBUkt30diwj0VF1elbS193JwyOF4AU%2FhJ%2FRYQtxUHrB%2BlRo65QEhktb7z79lsRuJwUKISPEbTkrCVzRx9QDGcihdsOe44ABgLH%2BmyFhgEsMC
// B2e+96mEodDQ5EIsmcpfh6cJbH7WhjSkXrIz8JtGf2gREqwSckolodE2/dNTTssxvwLTAltHlphKiSYWT91IpBUkt30diwj0VF1elbS193JwyOF4AU/hJ/RYQtxUHrB+lRo65QEhktb7z79lsRuJwUKISPEbTkrCVzRx9QDGcihdsOe44ABgLH+myFhgEsMC
async function pushScanPage(str:string) { async function pushScanPage(str:string) {
const liceScanSuccess = `liveScanSuccess}${str}}${authStore.getUser().uuid},${authStore.getUser().realName},${authStore.getUser().photo}` const liceScanSuccess = `liveScanSuccess}${str}}${authStore.getUser().uuid},${authStore.getUser().realName},${authStore.getUser().photo}`
const textEncoder = new util.TextEncoder() const scanData = await AESEncryptionDecryption.aesEncrypt(liceScanSuccess,BasicConstant.ExpertAesKey)
const inputData: Uint8Array = textEncoder.encodeInto(liceScanSuccess) const encodedString = encodeURIComponent(scanData)
const scanData = await AESEncryptionDecryption.aes128Encrypt(inputData,BasicConstant.ExpertAesKey) let httpRequest = http.createHttp();
const base64Data = AESEncryptionDecryption.customBase64Encode(scanData) let data = "message="+encodedString;
const encodedString = encodeURIComponent(base64Data) httpRequest.request(
const postContent = new rcp.MultipartForm({ BasicConstant.sendWebsocketMsg,
'message': encodedString {
}) method: http.RequestMethod.POST,
const session = rcp.createSession() header: { 'Content-Type': 'application/x-www-form-urlencoded' },
session.post(BasicConstant.sendWebsocketMsg, { extraData: data,
data: postContent, connectTimeout: 60000,
headers: { readTimeout: 60000,
'Content-Type': 'application/json' }, (err, data) => {
} if (!err) {
}) console.info('Result:' + JSON.stringify(data.result));
.then((response) => { let json:Record<string,string> = JSON.parse(data.result+'') as Record<string,string>
let json:Record<string,string> = JSON.parse(response+'') as Record<string,string>
if(json.code == '1') { if(json.code == '1') {
router.pushUrl({url:'pages/VideoPage/CustomScanPage'}) router.pushUrl({url:'pages/VideoPage/CustomScanResultPage',params: {"id":str}})
} else { } else {
console.error('获取患者信息失败:'+json.message)
promptAction.showToast({ message: String(json.message), duration: 1000 }) promptAction.showToast({ message: String(json.message), duration: 1000 })
} }
}) } else {
.catch((err: BusinessError) => { console.info('error:' + JSON.stringify(err)); // Unsubscribe from HTTP response header events
console.error(`Response err: Code is ${JSON.stringify(err.code)}, message is ${JSON.stringify(err)}`); httpRequest.off('headersReceive'); // When the request is exhausted, call the destroy method to actively destroy it.
}) httpRequest.destroy();
// hdHttp.post<string>(BasicConstant.sendWebsocketMsg, { }
// "message":base64Data })
// } as Record<string,string>).then(async (res: HdResponse<string>) => {
// console.info('Response caseUuid'+res);
// let json:Record<string,string | Record<string,string> | Array<Record<string,string>>> = JSON.parse(res+'') as Record<string,string | Record<string,string> | Array<Record<string,string>>>;
// if(json.code == '1') {
//
// } else {
// console.error('获取患者信息失败:'+json.message)
// promptAction.showToast({ message: String(json.message), duration: 1000 })
// }
// }).catch((err: BusinessError) => {
// console.error(`Response fails: ${err}`);
// })
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -4,9 +4,13 @@ import { TwoSection } from '../view/TwoSection'
import { ThreeSection } from '../view/ThreeSection' import { ThreeSection } from '../view/ThreeSection'
import { FourSection } from '../view/FourSection' import { FourSection } from '../view/FourSection'
import { OtherList } from '../view/OtherList' import { OtherList } from '../view/OtherList'
import { HdNav,hdHttp,HdResponse,BasicConstant,ExpertData,authStore,RequestDefaultModel } from '@itcast/basic' import { HdNav,hdHttp,HdResponse,BasicConstant,ExpertData,authStore,RequestDefaultModel,
ChangeUtil,
logger,
HdLoadingDialog} from '@itcast/basic'
import { BusinessError, emitter } from '@kit.BasicServicesKit'; import { BusinessError, emitter } from '@kit.BasicServicesKit';
import HashMap from '@ohos.util.HashMap' import HashMap from '@ohos.util.HashMap'
import { router } from '@kit.ArkUI'
interface heroFirst { interface heroFirst {
id: string; id: string;
@@ -18,13 +22,23 @@ export struct MyHomePage {
@State title: string = '我的'; @State title: string = '我的';
@StorageProp('topHeight') @StorageProp('topHeight')
topHeight: number = 0 topHeight: number = 0
@State rightBottom:string = ''
@State rightBottomPath:string = ''
@State rightBottomTitle:string = ''
@State myInfoBackGround:string = ''; @State myInfoBackGround:string = '';
@State heroArray:Array<object> = []; @State heroArray:Array<object> = [];
@State expertData:object | string = new Object; @State expertData:object | string = new Object;
scroller = new Scroller() scroller = new Scroller()
dialog: CustomDialogController = new CustomDialogController({
builder: HdLoadingDialog({ message: '加载中...' }),
customStyle: true,
alignment: DialogAlignment.Center
})
aboutToAppear(): void { aboutToAppear(): void {
this.getAppActivity()
this.uploadBackImgAction(); this.uploadBackImgAction();
emitter.on({ eventId: BasicConstant.notification_home_tab_change }, (eventData: emitter.EventData) => { emitter.on({ eventId: BasicConstant.notification_home_tab_change }, (eventData: emitter.EventData) => {
if (eventData.data?.changeIndex === 2) { if (eventData.data?.changeIndex === 2) {
@@ -61,6 +75,25 @@ export struct MyHomePage {
}) })
} }
getAppActivity() {
const hashMap: HashMap<string, string> = new HashMap()
this.dialog.open()
hashMap.clear()
hashMap.set('id','6')
hdHttp.httpReq<string>(BasicConstant.getAppActivity,hashMap).then(async (res: HdResponse<string>) => {
logger.info('Response addBonusPoints'+res)
this.dialog.close()
let json:Record<string,string | Record<string,string>> = JSON.parse(res+'') as Record<string,string | Record<string,string>>
if (json.code == '200') {
this.rightBottom = json.data['img']
this.rightBottomPath = json.data['url_path']
this.rightBottomTitle = json.data['name']
}
}).catch((err: BusinessError) => {
this.dialog.close()
})
}
build() { build() {
Column() { Column() {
HdNav({ title: '我的', showLeftIcon:false , showRightIcon: false, hasBorder: true }) HdNav({ title: '我的', showLeftIcon:false , showRightIcon: false, hasBorder: true })
@@ -73,6 +106,8 @@ export struct MyHomePage {
Column() { Column() {
HeaderView({heroArray:this.heroArray,expertData:this.expertData}) HeaderView({heroArray:this.heroArray,expertData:this.expertData})
OneSection() OneSection()
TwoSection()
ThreeSection()
FourSection() FourSection()
OtherList() OtherList()
} }
@@ -83,6 +118,20 @@ export struct MyHomePage {
.layoutWeight(1) .layoutWeight(1)
.scrollBar(BarState.Off) .scrollBar(BarState.Off)
.align(Alignment.TopStart) .align(Alignment.TopStart)
Image(BasicConstant.urlImage+this.rightBottom)
.width(76)
.height(40)
.position({ x: '100%', y: '100%' })
.translate({ x: -76, y: -76 })
.visibility(ChangeUtil.stringIsUndefinedAndNull(this.rightBottom)?Visibility.None:Visibility.Visible)
.onClick(()=>{
router.pushUrl({
url: 'pages/WebView/WebPage',
params: {'title':this.rightBottomTitle,'url':this.rightBottomPath}
})
})
} }
.width('100%') .width('100%')
.height('100%') .height('100%')
@@ -137,7 +137,7 @@ export struct HeaderView {
ForEach([{'title':'随访患者数','content':this.expertData['expert_apply_num'] || '0'}, ForEach([{'title':'随访患者数','content':this.expertData['expert_apply_num'] || '0'},
{'title':'公益咨询数','content':this.expertData['consult_total_num'] || '0'}], {'title':'公益咨询数','content':this.expertData['consult_total_num'] || '0'}],
// {'title':'患者送花数','content':this.expertData['ping_flowewr_num'] || '0'}], // {'title':'患者送花数','content':this.expertData['ping_flowewr_num'] || '0'}],
(item:object)=>{ (item:object,index:number)=>{
Column(){ Column(){
Text(item['content']?.toString() || '0') Text(item['content']?.toString() || '0')
.fontSize(20) .fontSize(20)
@@ -148,6 +148,13 @@ export struct HeaderView {
.fontColor('#333333') .fontColor('#333333')
.height(17) .height(17)
}.height('100%').justifyContent(FlexAlign.Center) }.height('100%').justifyContent(FlexAlign.Center)
.onClick(()=>{
if (index == 0) {
router.pushUrl({url:'pages/Netease/imTabPage'})
} else {
router.pushUrl({ url: 'pages/Netease/PublicConsultationPage' })
}
})
}) })
} }
}.width('95%').height(65).borderRadius(5).backgroundColor(Color.White) }.width('95%').height(65).borderRadius(5).backgroundColor(Color.White)
@@ -11,11 +11,14 @@ export struct MyPageSectionItem {
.width(24).height(24) .width(24).height(24)
.objectFit(ImageFit.Auto) .objectFit(ImageFit.Auto)
Text(this.sectionItem.title) Text(this.sectionItem.title)
.maxFontSize(14) // .maxFontSize(14)
.minFontSize(6) // .minFontSize(6)
.maxLines(1) .fontSize(14)
// .maxLines(1)
.fontColor('#333333') .fontColor('#333333')
.margin({ top: 10 }) .margin({ top: 10 })
.textAlign(TextAlign.Center)
.width('120%')
}.width('100%') }.width('100%')
if (this.sectionItem.status) { if (this.sectionItem.status) {
Text().backgroundColor(Color.Red).width(10).height(10).borderRadius(5) Text().backgroundColor(Color.Red).width(10).height(10).borderRadius(5)
@@ -1,6 +1,7 @@
import { it } from "@ohos/hypium"; import { it } from "@ohos/hypium";
import { MyPageSectionClass } from "../model/MyPageSectionClass"; import { MyPageSectionClass } from "../model/MyPageSectionClass";
import { MyPageSectionItem } from '../view/MyPageSectionItem' import { MyPageSectionItem } from '../view/MyPageSectionItem'
import { router } from "@kit.ArkUI";
@Preview @Preview
@Component @Component
@@ -9,12 +10,12 @@ export struct ThreeSection {
@State currentIndex: number = 0; @State currentIndex: number = 0;
@State threeSectionList:Array<MyPageSectionClass> = [ @State threeSectionList:Array<MyPageSectionClass> = [
new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的账户','/pages/MyHomePage',false), // new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的账户','/pages/MyHomePage',false),
new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的积分','/pages/MyHomePage',false), // new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的积分','/pages/MyHomePage',false),
new MyPageSectionClass('threeItem',$r('app.media.app_icon'),'我的福利','/pages/MyHomePage',false), // new MyPageSectionClass('threeItem',$r('app.media.app_icon'),'我的福利','/pages/MyHomePage',false),
new MyPageSectionClass('fourItem',$r('app.media.app_icon'),'我的鲜花','/pages/MyHomePage',false), new MyPageSectionClass('oneItem',$r('app.media.my_page_myFlower'),'我的鲜花','pages/Flower/FlowerDetailsPage',false),
new MyPageSectionClass('fiveItem',$r('app.media.app_icon'),'课件明细','/pages/MyHomePage',false), new MyPageSectionClass('twoItem',$r("app.media.my_page_coursewareDetails"),'课件明细','/pages/MyHomePage',false),
new MyPageSectionClass('fiveItem',$r('app.media.app_icon'),'课程明细','/pages/MyHomePage',false) // new MyPageSectionClass('fiveItem',$r('app.media.app_icon'),'课程明细','/pages/MyHomePage',false)
]; ];
private getPagedItems(): Array<Array<MyPageSectionClass>> { private getPagedItems(): Array<Array<MyPageSectionClass>> {
@@ -43,6 +44,11 @@ export struct ThreeSection {
ForEach(pageItems, (item: MyPageSectionClass) => { ForEach(pageItems, (item: MyPageSectionClass) => {
GridItem() { GridItem() {
MyPageSectionItem({ sectionItem: item }) MyPageSectionItem({ sectionItem: item })
.onClick(()=>{
router.pushUrl({
url:item.path
})
})
} }
}, (item: MyPageSectionClass) => item.id) }, (item: MyPageSectionClass) => item.id)
} }
@@ -1,6 +1,7 @@
import { it } from "@ohos/hypium"; import { it } from "@ohos/hypium";
import { MyPageSectionClass } from "../model/MyPageSectionClass"; import { MyPageSectionClass } from "../model/MyPageSectionClass";
import { MyPageSectionItem } from '../view/MyPageSectionItem' import { MyPageSectionItem } from '../view/MyPageSectionItem'
import { router } from "@kit.ArkUI";
@Preview @Preview
@Component @Component
@@ -9,10 +10,10 @@ export struct TwoSection {
@State currentIndex: number = 0; @State currentIndex: number = 0;
@State twoSectionList:Array<MyPageSectionClass> = [ @State twoSectionList:Array<MyPageSectionClass> = [
new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的视频','/pages/MyHomePage',false), // new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的视频','pages/MyHomePage',false),
new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的课程','/pages/MyHomePage',false), // new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的课程','pages/MyHomePage',false),
new MyPageSectionClass('threeItem',$r('app.media.app_icon'),'我的下载','/pages/MyHomePage',false), new MyPageSectionClass('oneItem',$r('app.media.my_page_download'),'我的下载','pages/Download/AllDownloadPage',false),
new MyPageSectionClass('fourItem',$r('app.media.app_icon'),'我的收藏','/pages/MyHomePage',false) // new MyPageSectionClass('fourItem',$r('app.media.app_icon'),'我的收藏','pages/MyHomePage',false)
]; ];
private getPagedItems(): Array<Array<MyPageSectionClass>> { private getPagedItems(): Array<Array<MyPageSectionClass>> {
@@ -41,6 +42,11 @@ export struct TwoSection {
ForEach(pageItems, (item: MyPageSectionClass) => { ForEach(pageItems, (item: MyPageSectionClass) => {
GridItem() { GridItem() {
MyPageSectionItem({ sectionItem: item }) MyPageSectionItem({ sectionItem: item })
.onClick(()=>{
router.pushUrl({
url:item.path
})
})
} }
}, (item: MyPageSectionClass) => item.id) }, (item: MyPageSectionClass) => item.id)
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -26,7 +26,7 @@ export struct ItemCompPublic {
.fontColor($r('app.color.top_title')) .fontColor($r('app.color.top_title'))
.layoutWeight(1) .layoutWeight(1)
Text(this.item.createDate ? Text(this.item.createDate ?
this.item.createDate.length > 16 ? this.item.createDate.substring(1, 16) : this.item.createDate : '') this.item.createDate.length > 16 ? this.item.createDate.substring(0, 16) : this.item.createDate : '')
.fontSize(15).fontColor($r('app.color.top_title')).padding({ left: 5 }) .fontSize(15).fontColor($r('app.color.top_title')).padding({ left: 5 })
Text(this.unreadCount>100?'...':this.unreadCount+'') Text(this.unreadCount>100?'...':this.unreadCount+'')
.width(18) .width(18)
@@ -1,11 +1,10 @@
import { HMRouterMgr } from '@hadss/hmrouter'; import { HMRouterMgr } from '@hadss/hmrouter';
import { BasicConstant, TimestampUtil } from '@itcast/basic'; import { BasicConstant, customAttachment, TimestampUtil } from '@itcast/basic';
import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet'; import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet';
import { LengthMetrics, router } from '@kit.ArkUI' import { LengthMetrics, router } from '@kit.ArkUI'
import { ChatKitClient } from '@nimkit/chatkit'; import { ChatKitClient } from '@nimkit/chatkit';
import { customAttachment } from '@nimkit/chatkit_ui/src/main/ets/view/MessageComponentBuilder';
import { StringIsEmpty } from '@nimkit/common'; import { StringIsEmpty } from '@nimkit/common';
import { PatientTBean } from '../model/TeachModel'; import { PatientTBean } from '@itcast/basic/src/main/ets/models/TeachModel';
@Preview @Preview
@@ -1,9 +1,8 @@
import { HMRouterMgr } from '@hadss/hmrouter'; import { HMRouterMgr } from '@hadss/hmrouter';
import { BasicConstant, TimestampUtil } from '@itcast/basic'; import { BasicConstant, customAttachment, TimestampUtil } from '@itcast/basic';
import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet'; import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet';
import { LengthMetrics, router } from '@kit.ArkUI' import { LengthMetrics, router } from '@kit.ArkUI'
import { ChatKitClient } from '@nimkit/chatkit'; import { ChatKitClient } from '@nimkit/chatkit';
import { customAttachment } from '@nimkit/chatkit_ui/src/main/ets/view/MessageComponentBuilder';
import { StringIsEmpty } from '@nimkit/common'; import { StringIsEmpty } from '@nimkit/common';
import { PatientVBean } from '../model/TeachVideoModel'; import { PatientVBean } from '../model/TeachVideoModel';
@@ -1,5 +1,5 @@
import { PatientsData } from '@itcast/basic'; import { PatientsData } from '@itcast/basic';
@Observed
export class Groups { export class Groups {
/** /**
@@ -1,6 +1,7 @@
import { authStore, BaseBean, BasicConstant, import { authStore, BaseBean, BasicConstant,
ChatParam, ChatParam,
customAttachment,
hdHttp, HdLoadingDialog, HdNav, HdResponse, hdHttp, HdLoadingDialog, HdNav, HdResponse,
PhotoGrids, PhotoGrids,
preferenceStore, preferenceStore,
@@ -11,7 +12,6 @@ import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction, router } from '@kit.ArkUI'; import { promptAction, router } from '@kit.ArkUI';
import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet'; import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet';
import { ChatKitClient } from '@nimkit/chatkit'; import { ChatKitClient } from '@nimkit/chatkit';
import { customAttachment } from '@nimkit/chatkit_ui/src/main/ets/view/MessageComponentBuilder';
import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'; import { HMRouter, HMRouterMgr } from '@hadss/hmrouter';
@HMRouter({ @HMRouter({
@@ -19,6 +19,7 @@ export struct InterrogationDetailComp {
@State state: string='1' ; @State state: string='1' ;
@State createDate: string='' ; @State createDate: string='' ;
@State content: string='' ; @State content: string='' ;
@State quest: string='' ;
@State diseaseName: string='' ; @State diseaseName: string='' ;
@State step1_uuid: string='' ; @State step1_uuid: string='' ;
@State imgListurl:string[]=[] @State imgListurl:string[]=[]
@@ -63,6 +64,7 @@ export struct InterrogationDetailComp {
this.birthday=json.data.birthday this.birthday=json.data.birthday
this.createDate=json.data.create_date this.createDate=json.data.create_date
this.content=json.data.disease_describe this.content=json.data.disease_describe
this.quest=json.data.your_question
this.diseaseName=json.data.disease_name this.diseaseName=json.data.disease_name
this.step1_uuid=json.data.step1_uuid this.step1_uuid=json.data.step1_uuid
if(json.data.imgs!=null) if(json.data.imgs!=null)
@@ -141,16 +143,21 @@ export struct InterrogationDetailComp {
.alignSelf(ItemAlign.Start) .alignSelf(ItemAlign.Start)
.padding({ left: 10, right: 10, bottom: 10 }) .padding({ left: 10, right: 10, bottom: 10 })
Row() { Column() {
Text(this.content) Text(this.content)
.fontSize(14) .fontSize(14)
.fontColor($r('app.color.common_gray_03')) .fontColor($r('app.color.common_gray_03'))
.padding(9) .padding({left:9,right:9,top:9})
.backgroundColor($r('app.color.f6f6f6')) .width('100%')
.borderRadius(8) Text(this.quest)
.fontSize(14)
.fontColor($r('app.color.common_gray_03'))
.padding({left:9,right:9,bottom:9})
.width('100%') .width('100%')
} }
.margin({ left: 10, right: 10, bottom: 10 }) .margin({ left: 10, right: 10, bottom: 10 })
.backgroundColor($r('app.color.f6f6f6'))
.borderRadius(8)
PhotoGrids({ imgList: this.changeToImg(this.imgListurl) }) PhotoGrids({ imgList: this.changeToImg(this.imgListurl) })
Row() Row()
@@ -14,15 +14,15 @@ export struct MessageComp {
// @Param pathStack: NavPathStack = new NavPathStack() // @Param pathStack: NavPathStack = new NavPathStack()
@Param onUreadMessageChange?: (unreadCount?: number) => void = undefined @Param onUreadMessageChange?: (unreadCount?: number) => void = undefined
loadUnreadApplication = async () => { // loadUnreadApplication = async () => {
//
try { // try {
const unreadCount = await ContactRepo.getAddApplicationUnreadCount() // const unreadCount = await ContactRepo.getAddApplicationUnreadCount()
//
} catch (err) { // } catch (err) {
console.log(err) // console.log(err)
} // }
} // }
//获取会话列表未读数 //获取会话列表未读数
@@ -48,14 +48,14 @@ export struct MessageComp {
} }
) )
ChatKitClient.nim.friendService?.on('onFriendAddApplication', async (application: V2NIMFriendAddApplication) => { // ChatKitClient.nim.friendService?.on('onFriendAddApplication', async (application: V2NIMFriendAddApplication) => {
await this.loadUnreadApplication() // await this.loadUnreadApplication()
}) // })
try { // try {
await this.loadUnreadApplication() // await this.loadUnreadApplication()
} catch (err) { // } catch (err) {
console.log(err) // console.log(err)
} // }
this.loadConfig() this.loadConfig()
this.loadUnreadMessageCount() this.loadUnreadMessageCount()
} }
@@ -2,6 +2,7 @@ import {
authStore, authStore,
BasicConstant, ChangePhotoGrids, BasicConstant, ChangePhotoGrids,
ChangeUtil, ChangeUtil,
compressedImage,
hdHttp, hdHttp,
HdLoadingDialog, HdNav, HdLoadingDialog, HdNav,
HdResponse, HdResponse,
@@ -11,13 +12,16 @@ import { AnswerListBean } from "../model/ConsulModel"
import { promptAction, router } from "@kit.ArkUI" import { promptAction, router } from "@kit.ArkUI"
import { PerfactInputSheet } from "@itcast/basic/src/main/ets/Views/PerfactInputSheet" import { PerfactInputSheet } from "@itcast/basic/src/main/ets/Views/PerfactInputSheet"
import { BusinessError } from "@kit.BasicServicesKit" import { BusinessError } from "@kit.BasicServicesKit"
import { HashMap } from "@kit.ArkTS" import { buffer, HashMap } from "@kit.ArkTS"
import { StringIsEmpty } from "@nimkit/common" import { StringIsEmpty } from "@nimkit/common"
import { http } from "@kit.NetworkKit" import { http } from "@kit.NetworkKit"
import { TimestampBean } from "@itcast/basic/src/main/ets/utils/request" import { TimestampBean } from "@itcast/basic/src/main/ets/utils/request"
import { bundleManager } from "@kit.AbilityKit" import { bundleManager } from "@kit.AbilityKit"
import { rcp } from "@kit.RemoteCommunicationKit" import { rcp } from "@kit.RemoteCommunicationKit"
import { CryptoJS } from '@ohos/crypto-js' import { CryptoJS } from '@ohos/crypto-js'
import fileIo from "@ohos.file.fs"
import { image } from "@kit.ImageKit"
@Component @Component
export struct MyOpinionComp { export struct MyOpinionComp {
@State photos: string[] = [] @State photos: string[] = []
@@ -46,12 +50,12 @@ export struct MyOpinionComp {
this.photos.splice(this.removeIndex, 1) this.photos.splice(this.removeIndex, 1)
this.maxSelectNumber =6- this.photos.length; this.maxSelectNumber =6- this.photos.length;
ChangeUtil.convertUrisOrUrlsToBase64(this.photos).then(base64Array => { // this.convertUrisOrUrlsToBase64(this.photos).then(base64Array => {
console.info('转换结果:', base64Array+'转换个数:'+base64Array.length) // console.info('转换结果:', base64Array+'转换个数:'+base64Array.length)
this.base64Array = base64Array // this.base64Array = base64Array
}).catch((err:BusinessError) => { // }).catch((err:BusinessError) => {
console.error('批量转换失败:', err) // console.error('批量转换失败:', err)
}) // })
} }
private custom!:CustomDialogController; private custom!:CustomDialogController;
dialog: CustomDialogController = new CustomDialogController({ dialog: CustomDialogController = new CustomDialogController({
@@ -103,12 +107,12 @@ export struct MyOpinionComp {
this.photos.push(...selectedUris); this.photos.push(...selectedUris);
this.maxSelectNumber = 6- this.photos.length; this.maxSelectNumber = 6- this.photos.length;
ChangeUtil.convertUrisOrUrlsToBase64(this.photos).then(base64Array => { // this.convertUrisOrUrlsToBase64(this.photos).then(base64Array => {
console.info('转换结果:', base64Array+'转换个数:'+base64Array.length) // console.info('转换结果:', base64Array+'转换个数:'+base64Array.length)
this.base64Array = base64Array // this.base64Array = base64Array
}).catch((err:BusinessError) => { // }).catch((err:BusinessError) => {
console.error('批量转换失败:', err) // console.error('批量转换失败:', err)
}) // })
} }
}), }),
alignment: DialogAlignment.Bottom, alignment: DialogAlignment.Bottom,
@@ -128,13 +132,12 @@ export struct MyOpinionComp {
if(this.params.myAnswer.imgs!=null) { if(this.params.myAnswer.imgs!=null) {
this.photos.push(...this.changeToImgs(this.params.myAnswer.imgs.split(","))) this.photos.push(...this.changeToImgs(this.params.myAnswer.imgs.split(",")))
this.maxSelectNumber = 6- this.photos.length; this.maxSelectNumber = 6- this.photos.length;
// this.convertUrisOrUrlsToBase64(this.photos).then(base64Array => {
ChangeUtil.convertUrisOrUrlsToBase64(this.changeToImgs(this.params.myAnswer.imgs.split(","))).then(base64Array => { // console.info('转换结果:', base64Array+'转换个数:'+base64Array.length)
console.info('转换结果:', base64Array+'转换个数:'+base64Array.length) // this.base64Array = base64Array
this.base64Array = base64Array // }).catch((err:BusinessError) => {
}).catch((err:BusinessError) => { // console.error('批量转换失败:', err)
console.error('批量转换失败:', err) // })
})
} }
} else { } else {
this.text=preferenceStore.getItemString('MyOpinionComp'+this.params.uuid) this.text=preferenceStore.getItemString('MyOpinionComp'+this.params.uuid)
@@ -222,6 +225,7 @@ export struct MyOpinionComp {
async updateInterrogationAnswer() { async updateInterrogationAnswer() {
this.dialog.open() this.dialog.open()
this.base64Array =await this.convertUrisOrUrlsToBase64(this.photos)
this.hashMap.clear(); this.hashMap.clear();
this.hashMap.set("note",this.text); this.hashMap.set("note",this.text);
this.hashMap.set('uuid', this.params.uuid) this.hashMap.set('uuid', this.params.uuid)
@@ -239,8 +243,9 @@ export struct MyOpinionComp {
// let req = new rcp.Request("http://example.com", "POST", headers, simpleForm, cookies, transferRange, configuration); // let req = new rcp.Request("http://example.com", "POST", headers, simpleForm, cookies, transferRange, configuration);
} }
addInterrogationAnswer() { async addInterrogationAnswer() {
this.dialog.open() this.dialog.open()
this.base64Array =await this.convertUrisOrUrlsToBase64(this.photos)
this.hashMap.clear(); this.hashMap.clear();
this.hashMap.set("note",this.text); this.hashMap.set("note",this.text);
this.hashMap.set('step1_uuid', this.params.step1_uuid) this.hashMap.set('step1_uuid', this.params.step1_uuid)
@@ -314,6 +319,173 @@ export struct MyOpinionComp {
} }
async convertUrisOrUrlsToBase64(items: string[]): Promise<string[]> {
const results: string[] = [];
const MAX_SIZE_KB = 500; // 最大500KB
for (const item of items) {
try {
let base64Str: string;
// 处理本地文件URI
if (item.startsWith('file://')) {
base64Str = await this.processLocalImage(item, MAX_SIZE_KB);
}
// 处理网络URL
else if (item.startsWith('http://') || item.startsWith('https://') || ChangeUtil.isImageFileByRegex(item)) {
base64Str = await this.processNetworkImage(item, MAX_SIZE_KB);
}
// 处理其他类型资源
else {
throw new Error(`不支持的URI格式: ${item}`);
}
results.push(base64Str);
} catch (err) {
console.error(`转换失败: ${JSON.stringify(err)}`);
results.push(''); // 失败时返回空字符串
}
}
return results;
}
// 处理本地图片
private async processLocalImage(uri: string, maxSizeKB: number): Promise<string> {
try {
// 创建图片源
const fileSource = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(fileSource.fd);
// 获取原始图片信息
const pixelMap = await imageSource.createPixelMap();
const originalWidth = pixelMap.getPixelBytesNumber();
// 计算压缩比例,目标500KB以内
let quality = 0.8;
let targetWidth = 800; // 初始目标宽度
// 如果原始图片太大,逐步压缩
if (originalWidth > maxSizeKB * 1024) {
quality = Math.min(0.6, (maxSizeKB * 1024) / originalWidth);
targetWidth = Math.min(800, Math.sqrt((maxSizeKB * 1024) / originalWidth) * 100);
}
// 压缩图片
const compressedImageInfo = await compressedImage(pixelMap, targetWidth);
const compressedUri = compressedImageInfo.imageUri;
// 读取压缩后的图片
const compressedBuffer = await this.readLocalFile(compressedUri);
// 如果还是太大,进一步压缩
if (compressedBuffer.byteLength > maxSizeKB * 1024) {
const imagePackerApi = image.createImagePacker();
const packOpts: image.PackingOption = {
format: "image/jpeg",
quality: Math.max(0.3, quality * 0.8) // 进一步降低质量
};
const finalBuffer = await imagePackerApi.packing(imageSource, packOpts);
const finalBase64 = buffer.from(finalBuffer).toString('base64');
// 清理资源
imagePackerApi.release();
imageSource.release();
await fileIo.close(fileSource.fd);
return finalBase64;
}
// 转换为Base64
const base64Str = buffer.from(compressedBuffer).toString('base64');
// 清理资源
imageSource.release();
await fileIo.close(fileSource.fd);
return base64Str;
} catch (err) {
throw new Error(`本地图片处理失败: ${JSON.stringify(err)}`);
}
}
// 处理网络图片
private async processNetworkImage(url: string, maxSizeKB: number): Promise<string> {
try {
// 下载网络资源
const arrayBuffer = await ChangeUtil.downloadNetworkResource(url);
// 如果下载的图片太大,进行压缩
if (arrayBuffer.byteLength > maxSizeKB * 1024) {
// 创建临时文件来压缩
const tempUri = await this.saveToTempFile(arrayBuffer);
const compressedBase64 = await this.processLocalImage(tempUri, maxSizeKB);
// 清理临时文件
await this.deleteTempFile(tempUri);
return compressedBase64;
}
// 直接转换为Base64
return buffer.from(arrayBuffer).toString('base64');
} catch (err) {
throw new Error(`网络图片处理失败: ${JSON.stringify(err)}`);
}
}
// 保存到临时文件
private async saveToTempFile(arrayBuffer: ArrayBuffer): Promise<string> {
try {
// 使用应用缓存目录作为临时目录
const context = getContext(this);
const tempDir = context.cacheDir;
const tempFileName = `temp_${Date.now()}.jpg`;
const tempPath = `${tempDir}/${tempFileName}`;
const file = await fileIo.open(tempPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
await fileIo.write(file.fd, arrayBuffer);
await fileIo.close(file.fd);
return tempPath;
} catch (err) {
throw new Error(`临时文件保存失败: ${JSON.stringify(err)}`);
}
}
// 删除临时文件
private async deleteTempFile(uri: string): Promise<void> {
try {
await fileIo.unlink(uri);
} catch (err) {
console.warn(`临时文件删除失败: ${JSON.stringify(err)}`);
}
}
// 优化后的本地文件读取方法
async readLocalFile(uri: string): Promise<ArrayBuffer> {
try {
// 使用异步方式读取,避免阻塞
const file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
const stat = await fileIo.stat(uri);
const size = stat.size;
// 限制文件大小,避免内存溢出
if (size > 10 * 1024 * 1024) { // 10MB限制
throw new Error('文件过大,超过10MB限制');
}
const buffer = new ArrayBuffer(size);
await fileIo.read(file.fd, buffer);
await fileIo.close(file);
return buffer;
} catch (err) {
throw new Error(`文件读取失败: ${JSON.stringify(err)}`);
}
}
} }
@@ -3,6 +3,7 @@ import {
applyListModel, applyListModel,
authStore, BasicConstant, authStore, BasicConstant,
ChangeUtil, ChangeUtil,
EmptyViewComp,
hdHttp, HdLoadingDialog, HdResponse, hdHttp, HdLoadingDialog, HdResponse,
HdTwoNav, HdTwoNav,
PatientData, PatientData,
@@ -23,6 +24,104 @@ let collator = new Intl.Collator(I18n.System.getSystemLocale(), {
collation: 'pinyin', collation: 'pinyin',
caseFirst: 'lower' caseFirst: 'lower'
}) })
// 数据源类,用于LazyForEach
class PatientDataSource implements IDataSource {
private dataList: Array<Groups> = []
private listeners: DataChangeListener[] = []
constructor(data: Array<Groups>) {
this.dataList = data
}
totalCount(): number {
return this.dataList.length
}
getTotalCount(): number {
return this.dataList.length
}
getData(index: number): Groups {
return this.dataList[index]
}
registerDataChangeListener(listener: DataChangeListener): void {
// this.listeners.push(listener)
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const index = this.listeners.indexOf(listener)
if (index > -1) {
this.listeners.splice(index, 1)
}
}
notifyDataChange(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
// listener.onDataChanged(this.getTotalCount())
})
}
updateData(newData: Array<Groups>): void {
this.dataList = [...newData] // 创建新数组以确保引用变化
this.notifyDataChange()
}
}
// 患者数据源类,用于内层LazyForEach
class PatientItemDataSource implements IDataSource {
private dataList: Array<PatientsData> = []
private listeners: DataChangeListener[] = []
constructor(data: Array<PatientsData>) {
this.dataList = data
}
totalCount(): number {
return this.dataList.length
}
getTotalCount(): number {
return this.dataList.length
}
getData(index: number): PatientsData {
return this.dataList[index]
}
registerDataChangeListener(listener: DataChangeListener): void {
// this.listeners.push(listener)
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const index = this.listeners.indexOf(listener)
if (index > -1) {
this.listeners.splice(index, 1)
}
}
notifyDataChange(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
// listener.onDataChanged(this.getTotalCount())
})
}
updateData(newData: Array<PatientsData>): void {
this.dataList = [...newData] // 创建新数组以确保引用变化
this.notifyDataChange()
}
}
@Component @Component
export struct PatientListComp { export struct PatientListComp {
@@ -32,6 +131,9 @@ export struct PatientListComp {
private indexList: Array<string> = [] private indexList: Array<string> = []
private normalIndexValue: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', private normalIndexValue: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#'] 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#']
// 新增:存储每个分组在List中的实际位置
private groupPositions: Map<string, number> = new Map()
private indexUtil = I18n.getInstance(I18n.System.getSystemLocale()); private indexUtil = I18n.getInstance(I18n.System.getSystemLocale());
displayClass = display.getDefaultDisplaySync() displayClass = display.getDefaultDisplaySync()
@@ -49,15 +151,19 @@ export struct PatientListComp {
@State delectuuid:string='' @State delectuuid:string=''
@State delectname:string='' @State delectname:string=''
@State positionDelete:number = 0 @State positionDelete:number = 0
@State isEmptyViewVisible: boolean = false; // 控制显隐的状态变量
// 数据源实例
private patientDataSource: PatientDataSource = new PatientDataSource([])
// 缓存患者数据源,避免重复创建
// private patientItemDataSourceCache: Map<string, PatientItemDataSource> = new Map()
dialogDelete = new CustomDialogController({ dialogDelete = new CustomDialogController({
builder: DelectDialog( builder: DelectDialog(
{ {
name:this.delectname, name:this.delectname,
CallBack:()=>{ CallBack:()=>{
// regionDataGroupsList
// this.applyArray.splice(this.positionDelete,1)
this.deletePatientAction(this.delectuuid) this.deletePatientAction(this.delectuuid)
} }
} }
@@ -69,7 +175,7 @@ export struct PatientListComp {
private lifecycleOwner = HMRouterMgr.getCurrentLifecycleOwner() private lifecycleOwner = HMRouterMgr.getCurrentLifecycleOwner()
private handleCallback = () => { private handleCallback = () => {
this.PatientsData() this.PatientsData()
this.loadPatients() // this.loadPatients()
this.getApplyList() this.getApplyList()
} }
aboutToAppear() { aboutToAppear() {
@@ -77,9 +183,15 @@ export struct PatientListComp {
this.loadPatients() this.loadPatients()
this.getApplyList() this.getApplyList()
this.lifecycleOwner?.addObserver(HMLifecycleState.onShown,this.handleCallback) this.lifecycleOwner?.addObserver(HMLifecycleState.onShown,this.handleCallback)
// 初始化AlphabetIndexer的调试信息
console.info('AlphabetIndexer 初始化 - normalIndexValue:', this.normalIndexValue);
console.info('AlphabetIndexer 初始化 - indexList:', this.indexList);
} }
aboutToDisappear(): void { aboutToDisappear(): void {
this.lifecycleOwner?.removeObserver(HMLifecycleState.onShown,this.handleCallback) this.lifecycleOwner?.removeObserver(HMLifecycleState.onShown,this.handleCallback)
// 清理数据源缓存
// this.patientItemDataSourceCache.clear()
} }
getApplyList() { getApplyList() {
@@ -114,6 +226,9 @@ export struct PatientListComp {
PatientsData() PatientsData()
{ {
// 清空现有数据,准备重新加载
this.clearListData();
this.dialog.open() this.dialog.open()
hdHttp.post<string>(BasicConstant.urlExpert + 'patientListByGBK', { hdHttp.post<string>(BasicConstant.urlExpert + 'patientListByGBK', {
expertUuid: authStore.getUser().uuid expertUuid: authStore.getUser().uuid
@@ -123,29 +238,23 @@ export struct PatientListComp {
console.log('服务器返回的患者数据:', json); console.log('服务器返回的患者数据:', json);
if(json.data!=null) if(json.data!=null)
{ {
// let regionRequestData: PatientsData[]=[...json.data] if(json.data!=null&&json.data.length>0)
// regionRequestData =[...json.data] {
// regionRequestData.push(...json.data) this.total=json.data.length
// regionRequestData.push(...json.data) this.old=preferenceStore.getItemNumber('old_patient_num')
// regionRequestData.push(...json.data) if(this.old<this.total/10)
// regionRequestData.push(...json.data)
// regionRequestData.push(...json.data)
// regionRequestData.push(...json.data)
// regionRequestData.push(...json.data)
// regionRequestData.push(...json.data)
// regionRequestData.push(...json.data)
// regionRequestData.push(...json.data)
// regionRequestData.push(...json.data)
// this.collationData(regionRequestData);
if(json.data!=null&&json.data.length>0)
{ {
this.total=json.data.length this.needShow=true
if(this.old<this.total/10) preferenceStore.setItemNumber('old_patient_num',this.total/10)
{ }
this.needShow=true console.info('开始处理患者数据,总数:', this.total)
preferenceStore.setItemNumber('old_patient_num',this.total/10) this.collationData(json.data);
} this.isEmptyViewVisible=false
this.collationData(json.data); }
else
{
this.isEmptyViewVisible=true
this.dialog.close()
} }
@@ -153,8 +262,12 @@ export struct PatientListComp {
} catch (parseError) { } catch (parseError) {
this.dialog.close()
console.error('解析服务器响应失败:', parseError); console.error('解析服务器响应失败:', parseError);
} }
}).catch((err: BusinessError) => {
this.dialog.close();
console.error('获取患者数据失败:', err);
}) })
} }
@@ -165,6 +278,35 @@ export struct PatientListComp {
console.error('加载患者数据失败:', error); console.error('加载患者数据失败:', error);
} }
} }
// 清空list数据,准备重新加载
private clearListData(): void {
console.info('清空list数据,准备重新加载');
// 清空数据数组
this.regionInfo = [];
this.regionDataGroupsList = [];
this.indexList = [];
// 清空位置映射
this.groupPositions.clear();
// 重置状态
this.total = 0;
this.alphabetSelected = 0;
this.isEmptyViewVisible = false;
// 更新数据源
this.patientDataSource.updateData([]);
console.info('list数据清空完成');
}
// 公共方法:重新加载list数据
// public reloadList(): void {
// console.info('开始重新加载list数据');
// this.PatientsData();
// }
@Builder @Builder
itemHead(text: string) { itemHead(text: string) {
ListItem() { ListItem() {
@@ -198,7 +340,7 @@ export struct PatientListComp {
},rightItemAction2:()=>{ },rightItemAction2:()=>{
HMRouterMgr.push({pageUrl:'PatientsListSearchComp'}) HMRouterMgr.push({pageUrl:'PatientsListSearchComp'})
}}) }})
Stack({ }) { Stack({ alignContent:Alignment.Top}) {
List({ scroller: this.scroller, space: 0, initialIndex: 0 }) { List({ scroller: this.scroller, space: 0, initialIndex: 0 }) {
ListItem() { ListItem() {
Column() Column()
@@ -303,11 +445,16 @@ export struct PatientListComp {
.onClick(() => { .onClick(() => {
HMRouterMgr.push({pageUrl:'PatientsGroup',param:"我的患者"}) HMRouterMgr.push({pageUrl:'PatientsGroup',param:"我的患者"})
}) })
ForEach(this.regionDataGroupsList, (regionDataGroups: Groups, index) => { if (this.isEmptyViewVisible){
EmptyViewComp({promptText:'暂无随访患者',isVisibility:this.isEmptyViewVisible})
.width('100%')
.height('60%')
}
LazyForEach(this.patientDataSource, (regionDataGroups: Groups, index) => {
ListItemGroup({ header: this.itemHead(regionDataGroups.title) }) { ListItemGroup({ header: this.itemHead(regionDataGroups.title) }) {
ForEach(regionDataGroups.regionDataList, (regionData: PatientsData, index: number) => { LazyForEach(new PatientItemDataSource(regionDataGroups.regionDataList), (regionData: PatientsData, index: number) => {
ListItem() { ListItem() {
Column() Column()
{ {
@@ -320,7 +467,7 @@ export struct PatientListComp {
.borderRadius(8) .borderRadius(8)
Column() Column()
{ {
Text(regionData.nickname) Text(regionData.nickname+'')
.fontSize(16) .fontSize(16)
.fontColor($r('app.color.common_gray_01')) .fontColor($r('app.color.common_gray_01'))
.width('100%') .width('100%')
@@ -386,7 +533,7 @@ export struct PatientListComp {
this.dialogDelete.open() this.dialogDelete.open()
}) })
) )
}) }, (item: PatientsData, index: number) => JSON.stringify(item) )
} }
.width('100%') .width('100%')
@@ -402,19 +549,18 @@ export struct PatientListComp {
endMargin: 0 endMargin: 0
}) })
.onScrollIndex((start: number, end: number) => { .onScrollIndex((start: number, end: number) => {
if(start<3) console.info('onScrollIndex - start:', start, 'end:', end);
{ // 计算实际的分组索引位置
start=0 let groupIndex = this.calculateGroupIndexFromListPosition(start);
console.info('Calculated groupIndex:', groupIndex);
if (groupIndex >= 0 && groupIndex < this.indexList.length) {
let target = this.indexList[groupIndex];
console.info('Target group:', target);
let index = this.searchIndex(this.normalIndexValue, target);
this.alphabetSelected = index === -1 ? this.onSlideList(this.normalIndexValue, target) - 1 : index;
console.info('Updated alphabetSelected:', this.alphabetSelected);
} }
else
{
start=start-2
}
let target = this.indexList[start]
let indexList = this.normalIndexValue;
let index = this.searchIndex(indexList, target)
this.alphabetSelected = index === -1 ? this.onSlideList(indexList, target) - 1 : index;
}) })
.width('100%') .width('100%')
.scrollBar(BarState.Off) .scrollBar(BarState.Off)
@@ -426,20 +572,40 @@ export struct PatientListComp {
.usingPopup(true) .usingPopup(true)
.selected(this.alphabetSelected) .selected(this.alphabetSelected)
.onSelect((index: number) => { .onSelect((index: number) => {
console.info('AlphabetIndexer onSelect, index:', index);
console.info('Scroller instance:', this.scroller);
console.info('Current alphabetSelected:', this.alphabetSelected);
console.info('Total groups:', this.regionDataGroupsList.length);
if (index === 0) { if (index === 0) {
console.info('Scrolling to index 0');
this.scroller.scrollToIndex(0); this.scroller.scrollToIndex(0);
return; return;
} }
let pos = 0
let useValue = this.normalIndexValue let currentWord = this.normalIndexValue[index]; // 在右侧索引上的位置
let currentWord = useValue[index] // 在右侧索引上的位置 console.info('Current word:', currentWord);
console.info('Available positions:', Array.from(this.groupPositions.entries()));
pos = this.searchIndex(this.indexList, currentWord) console.info('IndexList:', this.indexList);
if (pos === -1) { // 查找该字母在List中的实际位置
return; let targetPosition = this.groupPositions.get(currentWord);
console.info('Target position:', targetPosition);
if (targetPosition !== undefined) {
console.info('Scrolling to position:', targetPosition);
this.scroller.scrollToIndex(targetPosition);
} else {
// 如果当前字母不存在,查找下一个存在的字母
let nextPosition = this.findNextAvailablePosition(currentWord);
console.info('Next available position:', nextPosition);
if (nextPosition !== -1) {
console.info('Scrolling to next position:', nextPosition);
this.scroller.scrollToIndex(nextPosition);
} else {
console.warn('No available position found for word:', currentWord);
}
} }
this.scroller.scrollToIndex(pos+2);
}) })
.id('id_hwid_region_choose_alphabetIndexer') .id('id_hwid_region_choose_alphabetIndexer')
.onAreaChange((oldValue: Area, newValue: Area) => { .onAreaChange((oldValue: Area, newValue: Area) => {
@@ -451,7 +617,8 @@ export struct PatientListComp {
size: '20vp', style: FontStyle.Normal, size: '20vp', style: FontStyle.Normal,
weight: FontWeight.Bolder weight: FontWeight.Bolder
}) })
.position({ x: this.displayClass.width / this.displayClass.densityPixels - 25 }) // 侧边栏位置 .position({ x: this.displayClass.width / this.displayClass.densityPixels - 30 })
.zIndex(998) // 确保在最上层
Column() Column()
{ {
@@ -469,12 +636,14 @@ export struct PatientListComp {
.backgroundImagePosition(Alignment.Center) .backgroundImagePosition(Alignment.Center)
.backgroundImage($r('app.media.patientgif')) .backgroundImage($r('app.media.patientgif'))
.backgroundColor('#44444444') .backgroundColor('#44444444')
.zIndex(999) // 确保在最上层
} }
.layoutWeight(1) .layoutWeight(1)
.width('100%') .width('100%')
}.width('100%') }.width('100%')
.height('100%') .height('100%')
} }
private collationData(regionRequestData: PatientsData[]) { private collationData(regionRequestData: PatientsData[]) {
@@ -493,7 +662,15 @@ export struct PatientListComp {
this.indexList = []; this.indexList = [];
this.regionDataGroupsList = []; this.regionDataGroupsList = [];
this.regionInfo.forEach((value: PatientsData) => { this.regionInfo.forEach((value: PatientsData) => {
let index = this.indexUtil.getIndex(value.nickname); let index =''
if(StringIsEmpty(value.nickname))
{
index='1'//nickname为空是虚假的账号,随便设置一个index
}
else
{
index = this.indexUtil.getIndex(value.nickname);
}
if(!ChangeUtil.isLetter(index)) if(!ChangeUtil.isLetter(index))
{ {
index='#' index='#'
@@ -517,10 +694,29 @@ export struct PatientListComp {
this.regionDataGroupsList.sort((a, b) => { this.regionDataGroupsList.sort((a, b) => {
return this.normalIndexValue.indexOf(a.title) - this.normalIndexValue.indexOf(b.title); return this.normalIndexValue.indexOf(a.title) - this.normalIndexValue.indexOf(b.title);
}); });
// 排序后重新更新indexList以保持一致性
this.indexList = this.regionDataGroupsList.map(group => group.title);
console.info('排序后的indexList:', this.indexList);
console.info('排序后的分组:', this.regionDataGroupsList.map(g => g.title));
// 计算每个分组在List中的实际位置
this.calculateGroupPositions();
// 更新数据源 - 创建新数组以确保引用变化
// console.info('更新数据源,分组数量:', this.regionDataGroupsList.length)
this.patientDataSource.updateData([...this.regionDataGroupsList])
// 更新缓存的患者数据源
// this.patientItemDataSourceCache.clear()
// this.regionDataGroupsList.forEach(group => {
// this.patientItemDataSourceCache.set(group.title, new PatientItemDataSource([...group.regionDataList]))
// })
console.info('数据源更新完成')
this.dialog.close() this.dialog.close()
} }
private searchIndex(indexList: string[], target: string): number { private searchIndex(indexList: string[], target: string): number {
return indexList.indexOf(target); return indexList.indexOf(target);
} }
@@ -533,6 +729,98 @@ export struct PatientListComp {
return 0; return 0;
} }
// 计算每个分组在List中的实际位置
private calculateGroupPositions(): void {
this.groupPositions.clear();
let currentPosition = 2; // 前两个是固定的ListItem(新的患者、患者分组)
console.info('开始计算分组位置,分组数量:', this.regionDataGroupsList.length);
this.regionDataGroupsList.forEach((group, index) => {
// 每个ListItemGroup在List中占用一个位置
this.groupPositions.set(group.title, currentPosition);
console.info(`分组 ${group.title} 位置: ${currentPosition}, 患者数量: ${group.regionDataList.length}`);
// 移动到下一个ListItemGroup的位置
currentPosition += 1;
});
console.info('分组位置映射完成:', Array.from(this.groupPositions.entries()));
}
// 从List位置计算对应的分组索引
private calculateGroupIndexFromListPosition(listPosition: number): number {
if (listPosition < 2) {
return 0; // 前两个位置对应第一个分组
}
// 计算ListItemGroup的索引
let groupIndex = listPosition - 2; // 减去前两个固定项
if (groupIndex >= 0 && groupIndex < this.regionDataGroupsList.length) {
return groupIndex;
}
return this.regionDataGroupsList.length - 1; // 默认返回最后一个分组
}
// 查找下一个可用的位置
private findNextAvailablePosition(currentWord: string): number {
let currentIndex = this.normalIndexValue.indexOf(currentWord);
if (currentIndex === -1) return -1;
// 从当前字母开始向后查找
for (let i = currentIndex; i < this.normalIndexValue.length; i++) {
let word = this.normalIndexValue[i];
let position = this.groupPositions.get(word);
if (position !== undefined) {
return position;
}
}
// 如果向后没找到,向前查找
for (let i = currentIndex - 1; i >= 0; i--) {
let word = this.normalIndexValue[i];
let position = this.groupPositions.get(word);
if (position !== undefined) {
return position;
}
}
return -1;
}
// 从本地数据中移除被删除的患者
private removePatientFromLocalData(patientUuid: string): void {
// 从 regionInfo 中移除
this.regionInfo = this.regionInfo.filter(patient => patient.uuid !== patientUuid)
// 从分组数据中移除
this.regionDataGroupsList.forEach(group => {
group.regionDataList = group.regionDataList.filter(patient => patient.uuid !== patientUuid)
})
// 移除空的分组
this.regionDataGroupsList = this.regionDataGroupsList.filter(group => group.regionDataList.length > 0)
// 更新索引列表
this.indexList = this.regionDataGroupsList.map(group => group.title)
// 重新计算分组位置
this.calculateGroupPositions();
// 更新总数
this.total = this.regionInfo.length
// 立即更新数据源
this.patientDataSource.updateData([...this.regionDataGroupsList])
// 更新缓存的患者数据源
// this.patientItemDataSourceCache.clear()
// this.regionDataGroupsList.forEach(group => {
// this.patientItemDataSourceCache.set(group.title, new PatientItemDataSource([...group.regionDataList]))
// })
}
deletePatientAction(patientUuid:string) { deletePatientAction(patientUuid:string) {
this.dialog.open() this.dialog.open()
hdHttp.post<string>(BasicConstant.cancelRes, { hdHttp.post<string>(BasicConstant.cancelRes, {
@@ -562,6 +850,7 @@ export struct PatientListComp {
} else { } else {
console.info('修改失败') console.info('修改失败')
} }
// 重新获取数据以确保同步
this.PatientsData() this.PatientsData()
} else { } else {
console.error('获取患者信息失败:'+json.message) console.error('获取患者信息失败:'+json.message)
@@ -3,7 +3,7 @@ import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'
import { PullToRefreshLayout, RefreshController } from 'refreshlib' import { PullToRefreshLayout, RefreshController } from 'refreshlib'
import { HashMap } from '@kit.ArkTS'; import { HashMap } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit'; import { BusinessError } from '@kit.BasicServicesKit';
import { PatientTBean, TeachModel } from '../model/TeachModel'; import { PatientTBean, TeachModel } from '@itcast/basic/src/main/ets/models/TeachModel';
import { promptAction } from '@kit.ArkUI'; import { promptAction } from '@kit.ArkUI';
import { ItemCompTeach } from '../components/ItemCompTeach'; import { ItemCompTeach } from '../components/ItemCompTeach';
import { PatientVBean, TeachVideoModel } from '../model/TeachVideoModel'; import { PatientVBean, TeachVideoModel } from '../model/TeachVideoModel';
@@ -1,11 +1,13 @@
import { TabBarCompModel } from '../model/TabBarCompModel' import { TabBarCompModel } from '../model/TabBarCompModel'
import { TabBarItems } from '../components/TabBarItems' import { TabBarItems } from '../components/TabBarItems'
import { authStore, BasicConstant, hdHttp, HdLoadingDialog, HdResponse } from '@itcast/basic' import { authStore, BasicConstant, hdHttp, HdLoadingDialog, HdResponse,
NotificationManagementUtil } from '@itcast/basic'
import { MessageComp } from '../view/MessageComp' import { MessageComp } from '../view/MessageComp'
import { PatientListComp, updateExtraData } from './PatientListComp' import { PatientListComp, updateExtraData } from './PatientListComp'
import { applyListCallBacl, FollowPlanListComp } from 'patient' import { applyListCallBacl, FollowPlanListComp } from 'patient'
import { HMLifecycleState, HMRouterMgr } from '@hadss/hmrouter' import { HMLifecycleState, HMRouterMgr } from '@hadss/hmrouter'
import { BusinessError } from '@kit.BasicServicesKit' import { BusinessError } from '@kit.BasicServicesKit'
import { LocalConversationRepo } from '@nimkit/chatkit'
@Component @Component
export struct TabBarComp { export struct TabBarComp {
@@ -17,6 +19,8 @@ export struct TabBarComp {
private lifecycleOwner = HMRouterMgr.getCurrentLifecycleOwner() private lifecycleOwner = HMRouterMgr.getCurrentLifecycleOwner()
private handleCallback = () => { private handleCallback = () => {
this.getApplyList() this.getApplyList()
let unreadCount=LocalConversationRepo.getTotalUnreadCount() ?? -1
NotificationManagementUtil.setBadgeNumber(unreadCount)
} }
aboutToAppear() { aboutToAppear() {
this.getApplyList() this.getApplyList()
@@ -3,7 +3,7 @@ import { HMRouter, HMRouterMgr, HMRouterPathCallback, HMRouterPathInfo } from '@
import { PullToRefreshLayout, RefreshController } from 'refreshlib' import { PullToRefreshLayout, RefreshController } from 'refreshlib'
import { HashMap } from '@kit.ArkTS'; import { HashMap } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit'; import { BusinessError } from '@kit.BasicServicesKit';
import { PatientTBean, TeachModel } from '../model/TeachModel'; import { PatientTBean, TeachModel } from '@itcast/basic/src/main/ets/models/TeachModel';
import { promptAction } from '@kit.ArkUI'; import { promptAction } from '@kit.ArkUI';
import { ItemCompTeach } from '../components/ItemCompTeach'; import { ItemCompTeach } from '../components/ItemCompTeach';
import { StringIsEmpty } from '@nimkit/common'; import { StringIsEmpty } from '@nimkit/common';
@@ -3,6 +3,7 @@ import {
authStore, authStore,
BasicConstant, BasicConstant,
ChangePhotoGrids, ChangeUtil, ChangePhotoGrids, ChangeUtil,
compressedImage,
hdHttp, hdHttp,
HdLoadingDialog, HdLoadingDialog,
HdNav, HdNav,
@@ -11,9 +12,16 @@ import {
PhotoActionSheet, TimestampUtil, ViewImageInfo } from "@itcast/basic"; PhotoActionSheet, TimestampUtil, ViewImageInfo } from "@itcast/basic";
import { promptAction } from "@kit.ArkUI"; import { promptAction } from "@kit.ArkUI";
import { BusinessError } from "@kit.BasicServicesKit"; import { BusinessError } from "@kit.BasicServicesKit";
import { values } from "@nimsdk/vendor";
import { uri } from "@kit.ArkTS";
import { rcp } from "@kit.RemoteCommunicationKit"; import { rcp } from "@kit.RemoteCommunicationKit";
import { image } from "@kit.ImageKit";
import { fileIo } from "@kit.CoreFileKit";
import { buffer } from "@kit.ArkTS";
import fs from '@ohos.file.fs';
interface GeneratedTypeLiteralInterface_2 {
width: number;
height: number;
}
@HMRouter({pageUrl:'AddAndEditRecordComp'}) @HMRouter({pageUrl:'AddAndEditRecordComp'})
@Component @Component
@@ -48,12 +56,12 @@ export struct AddAndEditRecordComp {
this.photos.splice(this.removeIndex, 1) this.photos.splice(this.removeIndex, 1)
this.maxSelectNumber = 8 - this.photos.length; this.maxSelectNumber = 8 - this.photos.length;
ChangeUtil.convertUrisOrUrlsToBase64(this.photos).then(base64Array => { // this.convertUrisOrUrlsToBase64(this.photos).then(base64Array => {
console.info('转换结果:', base64Array+'转换个数:'+base64Array.length) // console.info('转换结果:', base64Array+'转换个数:'+base64Array.length)
this.base64Array = base64Array // this.base64Array = base64Array
}).catch((err:BusinessError) => { // }).catch((err:BusinessError) => {
console.error('批量转换失败:', err) // console.error('批量转换失败:', err)
}) // })
} }
aboutToAppear(): void { aboutToAppear(): void {
@@ -67,17 +75,17 @@ export struct AddAndEditRecordComp {
this.creat_time = model.create_date this.creat_time = model.create_date
this.maxSelectNumber = 8 - this.photos.length this.maxSelectNumber = 8 - this.photos.length
ChangeUtil.convertUrisOrUrlsToBase64(model.photo).then(base64Array => { // this.convertUrisOrUrlsToBase64(this.photos).then(base64Array => {
console.info('转换结果:', base64Array+'转换个数:'+base64Array.length) // console.info('转换结果:', base64Array+'转换个数:'+base64Array.length)
this.base64Array = base64Array // this.base64Array = base64Array
}).catch((err:BusinessError) => { // }).catch((err:BusinessError) => {
console.error('批量转换失败:', err) // console.error('批量转换失败:', err)
}) // })
} }
} }
} }
submitData() { async submitData() {
if (ChangeUtil.stringIsUndefinedAndNull(this.creat_time)) { if (ChangeUtil.stringIsUndefinedAndNull(this.creat_time)) {
promptAction.showToast({ message:'请选择日期', duration: 1000 }) promptAction.showToast({ message:'请选择日期', duration: 1000 })
return return
@@ -87,6 +95,7 @@ export struct AddAndEditRecordComp {
return return
} }
this.dialog.open() this.dialog.open()
this.base64Array =await this.convertUrisOrUrlsToBase64(this.photos)
const requestUrl = this.recordUuid?BasicConstant.upConditionRecord:BasicConstant.addConditionRecord const requestUrl = this.recordUuid?BasicConstant.upConditionRecord:BasicConstant.addConditionRecord
const entity: rcp.MultipartForm = this.recordUuid? new rcp.MultipartForm({ const entity: rcp.MultipartForm = this.recordUuid? new rcp.MultipartForm({
"uuid":this.recordUuid, "uuid":this.recordUuid,
@@ -96,8 +105,7 @@ export struct AddAndEditRecordComp {
"expert_uuid":authStore.getUser().uuid, "expert_uuid":authStore.getUser().uuid,
"patient_uuid":this.params["patient_uuid"], "patient_uuid":this.params["patient_uuid"],
"des":this.inputContent, "des":this.inputContent,
"create_date":this.creat_time, "create_date":this.creat_time
// "img1":this.base64Array[0]
}) })
this.base64Array.forEach((base64Str: string, index: number) => { this.base64Array.forEach((base64Str: string, index: number) => {
if (index < 8) { if (index < 8) {
@@ -258,12 +266,12 @@ export struct AddAndEditRecordComp {
} }
this.photos.push(...selectedUris); this.photos.push(...selectedUris);
this.maxSelectNumber = 8 - this.photos.length this.maxSelectNumber = 8 - this.photos.length
ChangeUtil.convertUrisOrUrlsToBase64(this.photos).then(base64Array => { // this.convertUrisOrUrlsToBase64(this.photos).then(base64Array => {
console.info('转换结果:', base64Array+'转换个数:'+base64Array.length) // console.info('转换结果:', base64Array+'转换个数:'+base64Array.length)
this.base64Array = base64Array // this.base64Array = base64Array
}).catch((err:BusinessError) => { // }).catch((err:BusinessError) => {
console.error('批量转换失败:', err) // console.error('批量转换失败:', err)
}) // })
} }
}), }),
alignment: DialogAlignment.Bottom, alignment: DialogAlignment.Bottom,
@@ -293,6 +301,475 @@ export struct AddAndEditRecordComp {
} }
}) })
} }
//
// async convertUrisOrUrlsToBase64(items: string[]): Promise<string[]> {
// const results: string[] = [];
//
// for (const item of items) {
// try {
// let arrayBuffer: ArrayBuffer;
// let mimeType = 'image/jpeg'; // 默认MIME类型
// // 处理本地文件URI
// if (item.startsWith('file://')) {// || !item.includes('://')
// let imageBuffers: ArrayBuffer = new ArrayBuffer(1);
// const imagePackerApi = image.createImagePacker();
// const packOpts: image.PackingOption = { format: "image/jpeg", quality: 1 };
// let imageSource: image.ImageSource | undefined = undefined
// let fileSource = await fileIo.open(item, fileIo.OpenMode.READ_ONLY)
// imageSource = image.createImageSource(fileSource.fd)
// let compressedImageInfo = compressedImage(await imageSource.createPixelMap(), 200)
// let compressedImageUri = (await compressedImageInfo).imageUri
// // let compressedImageSize = (await compressedImageInfo).imageByteLength
// // let compressedsImage = this.loadCompressedImage(compressedImageUri)
// arrayBuffer = await this.readLocalFile(compressedImageUri)
// imageBuffers = await imagePackerApi.packing(imageSource, packOpts);
// let base64Str = buffer.from(imageBuffers).toString('base64')
// results.push(base64Str)
// imageSource.release()
// imagePackerApi.release()
// // arrayBuffer = await ChangeUtil.readLocalFile(item);
// }
// // 处理网络URL
// else if (item.startsWith('http://') || item.startsWith('https://') || ChangeUtil.isImageFileByRegex(item)) {
// arrayBuffer = await ChangeUtil.downloadNetworkResource(item);
// }
// // 处理其他类型资源
// else {
// throw new Error(`Unsupported URI scheme: ${item}`);
// }
// // 关键优化:添加图片压缩步骤[6,8](@ref)
// // const compressedBuffer = await ChangeUtil.compression(
// // arrayBuffer,
// // mimeType,
// // 0.5 // 压缩质量为50%
// // );
// // // 转换为Base64
// // results.push(ChangeUtil.convertToBase64(compressedBuffer));
// } catch (err) {
// console.error(`转换失败: ${JSON.stringify(err)}`);
// results.push(''); // 失败时返回空字符串
// }
// }
// return results;
// }
// async loadCompressedImage(uri: string): Promise<image.PixelMap> {
// try {
// let file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
// let imageSource = image.createImageSource(file.fd);
// return await imageSource.createPixelMap();
// } catch (err) {
// let error = err as BusinessError;
// throw new Error(`Image loading failed: ${error.message}`);
// }
// }
// async readLocalFile(uri: string): Promise<ArrayBuffer> {
// try {
// // 打开文件
// const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
// // 获取文件大小
// const stat = fs.statSync(file.fd);
// const size = stat.size;
// // 创建缓冲区并读取数据
// const buffer = new ArrayBuffer(size);
// fs.readSync(file.fd, buffer);
// // 关闭文件
// fs.closeSync(file);
// return buffer;
// } catch (err) {
// throw new Error(`文件读取失败: ${JSON.stringify(err)}`);
// }
// }
async convertUrisOrUrlsToBase64(items: string[]): Promise<string[]> {
const results: string[] = [];
const MAX_SIZE_KB = 500; // 最大500KB
for (const item of items) {
try {
let base64Str: string;
// 处理本地文件URI
if (item.startsWith('file://')) {
base64Str = await this.processLocalImage(item, MAX_SIZE_KB);
}
// 处理网络URL
else if (item.startsWith('http://') || item.startsWith('https://') || ChangeUtil.isImageFileByRegex(item)) {
base64Str = await this.processNetworkImage(item, MAX_SIZE_KB);
}
// 处理其他类型资源
else {
throw new Error(`不支持的URI格式: ${item}`);
}
results.push(base64Str);
} catch (err) {
console.error(`转换失败: ${JSON.stringify(err)}`);
results.push(''); // 失败时返回空字符串
}
}
return results;
}
// 处理本地图片
private async processLocalImage(uri: string, maxSizeKB: number): Promise<string> {
try {
// 创建图片源
const fileSource = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(fileSource.fd);
// 获取原始图片信息
const pixelMap = await imageSource.createPixelMap();
const originalWidth = pixelMap.getPixelBytesNumber();
// 计算压缩比例,目标500KB以内
let quality = 0.9;
let targetWidth = 800; // 初始目标宽度
// 如果原始图片太大,逐步压缩
if (originalWidth > maxSizeKB * 1024) {
quality = Math.min(0.6, (maxSizeKB * 1024) / originalWidth);
targetWidth = Math.min(800, Math.sqrt((maxSizeKB * 1024) / originalWidth) * 100);
}
// 压缩图片
const compressedImageInfo = await compressedImage(pixelMap, targetWidth);
const compressedUri = compressedImageInfo.imageUri;
// 读取压缩后的图片
const compressedBuffer = await this.readLocalFile(compressedUri);
// 如果还是太大,进一步压缩
if (compressedBuffer.byteLength > maxSizeKB * 1024) {
const imagePackerApi = image.createImagePacker();
const packOpts: image.PackingOption = {
format: "image/jpeg",
quality: Math.max(0.3, quality * 0.8) // 进一步降低质量
};
const finalBuffer = await imagePackerApi.packing(imageSource, packOpts);
const finalBase64 = buffer.from(finalBuffer).toString('base64');
// 清理资源
imagePackerApi.release();
imageSource.release();
await fileIo.close(fileSource.fd);
return finalBase64;
}
// 转换为Base64
const base64Str = buffer.from(compressedBuffer).toString('base64');
// 清理资源
imageSource.release();
await fileIo.close(fileSource.fd);
return base64Str;
} catch (err) {
throw new Error(`本地图片处理失败: ${JSON.stringify(err)}`);
}
}
// 处理网络图片
private async processNetworkImage(url: string, maxSizeKB: number): Promise<string> {
try {
// 下载网络资源
const arrayBuffer = await ChangeUtil.downloadNetworkResource(url);
// 如果下载的图片太大,进行压缩
if (arrayBuffer.byteLength > maxSizeKB * 1024) {
// 创建临时文件来压缩
const tempUri = await this.saveToTempFile(arrayBuffer);
const compressedBase64 = await this.processLocalImage(tempUri, maxSizeKB);
// 清理临时文件
await this.deleteTempFile(tempUri);
return compressedBase64;
}
// 直接转换为Base64
return buffer.from(arrayBuffer).toString('base64');
} catch (err) {
throw new Error(`网络图片处理失败: ${JSON.stringify(err)}`);
}
}
// 保存到临时文件
private async saveToTempFile(arrayBuffer: ArrayBuffer): Promise<string> {
try {
// 使用应用缓存目录作为临时目录
const context = getContext(this);
const tempDir = context.cacheDir;
const tempFileName = `temp_${Date.now()}.jpg`;
const tempPath = `${tempDir}/${tempFileName}`;
const file = await fileIo.open(tempPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
await fileIo.write(file.fd, arrayBuffer);
await fileIo.close(file.fd);
return tempPath;
} catch (err) {
throw new Error(`临时文件保存失败: ${JSON.stringify(err)}`);
}
}
// 删除临时文件
private async deleteTempFile(uri: string): Promise<void> {
try {
await fileIo.unlink(uri);
} catch (err) {
console.warn(`临时文件删除失败: ${JSON.stringify(err)}`);
}
}
// 优化后的本地文件读取方法
async readLocalFile(uri: string): Promise<ArrayBuffer> {
try {
// 使用异步方式读取,避免阻塞
const file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
const stat = await fileIo.stat(uri);
const size = stat.size;
// 限制文件大小,避免内存溢出
if (size > 10 * 1024 * 1024) { // 10MB限制
throw new Error('文件过大,超过10MB限制');
}
const buffer = new ArrayBuffer(size);
await fileIo.read(file.fd, buffer);
await fileIo.close(file);
return buffer;
} catch (err) {
throw new Error(`文件读取失败: ${JSON.stringify(err)}`);
}
}
// async convertUrisOrUrlsToBase64(items: string[]): Promise<string[]> {
// const results: string[] = [];
// const MAX_SIZE_KB = 100; // 最大100KB,确保服务器兼容性
// const MAX_DIMENSION = 800; // 最大尺寸800px,平衡质量和兼容性
//
// for (const item of items) {
// try {
// let base64Str: string;
//
// // 处理本地文件URI
// if (item.startsWith('file://')) {
// base64Str = await this.processLocalImageCompatible(item, MAX_SIZE_KB, MAX_DIMENSION);
// }
// // 处理网络URL
// else if (item.startsWith('http://') || item.startsWith('https://') || ChangeUtil.isImageFileByRegex(item)) {
// base64Str = await this.processNetworkImageCompatible(item, MAX_SIZE_KB, MAX_DIMENSION);
// }
// // 处理其他类型资源
// else {
// throw new Error(`不支持的URI格式: ${item}`);
// }
//
// // 验证base64数据有效性
// if (base64Str && base64Str.length > 0) {
// results.push(base64Str);
// } else {
// results.push(''); // 空字符串表示处理失败
// }
// } catch (err) {
// console.error(`转换失败: ${JSON.stringify(err)}`);
// results.push(''); // 失败时返回空字符串
// }
// }
// return results;
// }
//
// // 兼容性本地图片处理
// private async processLocalImageCompatible(uri: string, maxSizeKB: number, maxDimension: number): Promise<string> {
// try {
// // 创建图片源
// const fileSource = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
// const imageSource = image.createImageSource(fileSource.fd);
//
// // 获取原始图片信息
// const imageInfo = await imageSource.getImageInfo();
// const originalWidth = imageInfo.size.width;
// const originalHeight = imageInfo.size.height;
//
// // 计算目标尺寸,确保兼容性
// const targetDimensions = this.calculateCompatibleDimensions(originalWidth, originalHeight, maxDimension);
//
// // 创建PixelMap
// const pixelMap = await imageSource.createPixelMap();
//
// // 使用标准JPEG格式,确保兼容性
// const imagePackerApi = image.createImagePacker();
// const packOpts: image.PackingOption = {
// format: "image/jpeg",
// quality: 0.85 // 适中的质量,平衡大小和兼容性
// };
//
// let finalPixelMap = pixelMap;
// let needsResize = false;
//
// // 检查是否需要调整尺寸
// if (targetDimensions.width < originalWidth || targetDimensions.height < originalHeight) {
// needsResize = true;
// // 使用compressedImage调整尺寸
// const compressedImageInfo = await compressedImage(pixelMap, targetDimensions.width);
// const compressedUri = compressedImageInfo.imageUri;
//
// // 从压缩后的URI创建新的PixelMap
// const compressedFileSource = await fileIo.open(compressedUri, fileIo.OpenMode.READ_ONLY);
// const compressedImageSource = image.createImageSource(compressedFileSource.fd);
// finalPixelMap = await compressedImageSource.createPixelMap();
//
// // 清理临时资源
// compressedImageSource.release();
// await fileIo.close(compressedFileSource.fd);
// }
//
// // 打包为JPEG格式
// const finalBuffer = await imagePackerApi.packing(finalPixelMap, packOpts);
//
// // 验证buffer大小
// if (finalBuffer.byteLength > maxSizeKB * 1024) {
// // 如果还是太大,进一步降低质量
// const fallbackOpts: image.PackingOption = {
// format: "image/jpeg",
// quality: 0.7
// };
// const fallbackBuffer = await imagePackerApi.packing(finalPixelMap, fallbackOpts);
//
// // 清理资源
// imagePackerApi.release();
// imageSource.release();
// await fileIo.close(fileSource.fd);
//
// if (needsResize && finalPixelMap !== pixelMap) {
// finalPixelMap.release();
// }
//
// return buffer.from(fallbackBuffer).toString('base64');
// }
//
// const base64Str = buffer.from(finalBuffer).toString('base64');
//
// // 清理资源
// imagePackerApi.release();
// imageSource.release();
// await fileIo.close(fileSource.fd);
//
// if (needsResize && finalPixelMap !== pixelMap) {
// finalPixelMap.release();
// }
//
// return base64Str;
//
// } catch (err) {
// throw new Error(`本地图片处理失败: ${JSON.stringify(err)}`);
// }
// }
//
// // 兼容性网络图片处理
// private async processNetworkImageCompatible(url: string, maxSizeKB: number, maxDimension: number): Promise<string> {
// try {
// // 下载网络资源
// const arrayBuffer = await ChangeUtil.downloadNetworkResource(url);
//
// // 检查下载的图片大小
// if (arrayBuffer.byteLength > maxSizeKB * 1024) {
// // 创建临时文件来压缩
// const tempUri = await this.saveToTempFile(arrayBuffer);
// const compressedBase64 = await this.processLocalImageCompatible(tempUri, maxSizeKB, maxDimension);
//
// // 清理临时文件
// await this.deleteTempFile(tempUri);
//
// return compressedBase64;
// }
//
// // 直接转换为Base64
// return buffer.from(arrayBuffer).toString('base64');
//
// } catch (err) {
// throw new Error(`网络图片处理失败: ${JSON.stringify(err)}`);
// }
// }
//
// // 计算兼容性尺寸
// private calculateCompatibleDimensions(originalWidth: number, originalHeight: number, maxDimension: number): GeneratedTypeLiteralInterface_2 {
// let targetWidth = originalWidth;
// let targetHeight = originalHeight;
//
// // 如果尺寸超过最大限制,按比例缩小
// if (originalWidth > maxDimension || originalHeight > maxDimension) {
// if (originalWidth > originalHeight) {
// targetWidth = maxDimension;
// targetHeight = Math.round((originalHeight * maxDimension) / originalWidth);
// } else {
// targetHeight = maxDimension;
// targetWidth = Math.round((originalWidth * maxDimension) / originalHeight);
// }
// }
//
// // 确保尺寸是偶数,提高兼容性
// targetWidth = Math.round(targetWidth / 2) * 2;
// targetHeight = Math.round(targetHeight / 2) * 2;
//
// // 设置最小尺寸限制
// if (targetWidth < 100) targetWidth = 100;
// if (targetHeight < 100) targetHeight = 100;
//
// return { width: targetWidth, height: targetHeight };
// }
//
// // 保存到临时文件
// private async saveToTempFile(arrayBuffer: ArrayBuffer): Promise<string> {
// try {
// const context = getContext(this);
// const tempDir = context.cacheDir;
// const tempFileName = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.jpg`;
// const tempPath = `${tempDir}/${tempFileName}`;
//
// const file = await fileIo.open(tempPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
// await fileIo.write(file.fd, arrayBuffer);
// await fileIo.close(file.fd);
//
// return tempPath;
// } catch (err) {
// throw new Error(`临时文件保存失败: ${JSON.stringify(err)}`);
// }
// }
//
// // 删除临时文件
// private async deleteTempFile(uri: string): Promise<void> {
// try {
// await fileIo.unlink(uri);
// } catch (err) {
// console.warn(`临时文件删除失败: ${JSON.stringify(err)}`);
// }
// }
//
// // 兼容性本地文件读取方法
// async readLocalFile(uri: string): Promise<ArrayBuffer> {
// try {
// const file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
// const stat = await fileIo.stat(uri);
// const size = stat.size;
//
// // 更保守的文件大小限制
// if (size > 3 * 1024 * 1024) { // 3MB限制
// throw new Error('文件过大,超过3MB限制');
// }
//
// const buffer = new ArrayBuffer(size);
// await fileIo.read(file.fd, buffer);
// await fileIo.close(file);
//
// return buffer;
// } catch (err) {
// throw new Error(`文件读取失败: ${JSON.stringify(err)}`);
// }
// }
} }
interface recordList { interface recordList {
@@ -18,7 +18,7 @@ export struct AddScheduleFollowComp {
@State date:string = '' @State date:string = ''
@State time:string = '请选择日期' @State time:string = '请选择日期'
@State isNotiMe:boolean = false @State isNotiMe:boolean = false
@State isNotiPa:boolean = false @State isNotiPa:boolean = true
@State patient_uuid:string = this.params.patientUuid @State patient_uuid:string = this.params.patientUuid
@State page:string = '个人' @State page:string = '个人'

Some files were not shown because too many files have changed in this diff Show More