Compare commits
5 Commits
dd9722862f
...
01f91d83d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01f91d83d8 | ||
|
|
a7ceb82cbb | ||
|
|
383fa4ded6 | ||
| 6e4a9020a2 | |||
| dfc80c46d2 |
@ -376,11 +376,20 @@ function eventTypeLabel(eventType) {
|
||||
}
|
||||
|
||||
function resolveUserName(userId) {
|
||||
const id = String(userId || "");
|
||||
if (!id) return "";
|
||||
const map = userNameMap.value || {};
|
||||
return String(map[id] || "") || id;
|
||||
}
|
||||
|
||||
function refreshChatRoom() {
|
||||
if (!props.fromChat) return;
|
||||
const pending = uni.getStorageSync(PENDING_FOLLOWUP_SEND_STORAGE_KEY);
|
||||
if (pending && typeof pending === "object") {
|
||||
chatGroupId.value = String(pending.chatGroupId || pending.groupId || "");
|
||||
}
|
||||
}
|
||||
|
||||
function formatTodo(todo) {
|
||||
const status = getStatus(todo);
|
||||
const plannedExecutionTime = todo?.plannedExecutionTime;
|
||||
@ -446,13 +455,23 @@ async function getMore() {
|
||||
toast(res?.message || "获取回访任务失败");
|
||||
return;
|
||||
}
|
||||
const arr = Array.isArray(res.data) ? res.data : [];
|
||||
|
||||
// 对齐管理端同接口返回结构:{ success, data: { data: [], total } }
|
||||
const payload = res?.data;
|
||||
const arr = payload && Array.isArray(payload.data)
|
||||
? payload.data
|
||||
: Array.isArray(payload)
|
||||
? payload
|
||||
: [];
|
||||
const next = arr.map(formatTodo);
|
||||
|
||||
total.value = typeof res.total === "number" ? res.total : 0;
|
||||
total.value = payload && typeof payload.total === "number" ? payload.total : 0;
|
||||
pages.value = Math.ceil(total.value / pageSize) || 0;
|
||||
list.value = page.value === 1 ? next : [...list.value, ...next];
|
||||
page.value += 1;
|
||||
} catch (e) {
|
||||
console.error("getCustomerTodos failed:", e);
|
||||
toast("获取回访任务失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@ -9,10 +9,15 @@
|
||||
</view>
|
||||
</picker>
|
||||
|
||||
<uni-datetime-picker type="daterange" :value="dateRange" @change="pickTimeRange">
|
||||
<uni-datetime-picker type="daterange" v-model="dateRange" @change="pickTimeRange">
|
||||
<view class="filter-pill">
|
||||
<view class="pill-text">{{ dateRangeLabel }}</view>
|
||||
<uni-icons type="arrowdown" size="12" color="#666" />
|
||||
<view class="pill-icons">
|
||||
<view v-if="isDateRangeActive" class="pill-clear" @tap.stop="clearTimeRange" @click.stop="clearTimeRange">
|
||||
<uni-icons type="closeempty" size="16" color="#666" />
|
||||
</view>
|
||||
<uni-icons type="arrowdown" size="12" color="#666" />
|
||||
</view>
|
||||
</view>
|
||||
</uni-datetime-picker>
|
||||
</view>
|
||||
@ -80,7 +85,7 @@ const props = defineProps({
|
||||
floatingBottom: { type: Number, default: 16 },
|
||||
});
|
||||
|
||||
const FALLBACK_TEMPLATE_TYPES = ['outpatient', 'inhospital', 'preConsultation', 'physicalExaminationTemplate'];
|
||||
const FALLBACK_TEMPLATE_TYPES = ['outpatient', 'inhospital', 'preConsultationRecord', 'physicalExaminationTemplate'];
|
||||
const templates = ref([]);
|
||||
const selectableTemplates = computed(() => templates.value.filter((i) => i && i.templateType && typeof i.name === 'string' && i.name.trim()));
|
||||
const useActionSheet = computed(() => selectableTemplates.value.length > 0 && selectableTemplates.value.length <= 6);
|
||||
@ -97,6 +102,9 @@ const currentType = ref({ name: '全部', value: 'ALL' });
|
||||
const records = ref([]);
|
||||
|
||||
const dateRange = ref([]);
|
||||
const isDateRangeActive = computed(
|
||||
() => Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]
|
||||
);
|
||||
const dateRangeLabel = computed(() => {
|
||||
if (Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]) {
|
||||
return `${dateRange.value[0]} 至 ${dateRange.value[1]}`;
|
||||
@ -226,12 +234,13 @@ async function loadTeamMembers() {
|
||||
|
||||
function getSortTimeTitle(templateType) {
|
||||
const rawType = String(templateType || '');
|
||||
const ui = normalizeMedicalType(rawType);
|
||||
// 预问诊记录:列表时间固定使用就诊日期字段,避免被模板配置的 timeTitle 覆盖
|
||||
if (ui === 'preConsultationRecord') return 'consultationDate';
|
||||
const t = templateMap.value[rawType] || {};
|
||||
if (t?.service?.timeTitle) return String(t.service.timeTitle);
|
||||
const ui = normalizeMedicalType(rawType);
|
||||
if (ui === 'outpatient') return 'visitTime';
|
||||
if (ui === 'inhospital') return 'inhosDate';
|
||||
if (ui === 'preConsultation') return 'consultDate';
|
||||
if (ui === 'physicalExaminationTemplate') return 'inspectDate';
|
||||
return '';
|
||||
}
|
||||
@ -275,14 +284,15 @@ function normalizeMedicalType(raw) {
|
||||
const s = String(raw || '').trim();
|
||||
if (!s) return '';
|
||||
const lower = s.toLowerCase();
|
||||
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultation';
|
||||
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultationRecord';
|
||||
if (lower === 'outpatient' || lower === 'out_patient' || lower === 'out-patient') return 'outpatient';
|
||||
if (lower === 'inhospital' || lower === 'in_hospital' || lower === 'in-hospital' || lower === 'inpatient') return 'inhospital';
|
||||
if (lower === 'preconsultation' || lower === 'pre_consultation' || lower === 'pre-consultation') return 'preConsultation';
|
||||
if (lower === 'preconsultation' || lower === 'pre_consultation' || lower === 'pre-consultation') return 'preConsultationRecord';
|
||||
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
|
||||
if (s === 'outPatient') return 'outpatient';
|
||||
if (s === 'inHospital') return 'inhospital';
|
||||
if (s === 'preConsultation') return 'preConsultation';
|
||||
if (s === 'preConsultation') return 'preConsultationRecord';
|
||||
if (s === 'preConsultationRecord') return 'preConsultationRecord';
|
||||
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
|
||||
return s;
|
||||
}
|
||||
@ -389,7 +399,7 @@ async function refreshList() {
|
||||
const tagClass = {
|
||||
outpatient: 'bg-amber',
|
||||
inhospital: 'bg-teal',
|
||||
preConsultation: 'bg-indigo',
|
||||
preConsultationRecord: 'bg-indigo',
|
||||
physicalExaminationTemplate: 'bg-green',
|
||||
};
|
||||
|
||||
@ -401,14 +411,14 @@ function resolveRecordType(r) {
|
||||
if (r.inspectDate || r.positiveFind || r.inspectSummary) return 'physicalExaminationTemplate';
|
||||
if (r.inhosDate || r.surgeryName || r.surgeryDate || r.operationDate) return 'inhospital';
|
||||
if (r.visitTime || r.disposePlan || r.treatmentPlan) return 'outpatient';
|
||||
if (r.consultDate || r.presentIllness || r.presentIllnessHistory || r.pastHistory) return 'preConsultation';
|
||||
if (r.consultationDate || r.consultDate || r.presentIllness || r.presentIllnessHistory || r.pastHistory) return 'preConsultationRecord';
|
||||
return '';
|
||||
}
|
||||
|
||||
function getDiagnosis(r) {
|
||||
if (!r) return '--';
|
||||
const t = resolveRecordType(r);
|
||||
if (t === 'preConsultation') return normalizeText(r.chiefComplaint) || normalizeText(r.summary) || '--';
|
||||
if (t === 'preConsultationRecord') return normalizeText(r.chiefComplaint) || normalizeText(r.summary) || '--';
|
||||
if (t === 'physicalExaminationTemplate') return formatPositiveFind(r.positiveFind) || normalizeText(r.summary) || '--';
|
||||
if (t === 'outpatient' || t === 'inhospital') return normalizeText(r.diagnosisName || r.diagnosis) || normalizeText(r.summary) || '--';
|
||||
return normalizeText(r.diagnosisName || r.diagnosis || r.summary) || '--';
|
||||
@ -433,7 +443,7 @@ function getDisplayLines(r) {
|
||||
if (t === 'physicalExaminationTemplate') {
|
||||
return [{ label: '体检小结:', value: firstLine(r.summary || r.inspectSummary) }];
|
||||
}
|
||||
if (t === 'preConsultation') {
|
||||
if (t === 'preConsultationRecord') {
|
||||
const lines = [
|
||||
{ label: '主诉:', value: firstLine(r.chiefComplaint) },
|
||||
{ label: '现病史:', value: firstLine(r.presentIllness || r.presentIllnessHistory) },
|
||||
@ -461,7 +471,19 @@ function pickType(e) {
|
||||
refreshList();
|
||||
}
|
||||
function pickTimeRange(val) {
|
||||
dateRange.value = val;
|
||||
if (Array.isArray(val)) {
|
||||
dateRange.value = val;
|
||||
} else if (val && typeof val === 'object' && Array.isArray(val.value)) {
|
||||
dateRange.value = val.value;
|
||||
} else if (val && typeof val === 'object' && val.detail && Array.isArray(val.detail.value)) {
|
||||
dateRange.value = val.detail.value;
|
||||
} else {
|
||||
dateRange.value = [];
|
||||
}
|
||||
refreshList();
|
||||
}
|
||||
function clearTimeRange() {
|
||||
dateRange.value = [];
|
||||
refreshList();
|
||||
}
|
||||
|
||||
@ -589,6 +611,17 @@ watch(
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pill-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.pill-clear {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.share-tip {
|
||||
padding: 20rpx 28rpx 0;
|
||||
|
||||
@ -136,7 +136,7 @@ function formatPatient(raw) {
|
||||
const lr = raw.latestRecord;
|
||||
const type = normalizeRecordTypeLabel(lr.type || lr.medicalTypeName || lr.medicalType || '');
|
||||
const date = lr.date || '';
|
||||
const diagnosis = lr.diagnosis || '';
|
||||
const diagnosis = normalizeBriefText(lr.diagnosisName || lr.inspectSummary || lr.chiefComplaint || lr.diagnosis || '');
|
||||
if (type || date || diagnosis) {
|
||||
record = {
|
||||
type: type || '-',
|
||||
@ -169,6 +169,34 @@ function normalizeText(value) {
|
||||
return String(value || '').trim();
|
||||
}
|
||||
|
||||
function normalizeBriefText(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
if (Array.isArray(value)) {
|
||||
const parts = value.map((i) => normalizeBriefText(i)).filter((i) => String(i).trim());
|
||||
return parts.join(',');
|
||||
}
|
||||
let s = String(value || '').trim();
|
||||
if (!s) return '';
|
||||
|
||||
// 常见异常:后端把数组字符串化成 "[]" 或带换行的 JSON 数组
|
||||
if (s === '[]' || s === '[ ]') return '';
|
||||
if (s.startsWith('[') && s.endsWith(']')) {
|
||||
try {
|
||||
const parsed = JSON.parse(s);
|
||||
if (Array.isArray(parsed)) {
|
||||
s = parsed.map((i) => normalizeBriefText(i)).filter((i) => String(i).trim()).join(',');
|
||||
}
|
||||
} catch {
|
||||
// 非 JSON 数组:去掉最外层括号,避免展示成 “[]”
|
||||
s = s.replace(/^\[\s*/, '').replace(/\s*\]$/, '');
|
||||
}
|
||||
}
|
||||
|
||||
s = String(s || '').replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
if (s === '[]' || s === '[ ]') return '';
|
||||
return s;
|
||||
}
|
||||
|
||||
function normalizeDigits(value) {
|
||||
const s = normalizeText(value);
|
||||
return s.replace(/\D/g, '');
|
||||
|
||||
@ -24,6 +24,10 @@ const ALIAS_MAP = {
|
||||
presentIllnessHistory: 'presentIllness',
|
||||
pastMedicalHistory: 'pastHistory',
|
||||
},
|
||||
preConsultationRecord: {
|
||||
presentIllnessHistory: 'presentIllness',
|
||||
pastMedicalHistory: 'pastHistory',
|
||||
},
|
||||
};
|
||||
|
||||
export function normalizeVisitRecordFormData(templateType, raw) {
|
||||
@ -37,4 +41,3 @@ export function normalizeVisitRecordFormData(templateType, raw) {
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,7 @@ const forms = computed(() => ({ ...detail.value, ...form }));
|
||||
const HIDDEN_FIELD_NAMES = {
|
||||
outpatient: ['就诊机构', '就诊科室', '机构名称', '责任医生'],
|
||||
inhospital: ['就诊机构', '机构名称'],
|
||||
physicalExaminationTemplate: ['体检套餐名称'],
|
||||
};
|
||||
const HIDDEN_FIELD_TITLES = {
|
||||
// 对应 systemFieldName/title(来自模板):
|
||||
@ -87,6 +88,8 @@ const HIDDEN_FIELD_TITLES = {
|
||||
// - 就诊机构: corp
|
||||
// - 机构名称: corpName
|
||||
inhospital: ['corp', 'corpName'],
|
||||
// - 体检套餐名称: inspectPakageName
|
||||
physicalExaminationTemplate: ['inspectPakageName'],
|
||||
};
|
||||
function shouldHideField(item) {
|
||||
const t = String(templateType.value || '');
|
||||
@ -306,10 +309,13 @@ async function remove() {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
.scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.scroll-gap {
|
||||
height: calc(240rpx + env(safe-area-inset-bottom));
|
||||
@ -340,6 +346,7 @@ async function remove() {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||
z-index: 50;
|
||||
}
|
||||
.btn {
|
||||
flex: 1;
|
||||
|
||||
@ -49,8 +49,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import dayjs from 'dayjs';
|
||||
import api from '@/utils/api';
|
||||
import { loading, hideLoading, toast } from '@/utils/widget';
|
||||
@ -65,6 +65,8 @@ const medicalType = ref('');
|
||||
const rawType = ref('');
|
||||
const record = ref({});
|
||||
const temp = ref(null);
|
||||
const needReload = ref(false);
|
||||
let recordChangedHandler = null;
|
||||
|
||||
const files = computed(() => {
|
||||
const arr = record.value?.files;
|
||||
@ -79,14 +81,16 @@ function normalizeMedicalType(raw) {
|
||||
const s = String(raw || '').trim();
|
||||
if (!s) return '';
|
||||
const lower = s.toLowerCase();
|
||||
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultation';
|
||||
if (lower.includes('preconsultationrecord')) return 'preConsultationRecord';
|
||||
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultationRecord';
|
||||
if (lower === 'outpatient' || lower === 'out_patient' || lower === 'out-patient') return 'outpatient';
|
||||
if (lower === 'inhospital' || lower === 'in_hospital' || lower === 'in-hospital' || lower === 'inpatient') return 'inhospital';
|
||||
if (lower === 'preconsultation' || lower === 'pre_consultation' || lower === 'pre-consultation') return 'preConsultation';
|
||||
if (lower === 'preconsultation' || lower === 'pre_consultation' || lower === 'pre-consultation') return 'preConsultationRecord';
|
||||
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
|
||||
if (s === 'outPatient') return 'outpatient';
|
||||
if (s === 'inHospital') return 'inhospital';
|
||||
if (s === 'preConsultation') return 'preConsultation';
|
||||
if (s === 'preConsultation') return 'preConsultationRecord';
|
||||
if (s === 'preConsultationRecord') return 'preConsultationRecord';
|
||||
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
|
||||
return s;
|
||||
}
|
||||
@ -98,7 +102,7 @@ const typeLabel = computed(() => record.value?.tempName || temp.value?.name || g
|
||||
function getDefaultTimeTitle(t) {
|
||||
if (t === 'outpatient') return 'visitTime';
|
||||
if (t === 'inhospital') return 'inhosDate';
|
||||
if (t === 'preConsultation') return 'consultDate';
|
||||
if (t === 'preConsultationRecord') return 'consultationDate';
|
||||
if (t === 'physicalExaminationTemplate') return 'inspectDate';
|
||||
return '';
|
||||
}
|
||||
@ -106,7 +110,7 @@ function getDefaultTimeTitle(t) {
|
||||
function getDefaultTimeName(t) {
|
||||
if (t === 'outpatient') return '就诊日期';
|
||||
if (t === 'inhospital') return '入院日期';
|
||||
if (t === 'preConsultation') return '问诊日期';
|
||||
if (t === 'preConsultationRecord') return '问诊日期';
|
||||
if (t === 'physicalExaminationTemplate') return '体检日期';
|
||||
return '日期';
|
||||
}
|
||||
@ -212,7 +216,9 @@ const sections = computed(() => {
|
||||
? ['corp', 'deptName', 'corpName', 'doctor']
|
||||
: t === 'inhospital'
|
||||
? ['corp', 'corpName']
|
||||
: []);
|
||||
: t === 'physicalExaminationTemplate'
|
||||
? ['inspectPakageName']
|
||||
: []);
|
||||
|
||||
const list = [];
|
||||
const pushedNames = new Set();
|
||||
@ -289,6 +295,41 @@ const topText = computed(() => {
|
||||
return suffix ? `${time || '--'} ${suffix}` : `${time || '--'}`;
|
||||
});
|
||||
|
||||
async function fetchRecord({ silent = false } = {}) {
|
||||
if (!archiveId.value || !id.value || !medicalType.value) return false;
|
||||
const corpId = getCorpId();
|
||||
if (!corpId) {
|
||||
if (!silent) toast('缺少 corpId');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!silent) loading('加载中...');
|
||||
try {
|
||||
const res = await api('getMedicalRecordById', {
|
||||
_id: id.value,
|
||||
corpId,
|
||||
memberId: archiveId.value,
|
||||
medicalType: medicalType.value,
|
||||
});
|
||||
const r = res?.record || res?.data?.record || null;
|
||||
if (!res?.success || !r) return false;
|
||||
|
||||
const raw = String(r?.templateType || r?.medicalType || medicalType.value || '');
|
||||
rawType.value = raw;
|
||||
const ui = normalizeMedicalType(raw);
|
||||
record.value = normalizeVisitRecordFormData(ui, r);
|
||||
temp.value = await loadTemplate(raw);
|
||||
uni.setNavigationBarTitle({ title: String(typeLabel.value || '病历详情') });
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('获取病历记录失败:', error);
|
||||
if (!silent) toast('加载失败');
|
||||
return false;
|
||||
} finally {
|
||||
if (!silent) hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (opt) => {
|
||||
archiveId.value = opt?.archiveId ? String(opt.archiveId) : '';
|
||||
id.value = opt?.id ? String(opt.id) : '';
|
||||
@ -298,45 +339,32 @@ onLoad(async (opt) => {
|
||||
setTimeout(() => uni.navigateBack(), 300);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用真实 API 获取病历记录
|
||||
loading('加载中...');
|
||||
try {
|
||||
const corpId = getCorpId();
|
||||
if (!corpId) {
|
||||
hideLoading();
|
||||
toast('缺少 corpId');
|
||||
setTimeout(() => uni.navigateBack(), 300);
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await api('getMedicalRecordById', {
|
||||
_id: id.value,
|
||||
corpId,
|
||||
memberId: archiveId.value,
|
||||
medicalType: medicalType.value,
|
||||
});
|
||||
hideLoading();
|
||||
const r = res?.record || res?.data?.record || null;
|
||||
if (!res?.success || !r) {
|
||||
toast('记录不存在');
|
||||
setTimeout(() => uni.navigateBack(), 300);
|
||||
return;
|
||||
}
|
||||
const raw = String(r?.templateType || r?.medicalType || medicalType.value || '');
|
||||
rawType.value = raw;
|
||||
const ui = normalizeMedicalType(raw);
|
||||
record.value = normalizeVisitRecordFormData(ui, r);
|
||||
temp.value = await loadTemplate(raw);
|
||||
uni.setNavigationBarTitle({ title: String(typeLabel.value || '病历详情') });
|
||||
} catch (error) {
|
||||
hideLoading();
|
||||
console.error('获取病历记录失败:', error);
|
||||
toast('加载失败');
|
||||
const ok = await fetchRecord();
|
||||
if (!ok) {
|
||||
toast('记录不存在');
|
||||
setTimeout(() => uni.navigateBack(), 300);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
recordChangedHandler = () => {
|
||||
needReload.value = true;
|
||||
};
|
||||
// 页面在编辑页返回前可能收到事件:先标记,回到页面再刷新
|
||||
uni.$on('archive-detail:visit-record-changed', recordChangedHandler);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (recordChangedHandler) uni.$off('archive-detail:visit-record-changed', recordChangedHandler);
|
||||
recordChangedHandler = null;
|
||||
});
|
||||
|
||||
onShow(async () => {
|
||||
if (!needReload.value) return;
|
||||
needReload.value = false;
|
||||
await fetchRecord({ silent: true });
|
||||
});
|
||||
|
||||
function preview(idx) {
|
||||
const urls = files.value.map((i) => i.url);
|
||||
uni.previewImage({ urls, current: urls[idx] });
|
||||
|
||||
@ -212,6 +212,7 @@ const lastPatientsLoadOk = ref(true);
|
||||
const loadedGroupsTeamId = ref('');
|
||||
let enterRefreshInflight = null;
|
||||
const hasEnteredOnce = ref(false);
|
||||
let lastReloadStartedAt = 0;
|
||||
|
||||
const accountStore = useAccountStore();
|
||||
const { account, doctorInfo } = storeToRefs(accountStore);
|
||||
@ -852,15 +853,16 @@ function normalizeMedicalType(raw) {
|
||||
// 中文兜底(部分接口返回展示名)
|
||||
if (s.includes('门诊')) return 'outpatient';
|
||||
if (s.includes('住院') || s.includes('入院')) return 'inhospital';
|
||||
if (s.includes('预问诊') || s.includes('问诊')) return 'preConsultation';
|
||||
if (s.includes('预问诊') || s.includes('问诊')) return 'preConsultationRecord';
|
||||
if (s.includes('体检')) return 'physicalExaminationTemplate';
|
||||
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultation';
|
||||
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultationRecord';
|
||||
if (lower === 'outpatient' || lower === 'out_patient' || lower === 'out-patient') return 'outpatient';
|
||||
if (lower === 'inhospital' || lower === 'in_hospital' || lower === 'in-hospital' || lower === 'inpatient') return 'inhospital';
|
||||
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
|
||||
if (s === 'outPatient') return 'outpatient';
|
||||
if (s === 'inHospital') return 'inhospital';
|
||||
if (s === 'preConsultation') return 'preConsultation';
|
||||
if (s === 'preConsultation') return 'preConsultationRecord';
|
||||
if (s === 'preConsultationRecord') return 'preConsultationRecord';
|
||||
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
|
||||
return s;
|
||||
}
|
||||
@ -940,6 +942,7 @@ function resolveLatestRecord(lr) {
|
||||
'date',
|
||||
'visitTime',
|
||||
'inhosDate',
|
||||
'consultationDate',
|
||||
'consultDate',
|
||||
'inspectDate',
|
||||
'sortTime',
|
||||
@ -951,77 +954,22 @@ function resolveLatestRecord(lr) {
|
||||
);
|
||||
const rawDateStr = String(rawDate ?? '').trim();
|
||||
const date = (/^\d{10,13}$/.test(rawDateStr) ? (formatAnyDate(rawDateStr, 'YYYY-MM-DD') || rawDateStr) : rawDateStr)
|
||||
|| formatAnyDate(pick('visitTime', 'inhosDate', 'consultDate', 'inspectDate', 'sortTime', 'createTime', 'updateTime'), 'YYYY-MM-DD')
|
||||
|| formatAnyDate(pick('visitTime', 'inhosDate', 'consultationDate', 'consultDate', 'inspectDate', 'sortTime', 'createTime', 'updateTime'), 'YYYY-MM-DD')
|
||||
|| '-';
|
||||
|
||||
let third = '';
|
||||
// 后端若已计算出统一展示字段 diagnosis,则优先使用
|
||||
const directDiagnosis = normalizeText(pick('diagnosis', 'diagnosisName'));
|
||||
if (String(directDiagnosis || '').trim()) {
|
||||
third = directDiagnosis;
|
||||
} else if (uiType === 'outpatient' || uiType === 'inhospital') {
|
||||
third = normalizeText(pick(
|
||||
'diagnosisName',
|
||||
'diagnosis',
|
||||
'diagnosisList',
|
||||
'diagnosisNames',
|
||||
'mainDiagnosis',
|
||||
'admissionDiagnosis',
|
||||
'inDiagnosis',
|
||||
'outDiagnosis',
|
||||
'outPatientDiagnosis',
|
||||
'inHospitalDiagnosis'
|
||||
));
|
||||
} else if (uiType === 'preConsultation') {
|
||||
third = normalizeText(
|
||||
pick(
|
||||
'diagnosis',
|
||||
'diagnosisName',
|
||||
'chiefComplaint',
|
||||
'chiefComplain',
|
||||
'chiefComplaintText',
|
||||
'chiefComplaintContent',
|
||||
'complaint',
|
||||
'complaintDesc',
|
||||
'complaintText',
|
||||
'mainComplaint',
|
||||
'mainSuit',
|
||||
'mainSuitText',
|
||||
'mainSuitContent',
|
||||
'chief',
|
||||
'zs',
|
||||
'zhuSu',
|
||||
'cc',
|
||||
'presentIllness',
|
||||
'historyOfPresentIllness',
|
||||
'currentIllness'
|
||||
)
|
||||
);
|
||||
// searchCorpCustomerForCaseList 接口存在问题,按类型优先展示:
|
||||
// - 预问诊:chiefComplaint
|
||||
// - 体检:inspectSummary
|
||||
// - 门诊/住院:diagnosisName
|
||||
if (uiType === 'outpatient' || uiType === 'inhospital') {
|
||||
third = normalizeText(pick('diagnosisName', 'diagnosis'));
|
||||
} else if (uiType === 'physicalExaminationTemplate') {
|
||||
third = normalizeText(
|
||||
pick(
|
||||
'diagnosis',
|
||||
'diagnosisName',
|
||||
'summary',
|
||||
'summaryText',
|
||||
'inspectSummary',
|
||||
'checkSummary',
|
||||
'examSummary',
|
||||
'physicalSummary',
|
||||
'briefSummary',
|
||||
'resultSummary',
|
||||
'conclusion',
|
||||
'conclusionText',
|
||||
'inspectConclusion',
|
||||
'inspectResult',
|
||||
'finalConclusion',
|
||||
'finalSummary',
|
||||
'reportConclusion',
|
||||
'reportSummary'
|
||||
)
|
||||
);
|
||||
third = normalizeText(pick('inspectSummary', 'summary', 'summaryText', 'inspectConclusion', 'inspectResult'));
|
||||
} else if (uiType === 'preConsultationRecord') {
|
||||
third = normalizeText(pick('chiefComplaint', 'chiefComplain', 'chiefComplaintText', 'chiefComplaintContent'));
|
||||
} else {
|
||||
third = normalizeText(pick('diagnosis', 'diagnosisName', 'summary', 'chiefComplaint'));
|
||||
third = normalizeText(pick('diagnosisName', 'diagnosis', 'inspectSummary', 'summary', 'chiefComplaint'));
|
||||
}
|
||||
third = String(third || '').replace(/\s+/g, ' ').trim();
|
||||
if (!third) {
|
||||
@ -1033,7 +981,7 @@ function resolveLatestRecord(lr) {
|
||||
|
||||
const type = typeLabel || (uiType === 'outpatient' ? '门诊记录'
|
||||
: uiType === 'inhospital' ? '住院记录'
|
||||
: uiType === 'preConsultation' ? '预问诊记录'
|
||||
: uiType === 'preConsultationRecord' ? '预问诊记录'
|
||||
: uiType === 'physicalExaminationTemplate' ? '体检档案'
|
||||
: '-');
|
||||
|
||||
@ -1217,6 +1165,7 @@ async function reload(reset = true, opts = {}) {
|
||||
query.includeRecentAddTime = true;
|
||||
}
|
||||
|
||||
lastReloadStartedAt = Date.now();
|
||||
loading.value = true;
|
||||
let res;
|
||||
try {
|
||||
@ -1769,6 +1718,8 @@ onLoad(async () => {
|
||||
});
|
||||
|
||||
onShow(async () => {
|
||||
const showAt = Date.now();
|
||||
const wasEnteredOnce = hasEnteredOnce.value;
|
||||
const need = uni.getStorageSync(NEED_RELOAD_STORAGE_KEY);
|
||||
if (need) {
|
||||
uni.removeStorageSync(NEED_RELOAD_STORAGE_KEY);
|
||||
@ -1781,6 +1732,12 @@ onShow(async () => {
|
||||
if (needGroups) uni.removeStorageSync(GROUPS_RELOAD_KEY);
|
||||
|
||||
await refreshOnEnter();
|
||||
|
||||
// 避免“复用旧的 inflight 请求”导致回到本页仍然展示旧数据:
|
||||
// 只有在非首次进入且本次 onShow 没有发起新的 reload 时,才补一次 reload。
|
||||
if (wasEnteredOnce && lastReloadStartedAt && lastReloadStartedAt < showAt) {
|
||||
await reload(true, { silent: true, keepOnFail: true });
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@ -1,61 +1,81 @@
|
||||
export default `
|
||||
隐私政策
|
||||
|
||||
欢迎您访问健康柚平台!
|
||||
您(下称“患者”、“用户”)在健康柚平台使用我们的服务或产品时,我们可能会收集您的相关个人信息。健康柚深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。我们致力于维持您对我们的信任,恪守以下原则,保护您的个人信息:权责一致原则、目的明确原则、选择同意原则、最少够用原则、确保安全原则、主体参与原则、公开透明原则等。同时,健康柚承诺,我们将按业界成熟的安全标准,采取相应的安全保护措施来保护您的个人信息。
|
||||
为此,我们制定本隐私政策,适用于我们为患者提供的产品和服务,包括但不限于健康柚微信小程序、柚助手微信小程序。
|
||||
在使用健康柚平台提供的产品或服务前,请您务必认真仔细阅读并确认充分理解本隐私政策,在确认充分理解并同意后再开始使用。一旦您主动选择确认本隐私政策并继续使用的,即视为同意本隐私政策的全部内容;如您不同意相关协议或其中的任何条款的,您应停止访问健康柚平台或使用健康柚产品和服务。
|
||||
如您是未成年人,请您和您的监护人仔细阅读本政策,并在征得您的监护人授权同意的前提下使用我们的服务或向我们提供个人信息。
|
||||
欢迎您访问柚助手平台!
|
||||
您在柚助手平台使用我们的服务或产品时,我们可能会收集您的相关个人信息。我们深知个人信息对您的重要性,我们将按照法律法规的规定并参照行业最佳实践保护您的个人信息及隐私安全。我们制定本隐私政策并特别提示:希望您在使用柚助手平台及相关服务前仔细阅读并理解本隐私政策,以便做出适当的选择。
|
||||
本隐私政策将帮助您了解:
|
||||
• 我们会遵循隐私政策收集、使用您的信息,但不会仅因您同意本隐私政策而采用强制捆绑的方式一揽子收集个人信息。
|
||||
• 当您使用或开启相关功能或使用服务时,为实现功能、服务所必需,我们会收集、使用相关信息。除非是为实现基本业务功能或根据法律法规要求所必需的必要信息,您均可以拒绝提供且不影响其他功能或服务。我们将在隐私政策中逐项说明哪些是必要信息。
|
||||
• 如果您未登录帐号,我们会通过设备对应的标识符信息来保障信息推送的基本功能。如果您登录了帐号,我们会根据帐号信息实现信息推送。
|
||||
• 精确地理位置、摄像头、麦克风、相册(存储)权限,均不会默认开启,只有经过您的明示授权才会在为实现特定功能或服务时使用,您也可以撤回授权。特别需要指出的是,即使经过您的授权,我们获得了这些敏感权限,也不会在相关功能或服务不需要时而收集您的信息。
|
||||
下文将帮您详细了解我们如何收集、使用、存储、传输、共享、转移(如适用)与保护个人信息;帮您了解查询、访问、删除、更正、复制、转移个人信息,撤回授权和要求我们解释对个人信息处理规则的方式。本政策与您使用我们的服务关系密切,我们建议您仔细阅读并理解本政策全部内容,作出您认为适当的选择,有关您个人信息权益的条款重要内容我们已用加粗形式提示,请特别关注。本隐私政策适用的功能并不面向未成年人开放,如您是未成年人,请务必不要使用柚助手平台本功能。
|
||||
|
||||
本隐私政策将帮助您了解以下内容:
|
||||
1、我们如何收集和使用您的个人信息
|
||||
2、我们如何使用Cookie和同类技术
|
||||
3、我们如何共享、转移、公开披露您的个人信息
|
||||
4、我们如何保存和保护您的个人信息
|
||||
5、您的权利
|
||||
6、我们如何处理未成年人的个人信息
|
||||
7、您的个人信息如何在全球范围转移
|
||||
8、本隐私政策更新及通知
|
||||
9、如何联系我们
|
||||
10、争议解决
|
||||
一、我们如何收集和使用您的个人信息
|
||||
个人信息是指以电子或其他方式记录的与已识别或者可识别的自然人有关的各种信息,不包括匿名化处理后的信息。
|
||||
我们仅会出于本政策所述的以下目的,收集和使用您的个人信息,当我们要将信息用于本政策未载明的其他用途时,会事先征求您的同意。
|
||||
一.我们如何收集和使用个人信息
|
||||
二.我们如何使用cookie和同类技术
|
||||
三.我们如何共享、转移、公开披露您的个人信息
|
||||
四.我们如何保护您的个人信息
|
||||
五. 管理您的个人信息
|
||||
六. 我们如何处理未成年人的个人信息
|
||||
七. 您的个人信息如何在全球范围转移
|
||||
八. 本政策更新及通知
|
||||
九.如何联系我们
|
||||
十.争议解决
|
||||
|
||||
一、我们如何收集和使用个人信息
|
||||
我们会按照如下方式收集您在使用服务时主动提供的,以及通过自动化手段收集您在使用功能或接受服务过程中产生的信息:
|
||||
(一) 注册成为用户
|
||||
健康柚平台提供的服务或产品是基于注册用户使用的,如您希望使用健康柚平台提供的服务或产品,则需要通过以下步骤完成账号注册。
|
||||
创建健康柚账户,我们将提供手机号码授权登陆方式,您需要提供您的手机号码。如不提供上述注册信息,您无法使用需注册成为健康柚平台用户方可使用的服务。
|
||||
(二) “成员档案管理”服务
|
||||
用户在使用“成员档案管理”服务时,需先添加成员信息,包括您的姓名、性别、年龄、与成员关系(本人/子女/父母/其他等),目的是协助医生对患者进行管理。
|
||||
(三) “我的服务团队”服务
|
||||
用户在“成员档案管理”中添加成员信息后,使用“团队”服务时,我们可能会收集在健康柚其他平台已与患者建立关系的服务团队信息,目的是与服务团队建立联系,以确保成员聊天咨询的连续性和准确性。我们还可能收集您的登记信息(姓名、身份证号码、性别、年龄),检查检验报告、用药记录、过敏史等个人健康生理信息,以及与个人身体健康状况相关的身高、体重信息,用于了解您的健康状况和咨询需求。如不收集这类信息,我们将无法为您提供健康咨询相关的服务,但不影响您使用其他服务。
|
||||
您与团队建立服务关系后,您理解并同意将添加的个人信息、病历信息、就诊记录将向该团队展示;
|
||||
(四) “回访”服务
|
||||
在您的服务团队人员对您进行回访的过程中,我们可能收集您与团队人员的沟通聊天记录、为您制定的回访计划、向您发送的文章,以及您填写的问卷信息。以支持您在健康柚平台上获得持续、可追溯的回访服务。
|
||||
(五) 客户服务
|
||||
当您向我们提出问题、投诉或建议时,我们需要收集您的通信/通话记录、您提供的联系方式信息、您为了证明相关事实提供的信息以及您参与问卷调查时向我们发送的问卷答复信息。我们收集上述信息的法律依据是基于向您提供健康柚平台服务所必需,为您解决您在使用平台及享受服务过程中所遇到的问题,以及向您提供相关问题的处理方案和结果。如不收集这类信息,您的投诉、建议和反馈将无法得到及时、有效处理,但不影响您使用其它服务。
|
||||
(六) 保障功能运行和风控服务
|
||||
为保障您正常使用我们及我们关联公司、合作伙伴提供的服务,维护我们系统基础功能的正常运行,拦截钓鱼网站、欺诈,防止网络漏洞、计算机病毒、网络攻击、网络侵入,改进及优化我们的服务体验以及保障您的账号安全,我们需要整合我们已根据本隐私政策合法收集的您的个人基本信息(姓名、身份证号码、手机号码、性别、年龄)、个人生理健康信息(既往病史、用药记录、体重),并收集、使用或整合您的网络身份标识信息(BSSID、DNS地址、IP地址、SSID、代理信息、网络类型、网络名称、掩码信息)、个人常用设备信息(IMEI、IMSI、设备ID、MAC地址、IDFA、IDFV、AndroidId、MCC、MNC、UUID、标准国家码、操作系统信息、Cookie启用状态、重力传感、陀螺仪传感、加速度传感、已安装应用列表)、位置信息(经纬度)、人脸识别信息以及我们关联公司、合作伙伴取得您授权或依据法律共享的信息。我们收集上述信息的法律依据是基于法定义务及向您提供健康柚平台服务所必需,以综合判断您账户及交易风险、进行身份验证、检测及防范账户安全事件,并依法采取必要的记录、审计、分析、处置措施。如不收集这类信息,您将无法使用健康柚平台及健康柚平台提供的相应服务。
|
||||
(七) 我们如何使用您的信息
|
||||
1、我们会对我们提供的服务使用情况进行统计,并可能会与公众或第三方共享这些统计信息,以用于产品开发、服务优化、安全保障、数据分析等目的。但这些统计信息不包含您的任何身份识别信息。
|
||||
2、根据相关法律法规规定,以下情形中收集、使用您的个人信息无需征得您的授权同意:
|
||||
(1)为订立、履行您作为一方当事人的合同所必需;
|
||||
(2)为履行法定职责或者法定义务所必需;
|
||||
(3)为应对突发公共卫生事件,或者紧急情况下为保护自然人的生命健康和财产安全所必需;
|
||||
(4)为公共利益实施新闻报道、舆论监督等行为,在合理的范围内处理您的个人信息;
|
||||
(5)依照法律规定在合理的范围内处理您自行公开或者其他已经合法公开的个人信息;
|
||||
(6)法律、行政法规规定的其他情形。
|
||||
二、我们如何使用Cookies和同类技术
|
||||
(一) Cookies
|
||||
柚助手平台提供的服务或产品是基于注册用户使用的,如您希望使用柚助手平台提供的服务或产品,则需要通过以下步骤完成账号注册。
|
||||
创建柚助手账户,我们将提供手机号码授权登陆方式,您需要提供您的手机号码。如不提供上述注册信息,您无法使用需注册成为柚助手平台用户方可使用的服务。
|
||||
2. 柚助手平台大多数服务是面向医疗卫生专业人士提供,注册成功后,为保障您专业人士身份的真实性从而保障内容的专业性/活动资格的真实性,平台有义务验证您的医疗卫生专业人士的身份,因此您需要完善您的身份信息,我们可能需要收集您以下相关信息:您需要提供姓名、医院名称、科室、岗位、职称、个人介绍情况、以及证件证明资料,如医师执业资格证(照片页)或医师执业资格证(信息页)进行实名(实人)认证及资质审核,
|
||||
如果您不提供这些信息,可能会影响您在柚助手平台部分核心业务功能的正常使用,如在线咨询等,但不会影响您进行基本的浏览、搜索。
|
||||
3. 如您拒绝提供手机号码进行身份验证,将导致注册不成功,您可以退出注册页面后进入柚助手平台,仅浏览平台内的相关信息内容,但不可进行任何其它的具体操作或使用平台提供的核心服务等。
|
||||
4.您提供的上述信息,将在您使用本服务期间持续授权我们使用。在您注销账号时,我们将停止使用并删除上述信息。
|
||||
6.您在使用我们提供的搜索、推送、统计等服务时,我们会收集您搜索的关键字信息、搜索历史记录、个人信息(BSSID、IMSI、SN、SSID、ICCID、SUPI、SUCI、传感器列表、软件安装列表、位置、联系人、通话记录、日历、短信、本机电话号码、图片、音视频)、设备信息(包括您使用的设备属性、连接和状态信息,例如设备型号、设备标识符(如IMEI/AndroidID/IDFA/OPENUDID/GUID/OAID)、设备MAC地址、电信运营商等软硬件特征信息)。为了提供高效的搜索服务,部分信息会暂时存储在您的本地设备中。
|
||||
二)向您提供的产品和服务
|
||||
1.在线沟通及回访服务
|
||||
当您通过柚助手小程序对患者进行沟通或随访时,我们可能会收集您与患者的沟通聊天记录、为患者制定的回访计划、向患者发送的宣教文章和问卷信息以便于支持您完成患者持续的,可追溯的随访。
|
||||
您知晓并同意,您的真实姓名、主要执业点医院名称及科室、职称,以及您补充的个人主页上的信息(如您的真实头像、性别、擅长疾病及个人简介)会在患者沟通及随访服务过程中展现。同时,出于患教以及帮助其他患者的目的,您与患者的交流信息将有可能作为优质内容在对患者进行匿名化和/或去标识化处理后公开展示,您的真实姓名、真实头像(如有)、职称、主要执业点医院以及科室也将一并展示。
|
||||
2.运营与安全保障
|
||||
(1)运营与安全
|
||||
我们致力于为您提供安全、可信的产品与使用环境,提供优质、高效、可靠的服务与信息是我们的核心目标。
|
||||
(2)设备信息与日志信息
|
||||
a.为了保障软件服务的安全、运营的质量及效率,我们会收集您的硬件型号、操作系统版本号、国际移动设备识别码、唯一设备标识符、网络设备硬件地址、IP地址、WLAN接入点、蓝牙、基站、软件版本号、网络接入方式、类型、状态、网络质量数据、操作、使用、服务日志。
|
||||
b.为了预防恶意程序、确保运营质量及效率,我们会收集安装的应用信息或正在运行的进程信息、应用程序的总体运行、使用情况与频率、应用崩溃情况、总体安装使用情况、性能数据、应用来源。
|
||||
c.我们可能使用您的账户信息、设备信息、服务日志信息以及我们关联公司、合作方在获得您授权或依法可以共享的信息,用于判断账户安全、进行身份验证、检测及防范安全事件。
|
||||
我们获取的设备权限
|
||||
为向您提供便捷、优质的服务,我们可能会调用您设备上的一些权限。在您使用相应功能时会看到弹窗提醒,询问您是否授权。
|
||||
请您注意:上述附加功能可能需要您在您的设备中向我们开启您的相机(摄像头)、相册(图片库)、麦克风、系统通知的访问权限,以实现这些功能所涉及的信息的收集和使用。我们在您首次使用该类附加功能需开启相应权限时,弹窗对您进行提示,您选择开启相应权限,则可进一步使用相应服务。
|
||||
请您注意:您开启这些权限即代表您授权我们可以收集和使用这些个人信息来实现上述的功能,您关闭权限即代表您取消了这些授权,则我们将不再继续收集和使用您的这些个人信息,也无法为您提供上述与这些授权所对应的功能。您关闭权限的决定不会影响此前基于您的授权所进行的个人信息的处理。
|
||||
(3)我们收集的个人信息
|
||||
当您通过网站、客户端、小程序等服务入口使用我们的服务时,我们可能从您的设备处收集您的IP地址、设备型号、唯一设备识别码、浏览器型号、粗略位置信息(例如国家或者城市信息)、安装的应用信息以及其他的设备技术信息。
|
||||
收集该等信息主要为了保障服务的安全运行。我们同时也需要留存相应的日志来记录我们网站、移动客户端等服务入口的运行状态,以符合法律法规的规定。
|
||||
(4)收集、使用个人信息目的变更
|
||||
请您了解,随着我们业务的发展,可能会对柚助手平台的功能和提供的服务有所调整变化。原则上,当新功能或服务与我们当前提供的功能或服务相关时,收集与使用的个人信息将与原处理目的具有直接或合理关联。在与原处理目的无直接或合理关联的场景下,我们收集、使用您的个人信息,会再次进行告知,并征得您的同意。
|
||||
(5)依法豁免征得同意收集和使用的个人信息
|
||||
请您理解,在下列情形中,根据法律法规及相关国家标准,我们收集和使用您的个人信息无需征得您的授权同意:
|
||||
a) 为订立、履行您作为一方当事人的合同所必需;
|
||||
b) 为履行法定职责或者法定义务所必需;
|
||||
c) 为应对突发公共卫生事件,或者紧急情况下为保护自然人的生命健康和财产安全所必需;
|
||||
d) 为公共利益实施新闻报道、舆论监督等行为,在合理的范围内处理您的个人信息;
|
||||
e) 依照法律规定在合理的范围内处理您自行公开或者其他已经合法公开的个人信息;
|
||||
f) 法律、行政法规规定的其他情形。
|
||||
(6)敏感个人信息
|
||||
我们收集和使用您的敏感个人信息的,将以弹窗等形式取得您的单独同意。您理解并且同意您的个人敏感信息可按本政策所述的目的和方式进行处理。我们会在得到您的同意后收集和使用您的敏感信息以实现与平台在线业务相关的功能,并允许您对这些敏感信息的收集与使用做出不同意的选择,但是拒绝使用这些信息会影响您使用相关功能。
|
||||
如我们停止运营柚助手产品或服务,我们将及时停止继续收集您个人信息,将停止运营的通知以逐一送达或公告的形式通知您,并对我们所持有的与已关停业务相关的个人信息进行删除或匿名化处理。涉及未满14周岁的未成年人个人信息的,我们会并将停止运营的通知及时告知未成年人监护人。
|
||||
(7)当我们要将信息用于本政策未载明的其他用途时,会事先征求您的同意,当我们要将基于特定目的收集而来的信息用于其他目的是,会事先征求您的同意。请您知悉,我们向您提供的产品与/或服务将不断更新变化。如果您选择使用本隐私政策中尚未列明的其他产品与/或服务时,我们会在收集您的个人信息前通过协议、页面提示等方式向您详细说明信息收集的目的、方式、范围并征求您的同意。若您不同意提供前述信息,您可能无法使用该项产品与/或服务,但不影响您使用现有产品与/或服务。
|
||||
|
||||
二、我们如何使用Cookie和同类技术
|
||||
为确保网站正常运转、为您获得更轻松的访问体验,我们会在您的计算机或移动设备上存储名为Cookies的小数据文件。Cookies通常包含标识符、站点名称以及一些号码和字符。借助于Cookies,网站能够记住您的选择,存储您的偏好等数据。
|
||||
我们不会将Cookies用于本政策所述目的之外的任何用途。您可根据自己的偏好管理或删除Cookies。您可以清除计算机或手机上保存的所有Cookies,大部分网络浏览器都设有阻止Cookies的功能。如果您这么做,则需要在每一次访问我们的网站时亲自更改用户设置。
|
||||
第三方合作伙伴通过Cookies收集和使用您的信息不受本政策约束,而是受到其自身的信息保护声明约束,我们不对第三方的Cookies或同类技术承担责任。
|
||||
|
||||
三、我们如何共享、转移、公开披露您的个人信息
|
||||
(一) 对外提供
|
||||
如您主动、自愿要求我们向第三方提供您的个人信息的,我们将基于您同意的目的,在相应页面中以适当方式告知您个人信息接收方的名称和联系方式。例如,您主动要求使用健康柚平台账户登录第三方产品或服务的,我们或第三方将在关联登录页面告知您为此目的,健康柚平台需向第三方提供的个人信息以及第三方的名称和联系方式。
|
||||
(一)共享
|
||||
如您主动、自愿要求我们向第三方提供您的个人信息的,我们将基于您同意的目的,在相应页面中以适当方式告知您个人信息接收方的名称和联系方式。例如,您主动要求使用柚助手平台账户登录第三方产品或服务的,我们或第三方将在关联登录页面告知您为此目的,柚助手平台需向第三方提供的个人信息以及第三方的名称和联系方式。
|
||||
我们基于以下情况,可能会对外共享您的个人信息:
|
||||
1、在法定情形下的共享:我们可能会根据法律法规规定,或按政府主管部门的强制性要求,对外共享您的个人信息。我们为履行法定义务而向第三方提供您的个人信息的,我们将在相应页面中以适当方式告知您个人信息接收方的名称和联系信息。
|
||||
2、与关联公司间共享:我们只会共享必要的个人信息(如为便于您通过统一账号使用我们关联公司产品或服务,我们会向关联公司共享您必要的账户信息),如果我们共享您的个人敏感信息或关联公司改变个人信息的使用及处理目的,将在此就分享目的、范围、形式等必要内容征求您的授权统一。
|
||||
3、基于向您提供健康柚平台服务所必需。部分服务可能是我们的关联公司和合作机构(“授权合作伙伴”)或我们与第三方共同向您提供。因此,为向您提供健康柚平台服务,我们必需将您的个人信息提供给我们的关联公司及业务合作伙伴。例如,在某些情况下,我们必须与物流服务提供商共享您的收货信息才能安排配送。我们仅会出于合法、正当、必要、特定、明确的目的共享您的个人信息,并且只会共享提供服务所必要的个人信息。我们的合作伙伴无权将共享的个人信息用于任何其他用途。
|
||||
3、基于向您提供柚助手平台服务所必需。部分服务可能是我们的关联公司和合作机构(“授权合作伙伴”)或我们与第三方共同向您提供。因此,为向您提供柚助手平台服务,我们必需将您的个人信息提供给我们的关联公司及业务合作伙伴。例如,在某些情况下,我们必须与物流服务提供商共享您的收货信息才能安排配送。我们仅会出于合法、正当、必要、特定、明确的目的共享您的个人信息,并且只会共享提供服务所必要的个人信息。我们的合作伙伴无权将共享的个人信息用于任何其他用途。
|
||||
目前,我们的授权合作伙伴包含以下类型:
|
||||
1)技术服务供应商。我们可能会将您的个人信息共享给支持我们功能的第三方。这些支持包括为我们提供基础设施技术服务、安全保障服务、代表我们发出短信的通讯服务供应商、物流配送服务、数据处理等。我们共享这些信息的目的是可以实现我们产品或服务的功能,比如我们必须与物流服务提供商共享您的收货信息才能安排送货。
|
||||
2)分析服务类的授权合作伙伴。在征得您的许可后,我们可能将不能识别您的个人身份信息的统计或匿名信息共享给提供分析服务的合作伙伴。对于分析数据的伙伴,我们仅会向这些合作伙伴提供不能识别个人身份的统计或匿名信息。
|
||||
@ -79,6 +99,7 @@ export default `
|
||||
4、依照本法规定在合理的范围内处理个人自行公开或者其他已经合法公开的个人信息;
|
||||
5、法律、行政法规规定的其他情形。
|
||||
6、已经匿名化处理的您的个人信息,指经过处理无法识别特定自然人且不能复原。
|
||||
|
||||
四、我们如何保存和保护您的个人信息
|
||||
(一) 个人信息的保存
|
||||
1、保存期限:如您删除或通过系统设置拒绝我们对您的个人信息进行收集,或者在您申请注销账号经核实身份注销后,我们将停止使用并删除或匿名化处理您的个人信息。我们的个人信息保存期限为实现目的所需及法律法规要求的最短时间,但法律法规另有规定或者您另行授权同意的除外。
|
||||
@ -92,26 +113,27 @@ export default `
|
||||
2、安全提醒
|
||||
1)互联网并非绝对安全的环境,我们强烈建议您不要通过电子邮件、即使通讯及与其他用户交流等未加密的方式发送个人信息。请登陆时使用手机验证码,协助我们保证您的账号安全。
|
||||
2)请使用复杂密码,协助我们保证您的账号安全。我们将尽力保障您发送给我们的任何信息的安全性。如果我们的物理、技术、或管理防护设施遭到破坏,导致信息被非授权访问、公开披露、篡改、或毁坏,导致您的合法权益受损,我们将承担相应的法律责任。
|
||||
3)您在使用健康柚平台及服务时,请谨慎发表、上传可能会涉及您或他人隐私的信息,也勿将该等信息通过健康柚平台的服务传播给他人,若因您该等行为引起您或他人的隐私泄露,由您自行承担责任。
|
||||
4)请勿在使用健康柚平台服务时公开透露自己的各类财产账户、银行卡、信用卡、第三方支付账户及对应密码等重要资料,否则由此带来的损失由您自行承担责任。
|
||||
5)健康柚平台一旦发现假冒、仿冒、盗用他人名义进行平台认证的,健康柚有权立即删除用户信息并有权在用户提供充分证据前禁止其使用平台服务。
|
||||
3)您在使用柚助手平台及服务时,请谨慎发表、上传可能会涉及您或他人隐私的信息,也勿将该等信息通过柚助手平台的服务传播给他人,若因您该等行为引起您或他人的隐私泄露,由您自行承担责任。
|
||||
4)请勿在使用柚助手平台服务时公开透露自己的各类财产账户、银行卡、信用卡、第三方支付账户及对应密码等重要资料,否则由此带来的损失由您自行承担责任。
|
||||
5)柚助手平台一旦发现假冒、仿冒、盗用他人名义进行平台认证的,柚助手有权立即删除用户信息并有权在用户提供充分证据前禁止其使用平台服务。
|
||||
3、安全事件通知
|
||||
1)我们会制定相应的网络安全事件应急预案,及时处置系统漏洞、计算机病毒、网络攻击、网络侵入等安全风险,在发生危害网络安全的事件时,我们会立即启动应急预案,采取相应的补救措施。
|
||||
2)在不幸发生个人信息安全事件后,我们将按照法律法规的要求,及时向您告知:安全事件的基本情况和可能的影响、我们已采取或将要采取的处置措施、您可自主防范和降低风险的建议、对您的补救措施等。我们将及时将事件相关情况以邮件、信函、电话、推送通知等方式告知您,难以逐一告知个人信息主体时,我们会采取合理、有效的方式发布公告。
|
||||
同时,我们还将按照监督部门要求,主动上报个人信息安全事件的处置情况。
|
||||
请您理解,根据法律法规的规定,如果我们采取的措施能够有效避免信息泄露、篡改、丢失造成危害的,除非监管部门要求向您通知,我们可以选择不向您通知该个人信息安全事件。
|
||||
3)如您发现自己的个人信息泄密,尤其是您的账户及密码发生泄露,请您立即通过健康柚平台或本隐私政策提供的联系方式联络我们,以便我们采取相应措施。
|
||||
五、您的权利
|
||||
3) 如您发现自己的个人信息泄密,尤其是您的账户及密码发生泄露,请您立即通过柚助手平台或本隐私政策提供的联系方式联络我们,以便我们采取相应措施。
|
||||
|
||||
五、管理您的个人信息
|
||||
按照中国相关的法律、法规、标准,以及其他国家、地区的通行做法,我们保障您对自己的个人信息行使以下权利:
|
||||
(一) 访问您的个人信息
|
||||
您有权访问您的个人信息,法律法规规定的例外情况除外。如果您想行使数据访问权,可以通过以下方式自行访问:
|
||||
档案信息:小程序中,您可以通过【档案管理】中新增、查阅、删除您的档案信息。
|
||||
咨询记录:小程序中,您可以通过【咨询】列表查阅历史咨询记录;
|
||||
问卷信息:小程序中,您可以通过【我的问卷】查阅历史填写的问卷记录;
|
||||
个人信息:小程序中,您可以通过【工作台】中点击个人头像,查看、编辑、删除个人信息。
|
||||
咨询记录:小程序中,您可以通过【消息】列表查阅历史咨询记录;
|
||||
病历记录:小程序中,您可以通过【病历】查阅您团队在管的患者病历信息;
|
||||
(二) 更正您的个人信息
|
||||
当您发现我们处理的关于您的个人信息有错误时,您有权通过客服提出更正申请。
|
||||
(三) 删除您的个人信息
|
||||
如果您决定不再使用我们平台,需要注销账户请联系客服,进入个人中心扫描客户二维码。
|
||||
如果您决定不再使用我们平台,需要注销账户请联系客服,进入工作台-更多-联系客服。
|
||||
(四) 改变您授权同意的范围
|
||||
您可以通过解除绑定、删除信息、关闭设备功能、修改个人设置、联系客服等方式改变您授权我们继续收集个人信息的范围或随时撤回您的授权(包括对第三方共享信息授权)。
|
||||
(五) 注销帐号
|
||||
@ -133,15 +155,18 @@ export default `
|
||||
6、响应您的请求将导致您或其他个人、组织的合法权益受到严重损害的;
|
||||
7、涉及商业秘密的。
|
||||
(九) 获得解释的权利
|
||||
您有权要求我们就个人信息处理规则作出解释说明。您可以通过第九部分中的联系方式与我们取得联系。
|
||||
您有权要求我们就个人信息处理规则作出解释说明。您可以通过微信小程序:进入工作台-更多-联系客服与我们取得联系。
|
||||
|
||||
六、我们如何处理未成年人的个人信息
|
||||
6.1如果没有父母或其他监护人的统一,儿童不得创建自己的用户账户。如您为儿童的,我们要求您请您的父母或其他监护人仔细阅读本政策,并在征得您的父母或其他监护人同意的前提下使用我们的服务或产品或向我们提供信息。
|
||||
6.2对于经父母或其他监护人同意使用我们的服务或产品而收集儿童个人信息的情况,我们只会在法律法规允许、父母或其他监护人明确同意或者保护儿童所必要的情况下使用、共享、转让或披露此信息。
|
||||
|
||||
七、您的个人信息如何在全球范围转移
|
||||
我们在中华人民共和国境内运营中收集和产生的个人信息,储存在中国境内,一下情形除外:
|
||||
1. 法律法规有明确规定。
|
||||
2. 获得您的明确授权且经过国家安全相关审查的。
|
||||
针对以上情形,我们会确保依据本政策对您的个人信息提供足够的保护。
|
||||
|
||||
八、本隐私政策更新及通知
|
||||
我们的隐私政策可能变更。
|
||||
未经您明确同意,我们不会削减您按照本隐私政策所应享有的权利。我们会在本页面上发布对本政策所做的任何变更并取得您的同意。
|
||||
@ -154,13 +179,15 @@ export default `
|
||||
5、我们负责处理个人信息安全的责任部门、联络方式及投诉渠道发生变化;
|
||||
6、个人信息安全影响评估报告表明存在高风险时。
|
||||
我们还会将本政策的旧版本存档,供您查阅。
|
||||
|
||||
九、如何联系我们
|
||||
如您对本政策内容有任何疑问、意见或建议,或发现个人信息可能被泄露的,您可以通过以下方式与我们联系,一般情况下我们将在15个工作日内回复您的请求。可以通过“我的-联系客服”联系我们。或邮寄至下列地址:
|
||||
如您对本政策内容有任何疑问、意见或建议,或发现个人信息可能被泄露的,您可以通过以下方式与我们联系,一般情况下我们将在15个工作日内回复您的请求。可以通过“工作台-更多-联系客服”联系我们。或邮寄至下列地址:
|
||||
公司名称:杭州柚康科技有限公司
|
||||
法定代表人:王荣波
|
||||
联系地址:中国浙江省杭州西湖区塘苗路1号,2号楼4楼。
|
||||
|
||||
十、争议解决
|
||||
因本政策以及我们处理您个人信息事宜引起的任何争议,您可随时联系我公司个人信息保护相关负责人要求给出回复,如果您对我们的回复不满意的,认为我们的个人信息处理行为严重损害了您的合法权益的,您还可以通过向本隐私政策服务提供商健康柚所在地【杭州市】有管辖权的人民法院提起诉讼来寻求解决方案。
|
||||
感谢您对健康柚平台以及健康柚产品和服务的信任和使用!
|
||||
因本政策以及我们处理您个人信息事宜引起的任何争议,您可随时联系我公司个人信息保护相关负责人要求给出回复,如果您对我们的回复不满意的,认为我们的个人信息处理行为严重损害了您的合法权益的,您还可以通过向本隐私政策服务提供商柚助手所在地【杭州市】有管辖权的人民法院提起诉讼来寻求解决方案。
|
||||
感谢您对柚助手平台以及柚助手产品和服务的信任和使用!
|
||||
|
||||
`
|
||||
Loading…
x
Reference in New Issue
Block a user