15
15
<div class =" patientContainer" >
16
16
<div class =" patientHeader" >
17
17
<div >
18
- <span class =" patentId" >
19
- 患者メモ:
20
- <InputField
21
- v-model =" memoValue"
22
- class =" memo"
23
- :disabled =" isEditDisabled"
24
- />
25
- <EditIcon
26
- v-if =" isEditDisabled"
27
- class =" icon"
28
- @click =" isEditDisabled = false"
29
- />
30
- <span v-else >
31
- <SaveIcon class =" icon" @click =" updateMemo" />
32
- <CloseIcon class =" icon" @click =" cancelMemo" />
18
+ <div >
19
+ <span class =" patentId" >
20
+ 患者メモ:
21
+ <InputField
22
+ v-model =" memoValue"
23
+ class =" memo"
24
+ :disabled =" isEditDisabled"
25
+ />
26
+ <EditIcon
27
+ v-if =" isEditDisabled"
28
+ class =" icon"
29
+ @click =" isEditDisabled = false"
30
+ />
31
+ <span v-else >
32
+ <SaveIcon class =" icon" @click =" updateMemo" />
33
+ <CloseIcon class =" icon" @click =" cancelMemo" />
34
+ </span >
33
35
</span >
34
- </span >
36
+ </div >
37
+ <div v-if =" !patient.memo" >患者ID:{{ patient.patientId }}</div >
38
+ <div >
39
+ <span class =" monitoringTerm" >
40
+ モニタリング開始:{{ getDate(patient.policy_accepted) }}
41
+ </span >
42
+ <span
43
+ v-if =" patient.display"
44
+ class =" isDataDisplay"
45
+ @click =" handleDisplayPatient(false)"
46
+ >
47
+ 患者データを非表示にする
48
+ </span >
49
+ <span
50
+ v-else
51
+ class =" isDataDisplay"
52
+ @click =" handleDisplayPatient(true)"
53
+ >
54
+ 患者データを表示する
55
+ </span >
56
+ </div >
35
57
</div >
36
- <div v-if =" !patient.memo" >患者ID:{{ patient.patientId }}</div >
37
58
<div >
38
- <span class =" monitoringTerm" >
39
- モニタリング開始:{{ getDate(patient.policy_accepted) }}
40
- </span >
41
- <span
42
- v-if =" patient.display"
43
- class =" isDataDisplay"
44
- @click =" handleDisplayPatient(false)"
45
- >
46
- 患者データを非表示にする
47
- </span >
48
- <span
49
- v-else
50
- class =" isDataDisplay"
51
- @click =" handleDisplayPatient(true)"
52
- >
53
- 患者データを表示する
59
+ <span class =" download" @click =" showModal = true" >
60
+ 患者データをダウンロード
54
61
</span >
55
62
</div >
56
63
</div >
57
64
<div class =" patientGraphLayout" >
58
65
<PatientGraph :patient =" patient" />
59
66
</div >
60
67
<SymptomsHistory class =" symptomsHistory" :statuses =" patient.statuses" />
68
+ <ModalBase :show =" showModal" @close =" closeModal" >
69
+ <h2 >患者データをダウンロード</h2 >
70
+ <div class =" inputContainer" >
71
+ <InputField
72
+ v-model =" zipPassword"
73
+ type =" password"
74
+ label =" パスワードを設定してください"
75
+ :rule-length =" 8"
76
+ required
77
+ class =" passwordField"
78
+ />
79
+ <InputField
80
+ v-model =" zipPasswordReEnter"
81
+ type =" password"
82
+ label =" パスワードの再入力"
83
+ :rule-length =" 8"
84
+ required
85
+ class =" passwordField"
86
+ />
87
+ </div >
88
+ <ActionButton
89
+ :theme =" downloadBtnTheme"
90
+ size =" L"
91
+ :is-submittable =" isDownloadable"
92
+ @click =" downloadZip"
93
+ >
94
+ ZIPをダウンロード
95
+ </ActionButton >
96
+ </ModalBase >
61
97
</div >
62
98
</div >
63
99
</template >
64
100
65
101
<script lang="ts">
66
102
import { Component , Vue } from ' vue-property-decorator'
67
103
import dayjs from ' dayjs'
104
+ import Papa from ' papaparse'
105
+ import { saveAs } from ' file-saver'
106
+ import * as zip from ' @zip.js/zip.js/dist/zip'
68
107
import { Patient } from ' @/types/component-interfaces/patient'
69
108
import ActionButton from ' @/components/ActionButton.vue'
70
109
import SymptomsHistory from ' @/components/SymptomsHistory.vue'
71
110
import InputField from ' @/components/InputField.vue'
111
+ import ModalBase from ' @/components/ModalBase.vue'
72
112
import EditIcon from ' @/static/icon-edit.svg'
73
113
import SaveIcon from ' @/static/icon-save.svg'
74
114
import CloseIcon from ' @/static/icon-close.svg'
@@ -80,6 +120,7 @@ import { patientsStore } from '@/store'
80
120
ActionButton ,
81
121
SymptomsHistory ,
82
122
InputField ,
123
+ ModalBase ,
83
124
EditIcon ,
84
125
SaveIcon ,
85
126
CloseIcon ,
@@ -88,6 +129,9 @@ import { patientsStore } from '@/store'
88
129
export default class PatientId extends Vue {
89
130
isEditDisabled = true
90
131
currentMemoValue = ' '
132
+ showModal = false
133
+ zipPassword = ' '
134
+ zipPasswordReEnter = ' '
91
135
patient: Patient = {
92
136
patientId: ' ' ,
93
137
centerId: ' ' ,
@@ -117,6 +161,20 @@ export default class PatientId extends Vue {
117
161
patientsStore .setPatient (value || ' ' )
118
162
}
119
163
164
+ get isDownloadable(): boolean {
165
+ return (
166
+ this .zipPassword !== ' ' &&
167
+ this .zipPasswordReEnter !== ' ' &&
168
+ this .zipPassword .length >= 8 &&
169
+ this .zipPassword .length >= 8 &&
170
+ this .zipPassword === this .zipPasswordReEnter
171
+ )
172
+ }
173
+
174
+ get downloadBtnTheme(): string {
175
+ return this .isDownloadable ? ' primary' : ' disable'
176
+ }
177
+
120
178
getDate(date : string ): string {
121
179
return dayjs (date ).format (' M/D (ddd) HH:mm' )
122
180
}
@@ -143,6 +201,45 @@ export default class PatientId extends Vue {
143
201
this .patient .display = patient .display
144
202
})
145
203
}
204
+
205
+ async downloadZip() {
206
+ if (this .isDownloadable ) {
207
+ const data = this .patient .statuses .map ((item ) => {
208
+ return {
209
+ 記録日時: dayjs (item .created ).format (' YYYY-MM-DDTHH:mm:ss+09:00' ),
210
+ SpO2: item .SpO2 ,
211
+ 体温: item .body_temperature ,
212
+ 脈拍: item .pulse ,
213
+ せき: item .symptom ?.cough ? 1 : 0 ,
214
+ たん: item .symptom ?.phlegm ? 1 : 0 ,
215
+ 息苦しさ: item .symptom ?.suffocation ? 1 : 0 ,
216
+ 頭痛: item .symptom ?.headache ? 1 : 0 ,
217
+ のど痛み: item .symptom ?.sore_throat ? 1 : 0 ,
218
+ 症状備考: item .symptom ?.remarks ,
219
+ }
220
+ })
221
+ const csv = Papa .unparse (data , {
222
+ delimiter: ' ,' ,
223
+ })
224
+ const now = new Date ()
225
+ const formattedNowDate = dayjs (now ).format (' YYYYMMDD_HHmm' )
226
+ const inputBlob = new Blob ([csv ], { type: ' text/csv' })
227
+ const zipWriter = new zip .ZipWriter (new zip .BlobWriter (' application/zip' ))
228
+ await zipWriter .add (
229
+ ` ${this .patient .phone }_${this .patient .memo }.csv ` ,
230
+ new zip .BlobReader (inputBlob ),
231
+ { password: this .zipPassword },
232
+ )
233
+ const blob = await zipWriter .close ()
234
+ saveAs (blob , ` patient_${formattedNowDate }.zip ` )
235
+ }
236
+ }
237
+
238
+ closeModal(): void {
239
+ this .showModal = false
240
+ this .zipPassword = ' '
241
+ this .zipPasswordReEnter = ' '
242
+ }
146
243
}
147
244
</script >
148
245
@@ -163,6 +260,8 @@ export default class PatientId extends Vue {
163
260
overflow : hidden ;
164
261
}
165
262
.patientHeader {
263
+ display : flex ;
264
+ justify-content : space-between ;
166
265
padding : 32px ;
167
266
}
168
267
.patentId {
@@ -221,4 +320,13 @@ export default class PatientId extends Vue {
221
320
.symptomsHistory {
222
321
padding : 32px ;
223
322
}
323
+ .download {
324
+ cursor : pointer ;
325
+ }
326
+ .inputContainer {
327
+ margin : 30px 0 ;
328
+ }
329
+ .passwordField {
330
+ margin-bottom : 30px ;
331
+ }
224
332
</style >
0 commit comments