患者相关

This commit is contained in:
xiaoxiao
2025-07-25 13:38:42 +08:00
parent 6744eb1a78
commit abbd0540ab
39 changed files with 1527 additions and 281 deletions
+3 -1
View File
@@ -91,4 +91,6 @@ export { ChatExtModel,ChatParam } from './src/main/ets/models/ChatExtModel'
export { PatientListModel,PatientsData } from './src/main/ets/models/PatientListModel'
export { applyListCallBacl, applyListModel, applyHistoryCallBacl , historyObjectModel, historyModel } from './src/main/ets/models/ApplyModel'
export { applyListCallBacl, applyListModel, applyHistoryCallBacl , historyObjectModel, historyModel } from './src/main/ets/models/ApplyModel'
export { TripleOptionDialog } from './src/main//ets/Views/TripleOptionDialog'
@@ -53,7 +53,7 @@ export struct DefaultHintProWindows {
Text(this.confirmTitle)
.fontSize(15)
.fontColor(this.confirmTitleColor)
}.width('45%').height(30).backgroundColor(this.cancleColor)
}.width('45%').height(30).backgroundColor(this.confirmColor)
.onClick(() => {
this.selectedButton(1)
})
@@ -1,5 +1,5 @@
import { router } from "@kit.ArkUI";
import { promptAction, router } from "@kit.ArkUI";
import http from '@ohos.net.http'
import fileio from '@ohos.fileio'
import prompt from '@ohos.promptAction'
@@ -8,11 +8,15 @@ import { BusinessError } from '@kit.BasicServicesKit';
import { BasicConstant } from "../constants/BasicConstant";
import { ViewImageInfo } from "../models/ViewImageInfo";
import { PermissionsUtils } from "../utils/PermissionsUtils";
import { fileIo, fileUri } from "@kit.CoreFileKit";
import { photoAccessHelper } from "@kit.MediaLibraryKit";
import fs from '@ohos.file.fs'
@Component
export struct PreviewPhotos {
private imageBuffer?: ArrayBuffer
@State
imgList: ViewImageInfo[]=[] // 传入图片数组
@State params:paramPhoto= router.getParams() as paramPhoto
@@ -46,54 +50,85 @@ export struct PreviewPhotos {
}
}
// 下载图片方法(伪代码,需根据实际API实现)
async downloadImage(url: string) {
// // 下载图片方法(伪代码,需根据实际API实现)
// async downloadImage(url: string) {
// try {
// // 1. 检查权限
// const hasPermission = await this.checkStoragePermission();
// if (!hasPermission) {
// // 申请权限
// const granted = await this.requestStoragePermission();
// if (!granted) {
// prompt.showToast({ message: $r('app.string.netease_permission_denied_tips') });
// return;
// }
// }
//
// // 显示下载中提示
// prompt.showToast({ message: $r('app.string.netease_saving_image_tips') });
//
// // 2. 下载图片
// const httpRequest = http.createHttp()
// const response = await httpRequest.request(url, {
// method: http.RequestMethod.GET,
// expectDataType: http.HttpDataType.ARRAY_BUFFER,
// connectTimeout: 30000, // 30秒连接超时
// readTimeout: 30000, // 30秒读取超时
// })
// httpRequest.destroy()
//
// if (response.responseCode !== 200) {
// prompt.showToast({ message: $r('app.string.netease_download_failed_tips') })
// return
// }
//
// // 3. 写入到公共图片目录
// const fileName = 'img_' + Date.now() + '.jpg'
// const filePath = '/storage/media/100/local/photos/' + fileName
//
// try {
// const fd = await fileio.open(filePath, 0o2 | 0o100) // 写入+创建
// await fileio.write(fd, new Uint8Array(response.result as ArrayBuffer))
// await fileio.close(fd)
// prompt.showToast({ message: $r('app.string.netease_save_success_tips') })
// } catch (fileError) {
// console.error('文件写入失败:', fileError);
// prompt.showToast({ message: $r('app.string.netease_save_failed_tips') })
// }
// } catch (e) {
// console.error('下载图片失败:', e);
// prompt.showToast({ message: $r('app.string.netease_save_error_tips') })
// }
// }
async downloadImage(url: string,result: SaveButtonOnClickResult) {
if (result !== SaveButtonOnClickResult.SUCCESS) {
promptAction.showToast({ message: '权限获取失败', duration: 2000 })
return
}
const context = getContext(this) as common.UIAbilityContext
try {
// 1. 检查权限
const hasPermission = await this.checkStoragePermission();
if (!hasPermission) {
// 申请权限
const granted = await this.requestStoragePermission();
if (!granted) {
prompt.showToast({ message: $r('app.string.netease_permission_denied_tips') });
return;
}
}
// 显示下载中提示
prompt.showToast({ message: $r('app.string.netease_saving_image_tips') });
// 2. 下载图片
const httpRequest = http.createHttp()
const response = await httpRequest.request(url, {
method: http.RequestMethod.GET,
expectDataType: http.HttpDataType.ARRAY_BUFFER,
connectTimeout: 30000, // 30秒连接超时
readTimeout: 30000, // 30秒读取超时
expectDataType: http.HttpDataType.ARRAY_BUFFER
})
httpRequest.destroy()
this.imageBuffer = response.result as ArrayBuffer
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context)
const uri = await phAccessHelper.createAsset(
photoAccessHelper.PhotoType.IMAGE,
'jpg' // 文件扩展名
)
if (response.responseCode !== 200) {
prompt.showToast({ message: $r('app.string.netease_download_failed_tips') })
return
}
const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
await fs.write(file.fd, this.imageBuffer)
await fs.close(file.fd)
// 3. 写入到公共图片目录
const fileName = 'img_' + Date.now() + '.jpg'
const filePath = '/storage/media/100/local/photos/' + fileName
try {
const fd = await fileio.open(filePath, 0o2 | 0o100) // 写入+创建
await fileio.write(fd, new Uint8Array(response.result as ArrayBuffer))
await fileio.close(fd)
prompt.showToast({ message: $r('app.string.netease_save_success_tips') })
} catch (fileError) {
console.error('文件写入失败:', fileError);
prompt.showToast({ message: $r('app.string.netease_save_failed_tips') })
}
} catch (e) {
console.error('下载图片失败:', e);
prompt.showToast({ message: $r('app.string.netease_save_error_tips') })
promptAction.showToast({ message: '图片已保存到相册', duration: 2000 })
} catch (error) {
const err = error as BusinessError
console.error(`保存失败: Code=${err.code}, Message=${err.message}`)
promptAction.showToast({ message: `保存失败: ${err.code}` })
}
}
@@ -165,25 +200,29 @@ export struct PreviewPhotos {
.onClick(() => {
router.back()
})
Row()
{
Image($r('app.media.ic_topbar_save')).width(30).height(30)
.onClick(()=>{
this.downloadImage( this.imgList[this.previewIndex].url?(BasicConstant.urlHtml + this.imgList[this.previewIndex].url): this.imgList[this.previewIndex].url)
})
Row() {
SaveButton({icon:SaveIconStyle.FULL_FILLED,buttonType:ButtonType.Capsule})
.iconSize(25)
.iconColor($r('app.color.main_color'))
.backgroundColor('rgba(255,255,255,0.01)')
.width(50)
.height(30)
.onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
const url = this.imgList[this.previewIndex].url
? (BasicConstant.urlHtml + this.imgList[this.previewIndex].url)
: this.imgList[this.previewIndex].url;
this.downloadImage(url,result)
})
Blank()
Text(`${this.previewIndex + 1}/${this.imgList.length}`)
.fontSize(18)
.fontColor($r('app.color.top_title'))
}
.visibility(this.downLoad?Visibility.Visible:Visibility.Hidden)
.height(30)
.width('100%')
.padding({ left:20,right:20 })
.padding({ left:10,right:20 })
.margin({bottom:40})
.alignRules({bottom: { anchor: "__container__", align: VerticalAlign.Bottom }} )
}
@@ -0,0 +1,65 @@
@CustomDialog
export struct TripleOptionDialog {
controller: CustomDialogController; // 必须包含的控制器属性
@Prop title: string = ''
@Prop subTitle:string = ''
@Prop oneButtonTitle:string = ''
@Prop twoButtonTitle:string = ''
private buttonSelected: (actionType:string) => void = () => {};
// 按钮点击事件处理
private handleAction(actionType: string) {
this.buttonSelected(actionType)
this.controller.close(); // 关闭弹窗
}
build() {
Column() {
Text(this.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
if (this.subTitle.length) {
Text(this.subTitle)
.fontSize(15)
.padding({top:5})
}
Column() {
Blank()
.width('100%')
.height(1)
.backgroundColor('#f4f4f4')
Text(this.oneButtonTitle)
.height(40)
.fontSize(14)
.fontColor('#333333')
.onClick(() => this.handleAction(this.oneButtonTitle))
Blank()
.width('100%')
.height(1)
.backgroundColor('#f4f4f4')
Text(this.twoButtonTitle)
.height(40)
.fontSize(14)
.fontColor('#333333')
.onClick(() => this.handleAction(this.twoButtonTitle))
Blank()
.width('100%')
.height(1)
.backgroundColor('#f4f4f4')
Text('取消')
.height(40)
.fontSize(14)
.fontColor('#999999')
.onClick(() => this.controller.close())
}
.width('100%')
.margin({ top:20,bottom: 20 })
}
.backgroundColor(Color.Transparent)
.borderRadius(16)
.width('100%')
}
}
@@ -38,6 +38,12 @@ export class BasicConstant {
static readonly updateNicknameNote = BasicConstant.urlExpertAPI+'updateNicknameNote'
static readonly applyListOperate = BasicConstant.urlExpert+'applyListOperate'
static readonly patientListNoInThisGroup = BasicConstant.urlExpertAPI+'patientListNoInThisGroup'
static readonly patientList = BasicConstant.urlExpert+'patientList'
static readonly listGroupSendMsg = BasicConstant.urlExpertAPI+'listGroupSendMsg'
static readonly delGroupSendMsg = BasicConstant.urlExpertAPI+'delGroupSendMsg'
static readonly addGroupSendMsg4YunXin = BasicConstant.urlExpertAPI+'addGroupSendMsg4YunXin'
static readonly addConditionRecord = BasicConstant.urlExpert+'addConditionRecord'
static readonly upConditionRecord = BasicConstant.urlExpert+'upConditionRecord'
static readonly patientCard = BasicConstant.urlExpertAPI+'patientCard'
static readonly toAddNickname = BasicConstant.urlExpert+'toAddNickname'
static readonly cancelRes = BasicConstant.urlExpert+'cancelRes'
+104 -61
View File
@@ -9,6 +9,7 @@ import util from '@ohos.util';
import { i18n } from '@kit.LocalizationKit';
import { connection } from '@kit.NetworkKit';
import http from '@ohos.net.http'
import {BasicConstant} from '../constants/BasicConstant'
export class ChangeUtil {
/**
* 将HashMap转成JsonString
@@ -98,67 +99,6 @@ export class ChangeUtil {
return bf
}
/**
* 将图片转换为base64字符串
* @param imageUri 图片URI
* @returns Promise<string> base64字符串
*/
static async imageToBase64(imageUri: string): Promise<string> {
// 1. 验证URI有效性
if (!imageUri || !imageUri.startsWith('file://')) {
throw new Error('无效的图片URI,必须以file://开头');
}
let imageSource: image.ImageSource | undefined;
let pixelMap: image.PixelMap | undefined;
let imagePacker: image.ImagePacker | undefined;
try {
const file = fs.openSync(imageUri, fs.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(file.fd);
if (!imageSource) {
throw new Error('创建ImageSource失败,请检查图片路径');
}
logger.info('正在获取图片信息...');
const imageInfo = await imageSource.getImageInfo();
logger.info(`图片尺寸: ${imageInfo.size.width}x${imageInfo.size.height}`);
pixelMap = await imageSource.createPixelMap({
desiredSize: {
width: imageInfo.size.width,
height: imageInfo.size.height
}
});
if (!pixelMap) {
throw new Error('创建PixelMap失败');
}
// 5. 压缩图片
imagePacker = image.createImagePacker();
const packOpts: image.PackingOption = {
format: "image/jpeg",
quality: 80 // 适当提高质量保证清晰度
};
logger.info('正在压缩图片...');
const arrayBuffer = await imagePacker.packing(pixelMap, packOpts);
const unit8Array = new Uint8Array(arrayBuffer);
let binary = '';
unit8Array.forEach(byte => {
binary += String.fromCharCode(byte);
});
const base64String = Base64Util.encodeToStrSync(binary);
logger.info(`图片转换成功,大小: ${Math.round(base64String.length / 1024)}KB`);
return base64String;
} catch (error) {
logger.error('图片处理失败: ' + JSON.stringify(error));
throw new Error(`图片处理失败: ${error.message}`);
} finally {
// 7. 确保释放资源
try {
pixelMap?.release();
imageSource?.release();
imagePacker?.release();
} catch (e) {
logger.error('资源释放异常: ' + JSON.stringify(e));
}
}
}
static isLetter(char: string): boolean {
if (char.length !== 1) return false;
@@ -244,4 +184,107 @@ export class ChangeUtil {
arr[arr.length - 1] = first; // 首位移至末尾
return arr;
}
/**
* 将URI或URL数组转换为Base64字符串数组
* @param items - 包含文件URI或网络URL的数组
* @returns Promise<string[]> - Base64字符串数组
*/
static 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('://')
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;
}
/**
* 读取本地文件到ArrayBuffer
* @param uri - 文件URI
* @returns Promise<ArrayBuffer>
*/
static 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)}`);
}
}
/**
* 下载网络资源到ArrayBuffer
* @param url - 资源URL
* @returns Promise<ArrayBuffer>
*/
static async downloadNetworkResource(url: string): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const httpRequest = http.createHttp();
httpRequest.request(
BasicConstant.urlImage+url,
{
method: http.RequestMethod.GET
},
(err, data) => {
httpRequest.destroy();
if (err) {
reject(new Error(`网络请求失败: ${JSON.stringify(err)}`));
return;
}
if (data.responseCode === 200) {
resolve(data.result as ArrayBuffer);
} else {
reject(new Error(`HTTP错误: ${data.responseCode}`));
}
}
)
})
}
static convertToBase64(buffer: ArrayBuffer): string {
const helper = new util.Base64Helper();
const uint8Array = new Uint8Array(buffer);
return helper.encodeToStringSync(uint8Array);
}
static isImageFileByRegex(path: string): boolean {
const pattern = /\.(jpg|jpeg|png|gif|bmp|webp)$/i;
return pattern.test(path);
}
}