Skip to content

Commit 9794438

Browse files
committed
患者詳細画面にcsvのパスワード付きzipダウンロード機能を追加
1 parent efa3a2b commit 9794438

File tree

1 file changed

+141
-33
lines changed

1 file changed

+141
-33
lines changed

pages/center/_centerId/patient/_patientId.vue

Lines changed: 141 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,100 @@
1515
<div class="patientContainer">
1616
<div class="patientHeader">
1717
<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>
3335
</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>
3557
</div>
36-
<div v-if="!patient.memo">患者ID:{{ patient.patientId }}</div>
3758
<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+
患者データをダウンロード
5461
</span>
5562
</div>
5663
</div>
5764
<div class="patientGraphLayout">
5865
<PatientGraph :patient="patient" />
5966
</div>
6067
<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>
6197
</div>
6298
</div>
6399
</template>
64100

65101
<script lang="ts">
66102
import { Component, Vue } from 'vue-property-decorator'
67103
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'
68107
import { Patient } from '@/types/component-interfaces/patient'
69108
import ActionButton from '@/components/ActionButton.vue'
70109
import SymptomsHistory from '@/components/SymptomsHistory.vue'
71110
import InputField from '@/components/InputField.vue'
111+
import ModalBase from '@/components/ModalBase.vue'
72112
import EditIcon from '@/static/icon-edit.svg'
73113
import SaveIcon from '@/static/icon-save.svg'
74114
import CloseIcon from '@/static/icon-close.svg'
@@ -80,6 +120,7 @@ import { patientsStore } from '@/store'
80120
ActionButton,
81121
SymptomsHistory,
82122
InputField,
123+
ModalBase,
83124
EditIcon,
84125
SaveIcon,
85126
CloseIcon,
@@ -88,6 +129,9 @@ import { patientsStore } from '@/store'
88129
export default class PatientId extends Vue {
89130
isEditDisabled = true
90131
currentMemoValue = ''
132+
showModal = false
133+
zipPassword = ''
134+
zipPasswordReEnter = ''
91135
patient: Patient = {
92136
patientId: '',
93137
centerId: '',
@@ -117,6 +161,20 @@ export default class PatientId extends Vue {
117161
patientsStore.setPatient(value || '')
118162
}
119163
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+
120178
getDate(date: string): string {
121179
return dayjs(date).format('M/D (ddd) HH:mm')
122180
}
@@ -143,6 +201,45 @@ export default class PatientId extends Vue {
143201
this.patient.display = patient.display
144202
})
145203
}
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+
}
146243
}
147244
</script>
148245

@@ -163,6 +260,8 @@ export default class PatientId extends Vue {
163260
overflow: hidden;
164261
}
165262
.patientHeader {
263+
display: flex;
264+
justify-content: space-between;
166265
padding: 32px;
167266
}
168267
.patentId {
@@ -221,4 +320,13 @@ export default class PatientId extends Vue {
221320
.symptomsHistory {
222321
padding: 32px;
223322
}
323+
.download {
324+
cursor: pointer;
325+
}
326+
.inputContainer {
327+
margin: 30px 0;
328+
}
329+
.passwordField {
330+
margin-bottom: 30px;
331+
}
224332
</style>

0 commit comments

Comments
 (0)