更新云信和公益咨询相关代码
This commit is contained in:
parent
2da6409a0b
commit
879646296d
@ -5,6 +5,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { authStore, BasicConstant, patientDbManager } from '@itcast/basic';
|
||||
import { ChatKitClient } from '@nimkit/chatkit';
|
||||
import { V2NIMMessagePin } from '@nimsdk/base';
|
||||
import { V2NIMMessage, V2NIMMessageSendingState } from '@nimsdk/base/src/main/ets/nim/sdk/V2NIMMessageService';
|
||||
@ -67,7 +68,12 @@ export class ChatInfo {
|
||||
};
|
||||
|
||||
getCurrentUserAvatarUrl(message?: V2NIMMessage): string {
|
||||
return '';
|
||||
|
||||
|
||||
return BasicConstant.urlHtml+authStore.getUser().photo;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
getCurrentUserAvatarName(): string {
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
NECommonUtils,
|
||||
NetworkBrokenBuilder,
|
||||
PermissionsUtils,
|
||||
UserUtils,
|
||||
VideoViewerDialog
|
||||
} from '@nimkit/common';
|
||||
import { NECameraSelectView } from '../view/NECameraSelectView';
|
||||
@ -179,6 +180,7 @@ export struct ChatP2PPage {
|
||||
cornerRadius: 4,
|
||||
width: '70%',
|
||||
})
|
||||
@Local showName:string=String(this.chatUserInfo.conversationName)
|
||||
waDialog: CustomDialogController = new CustomDialogController({
|
||||
builder: NewWaDialog({
|
||||
firstCallBack:()=>{
|
||||
@ -401,7 +403,7 @@ export struct ChatP2PPage {
|
||||
})
|
||||
}
|
||||
|
||||
patientDetail()
|
||||
patientDetail()
|
||||
{
|
||||
this.sessionId = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(this.conversationId)
|
||||
this.patientUuid=this.sessionId
|
||||
@ -411,14 +413,13 @@ export struct ChatP2PPage {
|
||||
let json = JSON.parse(res+'') as Record<string, object>;
|
||||
let datas=json.data as Record<string, string>;
|
||||
this.patientUuid=datas.uuid
|
||||
this.showName=await UserUtils.getAvatarName(this.patientUuid,String(this.chatUserInfo.conversationName))
|
||||
}).catch((err: BusinessError) => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
aboutToAppear(): void {
|
||||
|
||||
aboutToAppear() {
|
||||
NEEmojiManager.instance.setup();
|
||||
DeviceUtils.rootDirPath = getContext(this).filesDir
|
||||
this.operationMoreDataList = setupMoreOperationData()
|
||||
@ -455,7 +456,7 @@ export struct ChatP2PPage {
|
||||
})
|
||||
this.initDialog()
|
||||
this. getOutList()
|
||||
this.patientDetail()
|
||||
|
||||
}
|
||||
|
||||
computeScrollHeight() {
|
||||
@ -522,6 +523,7 @@ export struct ChatP2PPage {
|
||||
this.chatUserInfo.setConversationId(this.conversationId)
|
||||
this.chatViewModel.init(this.conversationId, this.chatUserInfo);
|
||||
this.chatViewModel.loadData();
|
||||
this.patientDetail()
|
||||
}
|
||||
|
||||
async showImageDetail(msg?: NIMMessageInfo, onlyMsg?: boolean) {
|
||||
@ -571,7 +573,7 @@ export struct ChatP2PPage {
|
||||
build() {
|
||||
NavDestination() {
|
||||
NavigationBackBuilder({
|
||||
title: this.chatUserInfo.conversationName,
|
||||
title:this.showName ,
|
||||
rightButtonIcon: this.showMultiSelect ? undefined : $r('app.media.ic_public_more_dot'),
|
||||
rightButtonTitle: this.showMultiSelect ? $r('app.string.chat_msg_dialog_cancel') : undefined,
|
||||
top:this.statusBarHeight,
|
||||
|
||||
@ -13,6 +13,7 @@ import { AvatarColorUntil, AvatarItem, CommonAvatar } from '@nimkit/common';
|
||||
import { messageContent } from './MessageComponentBuilder';
|
||||
import { MessageItemClick } from './MessageItemClick';
|
||||
import { ChatKitConfig } from '../ChatKitConfig';
|
||||
import { authStore, BasicConstant } from '@itcast/basic';
|
||||
|
||||
/**
|
||||
* 消息组件按照UI样式,划分为发送消息组件、接收消息组件、通知消息组件和提示消息组件
|
||||
@ -243,7 +244,7 @@ export struct SenderMessageComponent {
|
||||
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(this.chatUserInfo != null ?
|
||||
this.chatUserInfo?.getCurrentUserAvatarUrl() : '',
|
||||
this.chatUserInfo?.getCurrentUserAvatarUrl() : BasicConstant.urlHtml+authStore.getUser().photo,
|
||||
this.chatUserInfo?.getCurrentUserAvatarName() ?? '',
|
||||
AvatarColorUntil.getBackgroundColorById(this.message.message.senderId),
|
||||
),
|
||||
|
||||
@ -60,6 +60,7 @@ import { instanceToPlain } from 'class-transformer';
|
||||
import { NECommonUtils } from '@nimkit/common';
|
||||
import { HashMap, JSON } from '@kit.ArkTS';
|
||||
import { assign } from '@nimsdk/vendor';
|
||||
import { BasicConstant, ChatExtModel } from '@itcast/basic';
|
||||
|
||||
@ObservedV2
|
||||
export class ChatBaseViewModel {
|
||||
@ -681,13 +682,20 @@ export class ChatBaseViewModel {
|
||||
async sendTextMessage(text: string, replyMsg?: NIMMessageInfo, aitModel?: AitModel, pushList?: string[]) {
|
||||
const message = ChatRepo.createTextMessage(text)
|
||||
//设置Ait
|
||||
if (aitModel && aitModel.aitBlocks.size > 0) {
|
||||
let extensionMap: YxAitMsg = {
|
||||
yxAitMsg: aitModel.aitBlocks
|
||||
}
|
||||
let extension = JSON.stringify(instanceToPlain(extensionMap))
|
||||
message.serverExtension = extension
|
||||
// if (aitModel && aitModel.aitBlocks.size > 0) {
|
||||
// let extensionMap: YxAitMsg = {
|
||||
// yxAitMsg: aitModel.aitBlocks
|
||||
// }
|
||||
// let extension = JSON.stringify(instanceToPlain(extensionMap))
|
||||
// message.serverExtension = extension
|
||||
// }
|
||||
let extension:ChatExtModel= {
|
||||
gdxz_sessionType: '',
|
||||
gdxz_consult_uuid: '',
|
||||
gdxz_nickName: ''
|
||||
}
|
||||
message.serverExtension =JSON.stringify(extension)
|
||||
|
||||
|
||||
//设置推送
|
||||
let params: V2NIMSendMessageParams | undefined = undefined
|
||||
@ -742,6 +750,7 @@ export class ChatBaseViewModel {
|
||||
}
|
||||
params.aiConfig = aiParams
|
||||
}
|
||||
|
||||
this.sendMessage(message, params)
|
||||
}
|
||||
|
||||
|
||||
@ -75,4 +75,6 @@ export { MatchSearchText } from './src/main/ets/view/MatchSearchText'
|
||||
|
||||
export {CommonLongLoadingProgress } from './src/main/ets/builder/CommonLongLoadingProgress'
|
||||
|
||||
export { BreakpointConstants} from './src/main/ets/constants/BreakpointConstants'
|
||||
export { BreakpointConstants} from './src/main/ets/constants/BreakpointConstants'
|
||||
|
||||
export { UserUtils } from './src/main/ets/utils/UserUtils';
|
||||
@ -6,6 +6,7 @@
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@ohos/pinyin4js": "^2.0.0"
|
||||
"@ohos/pinyin4js": "^2.0.0",
|
||||
"@itcast/basic":"file:../commons/basic"
|
||||
}
|
||||
}
|
||||
22
common/src/main/ets/utils/UserUtils.ets
Normal file
22
common/src/main/ets/utils/UserUtils.ets
Normal file
@ -0,0 +1,22 @@
|
||||
import { patientDbManager } from "@itcast/basic"
|
||||
|
||||
export class UserUtils {
|
||||
static async getAvatarName(account: string,names:string){
|
||||
let patient =await patientDbManager.getPatientByUuid(account)
|
||||
if(patient!=null)
|
||||
{
|
||||
if(patient.nickname!=null&&patient.nickname!='')
|
||||
{
|
||||
return patient.nickname
|
||||
}
|
||||
if(patient.realName!=null&&patient.realName!='')
|
||||
{
|
||||
return patient.realName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return String(names)
|
||||
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { authStore, BasicConstant, patientDbManager } from '@itcast/basic';
|
||||
@ComponentV2
|
||||
export struct CommonAvatar {
|
||||
/**
|
||||
@ -29,24 +29,48 @@ export struct CommonAvatar {
|
||||
if (this.item != null) {
|
||||
|
||||
if (this.item.avatarUrl == null || this.item.avatarUrl.toString().length <= 0) {
|
||||
Stack() {
|
||||
Column() {
|
||||
}.width('100%').height('100%').backgroundColor(this.item.color)
|
||||
.borderRadius(this.roundRadius)
|
||||
Image(BasicConstant.urlHtml+authStore.getUser().photo)
|
||||
.onError(() => {
|
||||
if(this.item!=null)
|
||||
{
|
||||
this.item.avatarUrl = $r('app.media.icon_touxiang_persion_new');
|
||||
}
|
||||
|
||||
})
|
||||
.syncLoad(true)
|
||||
.borderRadius(this.roundRadius)
|
||||
.onGestureJudgeBegin(() => {
|
||||
return GestureJudgeResult.CONTINUE;
|
||||
}).parallelGesture(LongPressGesture().onAction(event => {
|
||||
this.longPressGesture?.(event)
|
||||
}
|
||||
))
|
||||
// Stack() {
|
||||
// Column() {
|
||||
// }.width('100%').height('100%').backgroundColor(this.item.color)
|
||||
// .borderRadius(this.roundRadius)
|
||||
//
|
||||
// Text(this.item.name)
|
||||
// .fontSize(this.textSize)
|
||||
// .fontColor(this.textColor)
|
||||
// .ellipsisMode(EllipsisMode.END)
|
||||
// .textAlign(TextAlign.Center)
|
||||
// .margin({
|
||||
// left: 6, right: 6
|
||||
// })
|
||||
// .width('100%')
|
||||
// .maxLines(1)
|
||||
// }.gesture(LongPressGesture().onAction(this.longPressGesture))
|
||||
|
||||
Text(this.item.name)
|
||||
.fontSize(this.textSize)
|
||||
.fontColor(this.textColor)
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({
|
||||
left: 6, right: 6
|
||||
})
|
||||
.width('100%')
|
||||
.maxLines(1)
|
||||
}.gesture(LongPressGesture().onAction(this.longPressGesture))
|
||||
} else {
|
||||
Image(this.item.avatarUrl)
|
||||
.onError(() => {
|
||||
if(this.item!=null)
|
||||
{
|
||||
this.item.avatarUrl = $r('app.media.icon_touxiang_persion_new');
|
||||
}
|
||||
|
||||
})
|
||||
.syncLoad(true)
|
||||
.borderRadius(this.roundRadius)
|
||||
.onGestureJudgeBegin(() => {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
@ -85,4 +85,6 @@ export { ViewImageInfo } from './src/main/ets/models/ViewImageInfo'
|
||||
|
||||
export { ChangePhotoGrids } from './src/main/ets/components/ChangePhotoGrids'
|
||||
|
||||
export { InputPopWindow } from './src/main/ets/Views/InputPopWindow'
|
||||
export { InputPopWindow } from './src/main/ets/Views/InputPopWindow'
|
||||
|
||||
export { ChatExtModel } from './src/main/ets/models/ChatExtModel'
|
||||
@ -14,7 +14,7 @@ export struct PerfactInputSheet {
|
||||
@State okText:ResourceStr='确定'
|
||||
@State cancelText:ResourceStr='取消'
|
||||
private inputCallBack: (input: string,title:string) => void = () => {};
|
||||
|
||||
@State needcancelCallBack:boolean=false
|
||||
// 修改构造函数
|
||||
constructor(controller: CustomDialogController, inputCallBack: (input: string,title:string) => void) {
|
||||
super();
|
||||
@ -115,6 +115,8 @@ export struct PerfactInputSheet {
|
||||
.textAlign(TextAlign.Center)
|
||||
.width('45%').height(30)
|
||||
.onClick(() => {
|
||||
|
||||
|
||||
this.controller.close()
|
||||
})
|
||||
Text('').height(30).width(1)
|
||||
@ -171,6 +173,10 @@ export struct PerfactInputSheet {
|
||||
|
||||
.width('45%').height(30)
|
||||
.onClick(() => {
|
||||
if(this.needcancelCallBack)
|
||||
{
|
||||
this.inputCallBack(this.inputText, 'needcancelCallBack');
|
||||
}
|
||||
this.controller.close()
|
||||
})
|
||||
Text('').height(30).width(1)
|
||||
|
||||
@ -79,6 +79,7 @@ export class BasicConstant {
|
||||
static readonly listMyAnsweredInterrogation = BasicConstant.urlExpertAPI +"listMyAnsweredInterrogation";// 一问多答 我回答的一问多答列表
|
||||
static readonly getInterrogation = BasicConstant.urlExpertAPI +"getInterrogation";// 一问多答 详情页
|
||||
static readonly InterrogationPatientInfo = BasicConstant.urlExpertAPI +"InterrogationPatientInfo";// 一问多答 患者详情页
|
||||
static readonly updateInterrogationAnswer = BasicConstant.urlExpertAPI+"updateInterrogationAnswer";// 一问多答 编辑回答
|
||||
static readonly province=['全国','北京市','天津市','河北省','山西省'
|
||||
,'内蒙古自治区','辽宁省','吉林省','黑龙江省','上海市','江苏省','浙江省'
|
||||
,'安徽省','福建省','江西省','山东省','河南省','湖北省','湖南省','广东省',
|
||||
|
||||
8
commons/basic/src/main/ets/models/ChatExtModel.ets
Normal file
8
commons/basic/src/main/ets/models/ChatExtModel.ets
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
export interface ChatExtModel{
|
||||
gdxz_sessionType:string,//是否为公益咨询
|
||||
gdxz_consult_uuid:string,//公益咨询uuid
|
||||
gdxz_nickName:string//患者备注姓名
|
||||
|
||||
}
|
||||
@ -8,6 +8,7 @@ import { fileIo } from '@kit.CoreFileKit';
|
||||
import util from '@ohos.util';
|
||||
import { i18n } from '@kit.LocalizationKit';
|
||||
import { connection } from '@kit.NetworkKit';
|
||||
import http from '@ohos.net.http'
|
||||
export class ChangeUtil {
|
||||
/**
|
||||
* 将HashMap转成JsonString
|
||||
@ -191,10 +192,46 @@ export class ChangeUtil {
|
||||
}
|
||||
|
||||
static stringIsUndefinedAndNull (string:string | undefined):boolean {
|
||||
if (string == undefined || string == "null" || string == "<null>" || string == "(null)" || string == 'undefined' || string.length <= 0) {
|
||||
if (string == null||string == undefined || string == "null" || string == "<null>" || string == "(null)" || string == 'undefined' || string.length <= 0) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static map2JsonO(map:HashMap<string, Object>) {
|
||||
let jsonObject: Record<string, Object> = {};
|
||||
map.forEach((value, key) => {
|
||||
if(key != undefined && value != undefined){
|
||||
jsonObject[key] = value;
|
||||
}
|
||||
})
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static async getImageBase64(url: string): Promise<string> {
|
||||
// 创建 http 实例
|
||||
let httpRequest = http.createHttp();
|
||||
// 发起 GET 请求
|
||||
let response = await httpRequest.request(url, {
|
||||
method: http.RequestMethod.GET,
|
||||
// 重要:设置 responseType 为 arraybuffer,获取二进制数据
|
||||
expectDataType: http.HttpDataType.ARRAY_BUFFER
|
||||
});
|
||||
// 检查响应
|
||||
if (response.responseCode === 200 && response.result) {
|
||||
// 1. 转成 Uint8Array
|
||||
const buffernew=await ChangeUtil.compression(response.result as ArrayBuffer,"image/jpeg",0.5)
|
||||
// let uint8Arr = new Uint8Array(response.result as ArrayBuffer)
|
||||
const base64Helper = new util.Base64Helper();
|
||||
return base64Helper.encodeToStringSync(new Uint8Array(buffernew));
|
||||
// response.result 是 ArrayBuffer
|
||||
// let base64Str = base64.encode(response.result as ArrayBuffer);
|
||||
// // 可选:拼接 data url 前缀
|
||||
// return "data:image/png;base64," + base64Str;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@ -249,8 +249,7 @@ export class PatientDao {
|
||||
if (!this.rdbStore) {
|
||||
throw new Error('数据库连接为空');
|
||||
}
|
||||
|
||||
const sql = 'SELECT * FROM patients WHERE uuid = ?';
|
||||
const sql = 'SELECT * FROM patients WHERE LOWER(uuid) = LOWER(?)';
|
||||
const resultSet = await this.rdbStore.querySql(sql, [uuid]);
|
||||
|
||||
if (resultSet.rowCount > 0) {
|
||||
|
||||
@ -67,7 +67,40 @@ class HdHttp {
|
||||
httpInstance.destroy()
|
||||
})
|
||||
}
|
||||
private requestObject<T>(path: string, method: http.RequestMethod = http.RequestMethod.POST, extraDatas :HashMap<string, Object>) {
|
||||
const httpInstance = http.createHttp()
|
||||
|
||||
const options: http.HttpRequestOptions = {
|
||||
method: http.RequestMethod.POST,
|
||||
// 可选,默认为60s
|
||||
connectTimeout: 60000,
|
||||
// 可选,默认为60s
|
||||
readTimeout: 60000,
|
||||
// 开发者根据自身业务需要添加header字段
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'sign':this.getSignO(extraDatas),
|
||||
'User-Agent':this.osFullName
|
||||
},
|
||||
extraData:ChangeUtil.map2JsonO(extraDatas)
|
||||
}
|
||||
|
||||
let fullUrl = this.baseURL + path
|
||||
|
||||
|
||||
return httpInstance.request(fullUrl, options).then((res) => {
|
||||
logger.info('Response param'+JSON.stringify(extraDatas))
|
||||
logger.info('Response fullUrl:' +fullUrl+ res.result);
|
||||
const result = res.result as HdResponse<T>
|
||||
return result
|
||||
}).catch((err: BusinessError) => {
|
||||
logger.info(fullUrl+`Response succeeded: ${err}`);
|
||||
promptAction.showToast({ message: err.message || '网络错误' })
|
||||
return Promise.reject(err)
|
||||
}).finally(() => {
|
||||
httpInstance.destroy()
|
||||
})
|
||||
}
|
||||
|
||||
private requestafter<T>(path: string, method: http.RequestMethod = http.RequestMethod.GET, extraData?: Object) {
|
||||
const httpInstance = http.createHttp()
|
||||
@ -129,6 +162,9 @@ class HdHttp {
|
||||
posts<T>(url: string, data: HashMap<string, string>): Promise<HdResponse<T>> {
|
||||
return this.request<T>(url, http.RequestMethod.POST, data)
|
||||
}
|
||||
postO<T>(url: string, data: HashMap<string, Object>): Promise<HdResponse<T>> {
|
||||
return this.requestObject<T>(url, http.RequestMethod.POST, data)
|
||||
}
|
||||
httpReq<T>(url: string, datas: HashMap<string, string>): Promise<HdResponse<T>> {
|
||||
// 创建httpRequest对象。
|
||||
let httpRequest = http.createHttp();
|
||||
@ -171,6 +207,47 @@ class HdHttp {
|
||||
})
|
||||
}
|
||||
|
||||
httpReqObject<T>(url: string, datas: HashMap<string, Object>): Promise<HdResponse<T>> {
|
||||
// 创建httpRequest对象。
|
||||
let httpRequest = http.createHttp();
|
||||
let url1 = "https://dev-app.igandan.com/app/manager/getSystemTimeStamp";
|
||||
let promise = httpRequest.request(
|
||||
// 请求url地址
|
||||
url1,
|
||||
{
|
||||
// 请求方式
|
||||
method: http.RequestMethod.GET,
|
||||
// 可选,默认为60s
|
||||
connectTimeout: 60000,
|
||||
// 可选,默认为60s
|
||||
readTimeout: 60000,
|
||||
// 开发者根据自身业务需要添加header字段
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
// 处理响应结果。
|
||||
return promise.then(async (data) => {
|
||||
if (data.responseCode === http.ResponseCode.OK) {
|
||||
logger.info('Response httpReq:' + data.result);
|
||||
let json:TimestampBean = JSON.parse(data.result.toString()) as TimestampBean;
|
||||
let tp = json.timestamp;
|
||||
datas.set("user_uuid", authStore.getUser().uuid?authStore.getUser().uuid:'');
|
||||
datas.set("client_type", 'H');
|
||||
datas.set("version", await this.getVersion() );
|
||||
datas.set('timestamp',tp+'');
|
||||
return this.postO<T>(url, datas);
|
||||
} else {
|
||||
return this.postO<T>(url, datas);
|
||||
}
|
||||
}
|
||||
).catch((err:BusinessError) => {
|
||||
logger.info('Response httpReq error:' + JSON.stringify(err));
|
||||
return Promise.reject(err);
|
||||
}).finally(() => {
|
||||
httpRequest.destroy()
|
||||
})
|
||||
}
|
||||
httpReqSimply<T>(url: string) {
|
||||
// 创建httpRequest对象。
|
||||
let httpRequest = http.createHttp();
|
||||
@ -203,6 +280,29 @@ class HdHttp {
|
||||
})
|
||||
}
|
||||
|
||||
getSignO(extraDatas1:HashMap<string, Object>): string {
|
||||
let secret= extraDatas1.get("timestamp")+''
|
||||
if(secret!=null) {
|
||||
let keyValueStr: string = "";
|
||||
let entriesArray: Array<string> = Array.from(extraDatas1.keys());
|
||||
entriesArray.sort();
|
||||
|
||||
let sortedMap:HashMap<string, Object> = new HashMap();
|
||||
entriesArray.forEach((value: string, index: number) => {
|
||||
sortedMap.set(value,extraDatas1.get(value));
|
||||
// keyValueStr +=value+extraDatas1.get(value)
|
||||
keyValueStr +=value+JSON.stringify(extraDatas1.get(value))
|
||||
});
|
||||
keyValueStr = keyValueStr + CryptoJS.MD5(secret).toString();
|
||||
keyValueStr = keyValueStr.replaceAll(" ", "").replaceAll("\"", "").replaceAll(":","=");
|
||||
let Md5keyValueStr: string = CryptoJS.MD5(keyValueStr).toString();
|
||||
let base64Str:string=Base64Util.encodeToStrSync(Md5keyValueStr);
|
||||
return base64Str;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
getSign(extraDatas1:HashMap<string, string>): string {
|
||||
let secret= extraDatas1.get("timestamp")
|
||||
if(secret!=null) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { iconsModel } from '../model/HomeModel'
|
||||
import { patientDbManager, PatientEntity } from '@itcast/basic';
|
||||
import { promptAction } from '@kit.ArkUI';
|
||||
import { promptAction, router } from '@kit.ArkUI';
|
||||
|
||||
// interface iconsModel {
|
||||
// img:string;
|
||||
@ -57,6 +57,13 @@ export struct HomeIconComp {
|
||||
const patients = await patientDbManager.getAllPatients();
|
||||
console.info(`添加了 ${patients.length} 个患者`);
|
||||
promptAction.showToast({message:`添加了 ${patients.length} 个患者`})
|
||||
if(item.name=='公益咨询') {
|
||||
router.pushUrl({ url: 'pages/Netease/PublicConsultationPage' })
|
||||
}
|
||||
else if(item.name=='我的患者') {
|
||||
router.pushUrl({url:'pages/Netease/imTabPage'})
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
}.width('100%').backgroundColor(Color.White)
|
||||
|
||||
@ -198,9 +198,9 @@ export struct VideoPage {
|
||||
})
|
||||
.margin({bottom:60})
|
||||
.onClick(() => {
|
||||
// router.pushUrl({url:'pages/VideoPage/PastVideoPage'})
|
||||
router.pushUrl({url:'pages/VideoPage/PastVideoPage'})
|
||||
// router.pushUrl({url:'pages/Netease/imTabPage'})
|
||||
router.pushUrl({url:'pages/Netease/PublicConsultationPage'})
|
||||
// router.pushUrl({url:'pages/Netease/PublicConsultationPage'})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,8 @@ export struct ConsultationDetailComp {
|
||||
builder:PerfactInputSheet({
|
||||
controller:this.custom,
|
||||
inputTitle:'',
|
||||
okText:'是',
|
||||
cancelText:'否',
|
||||
inputPlaceholder:this.inputPlaceholder,
|
||||
style:'2',
|
||||
okColor:$r('app.color.top_title'),
|
||||
|
||||
@ -263,11 +263,21 @@ export struct InterrogationDetailComp {
|
||||
.backgroundColor($r('app.color.patient_theme'))
|
||||
.fontColor(Color.White)
|
||||
.onClick(() => {
|
||||
if(this.params.isHistory =='false')
|
||||
{
|
||||
router.pushUrl({
|
||||
url: 'pages/Netease/MyOpinionPage',
|
||||
params: { uuid:this.params.uuid,isHistory:this.params.isHistory}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
router.pushUrl({
|
||||
url: 'pages/Netease/MyOpinionPage',
|
||||
params: { uuid:this.params.uuid,isHistory:this.params.isHistory,myAnswer:this.getMyanswer(this.AnswerList)}
|
||||
});
|
||||
}
|
||||
|
||||
router.pushUrl({
|
||||
url: 'pages/Netease/MyOpinionPage',
|
||||
// params: { uuid: this.item.uuid}
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
@ -307,5 +317,11 @@ export struct InterrogationDetailComp {
|
||||
|
||||
}
|
||||
|
||||
getMyanswer(AnswerList:AnswerListBean[])
|
||||
{
|
||||
let targetItem = AnswerList.find(item => authStore.getUser().uuid ==item.expert_uuid );
|
||||
return targetItem
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
import { ChangePhotoGrids, HdNav, ViewImageInfo } from "@itcast/basic"
|
||||
import { BasicConstant, ChangePhotoGrids,
|
||||
ChangeUtil,
|
||||
hdHttp,
|
||||
HdLoadingDialog, HdNav,
|
||||
HdResponse,
|
||||
preferenceStore, ViewImageInfo } from "@itcast/basic"
|
||||
import { PhotoActionSheet } from '@itcast/basic'
|
||||
|
||||
import { AnswerListBean } from "../model/ConsulModel"
|
||||
import { router } from "@kit.ArkUI"
|
||||
import { PerfactInputSheet } from "@itcast/basic/src/main/ets/Views/PerfactInputSheet"
|
||||
import { BusinessError } from "@kit.BasicServicesKit"
|
||||
import { HashMap } from "@kit.ArkTS"
|
||||
import { rcp } from '@kit.RemoteCommunicationKit';
|
||||
@Component
|
||||
export struct MyOpinionComp {
|
||||
@State photos: string[] = []
|
||||
@ -10,12 +20,15 @@ export struct MyOpinionComp {
|
||||
@State
|
||||
@Watch('onRemoveImg')
|
||||
removeImg: boolean=false
|
||||
|
||||
@State text: string = ''
|
||||
@State
|
||||
@Watch('onAddImg')
|
||||
addImg: boolean=false
|
||||
|
||||
@State params:param = router.getParams() as param;
|
||||
@State removeIndex: number=0
|
||||
hashMap: HashMap<string, Object> = new HashMap();
|
||||
hashMapImg: HashMap<string, string> = new HashMap();
|
||||
|
||||
onAddImg()
|
||||
{
|
||||
this.photoSheetDialog.open()
|
||||
@ -25,6 +38,42 @@ export struct MyOpinionComp {
|
||||
this.photos.splice(this.removeIndex, 1)
|
||||
this.maxSelectNumber = this.maxSelectNumber - this.photos.length;
|
||||
}
|
||||
private custom!:CustomDialogController;
|
||||
dialog: CustomDialogController = new CustomDialogController({
|
||||
builder: HdLoadingDialog({ message: '加载中...' }),
|
||||
customStyle: true,
|
||||
alignment: DialogAlignment.Center
|
||||
})
|
||||
initDialog() {
|
||||
this.custom = new CustomDialogController({
|
||||
builder:PerfactInputSheet({
|
||||
controller:this.custom,
|
||||
inputTitle:'提示',
|
||||
okText:'保存',
|
||||
needcancelCallBack:true,
|
||||
okColor:$r('app.color.top_title'),
|
||||
inputPlaceholder:'您有未发布的内容,是否保存?',
|
||||
style:'2',
|
||||
inputCallBack:(input: string,title:string)=>{
|
||||
if(title=='needcancelCallBack')
|
||||
{
|
||||
preferenceStore.setItemString('MyOpinionComp'+this.params.uuid,'')
|
||||
}
|
||||
else
|
||||
{
|
||||
preferenceStore.setItemString('MyOpinionComp'+this.params.uuid,this.text)
|
||||
}
|
||||
router.back()
|
||||
|
||||
}
|
||||
}),
|
||||
alignment: DialogAlignment.Center,
|
||||
customStyle: true,
|
||||
autoCancel: false,
|
||||
backgroundColor: ('rgba(0,0,0,0.5)'),
|
||||
height: '100%'
|
||||
})
|
||||
}
|
||||
private initPhotoDialog() {
|
||||
this.photoSheetDialog = new CustomDialogController({
|
||||
builder: PhotoActionSheet({
|
||||
@ -44,13 +93,7 @@ export struct MyOpinionComp {
|
||||
|
||||
|
||||
}
|
||||
// onPhotoSelected: async (uri: string) => {
|
||||
// if (uri && this.photos.length < 9) {
|
||||
// this.photos.push(uri)
|
||||
// }
|
||||
// // this.photoPath = uri;
|
||||
// // this.base64Stringphoto = await ChangeUtil.convertUriToBase64(uri);
|
||||
// }
|
||||
|
||||
}),
|
||||
alignment: DialogAlignment.Bottom,
|
||||
customStyle: true,
|
||||
@ -60,17 +103,90 @@ export struct MyOpinionComp {
|
||||
});
|
||||
}
|
||||
aboutToAppear(): void {
|
||||
console.log('Response aboutToAppear')
|
||||
this.initPhotoDialog()
|
||||
this.initDialog()
|
||||
if(this.params.isHistory =='true')
|
||||
{
|
||||
this.text=this.params.myAnswer.note
|
||||
if(this.params.myAnswer.imgs!=null)
|
||||
{
|
||||
this.photos.push(...this.changeToImgs(this.params.myAnswer.imgs.split(",")))
|
||||
this.maxSelectNumber = this.maxSelectNumber - this.photos.length;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
this.text=preferenceStore.getItemString('MyOpinionComp'+this.params.uuid)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
HdNav({ title: '我的意见', showRightIcon: false, showLeftIcon: true })
|
||||
HdNav({ title: '我的意见', showRightIcon: false, showLeftIcon: true ,isLeftAction:true,leftItemAction:()=>{
|
||||
|
||||
if(this.params.isHistory =='false')
|
||||
{
|
||||
if(this.text.length>0)
|
||||
{
|
||||
this.custom.open()
|
||||
}
|
||||
else
|
||||
{
|
||||
router.back()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
router.back()
|
||||
}
|
||||
}})
|
||||
Text('我的意见 *').fontSize(17).fontColor($r('app.color.top_title')).padding(10).width('100%').textAlign(TextAlign.Start)
|
||||
TextArea({ placeholder: '请依据患者的个人信息、疾病资料及患者所咨询的问题详细解答患者的问题(信息仅提问患者及医生可见、最多输入300个字)', text: $$this.text })
|
||||
.fontColor($r('app.color.common_gray_03'))
|
||||
.height(100)
|
||||
.textAlign(TextAlign.Start)
|
||||
.fontSize(14).padding(9).margin({left:10,right:10,bottom:10})
|
||||
.backgroundColor($r('app.color.f6f6f6')).borderRadius(8)
|
||||
Text('相关图片').fontSize(17).fontColor($r('app.color.top_title')).padding({left:10,right:10,bottom:10}).width('100%').textAlign(TextAlign.Start)
|
||||
Text('可以用部分科普或文献来协助问答问题,最多6张').fontSize(14).fontColor($r('app.color.999999')).padding({left:10,right:10,bottom:10}).width('100%').textAlign(TextAlign.Start)
|
||||
ChangePhotoGrids({imgList:this.changeToImg(this.photos),maxSelectNumber:6
|
||||
,addImg:this.addImg,removeImg:this.removeImg,removeIndex:this.removeIndex})
|
||||
.backgroundColor(Color.Red)
|
||||
,addImg:this.addImg,removeImg:this.removeImg,removeIndex:this.removeIndex}).layoutWeight(1)
|
||||
Column()
|
||||
{
|
||||
Button({ type: ButtonType.Normal }){
|
||||
Text('提交')
|
||||
}
|
||||
.width('100%')
|
||||
.height(53)
|
||||
.backgroundColor($r('app.color.patient_theme'))
|
||||
.fontColor(Color.White)
|
||||
.onClick(() => {
|
||||
if(this.params.isHistory =='false')
|
||||
{
|
||||
// router.pushUrl({
|
||||
// url: 'pages/Netease/MyOpinionPage',
|
||||
// params: { uuid:this.params.uuid,isHistory:this.params.isHistory}
|
||||
// });
|
||||
}
|
||||
else
|
||||
{
|
||||
this.updateInterrogationAnswer()
|
||||
// router.pushUrl({
|
||||
// url: 'pages/Netease/MyOpinionPage',
|
||||
// params: { uuid:this.params.uuid,isHistory:this.params.isHistory,myAnswer:this.getMyanswer(this.AnswerList)}
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
.backgroundColor(Color.White)
|
||||
.width('100%')
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -78,6 +194,48 @@ export struct MyOpinionComp {
|
||||
.width('100%')
|
||||
}
|
||||
|
||||
async getUploadImg()
|
||||
{
|
||||
this.hashMapImg.clear();
|
||||
|
||||
if(this.photos.length>0)
|
||||
{
|
||||
for (let index = 0; index < this.photos.length; index++) {
|
||||
if(this.photos[index].includes('http'))
|
||||
{
|
||||
this.hashMapImg.set('img'+index+1,await ChangeUtil.getImageBase64(this.photos[index]))
|
||||
}
|
||||
else
|
||||
{
|
||||
this.hashMapImg.set('img'+index+1, await ChangeUtil.convertUriToBase64(this.photos[index]))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return this.hashMapImg
|
||||
}
|
||||
|
||||
async updateInterrogationAnswer()
|
||||
{
|
||||
this.dialog.open()
|
||||
this.hashMap.clear();
|
||||
this.hashMap.set("note",this.text);
|
||||
this.hashMap.set('uuid', this.params.uuid)
|
||||
this.hashMap.set('imgsBean',await this.getUploadImg())
|
||||
hdHttp.httpReqObject<string>(BasicConstant.updateInterrogationAnswer,this.hashMap).then(async (res: HdResponse<string>) => {
|
||||
this.dialog.close()
|
||||
|
||||
|
||||
|
||||
}).catch((err: BusinessError) => {
|
||||
this.dialog.close()
|
||||
|
||||
})
|
||||
|
||||
// let req = new rcp.Request("http://example.com", "POST", headers, simpleForm, cookies, transferRange, configuration);
|
||||
}
|
||||
|
||||
changeToImg( imgListurl:string[])
|
||||
{
|
||||
let imgListtmps:ViewImageInfo[]=[]
|
||||
@ -88,4 +246,21 @@ export struct MyOpinionComp {
|
||||
return imgListtmps
|
||||
|
||||
}
|
||||
}
|
||||
changeToImgs( imgListurl:string[])
|
||||
{
|
||||
let imgListtmps:string[]=[]
|
||||
imgListurl.forEach((url: string) => {
|
||||
let item = BasicConstant.urlHtml + url
|
||||
imgListtmps.push(item)
|
||||
})
|
||||
return imgListtmps
|
||||
|
||||
}
|
||||
}
|
||||
interface param{
|
||||
uuid:string,
|
||||
isHistory:string,
|
||||
myAnswer:AnswerListBean
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -5,8 +5,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { patientDbManager, PatientEntity } from '@itcast/basic';
|
||||
import { ChatKitClient } from '@nimkit/chatkit';
|
||||
import { AvatarColorUntil, AvatarItem, CommonAvatar } from '@nimkit/common';
|
||||
import { AvatarColorUntil, AvatarItem, CommonAvatar, UserUtils } from '@nimkit/common';
|
||||
import { DateUtil } from '@nimkit/common/src/main/ets/utils/DateUtil';
|
||||
import { V2NIMLocalConversation, V2NIMMessageCallAttachment, V2NIMMessageType } from '@nimsdk/base';
|
||||
import { LocalConversationOperationDialog } from './LocalConversationOperationDialog';
|
||||
@ -25,6 +26,12 @@ export struct ConversationViewItem {
|
||||
width: '60%',
|
||||
borderColor: '#ffDCDFE5'
|
||||
})
|
||||
@Local showName:string=String(this.conversationInfo?.name)
|
||||
|
||||
async aboutToAppear(): Promise<void> {
|
||||
let account = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(this.conversationInfo?.conversationId)
|
||||
this.showName=await UserUtils.getAvatarName(account,String(this.conversationInfo?.name))
|
||||
}
|
||||
|
||||
build() {
|
||||
if (this.conversationInfo !== null) {
|
||||
@ -59,7 +66,7 @@ export struct ConversationViewItem {
|
||||
|
||||
Column() {
|
||||
//item 显示名称
|
||||
Text(this.conversationInfo.name)
|
||||
Text(this.showName)
|
||||
.fontSize(16)
|
||||
.fontColor("#ff333333")
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
@ -160,6 +167,25 @@ export struct ConversationViewItem {
|
||||
}
|
||||
}
|
||||
|
||||
// async getAvatarName(sourceName: string){
|
||||
// let account = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(this.conversationInfo?.conversationId)
|
||||
// let patient =await patientDbManager.getPatientByUuid(account)
|
||||
// if(patient!=null)
|
||||
// {
|
||||
// if(patient.nickname!=null&&patient.nickname!='')
|
||||
// {
|
||||
// return patient.nickname
|
||||
// }
|
||||
// if(patient.realName!=null&&patient.realName!='')
|
||||
// {
|
||||
// return patient.realName
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return String(this.conversationInfo?.name)
|
||||
//
|
||||
// }
|
||||
/**
|
||||
* 头像只显示后两位
|
||||
* @param sourceName
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
"license": "",
|
||||
"dependencies": {
|
||||
"@itcast/basic": "file:../../commons/basic",
|
||||
"mypage":"file:../../features/mypage",
|
||||
"home": 'file:../../features/Home',
|
||||
"register": 'file:../../features/register',
|
||||
"patient": 'file:../../features/patient',
|
||||
"mypage": "file:../../features/mypage",
|
||||
"home": "file:../../features/Home",
|
||||
"register": "file:../../features/register",
|
||||
"patient": "file:../../features/patient",
|
||||
"scene_single_video": "file:../../scene_single_video",
|
||||
"media-player-common": "file:../../polyv",
|
||||
"@polyvharmony/media-player-sdk": "2.5.0",
|
||||
@ -28,7 +28,8 @@
|
||||
"@nimsdk/nim": "10.9.10",
|
||||
"@nimsdk/base": "10.9.10",
|
||||
"@nimkit/corekit": "file:../../corekit",
|
||||
'@nimkit/chatkit': "file:../../chatkit",
|
||||
'netease': "file:../../features/netease"
|
||||
"@nimkit/chatkit": "file:../../chatkit",
|
||||
"netease": "file:../../features/netease",
|
||||
|
||||
}
|
||||
}
|
||||
@ -52,9 +52,9 @@ export class NimRepository {
|
||||
console.debug(`Performance Test im start loginSuccess`)
|
||||
await this.nim.loginService.login(accountId, token);
|
||||
console.error('----------- 登录成功 -----------')
|
||||
router.pushUrl({
|
||||
url: 'pages/Netease/imTabPage'
|
||||
});
|
||||
// router.pushUrl({
|
||||
// url: 'pages/Netease/imTabPage'
|
||||
// });
|
||||
console.debug(`Performance Test im loginSuccess`)
|
||||
|
||||
ChatKitClient.init(this.nim, appKey)
|
||||
@ -62,6 +62,7 @@ export class NimRepository {
|
||||
|
||||
} catch (error) {
|
||||
console.error('----------- 登录失败 -----------', error)
|
||||
ChatKitClient.init(this.nim, appKey)
|
||||
throw error as Error
|
||||
}
|
||||
}
|
||||
|
||||
344
products/expert/src/main/ets/pages/WebView/WebPageSnapshot.ets
Normal file
344
products/expert/src/main/ets/pages/WebView/WebPageSnapshot.ets
Normal file
@ -0,0 +1,344 @@
|
||||
import webview from '@ohos.web.webview';
|
||||
import { HdNav } from '@itcast/basic';
|
||||
import router from '@ohos.router';
|
||||
import { image } from '@kit.ImageKit';
|
||||
import { photoAccessHelper } from '@kit.MediaLibraryKit';
|
||||
import { promptAction } from '@kit.ArkUI';
|
||||
import { componentSnapshot } from '@kit.ArkUI';
|
||||
import { fileIo, fileUri } from '@kit.CoreFileKit';
|
||||
import { display} from '@kit.ArkUI';
|
||||
|
||||
|
||||
const TAG = 'WebViewSaveImage';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct WebPageSnapshot {
|
||||
private controller: webview.WebviewController = new webview.WebviewController();
|
||||
@State params:RouteParams = router.getParams() as RouteParams;
|
||||
|
||||
@State contentWidth: number = 0;
|
||||
@State contentHeight: number = 0;
|
||||
@State url: string = this.params.url;
|
||||
@State title: string = this.params.title;
|
||||
customUserAgent: string = 'gdxz-expert';
|
||||
// 网页尺寸
|
||||
@State h5Width: number = 0;
|
||||
@State h5Height: number = 0;
|
||||
// Web组件尺寸
|
||||
private webWidth: number = 0;
|
||||
private webHeight: number = 0;
|
||||
// 当前网页位置
|
||||
private curXOffset: number = 0;
|
||||
private curYOffset: number = 0;
|
||||
// 备份当前网页位置
|
||||
private xOffsetBefore: number = 0;
|
||||
private yOffsetBefore: number = 0;
|
||||
// 截图过程的Web组件覆盖
|
||||
@State webMaskImage: PixelMap | undefined = undefined;
|
||||
private webMaskImageZIndex: number = -1;
|
||||
// 合并后的图片
|
||||
@State mergedImage: PixelMap | undefined = undefined;
|
||||
|
||||
@State snapPopupPosition: Position = { x: 0, y: 0 };
|
||||
//Web是否已经滚动到底部
|
||||
@State WebTouchBottom: boolean = false;
|
||||
// 屏幕尺寸
|
||||
private displayWidth: number = 0;
|
||||
private displayHeight: number = 0;
|
||||
onBackPress(): boolean | void {
|
||||
if (this.controller.accessStep(-1)) {
|
||||
this.controller.backward();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
HdNav({ title: this.title, showRightIcon: false, hasBorder: true ,isLeftAction:true,leftItemAction:()=>{
|
||||
if (this.controller.accessBackward()) {
|
||||
this.controller.backward();
|
||||
} else {
|
||||
router.back();
|
||||
}
|
||||
}})
|
||||
|
||||
Web({
|
||||
src: this.url,
|
||||
controller: this.controller
|
||||
})
|
||||
.id('webViewShot')
|
||||
.mixedMode(MixedMode.All)
|
||||
.overScrollMode(OverScrollMode.ALWAYS)
|
||||
.domStorageAccess(true)
|
||||
.onAreaChange((oldValue, newValue) => {
|
||||
// TODO: 高性能知识点: onAreaChange为高频回调,组件变动时每帧都会调用,避免冗余和耗时操作。
|
||||
this.webWidth = newValue.width as number;
|
||||
this.webHeight = newValue.height as number;
|
||||
|
||||
})
|
||||
.onControllerAttached(() => {
|
||||
let userAgent = this.controller.getUserAgent() + this.customUserAgent;
|
||||
this.controller.setCustomUserAgent(userAgent);
|
||||
})
|
||||
.onOverScroll((event) => {
|
||||
if (event?.yOffset > 0) {
|
||||
this.WebTouchBottom = true
|
||||
} else if (event?.yOffset === 0 && this.WebTouchBottom) {
|
||||
this.WebTouchBottom = false
|
||||
}
|
||||
})
|
||||
.onScroll((event) => {
|
||||
this.curXOffset = event.xOffset;
|
||||
this.curYOffset = event.yOffset;
|
||||
|
||||
})
|
||||
.width('100%')
|
||||
.layoutWeight(1)// 占据剩余空间
|
||||
.height('100%')
|
||||
|
||||
if (this.title == '随访二维码') {
|
||||
Row(){
|
||||
SaveButton({text:SaveDescription.SAVE_IMAGE,buttonType:ButtonType.Normal})
|
||||
.fontSize(16)
|
||||
.fontColor(Color.White)
|
||||
.backgroundColor('rgb(63,199,193)')
|
||||
.width('100%').height(50)
|
||||
.onClick( () => {
|
||||
|
||||
this.snapShot();
|
||||
|
||||
})
|
||||
}.width('100%').height(56).backgroundColor(Color.White).alignItems(VerticalAlign.Top)
|
||||
}
|
||||
}
|
||||
.height('100%')// 关键:约束父容器高度
|
||||
}
|
||||
aboutToAppear(): void {
|
||||
let a=this.url
|
||||
// 获取屏幕尺寸
|
||||
const displayData = display.getDefaultDisplaySync();
|
||||
this.displayWidth = px2vp(displayData.width);
|
||||
this.displayHeight = px2vp(displayData.height);
|
||||
|
||||
}
|
||||
async saveWebViewImage() {
|
||||
try {
|
||||
// 1. 获取整个网页的长截图
|
||||
this.controller.webPageSnapshot({
|
||||
id: "webView",
|
||||
size: { width: this.contentWidth, height:this.contentHeight } // 确保捕获全尺寸
|
||||
}, async (error, result) => {
|
||||
if (error) {
|
||||
promptAction.showToast({ message: `截图失败: ${error.message}` });
|
||||
console.error('webPageSnapshot error:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result?.imagePixelMap) {
|
||||
promptAction.showToast({ message: '获取截图数据失败' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 转为JPEG并写入沙箱
|
||||
const ctx = getContext(this);
|
||||
const imagePacker = image.createImagePacker();
|
||||
const arrayBuffer = await imagePacker.packToData(
|
||||
result.imagePixelMap,
|
||||
{ format: 'image/jpeg', quality: 100 } // 平衡质量与大小
|
||||
);
|
||||
|
||||
const sandboxPath = ctx.cacheDir + `/web_fullpage_${Date.now()}.jpg`;
|
||||
const file = fileIo.openSync(sandboxPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
|
||||
fileIo.writeSync(file.fd, arrayBuffer);
|
||||
fileIo.closeSync(file.fd);
|
||||
|
||||
// 3. 保存到相册
|
||||
const helper = photoAccessHelper.getPhotoAccessHelper(ctx);
|
||||
const sandboxUri = fileUri.getUriFromPath(sandboxPath);
|
||||
const request = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, sandboxUri);
|
||||
await helper.applyChanges(request);
|
||||
|
||||
promptAction.showToast({ message: '网页已保存至相册' });
|
||||
});
|
||||
} catch (e) {
|
||||
promptAction.showToast({ message: '保存失败: ' + e.message });
|
||||
console.error('saveWebViewImage error:', e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 截图函数。
|
||||
*/
|
||||
async snapShot() {
|
||||
// 获取Web页面尺寸
|
||||
this.getWebSize();
|
||||
// 截图前的状态初始化
|
||||
await this.beforeSnapshot();
|
||||
// TODO: 性能知识点: 使用Canvas离屏绘制在缓冲区拼接截图
|
||||
const canvasSetting: RenderingContextSettings = new RenderingContextSettings(true);
|
||||
const offCanvasCtx: OffscreenCanvasRenderingContext2D =
|
||||
new OffscreenCanvasRenderingContext2D(this.h5Width, this.h5Height, canvasSetting);
|
||||
// 前置常量
|
||||
const snipTimes = Math.ceil(this.h5Height / this.webHeight);
|
||||
|
||||
const lastTime = snipTimes - 1;
|
||||
const leftoverHeight = this.h5Height % this.webHeight;
|
||||
let cropLeftover: image.Region = { x: 0, y: 0, size: { height: 0, width: 0 } }
|
||||
if (this.WebTouchBottom) {
|
||||
// 这里要分两种情况,1.滚动到底部时,裁剪应该取最后一张除去重复部分以外的底部
|
||||
cropLeftover = {
|
||||
x: 0,
|
||||
y: vp2px(this.webHeight - leftoverHeight),
|
||||
size: {
|
||||
height: vp2px(leftoverHeight),
|
||||
width: vp2px(this.webWidth)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// 2.未滚动到底部时,裁剪应该取最后一张leftoverHeight的上部分
|
||||
cropLeftover = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
size: {
|
||||
height: vp2px(leftoverHeight),
|
||||
width: vp2px(this.webWidth)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 开始截图
|
||||
for (let i = 0; i < snipTimes; i++) {
|
||||
const curSnip = await componentSnapshot.get('webViewShot');
|
||||
// 最后一次截图需要特殊处理,去除重复部分
|
||||
if (i === lastTime) {
|
||||
|
||||
await curSnip.crop(cropLeftover);
|
||||
offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, leftoverHeight);
|
||||
} else {
|
||||
offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, this.webHeight);
|
||||
}
|
||||
|
||||
// 继续滚动
|
||||
this.controller.scrollBy(0, this.webHeight);
|
||||
// 延时保证滚动完成
|
||||
await sleep(200);
|
||||
}
|
||||
// 截图后的操作
|
||||
await this.afterSnapshot();
|
||||
// 获取pixelMap
|
||||
this.mergedImage = offCanvasCtx.getPixelMap(0, 0, this.h5Width, this.h5Height);
|
||||
try {
|
||||
// 2. 转为JPEG并写入沙箱
|
||||
const ctx = getContext(this);
|
||||
const imagePacker = image.createImagePacker();
|
||||
const arrayBuffer = await imagePacker.packToData(
|
||||
this.mergedImage,
|
||||
{ format: 'image/jpeg', quality: 100 } // 平衡质量与大小
|
||||
);
|
||||
|
||||
const sandboxPath = ctx.cacheDir + `/web_fullpage_${Date.now()}.jpg`;
|
||||
const file = fileIo.openSync(sandboxPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
|
||||
fileIo.writeSync(file.fd, arrayBuffer);
|
||||
fileIo.closeSync(file.fd);
|
||||
|
||||
// 3. 保存到相册
|
||||
const helper = photoAccessHelper.getPhotoAccessHelper(ctx);
|
||||
const sandboxUri = fileUri.getUriFromPath(sandboxPath);
|
||||
const request = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, sandboxUri);
|
||||
await helper.applyChanges(request);
|
||||
|
||||
promptAction.showToast({ message: '网页已保存至相册' });
|
||||
// 拼接之后修改可动画变量
|
||||
// this.afterGeneratorImage();
|
||||
} catch (e) {
|
||||
promptAction.showToast({ message: '保存失败: ' + e.message });
|
||||
console.error('saveWebViewImage error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 截图前获取尺寸
|
||||
*/
|
||||
getWebSize() {
|
||||
const SCRIPT = '[document.documentElement.scrollWidth, document.documentElement.scrollHeight]';
|
||||
this.controller.runJavaScriptExt(SCRIPT).then((result) => {
|
||||
try {
|
||||
switch (result.getType()) {
|
||||
case webview.JsMessageType.ARRAY:
|
||||
this.h5Width = (result.getArray() as number[])[0]; // 单位是vp
|
||||
this.h5Height = (result.getArray() as number[])[1];
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 截图开始前的操作。
|
||||
* - 保存网页当前位置,用于恢复状态
|
||||
* - 截图当前页面作为遮罩层,避免用户察觉组件的滚动,提高用户体验
|
||||
* - Web页面滚动到顶部,准备开始截图
|
||||
* - 设置截图后小弹窗的位置,提示用户暂时不要操作,等待截图
|
||||
* - 开启提示小弹窗
|
||||
*/
|
||||
async beforeSnapshot() {
|
||||
// 保存网页当前位置,用于恢复
|
||||
this.xOffsetBefore = this.curXOffset;
|
||||
this.yOffsetBefore = this.curYOffset;
|
||||
this.h5Height = this.curYOffset + Math.ceil(this.webHeight);
|
||||
// TODO: 知识点: 使用componentSnapshot.get接口直接获取组件的渲染结果,而不需要将屏幕截图
|
||||
this.webMaskImage = await componentSnapshot.get('webViewShot');
|
||||
this.webMaskImageZIndex =2;
|
||||
this.controller.scrollTo(0, 0);
|
||||
promptAction.showToast({
|
||||
message: '正在截图,请勿操作...',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// 延时确保已经滚动到了顶部
|
||||
await sleep(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截图之后的操作。
|
||||
* - 恢复web页面到截图之前的位置
|
||||
* - 取消遮罩层
|
||||
*/
|
||||
async afterSnapshot() {
|
||||
this.controller.scrollTo(this.xOffsetBefore, this.yOffsetBefore);
|
||||
await sleep(200);
|
||||
this.webMaskImageZIndex = -1;
|
||||
this.webMaskImage = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成拼接后图片的操作。用于窗口形成移动的动画。
|
||||
*/
|
||||
async afterGeneratorImage() {
|
||||
// 小窗在屏幕中间短暂停留,避免位置突变,无法形成动画
|
||||
await sleep(200);
|
||||
// 修改弹窗位置,形成移动动画
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface RouteParams {
|
||||
url:string;
|
||||
title:string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步延时函数。
|
||||
* @param ms 延时时长,单位 ms。
|
||||
* @returns Promise 对象,执行回调。
|
||||
*/
|
||||
export function sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
@ -41,6 +41,7 @@
|
||||
"pages/Netease/InterrogationDetailCompPage",
|
||||
"pages/Netease/PatientSimplyPage",
|
||||
"pages/Netease/MyOpinionPage",
|
||||
"pages/PatientsPage/GroupManagementPage"
|
||||
"pages/PatientsPage/GroupManagementPage",
|
||||
"pages/WebView/WebPageSnapshot"
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user