Compare commits
1 Commits
dev-1.0
...
dev-ykt-wx
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ace7bd8fb |
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<common-cell :name="name" :required="required">
|
||||
<view class="form-content__wrapper" @click="select()">
|
||||
<view class="flex-main-content truncate" :class="str ? '' : 'form__placeholder'">
|
||||
{{ str || placeholder }}
|
||||
</view>
|
||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||
</view>
|
||||
</common-cell>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { set } from '@/utils/cache';
|
||||
|
||||
import commonCell from '../common-cell.vue';
|
||||
|
||||
const emits = defineEmits(['change']);
|
||||
const props = defineProps({
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
name: {
|
||||
default: ''
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
default: ''
|
||||
},
|
||||
})
|
||||
|
||||
const placeholder = computed(() => `请输入${props.name || ''}`)
|
||||
const value = computed(() => props.form && props.form && Array.isArray(props.form[props.title]) ? props.form[props.title] : []);
|
||||
const str = computed(() => value.value.join(','))
|
||||
|
||||
function change(e) {
|
||||
emits('change', {
|
||||
title: props.title,
|
||||
value: e
|
||||
})
|
||||
}
|
||||
|
||||
function select() {
|
||||
const eventName = `onChangeMultDisease_${+new Date()}`
|
||||
uni.$once(eventName, change);
|
||||
set('diagnosis-list-selections', value.value)
|
||||
uni.navigateTo({
|
||||
url: `/pages/library/diagnosis-list?eventName=${eventName}`
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
@import '../cell-style.css';
|
||||
</style>
|
||||
110
components/form-template/form-cell/form-upload.vue
Normal file
110
components/form-template/form-cell/form-upload.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<view class="textarea-row">
|
||||
<view class="form-row__label">
|
||||
{{ name }}<text v-if="required" class="form-cell--required"></text>
|
||||
</view>
|
||||
<view class="flex flex-wrap">
|
||||
<view v-for="(file, idx) in files" :key="idx" class="upload-item mt-10">
|
||||
<image v-if="file.isImage" :src="file.url" class="w-full h-full"></image>
|
||||
<image v-else src="/static/file.svg" class="w-full h-full"></image>
|
||||
<uni-icons type="close" :size="32" color="red" class="remove-icon" @click="remove(idx)"></uni-icons>
|
||||
</view>
|
||||
<view v-if="value.length < 3"
|
||||
class="upload-item border-primary mt-10 flex items-center justify-center text-primary" @click="addImage()">
|
||||
<uni-icons type="camera" :size="40" color="#0074ff"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { upload } from '@/utils/http';
|
||||
import { loading, hideLoading, toast } from '@/utils/widget';
|
||||
|
||||
const emits = defineEmits(['change']);
|
||||
const props = defineProps({
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
name: {
|
||||
default: ''
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
default: ''
|
||||
},
|
||||
disableChange: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const value = computed(() => Array.isArray(props.form[props.title]) ? props.form[props.title] : [])
|
||||
const files = computed(() => value.value.map(i => {
|
||||
return {
|
||||
url: i.url,
|
||||
name: i.name,
|
||||
type: i.type,
|
||||
isImage: /image/i.test(i.type)
|
||||
}
|
||||
}))
|
||||
|
||||
function addImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: async (res) => {
|
||||
loading();
|
||||
const result = await upload(res.tempFilePaths[0]);
|
||||
hideLoading();
|
||||
if (result) {
|
||||
change([...value.value, { url: result, type: 'image/png' }])
|
||||
} else {
|
||||
toast('上传失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function change(value) {
|
||||
emits('change', {
|
||||
title: props.title,
|
||||
value: value
|
||||
})
|
||||
}
|
||||
|
||||
function remove(idx) {
|
||||
const value = [...files.value];
|
||||
value.splice(idx, 1);
|
||||
change(value)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.upload-item {
|
||||
position: relative;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 16rpx;
|
||||
box-sizing: border-box;
|
||||
margin-right: 30rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.textarea-row {
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
|
||||
.remove-icon {
|
||||
position: absolute;
|
||||
top: -30rpx;
|
||||
right: -30rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
@ -1,11 +1,21 @@
|
||||
<template>
|
||||
<form-datepicker v-if="attrs.type === 'date'" v-bind="attrs" :form="form" :disableChange="disableChange" @change="change" />
|
||||
<form-input v-else-if="attrs.type === 'input'" v-bind="attrs" :form="form" :disableChange="disableChange" @change="change" />
|
||||
<form-radio v-else-if="attrs.type === 'radio'" v-bind="attrs" :form="form" :disableChange="disableChange" @change="change" />
|
||||
<form-region v-else-if="attrs.type === 'region'" v-bind="attrs" :form="form" :disableChange="disableChange" @change="change" />
|
||||
<form-select v-else-if="attrs.type === 'select'" v-bind="attrs" :form="form" :disableChange="disableChange" @change="change" />
|
||||
<form-datepicker v-if="attrs.type === 'date'" v-bind="attrs" :form="form" :disableChange="disableChange"
|
||||
@change="change" />
|
||||
<form-input v-else-if="attrs.type === 'input'" v-bind="attrs" :form="form" :disableChange="disableChange"
|
||||
@change="change" />
|
||||
<form-radio v-else-if="attrs.type === 'radio'" v-bind="attrs" :form="form" :disableChange="disableChange"
|
||||
@change="change" />
|
||||
<form-region v-else-if="attrs.type === 'region'" v-bind="attrs" :form="form" :disableChange="disableChange"
|
||||
@change="change" />
|
||||
<form-select v-else-if="attrs.type === 'select'" v-bind="attrs" :form="form" :disableChange="disableChange"
|
||||
@change="change" />
|
||||
<form-textarea v-else-if="attrs.type === 'textarea'" v-bind="attrs" :form="form" :disableChange="disableChange"
|
||||
@change="change" />
|
||||
<form-mult-disease v-else-if="attrs.type === 'selfMultipleDiseases'" v-bind="attrs" :form="form"
|
||||
@change="change"></form-mult-disease>
|
||||
<form-mult-disease v-else-if="attrs.type === 'diagnosis'" v-bind="attrs" :form="form"
|
||||
@change="change"></form-mult-disease>
|
||||
<form-upload v-else-if="attrs.type === 'files'" v-bind="attrs" :form="form" @change="change" />
|
||||
|
||||
<!--
|
||||
<form-operation v-else-if="attrs.title === 'surgicalHistory'" v-bind="attrs" :form="form" @change="change"
|
||||
@ -25,6 +35,8 @@ import formRadio from './form-radio.vue';
|
||||
import formDatepicker from './form-datepicker.vue';
|
||||
import formRegion from './form-region.vue';
|
||||
import formTextarea from './form-textarea.vue';
|
||||
import formMultDisease from './form-multiple-diseases.vue';
|
||||
import formUpload from './form-upload.vue';
|
||||
|
||||
defineProps({
|
||||
form: {
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<template v-for="item in formItems" :key="item.title">
|
||||
<form-cell v-if="item.cellType === 'formCellItem'" v-bind="item" :form="form" :disableChange="disabledMap[item.title]"
|
||||
@change="change" @addRule="addRule" />
|
||||
<custom-cell v-else-if="item.cellType === 'customCellItem'" v-bind="item" :form="form"
|
||||
:readonly="disabledMap[item.title]" @change="change" @addRule="addRule" />
|
||||
<form-cell v-if="item.cellType === 'formCellItem'" v-bind="item" :form="form"
|
||||
:disableChange="disabledMap[item.title]" @change="change" @addRule="addRule" />
|
||||
</template>
|
||||
<form-cell />
|
||||
</template>
|
||||
@ -34,9 +32,9 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const formCellType = ['input', 'select', 'date', 'radio', 'region', 'textarea', 'multiSelectAndOther', 'selfMultipleDiseases'];
|
||||
const formCellType = ['input', 'select', 'date', 'radio', 'region', 'textarea', 'multiSelectAndOther', 'selfMultipleDiseases', 'files','diagnosis'];
|
||||
const formCellTitle = ['surgicalHistory'];
|
||||
const customCellType = ['BMI', 'selfMultipleDiseases', 'bloodPressure', 'diagnosis'];
|
||||
const customCellType = ['BMI', 'bloodPressure'];
|
||||
const disabledMap = computed(() => props.disableTitles.reduce((m, i) => {
|
||||
if (typeof i === 'string' && i.trim()) {
|
||||
m[i] = true;
|
||||
|
||||
58
pages.json
58
pages.json
@ -15,7 +15,65 @@
|
||||
{
|
||||
"path": "pages/work/work",
|
||||
"style": {
|
||||
<<<<<<< HEAD
|
||||
"navigationBarTitleText": "柚健康"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/archive-manage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "档案管理"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/edit-archive",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增档案"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/article-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康宣教"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/health/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/health/record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/library/diagnosis-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择诊断"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/team-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队介绍"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/homepage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人主页"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/friend",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加好友"
|
||||
=======
|
||||
"navigationBarTitleText": "工作台"
|
||||
>>>>>>> e838f8af157c539c27370cbdd7d4a3bbc09e184b
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
106
pages/archive/archive-manage.vue
Normal file
106
pages/archive/archive-manage.vue
Normal file
@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<full-page :customScroll="empty">
|
||||
<view v-if="customers.length === 0" class="flex items-center justify-center h-full">
|
||||
<empty-data />
|
||||
</view>
|
||||
<view class="p-15">
|
||||
<view v-for="customer in customers" :key="customer._id" class="bg-white rounded shadow-lg mb-10">
|
||||
<view class="flex items-center px-15 py-12 border-b">
|
||||
<view class="flex-shrink-0 mr-5 text-lg text-dark font-semibold">{{ customer.name }}</view>
|
||||
<view v-if="customer.relationship"
|
||||
class="flex-shrink-0 mr-5 border-primary text-primary px-10 text-sm leading-normal rounded-sm">
|
||||
{{ customer.relationship }}
|
||||
</view>
|
||||
<view class="flex-grow mr-5"></view>
|
||||
<view v-if="enableHis && customer.isConnectHis"
|
||||
class="px-15 py-5 text-sm leading-normal bg-success text-white rounded-sm">
|
||||
未关联档案
|
||||
</view>
|
||||
<view v-else-if="enableHis" class="px-15 py-5 text-sm leading-normal bg-warning text-white rounded-sm">未关联档案
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-15 py-12 border-b">
|
||||
<view class="text-base leading-normal">
|
||||
<text v-if="customer.sex"> {{ customer.sex }}</text>
|
||||
<text v-if="customer.sex && customer.age">/</text>
|
||||
<text v-if="customer.age"> {{ customer.age }}岁</text>
|
||||
<text v-if="customer.sex || customer.age">,</text>
|
||||
<text v-if="customer.mobile"> {{ customer.mobile }}</text>
|
||||
</view>
|
||||
<view class="text-base leading-normal">证件号:{{ customer.idCard || '--' }}</view>
|
||||
</view>
|
||||
<view class="px-15 py-12 flex justify-end">
|
||||
<view v-if="enableHis && !customer.isConnectHis" class="mr-10 text-base text-primary">关联档案</view>
|
||||
<view class="mr-10 text-base text-success" @click="changeArchive(customer)">完善个人信息</view>
|
||||
<view class="mr-10 text-base text-danger" @click="unBindArchive(customer)">删除档案</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<button-footer confirmText="新增档案" :showCancel="false" @confirm="addArchive()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia'
|
||||
import useGuard from '@/hooks/useGuard';
|
||||
import useAccount from '@/store/account';
|
||||
import api from '@/utils/api';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
import EmptyData from '@/components/empty-data.vue';
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
import { confirm, toast } from '../../utils/widget';
|
||||
|
||||
const empty = ref(false)
|
||||
const { useLoad, useShow } = useGuard();
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const corpId = ref('');
|
||||
const teamId = ref('');
|
||||
const enableHis = ref(false);
|
||||
const customers = ref([]);
|
||||
|
||||
function addArchive() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/archive/edit-archive?teamId=${teamId.value}&corpId=${corpId.value}`
|
||||
})
|
||||
}
|
||||
|
||||
function changeArchive(customer) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/archive/edit-archive?teamId=${teamId.value}&corpId=${corpId.value}&id=${customer._id}`
|
||||
})
|
||||
}
|
||||
|
||||
async function getMembers() {
|
||||
const res = await api('getTeamCustomers', { corpId: corpId.value, teamId: teamId.value, miniAppId: account.value.openid });
|
||||
customers.value = res && Array.isArray(res.data) ? res.data : [];
|
||||
// enableHis.value = res && typeof res.enableHis === 'boolean' ? res.enableHis : false;
|
||||
if (!res || !res.data) {
|
||||
toast(res?.message || '获取档案信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function unBindArchive(customer) {
|
||||
await confirm('确定删除档案吗?')
|
||||
const res = await api('unbindMiniAppArchive', { id: customer._id, corpId: corpId.value, teamId: teamId.value, miniAppId: account.value.openid });
|
||||
if (res && res.success) {
|
||||
await toast('删除成功');
|
||||
getMembers();
|
||||
} else {
|
||||
toast(res?.message || '删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
useLoad(options => {
|
||||
teamId.value = options.teamId;
|
||||
corpId.value = options.corpId;
|
||||
})
|
||||
|
||||
useShow(() => {
|
||||
if (teamId.value && corpId.value) {
|
||||
getMembers()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
98
pages/archive/bind-popup.vue
Normal file
98
pages/archive/bind-popup.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<uni-popup ref="popup" type="center" :mask-click="false">
|
||||
<view class="bg-white rounded overflow-hidden" style="width: 690rpx;">
|
||||
<view class="flex items-center justify-between px-15 py-12 border-b">
|
||||
<view class="text-lg font-semibold text-dark">通过档案手机号验证</view>
|
||||
<uni-icons type="closeempty" :size="24" color="#999" @click="close"></uni-icons>
|
||||
</view>
|
||||
<view class="px-15 pt-15 text-base text-dark">
|
||||
您在“{{ corpName }}”医客通平台已存在档案,请选择档案绑定。
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="popup-content-scroll">
|
||||
<view class="px-15 py-12">
|
||||
<view v-for="customer in customers" :key="customer._id"
|
||||
class="flex items-center p-10 mb-10 rounded-sm bg-gray" @click="id = customer._id">
|
||||
<view class="flex-grow w-0 mr-5 text-base leading-normal text-dark">
|
||||
<view class="flex items-center">
|
||||
<view class="flex-shrink-0 min-w-60">姓名:</view>
|
||||
<view>{{ customer.name }}</view>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view class="flex-shrink-0 min-w-60">性别:</view>
|
||||
<view>{{ customer.sex || '--' }}</view>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view class="flex-shrink-0 min-w-60">手机号:</view>
|
||||
<view>{{ customer.mobile }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<image class="check-icon" :src="`/static/form/${id === customer._id ? 'checked' : 'uncheck'}.svg`"></image>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="flex justify-end px-15 pb-10">
|
||||
<view class="mr-5 text-base text-dark">以上档案都不对,可以:</view>
|
||||
<view class="px-10 text-base leading-normal text-primary border-auto rounded-sm" @click="close()">新增档案</view>
|
||||
</view>
|
||||
<view class="footer-buttons">
|
||||
<button-footer hideden-shadow confirmText="确定" :showCancel="false" @confirm="confirm()" />
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
|
||||
const emits = defineEmits(['close', 'confirm'])
|
||||
const props = defineProps({
|
||||
corpName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
customers: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const popup = ref()
|
||||
const id = ref('')
|
||||
|
||||
function close() {
|
||||
emits('close')
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
if (props.customers.some(i => i._id === id.value && id.value)) {
|
||||
emits('confirm', id.value)
|
||||
} else {
|
||||
toast('请选择档案')
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.visible, n => {
|
||||
if (n) {
|
||||
popup.value && popup.value.open()
|
||||
} else {
|
||||
popup.value && popup.value.close()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.min-w-60 {
|
||||
min-width: 120rpx;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
</style>
|
||||
180
pages/archive/edit-archive.vue
Normal file
180
pages/archive/edit-archive.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<full-page v-if="!visible" :customScroll="empty">
|
||||
<view v-if="formItems.length === 0" class="flex items-center justify-center h-full">
|
||||
<empty-data />
|
||||
</view>
|
||||
<view v-else class="p-15">
|
||||
<view class="bg-white rounded shadow-lg">
|
||||
<form-template ref="tempRef" :disableTitles="disableTitles" :items="formItems" :form="formData"
|
||||
@change="change($event)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template #footer>
|
||||
<button-footer confirmText="新增档案" :showCancel="false" @confirm="confirm()" />
|
||||
</template>
|
||||
</full-page>
|
||||
<bind-popup :customers="customers" :corpName="corpName" :visible="visible" @close="visible = false"
|
||||
@confirm="bindArchive($event)" />
|
||||
<verify-popup :visible="verifyVisible" @close="verifyVisible = false" />
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import dayjs from 'dayjs';
|
||||
import useGuard from '@/hooks/useGuard';
|
||||
import useAccount from '@/store/account';
|
||||
import api from '@/utils/api';
|
||||
import { toast } from '@/utils/widget';
|
||||
import validate from '@/utils/validate';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
import EmptyData from '@/components/empty-data.vue';
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
import bindPopup from './bind-popup.vue';
|
||||
import verifyPopup from './verify-popup.vue';
|
||||
import formTemplate from '@/components/form-template/index.vue';
|
||||
|
||||
const empty = ref(false)
|
||||
const { useLoad } = useGuard();
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const corpId = ref('');
|
||||
const corpName = ref('');
|
||||
const customer = ref({});
|
||||
const customerId = ref('');
|
||||
const customers = ref([]);
|
||||
const disableTitles = ref(['mobile']);
|
||||
const form = ref({});
|
||||
const formItems = ref([]);
|
||||
const loading = ref(false);
|
||||
const teamId = ref('');
|
||||
const tempRef = ref(null);
|
||||
const customerArchive = ref(null);
|
||||
const verifyVisible = ref(false);
|
||||
const visible = ref(false);
|
||||
|
||||
const formData = computed(() => ({ ...customer.value, ...form.value }));
|
||||
|
||||
function back() {
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.redirectTo({ url: `/pages/home/home?corpId=${corpId.value}&teamId=${teamId.value}` })
|
||||
}
|
||||
}
|
||||
|
||||
function change({ title, value }) {
|
||||
if (title) {
|
||||
form.value[title] = value;
|
||||
}
|
||||
if (title !== 'idCard') return;
|
||||
const [isIdCard, birthday, gender] = validate.isChinaId(value);
|
||||
if (isIdCard) {
|
||||
form.value.birthday = birthday;
|
||||
form.value.sex = gender == 'MALE' ? '男' : '女';
|
||||
const age = dayjs().diff(birthday, 'year');
|
||||
form.value.age = Math.max(1, age);
|
||||
}
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
if (!tempRef.value.verify() || Object.keys(form.value).length === 0) return;
|
||||
if (customerId.value) {
|
||||
updateArchive();
|
||||
} else {
|
||||
addArchive();
|
||||
}
|
||||
}
|
||||
|
||||
async function addArchive() {
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
const params = {
|
||||
...form.value,
|
||||
addMethod: 'customerManual',
|
||||
teamId: teamId.value,
|
||||
corpId: corpId.value,
|
||||
mobile: account.value.mobile,
|
||||
miniAppId: account.value.openid
|
||||
}
|
||||
loading.value = false;
|
||||
const res = await api('addCustomer', { params });
|
||||
if (res && res.success) {
|
||||
back()
|
||||
} else {
|
||||
toast(res?.message || '新增档案失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function bindArchive(customerId) {
|
||||
const res = await api('bindMiniAppArchive', { id: customerId, corpId: corpId.value, teamId: teamId.value, miniAppId: account.value.openid });
|
||||
if (res && res.success) {
|
||||
await toast('绑定成功');
|
||||
uni.reLaunch({ url: `/pages/home/home?corpId=${corpId.value}&teamId=${teamId.value}` })
|
||||
} else {
|
||||
toast(res?.message || '绑定失败');
|
||||
}
|
||||
// customerArchive.value = customers.value.find(item => item.customerId === customerId);
|
||||
// verifyVisible.value = true;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
if (customerId.value) {
|
||||
await getCustomer()
|
||||
} else {
|
||||
const res = await getArchives();
|
||||
if (res.length > 0) {
|
||||
visible.value = true;
|
||||
}
|
||||
}
|
||||
await getBaseForm();
|
||||
}
|
||||
|
||||
async function getArchives() {
|
||||
const res = await api('getUnbindMiniAppCustomers', { corpId: corpId.value, mobile: account.value.mobile });
|
||||
if (res && res.success) {
|
||||
corpName.value = res.corpName;
|
||||
customers.value = Array.isArray(res.data) ? res.data : [];
|
||||
} else {
|
||||
toast(res?.message || '查询档案信息失败');
|
||||
return Promise.reject()
|
||||
}
|
||||
return customers.value
|
||||
}
|
||||
|
||||
async function getBaseForm() {
|
||||
const res = await api('getTeamBaseInfo', { corpId: corpId.value, teamId: teamId.value });
|
||||
if (res && res.success) {
|
||||
formItems.value = Array.isArray(res.data) ? res.data : [];
|
||||
} else {
|
||||
toast(res?.message || '查询失败');
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
|
||||
async function getCustomer() {
|
||||
const res = await api('getCustomerByCustomerId', { customerId: customerId.value });
|
||||
console.log(res.data)
|
||||
if (res && res.success && res.data) {
|
||||
customer.value = res.data;
|
||||
} else {
|
||||
await toast(res?.message || '查询档案信息失败');
|
||||
uni.navigateBack();
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(options => {
|
||||
customerId.value = options.id || '';
|
||||
uni.setNavigationBarTitle({ title: customerId.value ? '编辑档案' : '新增档案' })
|
||||
})
|
||||
useLoad(options => {
|
||||
teamId.value = options.teamId;
|
||||
corpId.value = options.corpId;
|
||||
init();
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped></style>
|
||||
108
pages/archive/verify-popup.vue
Normal file
108
pages/archive/verify-popup.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<uni-popup ref="popup" type="center" :mask-click="false">
|
||||
<view class="bg-white rounded overflow-hidden" style="width: 690rpx;">
|
||||
<view class="flex items-center justify-between px-15 py-12 border-b">
|
||||
<view class="text-lg font-semibold text-dark">身份校验</view>
|
||||
<uni-icons type="closeempty" :size="24" color="#999" @click="close"></uni-icons>
|
||||
</view>
|
||||
<view class="mt-15 px-15 pt-15 text-base text-dark">
|
||||
请填写档案人身份证号后四位,以确认身份。
|
||||
</view>
|
||||
<view class="relative flex justify-between mt-15 px-15 py-12 overflow-hidden">
|
||||
<view v-for="i in 4" :key="i"
|
||||
class="flex items-center justify-center code-cell text-large font-semibold border-auto text-gray rounded"
|
||||
:class="i === activeIndex ? 'text-primary' : 'text-gray'">
|
||||
<text :class="i === activeIndex ? 'text-primary' : 'text-dark'">{{ validCodes[i - 1] || '' }}</text>
|
||||
</view>
|
||||
<input class="code-input" type="idcard" :focus="focus" @focus="onFocus" @blur="onBlur" v-model="codes" />
|
||||
</view>
|
||||
<view class="footer-buttons">
|
||||
<button-footer hideden-shadow confirmText="确定" :showCancel="false" @confirm="confirm()" />
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
|
||||
const emits = defineEmits(['close', 'confirm'])
|
||||
const props = defineProps({
|
||||
corpName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
customers: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const popup = ref()
|
||||
const codes = ref('');
|
||||
const validCodes = computed(() => codes.value.replace(/\s/g, '').split('').slice(0, 4));
|
||||
const codeStr = computed(() => validCodes.value.join(''));
|
||||
const activeIndex = computed(() => Math.min(validCodes.value.length + 1, 4))
|
||||
const focus = ref(false)
|
||||
const timer = ref(null)
|
||||
|
||||
function close() {
|
||||
emits('close')
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
if (props.customers.some(i => i._id === id.value && id.value)) {
|
||||
emits('confirm', id.value)
|
||||
} else {
|
||||
toast('请选择档案')
|
||||
}
|
||||
}
|
||||
|
||||
function onFocus() {
|
||||
focus.value = true
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
focus.value = false
|
||||
}
|
||||
|
||||
watch(() => props.visible, n => {
|
||||
if (n) {
|
||||
codes.value = '';
|
||||
popup.value && popup.value.open();
|
||||
focus.value = false;
|
||||
setTimeout(() => focus.value = true, 500)
|
||||
} else {
|
||||
popup.value && popup.value.close()
|
||||
}
|
||||
})
|
||||
|
||||
watch(codes, n => {
|
||||
if (timer.value) clearTimeout(timer.value);
|
||||
timer.value = setTimeout(() => {
|
||||
codes.value = codeStr.value;
|
||||
}, 500)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.code-cell {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
font-size: 52rpx;
|
||||
}
|
||||
|
||||
.code-input {
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
}
|
||||
</style>
|
||||
55
pages/article/article-list.vue
Normal file
55
pages/article/article-list.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<full-page :customScroll="articles.length === 0" @reachBottom="loadMore()">
|
||||
<view v-if="articles.length === 0" class="flex items-center justify-center h-full">
|
||||
<empty-data />
|
||||
</view>
|
||||
<view v-else class="p-15">
|
||||
<view v-for="article in articles" :key="article._id" class="flex px-15 mb-10 py-12 shadow-lg bg-white rounded">
|
||||
<image class="flex-shrink-0 mr-10 cover" :src="article.cover || '/static/book.svg'" />
|
||||
<view class="w-0 flex-grow">
|
||||
<view class="text-base leading-normal font-semibold truncate mb-5">
|
||||
{{ article.title }}
|
||||
</view>
|
||||
<view v-if="article.summary" class="text-base text-gray line-clamp-2">
|
||||
{{ article.summary }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import api from '@/utils/api';
|
||||
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
import EmptyData from '@/components/empty-data.vue';
|
||||
|
||||
const corpId = ref('');
|
||||
const ids = ref('');
|
||||
const articles = ref([]);
|
||||
|
||||
onLoad((options) => {
|
||||
corpId.value = options.corpId;
|
||||
ids.value = options.ids;
|
||||
if (ids.value) {
|
||||
getArticles()
|
||||
}
|
||||
});
|
||||
|
||||
async function getArticles() {
|
||||
const res = await api('getArticleByIds', { corpId: corpId.value, ids: ids.value });
|
||||
articles.value = res && Array.isArray(res.list) ? res.list : [];
|
||||
}
|
||||
|
||||
function loadMore() {
|
||||
console.log('addArchive')
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.cover {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
}
|
||||
</style>
|
||||
278
pages/health/list.vue
Normal file
278
pages/health/list.vue
Normal file
@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<full-page :customScroll="empty">
|
||||
<template #header>
|
||||
<view class="flex items-center bg-white text-base px-15 py-12">
|
||||
<view class="flex-shrink-0 mr-5 text-gray">档案所属人</view>
|
||||
<view class="text-dark">{{ name }}</view>
|
||||
</view>
|
||||
<view class="flex items-center py-12 px-15">
|
||||
<view class="min-w-90 flex items-center bg-white rounded-sm px-10 mr-5" @click="selectType()">
|
||||
<view class="leading-normal text-base py-5 px-10 text-dark mr-5">
|
||||
{{ tempType[type] || '全部' }}
|
||||
</view>
|
||||
<uni-icons type="down" size="16" color="#666" />
|
||||
</view>
|
||||
<view class="w-100 flex items-center bg-white rounded-sm mr-5">
|
||||
<uni-datetime-picker v-model="dates" type="daterange">
|
||||
<view class="flex items-center rounded-sm mr-5 px-10 bg-white">
|
||||
<view class="flex-grow w-0 text-dark truncate leading-normal text-base py-5 mr-5">
|
||||
{{ dates.length ? dates.join(' ~ ') : '时间筛选' }}
|
||||
</view>
|
||||
<uni-icons type="down" size="16" color="#666" />
|
||||
</view>
|
||||
</uni-datetime-picker>
|
||||
</view>
|
||||
<view class="flex-grow"></view>
|
||||
</view>
|
||||
</template>
|
||||
<view v-if="list.length === 0" class="flex items-center justify-center h-full">
|
||||
<empty-data />
|
||||
</view>
|
||||
<view v-else class="px-15 pb-15">
|
||||
<!-- v-for="customer in list" :key="customer._id" -->
|
||||
<view v-for="i in list" :key="i._id" class="bg-white rounded shadow-lg mb-10"
|
||||
@click="toRecord(i.medicalType, i._id)">
|
||||
<view class="flex items-center py-12 px-15">
|
||||
<view class="text-lg font-semibold text-dark mr-5">
|
||||
{{ i.date }}
|
||||
</view>
|
||||
<view v-if="i.medicalType === 'outpatient'"
|
||||
class="mr-5 px-10 leading-normal text-white text-base bg-warning rounded-full">门诊信息</view>
|
||||
<view v-else-if="i.medicalType === 'inhospital'"
|
||||
class="mr-5 px-10 leading-normal text-white text-base bg-success rounded-full">住院信息</view>
|
||||
<view v-if="i.corp === '其他'" class="px-10 leading-normal text-white text-base bg-danger rounded-full">
|
||||
外院
|
||||
</view>
|
||||
<view class="flex-grow"></view>
|
||||
<view class="flex items-center text-base text-primary">
|
||||
<view class="mr-5">查看详情</view>
|
||||
<uni-icons type="right" :size="14" color="#0074ff" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="i.rows.length" class="px-15 border-b">
|
||||
<view v-for="row in i.rows" :key="row.key" class="flex leading-normal mb-10">
|
||||
<view class="flex-shrink-0 mr-5 w-90 text-base text-gray">{{ row.label }}:</view>
|
||||
<view v-if="row.title === 'diagnosisName'" class="w-0 flex-grow text-base text-dark">
|
||||
{{ row.value && row.value.join ? row.value.join(', ') : '' }}
|
||||
</view>
|
||||
<view v-else-if="row.title === 'files'" class="w-0 flex-grow text-base text-dark">
|
||||
<view v-if="row.value && row.value.length" class="w-full flex flex-wrap">
|
||||
<image v-for="file in row.value" :src="file.url" class="file-icon" @click.stop="preview(file)" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="w-0 flex-grow text-base text-dark">
|
||||
{{ row.value }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="flex leading-normal mb-10">
|
||||
<view class="flex-shrink-0 mr-5 w-90 text-base text-gray">临床诊断:</view>
|
||||
<view class="w-0 flex-grow text-base text-dark">龋齿</view>
|
||||
</view>
|
||||
<view class="flex leading-normal mb-10">
|
||||
<view class="flex-shrink-0 mr-5 w-90 text-base text-gray">临床诊断:</view>
|
||||
<view class="w-0 flex-grow text-base text-dark">龋齿</view>
|
||||
</view> -->
|
||||
</view>
|
||||
<view v-if="i.dataSource === 'customerReport'" class="px-15 py-12 text-base text-dark">
|
||||
记录时间:{{ i.createTime }} 记录人:自己
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<button-footer v-if="healthTempList.length" confirmText="+新增健康档案" :showCancel="false" @confirm="addArchive()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import useGuard from '@/hooks/useGuard';
|
||||
import api from '@/utils/api';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
import EmptyData from '@/components/empty-data.vue';
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
import { toast } from '../../utils/widget';
|
||||
|
||||
const tempType = {
|
||||
outpatient: '门诊信息',
|
||||
inhospital: '住院信息'
|
||||
}
|
||||
const empty = ref(false)
|
||||
const { useLoad, useShow } = useGuard();
|
||||
const corpId = ref('');
|
||||
const customerId = ref('');
|
||||
const dates = ref([])
|
||||
const list = ref([]);
|
||||
const name = ref('');
|
||||
const team = ref(null);
|
||||
const type = ref('all');
|
||||
const teamId = ref('');
|
||||
const page = ref(1);
|
||||
const more = ref(false)
|
||||
const loading = ref(false);
|
||||
const qrcode = computed(() => team.value && Array.isArray(team.value.qrcodes) ? team.value.qrcodes[0] : null)
|
||||
const healthTempList = computed(() => qrcode.value && Array.isArray(qrcode.value.healthTempList) ? qrcode.value.healthTempList.map(i => ({ label: tempType[i.templateType], value: i.templateType })).filter(i => i.label) : [])
|
||||
const tempShowField = ref(null);
|
||||
|
||||
const config = {
|
||||
outpatient: ["corp", "deptName", "doctor", "diagnosisName", "files"],
|
||||
inhospital: ["corp", "diagnosisName", "operation", "operationDate", "outhosDate", "files"],
|
||||
};
|
||||
|
||||
function addArchive() {
|
||||
if (healthTempList.value.length > 1) {
|
||||
uni.showActionSheet({
|
||||
title: '选择新增档案类型',
|
||||
itemList: healthTempList.value.map(i => i.label),
|
||||
success: function (res) {
|
||||
toRecord(healthTempList.value[res.tapIndex].value)
|
||||
}
|
||||
});
|
||||
} else {
|
||||
toRecord(healthTempList.value[0].value)
|
||||
}
|
||||
}
|
||||
|
||||
function getTempRows(type, data) {
|
||||
if (config[type] && tempShowField.value && tempShowField.value[type]) {
|
||||
const list = [];
|
||||
config[type].forEach(i => {
|
||||
if (tempShowField.value[type][i]) {
|
||||
list.push({ title: i, label: tempShowField.value[type][i], value: data[i], key: `${i}_${data._id}` })
|
||||
}
|
||||
})
|
||||
return list;
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function toRecord(type, id = '') {
|
||||
uni.navigateTo({
|
||||
url: `/pages/health/record?type=${type}&teamId=${teamId.value}&corpId=${corpId.value}&id=${id}&customerId=${customerId.value}`
|
||||
})
|
||||
}
|
||||
|
||||
function preview(file) {
|
||||
if (/image/i.test(file.type)) {
|
||||
uni.previewImage({
|
||||
urls: [file.url]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function selectType() {
|
||||
const itemList = [{ label: '全部', value: 'all' }, ...healthTempList.value]
|
||||
uni.showActionSheet({
|
||||
title: '选择新增档案类型',
|
||||
itemList: itemList.map(i => i.label),
|
||||
success: function (res) {
|
||||
type.value = itemList[res.tapIndex].value;
|
||||
page.value = 1;
|
||||
getList()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function getTeam() {
|
||||
const res = await api('getTeamData', { teamId: teamId.value, corpId: corpId.value });
|
||||
if (res && res.data) {
|
||||
team.value = res.data;
|
||||
} else {
|
||||
toast(res?.message || '获取团队信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
if (!tempShowField.value) {
|
||||
await getTeamHealthTemps()
|
||||
}
|
||||
if (loading.value) return;
|
||||
const params = {
|
||||
corpId: corpId.value,
|
||||
memberId: customerId.value,
|
||||
page: page.value,
|
||||
pageSize: 20,
|
||||
medicalType: ['outpatient', 'inhospital']
|
||||
}
|
||||
if (type.value !== 'all') {
|
||||
params.medicalType = [type.value]
|
||||
}
|
||||
if (dates.value.length) {
|
||||
params.startTime = dates.value[0];
|
||||
params.endTime = dates.value[1];
|
||||
}
|
||||
loading.value = true;
|
||||
const res = await api('getCustomerMedicalRecord', params);
|
||||
loading.value = false;
|
||||
const arr = res && Array.isArray(res.list) ? res.list.map(i => ({
|
||||
...i,
|
||||
date: i.sortTime && dayjs(i.sortTime).isValid ? dayjs(i.sortTime).format('YYYY-MM-DD') : '',
|
||||
createTime: i.createTime && dayjs(i.createTime).isValid ? dayjs(i.createTime).format('YYYY-MM-DD HH:mm') : '',
|
||||
rows: getTempRows(i.medicalType, i)
|
||||
})) : [];
|
||||
list.value = page.value === 1 ? arr : [...list.value, ...arr];
|
||||
more.value = page.value < res.pages;
|
||||
console.log(list.value)
|
||||
}
|
||||
|
||||
async function getTeamHealthTemps() {
|
||||
const res = await api('getTeamHealthTemps', { teamId: teamId.value, corpId: corpId.value });
|
||||
if (res && res.success) {
|
||||
const arr = res && Array.isArray(res.data) ? res.data : [];
|
||||
tempShowField.value = arr.reduce((m, item) => {
|
||||
const templateList = Array.isArray(item.templateList) ? item.templateList : [];
|
||||
const data = {}
|
||||
templateList.forEach(i => (data[i.title] = i.name));
|
||||
m[item.templateType] = data
|
||||
return m
|
||||
}, {})
|
||||
} else {
|
||||
toast(res?.message || '获取团队健康档案模板失败');
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
|
||||
useLoad(options => {
|
||||
name.value = decodeURIComponent(options.name || '');
|
||||
teamId.value = options.teamId;
|
||||
corpId.value = options.corpId;
|
||||
customerId.value = options.id || '';
|
||||
getTeam()
|
||||
// getTeamHealthTemps();
|
||||
})
|
||||
|
||||
useShow(() => {
|
||||
page.value = 1;
|
||||
getList()
|
||||
})
|
||||
|
||||
watch(dates, n => {
|
||||
page.value = 1;
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.min-w-90 {
|
||||
min-width: 180rpx;
|
||||
}
|
||||
|
||||
.w-90 {
|
||||
width: 160rpx;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 240rpx;
|
||||
}
|
||||
|
||||
.whitespace-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
margin-right: 10rpx;
|
||||
margin-bottom: 10rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
</style>
|
||||
176
pages/health/record.vue
Normal file
176
pages/health/record.vue
Normal file
@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<full-page v-if="!visible">
|
||||
<view v-if="formItems.length === 0" class="flex items-center justify-center h-full">
|
||||
<empty-data />
|
||||
</view>
|
||||
<view v-else class="p-15" :class="canEdit ? '' : 'disabled'">
|
||||
<view class="bg-white rounded shadow-lg">
|
||||
<form-template ref="tempRef" :items="displayFormItems" :form="formData" @change="change($event)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template #footer>
|
||||
<button-footer v-if="canEdit" confirmText="保存" :showCancel="false" @confirm="confirm()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import dayjs from 'dayjs';
|
||||
import useGuard from '@/hooks/useGuard';
|
||||
import useAccount from '@/store/account';
|
||||
import api from '@/utils/api';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
import EmptyData from '@/components/empty-data.vue';
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
import formTemplate from '@/components/form-template/index.vue';
|
||||
|
||||
const { useLoad } = useGuard();
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const corpId = ref('');
|
||||
const customerId = ref('');
|
||||
const id = ref('');
|
||||
const form = ref({});
|
||||
const record = ref({});
|
||||
const formItems = ref([]);
|
||||
const teamId = ref('');
|
||||
const tempRef = ref(null);
|
||||
const type = ref('');
|
||||
const visible = ref(false);
|
||||
const timeTitle = ref('');
|
||||
const canEdit = ref(false)
|
||||
|
||||
const formData = computed(() => ({ ...record.value, ...form.value }));
|
||||
const displayFormItems = computed(() => {
|
||||
const list = formItems.value.map(i => {
|
||||
const item = { ...i };
|
||||
if (i.referenceField && i.referenceValue) {
|
||||
item.displayRow = formData.value[i.referenceField] === i.referenceValue;
|
||||
} else {
|
||||
item.displayRow = true
|
||||
}
|
||||
return item
|
||||
})
|
||||
return list.filter(i => i.displayRow);
|
||||
})
|
||||
|
||||
|
||||
function change({ title, value }) {
|
||||
if (title) {
|
||||
form.value[title] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
if (!tempRef.value.verify() || Object.keys(form.value).length === 0) return;
|
||||
if (id.value) {
|
||||
updateHealthRecord();
|
||||
} else {
|
||||
addHealthRecord()
|
||||
}
|
||||
}
|
||||
|
||||
async function addHealthRecord() {
|
||||
const data = {
|
||||
...form.value,
|
||||
corpId: corpId.value,
|
||||
memberId: customerId.value,
|
||||
// teamId: teamId.value,
|
||||
dataSource: 'customerReport',
|
||||
miniAppId: account.value.openid,
|
||||
medicalType: type.value
|
||||
}
|
||||
if (timeTitle.value) {
|
||||
data.sortTime = data[timeTitle.value] && dayjs(data[timeTitle.value]).isValid() ? dayjs(data[timeTitle.value]).valueOf() : dayjs().valueOf();
|
||||
}
|
||||
const res = await api('addMedicalRecord', data);
|
||||
if (res && res.success) {
|
||||
await toast('保存成功');
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
toast(res?.message || '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function updateHealthRecord() {
|
||||
if (Object.keys(form.value).length === 0) {
|
||||
uni.navigateBack();
|
||||
return;
|
||||
};
|
||||
const data = {
|
||||
...form.value,
|
||||
_id: id.value,
|
||||
corpId: corpId.value,
|
||||
memberId: customerId.value,
|
||||
miniAppId: account.value.openid,
|
||||
medicalType: type.value
|
||||
}
|
||||
if (timeTitle.value) {
|
||||
data.sortTime = formData.value[timeTitle.value] && dayjs(formData.value[timeTitle.value]).isValid() ? dayjs(formData.value[timeTitle.value]).valueOf() : dayjs().valueOf();
|
||||
}
|
||||
const res = await api('updateMedicalRecord', data);
|
||||
if (res && res.success) {
|
||||
await toast('保存成功');
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
toast(res?.message || '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
await getBaseForm();
|
||||
if (id.value) {
|
||||
getRecord()
|
||||
} else {
|
||||
canEdit.value = true
|
||||
}
|
||||
}
|
||||
|
||||
async function getBaseForm() {
|
||||
const res = await api('getTeamHealthTemplate', { corpId: corpId.value, teamId: teamId.value, templateType: type.value });
|
||||
if (res && res.success) {
|
||||
formItems.value = Array.isArray(res.data) ? res.data : [];
|
||||
timeTitle.value = typeof res.timeTitle === 'string' ? res.timeTitle : '';
|
||||
} else {
|
||||
toast(res?.message || '查询失败');
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
|
||||
async function getRecord() {
|
||||
const res = await api('getMedicalRecordById', {
|
||||
corpId: corpId.value,
|
||||
memberId: customerId.value,
|
||||
_id: id.value,
|
||||
medicalType: type.value
|
||||
});
|
||||
if (res && res.success) {
|
||||
record.value = res.record
|
||||
canEdit.value = record.value.dataSource === 'customerReport'
|
||||
} else {
|
||||
await toast(res?.message || '查询失败');
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
onLoad(options => {
|
||||
type.value = options.type;
|
||||
id.value = options.id || '';
|
||||
customerId.value = options.customerId || '';
|
||||
corpId.value = options.corpId;
|
||||
teamId.value = options.teamId;
|
||||
uni.setNavigationBarTitle({ title: id.value ? '编辑健康档案' : '新增健康档案' })
|
||||
})
|
||||
useLoad(options => {
|
||||
init();
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
78
pages/home/article-list.vue
Normal file
78
pages/home/article-list.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<view v-if="articles.length" class="mt-12 px-15 flex items-center justify-between">
|
||||
<view class="text-lg font-semibold text-dark">健康宣教</view>
|
||||
<view class="flex items-center" @click="toList()">
|
||||
<view class="mr-5 text-base text-gray">更多</view>
|
||||
<uni-icons type="right" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-15 mt-10">
|
||||
<view class="shadow-lg bg-white rounded">
|
||||
<view v-for="article in articles" :key="article._id"
|
||||
class="flex px-15 py-12 border-b border-solid border-gray-200">
|
||||
<image class="flex-shrink-0 mr-10 cover" :src="article.cover || '/static/book.svg'" />
|
||||
<view class="w-0 flex-grow">
|
||||
<view class="text-base leading-normal font-semibold truncate mb-5">
|
||||
{{ article.title }}
|
||||
</view>
|
||||
<view v-if="article.summary" class="text-base text-gray line-clamp-2">
|
||||
{{ article.summary }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import api from '@/utils/api';
|
||||
|
||||
const props = defineProps({
|
||||
team: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const articles = ref([])
|
||||
|
||||
const qrcode = computed(() => {
|
||||
const qrcodes = props.team && Array.isArray(props.team.qrcodes) ? props.team.qrcodes : [];
|
||||
return qrcodes[0] || {}
|
||||
})
|
||||
const articleIds = computed(() => {
|
||||
const articles = qrcode.value && Array.isArray(qrcode.value.articles) ? qrcode.value.articles : [];
|
||||
const ids = articles.map(item => item._id).filter(i => typeof i === 'string' && i.trim());
|
||||
return qrcode.value.enableAnnounce === 'YES' ? ids : [];
|
||||
})
|
||||
|
||||
function toList() {
|
||||
uni.navigateTo({ url: `/pages/article/article-list?corpId=${props.team.corpId}&ids=${articleIds.value.join()}` })
|
||||
}
|
||||
|
||||
async function getArticles() {
|
||||
const res = await api('getArticleByIds', { corpId: props.team.corpId, ids: articleIds.value.join() });
|
||||
articles.value = res && Array.isArray(res.list) ? res.list : [];
|
||||
}
|
||||
|
||||
watch(articleIds, n => {
|
||||
if (n.length) {
|
||||
getArticles()
|
||||
} else {
|
||||
articles.value = []
|
||||
}
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
<style scoped>
|
||||
.cover {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
}
|
||||
|
||||
.min-w-120 {
|
||||
min-width: 240rpx;
|
||||
}
|
||||
|
||||
.w-80 {
|
||||
width: 160rpx;
|
||||
}
|
||||
</style>
|
||||
180
pages/home/customer-archive.vue
Normal file
180
pages/home/customer-archive.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<view class="px-15 py-12 mb-10 bg-white shadow-lg">
|
||||
<view class="mb-10 flex items-center justify-between">
|
||||
<view class="flex-shrink-0 text-lg font-semibold truncate">
|
||||
成员档案
|
||||
</view>
|
||||
<view class="px-10 leading-normal border-dashed-auto text-base text-primary rounded-sm" @click="toManagePage()">
|
||||
档案管理
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="customers.length === 0" class="flex items-center justify-center h-80 border-dashed text-dark rounded">
|
||||
<uni-icons type="plusempty" size="16" color="#999"></uni-icons>
|
||||
<view class="text-base text-dark">新建档案</view>
|
||||
</view>
|
||||
<scroll-view scroll-x="true">
|
||||
<view class="flex flex-nowrap pb-5 ">
|
||||
<view v-for="i in customers" :key="i._id"
|
||||
class="flex-shrink-0 min-w-100 mr-10 p-10 rounded relative border-primary"
|
||||
:class="current && i._id === current._id ? 'bg-primary current-customer' : ''" @click="toggle(i)">
|
||||
<view class="flex justify-between mb-5">
|
||||
<view class="text-base leading-normal font-semibold whitespace-nowrap"
|
||||
:class="current && i._id === current._id ? 'text-white' : 'text-dark'">
|
||||
{{ i.name }}
|
||||
</view>
|
||||
<view v-if="i.relationship" class="flex-shrink-0 px-5 rounded-sm border-auto text-sm leading-normal"
|
||||
:class="current && i._id === current._id ? 'text-white' : 'text-gray'">
|
||||
{{ i.relationship }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-base leading-normal h-normal"
|
||||
:class="current && i._id === current._id ? 'text-white' : 'text-gray'">
|
||||
{{ i.sex }} {{ i.age > 0 ? i.age + '岁' : '' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view v-if="canAuth" class="px-10 py-5 mt-5 flex items-center bg-danger rounded-sm">
|
||||
<view class="mr-5 w-0 flex-grow text-base text-white">
|
||||
该档案还未授权本服务团队,点击右侧授权按钮。我们将更精准的为您服务。
|
||||
</view>
|
||||
<view class="px-12 py-5 text-base rounded-sm text-dark bg-white">
|
||||
授权
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="current" class="flex mt-10">
|
||||
<view class="flex-grow p-10 rounded bg-gray mr-10" @click="fillBaseInfo()">
|
||||
<view class="flex items-center justify-between mb-5">
|
||||
<view class="text-lg">个人基本信息</view>
|
||||
<uni-icons color="#999" type="arrowright"></uni-icons>
|
||||
</view>
|
||||
<!-- <view v-if="formError.base" class="text-sm text-danger">
|
||||
请完善您的个人信息 v-else
|
||||
</view> -->
|
||||
<view class="text-base text-gray">基础信息填写</view>
|
||||
</view>
|
||||
<!-- v-if="healthTempList && healthTempList.length" -->
|
||||
<view class="flex-grow p-10 rounded bg-gray" @click="toHealthList()">
|
||||
<view class="flex items-center justify-between mb-5">
|
||||
<view class="text-lg">个人健康信息</view>
|
||||
<uni-icons color="#999" type="arrowright"></uni-icons>
|
||||
</view>
|
||||
<view class="text-base text-gray">健康信息列表</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia'
|
||||
import useAccount from '@/store/account';
|
||||
import api from '@/utils/api';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
const props = defineProps({
|
||||
corpId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
customers: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
team: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const current = ref(null);
|
||||
const customers = ref([]);
|
||||
|
||||
const canAuth = computed(() => {
|
||||
if (current.value && props.team && props.team.teamId) {
|
||||
const teamIds = Array.isArray(current.value.teamId) ? current.value.teamId : [];
|
||||
return !teamIds.includes(props.team.teamId);
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
function fillBaseInfo() {
|
||||
if (canAuth.value) {
|
||||
toast('请先授权本服务团队')
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: `/pages/archive/edit-archive?teamId=${props.team.teamId}&corpId=${props.corpId}&id=${current.value._id}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function toHealthList() {
|
||||
if (canAuth.value) {
|
||||
toast('请先授权本服务团队')
|
||||
} else {
|
||||
const name = `${current.value.name} ${current.value.relationship ? `(${current.value.relationship})` : ''}`
|
||||
uni.navigateTo({
|
||||
url: `/pages/health/list?teamId=${props.team.teamId}&corpId=${props.corpId}&id=${current.value._id}&name=${encodeURIComponent(name)}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(i) {
|
||||
current.value = i;
|
||||
}
|
||||
|
||||
function toManagePage() {
|
||||
uni.navigateTo({ url: `/pages/archive/archive-manage?corpId=${props.corpId}&teamId=${props.team.teamId}` })
|
||||
}
|
||||
|
||||
async function getCustomers() {
|
||||
const res = await api('getMiniAppCustomers', { miniAppId: account.value.openid, corpId: props.corpId });
|
||||
if (res && res.success) {
|
||||
customers.value = res && Array.isArray(res.data) ? res.data : [];
|
||||
} else {
|
||||
toast(res.message || '获取档案失败');
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.corpId, n => {
|
||||
if (n) {
|
||||
getCustomers()
|
||||
} else {
|
||||
customers.value = [];
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
watch(customers, n => {
|
||||
if (n.length && !(current.value && n.some(i => i._id === current.value._id))) {
|
||||
toggle(n[0]);
|
||||
} else {
|
||||
current.value = null;
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.h-80 {
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.h-normal {
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.min-w-100 {
|
||||
min-width: 200rpx;
|
||||
}
|
||||
|
||||
.current-customer::after {
|
||||
bottom: -1px;
|
||||
left: calc(50% - 10px);
|
||||
border: 10px solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-bottom-color: white;
|
||||
}
|
||||
</style>
|
||||
75
pages/home/home.vue
Normal file
75
pages/home/home.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<page-loading v-if="loading" />
|
||||
<full-page v-else-if="teams.length && team">
|
||||
<template #header>
|
||||
<team-head :team="team" :teams="teams" @changeTeam="changeTeam" />
|
||||
<view class="pb-10"></view>
|
||||
</template>
|
||||
<customer-archive :corpId="corpId" :team="team" />
|
||||
<team-mate :team="team" />
|
||||
<article-list :team="team" />
|
||||
</full-page>
|
||||
<yc-home v-else />
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia'
|
||||
import useGuard from '@/hooks/useGuard';
|
||||
import useAccount from '@/store/account';
|
||||
import api from '@/utils/api';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
import articleList from './article-list.vue';
|
||||
import customerArchive from './customer-archive.vue';
|
||||
import teamHead from './team-head.vue';
|
||||
import teamMate from './team-mate.vue';
|
||||
import ycHome from './yc-home.vue';
|
||||
import pageLoading from './loading.vue';
|
||||
|
||||
const { useLoad, useShow } = useGuard();
|
||||
const { account } = storeToRefs(useAccount());
|
||||
|
||||
const team = ref(null);
|
||||
const teams = ref([]);
|
||||
const loading = ref(true)
|
||||
|
||||
const corpId = computed(() => team.value?.corpId);
|
||||
|
||||
async function changeTeam({ teamId, corpId, corpName }) {
|
||||
loading.value = true;
|
||||
const res = await api('getTeamData', { teamId, corpId });
|
||||
loading.value = false;
|
||||
if (res && res.data) {
|
||||
team.value = res.data;
|
||||
team.value.corpName = corpName;
|
||||
} else {
|
||||
toast(res?.message || '获取团队信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function getTeams() {
|
||||
loading.value = true;
|
||||
const res = await api('queryWxJoinedTeams', { openid: account.value.openid });
|
||||
teams.value = res && Array.isArray(res.data) ? res.data : [];
|
||||
const valid = team.value && teams.value.some(item => item.teamId === team.value.teamId);
|
||||
if (teams.value.length) {
|
||||
changeTeam(teams.value[0])
|
||||
return
|
||||
} else if (!valid) {
|
||||
team.value = null;
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
useLoad(opts => {
|
||||
if (opts.teamId) {
|
||||
team.value = { teamId: opts.teamId, corpId: opts.corpId };
|
||||
}
|
||||
})
|
||||
|
||||
useShow(() => {
|
||||
getTeams();
|
||||
})
|
||||
|
||||
</script>
|
||||
213
pages/home/loading.vue
Normal file
213
pages/home/loading.vue
Normal file
@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<view class="loader-container">
|
||||
<view class="loader">
|
||||
<view class="hexagon-container">
|
||||
<view class="hexagon hex_1"></view>
|
||||
<view class="hexagon hex_2"></view>
|
||||
<view class="hexagon hex_3"></view>
|
||||
<view class="hexagon hex_4"></view>
|
||||
<view class="hexagon hex_5"></view>
|
||||
<view class="hexagon hex_6"></view>
|
||||
<view class="hexagon hex_7"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.loader-container {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
z-index: 9999;
|
||||
background-color: #fefefe;
|
||||
}
|
||||
|
||||
.loader {
|
||||
position: fixed;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: -40px 0px 0px -40px;
|
||||
background-color: transparent;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgb(0, 122, 255); //#007aff
|
||||
|
||||
// animation: rotate3 3s linear infinite;
|
||||
&:before {
|
||||
content: '';
|
||||
width: 82px;
|
||||
height: 82px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
border: 1px solid rgba(0, 122, 255, 0.514);
|
||||
border-radius: 50%;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
box-sizing: border-box;
|
||||
// border-bottom-color: transparent;
|
||||
// border-left-color: transparent;
|
||||
// border-right-color: transparent;
|
||||
clip: rect(0px, 17.5px, 17.5px, 0px);
|
||||
z-index: 10;
|
||||
animation: rotate infinite;
|
||||
animation-duration: 3s;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
width: 82px;
|
||||
height: 82px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
border: 1px solid rgba(0, 122, 255, 0.11);
|
||||
border-radius: 50%;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
box-sizing: border-box;
|
||||
// transform: rotate(30deg);
|
||||
// border-bottom-color: transparent;
|
||||
// border-left-color: transparent;
|
||||
// border-right-color: transparent;
|
||||
clip: rect(0px, 82px, 75px, 0px);
|
||||
z-index: 9;
|
||||
// animation: rotate2 infinite, rotate3 infinite;
|
||||
// animation-duration: 3s;
|
||||
// animation-timing-function: linner;
|
||||
animation: rotate2 3s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.hexagon-container {
|
||||
position: relative;
|
||||
top: 16.5px;
|
||||
left: 20.5px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.hexagon {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 11.5px;
|
||||
background-color: #007aff;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -5.5px;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 6px solid #007aff;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-top: 5.7px solid #007aff;
|
||||
}
|
||||
}
|
||||
|
||||
$hexagons: (
|
||||
(1, 0px, 0px),
|
||||
(2, 0px, 21px),
|
||||
(3, 18px, 31.5px),
|
||||
(4, 36px, 21px),
|
||||
(5, 36px, 0px),
|
||||
(6, 18px, -10.5px),
|
||||
(7, 18px, 10.5px)
|
||||
);
|
||||
|
||||
$time: 3s;
|
||||
$delay: $time / 14;
|
||||
|
||||
@each $hexagon in $hexagons {
|
||||
$index: nth($hexagon, 1);
|
||||
$top: nth($hexagon, 2);
|
||||
$left: nth($hexagon, 3);
|
||||
|
||||
.hexagon.hex_#{$index} {
|
||||
top: $top;
|
||||
left: $left;
|
||||
animation: Animasearch $time ease-in-out infinite;
|
||||
animation-delay: $delay * $index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes Animasearch {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
15%,
|
||||
50% {
|
||||
transform: scale(0.5);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
65% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
clip: rect(0px, 17.5px, 17.5px, 0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
clip: rect(0px, 20px, 20px, 0px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
clip: rect(0px, 17.5px, 17.5px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate2 {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
clip: rect(0px, 82, 75px, 0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
clip: rect(0px, 82, 0px, 0px);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(720deg);
|
||||
clip: rect(0px, 164px, 75px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate3 {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
123
pages/home/team-head.vue
Normal file
123
pages/home/team-head.vue
Normal file
@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<view>
|
||||
<view :style="{ height: statusBarHeight }" class="bg-primary"></view>
|
||||
<view class="relative z-3 flex items-center px-15 py-12 bg-primary">
|
||||
<view class="flex-shrink-0 mr-5">
|
||||
<group-avatar :size="120" :avatarList="currentTeam ? currentTeam.avatarList : []" />
|
||||
</view>
|
||||
<view class="w-0 flex-grow ">
|
||||
<view class="flex mb-10">
|
||||
<view class="w-0 flex-grow truncate text-lg font-semibold text-white">{{ team.name }}</view>
|
||||
<view v-if="teams.length > 1" class="flex-shinrk-0 flex items-center px-10 bg-white rounded-sm"
|
||||
@click="showDropDown = true">
|
||||
<view class="text-base">切换</view>
|
||||
<uni-icons type="down" size="12"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="currentTeam" class="text-base text-white truncate">{{ currentTeam.corpName }}</view>
|
||||
</view>
|
||||
<view v-if="menuButtonInfo && menuButtonInfo.width > 0" class="flex-shrink-0"
|
||||
:style="{ width: menuButtonInfo.width + 'px', height: menuButtonInfo.height + 'px' }">
|
||||
</view>
|
||||
</view>
|
||||
<view class="relative">
|
||||
<view v-if="showDropDown" class="team-dropdown py-12 bg-white shadow-lg">
|
||||
<scroll-view scroll-y="true" style="max-height: 50vh;">
|
||||
<view class="px-15">
|
||||
<view v-for="item in teams" :key="item.teamId" class="mb-10 p-10 flex items-center bg-gray rounded-sm">
|
||||
<view class="flex-shrink-0 mr-5">
|
||||
<group-avatar :size="96" :avatarList="item.avatarList" />
|
||||
</view>
|
||||
<view class="w-0 flex-grow mr-5">
|
||||
<view class="mb-5 text-lg font-semibold text-dark">{{ item.name }}</view>
|
||||
<view class="text-base text-gray leading-normal">{{ item.corpName }}</view>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<image class="check-icon"
|
||||
:src="team && team.teamId === item.teamId ? '/static/form/checked.svg' : '/static/form/unchecked.svg'">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="team.teamTroduce" class="px-15 py-12 flex bg-white border-b shadow-lg">
|
||||
<image class="laba-icon flex-shrink-0 mr-5" src="/static/laba.svg"></image>
|
||||
<view class="w-0 flex-grow text-sm text-gray leading-normal line-clamp-2">{{ team.teamTroduce }} </view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="showDropDown" class="mask" @click="showDropDown = false"></view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref, onMounted, watch } from 'vue';
|
||||
|
||||
import groupAvatar from '@/components/group-avatar.vue';
|
||||
|
||||
const statusBarHeight = ref('50px');
|
||||
const menuButtonInfo = ref(null);
|
||||
const showDropDown = ref(false)
|
||||
|
||||
const emits = defineEmits(['changeTeam']);
|
||||
const props = defineProps({
|
||||
team: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
teams: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const currentTeam = computed(() => props.teams.find(i => props.team && i.teamId === props.team.teamId))
|
||||
|
||||
watch(() => props.teams, (teams) => {
|
||||
if (teams.length && !(currentTeam.value && teams.some(i => i.teamId === currentTeam.value.teamId))) {
|
||||
emits('changeTeam', teams[0])
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const win = uni.getWindowInfo();
|
||||
if (win && win.statusBarHeight > 0) {
|
||||
statusBarHeight.value = win.statusBarHeight + 'px';
|
||||
}
|
||||
menuButtonInfo.value = uni.getMenuButtonBoundingClientRect();
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.laba-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.z-3 {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.team-dropdown {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
border-bottom-left-radius: 16rpx;
|
||||
border-bottom-right-radius: 16rpx;
|
||||
}
|
||||
</style>
|
||||
75
pages/home/team-mate.vue
Normal file
75
pages/home/team-mate.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<view class="mt-12 px-15 flex items-center justify-between">
|
||||
<view class="text-lg font-semibold text-dark">团队成员</view>
|
||||
<view class="flex items-center" @click="toTeamDetail()">
|
||||
<view class="mr-5 text-base text-gray">团队详情</view>
|
||||
<uni-icons type="right" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-15 mt-10">
|
||||
<scroll-view scroll-x="true">
|
||||
<view class="flex flex-nowrap pb-5 border-b">
|
||||
<view v-for="i in teamates" :key="i.userid"
|
||||
class="flex flex-shrink-0 min-w-120 p-10 mr-10 rounded-sm border-auto text-primary bg-white"
|
||||
@click="toHomePage(i)">
|
||||
<image class="flex-shrink-0 avatar mr-5" :src="i.avatar || '/static/default-avatar.png'" />
|
||||
<view class="flex-grow flex flex-col">
|
||||
<view class="text-lg font-semibold text-dark whitespace-nowrap">
|
||||
{{ i.anotherName }}
|
||||
</view>
|
||||
<view class="text-base text-gray truncate">
|
||||
医生
|
||||
</view>
|
||||
<view v-if="i.canAddFriend" class="w-80 text-base leading-none border text-center text-dark rounded-full"
|
||||
@click.stop="toQrcode(i)">
|
||||
添加好友
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
team: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const teamates = computed(() => {
|
||||
const friendlyMembers = props.team && Array.isArray(props.team.friendlyMembers) ? props.team.friendlyMembers : [];
|
||||
const memberList = props.team && Array.isArray(props.team.memberList) ? props.team.memberList : [];
|
||||
return memberList.map(i => ({ ...i, canAddFriend: friendlyMembers.includes(i.userid) }))
|
||||
})
|
||||
|
||||
function toHomePage(item) {
|
||||
uni.navigateTo({ url: `/pages/team/homepage?userid=${item.userid}&corpId=${item.corpId}` })
|
||||
}
|
||||
|
||||
function toQrcode(item) {
|
||||
uni.navigateTo({ url: `/pages/team/friend?userid=${item.userid}&corpId=${item.corpId}` })
|
||||
}
|
||||
|
||||
function toTeamDetail() {
|
||||
uni.navigateTo({ url: `/pages/team/team-detail?teamId=${props.team.teamId}&corpId=${props.team.corpId}&corpName=${encodeURIComponent(props.team.corpName)}` })
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.avatar {
|
||||
width: 120rpx;
|
||||
height: 128rpx;
|
||||
}
|
||||
|
||||
.min-w-120 {
|
||||
min-width: 240rpx;
|
||||
}
|
||||
|
||||
.w-80 {
|
||||
width: 160rpx;
|
||||
}
|
||||
</style>
|
||||
45
pages/home/yc-home.vue
Normal file
45
pages/home/yc-home.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<view class="h-full flex flex-col">
|
||||
<view class="flex-shrink-0 w-full">
|
||||
<view :style="{ height: statusBarHeight }" class="bg-primary"></view>
|
||||
<view class="relative z-3 flex items-center px-15 py-12 bg-primary">
|
||||
<view class="flex-shrink-0 mr-10">
|
||||
<image class="logo" src="/static/logo-plain.png"></image>
|
||||
</view>
|
||||
<view class="w-0 flex-grow">
|
||||
<view class="text-lg font-semibold text-white">柚健康 </view>
|
||||
<view class="leading-normal text-base text-white truncate">生命全周期健康管理伙伴</view>
|
||||
</view>
|
||||
<view v-if="menuButtonInfo && menuButtonInfo.width > 0" class="flex-shrink-0"
|
||||
:style="{ width: menuButtonInfo.width + 'px', height: menuButtonInfo.height + 'px' }">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-grow flex flex-col items-center justify-center bg-white">
|
||||
<empty-data :showText="false" />
|
||||
<view class="mb-10 text-lg text-dark font-semibold">暂无团队</view>
|
||||
<view class="text-lg text-dark font-semibold">需要扫团队二维码绑定服务团队哦!</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import emptyData from '@/components/empty-data.vue';
|
||||
|
||||
const statusBarHeight = ref('50px');
|
||||
const menuButtonInfo = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
const win = uni.getWindowInfo();
|
||||
if (win && win.statusBarHeight > 0) {
|
||||
statusBarHeight.value = win.statusBarHeight + 'px';
|
||||
}
|
||||
menuButtonInfo.value = uni.getMenuButtonBoundingClientRect();
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.logo {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
</style>
|
||||
159
pages/library/diagnosis-list.vue
Normal file
159
pages/library/diagnosis-list.vue
Normal file
@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<full-page>
|
||||
<template #header>
|
||||
<view class="page-item border-bottom bg-white">
|
||||
<input class="search-input" placeholder-style="font-size:28rpx" placeholder="请搜索名称" v-model="name"
|
||||
@input="search" />
|
||||
</view>
|
||||
</template>
|
||||
<view v-for="i in list" :key="i.label" class="search-item bg-white" @click="select(i.label)">
|
||||
<view class="name">{{ i.label }}</view>
|
||||
<icon class="icon-checked" :class="selectMap[i.label] ? '' : 'hidden'" type="success_no_circle" size="18" />
|
||||
</view>
|
||||
<template #footer>
|
||||
<button-footer confirmText="确定" :showCancel="false" @confirm="confirm()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import api from '@/utils/api';
|
||||
import { remove, get } from '@/utils/cache';
|
||||
import useDebounce from '@/utils/useDebounce';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
|
||||
const name = ref('');
|
||||
const list = ref([]);
|
||||
const page = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const more = ref(false);
|
||||
const selections = ref([]);
|
||||
const eventName = ref('')
|
||||
const selectMap = computed(() => selections.value.reduce((val, i) => {
|
||||
i && (val[i] = i);
|
||||
return val
|
||||
}, {}))
|
||||
|
||||
const search = useDebounce(() => {
|
||||
page.value = 1;
|
||||
getList()
|
||||
})
|
||||
|
||||
function confirm() {
|
||||
if (selections.value.length) {
|
||||
uni.$emit(eventName.value, selections.value);
|
||||
uni.navigateBack()
|
||||
} else {
|
||||
toast('请选择疾病')
|
||||
}
|
||||
}
|
||||
|
||||
function select(label) {
|
||||
if (selections.value.includes(label)) {
|
||||
selections.value = selections.value.filter(i => i !== label)
|
||||
} else {
|
||||
selections.value.push(label)
|
||||
}
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
const res = await api('getPageDisease', { name: name.value.trim(), page: page.value, pageSize: pageSize.value });
|
||||
const diseases = res && Array.isArray(res.list) ? res.list.map(i => ({
|
||||
label: i.diseaseName,
|
||||
value: i.diseaseName
|
||||
})).filter(i => i.label !== name.value.trim()) : [];
|
||||
const pages = res && res.pages ? res.pages : 0;
|
||||
more.value = pages > page.value;
|
||||
|
||||
if (page.value === 1) {
|
||||
const data = name.value.trim() ? [{
|
||||
label: name.value.trim(),
|
||||
value: name.value.trim()
|
||||
}] : [];
|
||||
data.push(...diseases);
|
||||
list.value = data
|
||||
} else {
|
||||
list.value = [...list.value, ...diseases];
|
||||
}
|
||||
}
|
||||
onLoad(options => {
|
||||
eventName.value = options.eventName;
|
||||
const data = get('diagnosis-list-selections');
|
||||
selections.value = Array.isArray(data) ? data : [];
|
||||
remove('diagnosis-list-selections');
|
||||
if (selections.value.length) {
|
||||
list.value = selections.value.map(i => ({
|
||||
label: i,
|
||||
value: i
|
||||
}))
|
||||
} else {
|
||||
getList()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.list-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
border: 1px solid #eee;
|
||||
padding: 16rpx 20rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
|
||||
}
|
||||
|
||||
.scroll-wrapper {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.list-scroll {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.search-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 30rpx;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.icon-checked {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
margin-right: 20rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.page-item {
|
||||
flex-shrink: 0;
|
||||
padding: 30rpx;
|
||||
}
|
||||
</style>
|
||||
@ -38,10 +38,19 @@ async function changeTeam({ teamId, corpId, corpName }) {
|
||||
}
|
||||
|
||||
onLoad(options => {
|
||||
teamId.value = options.teamId || '';
|
||||
corpId.value = options.corpId || '';
|
||||
changeTeam({ teamId: teamId.value, corpId: corpId.value });
|
||||
const href = typeof options.q === 'string' ? decodeURIComponent(options.q) : '';
|
||||
const [, url = ''] = href.split('?');
|
||||
const data = url.split('&').reduce((acc, cur) => {
|
||||
console.log(cur)
|
||||
const [key, value] = cur.split('=');
|
||||
console.log(key, '=====', value)
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {})
|
||||
changeTeam(data)
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<style>
|
||||
.flash-logo {
|
||||
|
||||
93
pages/team/friend.vue
Normal file
93
pages/team/friend.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<view v-if="member" class="flex flex-col h-full items-center justify-center">
|
||||
<view class="business-card">
|
||||
<view class="flex">
|
||||
<image class="mr-10 avatar" :src="member.avatar || '/static/default-avatar.png'"></image>
|
||||
<view class="w-0 flex-grow leading-normal">
|
||||
<view class="flex items-center">
|
||||
<view class="mr-5 text-lg font-semibold text-dark">{{ member.anotherName }}</view>
|
||||
<view class="text-base text-warning">@企业微信</view>
|
||||
</view>
|
||||
<view class="text-base text-dark">咨询师</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-12 border-primary qrcode p-15 mx-auto rounded" @click="previewImage()">
|
||||
<image v-if="qrcode" class="h-full w-full" :src="qrcode"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import api from '@/utils/api';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
const corpId = ref('');
|
||||
const userid = ref('');
|
||||
const qrcode = ref('')
|
||||
const member = ref(null);
|
||||
|
||||
const corpNames = computed(() => {
|
||||
const corpNames = member.value && Array.isArray(member.value.corpNames) ? member.value.corpNames : [];
|
||||
return corpNames.join('、');
|
||||
})
|
||||
const deptNames = computed(() => {
|
||||
const deptNames = member.value && Array.isArray(member.value.deptNames) ? member.value.deptNames : [];
|
||||
return deptNames.join('、');
|
||||
})
|
||||
|
||||
|
||||
function previewImage() {
|
||||
if (!qrcode.value) return;
|
||||
uni.previewImage({
|
||||
urls: [qrcode.value]
|
||||
})
|
||||
}
|
||||
|
||||
async function getMember() {
|
||||
const res = await api('getCorpMemberHomepageInfo', { userid: userid.value, corpId: corpId.value });
|
||||
if (res && res.success) {
|
||||
member.value = res.data;
|
||||
if (!qrcode.value) {
|
||||
getQrcode();
|
||||
}
|
||||
} else {
|
||||
toast(res.message || '获取医生信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function getQrcode() {
|
||||
const res = await api('addContactWay', { corpUserId: userid.value, corpId: corpId.value });
|
||||
if (res && res.data) {
|
||||
qrcode.value = res.data;
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
corpId.value = options.corpId;
|
||||
userid.value = options.userid;
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
if (corpId.value && userid.value) {
|
||||
getMember()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.business-card {
|
||||
width: 600rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
width: 560rpx;
|
||||
height: 560rpx;
|
||||
}
|
||||
</style>
|
||||
159
pages/team/homepage.vue
Normal file
159
pages/team/homepage.vue
Normal file
@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<view v-if="member" class="flex p-15 bg-whtie shadow-lg">
|
||||
<view class="flex-grow w-0 mr-10 leading-normal">
|
||||
<view>
|
||||
<text class="mr-5 text-xl text-dark font-semibold">{{ member.anotherName }}</text>
|
||||
<text class="text-base text-gray">医生</text>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<view class="flex-shrink-0 text-base text-gray">机构部门:</view>
|
||||
<view class="flex-shrink-0 text-base text-dark">{{ deptNames }}</view>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<view class="flex-shrink-0 text-base text-gray">执业机构:</view>
|
||||
<view class="flex-shrink-0 text-base text-dark">{{ corpNames }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<image class="avatar" :src="member.avatar"></image>
|
||||
</view>
|
||||
<view v-if="member" class="p-15 mt-12 leading-normal bg-white shadow-lg">
|
||||
<view class="flex items-center" @click="expand = !expand">
|
||||
<image class="flex-shrink-0 mr-10 section-icon" src="https://picsum.photos/300/300"></image>
|
||||
<view class="w-0 flex-grow text-lg font-semibold">个人简介</view>
|
||||
<uni-icons v-if="member.memberTroduce" :type="expand ? 'up' : 'down'" size="16"></uni-icons>
|
||||
</view>
|
||||
<view class="mt-10 text-dark text-base" :class="expand ? '' : 'line-clamp-4'">
|
||||
{{ member.memberTroduce || '暂无简介' }}
|
||||
</view>
|
||||
<view class="mt-20 flex items-center">
|
||||
<image class="flex-shrink-0 mr-10 section-icon" src="https://picsum.photos/300/300"></image>
|
||||
<view class="w-0 flex-grow text-lg font-semibold">门诊时间</view>
|
||||
</view>
|
||||
<view class="mt-10 text-dark text-base">
|
||||
{{ member.outpatientTime || '暂无门诊时间' }}
|
||||
</view>
|
||||
<view class="mt-20 flex items-center">
|
||||
<image class="flex-shrink-0 mr-10 section-icon" src="https://picsum.photos/300/300"></image>
|
||||
<view class="w-0 flex-grow text-lg font-semibold">对外联系电话</view>
|
||||
</view>
|
||||
<view class="mt-10 text-dark" :class="member.callNumber ? 'text-primary' : 'text-gray'" @click="callNumber()">
|
||||
{{ member.callNumber || '暂无联系电话' }}
|
||||
</view>
|
||||
<view class="mt-20 flex items-center">
|
||||
<image class="flex-shrink-0 mr-10 section-icon" src="https://picsum.photos/300/300"></image>
|
||||
<view class="w-0 flex-grow text-lg font-semibold">便民服务</view>
|
||||
</view>
|
||||
<view class="mt-10 flex">
|
||||
<view class="px-15 py-5 text-base text-white bg-primary rounded-sm">预约挂号</view>
|
||||
<view class="px-15 py-5 ml-15 text-base text-white bg-primary rounded-sm">在线咨询</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="qrcode" class="p-15 mt-12 leading-normal bg-white shadow-lg">
|
||||
<view class="text-lg font-semibold text-center text-dark">
|
||||
点击下方二维码,加我为好友
|
||||
</view>
|
||||
<view class="mt-12 border-primary qrcode p-15 mx-auto rounded" @click="previewImage()">
|
||||
<image :src="qrcode" class="h-full w-full"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="safe-bottom-padding"></view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import api from '@/utils/api';
|
||||
import { toast } from '@/utils/widget';
|
||||
|
||||
const corpId = ref('');
|
||||
const userid = ref('');
|
||||
const showQrcode = ref(false);
|
||||
const qrcode = ref('')
|
||||
const member = ref(null);
|
||||
const expand = ref(false);
|
||||
|
||||
const corpNames = computed(() => {
|
||||
const corpNames = member.value && Array.isArray(member.value.corpNames) ? member.value.corpNames : [];
|
||||
return corpNames.join('、');
|
||||
})
|
||||
const deptNames = computed(() => {
|
||||
const deptNames = member.value && Array.isArray(member.value.deptNames) ? member.value.deptNames : [];
|
||||
return deptNames.join('、');
|
||||
})
|
||||
|
||||
function callNumber() {
|
||||
if (member.value && member.value.callNumber) {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: member.value.callNumber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function previewImage() {
|
||||
uni.previewImage({
|
||||
urls: [qrcode.value]
|
||||
})
|
||||
}
|
||||
|
||||
async function getMember() {
|
||||
const res = await api('getCorpMemberHomepageInfo', { userid: userid.value, corpId: corpId.value });
|
||||
if (res && res.success) {
|
||||
member.value = res.data;
|
||||
if ( showQrcode.value && !qrcode.value) {
|
||||
getQrcode();
|
||||
}
|
||||
} else {
|
||||
toast(res.message || '获取医生信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function getQrcode() {
|
||||
const res = await api('addContactWay', { corpUserId: userid.value, corpId: corpId.value });
|
||||
if (res && res.data ) {
|
||||
qrcode.value = res.data;
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
corpId.value = options.corpId;
|
||||
userid.value = options.userid;
|
||||
showQrcode.value = options.showQrcode;
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
if (corpId.value && userid.value) {
|
||||
getMember()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 120rpx;
|
||||
height: 128rpx;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.line-clamp-4 {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 4;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
width: 560rpx;
|
||||
height: 560rpx;
|
||||
}
|
||||
</style>
|
||||
150
pages/team/team-detail.vue
Normal file
150
pages/team/team-detail.vue
Normal file
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<view v-if="team" class="p-15">
|
||||
<view class="flex items-center">
|
||||
<view class="flex-shrink-0 mr-10">
|
||||
<group-avatar :size="96" :avatarList="avatarList" />
|
||||
</view>
|
||||
<view class="flex-grow w-0 leading-noraml">
|
||||
<view class="mb-5 text-lg font-semibold text-dark">{{ team.name }}</view>
|
||||
<view class="text-base text-gray">
|
||||
{{ corpName }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="team.teamTroduce" class="mt-12 text-base text-dark leading-normal break-all">
|
||||
{{ team.teamTroduce }}
|
||||
</view>
|
||||
<template v-if="teammate.leaders.length">
|
||||
<view class="min-w-100 inline-block mt-12 px-10 py-5 text-center text-base text-white bg-primary rounded-sm">
|
||||
团队负责人
|
||||
</view>
|
||||
<view v-for="i in teammate.leaders" :key="i._id" class="mt-12 flex p-10 border-primary rounded-sm"
|
||||
@click="toHomePage(i.userid)">
|
||||
<image class="flex-shrink-0 mr-10 avatar" :src="i.avatar || '/static/default-avatar.png'"></image>
|
||||
<view class="w-0 flex-grow leading-normal">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex-grow w-0">
|
||||
<text class="mr-5 text-lg text-dark font-semibold">{{ i.anotherName }}</text>
|
||||
<text class="text-base text-dark">医生</text>
|
||||
</view>
|
||||
<view v-if="friendlyMember[i.userid]"
|
||||
class="px-10 leading-normal text-sm border-auto text-primary rounded-full"
|
||||
@click.stop="toFriend(i.userid)">
|
||||
添加好友
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-clamp-2 text-base text-gray">
|
||||
{{ i.memberTroduce || '暂无简介' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template v-if="teammate.members.length">
|
||||
<view class="min-w-100 inline-block mt-12 px-10 py-5 text-center text-base text-white bg-primary rounded-sm">团队成员
|
||||
</view>
|
||||
<view v-for="i in teammate.members" :key="i._id" class="mt-12 flex p-10 border-primary rounded-sm"
|
||||
@click="toHomePage(i.userid)">
|
||||
<image class="flex-shrink-0 mr-10 avatar" :src="i.avatar || '/static/default-avatar.png'"></image>
|
||||
<view class="w-0 flex-grow leading-normal">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex-grow w-0">
|
||||
<text class="mr-5 text-lg text-dark font-semibold">{{ i.anotherName }}</text>
|
||||
<text class="text-base text-dark">医生</text>
|
||||
</view>
|
||||
<view v-if="friendlyMember[i.userid]"
|
||||
class="px-10 leading-normal text-sm border-auto text-primary rounded-full"
|
||||
@click.stop="toFriend(i.userid)">
|
||||
添加好友
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-clamp-2 text-base text-gray">
|
||||
{{ i.memberTroduce || '暂无简介' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="safe-bottom-padding"></view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import api from '@/utils/api';
|
||||
|
||||
import groupAvatar from '@/components/group-avatar.vue';
|
||||
|
||||
const corpId = ref('');
|
||||
const teamId = ref('');
|
||||
const team = ref(null);
|
||||
const corpName = ref('');
|
||||
|
||||
|
||||
const memberList = computed(() => team.value && Array.isArray(team.value.memberList) ? team.value.memberList : [])
|
||||
|
||||
const avatarList = computed(() => memberList.value.map(i => i.avatar || '/static/default-avatar.png').filter(Boolean))
|
||||
|
||||
const teammate = computed(() => {
|
||||
const memberLeaderList = team.value && Array.isArray(team.value.memberLeaderList) ? team.value.memberLeaderList : [];
|
||||
return memberList.value.reduce((data, item) => {
|
||||
if (memberLeaderList.includes(item.userid)) {
|
||||
data.leaders.push(item)
|
||||
} else {
|
||||
data.members.push(item)
|
||||
}
|
||||
return data
|
||||
}, { leaders: [], members: [] })
|
||||
})
|
||||
|
||||
const friendlyMember = computed(() => {
|
||||
const friendlyMembers = team.value && Array.isArray(team.value.friendlyMembers) ? team.value.friendlyMembers : [];
|
||||
return friendlyMembers.reduce((data, item) => {
|
||||
data[item] = true;
|
||||
return data
|
||||
}, {})
|
||||
})
|
||||
|
||||
function toFriend(userid) {
|
||||
uni.navigateTo({ url: `/pages/team/friend?corpId=${corpId.value}&userid=${userid}` })
|
||||
}
|
||||
|
||||
function toHomePage(userid) {
|
||||
uni.navigateTo({ url: `/pages/team/homepage?corpId=${corpId.value}&userid=${userid}&showQrcode=${friendlyMember[userid] ? 'YES' : ''}` })
|
||||
}
|
||||
|
||||
async function getTeam() {
|
||||
const res = await api('getTeamData', { teamId: teamId.value, corpId: corpId.value });
|
||||
if (res && res.data) {
|
||||
team.value = res.data;
|
||||
} else {
|
||||
toast(res?.message || '获取团队信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(options => {
|
||||
corpId.value = options.corpId;
|
||||
teamId.value = options.teamId;
|
||||
corpName.value = decodeURIComponent(options.corpName || '');
|
||||
})
|
||||
onShow(() => {
|
||||
if (teamId.value && corpId.value) {
|
||||
getTeam()
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: white;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.min-w-100 {
|
||||
min-width: 200rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 120rpx;
|
||||
height: 128rpx;
|
||||
}
|
||||
</style>
|
||||
@ -3,6 +3,50 @@ export default [
|
||||
path: 'pages/home/home',
|
||||
meta: { title: '首页', login: true },
|
||||
style: { navigationStyle: 'custom' }
|
||||
},
|
||||
{
|
||||
path: 'pages/login/login',
|
||||
meta: { title: '柚健康' },
|
||||
},
|
||||
{
|
||||
path: 'pages/login/redirect-page',
|
||||
meta: { title: '柚健康' },
|
||||
},
|
||||
{
|
||||
path: 'pages/archive/archive-manage',
|
||||
meta: { title: '档案管理', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/archive/edit-archive',
|
||||
meta: { title: '新增档案', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/article/article-list',
|
||||
meta: { title: '健康宣教', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/health/list',
|
||||
meta: { title: '健康信息', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/health/record',
|
||||
meta: { title: '健康信息', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/library/diagnosis-list',
|
||||
meta: { title: '选择诊断' }
|
||||
},
|
||||
{
|
||||
path: 'pages/team/team-detail',
|
||||
meta: { title: '团队介绍', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/team/homepage',
|
||||
meta: { title: '个人主页', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/team/friend',
|
||||
meta: { title: '添加好友', login: true }
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
1
static/file.svg
Normal file
1
static/file.svg
Normal file
@ -0,0 +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="1768890544193" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9729" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M 518.72 238.4 l -84.8 -95.68 c -8 -9.28 -20.16 -14.72 -32.64 -14.72 H 74.56 C 50.24 128 30.4 147.52 30.4 171.84 v 696.64 c 0 24.32 19.52 43.84 43.84 43.84 H 969.6 c 24.32 0 43.84 -19.52 43.84 -43.84 V 297.28 c 0 -24.32 -19.52 -43.84 -43.84 -43.84 H 551.68 c -12.48 0 -24.64 -5.44 -32.96 -15.04 Z" fill="#06a8f5" p-id="9730"></path></svg>
|
||||
|
After Width: | Height: | Size: 671 B |
10
utils/api.js
10
utils/api.js
@ -7,19 +7,26 @@ const urlsConfig = {
|
||||
getTeamData: 'getTeamData',
|
||||
queryWxJoinedTeams: 'queryWxJoinedTeams',
|
||||
wxAppLogin: 'wxAppLogin',
|
||||
getTeamHealthTemplate: 'getTeamHealthTemplate',
|
||||
getTeamHealthTemps: "getTeamHealthTemps",
|
||||
},
|
||||
|
||||
knowledgeBase: {
|
||||
getArticleByIds: 'getArticleByIds'
|
||||
getArticleByIds: 'getArticleByIds',
|
||||
getPageDisease: "getPageDisease"
|
||||
},
|
||||
member: {
|
||||
addCustomer: 'add',
|
||||
addMedicalRecord: 'addMedicalRecord',
|
||||
bindMiniAppArchive: "bindMiniAppArchive",
|
||||
getCustomerByCustomerId: 'getCustomerByCustomerId',
|
||||
getMiniAppCustomers: 'getMiniAppCustomers',
|
||||
getTeamCustomers: 'getTeamCustomers',
|
||||
getUnbindMiniAppCustomers: 'getUnbindMiniAppCustomers',
|
||||
getCustomerMedicalRecord: 'getCustomerMedicalRecord',
|
||||
getMedicalRecordById: 'getMedicalRecordById',
|
||||
unbindMiniAppArchive: 'unbindMiniAppArchive',
|
||||
updateMedicalRecord: 'updateMedicalRecord'
|
||||
},
|
||||
wecom: {
|
||||
addContactWay: 'addContactWay'
|
||||
@ -38,7 +45,6 @@ const urls = Object.keys(urlsConfig).reduce((acc, path) => {
|
||||
})
|
||||
return acc
|
||||
}, {})
|
||||
console.log('urls: ', urls)
|
||||
|
||||
export default async function api(urlId, data) {
|
||||
const config = urls[urlId];
|
||||
|
||||
@ -202,5 +202,28 @@ export default request;
|
||||
export const uploadUrl = `${baseUrl}/upload`;
|
||||
|
||||
export function getFullPath(path) {
|
||||
return `${baseUrl}${path}`;
|
||||
return `${baseUrl}/${path}`;
|
||||
}
|
||||
|
||||
|
||||
export function upload(path) {
|
||||
return new Promise((resolve) => {
|
||||
uni.uploadFile({
|
||||
url: uploadUrl, // 替换为你的上传接口地址
|
||||
filePath: path,
|
||||
name: 'file',
|
||||
fileType: 'image',
|
||||
success: (res) => {
|
||||
try {
|
||||
const url = JSON.parse(res.data).filePath;
|
||||
resolve(url ? getFullPath(url) : '')
|
||||
} catch (e) {
|
||||
resolve()
|
||||
}
|
||||
},
|
||||
fail: res => {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user