登陆授权流程调整
This commit is contained in:
parent
c9df1e6341
commit
f6b504a2bd
16
App.vue
16
App.vue
@ -1,15 +1,19 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
export default {
|
export default {
|
||||||
onLaunch: function () {
|
onLaunch: function () {
|
||||||
console.log('App Launch: ')
|
// 需在 pinia 安装后再获取 store,避免 getActivePinia 报错
|
||||||
|
const { login } = useAccountStore();
|
||||||
|
login();
|
||||||
|
console.log("App Launch: ");
|
||||||
},
|
},
|
||||||
onShow: function () {
|
onShow: function () {
|
||||||
console.log('App Show')
|
console.log("App Show");
|
||||||
},
|
},
|
||||||
onHide: function () {
|
onHide: function () {
|
||||||
console.log('App Hide')
|
console.log("App Hide");
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -25,7 +29,6 @@ page {
|
|||||||
rgba(0, 0, 0, 0.1) 0px -10px 15px -3px, rgba(0, 0, 0, 0.1) 0px -4px 6px -4px;
|
rgba(0, 0, 0, 0.1) 0px -10px 15px -3px, rgba(0, 0, 0, 0.1) 0px -4px 6px -4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.shadow-lg {
|
.shadow-lg {
|
||||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||||
}
|
}
|
||||||
@ -317,7 +320,6 @@ page {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.w-0 {
|
.w-0 {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
38
api/file.js
38
api/file.js
@ -1,38 +0,0 @@
|
|||||||
const env = __VITE_ENV__;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件(图片等)
|
|
||||||
* 与 zdh-hlw-patient 的 /file/upload 接口保持一致
|
|
||||||
*/
|
|
||||||
export function uploadFile(tempFilePath, businessType, accessLevel = 'public') {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
uni.uploadFile({
|
|
||||||
url: `${env.MP_API_BASE_URL}/file/upload`,
|
|
||||||
filePath: tempFilePath,
|
|
||||||
name: 'file',
|
|
||||||
formData: {
|
|
||||||
businessType,
|
|
||||||
accessLevel,
|
|
||||||
},
|
|
||||||
success: (res) => {
|
|
||||||
try {
|
|
||||||
const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
|
|
||||||
if (data && data.success) {
|
|
||||||
resolve(data.data);
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('upload file parse error:', e);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.log('upload file error:', err);
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -112,7 +112,6 @@ onLoad((opts) => {
|
|||||||
console.log("redirectUrl", redirectUrl.value);
|
console.log("redirectUrl", redirectUrl.value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 支持 redirect 参数(来自 useGuard)或 redirectUrl 参数
|
|
||||||
if (opts.redirect) {
|
if (opts.redirect) {
|
||||||
redirectUrl.value = decodeURIComponent(opts.redirect);
|
redirectUrl.value = decodeURIComponent(opts.redirect);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,45 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="dept-page">
|
<view class="dept-page">
|
||||||
<view class="dept-container bg-white">
|
<view class="dept-container bg-white">
|
||||||
<scroll-view class="sidebar" scroll-y>
|
<view class="sidebar">
|
||||||
|
<view class="sidebar-search">
|
||||||
|
<input
|
||||||
|
v-model="keyword"
|
||||||
|
class="search-input"
|
||||||
|
type="text"
|
||||||
|
confirm-type="search"
|
||||||
|
placeholder="搜索一级科室"
|
||||||
|
placeholder-class="search-placeholder"
|
||||||
|
/>
|
||||||
|
<view v-if="keyword" class="clear-btn" @click="keyword = ''"
|
||||||
|
>清空</view
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
<scroll-view class="sidebar-list" scroll-y>
|
||||||
<view
|
<view
|
||||||
v-for="item in level1List"
|
v-for="item in filteredLevel1List"
|
||||||
:key="item._id"
|
:key="item.deptId"
|
||||||
:class="[
|
:class="[
|
||||||
'sidebar-item',
|
'sidebar-item',
|
||||||
item._id === selectedParentId ? 'active' : '',
|
item.deptId === selectedParentId ? 'active' : '',
|
||||||
]"
|
]"
|
||||||
@click="selectParent(item)"
|
@click="selectParent(item)"
|
||||||
>
|
>
|
||||||
<text class="sidebar-text">{{ item.hlwDeptName }}</text>
|
<text class="sidebar-text">{{
|
||||||
|
item.hlwDeptName || item.deptName
|
||||||
|
}}</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<scroll-view class="content" scroll-y>
|
<scroll-view class="content" scroll-y>
|
||||||
<view v-if="contentList.length" class="content-list">
|
<view v-if="contentList.length" class="content-list">
|
||||||
<view
|
<view
|
||||||
v-for="child in contentList"
|
v-for="child in contentList"
|
||||||
:key="child._id"
|
:key="child.deptId"
|
||||||
:class="['dept-item', isSelected(child) ? 'selected' : '']"
|
:class="['dept-item', isSelected(child) ? 'selected' : '']"
|
||||||
|
@click="selectDept(child)"
|
||||||
>
|
>
|
||||||
<view class="dept-name-row" @click="selectDept(child)">
|
<view class="dept-name-row">
|
||||||
<view class="dept-name">{{ child.deptName }}</view>
|
<view class="dept-name">{{ child.deptName }}</view>
|
||||||
<text v-if="isSelected(child)" class="check-tag">已选</text>
|
<text v-if="isSelected(child)" class="check-tag">已选</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
|
||||||
v-if="child.children && child.children.length"
|
|
||||||
class="dept-sub-list"
|
|
||||||
>
|
|
||||||
<view
|
|
||||||
v-for="sub in child.children"
|
|
||||||
:key="sub._id"
|
|
||||||
:class="['dept-sub-item', isSelected(sub) ? 'selected' : '']"
|
|
||||||
@click.stop="selectDept(sub)"
|
|
||||||
>
|
|
||||||
<text class="dept-sub-text">{{ sub.deptName }}</text>
|
|
||||||
<text v-if="isSelected(sub)" class="check-dot" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-else class="empty-wrapper">
|
<view v-else class="empty-wrapper">
|
||||||
@ -47,16 +51,20 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="footer-tip">
|
<view class="footer-tip">
|
||||||
如没有符合的内容,请联系客服
|
如没有符合的内容,请联系客服
|
||||||
<text class="link" @click="contactService">点击添加客服</text>
|
<text class="link" @click="contactService">点击添加客服</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
import useGuard from "@/hooks/useGuard.js";
|
import useGuard from "@/hooks/useGuard.js";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
import EmptyData from "@/components/empty-data.vue";
|
import EmptyData from "@/components/empty-data.vue";
|
||||||
import api from "@/utils/api";
|
import api from "@/utils/api";
|
||||||
import { toast } from "@/utils/widget";
|
import { toast } from "@/utils/widget";
|
||||||
@ -66,6 +74,7 @@ const { useLoad } = useGuard();
|
|||||||
const deptList = ref([]);
|
const deptList = ref([]);
|
||||||
const selectedParentId = ref("");
|
const selectedParentId = ref("");
|
||||||
const selectedDeptId = ref("");
|
const selectedDeptId = ref("");
|
||||||
|
const keyword = ref("");
|
||||||
|
|
||||||
const sorter = (a, b) => {
|
const sorter = (a, b) => {
|
||||||
const sortA = typeof a.sort === "number" ? a.sort : 0;
|
const sortA = typeof a.sort === "number" ? a.sort : 0;
|
||||||
@ -80,6 +89,14 @@ const level1List = computed(() =>
|
|||||||
deptList.value.filter((i) => i.level === 1).sort(sorter)
|
deptList.value.filter((i) => i.level === 1).sort(sorter)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const filteredLevel1List = computed(() => {
|
||||||
|
const key = keyword.value.trim().toLowerCase();
|
||||||
|
if (!key) return level1List.value;
|
||||||
|
return level1List.value.filter((i) =>
|
||||||
|
i.deptName.toString().toLowerCase().includes(key)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const contentList = computed(() => {
|
const contentList = computed(() => {
|
||||||
const children = deptList.value
|
const children = deptList.value
|
||||||
.filter(
|
.filter(
|
||||||
@ -87,31 +104,20 @@ const contentList = computed(() => {
|
|||||||
i.level === 2 && i.parentId && i.parentId === selectedParentId.value
|
i.level === 2 && i.parentId && i.parentId === selectedParentId.value
|
||||||
)
|
)
|
||||||
.sort(sorter);
|
.sort(sorter);
|
||||||
if (children.length === 0) {
|
return children;
|
||||||
// 如果没有二级科室,允许一级科室直接选择
|
|
||||||
const currentParent = deptList.value.find(
|
|
||||||
(i) => i._id === selectedParentId.value
|
|
||||||
);
|
|
||||||
return currentParent ? [{ ...currentParent, children: [] }] : [];
|
|
||||||
}
|
|
||||||
const level3List = deptList.value.filter((i) => i.level === 3);
|
|
||||||
return children.map((item) => ({
|
|
||||||
...item,
|
|
||||||
children: level3List.filter((c) => c.parentId === item._id).sort(sorter),
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function isSelected(item) {
|
function isSelected(item) {
|
||||||
const targetId = selectedDeptId.value;
|
const targetId = selectedDeptId.value;
|
||||||
return !!targetId && (item.deptId === targetId || item._id === targetId);
|
return !!targetId && (item.deptId === targetId || item.deptId === targetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchDeptList() {
|
async function fetchDeptList() {
|
||||||
uni.showLoading({ title: "加载中..." });
|
uni.showLoading({ title: "加载中..." });
|
||||||
try {
|
try {
|
||||||
const res = await api("getDeptList");
|
const res = await api("getDeptList");
|
||||||
if (res && res.success && Array.isArray(res.data.list)) {
|
if (res && res.success && Array.isArray(res.data)) {
|
||||||
deptList.value = res.data.list;
|
deptList.value = res.data;
|
||||||
hydrateSelectedParent();
|
hydrateSelectedParent();
|
||||||
} else {
|
} else {
|
||||||
toast(res?.message || "获取科室失败");
|
toast(res?.message || "获取科室失败");
|
||||||
@ -130,7 +136,7 @@ function hydrateSelectedParent() {
|
|||||||
const match =
|
const match =
|
||||||
deptList.value.find(
|
deptList.value.find(
|
||||||
(i) =>
|
(i) =>
|
||||||
i._id === selectedDeptId.value || i.deptId === selectedDeptId.value
|
i.deptId === selectedDeptId.value || i.deptId === selectedDeptId.value
|
||||||
) || null;
|
) || null;
|
||||||
if (match?.parentId) {
|
if (match?.parentId) {
|
||||||
selectedParentId.value = match.parentId;
|
selectedParentId.value = match.parentId;
|
||||||
@ -144,12 +150,25 @@ function hydrateSelectedParent() {
|
|||||||
// 最后兜底选第一个一级科室
|
// 最后兜底选第一个一级科室
|
||||||
const first = level1List.value[0];
|
const first = level1List.value[0];
|
||||||
if (first) {
|
if (first) {
|
||||||
selectedParentId.value = first._id;
|
selectedParentId.value = first.deptId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
filteredLevel1List,
|
||||||
|
(list) => {
|
||||||
|
if (!list.length) return;
|
||||||
|
const exists = list.some((i) => i.deptId === selectedParentId.value);
|
||||||
|
if (!exists) {
|
||||||
|
selectedParentId.value = list[0].deptId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: false }
|
||||||
|
);
|
||||||
|
|
||||||
function selectParent(item) {
|
function selectParent(item) {
|
||||||
selectedParentId.value = item._id;
|
selectedParentId.value = item.deptId;
|
||||||
|
console.log("selectedParentId", selectedParentId.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitSelection(item) {
|
function emitSelection(item) {
|
||||||
@ -158,14 +177,16 @@ function emitSelection(item) {
|
|||||||
? getOpenerEventChannel()
|
? getOpenerEventChannel()
|
||||||
: null;
|
: null;
|
||||||
eventChannel?.emit("deptSelected", {
|
eventChannel?.emit("deptSelected", {
|
||||||
id: item._id,
|
id: item.deptId,
|
||||||
deptId: item.deptId || item._id,
|
deptId: item.deptId || item.deptId,
|
||||||
name: item.deptName,
|
name: item.deptName,
|
||||||
|
level: item.level,
|
||||||
|
parentId: item.parentId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectDept(item) {
|
function selectDept(item) {
|
||||||
selectedDeptId.value = item.deptId || item._id;
|
selectedDeptId.value = item.deptId || item.deptId;
|
||||||
emitSelection(item);
|
emitSelection(item);
|
||||||
uni.navigateBack();
|
uni.navigateBack();
|
||||||
}
|
}
|
||||||
@ -211,9 +232,43 @@ onLoad((opts) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 210rpx;
|
width: 230rpx;
|
||||||
background: #f8f9fb;
|
background: #f8f9fb;
|
||||||
border-right: 1px solid #eef0f5;
|
border-right: 1px solid #eef0f5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-search {
|
||||||
|
padding: 16rpx 14rpx 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 64rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
border-radius: 32rpx;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5e8ef;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-placeholder {
|
||||||
|
color: #a0a8b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #2a6ff2;
|
||||||
|
padding: 6rpx 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-list {
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item {
|
.sidebar-item {
|
||||||
@ -237,6 +292,13 @@ onLoad((opts) => {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-empty {
|
||||||
|
padding: 40rpx 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #8c94a6;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|||||||
@ -3,17 +3,13 @@
|
|||||||
<!-- 表单区域 -->
|
<!-- 表单区域 -->
|
||||||
<view class="form-section bg-white">
|
<view class="form-section bg-white">
|
||||||
<!-- 姓名 -->
|
<!-- 姓名 -->
|
||||||
<common-cell name="姓名" :required="true">
|
<form-input
|
||||||
<view class="form-content__wrapper">
|
name="姓名"
|
||||||
<input
|
:required="true"
|
||||||
class="flex-main-content text-right"
|
:form="formData"
|
||||||
v-model="formData.name"
|
title="name"
|
||||||
placeholder="请输入"
|
@change="handleFieldChange"
|
||||||
placeholder-style="color: #999"
|
|
||||||
/>
|
/>
|
||||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
|
||||||
</view>
|
|
||||||
</common-cell>
|
|
||||||
<!-- 头像 -->
|
<!-- 头像 -->
|
||||||
<common-cell name="头像">
|
<common-cell name="头像">
|
||||||
<view class="form-content__wrapper" @click="chooseAvatar">
|
<view class="form-content__wrapper" @click="chooseAvatar">
|
||||||
@ -24,9 +20,6 @@
|
|||||||
:src="formData.avatar"
|
:src="formData.avatar"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
<view v-else class="avatar-placeholder">
|
|
||||||
<text class="avatar-icon">👤</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
@ -39,14 +32,12 @@
|
|||||||
:range="genderOptions"
|
:range="genderOptions"
|
||||||
@change="handleFieldChange"
|
@change="handleFieldChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 手机号(不可修改) -->
|
<!-- 手机号(不可修改) -->
|
||||||
<common-cell name="手机号 (不可修改)">
|
<common-cell name="手机号 (不可修改)">
|
||||||
<view class="form-content__wrapper">
|
<view class="form-content__wrapper">
|
||||||
<view class="flex-main-content text-dark">{{ formData.phone }}</view>
|
<view class="flex-main-content text-dark">{{ formData.mobile }}</view>
|
||||||
</view>
|
</view>
|
||||||
</common-cell>
|
</common-cell>
|
||||||
|
|
||||||
<!-- 岗位 -->
|
<!-- 岗位 -->
|
||||||
<form-select
|
<form-select
|
||||||
name="岗位"
|
name="岗位"
|
||||||
@ -55,7 +46,6 @@
|
|||||||
:range="positionOptions"
|
:range="positionOptions"
|
||||||
@change="handleFieldChange"
|
@change="handleFieldChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 职称 -->
|
<!-- 职称 -->
|
||||||
<form-select
|
<form-select
|
||||||
name="职称"
|
name="职称"
|
||||||
@ -64,9 +54,8 @@
|
|||||||
:range="titleOptions"
|
:range="titleOptions"
|
||||||
@change="handleFieldChange"
|
@change="handleFieldChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 科室 -->
|
<!-- 科室 -->
|
||||||
<common-cell name="科室" :required="true">
|
<common-cell name="科室">
|
||||||
<view class="form-content__wrapper" @click="openDepartmentSelect">
|
<view class="form-content__wrapper" @click="openDepartmentSelect">
|
||||||
<view
|
<view
|
||||||
class="flex-main-content text-right"
|
class="flex-main-content text-right"
|
||||||
@ -78,6 +67,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</common-cell>
|
</common-cell>
|
||||||
|
|
||||||
|
|
||||||
<!-- 个人介绍 -->
|
<!-- 个人介绍 -->
|
||||||
<form-textarea
|
<form-textarea
|
||||||
name="个人介绍"
|
name="个人介绍"
|
||||||
@ -87,7 +77,6 @@
|
|||||||
@change="handleFieldChange"
|
@change="handleFieldChange"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部按钮 -->
|
<!-- 底部按钮 -->
|
||||||
<view class="button-footer">
|
<view class="button-footer">
|
||||||
<view class="btn btn-cancel" @click="handleCancel">取消</view>
|
<view class="btn btn-cancel" @click="handleCancel">取消</view>
|
||||||
@ -99,26 +88,22 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import useGuard from "@/hooks/useGuard.js";
|
import useGuard from "@/hooks/useGuard.js";
|
||||||
import {
|
import { chooseAndUploadImage } from "@/utils/file.js";
|
||||||
getDoctorInfoByAccountId,
|
|
||||||
updateDoctorInfo,
|
|
||||||
} from "@/api/doctor/doctor.js";
|
|
||||||
import { uploadFile } from "@/api/file.js";
|
|
||||||
import useAccountStore from "@/store/account.js";
|
import useAccountStore from "@/store/account.js";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import CommonCell from "@/components/form-template/common-cell.vue";
|
import CommonCell from "@/components/form-template/common-cell.vue";
|
||||||
|
import FormInput from "@/components/form-template/form-cell/form-input.vue";
|
||||||
import FormSelect from "@/components/form-template/form-cell/form-select.vue";
|
import FormSelect from "@/components/form-template/form-cell/form-select.vue";
|
||||||
import FormTextarea from "@/components/form-template/form-cell/form-textarea.vue";
|
import FormTextarea from "@/components/form-template/form-cell/form-textarea.vue";
|
||||||
|
import api from "@/utils/api.js";
|
||||||
const { account, mobile } = storeToRefs(useAccountStore());
|
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||||
const { useLoad } = useGuard();
|
const { useLoad } = useGuard();
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
name: "",
|
name: "",
|
||||||
avatar: "",
|
avatar: "",
|
||||||
gender: "",
|
gender: "",
|
||||||
phone: "",
|
mobile: "",
|
||||||
position: "",
|
position: "",
|
||||||
title: "",
|
title: "",
|
||||||
department: "",
|
department: "",
|
||||||
@ -130,7 +115,7 @@ const formData = ref({
|
|||||||
// 选项数据
|
// 选项数据
|
||||||
const genderOptions = ["男", "女"];
|
const genderOptions = ["男", "女"];
|
||||||
const positionOptions = ["医生", "护士", "药师", "技师", "其他"];
|
const positionOptions = ["医生", "护士", "药师", "技师", "其他"];
|
||||||
const titleOptions = ["主任医师", "副主任医师", "主治医师", "住院医师", "其他"];
|
const titleOptions = ["主任医师", "副主任医师", "主治医师", "医师", "其他"];
|
||||||
|
|
||||||
// 字段变更处理
|
// 字段变更处理
|
||||||
const handleFieldChange = (e) => {
|
const handleFieldChange = (e) => {
|
||||||
@ -138,40 +123,17 @@ const handleFieldChange = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 选择头像
|
// 选择头像
|
||||||
const chooseAvatar = () => {
|
const chooseAvatar = async () => {
|
||||||
uni.chooseImage({
|
const uploadRes = await chooseAndUploadImage({
|
||||||
count: 1,
|
businessType: "other",
|
||||||
sizeType: ["compressed"],
|
accessLevel: "public",
|
||||||
sourceType: ["album", "camera"],
|
loadingTitle: "上传中...",
|
||||||
success: async (res) => {
|
|
||||||
const tempFilePath = res.tempFilePaths[0];
|
|
||||||
uni.showLoading({
|
|
||||||
title: "上传中...",
|
|
||||||
});
|
});
|
||||||
try {
|
if (uploadRes?.previewUrl) {
|
||||||
const uploadRes = await uploadFile(tempFilePath, "other", "public");
|
|
||||||
if (uploadRes && uploadRes.previewUrl) {
|
|
||||||
formData.value.avatar = uploadRes.previewUrl;
|
formData.value.avatar = uploadRes.previewUrl;
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: "上传成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error("上传失败");
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: "上传失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 取消
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
uni.navigateBack();
|
uni.navigateBack();
|
||||||
};
|
};
|
||||||
@ -186,98 +148,45 @@ const handleSave = async () => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: "保存中...",
|
title: "保存中...",
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
|
||||||
// 调用更新接口
|
|
||||||
const result = await updateDoctorInfo({
|
|
||||||
name: formData.value.name,
|
|
||||||
avatar: formData.value.avatar,
|
|
||||||
gender: formData.value.gender,
|
|
||||||
position: formData.value.position,
|
|
||||||
title: formData.value.title,
|
|
||||||
departmentId: formData.value.departmentId,
|
|
||||||
department: formData.value.departmentName || formData.value.department,
|
|
||||||
intro: formData.value.intro,
|
|
||||||
});
|
|
||||||
|
|
||||||
uni.hideLoading();
|
|
||||||
|
|
||||||
if (result && result.success) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "保存成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateBack();
|
|
||||||
}, 1500);
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: result?.message || "保存失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: "保存失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
console.error("保存失败:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载医生信息
|
|
||||||
const loadDoctorInfo = async () => {
|
|
||||||
if (!account.value?.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
uni.showLoading({
|
|
||||||
title: "加载中...",
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await getDoctorInfoByAccountId(account.value.id);
|
|
||||||
uni.hideLoading();
|
|
||||||
|
|
||||||
if (result && result.success && result.data) {
|
|
||||||
const doctorInfo = result.data;
|
|
||||||
formData.value = {
|
|
||||||
name: doctorInfo.name || "",
|
|
||||||
avatar: doctorInfo.avatar || "",
|
|
||||||
gender: doctorInfo.gender || "",
|
|
||||||
phone: doctorInfo.phone || mobile.value || "",
|
|
||||||
position: doctorInfo.position || "",
|
|
||||||
title: doctorInfo.title || "",
|
|
||||||
department: doctorInfo.department || "",
|
|
||||||
departmentName: doctorInfo.department || "",
|
|
||||||
departmentId: doctorInfo.departmentId || "",
|
|
||||||
intro: doctorInfo.intro || "",
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// 如果没有医生信息,设置手机号为授权登录的手机号
|
|
||||||
if (mobile.value) {
|
|
||||||
formData.value.phone = mobile.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
uni.hideLoading();
|
|
||||||
console.error("加载医生信息失败:", error);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useLoad(() => {
|
useLoad(() => {
|
||||||
// 初始化时设置手机号为授权登录的手机号
|
debugger
|
||||||
if (mobile.value && !formData.value.phone) {
|
if (doctorInfo.value) {
|
||||||
formData.value.phone = mobile.value;
|
formData.value = doctorInfo.value;
|
||||||
|
} else {
|
||||||
|
formData.value.mobile = account.value.mobile;
|
||||||
}
|
}
|
||||||
loadDoctorInfo();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 创建医生信息
|
||||||
|
const createDoctorInfo = async () => {
|
||||||
|
let params = {
|
||||||
|
anotherName: formData.value.name,
|
||||||
|
avatar: formData.value.avatar,
|
||||||
|
gender: formData.value.gender,
|
||||||
|
mobile: formData.value.mobile,
|
||||||
|
deptIds: [],
|
||||||
|
};
|
||||||
|
const res = await api("addCorpMember", {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
if (res.success && res.data) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "创建成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "创建失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
console.error("创建医生信息失败:", res);
|
||||||
|
}
|
||||||
|
};
|
||||||
// 打开科室选择
|
// 打开科室选择
|
||||||
const openDepartmentSelect = () => {
|
const openDepartmentSelect = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: 'pages/message/message',
|
path: 'pages/message/message',
|
||||||
meta: { title: '首页', login: true },
|
meta: { title: '首页', login: false },
|
||||||
style: { navigationStyle: 'custom' }
|
style: { navigationStyle: 'custom' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/work/work',
|
path: 'pages/work/work',
|
||||||
meta: { title: '工作台', login: true }
|
meta: { title: '工作台', login: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/work/profile',
|
path: 'pages/work/profile',
|
||||||
meta: { title: '完善个人信息', login: true }
|
meta: { title: '完善个人信息', login: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/work/department-select',
|
path: 'pages/work/department-select',
|
||||||
meta: { title: '选择科室', login: true }
|
meta: { title: '选择科室', login: false }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -12,14 +12,14 @@ export default defineStore("accountStore", () => {
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
// IM 相关
|
// IM 相关
|
||||||
const openid = ref("");
|
const openid = ref("");
|
||||||
const isIMInitialized = ref(false);
|
// 医生信息
|
||||||
// 手机号(从授权登录获取)
|
const doctorInfo = ref(null);
|
||||||
const mobile = ref(uni.getStorageSync('user_mobile') || '');
|
|
||||||
|
|
||||||
async function login(phoneCode = '') {
|
async function login(phoneCode = '') {
|
||||||
|
debugger
|
||||||
if (loading.value) return;
|
if (loading.value) return;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { code } = await uni.login({
|
const { code } = await uni.login({
|
||||||
appid,
|
appid,
|
||||||
@ -32,18 +32,23 @@ export default defineStore("accountStore", () => {
|
|||||||
phoneCode,
|
phoneCode,
|
||||||
code,
|
code,
|
||||||
});
|
});
|
||||||
loading.value = false
|
loading.value = false;
|
||||||
console.log(res)
|
if (res.success && res.data) {
|
||||||
if (res.success && res.data && res.data.mobile) {
|
if (!res.data.mobile) {
|
||||||
account.value = res.data;
|
const pages = getCurrentPages();
|
||||||
openid.value = res.data.openid || res.data.openId || res.data.userId || "";
|
const current = pages[pages.length - 1];
|
||||||
isIMInitialized.value = false;
|
const params = current && current.options
|
||||||
clearInitIMPromise();
|
? Object.keys(current.options).map(key => `${key}=${current.options[key]}`).join('&')
|
||||||
// 存储手机号
|
: '';
|
||||||
if (res.data.mobile) {
|
const redirectUrl = current && current.route ? `/${current.route}${params ? `?${params}` : ''}` : '';
|
||||||
mobile.value = res.data.mobile;
|
const target = redirectUrl ? `/pages/login/login?redirect=${encodeURIComponent(redirectUrl)}` : '/pages/login/login';
|
||||||
uni.setStorageSync('user_mobile', res.data.mobile);
|
uni.redirectTo({ url: target });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
account.value = res.data;
|
||||||
|
openid.value = res.data.openid;
|
||||||
|
debugger;
|
||||||
|
await getDoctorInfo(openid.value);
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,25 +59,18 @@ export default defineStore("accountStore", () => {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async function getDoctorInfo(weChatOpenId) {
|
||||||
* 登录后初始化 IM(供聊天页调用)
|
|
||||||
* @param {string} userID - IM userID(通常用 openid/openId)
|
|
||||||
*/
|
|
||||||
async function initIMAfterLogin(userID) {
|
|
||||||
const uid = userID || openid.value;
|
|
||||||
if (!uid) {
|
|
||||||
toast("缺少 openid,无法初始化IM");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await getInitIMPromise(uid, true);
|
const res = await api('getCorpMemberData', {
|
||||||
isIMInitialized.value = true;
|
weChatOpenId,
|
||||||
return true;
|
});
|
||||||
|
if (res.success && res.data) {
|
||||||
|
doctorInfo.value = res.data;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
isIMInitialized.value = false;
|
console.error('获取医生信息失败:', e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { account, openid, isIMInitialized, mobile, login, initIMAfterLogin }
|
return { account, openid, doctorInfo, login }
|
||||||
})
|
})
|
||||||
@ -7,7 +7,10 @@ const urlsConfig = {
|
|||||||
getTeamData: 'getTeamData',
|
getTeamData: 'getTeamData',
|
||||||
queryWxJoinedTeams: 'queryWxJoinedTeams',
|
queryWxJoinedTeams: 'queryWxJoinedTeams',
|
||||||
wxAppLogin: 'wxAppLogin',
|
wxAppLogin: 'wxAppLogin',
|
||||||
getDeptList:'getHlwDeptList',
|
getDeptList: 'getRealDeptList',
|
||||||
|
getHospitalList: 'getRealHospital',
|
||||||
|
addCorpMember: 'addCorpMember',
|
||||||
|
getCorpMemberData: 'getCorpMemberData'
|
||||||
},
|
},
|
||||||
|
|
||||||
knowledgeBase: {
|
knowledgeBase: {
|
||||||
@ -25,6 +28,7 @@ const urlsConfig = {
|
|||||||
wecom: {
|
wecom: {
|
||||||
addContactWay: 'addContactWay'
|
addContactWay: 'addContactWay'
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
const urls = Object.keys(urlsConfig).reduce((acc, path) => {
|
const urls = Object.keys(urlsConfig).reduce((acc, path) => {
|
||||||
const config = urlsConfig[path] || {};
|
const config = urlsConfig[path] || {};
|
||||||
@ -55,3 +59,4 @@ export default async function api(urlId, data) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
75
utils/file.js
Normal file
75
utils/file.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
const env = __VITE_ENV__;
|
||||||
|
|
||||||
|
export async function uploadFile(tempFilePath, businessType, accessLevel = 'public') {
|
||||||
|
try {
|
||||||
|
const res = await new Promise((resolve, reject) => {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: `${env.MP_API_BASE_URL}/upload`,
|
||||||
|
filePath: tempFilePath,
|
||||||
|
name: 'file',
|
||||||
|
formData: { businessType, accessLevel },
|
||||||
|
success: (resp) => resolve(resp),
|
||||||
|
fail: (err) => reject(err),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
|
||||||
|
if (data && data.success) {
|
||||||
|
return data.data;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('upload file error:', e);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择单张图片并上传,成功返回上传结果(接口返回的 data.data)
|
||||||
|
* - 内部会处理 loading / toast
|
||||||
|
* - 失败或取消返回 null
|
||||||
|
*/
|
||||||
|
export async function chooseAndUploadImage(options = {}) {
|
||||||
|
const {
|
||||||
|
count = 1,
|
||||||
|
sizeType = ['compressed'],
|
||||||
|
sourceType = ['album', 'camera'],
|
||||||
|
businessType = 'other',
|
||||||
|
accessLevel = 'public',
|
||||||
|
loadingTitle = '上传中...',
|
||||||
|
successToast = '上传成功',
|
||||||
|
failToast = '上传失败',
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const imageResult = await new Promise((resolve) => {
|
||||||
|
uni.chooseImage({
|
||||||
|
count,
|
||||||
|
sizeType,
|
||||||
|
sourceType,
|
||||||
|
success: (res) => resolve(res),
|
||||||
|
fail: () => resolve(null),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const tempFilePath = imageResult?.tempFilePaths?.[0];
|
||||||
|
if (!tempFilePath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showLoading({ title: loadingTitle });
|
||||||
|
try {
|
||||||
|
const uploadRes = await uploadFile(tempFilePath, businessType, accessLevel);
|
||||||
|
uni.hideLoading();
|
||||||
|
if (uploadRes) {
|
||||||
|
uni.showToast({ title: successToast, icon: 'success' });
|
||||||
|
return uploadRes;
|
||||||
|
}
|
||||||
|
uni.showToast({ title: failToast, icon: 'none' });
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({ title: failToast, icon: 'none' });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user