作者 xiaoqiu

添加了自定义视频录制功能

正在显示 39 个修改的文件 包含 1172 行增加217 行删除
不能预览此文件类型
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 "bundleName": "com.example.fireMaintenanceAssistant", 3 "bundleName": "com.example.fireMaintenanceAssistant",
4 "vendor": "example", 4 "vendor": "example",
5 "versionCode": 1000000, 5 "versionCode": 1000000,
6 - "versionName": "1.0.0", 6 + "versionName": "1.0.1",
7 "icon": "$media:app_icon", 7 "icon": "$media:app_icon",
8 "label": "$string:app_name" 8 "label": "$string:app_name"
9 } 9 }
不能预览此文件类型
不能预览此文件类型
@@ -273,6 +273,7 @@ export interface ItemList { @@ -273,6 +273,7 @@ export interface ItemList {
273 proportion: string; 273 proportion: string;
274 state: string; 274 state: string;
275 record: recordType; 275 record: recordType;
  276 + cosKey: string | null;
276 } 277 }
277 278
278 export interface recordType { 279 export interface recordType {
@@ -10,6 +10,8 @@ export default struct AddPersonDialog { @@ -10,6 +10,8 @@ export default struct AddPersonDialog {
10 personName: '', 10 personName: '',
11 idNo: '' 11 idNo: ''
12 } 12 }
  13 + //确认添加后的回调
  14 + onChange: () => void = () => {}
13 build() { 15 build() {
14 Column(){ 16 Column(){
15 Text('添加人员').margin({top: 20}) 17 Text('添加人员').margin({top: 20})
@@ -40,6 +42,7 @@ export default struct AddPersonDialog { @@ -40,6 +42,7 @@ export default struct AddPersonDialog {
40 .onClick(async () => { 42 .onClick(async () => {
41 const res: AxiosResponse<beanType> = await companyAdd(this.addForm) 43 const res: AxiosResponse<beanType> = await companyAdd(this.addForm)
42 promptAction.showToast({message: res.data.msg}) 44 promptAction.showToast({message: res.data.msg})
  45 + this.onChange()
43 this.controller.close() 46 this.controller.close()
44 }) 47 })
45 } 48 }
@@ -14,7 +14,7 @@ import { PrintBiddingTokenUtils } from '../mediation_adtype/test/PrintBiddintTok @@ -14,7 +14,7 @@ import { PrintBiddingTokenUtils } from '../mediation_adtype/test/PrintBiddintTok
14 import { window, promptAction, router } from '@kit.ArkUI' 14 import { window, promptAction, router } from '@kit.ArkUI'
15 import { DemoConstants } from '../entryability/DemoConstants' 15 import { DemoConstants } from '../entryability/DemoConstants'
16 16
17 -import('./SplashAdShowPage') 17 +import('./CreateCamera')
18 18
19 @Builder 19 @Builder
20 function bottomViewBuilder() { 20 function bottomViewBuilder() {
@@ -111,7 +111,13 @@ export struct SplashAdDemoPage { @@ -111,7 +111,13 @@ export struct SplashAdDemoPage {
111 111
112 build() { 112 build() {
113 Column() { 113 Column() {
114 - Image($r('app.media.logo')).width(60).height(60) 114 + Stack(){
  115 + Progress({ value: 0, total: 10, type: ProgressType.Ring })
  116 + .width(120).color('#409EFF').backgroundColor('#fff')
  117 + .style({ strokeWidth: 10, status: ProgressStatus.LOADING })
  118 +
  119 + Image($r('app.media.logo')).width(60).height(60)
  120 + }
115 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 121 }.width('100%').height('100%').justifyContent(FlexAlign.Center)
116 } 122 }
117 123
@@ -496,9 +496,6 @@ struct EditUser { @@ -496,9 +496,6 @@ struct EditUser {
496 } 496 }
497 497
498 pageTransition() { 498 pageTransition() {
499 - // 该页面进入动画时长为1000ms,尽量与另一页面的退出动画时长匹配  
500 - PageTransitionEnter({ duration: 500 })  
501 - .slide(SlideEffect.Left).opacity(0)  
502 // 该页面退出动画时长为1200ms,尽量与另一页面的进入动画时长匹配 499 // 该页面退出动画时长为1200ms,尽量与另一页面的进入动画时长匹配
503 PageTransitionExit({ duration: 500 }) 500 PageTransitionExit({ duration: 500 })
504 .translate({ x: 150.0 }) 501 .translate({ x: 150.0 })
  1 +import camera from '@ohos.multimedia.camera';
  2 +import media from '@ohos.multimedia.media';
  3 +import { BusinessError } from '@ohos.base';
  4 +import { promptAction, router } from '@kit.ArkUI'
  5 +import { common } from '@kit.AbilityKit';
  6 +import { FileUtil, CommonConstants } from '../utils/FileUtil';
  7 +import { uploadFileByTask } from '../utils/uploadCloud'
  8 +
  9 +/**
  10 + * 视频录制
  11 + */
  12 +
  13 +interface routerParams {
  14 + cosKeyStr: string;
  15 + relateId: number;
  16 +}
  17 +
  18 +const TAG: string = 'Record';
  19 +let context = getContext(this) as common.UIAbilityContext;
  20 +let routerParamsData: routerParams = router.getParams() as routerParams;
  21 +@Entry
  22 +@Component
  23 +struct CreateCamera {
  24 + @State xComponentWidth: number = 0;
  25 + @State xComponentHeight: number = 0;
  26 + @State recording: boolean = false;
  27 + @State path: string = '';
  28 + @State countTime: number = 0;
  29 + @State cameraManager: camera.CameraManager | undefined = undefined;
  30 + @State videoOutput: camera.VideoOutput | undefined = undefined;
  31 + @State captureSession: camera.Session | undefined = undefined;
  32 + @State cameraInput: camera.CameraInput | undefined = undefined;
  33 + @State previewOutput: camera.PreviewOutput | undefined = undefined;
  34 + @State avRecorder: media.AVRecorder | undefined = undefined;
  35 + private mXComponentController: XComponentController = new XComponentController;
  36 + private surfaceId: string = '';
  37 + url: string = '';
  38 + timer: number = 0;
  39 +
  40 + aboutToAppear() {
  41 + // 创建存放视频文件
  42 + this.path = context.filesDir + '/' + 'VIDEO_' + Date.parse(new Date().toString()) + '.mp4';
  43 + let file = FileUtil.createOrOpen(this.path);
  44 + this.url = 'fd://' + file.fd;
  45 + }
  46 +
  47 + build() {
  48 + Stack({ alignContent: Alignment.Top }) {
  49 + XComponent({
  50 + id: 'componentId',
  51 + type: XComponentType.SURFACE,
  52 + controller: this.mXComponentController,
  53 + })
  54 + .onLoad(async () => {
  55 + this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
  56 + await this.initCamera(getContext(this), this.surfaceId);
  57 + })
  58 + .height(CommonConstants.SEVENTY_PERCENT)
  59 + .margin({
  60 + top: CommonConstants.FIFTEEN_PERCENT
  61 + })
  62 + Column() {
  63 + Text(`录制时长:${this.countTime}s`).fontSize(16).fontColor(Color.White)
  64 + Blank()
  65 + Row() {
  66 + Image(this.recording ? $r('app.media.camera_pause_video_4x') : $r('app.media.camera_take_video_4x'))
  67 + .width(px2vp(CommonConstants.IMAGE_SIZE))
  68 + .height(px2vp(CommonConstants.IMAGE_SIZE))
  69 + .onClick(async () => {
  70 + if (this.recording) {
  71 + clearInterval(this.timer);
  72 + this.timer = 0
  73 + await this.stopRecord();
  74 + await uploadFileByTask(routerParamsData.cosKeyStr, routerParamsData.relateId, this.path);
  75 + router.back()
  76 + } else {
  77 + this.timer = setInterval(async () => {
  78 + this.countTime++;
  79 + if(this.countTime >= 120){
  80 + clearInterval(this.timer);
  81 + this.timer = 0
  82 + await this.stopRecord();
  83 + await uploadFileByTask(routerParamsData.cosKeyStr, routerParamsData.relateId, this.path);
  84 + router.back()
  85 + }
  86 + }, 1000);
  87 + this.startRecord();
  88 + }
  89 + this.recording = !this.recording;
  90 + })
  91 + }
  92 + .width(CommonConstants.FULL_PERCENT)
  93 + .height(120)
  94 + .margin({ bottom: 60 })
  95 + .justifyContent(FlexAlign.Center)
  96 + .alignItems(VerticalAlign.Center)
  97 + }
  98 + .width(CommonConstants.FULL_PERCENT)
  99 + .height(CommonConstants.FULL_PERCENT)
  100 + .justifyContent(FlexAlign.Start)
  101 + .alignItems(HorizontalAlign.Start)
  102 + }
  103 + .backgroundColor(Color.Black)
  104 + .width(CommonConstants.FULL_PERCENT)
  105 + .height(CommonConstants.FULL_PERCENT)
  106 + }
  107 +
  108 + // 初始化相机
  109 + async initCamera(context: common.Context, surfaceId: string) {
  110 + this.cameraManager = camera.getCameraManager(context);
  111 + if (!this.cameraManager) {
  112 + promptAction.showToast({ message: 'camera.getCameraManager error' })
  113 + return;
  114 + }
  115 +
  116 + this.cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
  117 + promptAction.showToast({ message: `camera : ${cameraStatusInfo.camera.cameraId},status: ${cameraStatusInfo.status}` })
  118 + });
  119 +
  120 + let cameraArray: Array<camera.CameraDevice> = [];
  121 + try {
  122 + cameraArray = this.cameraManager.getSupportedCameras();
  123 + } catch (error) {
  124 + let err = error as BusinessError;
  125 + promptAction.showToast({ message: `getSupportedCameras call failed. error code: ${err.code}` })
  126 + }
  127 +
  128 + if (cameraArray.length <= 0) {
  129 + promptAction.showToast({ message: 'cameraManager.getSupportedCameras error' })
  130 + return;
  131 + }
  132 +
  133 + let cameraOutputCap: camera.CameraOutputCapability =
  134 + this.cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO);
  135 + if (!cameraOutputCap) {
  136 + promptAction.showToast({ message: 'cameraManager.getSupportedOutputCapability error' })
  137 + return;
  138 + }
  139 + promptAction.showToast({ message: 'outputCapability: ' + JSON.stringify(cameraOutputCap) })
  140 +
  141 + let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
  142 + if (!previewProfilesArray) {
  143 + promptAction.showToast({ message: 'createOutput previewProfilesArray === null || undefined' })
  144 + }
  145 +
  146 + let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
  147 + if (!photoProfilesArray) {
  148 + promptAction.showToast({ message: 'createOutput photoProfilesArray === null || undefined' })
  149 + }
  150 +
  151 + let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles;
  152 + if (!videoProfilesArray) {
  153 + promptAction.showToast({ message: 'createOutput videoProfilesArray === null || undefined' })
  154 + }
  155 +
  156 + let videoSize: camera.Size = {
  157 + width: 640,
  158 + height: 480
  159 + }
  160 + let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => {
  161 + return profile.size.width === videoSize.width && profile.size.height === videoSize.height;
  162 + });
  163 +
  164 + if (!videoProfile) {
  165 + promptAction.showToast({ message: 'videoProfile is not found' })
  166 + return;
  167 + }
  168 +
  169 + let aVRecorderProfile: media.AVRecorderProfile = {
  170 + audioBitrate: 48000,
  171 + audioChannels: 2,
  172 + audioCodec: media.CodecMimeType.AUDIO_AAC,
  173 + audioSampleRate: 48000,
  174 + fileFormat: media.ContainerFormatType.CFT_MPEG_4,
  175 + videoBitrate: 2000000,
  176 + videoCodec: media.CodecMimeType.VIDEO_AVC,
  177 + videoFrameWidth: videoSize.width,
  178 + videoFrameHeight: videoSize.height,
  179 + videoFrameRate: 30
  180 + };
  181 +
  182 + let aVRecorderConfig: media.AVRecorderConfig = {
  183 + audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
  184 + videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
  185 + profile: aVRecorderProfile,
  186 + url: this.url,
  187 + rotation: 0,
  188 + location: {
  189 + latitude: 30,
  190 + longitude: 130
  191 + }
  192 + };
  193 +
  194 + try {
  195 + this.avRecorder = await media.createAVRecorder();
  196 + } catch (error) {
  197 + let err = error as BusinessError;
  198 + promptAction.showToast({ message: `createAVRecorder call failed. error code: ${err.code}` })
  199 + }
  200 +
  201 + if (this.avRecorder === undefined) {
  202 + return;
  203 + }
  204 +
  205 + try {
  206 + await this.avRecorder.prepare(aVRecorderConfig);
  207 + } catch (error) {
  208 + let err = error as BusinessError;
  209 + promptAction.showToast({ message: `prepare call failed. error code: ${err.code}` })
  210 + }
  211 +
  212 + let videoSurfaceId: string | undefined = undefined;
  213 + try {
  214 + videoSurfaceId = await this.avRecorder.getInputSurface();
  215 + } catch (error) {
  216 + let err = error as BusinessError;
  217 + promptAction.showToast({ message: `getInputSurface call failed. error code: ${err.code}` })
  218 + }
  219 + if (videoSurfaceId === undefined) {
  220 + return;
  221 + }
  222 +
  223 + try {
  224 + this.videoOutput = this.cameraManager.createVideoOutput(videoProfile, videoSurfaceId);
  225 + } catch (error) {
  226 + let err = error as BusinessError;
  227 + promptAction.showToast({ message: `Failed to create the videoOutput instance. error: ${JSON.stringify(err)}` })
  228 + }
  229 + if (this.videoOutput === undefined) {
  230 + return;
  231 + }
  232 + this.videoOutput.on('frameStart', () => {
  233 + promptAction.showToast({ message: 'Video frame started' })
  234 + });
  235 +
  236 + this.videoOutput.on('error', (error: BusinessError) => {
  237 + promptAction.showToast({ message: `Video frame error code: ${error.code}` })
  238 + });
  239 +
  240 + try {
  241 + this.captureSession = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;
  242 + ;
  243 + } catch (error) {
  244 + let err = error as BusinessError;
  245 + promptAction.showToast({ message: `Failed to create the CaptureSession instance. errorCode = ${err.code}` })
  246 + }
  247 + if (this.captureSession === undefined) {
  248 + return;
  249 + }
  250 +
  251 + try {
  252 + this.captureSession.beginConfig();
  253 + } catch (error) {
  254 + let err = error as BusinessError;
  255 + promptAction.showToast({ message: `Failed to beginConfig. errorCode = ${err.code}` })
  256 + }
  257 +
  258 + let cameraInput: camera.CameraInput | undefined = undefined;
  259 + try {
  260 + cameraInput = this.cameraManager.createCameraInput(cameraArray[0]);
  261 + } catch (error) {
  262 + let err = error as BusinessError;
  263 + promptAction.showToast({ message: `Failed to createCameraInput. errorCode = ${err.code}` })
  264 + }
  265 + if (cameraInput === undefined) {
  266 + return;
  267 + }
  268 +
  269 + let cameraDevice: camera.CameraDevice = cameraArray[0];
  270 + cameraInput.on('error', cameraDevice, (error: BusinessError) => {
  271 + promptAction.showToast({ message: `Camera input error code: ${error.code}` })
  272 + });
  273 +
  274 + try {
  275 + await cameraInput.open();
  276 + } catch (error) {
  277 + let err = error as BusinessError;
  278 + promptAction.showToast({ message: `Failed to open cameraInput. errorCode = ${err.code}` })
  279 + }
  280 +
  281 + try {
  282 + this.captureSession.addInput(cameraInput);
  283 + } catch (error) {
  284 + let err = error as BusinessError;
  285 + promptAction.showToast({ message: `Failed to add cameraInput. errorCode = ${err.code}` })
  286 + }
  287 +
  288 + let previewOutput: camera.PreviewOutput | undefined = undefined;
  289 + try {
  290 + previewOutput = this.cameraManager.createPreviewOutput(videoProfile, surfaceId);
  291 + } catch (error) {
  292 + let err = error as BusinessError;
  293 + promptAction.showToast({ message: `Failed to create the previewOutput instance. error: ${JSON.stringify(err)}` })
  294 + }
  295 +
  296 + if (previewOutput === undefined) {
  297 + return;
  298 + }
  299 +
  300 + try {
  301 + this.captureSession.addOutput(previewOutput);
  302 + } catch (error) {
  303 + let err = error as BusinessError;
  304 + promptAction.showToast({ message: `Failed to add previewOutput. errorCode = ${err.code}` })
  305 + }
  306 +
  307 + try {
  308 + this.captureSession.addOutput(this.videoOutput);
  309 + } catch (error) {
  310 + let err = error as BusinessError;
  311 + promptAction.showToast({ message: `Failed to add videoOutput. errorCode = ${err.code}` })
  312 + }
  313 +
  314 + try {
  315 + await this.captureSession.commitConfig();
  316 + } catch (error) {
  317 + let err = error as BusinessError;
  318 + promptAction.showToast({ message: `Failed to commitConfig. errorCode = ${err.code}` })
  319 + }
  320 +
  321 + try {
  322 + await this.captureSession.start();
  323 + } catch (error) {
  324 + let err = error as BusinessError;
  325 + promptAction.showToast({ message: `Failed to start captureSession. errorCode = ${err.code}` })
  326 + }
  327 +
  328 + this.videoOutput.start((err: BusinessError) => {
  329 + if (err) {
  330 + promptAction.showToast({ message: `Failed to start the video output. error: ${JSON.stringify(err)}` })
  331 + return;
  332 + }
  333 + promptAction.showToast({message: 'Callback invoked to indicate the video output start success.'})
  334 + });
  335 + }
  336 +
  337 + // 开始录像
  338 + async startRecord() {
  339 + if (this.avRecorder) {
  340 + try {
  341 + await this.avRecorder.start();
  342 + } catch (error) {
  343 + let err = error as BusinessError;
  344 + promptAction.showToast({ message: `avRecorder start error: ${JSON.stringify(err)}` })
  345 + }
  346 + }
  347 + }
  348 +
  349 + // 停止录像
  350 + async stopRecord() {
  351 + if (this.avRecorder) {
  352 + try {
  353 + if (this.videoOutput) {
  354 + this.videoOutput.stop((err: BusinessError) => {
  355 + if (err) {
  356 + promptAction.showToast({ message: `Failed to stop the video output. error: ${JSON.stringify(err)}` })
  357 + return;
  358 + }
  359 + promptAction.showToast({message: 'Callback invoked to indicate the video output stop success.'})
  360 + });
  361 + }
  362 + try {
  363 + await this.avRecorder.stop();
  364 + await this.avRecorder.release();
  365 + } catch (error) {
  366 + let err = error as BusinessError;
  367 + promptAction.showToast({ message: `avRecorder stop error: ${JSON.stringify(err)}` })
  368 + }
  369 + } catch (error) {
  370 + let err = error as BusinessError;
  371 + promptAction.showToast({ message: `avRecorder stop error: ${JSON.stringify(err)}` })
  372 + }
  373 + try {
  374 + if (this.captureSession) {
  375 + this.captureSession.stop();
  376 + }
  377 + if (this.cameraInput) {
  378 + this.cameraInput.close();
  379 + }
  380 + if (this.previewOutput) {
  381 + this.previewOutput.release();
  382 + }
  383 + if (this.videoOutput) {
  384 + this.videoOutput.release();
  385 + }
  386 + if (this.captureSession) {
  387 + this.captureSession.release();
  388 + }
  389 + if (this.captureSession) {
  390 + this.captureSession = undefined;
  391 + }
  392 + } catch (error) {
  393 + let err = error as BusinessError;
  394 + promptAction.showToast({ message: `avRecorder stop error: ${JSON.stringify(err)}` })
  395 + }
  396 + }
  397 + }
  398 +}
@@ -3,19 +3,20 @@ import { pasteboard } from '@kit.BasicServicesKit'; @@ -3,19 +3,20 @@ import { pasteboard } from '@kit.BasicServicesKit';
3 import preferencesUtils from '../utils/preferences' 3 import preferencesUtils from '../utils/preferences'
4 import { AxiosResponse } from '@ohos/axios' 4 import { AxiosResponse } from '@ohos/axios'
5 import { HmParseHTML } from "@wuyan/html_parse" 5 import { HmParseHTML } from "@wuyan/html_parse"
6 -import { BusinessError } from '@kit.BasicServicesKit';  
7 -import { uploadFileByTask, selectImgOrVideo } from '../utils/uploadCloud'  
8 import { basePath } from '../utils/baseUrl' 6 import { basePath } from '../utils/baseUrl'
9 import {getCloudDown, beanType} from '../api/user' 7 import {getCloudDown, beanType} from '../api/user'
10 import loadingDialog from '../dialog/LoadingDialog' 8 import loadingDialog from '../dialog/LoadingDialog'
11 import UploadTipDialog from '../dialog/UploadTipDialog' 9 import UploadTipDialog from '../dialog/UploadTipDialog'
12 import { getReportDetail, getMalfunctionList, deleteRecords, successReport, returnSing } from '../api/originalRecords' 10 import { getReportDetail, getMalfunctionList, deleteRecords, successReport, returnSing } from '../api/originalRecords'
13 -import { reportDetailTest, reportDetailData, ProjectList, MalfunctionListTest, MalfunctionListRow, configTest } from '../api/recordsType' 11 +import { reportDetailTest, reportDetailData, ProjectList, ItemList, MalfunctionListTest, MalfunctionListRow, configTest } from '../api/recordsType'
14 import { pushOutsideWeb } from '../utils/pushOutsideWeb' 12 import { pushOutsideWeb } from '../utils/pushOutsideWeb'
15 import ThemeStaticTest from '../components/ThemeStaticText' 13 import ThemeStaticTest from '../components/ThemeStaticText'
16 import NavHeader from '../components/NavHeader' 14 import NavHeader from '../components/NavHeader'
17 15
18 - 16 +interface showVideoOrImg {
  17 + cosKey: string | null;
  18 + itemName: string;
  19 +}
19 interface routerParams { 20 interface routerParams {
20 reportId: number 21 reportId: number
21 } 22 }
@@ -86,6 +87,10 @@ struct DetailRecords { @@ -86,6 +87,10 @@ struct DetailRecords {
86 }) 87 })
87 } 88 }
88 89
  90 + // 判断是否存在视频或图片
  91 + isImgOrVideo = (list: ItemList[]): boolean => {
  92 + return list.some(item => item.cosKey !== null)
  93 + }
89 build() { 94 build() {
90 Column(){ 95 Column(){
91 NavHeader({title: '维保记录详情'}) 96 NavHeader({title: '维保记录详情'})
@@ -235,20 +240,33 @@ struct DetailRecords { @@ -235,20 +240,33 @@ struct DetailRecords {
235 Column({space: 5}){ 240 Column({space: 5}){
236 Row(){ 241 Row(){
237 Row({space: 5}){ 242 Row({space: 5}){
238 - Text((index + 1).toString()).padding(10).borderRadius(5)  
239 - .backgroundColor(Color.Gray).fontSize('#666')  
240 - Text(item.projectName).fontSize(12).fontColor('#999') 243 + Text((index + 1).toString()).width(40).height(40).borderRadius(5)
  244 + .backgroundColor('#eee').fontSize('#666').textAlign(TextAlign.Center)
  245 + Row(){
  246 + Text(item.projectName).fontSize(16).fontColor('#1890ff')
  247 + Image($r('app.media.edit_1')).width(16).margin({right: 4})
  248 + }.onClick(() => {
  249 + router.pushUrl({
  250 + url: 'pages/FireProtectionDetail',
  251 + params: item
  252 + })
  253 + })
241 } 254 }
242 Row(){ 255 Row(){
243 Text('查看视频/图片内容').fontSize(12).fontColor('#1890ff') 256 Text('查看视频/图片内容').fontSize(12).fontColor('#1890ff')
244 Image($r('app.media.right_arrow')).width(12) 257 Image($r('app.media.right_arrow')).width(12)
245 - }.visibility(this.isShowCloudDown && item.cosKey !== null ? Visibility.Visible : Visibility.None) 258 + }.visibility(this.isImgOrVideo(item.itemList) ? Visibility.Visible : Visibility.None)
246 .onClick(() => { 259 .onClick(() => {
  260 + const newArr: showVideoOrImg[] = item.itemList.map((item: ItemList): showVideoOrImg => {
  261 + return {
  262 + cosKey: item.cosKey,
  263 + itemName: item.itemName
  264 + }
  265 + })
247 router.pushUrl({ 266 router.pushUrl({
248 url: 'pages/VideoDetail', 267 url: 'pages/VideoDetail',
249 params: { 268 params: {
250 - cosKey: item.cosKey,  
251 - relateId: item.relateId 269 + cosKeyList: newArr
252 } 270 }
253 }) 271 })
254 }) 272 })
@@ -260,37 +278,6 @@ struct DetailRecords { @@ -260,37 +278,6 @@ struct DetailRecords {
260 .borderWidth(1).fontSize(12).borderRadius(4).lineHeight(16).fontColor(item.state == '0' ? '#ff4949' : '#13ce66') 278 .borderWidth(1).fontSize(12).borderRadius(4).lineHeight(16).fontColor(item.state == '0' ? '#ff4949' : '#13ce66')
261 .backgroundColor(item.state == '0' ? '#ffeded' : '#e7faf0').borderColor(item.state == '0' ? '#ffdbdb' : '#d0f5e0') 279 .backgroundColor(item.state == '0' ? '#ffeded' : '#e7faf0').borderColor(item.state == '0' ? '#ffdbdb' : '#d0f5e0')
262 }.width('100%').justifyContent(FlexAlign.SpaceBetween) 280 }.width('100%').justifyContent(FlexAlign.SpaceBetween)
263 - Row(){  
264 - Row(){  
265 - Image($r('app.media.photo')).width(12).margin({right: 4})  
266 - Text('上传视频/图片').fontSize(12).fontColor('#1890ff')  
267 - }.layoutWeight(1).justifyContent(FlexAlign.Center)  
268 - .visibility(this.isShowCloudDown ? Visibility.Visible : Visibility.None)  
269 - .onClick(async () => {  
270 - // 上传文件到腾讯云存储  
271 - try {  
272 - let key: string = item.cosKey == null ? '' : item.cosKey  
273 - let systemPhotoImagePath: string = await selectImgOrVideo() // 选择文件  
274 - this.loadingController.open()  
275 - await uploadFileByTask(key, item.relateId, systemPhotoImagePath)  
276 - this.loadingController.close()  
277 - } catch (error) {  
278 - let err: BusinessError = error as BusinessError;  
279 - console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);  
280 - }  
281 - })  
282 - Text().width(2).height(16).backgroundColor('#eee').visibility(this.isShowCloudDown ? Visibility.Visible : Visibility.None)  
283 - Row(){  
284 - Image($r('app.media.edit_1')).width(12).margin({right: 4})  
285 - Text('编辑详情').fontSize(12).fontColor('#1890ff').onClick(() => {  
286 - router.pushUrl({  
287 - url: 'pages/FireProtectionDetail',  
288 - params: item  
289 - })  
290 - })  
291 - }.layoutWeight(1).justifyContent(FlexAlign.Center)  
292 - }.width('100%').padding({top: 5}).border({width: {top: 1}, color: '#eee'})  
293 - .visibility(item.state == '0' ? Visibility.Visible : Visibility.None)  
294 }.padding({top: 10, left: 10, right: 10, bottom: 10}).borderWidth(1).borderColor('#eee').shadow({ radius: 5, color: Color.Gray }).borderRadius(5) 281 }.padding({top: 10, left: 10, right: 10, bottom: 10}).borderWidth(1).borderColor('#eee').shadow({ radius: 5, color: Color.Gray }).borderRadius(5)
295 }) 282 })
296 }.backgroundColor('#fff').borderRadius(10).padding(10).width('100%') 283 }.backgroundColor('#fff').borderRadius(10).padding(10).width('100%')
@@ -500,9 +487,6 @@ struct DetailRecords { @@ -500,9 +487,6 @@ struct DetailRecords {
500 487
501 488
502 pageTransition() { 489 pageTransition() {
503 - // 该页面进入动画时长为1000ms,尽量与另一页面的退出动画时长匹配  
504 - PageTransitionEnter({ duration: 500 })  
505 - .slide(SlideEffect.Left).opacity(0)  
506 // 该页面退出动画时长为1200ms,尽量与另一页面的进入动画时长匹配 490 // 该页面退出动画时长为1200ms,尽量与另一页面的进入动画时长匹配
507 PageTransitionExit({ duration: 500 }) 491 PageTransitionExit({ duration: 500 })
508 .translate({ x: 150.0 }) 492 .translate({ x: 150.0 })
1 -import { router } from '@kit.ArkUI' 1 +import { router, promptAction } from '@kit.ArkUI'
2 import { ProjectList } from '../api/recordsType' 2 import { ProjectList } from '../api/recordsType'
3 import { webview } from '@kit.ArkWeb' 3 import { webview } from '@kit.ArkWeb'
4 import { basePath } from '../utils/baseUrl' 4 import { basePath } from '../utils/baseUrl'
  5 +import fs from '@ohos.file.fs';
5 import preferencesUtils from '../utils/preferences' 6 import preferencesUtils from '../utils/preferences'
  7 +import { BusinessError, request } from '@kit.BasicServicesKit';
  8 +import { cameraPickerImg, cameraPickerVideo, videoCompressMethod } from '../utils/uploadFile'
  9 +import { getCosKey, cosKeyTest, cosKeyData, uploadVideoOrImg } from '../api/cosKey'
  10 +import loadingDialog from '../dialog/LoadingDialog'
  11 +import { rpc } from '@kit.IPCKit';
  12 +import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
  13 +import { AxiosResponse } from '@ohos/axios'
  14 +
6 let routerInfo: ProjectList = router.getParams() as ProjectList 15 let routerInfo: ProjectList = router.getParams() as ProjectList
  16 +const context = getContext() as common.UIAbilityContext;
7 17
8 @Entry 18 @Entry
9 @Component 19 @Component
@@ -11,13 +21,161 @@ struct FireProtectionDetail { @@ -11,13 +21,161 @@ struct FireProtectionDetail {
11 controller: RichEditorController = new RichEditorController(); 21 controller: RichEditorController = new RichEditorController();
12 options: RichEditorOptions = { controller: this.controller }; 22 options: RichEditorOptions = { controller: this.controller };
13 @State projectInfo: ProjectList = routerInfo 23 @State projectInfo: ProjectList = routerInfo
  24 + private result: boolean = false;
  25 + permissions: Array<Permissions> = [
  26 + 'ohos.permission.CAMERA',
  27 + 'ohos.permission.MICROPHONE',
  28 + 'ohos.permission.MEDIA_LOCATION'
  29 + ];
14 webviewController: webview.WebviewController = new webview.WebviewController() 30 webviewController: webview.WebviewController = new webview.WebviewController()
  31 +
  32 + aboutToAppear(): void {
  33 + this.reqPermissionsFromUser(this.permissions, context)
  34 + }
  35 + // 加载弹窗
  36 + loadingController: CustomDialogController = new CustomDialogController({
  37 + builder: loadingDialog(),
  38 + customStyle: true,
  39 + offset: { dx: 0, dy: 0 },
  40 + alignment: DialogAlignment.Center,
  41 + autoCancel: false
  42 + })
  43 +
  44 + // 获取用户授权
  45 + reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext) {
  46 + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  47 + atManager.requestPermissionsFromUser(context, permissions).then((data) => {
  48 + let grantStatus: Array<number> = data.authResults;
  49 + let length: number = grantStatus.length;
  50 + for (let i = 0; i < length; i++) {
  51 + if (grantStatus[i] === 0) {
  52 + promptAction.showToast({message: 'User authorized.'})
  53 + } else {
  54 + promptAction.showToast({message: 'User denied authorization.'})
  55 + return;
  56 + }
  57 + }
  58 + }).catch((err: BusinessError) => {
  59 + promptAction.showToast({message: `Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`})
  60 + })
  61 + }
  62 + // 检查用户是否授权
  63 + checkAccessToken(permissions: Array<Permissions>) {
  64 + // Determine the authorization status.
  65 + let callerTokenId: number = rpc.IPCSkeleton.getCallingTokenId();
  66 + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  67 + try {
  68 + for (let i = 0; i < permissions.length; i++) {
  69 + let data: abilityAccessCtrl.GrantStatus = atManager.verifyAccessTokenSync(callerTokenId, permissions[i]);
  70 + if (data === -1) {
  71 + this.result = false;
  72 + } else {
  73 + this.result = true;
  74 + }
  75 + if (!this.result) {
  76 + break;
  77 + }
  78 + }
  79 + } catch (err) {
  80 + promptAction.showToast({message: `checkAccessToken catch err->${JSON.stringify(err)}`})
  81 + }
  82 + }
  83 +
  84 + // 上传方法
  85 + uploadMethods = async (cosKeyStr: string | null, relateId: number, systemPhotoImagePath: string, fileType: string) => {
  86 + let _this = this
  87 + _this.loadingController.open()
  88 + let cacheDir = getContext().cacheDir // 获取缓存目录
  89 + let filetype = fileType // 设置文件类型
  90 + let filename = Date.now() + '.' + filetype // 设置文件名称
  91 + let fullPath = cacheDir + '/' + filename // 设置文件路径
  92 + const file = fs.openSync(systemPhotoImagePath, fs.OpenMode.READ_ONLY) // 打开文件
  93 + fs.copyFileSync(file.fd, fullPath) // 复制文件
  94 + // 获取直传签名等数据
  95 + let directTransferResult: AxiosResponse<cosKeyTest> = await getCosKey(filename);
  96 + let directTransferData: cosKeyData = directTransferResult.data.data
  97 + if (directTransferData == null) {
  98 + promptAction.showToast({ message: 'getStsDirectSign fail' });
  99 + return;
  100 + }
  101 + let cosKey = directTransferData.coskey
  102 + // 生成上传的url
  103 + let url = directTransferData.preSignedUrl
  104 + // 开始上传
  105 + try {
  106 + let uploadTask = await request.uploadFile(getContext(),{ // 上传图片
  107 + url: url, // 请求地址
  108 + // 请求头
  109 + header:{
  110 + "Content-Type": "application/octet-stream"
  111 + },
  112 + method: "PUT",
  113 + files:[{ // 上传文件
  114 + filename: filename, // 文件名
  115 + type: 'jpg', // 文件扩展名
  116 + name:'file', // 接口参数名
  117 + uri:`internal://cache/${filename}` // 缓存目录中的要上传给服务器的图片路径
  118 + }],
  119 + data:[]
  120 + })
  121 + // 上传成功
  122 + uploadTask.on('complete', async (taskStates: Array<request.TaskState>) => {
  123 + _this.loadingController.close()
  124 + for (let i = 0; i < taskStates.length; i++) {
  125 + let cosKeyArr: string[] = []
  126 + if(cosKeyStr !== null) {
  127 + cosKeyArr = cosKeyStr.split(';')
  128 + }
  129 + cosKeyArr.push(cosKey)
  130 + console.log('最终的cosKeys数据: ' + cosKeyArr.join(';'))
  131 + await uploadVideoOrImg({ cosKey: cosKeyArr.join(';'), relateId: relateId })
  132 + await _this.webviewController.runJavaScript(`window.setKey(${JSON.stringify(cosKeyArr.join(';'))})`)
  133 + _this.loadingController.close()
  134 + }
  135 + });
  136 + // 上传失败
  137 + uploadTask.on('fail', (taskStates: Array<request.TaskState>) => {
  138 + _this.loadingController.close()
  139 + for (let i = 0; i < taskStates.length; i++) {
  140 + promptAction.showToast({ message: '上传失败' });
  141 + _this.loadingController.close()
  142 + }
  143 + });
  144 + } catch (error) {
  145 + let err: BusinessError = error as BusinessError;
  146 + console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
  147 + return error
  148 + }
  149 + }
15 build() { 150 build() {
16 Column(){ 151 Column(){
17 Web({ 152 Web({
18 - src: `${basePath}/report/handle?id=${this.projectInfo.reportId}&pid=${this.projectInfo.projectId}&token=${preferencesUtils.get('XF_TOKEN', '')}`, 153 + src: `${basePath}/report/handle?id=${this.projectInfo.reportId}&pid=${this.projectInfo.projectId}&source=harmony&token=${preferencesUtils.get('XF_TOKEN', '')}`,
  154 + // src: 'http://www.xiao-ming.love/',
19 controller: this.webviewController, 155 controller: this.webviewController,
20 }).mixedMode(MixedMode.All).javaScriptAccess(true).domStorageAccess(true) 156 }).mixedMode(MixedMode.All).javaScriptAccess(true).domStorageAccess(true)
  157 + .onConsole((event) => {
  158 + let data = event.message.getMessage().replace(/^['"]|['"]$/g, '');
  159 + let formatData = data.split('&')
  160 + let cosKeyStr: string | null = formatData[1]
  161 + let relateId: number = parseInt(formatData[2])
  162 + if (formatData[0] == '鸿蒙图片上传') {
  163 + // 使用相机拍照
  164 + cameraPickerImg().then(async systemPhotoImagePath => {
  165 + if (systemPhotoImagePath == '' || systemPhotoImagePath == null) {
  166 + return promptAction.showToast({ message: '用户取消选择' });
  167 + }
  168 + this.uploadMethods(cosKeyStr, relateId, systemPhotoImagePath, 'jpg')
  169 + })
  170 + } else if(formatData[0] == '鸿蒙视频上传'){
  171 + // 使用相机录像
  172 + this.checkAccessToken(this.permissions);
  173 + if (this.result) {
  174 + router.pushUrl({ url: 'pages/CreateCamera', params: { cosKeyStr: cosKeyStr, relateId: relateId} })
  175 + }
  176 + }
  177 + return false
  178 + })
21 }.height('100%').width('100%') 179 }.height('100%').width('100%')
22 } 180 }
23 } 181 }
@@ -38,25 +38,26 @@ struct Login { @@ -38,25 +38,26 @@ struct Login {
38 38
39 build() { 39 build() {
40 Column(){ 40 Column(){
41 - Column({space: 20}){  
42 - Image($r('app.media.logo')).width(100).borderRadius(22)  
43 - Text('消防维保助手').fontSize(30).fontWeight(500).fontColor('#fff')  
44 - }.margin({top: 50, bottom: 50})  
45 -  
46 Column(){ 41 Column(){
  42 + Image($r('app.media.logo')).width(100).borderRadius(22).margin({bottom: 20})
  43 + Text('消防维保助手').fontSize(30).fontWeight(500).fontColor('#fff').margin({bottom: 5})
  44 + Text('鸿蒙专版').fontSize(14).fontColor(Color.White).fontWeight(500)
  45 + }.margin({top: 50, bottom: 30})
  46 +
  47 + Column({space:10}){
47 Row(){ 48 Row(){
48 Image($r('app.media.account')).width(20) 49 Image($r('app.media.account')).width(20)
49 TextInput({text: $$this.loginForm.username, placeholder: '请填写用户名'}) 50 TextInput({text: $$this.loginForm.username, placeholder: '请填写用户名'})
50 - .backgroundColor(Color.Transparent).fontColor('#fff').fontSize(14) 51 + .backgroundColor(Color.Transparent).fontColor('#fff').fontSize(16)
51 .borderRadius(0).layoutWeight(1).placeholderColor('#fff') 52 .borderRadius(0).layoutWeight(1).placeholderColor('#fff')
52 }.width('100%').border({width:{bottom: 1}, color: '#eee'}) 53 }.width('100%').border({width:{bottom: 1}, color: '#eee'})
53 Row(){ 54 Row(){
54 Image($r('app.media.password')).width(20) 55 Image($r('app.media.password')).width(20)
55 - TextInput({text: $$this.loginForm.password, placeholder: '请填写密码'}).type(InputType.Password)  
56 - .backgroundColor(Color.Transparent).fontColor('#fff').fontSize(14) 56 + TextInput({text: $$this.loginForm.password, placeholder: '请填写密码'})
  57 + .backgroundColor(Color.Transparent).fontColor('#fff').fontSize(16)
57 .borderRadius(0).layoutWeight(1).placeholderColor('#fff') 58 .borderRadius(0).layoutWeight(1).placeholderColor('#fff')
58 }.width('100%').border({width:{bottom: 1}, color: '#eee'}) 59 }.width('100%').border({width:{bottom: 1}, color: '#eee'})
59 - }.margin({bottom: 10}).padding({left: 10, right: 10}) 60 + }.margin({bottom: 10}).padding({left: 20, right: 20})
60 Column({space: 10}){ 61 Column({space: 10}){
61 Row({space: 5}){ 62 Row({space: 5}){
62 Checkbox({group: 'user' }) 63 Checkbox({group: 'user' })
@@ -112,7 +113,7 @@ struct Login { @@ -112,7 +113,7 @@ struct Login {
112 }) 113 })
113 Text('记住密码').fontSize(12).fontColor('#fff') 114 Text('记住密码').fontSize(12).fontColor('#fff')
114 }.width('100%').justifyContent(FlexAlign.Start) 115 }.width('100%').justifyContent(FlexAlign.Start)
115 - }.alignItems(HorizontalAlign.Start).padding({left: 10, right: 10}) 116 + }.alignItems(HorizontalAlign.Start).padding({left: 20, right: 20})
116 117
117 Button('立即登录') 118 Button('立即登录')
118 .width('80%').height(40) 119 .width('80%').height(40)
@@ -155,7 +156,7 @@ struct Login { @@ -155,7 +156,7 @@ struct Login {
155 preferencesUtil.set('XF_PERSON_ID', personInfo.data.data.personId) 156 preferencesUtil.set('XF_PERSON_ID', personInfo.data.data.personId)
156 preferencesUtil.set('XF_USERNAME', personInfo.data.data.username) 157 preferencesUtil.set('XF_USERNAME', personInfo.data.data.username)
157 } 158 }
158 - router.replaceUrl({ 159 + router.pushUrl({
159 url: 'pages/Index' 160 url: 'pages/Index'
160 }) 161 })
161 }) 162 })
@@ -17,7 +17,8 @@ struct LookRecords { @@ -17,7 +17,8 @@ struct LookRecords {
17 build() { 17 build() {
18 Column(){ 18 Column(){
19 Web({ 19 Web({
20 - src: `${basePath}/report/record?id=${reportId}&token=${preferencesUtils.get('XF_TOKEN', '')}&time=${new Date().getTime()}&pid=${pid}&type=${preferencesUtils.get('XF_ROLE_NAME', '')}&username=${preferencesUtils.get('XF_USERNAME', '')}`, 20 + src: `${basePath}/report/record?id=${reportId}&token=${preferencesUtils.get('XF_TOKEN', '')}
  21 + &time=${new Date().getTime()}&pid=${pid}&type=${preferencesUtils.get('XF_ROLE_NAME', '')}&username=${preferencesUtils.get('XF_USERNAME', '')}`,
21 controller: this.webviewController, 22 controller: this.webviewController,
22 }).mixedMode(MixedMode.All).javaScriptAccess(true).domStorageAccess(true) 23 }).mixedMode(MixedMode.All).javaScriptAccess(true).domStorageAccess(true)
23 }.width('100%').height('100%').backgroundColor('#f2f3f7') 24 }.width('100%').height('100%').backgroundColor('#f2f3f7')
@@ -35,7 +35,12 @@ let getTextInfo = (state: string | null) => { @@ -35,7 +35,12 @@ let getTextInfo = (state: string | null) => {
35 @Component 35 @Component
36 struct PersonList { 36 struct PersonList {
37 dialogController: CustomDialogController = new CustomDialogController({ 37 dialogController: CustomDialogController = new CustomDialogController({
38 - builder: AddPersonDialog(), 38 + builder: AddPersonDialog({
  39 + // 确认添加后的回调
  40 + onChange: () => {
  41 + this.getList()
  42 + }
  43 + }),
39 cornerRadius: 10 44 cornerRadius: 10
40 }) 45 })
41 @State params: QueryParams = { 46 @State params: QueryParams = {
1 import { webview } from '@kit.ArkWeb' 1 import { webview } from '@kit.ArkWeb'
  2 +import NavHeader from '../components/NavHeader'
2 @Entry 3 @Entry
3 @Component 4 @Component
4 struct FireProtectionDetail { 5 struct FireProtectionDetail {
5 webviewController: webview.WebviewController = new webview.WebviewController() 6 webviewController: webview.WebviewController = new webview.WebviewController()
6 build() { 7 build() {
7 Column(){ 8 Column(){
  9 + NavHeader({title: '隐私政策'})
8 Web({ 10 Web({
9 src: `https://doc.crgx.net/privacy.html`, 11 src: `https://doc.crgx.net/privacy.html`,
10 controller: this.webviewController, 12 controller: this.webviewController,
1 -import { CSJSplashAd } from '@csj/openadsdk';  
2 -import { NodeController, window } from '@kit.ArkUI';  
3 -  
4 -@Entry({  
5 - routeName: "GMSplashAdShowPage",  
6 - storage: LocalStorage.getShared()  
7 -})  
8 -@Component  
9 -export struct SplashAdShowPage {  
10 - @LocalStorageLink('GMSplashAd') splashAd: CSJSplashAd | undefined = undefined  
11 - @LocalStorageLink('GMSplashSubWindow') _window: window.Window | undefined = undefined  
12 - @LocalStorageProp('GMSplashCustomCloseBtn') splashCustomCloseBtn: boolean = false  
13 - @State splashAdComponent: NodeController | undefined = undefined  
14 -  
15 - aboutToAppear(): void {  
16 - this.splashAdComponent = this.splashAd?.getAdComponent(this.splashCustomCloseBtn, this._window)  
17 - }  
18 -  
19 - onPageShow(): void {  
20 - this._window?.setWindowLayoutFullScreen(true)  
21 - }  
22 -  
23 - onPageHide(): void {  
24 - this._window?.setWindowLayoutFullScreen(false)  
25 - }  
26 -  
27 - build() {  
28 - Stack() {  
29 - if (this.splashAdComponent) {  
30 - NodeContainer(this.splashAdComponent)  
31 - }  
32 - if (this.splashCustomCloseBtn) {  
33 - Text('close')  
34 - .textAlign(TextAlign.Center)  
35 - .backgroundColor(Color.Yellow)  
36 - .width(60)  
37 - .height(60)  
38 - .borderRadius(30)  
39 - .margin({ left: 50, top: 50 })  
40 - .onClick(() => {  
41 - this.closeBtnClicked()  
42 - })  
43 - }  
44 - }  
45 - .alignContent(Alignment.TopStart)  
46 - }  
47 -  
48 - closeBtnClicked() {  
49 - this._window?.destroyWindow()  
50 - }  
51 -}  
@@ -26,7 +26,7 @@ struct StartAd { @@ -26,7 +26,7 @@ struct StartAd {
26 const result = await CSJAdSdk.start() 26 const result = await CSJAdSdk.start()
27 if (result) { 27 if (result) {
28 if (result.code == 0 || result.code == 4200) { 28 if (result.code == 0 || result.code == 4200) {
29 - router.pushUrl({ url: 'pages/AdMainPage' }) 29 + router.replaceUrl({ url: 'pages/AdMainPage' })
30 // this.loadOpenAd() 30 // this.loadOpenAd()
31 } else { 31 } else {
32 promptAction.showToast({ message: result.msg }); 32 promptAction.showToast({ message: result.msg });
@@ -41,7 +41,13 @@ struct StartAd { @@ -41,7 +41,13 @@ struct StartAd {
41 41
42 build() { 42 build() {
43 Column() { 43 Column() {
44 - Image($r('app.media.logo')).width(60).height(60) 44 + Stack(){
  45 + Progress({ value: 0, total: 10, type: ProgressType.Ring })
  46 + .width(120).color('#409EFF').backgroundColor('#fff')
  47 + .style({ strokeWidth: 10, status: ProgressStatus.LOADING })
  48 +
  49 + Image($r('app.media.logo')).width(60).height(60)
  50 + }
45 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 51 }.width('100%').height('100%').justifyContent(FlexAlign.Center)
46 } 52 }
47 } 53 }
@@ -4,17 +4,25 @@ import { downFile } from '../utils/downFile' @@ -4,17 +4,25 @@ import { downFile } from '../utils/downFile'
4 import { router } from '@kit.ArkUI' 4 import { router } from '@kit.ArkUI'
5 import PhotoBrowser from '../dialog/PhotoBrowserDialog' 5 import PhotoBrowser from '../dialog/PhotoBrowserDialog'
6 import NavHeader from '../components/NavHeader' 6 import NavHeader from '../components/NavHeader'
  7 +interface showVideoOrImg {
  8 + cosKey: string | null;
  9 + itemName: string;
  10 + videoList?: string[];
  11 + imgList?: string[];
  12 +}
  13 +interface VideoOrImgList {
  14 + videoList: string[];
  15 + imgList: string[];
  16 +}
7 interface routerParams { 17 interface routerParams {
8 - cosKey: string  
9 - relateId: number 18 + cosKeyList: showVideoOrImg[]
10 } 19 }
11 let routerQuery = router.getParams() as routerParams 20 let routerQuery = router.getParams() as routerParams
12 @Entry 21 @Entry
13 @Component 22 @Component
14 struct DownLoadImage { 23 struct DownLoadImage {
15 - @State imgList: string[] = []  
16 - @State videoList: string[] = []  
17 - @State cosKeyData: string[] = routerQuery?.cosKey.split(';') 24 + @State cosKeyData: showVideoOrImg[] = routerQuery?.cosKeyList
  25 + @State showCosKeyList: showVideoOrImg[] = []
18 @State viewImg: string[] = [] 26 @State viewImg: string[] = []
19 @State viewVideo: string[] = [] 27 @State viewVideo: string[] = []
20 @State saveButtonOptions: SaveButtonOptions = { 28 @State saveButtonOptions: SaveButtonOptions = {
@@ -28,100 +36,111 @@ struct DownLoadImage { @@ -28,100 +36,111 @@ struct DownLoadImage {
28 offset: { dx: 0, dy: 0 }, 36 offset: { dx: 0, dy: 0 },
29 alignment: DialogAlignment.Top, 37 alignment: DialogAlignment.Top,
30 }) 38 })
31 - aboutToAppear() {  
32 - this.cosKeyData.forEach((item: string) => {  
33 - let newArr = item.split('.')  
34 - let index = newArr.length - 1  
35 - if(newArr[index] == 'mp4') {  
36 - this.videoList.push(item)  
37 - }else {  
38 - this.imgList.push(item) 39 + async aboutToAppear() {
  40 + this.showCosKeyList = await Promise.all(this.cosKeyData.map(async (item: showVideoOrImg) => {
  41 + if (item.cosKey) {
  42 + const arrList: VideoOrImgList | [] = await this.getVideoOrImgUrl(item.cosKey)
  43 + if (!Array.isArray(arrList)) {
  44 + item.videoList = arrList.videoList
  45 + item.imgList = arrList.imgList
  46 + }
  47 + }
  48 + return item
  49 + }))
  50 + }
  51 + // 请求获取图片或视频链接
  52 + getVideoOrImgUrl = async (cosKey: string | null) => {
  53 + let result: VideoOrImgList | [] = this.filterVideoOrImg(cosKey)
  54 + if (!Array.isArray(result)) {
  55 + let videoData: string[] = await Promise.all(result.videoList.map(async (item: string) => {
  56 + const res: AxiosResponse<downloadUrl> = await getDownloadUrl(item, 0)
  57 + return res.data.data
  58 + }))
  59 + let imgData: string[] = await Promise.all(result.imgList.map(async (item: string) => {
  60 + const res: AxiosResponse<downloadUrl> = await getDownloadUrl(item, 0)
  61 + return res.data.data
  62 + }))
  63 + result = {
  64 + videoList: videoData,
  65 + imgList: imgData
  66 + }
  67 + }
  68 + return result
  69 + }
  70 +
  71 + // 筛选图片或者视频
  72 + filterVideoOrImg = (cosKey: string | null): VideoOrImgList | [] => {
  73 + let urlList: string[] | undefined = cosKey?.split(';')
  74 + let videoList: string[] = []
  75 + let imgList: string[] = []
  76 + if(urlList === undefined) return []
  77 + urlList.forEach((item: string) => {
  78 + let newItemList: string[] = item.split('.')
  79 + if(newItemList[newItemList.length - 1] == 'mp4' || newItemList[newItemList.length - 1] == 'mkv'){
  80 + videoList.push(item)
  81 + } else {
  82 + imgList.push(item)
39 } 83 }
40 }) 84 })
41 - this.imgList.forEach(async (item: string) => {  
42 - const imgDta: AxiosResponse<downloadUrl> = await getDownloadUrl(item, 0)  
43 - this.viewImg.push(imgDta.data.data)  
44 - })  
45 - this.videoList.forEach(async (item: string) => {  
46 - const imgDta: AxiosResponse<downloadUrl> = await getDownloadUrl(item, 0)  
47 - this.viewVideo.push(imgDta.data.data)  
48 - }) 85 +
  86 + return {
  87 + videoList,
  88 + imgList
  89 + }
49 } 90 }
  91 +
  92 +
50 build() { 93 build() {
51 Column(){ 94 Column(){
52 - NavHeader({title: '下载图片'}) 95 + NavHeader({title: '视频/图片下载'})
53 List(){ 96 List(){
54 - ListItem(){  
55 - Column({ space: 10 }) {  
56 - Row(){ 97 + ForEach(this.showCosKeyList, (item:showVideoOrImg ) => {
  98 + ListItem(){
  99 + Column({ space: 10 }) {
57 Row({space: 5}){ 100 Row({space: 5}){
58 Text().width(2).height(20).backgroundColor('#1890ff') 101 Text().width(2).height(20).backgroundColor('#1890ff')
59 - Text('图片').fontSize(14).fontWeight(600)  
60 - }  
61 - Text('预览图片')  
62 - .fontSize(14).fontColor('#fff').backgroundColor('#1890ff')  
63 - .padding({left: 15, right: 15, top: 2, bottom: 2})  
64 - .borderRadius(4)  
65 - .onClick(() => {  
66 - this.photoBrowserController.open() 102 + Text(item.itemName).fontSize(14).fontWeight(600)
  103 + }.width('100%')
  104 + GridRow({ columns: 2, gutter: 10 }) {
  105 + ForEach(item.imgList, (childrenImg: string) => {
  106 + GridCol() {
  107 + Column({space: 10}){
  108 + Image(childrenImg)
  109 + .width('100%')
  110 + .height(150)
  111 + .borderRadius(4)
  112 + Row(){
  113 + SaveButton(this.saveButtonOptions)
  114 + // 创建安全控件按钮
  115 + .onClick(async (event, result: SaveButtonOnClickResult) => {
  116 + if (result == SaveButtonOnClickResult.SUCCESS) {
  117 + downFile(childrenImg, 'jpg')
  118 + }
  119 + })
  120 + }
  121 + }
  122 + }
67 }) 123 })
68 - }  
69 - .justifyContent(FlexAlign.SpaceBetween)  
70 - .width('100%')  
71 - GridRow({ columns: 2, gutter: 10 }) {  
72 - ForEach(this.viewImg, (item: string) => {  
73 - GridCol() {  
74 - Column({space: 10}){  
75 - Image(item)  
76 - .width('100%')  
77 - .height(150)  
78 - .borderRadius(4)  
79 - Row(){ 124 + }
  125 + GridRow({ columns: 1}) {
  126 + ForEach(item.videoList, (childrenVideo: string) => {
  127 + GridCol() {
  128 + Column({ space: 10}){
  129 + Video({ src: childrenVideo }).width('100%').height(300)
80 SaveButton(this.saveButtonOptions) 130 SaveButton(this.saveButtonOptions)
81 // 创建安全控件按钮 131 // 创建安全控件按钮
82 .onClick(async (event, result: SaveButtonOnClickResult) => { 132 .onClick(async (event, result: SaveButtonOnClickResult) => {
83 if (result == SaveButtonOnClickResult.SUCCESS) { 133 if (result == SaveButtonOnClickResult.SUCCESS) {
84 - downFile(item, 'jpg') 134 + downFile(childrenVideo, 'mp4')
85 } 135 }
86 }) 136 })
87 } 137 }
88 - }  
89 - }  
90 - })  
91 - }  
92 - }  
93 - }.visibility(this.viewImg.length == 0 ? Visibility.None : Visibility.Visible)  
94 - ListItem(){  
95 - Column({ space: 10 }) {  
96 - Row(){  
97 - Row({space: 5}){  
98 - Text().width(2).height(20).backgroundColor('#1890ff')  
99 - Text('视频').fontSize(14).fontWeight(600)  
100 - }  
101 - }  
102 - .justifyContent(FlexAlign.Start)  
103 - .width('100%')  
104 - GridRow({ columns: 1}) {  
105 - ForEach(this.viewVideo, (item: string) => {  
106 - GridCol() {  
107 - Column({ space: 10}){  
108 - Video({ src: item }).width('100%').height(300).controls(true)  
109 - SaveButton(this.saveButtonOptions)  
110 - // 创建安全控件按钮  
111 - .onClick(async (event, result: SaveButtonOnClickResult) => {  
112 - if (result == SaveButtonOnClickResult.SUCCESS) {  
113 - downFile(item, 'mp4')  
114 - }  
115 - })  
116 - }  
117 - }  
118 - .margin({  
119 - top: 10 138 + }.margin({ top: 10 })
120 }) 139 })
121 - }) 140 + }
122 } 141 }
123 } 142 }
124 - }.visibility(this.viewVideo.length == 0 ? Visibility.None : Visibility.Visible) 143 + })
125 }.padding(10) 144 }.padding(10)
126 } 145 }
127 } 146 }
  1 +import { media } from '@kit.MediaKit';
  2 +import { BusinessError } from '@kit.BasicServicesKit';
  3 +import { fileIo as fs, fileUri } from '@kit.CoreFileKit';
  4 +import { photoAccessHelper } from '@kit.MediaLibraryKit';
  5 +
  6 +
  7 +
  8 +const TAG = 'VideoRecorderDemo:';
  9 +export class VideoRecorderDemo {
  10 + private context: Context;
  11 + constructor() {
  12 + this.context = getContext(this);
  13 + }
  14 + private avRecorder: media.AVRecorder | undefined = undefined;
  15 + private videoOutSurfaceId: string = "";
  16 + private avProfile: media.AVRecorderProfile = {
  17 + fileFormat : media.ContainerFormatType.CFT_MPEG_4, // 视频文件封装格式,只支持MP4
  18 + videoBitrate : 100000, // 视频比特率
  19 + videoCodec : media.CodecMimeType.VIDEO_AVC, // 视频文件编码格式,支持avc格式
  20 + videoFrameWidth : 640, // 视频分辨率的宽
  21 + videoFrameHeight : 480, // 视频分辨率的高
  22 + videoFrameRate : 30 // 视频帧率
  23 + };
  24 + private avConfig: media.AVRecorderConfig = {
  25 + videoSourceType : media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, // 视频源类型,支持YUV和ES两种格式
  26 + profile : this.avProfile,
  27 + url : 'fd://35', // 参考应用文件访问与管理开发示例新建并读写一个文件
  28 + rotation : 0 // 视频旋转角度,默认为0不旋转,支持的值为0、90、180、270
  29 + };
  30 +
  31 + private uriPath: string = ''; // 文件uri,可用于安全控件保存媒体资源
  32 + private filePath: string = ''; // 文件路径
  33 + private fileFd: number = 0;
  34 +
  35 + // 创建文件以及设置avConfig.url
  36 + async createAndSetFd() {
  37 + const path: string = this.context.filesDir + '/example.mp4'; // 文件沙箱路径,文件后缀名应与封装格式对应
  38 + const videoFile: fs.File = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  39 + this.avConfig.url = 'fd://' + videoFile.fd; // 设置url
  40 + this.fileFd = videoFile.fd; // 文件fd
  41 + this.filePath = path;
  42 + }
  43 +
  44 + // 注册avRecorder回调函数
  45 + setAvRecorderCallback() {
  46 + if (this.avRecorder != undefined) {
  47 + // 状态机变化回调函数
  48 + this.avRecorder.on('stateChange', (state: media.AVRecorderState, reason: media.StateChangeReason) => {
  49 + console.info(TAG + 'current state is: ' + state);
  50 + })
  51 + // 错误上报回调函数
  52 + this.avRecorder.on('error', (err: BusinessError) => {
  53 + console.error(TAG + 'error ocConstantSourceNode, error message is ' + err);
  54 + })
  55 + }
  56 + }
  57 +
  58 + // 相机相关准备工作
  59 + async prepareCamera() {
  60 + // 具体实现查看相机资料
  61 + }
  62 +
  63 + // 启动相机出流
  64 + async startCameraOutput() {
  65 + // 调用VideoOutput的start接口开始录像输出
  66 + }
  67 +
  68 + // 停止相机出流
  69 + async stopCameraOutput() {
  70 + // 调用VideoOutput的stop接口停止录像输出
  71 + }
  72 +
  73 + // 释放相机实例
  74 + async releaseCamera() {
  75 + // 释放相机准备阶段创建出的实例
  76 + }
  77 +
  78 + // 开始录制对应的流程
  79 + async startRecordingProcess() {
  80 + if (this.avRecorder === undefined) {
  81 + // 1.创建录制实例;
  82 + this.avRecorder = await media.createAVRecorder();
  83 + this.setAvRecorderCallback();
  84 + }
  85 + // 2. 获取录制文件fd;获取到的值传递给avConfig里的url,实现略
  86 + // 3.配置录制参数完成准备工作
  87 + await this.avRecorder.prepare(this.avConfig);
  88 + this.videoOutSurfaceId = await this.avRecorder.getInputSurface();
  89 + // 4.完成相机相关准备工作
  90 + await this.prepareCamera();
  91 + // 5.启动相机出流
  92 + await this.startCameraOutput();
  93 + // 6. 启动录制
  94 + await this.avRecorder.start();
  95 +
  96 + }
  97 +
  98 + // 暂停录制对应的流程
  99 + async pauseRecordingProcess() {
  100 + if (this.avRecorder != undefined && this.avRecorder.state === 'started') { // 仅在started状态下调用pause为合理状态切换
  101 + await this.avRecorder.pause();
  102 + await this.stopCameraOutput(); // 停止相机出流
  103 + }
  104 + }
  105 +
  106 + // 恢复录制对应的流程
  107 + async resumeRecordingProcess() {
  108 + if (this.avRecorder != undefined && this.avRecorder.state === 'paused') { // 仅在paused状态下调用resume为合理状态切换
  109 + await this.startCameraOutput(); // 启动相机出流
  110 + await this.avRecorder.resume();
  111 + }
  112 + }
  113 +
  114 + async stopRecordingProcess() {
  115 + if (this.avRecorder != undefined) {
  116 + // 1. 停止录制
  117 + if (this.avRecorder.state === 'started'
  118 + || this.avRecorder.state === 'paused' ) { // 仅在started或者paused状态下调用stop为合理状态切换
  119 + await this.avRecorder.stop();
  120 + await this.stopCameraOutput();
  121 + }
  122 + // 2.重置
  123 + await this.avRecorder.reset();
  124 + // 3.释放录制实例
  125 + await this.avRecorder.release();
  126 + // 4.文件录制完成后,关闭fd,实现略
  127 + await fs.close(this.fileFd);
  128 + // 5.释放相机相关实例
  129 + await this.releaseCamera();
  130 + }
  131 + }
  132 +
  133 + // 安全控件保存媒体资源至图库
  134 + async saveRecorderAsset() {
  135 + let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
  136 + // 需要确保uriPath对应的资源存在
  137 + this.uriPath = fileUri.getUriFromPath(this.filePath); // 获取录制文件的uri,用于安全控件保存至图库
  138 + let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
  139 + photoAccessHelper.MediaAssetChangeRequest.createVideoAssetRequest(this.context, this.uriPath);
  140 + await phAccessHelper.applyChanges(assetChangeRequest);
  141 + }
  142 +
  143 + // 一个完整的【开始录制-暂停录制-恢复录制-停止录制】示例
  144 + async videoRecorderDemo() {
  145 + await this.startRecordingProcess(); // 开始录制
  146 + // 用户此处可以自行设置录制时长,例如通过设置休眠阻止代码执行
  147 + await this.pauseRecordingProcess(); //暂停录制
  148 + await this.resumeRecordingProcess(); // 恢复录制
  149 + await this.stopRecordingProcess(); // 停止录制
  150 + // 安全控件保存媒体资源至图库
  151 + await this.saveRecorderAsset();
  152 + }
  153 +}
  1 +import fs from '@ohos.file.fs';
  2 +import buffer from '@ohos.buffer';
  3 +
  4 +
  5 +// 大小和单位
  6 +const GB_MAGNITUDE: number = 1024 * 1024 * 1024
  7 +const MB_MAGNITUDE: number = 1024 * 1024
  8 +const KB_MAGNITUDE: number = 1024
  9 +const GB_SYMBOL: string = 'GB'
  10 +const MB_SYMBOL: string = 'MB'
  11 +const KB_SYMBOL: string = 'KB'
  12 +const BYTE_SYMBOL: string = 'B'
  13 +
  14 +export class FileUtil {
  15 +
  16 +
  17 + /**
  18 + * 新建并打开文件
  19 + */
  20 + static createOrOpen(path: string) : fs.File{
  21 + let isExist = fs.accessSync(path);
  22 + let file: fs.File;
  23 + if(isExist) {
  24 + file = fs.openSync(path, fs.OpenMode.READ_WRITE);
  25 + }else {
  26 + file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
  27 + }
  28 + return file;
  29 + }
  30 +
  31 +
  32 + /**
  33 + * 保存arrayBuffer到文件
  34 + * @param path
  35 + * @param arrayBuffer
  36 + * @returns
  37 + */
  38 + static writeBufferToFile(path: string, arrayBuffer: ArrayBuffer): number {
  39 +
  40 + try {
  41 + let file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  42 + let value = fs.writeSync(file.fd, arrayBuffer);
  43 + fs.closeSync(file);
  44 + return value;
  45 + }catch (err){
  46 + console.log("FileUtil", "writeFile err:" + err);
  47 + return -1;
  48 + }
  49 + }
  50 +
  51 +
  52 + /**
  53 + * 保存文本到文件
  54 + * @param path
  55 + * @param text
  56 + * @returns
  57 + */
  58 + static writeStrToFile(path: string, text: string): number {
  59 + try {
  60 + let file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  61 + let value = fs.writeSync(file.fd, text);
  62 + fs.closeSync(file);
  63 + return value;
  64 + }catch (err) {
  65 + console.log("FileUtil", "writeFile err:" + err);
  66 + return -1;
  67 + }
  68 + }
  69 +}
  70 +
  71 +
  72 +export class CommonConstants {
  73 + /**
  74 + * ShowToast duration.
  75 + */
  76 + static readonly SHOW_TOAST_DURATION: number = 4000;
  77 +
  78 + /**
  79 + * ShowToast bottom.
  80 + */
  81 + static readonly SHOW_TOAST_BOTTOM: number = 108;
  82 +
  83 + /**
  84 + * Image size.
  85 + */
  86 + static readonly IMAGE_SIZE: number = 200;
  87 +
  88 + /**
  89 + * The full percentage of component.
  90 + */
  91 + static readonly FULL_PERCENT: string = '100%';
  92 +
  93 + /**
  94 + * The ninety percent of the components.
  95 + */
  96 + static readonly NINETY_PERCENT: string = '90%';
  97 +
  98 + /**
  99 + * The seventy percent of the components.
  100 + */
  101 + static readonly SEVENTY_PERCENT: string = '70%';
  102 +
  103 + /**
  104 + * The fifteen percent of the bottom of the margin.
  105 + */
  106 + static readonly FIFTEEN_PERCENT: string = '15%';
  107 +}
1 -// export default 'https://xfwbzshd.crgx.net'  
2 -// 后端域名  
3 -export default 'https://xfappht.crgx.net' 1 +export default 'https://xfwbzshd.crgx.net'
  2 +// 正式后端域名
  3 +// export default 'https://xfappht.crgx.net'
4 // 前端地址 4 // 前端地址
5 export const basePath = 'https://xfwbzs.crgx.net' 5 export const basePath = 'https://xfwbzs.crgx.net'
@@ -4,7 +4,7 @@ import { promptAction } from '@kit.ArkUI' @@ -4,7 +4,7 @@ import { promptAction } from '@kit.ArkUI'
4 import { photoAccessHelper } from '@kit.MediaLibraryKit' 4 import { photoAccessHelper } from '@kit.MediaLibraryKit'
5 import { getCosKey, cosKeyTest, cosKeyData, uploadVideoOrImg } from '../api/cosKey' 5 import { getCosKey, cosKeyTest, cosKeyData, uploadVideoOrImg } from '../api/cosKey'
6 import { AxiosResponse } from '@ohos/axios' 6 import { AxiosResponse } from '@ohos/axios'
7 - 7 +import { camera, cameraPicker } from '@kit.CameraKit'
8 8
9 // 选择一张图片或视频 9 // 选择一张图片或视频
10 export async function selectImgOrVideo(){ 10 export async function selectImgOrVideo(){
@@ -18,6 +18,20 @@ export async function selectImgOrVideo(){ @@ -18,6 +18,20 @@ export async function selectImgOrVideo(){
18 return photos.photoUris[0] // 返回文件路径 18 return photos.photoUris[0] // 返回文件路径
19 } 19 }
20 20
  21 +// 相机拍照
  22 +export async function selectImgByCamera(){
  23 + try {
  24 + let pickerProfile: cameraPicker.PickerProfile = {
  25 + cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK // 设置相机位置
  26 + }
  27 + let pickerResult: cameraPicker.PickerResult = await cameraPicker.pick(getContext(),
  28 + [cameraPicker.PickerMediaType.PHOTO, cameraPicker.PickerMediaType.PHOTO], pickerProfile);
  29 + } catch (error) {
  30 + let err = error as BusinessError;
  31 + console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`)
  32 + }
  33 +}
  34 +
21 /** 35 /**
22 * 上传文件(通过uploadTask实现) 36 * 上传文件(通过uploadTask实现)
23 * @param context context 37 * @param context context
1 import { photoAccessHelper } from '@kit.MediaLibraryKit' 1 import { photoAccessHelper } from '@kit.MediaLibraryKit'
2 import fs from '@ohos.file.fs'; 2 import fs from '@ohos.file.fs';
3 import { request, BusinessError } from '@kit.BasicServicesKit'; 3 import { request, BusinessError } from '@kit.BasicServicesKit';
  4 +import { cameraPicker as picker, camera } from '@kit.CameraKit';
  5 +import { common } from '@kit.AbilityKit';
4 import preferencesUtils from '../utils/preferences' 6 import preferencesUtils from '../utils/preferences'
  7 +import { promptAction } from '@kit.ArkUI'
  8 +import { fileIo, fileUri } from '@kit.CoreFileKit'
  9 +import { VideoCompressor, CompressQuality, CompressorResponseCode } from '@ohos/videocompressor'
  10 +
  11 +let mContext = getContext(this) as common.Context;
5 export interface uploadResult { 12 export interface uploadResult {
6 code?: number 13 code?: number
7 msg?: string 14 msg?: string
@@ -23,6 +30,67 @@ export async function selectImg(){ @@ -23,6 +30,67 @@ export async function selectImg(){
23 return photos.photoUris[0] // 返回图片路径 30 return photos.photoUris[0] // 返回图片路径
24 } 31 }
25 32
  33 +// 使用相机拍照
  34 +export async function cameraPickerImg() {
  35 + let pathDir = getContext().cacheDir;
  36 + let fileName = `${new Date().getTime()}`
  37 + let filePath = pathDir + `/${fileName}.tmp`
  38 + fileIo.createRandomAccessFileSync(filePath, fileIo.OpenMode.CREATE);
  39 + let uri = fileUri.getUriFromPath(filePath);
  40 + let pickerProfile: picker.PickerProfile = {
  41 + cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,
  42 + saveUri: uri
  43 + };
  44 + try {
  45 + let result: picker.PickerResult = await picker.pick(getContext(), [picker.PickerMediaType.PHOTO], pickerProfile);
  46 + console.info(`picker resultCode: ${result.resultCode},resultUri: ${result.resultUri},mediaType: ${result.mediaType}`);
  47 + return result.resultUri
  48 + } catch (error) {
  49 + let err = error as BusinessError;
  50 + console.error(`the pick call failed. error code: ${err.code}`);
  51 + return ''
  52 + }
  53 +}
  54 +
  55 +// 使用相机录像
  56 +export async function cameraPickerVideo() {
  57 + let pathDir = getContext().cacheDir;
  58 + let fileName = `${new Date().getTime()}`
  59 + let filePath = pathDir + `/${fileName}.tmp`
  60 + fileIo.createRandomAccessFileSync(filePath, fileIo.OpenMode.CREATE);
  61 + let uri = fileUri.getUriFromPath(filePath);
  62 + let pickerProfile: picker.PickerProfile = {
  63 + cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,
  64 + saveUri: uri,
  65 + videoDuration: 120
  66 + };
  67 + try {
  68 + let result: picker.PickerResult = await picker.pick(getContext(), [picker.PickerMediaType.VIDEO], pickerProfile);
  69 + console.info(`picker resultCode: ${result.resultCode},resultUri: ${result.resultUri},mediaType: ${result.mediaType}`);
  70 + return result.resultUri
  71 + } catch (error) {
  72 + let err = error as BusinessError;
  73 + console.error(`the pick call failed. error code: ${err.code}`);
  74 + return ''
  75 + }
  76 +}
  77 +
  78 +
  79 +// 视频解码压缩
  80 +export async function videoCompressMethod(selectFilePath: string) {
  81 + let videoCompressor = new VideoCompressor();
  82 + const data = await videoCompressor.compressVideo(getContext(), selectFilePath, CompressQuality.COMPRESS_QUALITY_LOW) // 分别对应3个压缩质量 COMPRESS_QUALITY_HIGH,COMPRESS_QUALITY_MEDIUM, COMPRESS_QUALITY_LOW
  83 + if (data.code == CompressorResponseCode.SUCCESS) {
  84 + //outputPath: 压缩后的文件地址
  85 + console.log("videoCompressor HIGH message:" + data.message + "--outputPath:" + data.outputPath);
  86 + return data.outputPath
  87 + } else {
  88 + console.log("videoCompressor HIGH code:" + data.code + "--error message:" + data.message);
  89 + return ''
  90 + }
  91 +}
  92 +
  93 +
26 94
27 // 拷贝图片路径到缓存 95 // 拷贝图片路径到缓存
28 export async function copyCachePath(systemPhotoImagePath: string) { 96 export async function copyCachePath(systemPhotoImagePath: string) {
@@ -42,6 +110,8 @@ export async function uploadFile() { @@ -42,6 +110,8 @@ export async function uploadFile() {
42 // 1. 完成图片上传并获得上传对象 110 // 1. 完成图片上传并获得上传对象
43 try { 111 try {
44 let systemPhotoImagePath = await selectImg() // 选择图片 112 let systemPhotoImagePath = await selectImg() // 选择图片
  113 + // let systemPhotoImagePath = await cameraPickerImg() // 使用相机拍照
  114 + if(systemPhotoImagePath == '') return promptAction.showToast({message: '未选择图片'})
45 const fileData: string[] = await copyCachePath(systemPhotoImagePath) 115 const fileData: string[] = await copyCachePath(systemPhotoImagePath)
46 let uploader = await request.uploadFile(getContext(),{ // 上传图片 116 let uploader = await request.uploadFile(getContext(),{ // 上传图片
47 url:'http://xfwbzshd.crgx.net/common/upload', // 请求地址 117 url:'http://xfwbzshd.crgx.net/common/upload', // 请求地址
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 }, 8 },
9 { 9 {
10 "name": "ohos.permission.READ_MEDIA", 10 "name": "ohos.permission.READ_MEDIA",
11 - "reason": "$string:EntryAbility1_desc", 11 + "reason": "$string:EntryAbilityReadFile_desc",
12 "usedScene": { 12 "usedScene": {
13 "abilities": [ 13 "abilities": [
14 "EntryAbility" 14 "EntryAbility"
@@ -18,13 +18,43 @@ @@ -18,13 +18,43 @@
18 }, 18 },
19 { 19 {
20 "name": "ohos.permission.WRITE_MEDIA", 20 "name": "ohos.permission.WRITE_MEDIA",
21 - "reason": "$string:EntryAbility1_desc", 21 + "reason": "$string:EntryAbilityReadFile_desc",
22 "usedScene": { 22 "usedScene": {
23 "abilities": [ 23 "abilities": [
24 "EntryAbility" 24 "EntryAbility"
25 ], 25 ],
26 "when": "inuse" 26 "when": "inuse"
27 } 27 }
  28 + },
  29 + {
  30 + "name": "ohos.permission.MICROPHONE",
  31 + "reason": "$string:EntryAbilityMicroPhone_desc",
  32 + "usedScene": {
  33 + "abilities": [
  34 + "EntryAbility"
  35 + ],
  36 + "when": "inuse"
  37 + }
  38 + },
  39 + {
  40 + "name": "ohos.permission.MEDIA_LOCATION",
  41 + "reason": "$string:EntryAbilityMedia_desc",
  42 + "usedScene": {
  43 + "abilities": [
  44 + "FormAbility"
  45 + ],
  46 + "when":"always"
  47 + }
  48 + },
  49 + {
  50 + "name": "ohos.permission.CAMERA",
  51 + "reason": "$string:EntryAbilityCamera_desc",
  52 + "usedScene": {
  53 + "abilities": [
  54 + "EntryAbility"
  55 + ],
  56 + "when": "always"
  57 + }
28 } 58 }
29 ], 59 ],
30 "description": "$string:module_desc", 60 "description": "$string:module_desc",
@@ -9,18 +9,30 @@ @@ -9,18 +9,30 @@
9 "value": "description" 9 "value": "description"
10 }, 10 },
11 { 11 {
12 - "name": "EntryAbility1_desc", 12 + "name": "EntryAbilityReadFile_desc",
13 "value": "读取文件" 13 "value": "读取文件"
14 }, 14 },
15 { 15 {
16 - "name": "EntryAbility2_desc", 16 + "name": "EntryAbilityReadPhoto_desc",
17 "value": "读取相册" 17 "value": "读取相册"
18 }, 18 },
19 { 19 {
20 - "name": "EntryAbility3_desc", 20 + "name": "EntryAbilitySharePlate_desc",
21 "value": "读取剪切板" 21 "value": "读取剪切板"
22 }, 22 },
23 { 23 {
  24 + "name": "EntryAbilityMedia_desc",
  25 + "value": "访问你的地理位置"
  26 + },
  27 + {
  28 + "name": "EntryAbilityCamera_desc",
  29 + "value": "使用相机拍照录像"
  30 + },
  31 + {
  32 + "name": "EntryAbilityMicroPhone_desc",
  33 + "value": "使用你的麦克风录音"
  34 + },
  35 + {
24 "name": "EntryAbility_label", 36 "name": "EntryAbility_label",
25 "value": "消防维保助手" 37 "value": "消防维保助手"
26 }, 38 },
  1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1742787599186" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10839" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3-7.7 16.2-7.7 35.2 0 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766z" p-id="10840" fill="#ffffff"></path><path d="M508 336c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176z m0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z" p-id="10841" fill="#ffffff"></path></svg>
  1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1742787609097" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11050" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M955.733333 492.8c-6.4-12.8-162.133333-317.866667-443.733333-317.866667-23.466667 0-46.933333 2.133333-70.4 6.4-17.066667 4.266667-29.866667 19.2-25.6 36.266667 4.266667 17.066667 19.2 29.866667 36.266667 25.6 19.2-4.266667 38.4-4.266667 57.6-4.266667 209.066667 0 345.6 209.066667 379.733333 266.666667-10.666667 19.2-32 53.333333-64 91.733333-10.666667 12.8-8.533333 34.133333 4.266667 44.8 6.4 4.266667 12.8 6.4 21.333333 6.4s19.2-4.266667 25.6-10.666666c51.2-61.866667 78.933333-115.2 78.933333-117.333334 6.4-8.533333 6.4-19.2 0-27.733333zM215.466667 125.866667c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l91.733333 91.733333C138.666667 354.133333 72.533333 484.266667 68.266667 490.666667c-4.266667 8.533333-4.266667 19.2 0 29.866666 6.4 12.8 162.133333 315.733333 443.733333 315.733334 83.2 0 164.266667-27.733333 241.066667-81.066667l96 96c6.4 6.4 14.933333 8.533333 23.466666 8.533333s17.066667-2.133333 23.466667-8.533333c12.8-12.8 12.8-32 0-44.8L215.466667 125.866667z m243.2 334.933333l104.533333 104.533333c-12.8 12.8-32 21.333333-51.2 21.333334-40.533333 0-74.666667-34.133333-74.666667-74.666667 0-19.2 8.533333-38.4 21.333334-51.2zM512 772.266667c-209.066667 0-345.6-209.066667-379.733333-266.666667 21.333333-36.266667 81.066667-130.133333 174.933333-196.266667l104.533333 104.533334c-25.6 25.6-38.4 59.733333-38.4 96 0 76.8 61.866667 138.666667 138.666667 138.666666 36.266667 0 70.4-14.933333 96-38.4l98.133333 98.133334c-61.866667 42.666667-128 64-194.133333 64z" fill="#ffffff" p-id="11051"></path></svg>
@@ -31,6 +31,6 @@ @@ -31,6 +31,6 @@
31 "pages/ViewFile", 31 "pages/ViewFile",
32 "pages/StartAd", 32 "pages/StartAd",
33 "pages/AdMainPage", 33 "pages/AdMainPage",
34 - "pages/SplashAdShowPage" 34 + "pages/CreateCamera"
35 ] 35 ]
36 } 36 }
@@ -9,8 +9,28 @@ @@ -9,8 +9,28 @@
9 "value": "description" 9 "value": "description"
10 }, 10 },
11 { 11 {
  12 + "name": "EntryAbilityReadFile_desc",
  13 + "value": "readFile"
  14 + },
  15 + {
  16 + "name": "EntryAbilityReadPhoto_desc",
  17 + "value": "readPhoto"
  18 + },
  19 + {
  20 + "name": "EntryAbilitySharePlate_desc",
  21 + "value": "readSharePlate"
  22 + },
  23 + {
  24 + "name": "EntryAbilityCamera_desc",
  25 + "value": "Use your camera for video recording"
  26 + },
  27 + {
12 "name": "EntryAbility_label", 28 "name": "EntryAbility_label",
13 - "value": "消防维保助手" 29 + "value": "Fire maintenance assistant"
  30 + },
  31 + {
  32 + "name": "video",
  33 + "value": "Fire maintenance assistant for advertising"
14 } 34 }
15 ] 35 ]
16 } 36 }
@@ -9,20 +9,28 @@ @@ -9,20 +9,28 @@
9 "value": "description" 9 "value": "description"
10 }, 10 },
11 { 11 {
12 - "name": "EntryAbility_label",  
13 - "value": "消防维保助手"  
14 - },  
15 - {  
16 - "name": "EntryAbility1_desc", 12 + "name": "EntryAbilityReadFile_desc",
17 "value": "读取文件" 13 "value": "读取文件"
18 }, 14 },
19 { 15 {
20 - "name": "EntryAbility2_desc", 16 + "name": "EntryAbilityReadPhoto_desc",
21 "value": "读取相册" 17 "value": "读取相册"
22 }, 18 },
23 { 19 {
24 - "name": "EntryAbility3_desc", 20 + "name": "EntryAbilitySharePlate_desc",
25 "value": "读取剪切板" 21 "value": "读取剪切板"
  22 + },
  23 + {
  24 + "name": "EntryAbilityCamera_desc",
  25 + "value": "使用相机拍照录像"
  26 + },
  27 + {
  28 + "name": "EntryAbility_label",
  29 + "value": "消防维保助手"
  30 + },
  31 + {
  32 + "name": "video",
  33 + "value": "消防维保助手广告"
26 } 34 }
27 ] 35 ]
28 } 36 }
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0", 19 "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0",
20 "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19", 20 "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19",
21 "@ohos/lottie@^2.0.9": "@ohos/lottie@2.0.16", 21 "@ohos/lottie@^2.0.9": "@ohos/lottie@2.0.16",
  22 + "@ohos/videocompressor@^1.0.4": "@ohos/videocompressor@1.0.4",
22 "@wuyan/html_parse@^1.0.7": "@wuyan/html_parse@1.0.7", 23 "@wuyan/html_parse@^1.0.7": "@wuyan/html_parse@1.0.7",
23 "ksadsdk@entry/libs/KSAdSDK.har": "ksadsdk@entry/libs/KSAdSDK.har", 24 "ksadsdk@entry/libs/KSAdSDK.har": "ksadsdk@entry/libs/KSAdSDK.har",
24 "libapplogrs.so@oh_modules/.ohpm/@dp+applog@+e6gtiy4h3epopwj9muptqswanljuqcdrohmi055hmq=/oh_modules/@dp/applog/src/main/ets/types/libapplogrs": "libapplogrs.so@oh_modules/.ohpm/@dp+applog@+e6gtiy4h3epopwj9muptqswanljuqcdrohmi055hmq=/oh_modules/@dp/applog/src/main/ets/types/libapplogrs", 25 "libapplogrs.so@oh_modules/.ohpm/@dp+applog@+e6gtiy4h3epopwj9muptqswanljuqcdrohmi055hmq=/oh_modules/@dp/applog/src/main/ets/types/libapplogrs": "libapplogrs.so@oh_modules/.ohpm/@dp+applog@+e6gtiy4h3epopwj9muptqswanljuqcdrohmi055hmq=/oh_modules/@dp/applog/src/main/ets/types/libapplogrs",
@@ -139,6 +140,13 @@ @@ -139,6 +140,13 @@
139 "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/lottie/-/lottie-2.0.16.har", 140 "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/lottie/-/lottie-2.0.16.har",
140 "registryType": "ohpm" 141 "registryType": "ohpm"
141 }, 142 },
  143 + "@ohos/videocompressor@1.0.4": {
  144 + "name": "@ohos/videocompressor",
  145 + "version": "1.0.4",
  146 + "integrity": "sha512-NGQOgKK81plBt7sfe314sp0U1B+r4eheYnsNI4CWG4PkJU6o4rU4lEGVDQ/wF/42RGYkwNw8A+tr26qXoX3kGQ==",
  147 + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/videocompressor/-/videocompressor-1.0.4.har",
  148 + "registryType": "ohpm"
  149 + },
142 "@wuyan/html_parse@1.0.7": { 150 "@wuyan/html_parse@1.0.7": {
143 "name": "@wuyan/html_parse", 151 "name": "@wuyan/html_parse",
144 "version": "1.0.7", 152 "version": "1.0.7",
@@ -9,7 +9,8 @@ @@ -9,7 +9,8 @@
9 "@csj/adapter_gdt": "^1.0.0-2", 9 "@csj/adapter_gdt": "^1.0.0-2",
10 "@csj/adapter_ks": "^2.0.3-beta-2", 10 "@csj/adapter_ks": "^2.0.3-beta-2",
11 "ksadsdk": "file:./entry/libs/KSAdSDK.har", 11 "ksadsdk": "file:./entry/libs/KSAdSDK.har",
12 - "@gdt/gdt-union-sdk": "file:./entry/libs/GDTUnionSDK-default-release.har" 12 + "@gdt/gdt-union-sdk": "file:./entry/libs/GDTUnionSDK-default-release.har",
  13 + "@ohos/videocompressor": "^1.0.4"
13 }, 14 },
14 "devDependencies": { 15 "devDependencies": {
15 "@ohos/hypium": "1.0.19", 16 "@ohos/hypium": "1.0.19",