Merge branch 'dev-wdb' of http://175.27.226.205:3000/huxuejian/ykt-wxapp into dev-wdb
# Conflicts: # App.vue # pages.json
@ -1,4 +1,5 @@
|
||||
MP_API_BASE_URL=http://192.168.60.2:8080
|
||||
MP_CACHE_PREFIX=development
|
||||
MP_WX_APP_ID=wx93af55767423938e
|
||||
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
||||
MP_TIM_SDK_APP_ID=1600072268
|
||||
|
||||
737
App.vue
@ -1,369 +1,368 @@
|
||||
<script>
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import {
|
||||
globalTimChatManager
|
||||
} from "@/utils/tim-chat.js";
|
||||
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
// 需在 pinia 安装后再获取 store,避免 getActivePinia 报错
|
||||
const {
|
||||
login
|
||||
} = useAccountStore();
|
||||
login();
|
||||
console.log("App Launch: ");
|
||||
},
|
||||
onShow: function() {
|
||||
console.log("App Show");
|
||||
},
|
||||
onHide: function() {
|
||||
console.log("App Hide");
|
||||
try {
|
||||
if (globalTimChatManager && globalTimChatManager.tim) {
|
||||
console.log('小程序退出,开始退出腾讯IM');
|
||||
globalTimChatManager.destroy();
|
||||
console.log('腾讯IM退出成功');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('退出腾讯IM失败:', error);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$primary-color: #0074ff;
|
||||
|
||||
page {
|
||||
height: 100%;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.shadow-up {
|
||||
box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0.1) 0px -10px 15px -3px, rgba(0, 0, 0, 0.1) 0px -4px 6px -4px;
|
||||
}
|
||||
|
||||
.shadow-lg {
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.bg-gray {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.bg-primary {
|
||||
background: $primary-color;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background: green;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bg-warning {
|
||||
background: orange;
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
background: rgb(248 113 113);
|
||||
}
|
||||
|
||||
.py-5 {
|
||||
padding-top: 10rpx;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.p-15 {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.p-10 {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.px-5 {
|
||||
padding-left: 10rpx;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.px-10 {
|
||||
padding-left: 20rpx;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.px-12 {
|
||||
padding-left: 24rpx;
|
||||
padding-right: 24rpx;
|
||||
}
|
||||
|
||||
.px-15 {
|
||||
padding-left: 30rpx;
|
||||
padding-right: 30rpx;
|
||||
}
|
||||
|
||||
.pt-5 {
|
||||
padding-top: 10rpx;
|
||||
}
|
||||
|
||||
.pt-15 {
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.break-all {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.pb-5 {
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.pb-10 {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.py-10 {
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.py-12 {
|
||||
padding-top: 24rpx;
|
||||
padding-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.py-15 {
|
||||
padding-top: 30rpx;
|
||||
padding-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.mr-5 {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.mr-10 {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.ml-15 {
|
||||
margin-left: 30rpx;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.mx-10 {
|
||||
margin-left: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.rounded-sm {
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.rounded-full {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.text-black {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.leading-normal {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.border {
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
.border-dashed {
|
||||
border: 1px dashed #eee;
|
||||
}
|
||||
|
||||
.border-b {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.border-auto {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.border-dashed-auto {
|
||||
border: 1px dashed;
|
||||
}
|
||||
|
||||
.border-primary {
|
||||
border: 1px solid $primary-color;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.safe-bottom-padding {
|
||||
height: env(safe-area-inset-bottom);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import { globalTimChatManager } from "@/utils/tim-chat.js";
|
||||
|
||||
export default {
|
||||
onLaunch: function () {
|
||||
// 需在 pinia 安装后再获取 store,避免 getActivePinia 报错
|
||||
const { login } = useAccountStore();
|
||||
login();
|
||||
console.log("App Launch: ");
|
||||
},
|
||||
onShow: function () {
|
||||
console.log("App Show");
|
||||
},
|
||||
onHide: function () {
|
||||
console.log("App Hide");
|
||||
// 小程序退出时退出腾讯IM登录
|
||||
// try {
|
||||
// if (globalTimChatManager && globalTimChatManager.tim) {
|
||||
// console.log('小程序退出,开始退出腾讯IM');
|
||||
// globalTimChatManager.destroy();
|
||||
// console.log('腾讯IM退出成功');
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error('退出腾讯IM失败:', error);
|
||||
// }
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$primary-color: #0074ff;
|
||||
|
||||
page {
|
||||
height: 100%;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.shadow-up {
|
||||
box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0.1) 0px -10px 15px -3px, rgba(0, 0, 0, 0.1) 0px -4px 6px -4px;
|
||||
}
|
||||
|
||||
.shadow-lg {
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
.absolute{
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.bg-gray {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.bg-primary {
|
||||
background: $primary-color;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background: green;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bg-warning {
|
||||
background: orange;
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
background: rgb(248 113 113);
|
||||
}
|
||||
|
||||
.py-5 {
|
||||
padding-top: 10rpx;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.p-15 {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.p-10 {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.px-5 {
|
||||
padding-left: 10rpx;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.px-10 {
|
||||
padding-left: 20rpx;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.px-12 {
|
||||
padding-left: 24rpx;
|
||||
padding-right: 24rpx;
|
||||
}
|
||||
|
||||
.px-15 {
|
||||
padding-left: 30rpx;
|
||||
padding-right: 30rpx;
|
||||
}
|
||||
|
||||
.pt-5 {
|
||||
padding-top: 10rpx;
|
||||
}
|
||||
.pt-15 {
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.break-all {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.pb-5 {
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.pb-10 {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.py-10 {
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.py-12 {
|
||||
padding-top: 24rpx;
|
||||
padding-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.py-15 {
|
||||
padding-top: 30rpx;
|
||||
padding-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.mr-5 {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.mr-10 {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.ml-15 {
|
||||
margin-left: 30rpx;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.mx-10 {
|
||||
margin-left: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.rounded-sm {
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.rounded-full {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.text-black {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.leading-normal {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.border {
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
.border-dashed {
|
||||
border: 1px dashed #eee;
|
||||
}
|
||||
|
||||
.border-b {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.border-auto {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.border-dashed-auto {
|
||||
border: 1px dashed;
|
||||
}
|
||||
|
||||
.border-primary {
|
||||
border: 1px solid $primary-color;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.safe-bottom-padding {
|
||||
height: env(safe-area-inset-bottom);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view v-if="showCancel || showConfirm" class="relative px-15 py-12 bg-white text-center"
|
||||
<view v-if="showCancel || showConfirm" class="relative flex px-15 py-12 bg-white text-center"
|
||||
:class="hidedenShadow ? '' : 'shadow-up'">
|
||||
<view v-if="showCancel" class="flex-grow py-10 text-base border-primary rounded text-primary rounded"
|
||||
@click="cancel()">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<picker mode="selector" :range="displayRange" :disabled="disableChange" @change="change($event)">
|
||||
<picker mode="selector" :range="displayRange" :disabled="disableChange" @change="change($event)">
|
||||
<common-cell :name="name" :required="required">
|
||||
<view class="form-content__wrapper">
|
||||
<view class="flex-main-content truncate" :class="value ? '' : 'form__placeholder'">
|
||||
@ -49,9 +49,9 @@ const displayRange = computed(() => {
|
||||
}
|
||||
return props.range;
|
||||
})
|
||||
|
||||
const value = computed(() => {
|
||||
if (!props.form || !props.form[props.title]) return '';
|
||||
|
||||
const currentValue = props.form[props.title];
|
||||
// 如果range是对象数组,找到对应的label显示
|
||||
if (Array.isArray(props.range) && props.range.length > 0 && typeof props.range[0] === 'object') {
|
||||
@ -65,7 +65,7 @@ function change(e) {
|
||||
const selectedValue = props.range[e.detail.value];
|
||||
emits('change', {
|
||||
title: props.title,
|
||||
value: selectedValue
|
||||
value: typeof selectedValue === 'object' ? selectedValue.value : selectedValue
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -4,8 +4,9 @@
|
||||
{{ name }}<text v-if="required" class="form-cell--required"></text>
|
||||
</view>
|
||||
<view class="mt-10">
|
||||
<textarea :disabled="disableChange" :value="value" class="form-textarea" :placeholder="placeholder"
|
||||
placeholder-class="form__placeholder" :maxlength="wordLimit" @input="change($event)" />
|
||||
<textarea :disabled="disableChange" :value="value" class="form-textarea"
|
||||
:class="border ? 'form-textarea--border' : ''" :placeholder="placeholder" placeholder-class="form__placeholder"
|
||||
:maxlength="wordLimit" @input="change($event)" />
|
||||
<view v-if="wordLimit > 0" class="form-textarea__count">
|
||||
{{ value && value.length ? value.length : 0 }} / {{ wordLimit }}
|
||||
</view>
|
||||
@ -17,6 +18,10 @@ import { computed } from 'vue';
|
||||
|
||||
const emits = defineEmits(['change']);
|
||||
const props = defineProps({
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
@ -45,7 +50,7 @@ const placeholder = computed(() => `请输入${props.name || ''}`)
|
||||
const value = computed(() => props.form && props.form && props.form[props.title] ? props.form[props.title] : '')
|
||||
const wordLimit = computed(() => {
|
||||
if (typeof props.wordLimit === 'string' && Number(props.wordLimit) > 0) {
|
||||
return Number.ceil(props.wordLimit)
|
||||
return Math.ceil(props.wordLimit)
|
||||
}
|
||||
if (typeof props.wordLimit === 'number' && props.wordLimit > 0) {
|
||||
return props.wordLimit
|
||||
@ -72,12 +77,15 @@ function change(e) {
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
border: 1px solid #eee;
|
||||
padding: 20rpx;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-textarea--border {
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
.form-textarea__count {
|
||||
padding-top: 20rpx;
|
||||
text-align: right;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<view class="full-page" :style="pageStyle">
|
||||
<view class="full-page" :class="pageClass" :style="pageStyle">
|
||||
<view v-if="hasHeader" class="page-header">
|
||||
<slot name="header"></slot>
|
||||
</view>
|
||||
<view class="page-main" :style="mainStyle">
|
||||
<view class="page-main" :class="mainClass" :style="mainStyle">
|
||||
<view v-if="customScroll" class="page-scroll">
|
||||
<slot></slot>
|
||||
</view>
|
||||
@ -16,7 +16,7 @@
|
||||
<slot name="footer"></slot>
|
||||
</view>
|
||||
<!-- #ifdef MP-->
|
||||
<view class="safeareaBottom"></view>
|
||||
<view v-if="showSafeArea" class="safeareaBottom"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
@ -27,8 +27,11 @@ import useDebounce from '@/utils/useDebounce';
|
||||
const emits = defineEmits(['reachBottom']);
|
||||
const props = defineProps({
|
||||
customScroll: { type: Boolean, default: false },
|
||||
mainClass: { type: String, default: '' },
|
||||
mainStyle: { default: '' },
|
||||
pageStyle: { default: '' }
|
||||
pageClass: { type: String, default: '' },
|
||||
pageStyle: { default: '' },
|
||||
showSafeArea: { type: Boolean, default: true }
|
||||
});
|
||||
const slots = useSlots();
|
||||
const hasHeader = computed(() => !!slots.header);
|
||||
|
||||
@ -39,7 +39,9 @@ export default function useGuard() {
|
||||
|
||||
async function triggleShowEvents() {
|
||||
await promise;
|
||||
onShowEvents.value.forEach(fn => fn(onShowOptions.value))
|
||||
if (account.value && account.value.openid) {
|
||||
onShowEvents.value.forEach(fn => fn(onShowOptions.value))
|
||||
}
|
||||
}
|
||||
|
||||
function useShow(fn) {
|
||||
@ -53,8 +55,10 @@ export default function useGuard() {
|
||||
const route = routes.find(i => page && i.path === page.route);
|
||||
const requireLogin = route && route.meta && route.meta.login;
|
||||
if (requireLogin && !account.value) {
|
||||
const res = await login()
|
||||
if (res) {
|
||||
await login()
|
||||
console.log('login success')
|
||||
console.log(account.value)
|
||||
if (account.value) {
|
||||
resolve()
|
||||
} else {
|
||||
return toLoginPage(opts, page.route);
|
||||
|
||||
26
pages.json
@ -19,18 +19,18 @@
|
||||
"navigationBarTitleText": "常用语"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/case/case",
|
||||
"style": {
|
||||
"navigationBarTitleText": "病例"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/work",
|
||||
"style": {
|
||||
"navigationBarTitleText": "工作台"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/case/case",
|
||||
"style": {
|
||||
"navigationBarTitleText": "病例"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/profile",
|
||||
"style": {
|
||||
@ -43,10 +43,22 @@
|
||||
"navigationBarTitleText": "选择科室"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/verify/assistant",
|
||||
"style": {
|
||||
"navigationBarTitleText": "上传证照"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/verify/doctor",
|
||||
"style": {
|
||||
"navigationBarTitleText": "上传证照"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
"navigationBarTitleText": "授权登录"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view v-if="team" class="pt-lg px-15 flex flex-col items-center text-center">
|
||||
<!-- <view v-if="team" class="pt-lg px-15 flex flex-col items-center text-center">
|
||||
<group-avatar :avatarList="team.avatars" />
|
||||
<view class="mt-15 text-base font-semibold text-dark">{{
|
||||
team.teamName
|
||||
@ -8,20 +8,15 @@
|
||||
<view class="mt-15 text-lg text-dark font-semibold"
|
||||
>为您提供团队个性化专属服务</view
|
||||
>
|
||||
</view>
|
||||
<view v-else class="pt-lg px-15 flex flex-col items-center text-center">
|
||||
v-else
|
||||
</view> -->
|
||||
<view class="pt-lg px-15 flex flex-col items-center text-center">
|
||||
<image src="/static/logo-plain.png" class="logo"></image>
|
||||
<view class="mt-15 text-xl font-semibold text-dark">柚健康</view>
|
||||
<view class="mt-12 text-base text-dark">生命全周期健康管理伙伴</view>
|
||||
</view>
|
||||
<view class="login-btn-wrap">
|
||||
<button
|
||||
v-if="checked"
|
||||
class="login-btn"
|
||||
type="primary"
|
||||
open-type="getPhoneNumber"
|
||||
@getphonenumber="getPhoneNumber"
|
||||
>
|
||||
<button v-if="checked" class="login-btn" type="primary" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
||||
手机号快捷登录
|
||||
</button>
|
||||
<!-- <button v-if="checked" class="login-btn" type="primary" @click="getPhoneNumber()">
|
||||
@ -31,10 +26,7 @@
|
||||
手机号快捷登录
|
||||
</button>
|
||||
</view>
|
||||
<view
|
||||
class="flex items-center justify-center mt-12 px-15"
|
||||
@click="checked = !checked"
|
||||
>
|
||||
<view class="flex items-center justify-center mt-12 px-15" @click="checked = !checked">
|
||||
<checkbox :checked="checked" style="transform: scale(0.7)" />
|
||||
<view class="text-sm text-gray">我已阅读并同意</view>
|
||||
<view class="text-sm text-primary">《用户协议》、</view>
|
||||
@ -44,16 +36,16 @@
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import useAccountStore from "@/store/account";
|
||||
import { get } from "@/utils/cache";
|
||||
import { toast } from "@/utils/widget";
|
||||
|
||||
import groupAvatar from "@/components/group-avatar.vue";
|
||||
|
||||
const team = ref(true);
|
||||
const checked = ref(false);
|
||||
const redirectUrl = ref("");
|
||||
const { doctorInfo } = storeToRefs(useAccountStore());
|
||||
const { login } = useAccountStore();
|
||||
|
||||
function attempRedirect(url) {
|
||||
@ -81,8 +73,8 @@ function remind() {
|
||||
}
|
||||
|
||||
function toHome() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/message/message",
|
||||
uni.switchTab({
|
||||
url: "/pages/work/work",
|
||||
});
|
||||
}
|
||||
|
||||
@ -92,6 +84,10 @@ async function getPhoneNumber(e) {
|
||||
const res = await login(phoneCode);
|
||||
if (res && redirectUrl.value) {
|
||||
await attempToPage(redirectUrl.value);
|
||||
} else if (res && !doctorInfo.value) {
|
||||
uni.redirectTo({
|
||||
url: '/pages/work/profile'
|
||||
})
|
||||
} else if (res) {
|
||||
toHome();
|
||||
}
|
||||
@ -109,7 +105,6 @@ onLoad((opts) => {
|
||||
if (opts.source === "teamInvite") {
|
||||
team.value = get("invite-team-info");
|
||||
redirectUrl.value = `/pages/archive/edit-archive?teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
|
||||
console.log("redirectUrl", redirectUrl.value);
|
||||
return;
|
||||
}
|
||||
if (opts.redirect) {
|
||||
|
||||
64
pages/work/components/cert-popup.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<uni-popup ref="popup" type="center" :mask-click="false">
|
||||
<view class="bg-white rounded overflow-hidden" style="width: 690rpx;">
|
||||
<view class="px-15 py-12 text-center text-lg font-semibold text-dark">
|
||||
认证须知
|
||||
</view>
|
||||
<view class="text-base text-dark px-15 leading-normal font-semibold mt-10">
|
||||
1、认证通过后,您个人账号病历档案管理数(含所有团队)上限由10个升级至100个;
|
||||
</view>
|
||||
<view class="px-15 leading-normal mt-10 ">
|
||||
<text class="text-base text-dark">2、认证前请仔细核对个人信息,确保准确无误。认证后部分信息不支持修改,包括姓名、岗位等。如需修改以上信息,请联系客服人工处理</text>
|
||||
</view>
|
||||
<view class="mt-10 px-15 leading-normal font-semibold pb-50 text-lg text-primary" @click="toService()">点击添加客服
|
||||
</view>
|
||||
<view class="footer-buttons">
|
||||
<button-footer hideden-shadow confirmText="去认证" @confirm="confirm()" @cancel="close()" />
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import ButtonFooter from '@/components/button-footer.vue';
|
||||
|
||||
const emits = defineEmits(['close', 'confirm'])
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const popup = ref()
|
||||
|
||||
function close() {
|
||||
emits('close')
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
close()
|
||||
uni.navigateTo({
|
||||
url: "/pages/work/profile?type=cert",
|
||||
});
|
||||
}
|
||||
|
||||
function toService() {
|
||||
close()
|
||||
}
|
||||
|
||||
watch(() => props.visible, n => {
|
||||
if (n) {
|
||||
popup.value && popup.value.open()
|
||||
} else {
|
||||
popup.value && popup.value.close()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pb-50 {
|
||||
padding-bottom: 100rpx;
|
||||
}
|
||||
</style>
|
||||
308
pages/work/profile copy.vue
Normal file
@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<view class="profile-page">
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section bg-white">
|
||||
<!-- 姓名 -->
|
||||
<form-input
|
||||
name="姓名"
|
||||
:required="true"
|
||||
:form="formData"
|
||||
title="anotherName"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 头像 -->
|
||||
<common-cell name="头像">
|
||||
<view class="form-content__wrapper" @click="chooseAvatar">
|
||||
<view class="flex-main-content flex items-center">
|
||||
<image
|
||||
v-if="formData.avatar"
|
||||
class="avatar-preview"
|
||||
:src="formData.avatar"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||
</view>
|
||||
</common-cell>
|
||||
<!-- 性别 -->
|
||||
<form-select
|
||||
name="性别"
|
||||
:form="formData"
|
||||
title="gender"
|
||||
:range="genderOptions"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 手机号(不可修改) -->
|
||||
<common-cell name="手机号 (不可修改)">
|
||||
<view class="form-content__wrapper">
|
||||
<view class="flex-main-content text-dark">{{ formData.mobile }}</view>
|
||||
</view>
|
||||
</common-cell>
|
||||
<!-- 岗位 -->
|
||||
<form-select
|
||||
name="岗位"
|
||||
:form="formData"
|
||||
title="position"
|
||||
:range="positionOptions"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 职称 -->
|
||||
<form-select
|
||||
name="职称"
|
||||
:form="formData"
|
||||
title="title"
|
||||
:range="titleOptions"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 科室 -->
|
||||
<common-cell name="科室">
|
||||
<view class="form-content__wrapper" @click="openDepartmentSelect">
|
||||
<view
|
||||
class="flex-main-content text-right"
|
||||
:class="{ 'text-placeholder': !formData.departmentName }"
|
||||
>
|
||||
{{ formData.departmentName || "请选择" }}
|
||||
</view>
|
||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||
</view>
|
||||
</common-cell>
|
||||
|
||||
<!-- 个人介绍 -->
|
||||
<form-textarea
|
||||
name="个人介绍"
|
||||
:form="formData"
|
||||
title="intro"
|
||||
:word-limit="500"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
</view>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="button-footer">
|
||||
<view class="btn btn-cancel" @click="handleCancel">取消</view>
|
||||
<view class="btn btn-save" @click="handleSave">保存</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import useGuard from "@/hooks/useGuard.js";
|
||||
import { chooseAndUploadImage } from "@/utils/file.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
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 FormTextarea from "@/components/form-template/form-cell/form-textarea.vue";
|
||||
import api from "@/utils/api.js";
|
||||
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||
const { useLoad } = useGuard();
|
||||
const { getDoctorInfo } = useAccountStore();
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
anotherName: "",
|
||||
avatar: "",
|
||||
gender: "",
|
||||
mobile: "",
|
||||
position: "",
|
||||
title: "",
|
||||
department: "",
|
||||
departmentName: "",
|
||||
departmentId: "",
|
||||
intro: "",
|
||||
});
|
||||
|
||||
// 选项数据
|
||||
const genderOptions = [
|
||||
{ label: "男", value: "0" },
|
||||
{ label: "女", value: "1" },
|
||||
];
|
||||
const positionOptions = ["医生", "护士", "药师", "技师", "其他"];
|
||||
const titleOptions = ["主任医师", "副主任医师", "主治医师", "医师", "其他"];
|
||||
// 字段变更处理
|
||||
const handleFieldChange = (e) => {
|
||||
if (e.title === "gender") {
|
||||
formData.value[e.title] = e.value.value;
|
||||
} else {
|
||||
formData.value[e.title] = e.value;
|
||||
}
|
||||
};
|
||||
|
||||
// 选择头像
|
||||
const chooseAvatar = async () => {
|
||||
const url = await chooseAndUploadImage();
|
||||
if (url) {
|
||||
formData.value.avatar = url;
|
||||
}
|
||||
};
|
||||
const handleCancel = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
// 保存
|
||||
const handleSave = async () => {
|
||||
createDoctorInfo();
|
||||
};
|
||||
useLoad(() => {
|
||||
if (doctorInfo.value) {
|
||||
formData.value = { ...doctorInfo.value };
|
||||
} else {
|
||||
formData.value.mobile = account.value.mobile;
|
||||
}
|
||||
});
|
||||
|
||||
// 创建医生信息
|
||||
const createDoctorInfo = async () => {
|
||||
if (!formData.value.anotherName) {
|
||||
uni.showToast({
|
||||
title: "请输入姓名",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
let params = {
|
||||
anotherName: formData.value.anotherName,
|
||||
avatar: formData.value.avatar,
|
||||
gender: formData.value.gender,
|
||||
mobile: formData.value.mobile,
|
||||
weChatOpenId: account.value.openid,
|
||||
deptIds: [],
|
||||
loginTypes: ["wxApp"],
|
||||
corpId: account.value.corpId,
|
||||
};
|
||||
const res = await api("addCorpMember", {
|
||||
params,
|
||||
});
|
||||
if (res.success && res.data) {
|
||||
uni.showToast({
|
||||
title: "创建成功",
|
||||
icon: "success",
|
||||
});
|
||||
await getDoctorInfo();
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: "创建失败",
|
||||
icon: "none",
|
||||
});
|
||||
console.error("创建医生信息失败:", res);
|
||||
}
|
||||
};
|
||||
|
||||
const updateDoctorInfo = async () => {
|
||||
let params = {
|
||||
anotherName: formData.value.anotherName,
|
||||
avatar: formData.value.avatar,
|
||||
gender: formData.value.gender,
|
||||
};
|
||||
const res = await api("updateCorpMember", {
|
||||
params,
|
||||
});
|
||||
if (res.success && res.data) {
|
||||
uni.showToast({
|
||||
title: "更新成功",
|
||||
icon: "success",
|
||||
});
|
||||
}
|
||||
await getDoctorInfo();
|
||||
uni.navigateBack();
|
||||
};
|
||||
// 打开科室选择
|
||||
const openDepartmentSelect = () => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/work/department-select",
|
||||
events: {
|
||||
deptSelected: ({ name, deptId }) => {
|
||||
formData.value.department = name || "";
|
||||
formData.value.departmentName = name || "";
|
||||
formData.value.departmentId = deptId || "";
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.form-content__wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
font-size: 28rpx;
|
||||
|
||||
input {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.text-placeholder {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #e5e5e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.avatar-icon {
|
||||
font-size: 48rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.button-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
padding: 20rpx 30rpx;
|
||||
background: #fff;
|
||||
border-top: 1px solid #eee;
|
||||
gap: 20rpx;
|
||||
z-index: 100;
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background: #fff;
|
||||
border: 1px solid #007aff;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,211 +1,83 @@
|
||||
<template>
|
||||
<view class="profile-page">
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section bg-white">
|
||||
<!-- 姓名 -->
|
||||
<form-input
|
||||
name="姓名"
|
||||
:required="true"
|
||||
:form="formData"
|
||||
title="anotherName"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 头像 -->
|
||||
<common-cell name="头像">
|
||||
<view class="form-content__wrapper" @click="chooseAvatar">
|
||||
<view class="flex-main-content flex items-center">
|
||||
<image
|
||||
v-if="formData.avatar"
|
||||
class="avatar-preview"
|
||||
:src="formData.avatar"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<full-page>
|
||||
<view class="p-15">
|
||||
<view class="bg-white px-10 mb-10 rounded">
|
||||
<form-input :form="formData" required wordLimit="10" title="anotherName" name="姓名" @change="onChange($event)" />
|
||||
<common-cell title="avatar" name="头像">
|
||||
<view class="flex-grow flex items-center justify-end" @click="chooseAvatar()">
|
||||
<image v-if="formData.avatar" class="avatar mr-5 rounded-full" :src="formData.avatar" />
|
||||
<image v-else class="avatar mr-5 rounded-full" src="/static/default-avatar.png" />
|
||||
<uni-icons color="#999" type="right" size="16" />
|
||||
</view>
|
||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||
</view>
|
||||
</common-cell>
|
||||
<!-- 性别 -->
|
||||
<form-select
|
||||
name="性别"
|
||||
:form="formData"
|
||||
title="gender"
|
||||
:range="genderOptions"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 手机号(不可修改) -->
|
||||
<common-cell name="手机号 (不可修改)">
|
||||
<view class="form-content__wrapper">
|
||||
<view class="flex-main-content text-dark">{{ formData.mobile }}</view>
|
||||
</view>
|
||||
</common-cell>
|
||||
<!-- 岗位 -->
|
||||
<form-select
|
||||
name="岗位"
|
||||
:form="formData"
|
||||
title="position"
|
||||
:range="positionOptions"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 职称 -->
|
||||
<form-select
|
||||
name="职称"
|
||||
:form="formData"
|
||||
title="title"
|
||||
:range="titleOptions"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<!-- 科室 -->
|
||||
<common-cell name="科室">
|
||||
<view class="form-content__wrapper" @click="openDepartmentSelect">
|
||||
<view
|
||||
class="flex-main-content text-right"
|
||||
:class="{ 'text-placeholder': !formData.departmentName }"
|
||||
>
|
||||
{{ formData.departmentName || "请选择" }}
|
||||
</view>
|
||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||
</view>
|
||||
</common-cell>
|
||||
</common-cell>
|
||||
<form-select :form="formData" name="性别" title="gender" :range="genderOptions" @change="onChange($event)" />
|
||||
<form-input :form="formData" disableChange wordLimit="11" title="mobile" name="手机号 (不可修改)" />
|
||||
</view>
|
||||
|
||||
<!-- 个人介绍 -->
|
||||
<form-textarea
|
||||
name="个人介绍"
|
||||
:form="formData"
|
||||
title="intro"
|
||||
:word-limit="500"
|
||||
@change="handleFieldChange"
|
||||
/>
|
||||
<view class="bg-white px-10 mb-10 rounded">
|
||||
<common-cell title="avatar" name="岗位">
|
||||
<view class="flex-grow flex items-center justify-end">
|
||||
<!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> -->
|
||||
<uni-icons color="#999" type="right" size="16" />
|
||||
</view>
|
||||
</common-cell>
|
||||
<common-cell title="avatar" name="职称">
|
||||
<view class="flex-grow flex items-center justify-end">
|
||||
<!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> -->
|
||||
<uni-icons color="#999" type="right" size="16" />
|
||||
</view>
|
||||
</common-cell>
|
||||
<common-cell title="avatar" name="科室">
|
||||
<view class="flex-grow flex items-center justify-end">
|
||||
<!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> -->
|
||||
<uni-icons color="#999" type="right" size="16" />
|
||||
</view>
|
||||
</common-cell>
|
||||
</view>
|
||||
|
||||
<view class="bg-white rounded">
|
||||
<form-textarea :border="false" :form="formData" title="intro" name="个人介绍" :wordLimit="300"
|
||||
@change="onChange($event)" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="button-footer">
|
||||
<view class="btn btn-cancel" @click="handleCancel">取消</view>
|
||||
<view class="btn btn-save" @click="handleSave">保存</view>
|
||||
</view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<button-footer :cancelText="cancelText" :confirmText="confirmText" @confirm="save()" @cancel="back()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import useGuard from "@/hooks/useGuard.js";
|
||||
import { chooseAndUploadImage } from "@/utils/file.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import { computed, ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import CommonCell from "@/components/form-template/common-cell.vue";
|
||||
import useGuard from "@/hooks/useGuard.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import api from "@/utils/api.js";
|
||||
import { upload } from "@/utils/http.js";
|
||||
import { toast } from "@/utils/widget";
|
||||
|
||||
import buttonFooter from '@/components/button-footer.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 FormTextarea from "@/components/form-template/form-cell/form-textarea.vue";
|
||||
import api from "@/utils/api.js";
|
||||
import fullPage from '@/components/full-page.vue';
|
||||
|
||||
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||
const { useLoad } = useGuard();
|
||||
const { useLoad, useShow } = useGuard();
|
||||
const { getDoctorInfo } = useAccountStore();
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
anotherName: "",
|
||||
avatar: "",
|
||||
gender: "",
|
||||
mobile: "",
|
||||
position: "",
|
||||
title: "",
|
||||
department: "",
|
||||
departmentName: "",
|
||||
departmentId: "",
|
||||
intro: "",
|
||||
});
|
||||
|
||||
const form = ref({});
|
||||
const type = ref('')
|
||||
const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value, mobile: account.value?.mobile }));
|
||||
const cancelText = computed(() => doctorInfo.value ? '取消' : '暂不填写');
|
||||
const confirmText = computed(() => type.value === 'cert' ? '下一步' : '保存');
|
||||
|
||||
// 选项数据
|
||||
const genderOptions = [
|
||||
{ label: "男", value: "0" },
|
||||
{ label: "女", value: "1" },
|
||||
];
|
||||
const positionOptions = ["医生", "护士", "药师", "技师", "其他"];
|
||||
const titleOptions = ["主任医师", "副主任医师", "主治医师", "医师", "其他"];
|
||||
// 字段变更处理
|
||||
const handleFieldChange = (e) => {
|
||||
if (e.title === "gender") {
|
||||
formData.value[e.title] = e.value.value;
|
||||
} else {
|
||||
formData.value[e.title] = e.value;
|
||||
}
|
||||
};
|
||||
|
||||
// 选择头像
|
||||
const chooseAvatar = async () => {
|
||||
const url = await chooseAndUploadImage();
|
||||
if (url) {
|
||||
formData.value.avatar = url;
|
||||
}
|
||||
};
|
||||
const handleCancel = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
// 保存
|
||||
const handleSave = async () => {
|
||||
createDoctorInfo();
|
||||
};
|
||||
useLoad(() => {
|
||||
if (doctorInfo.value) {
|
||||
formData.value = { ...doctorInfo.value };
|
||||
} else {
|
||||
formData.value.mobile = account.value.mobile;
|
||||
}
|
||||
});
|
||||
|
||||
// 创建医生信息
|
||||
const createDoctorInfo = async () => {
|
||||
if (!formData.value.anotherName) {
|
||||
uni.showToast({
|
||||
title: "请输入姓名",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
let params = {
|
||||
anotherName: formData.value.anotherName,
|
||||
avatar: formData.value.avatar,
|
||||
gender: formData.value.gender,
|
||||
mobile: formData.value.mobile,
|
||||
weChatOpenId: account.value.openid,
|
||||
deptIds: [],
|
||||
loginTypes: ["wxApp"],
|
||||
corpId: account.value.corpId,
|
||||
};
|
||||
const res = await api("addCorpMember", {
|
||||
params,
|
||||
});
|
||||
if (res.success && res.data) {
|
||||
uni.showToast({
|
||||
title: "创建成功",
|
||||
icon: "success",
|
||||
});
|
||||
await getDoctorInfo();
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: "创建失败",
|
||||
icon: "none",
|
||||
});
|
||||
console.error("创建医生信息失败:", res);
|
||||
}
|
||||
};
|
||||
|
||||
const updateDoctorInfo = async () => {
|
||||
let params = {
|
||||
anotherName: formData.value.anotherName,
|
||||
avatar: formData.value.avatar,
|
||||
gender: formData.value.gender,
|
||||
};
|
||||
const res = await api("updateCorpMember", {
|
||||
params,
|
||||
});
|
||||
if (res.success && res.data) {
|
||||
uni.showToast({
|
||||
title: "更新成功",
|
||||
icon: "success",
|
||||
});
|
||||
}
|
||||
await getDoctorInfo();
|
||||
uni.navigateBack();
|
||||
};
|
||||
// 打开科室选择
|
||||
const openDepartmentSelect = () => {
|
||||
uni.navigateTo({
|
||||
@ -219,89 +91,74 @@ const openDepartmentSelect = () => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function back() {
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.switchTab({
|
||||
url: "/pages/work/work",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function chooseAvatar() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: async (res) => {
|
||||
const [path] = res.tempFilePaths;
|
||||
const url = await upload(path);
|
||||
if (url) {
|
||||
form.value.avatar = url;
|
||||
} else {
|
||||
toast('上传失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function onChange({ title, value }) {
|
||||
form.value[title] = value
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (typeof formData.value.anotherName !== 'string' || !formData.value.anotherName.trim()) {
|
||||
return toast('请输入姓名')
|
||||
}
|
||||
if (type.value === 'cert' && !formData.value.departmentId) {
|
||||
return toast('请输入岗位信息')
|
||||
}
|
||||
const apiName = doctorInfo.value ? 'updateCorpMemberFromWxapp' : 'addCorpMemberFromWxapp';
|
||||
const data = {
|
||||
...form.value,
|
||||
weChatOpenId: account.value.openid,
|
||||
mobile: account.value.mobile,
|
||||
corpId: account.value.corpId,
|
||||
}
|
||||
const res = await api(apiName, data);
|
||||
if (res && res.success) {
|
||||
await toast('保存成功');
|
||||
await getDoctorInfo()
|
||||
back()
|
||||
} else {
|
||||
await toast(res?.message || '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
useLoad(opts => {
|
||||
type.value = opts?.type;
|
||||
})
|
||||
|
||||
useShow(() => {
|
||||
getDoctorInfo()
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.form-content__wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
font-size: 28rpx;
|
||||
|
||||
input {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.text-placeholder {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #e5e5e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.avatar-icon {
|
||||
font-size: 48rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.button-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
padding: 20rpx 30rpx;
|
||||
background: #fff;
|
||||
border-top: 1px solid #eee;
|
||||
gap: 20rpx;
|
||||
z-index: 100;
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background: #fff;
|
||||
border: 1px solid #007aff;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
.avatar {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
164
pages/work/verify/assistant.vue
Normal file
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<full-page pageClass="bg-white">
|
||||
<view class="p-15">
|
||||
<view class="mt-15 title-bar relative font-semibold">
|
||||
<text class="mr-5 text-dark text-base">请上传</text>
|
||||
<text class="text-primary text-base">身份证正反面</text>
|
||||
</view>
|
||||
<view class="mt-12 flex items-center justify-between">
|
||||
<view class="album border rounded overflow-hidden" @click="uploadIdCard('idCardFront')">
|
||||
<image v-if="form.idCardFront" class="w-full h-full" :src="form.idCardFront" />
|
||||
<view v-else class="relative w-full h-full ">
|
||||
<image class="absolute w-full h-full" src="/static/work/fIDCard.png" />
|
||||
<view class="absolute w-full h-full flex flex-col items-center justify-center">
|
||||
<image class="carmra-icon" src="/static/work/camera.png" />
|
||||
<view class="text-lg font-semibold color-428">上传人像面</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="album border rounded overflow-hidden" @click="uploadIdCard('idCardBack')">
|
||||
<image v-if="form.idCardBack" class="w-full h-full" :src="form.idCardBack" />
|
||||
<view v-else class="relative w-full h-full ">
|
||||
<image class="absolute w-full h-full" src="/static/work/aIDCard.png" />
|
||||
<view class="absolute w-full h-full flex flex-col items-center justify-center">
|
||||
<image class="carmra-icon" src="/static/work/camera.png" />
|
||||
<view class="text-lg font-semibold color-428">上传国徽面</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="exam px-15 rounded-sm leading-normal text-dark text-base">
|
||||
身份证示例
|
||||
</view>
|
||||
<view class="mt-12 flex items-center justify-between">
|
||||
<image class="album" src="/static/work/cardFront.png" />
|
||||
<image class="album" src="/static/work/cardBack.png" />
|
||||
</view>
|
||||
<view class="mt-12 text-dark font-semibold text-base">拍摄须知</view>
|
||||
<view class="mt-10 flex items-center justify-between">
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<image class="mb-5 exam-icon" src="/static/work/fIDCard.png" />
|
||||
<image class="mb-5 status-icon" src="/static/work/hook.png" />
|
||||
<view class="text-base text-dark">标准</view>
|
||||
</view>
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<image class="mb-5 exam-icon" src="/static/work/lackCard.png" />
|
||||
<image class="mb-5 status-icon" src="/static/work/error.png" />
|
||||
<view class="text-base text-dark">缺边</view>
|
||||
</view>
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<image class="mb-5 exam-icon" src="/static/work/vagueCard.png" />
|
||||
<image class="mb-5 status-icon" src="/static/work/error.png" />
|
||||
<view class="text-base text-dark">模糊</view>
|
||||
</view>
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<image class="mb-5 exam-icon" src="/static/work/flashCard.png" />
|
||||
<image class="mb-5 status-icon" src="/static/work/error.png" />
|
||||
<view class="text-base text-dark">闪光</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<template #footer>
|
||||
<button-footer cancelText="上一步" confirmText="提交审核" @confirm="save()" @cancel="back()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import useGuard from "@/hooks/useGuard.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import { upload } from "@/utils/http.js";
|
||||
import { hideLoading, loading as showLoading, toast } from "@/utils/widget";
|
||||
|
||||
import buttonFooter from '@/components/button-footer.vue';
|
||||
import fullPage from '@/components/full-page.vue';
|
||||
|
||||
useGuard();
|
||||
|
||||
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||
const form = ref({})
|
||||
const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value }))
|
||||
|
||||
function back() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
function uploadIdCard(type) {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: async (res) => {
|
||||
const [path] = res.tempFilePaths;
|
||||
showLoading('正在上传')
|
||||
const url = await upload(path);
|
||||
hideLoading()
|
||||
if (url) {
|
||||
form.value[type] = url;
|
||||
} else {
|
||||
toast('上传失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (typeof formData.value.idCardFront !== 'string' || formData.value.idCardFront.trim() === '') {
|
||||
return toast('请上传人像面')
|
||||
}
|
||||
if (typeof formData.value.idCardBack !== 'string' || formData.value.idCardBack.trim() === '') {
|
||||
return toast('请上传国徽面')
|
||||
}
|
||||
console.log('form.value: ', formData.value)
|
||||
|
||||
// uni.showToast({ title: '提交成功', icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.title-bar {
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.title-bar::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
height: 80%;
|
||||
width: 8rpx;
|
||||
transform: translateY(-50%);
|
||||
border-radius: 4rpx;
|
||||
background: #0074ff;
|
||||
}
|
||||
|
||||
.album {
|
||||
width: 330rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.carmra-icon {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.color-428 {
|
||||
color: #428bf0;
|
||||
}
|
||||
|
||||
.exam {
|
||||
margin-top: 80rpx;
|
||||
background: #dde6f6;
|
||||
padding: 8rpx 30rpx;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.exam-icon {
|
||||
width: 160rpx;
|
||||
height: 104rpx;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
</style>
|
||||
126
pages/work/verify/doctor.vue
Normal file
@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<full-page pageClass="bg-white">
|
||||
<view class="p-15">
|
||||
<view class="title-bar relative text-dark text-base font-semibold">请填写执业医院</view>
|
||||
<view class="mt-12 p-10 flex items-center justify-between border rounded">
|
||||
<view class="w-0 flex-grow truncate text-base mr-10" :class="formData.hospitalName ? 'text-dark' : 'text-gray'">
|
||||
{{ formData.hospitalName || '请填写执业医院' }}
|
||||
</view>
|
||||
<uni-icons class="flex-shrink-0" color="#666" type="right" size="16" />
|
||||
</view>
|
||||
<view class="mt-15 title-bar relative font-semibold">
|
||||
<text class="mr-5 text-dark text-base">请上传</text>
|
||||
<text class="text-primary text-base">医师执业资格证</text>
|
||||
</view>
|
||||
<view class="mt-12 flex items-center justify-between">
|
||||
<view class="album border rounded overflow-hidden" @click="uploadLicense('medicalLicenseFront')">
|
||||
<image v-if="formData.medicalLicenseFront" class="w-full h-full" :src="formData.medicalLicenseFront" />
|
||||
<view v-else class="w-full h-full flex flex-col items-center justify-center">
|
||||
<uni-icons color="#666" type="camera" size="36" />
|
||||
<view class="text-dark text-base">上传第一页</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="album border rounded overflow-hidden" @click="uploadLicense('medicalLicenseBack')">
|
||||
<image v-if="formData.medicalLicenseBack" class="w-full h-full" :src="formData.medicalLicenseBack" />
|
||||
<view v-else class="w-full h-full flex flex-col items-center justify-center">
|
||||
<uni-icons color="#666" type="camera" size="36" />
|
||||
<view class="text-dark text-base">上传第二页</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="exam px-15 rounded-sm leading-normal text-dark text-base">
|
||||
证书示例
|
||||
</view>
|
||||
<view class="mt-10 text-sm text-dark leading-normal">1、确保姓名、照片、编号、执业范围、签发机关等清晰可见</view>
|
||||
<view class="mt-10 text-sm text-dark leading-normal">2、需上传证书第一、第二页,图片仅供参考,以实际证书为准</view>
|
||||
<view class="mt-12 flex items-center justify-between">
|
||||
<image class="album" src="/static/work/licenseFront.png" />
|
||||
<image class="album" src="/static/work/licenseBack.png" />
|
||||
</view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<button-footer cancelText="上一步" confirmText="提交审核" @confirm="save()" @cancel="back()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import useGuard from "@/hooks/useGuard.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import { upload } from "@/utils/http.js";
|
||||
import { hideLoading, loading as showLoading, toast } from "@/utils/widget";
|
||||
|
||||
import buttonFooter from '@/components/button-footer.vue';
|
||||
import fullPage from '@/components/full-page.vue';
|
||||
|
||||
useGuard();
|
||||
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||
const form = ref({});
|
||||
const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value }))
|
||||
|
||||
function back() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
function uploadLicense(type) {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: async (res) => {
|
||||
const [path] = res.tempFilePaths;
|
||||
showLoading('正在上传')
|
||||
const url = await upload(path);
|
||||
hideLoading()
|
||||
if (url) {
|
||||
form.value[type] = url;
|
||||
} else {
|
||||
toast('上传失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (typeof formData.value.hospitalName !== 'string' || formData.value.hospitalName.trim() === '') {
|
||||
return toast('请填写执业医院')
|
||||
}
|
||||
if (typeof formData.value.medicalLicenseFront !== 'string' || formData.value.medicalLicenseFront.trim() === '') {
|
||||
return toast('请上传医师执业资格证第一页')
|
||||
}
|
||||
if (typeof formData.value.medicalLicenseBack !== 'string' || formData.value.medicalLicenseBack.trim() === '') {
|
||||
return toast('请上传医师执业资格证第二页')
|
||||
}
|
||||
console.log('form.value: ', formData.value)
|
||||
|
||||
// uni.showToast({ title: '提交成功', icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.title-bar {
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.title-bar::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
height: 80%;
|
||||
width: 8rpx;
|
||||
transform: translateY(-50%);
|
||||
border-radius: 4rpx;
|
||||
background: #0074ff;
|
||||
}
|
||||
|
||||
.album {
|
||||
width: 330rpx;
|
||||
height: 240rpx;
|
||||
}
|
||||
|
||||
.exam {
|
||||
margin-top: 80rpx;
|
||||
background: #dde6f6;
|
||||
padding: 8rpx 30rpx;
|
||||
width: fit-content;
|
||||
}
|
||||
</style>
|
||||
@ -1,80 +1,93 @@
|
||||
<template>
|
||||
<view class="work-page">
|
||||
<!-- 顶部用户信息区域 -->
|
||||
<view class="user-header bg-white px-15 py-15">
|
||||
<view class="flex items-center justify-between">
|
||||
<!-- 左侧:用户头像和信息 -->
|
||||
<view class="flex items-center flex-grow">
|
||||
<view class="user-avatar mr-10">
|
||||
<image
|
||||
class="avatar-img"
|
||||
src="/static/default-avatar.png"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
<view class="flex-col">
|
||||
<text class="user-name text-black text-lg font-semibold"
|
||||
>请完善信息</text
|
||||
>
|
||||
<view class="flex items-center mt-5">
|
||||
<view
|
||||
class="status-tag tag-orange mr-10"
|
||||
@click="handleCompleteInfo"
|
||||
>
|
||||
<text class="tag-text text-white">信息待完善</text>
|
||||
<full-page :showSafeArea="false" :customScroll="true">
|
||||
<template #header>
|
||||
<view class="user-header bg-white px-15 py-15">
|
||||
<view class="flex items-center justify-between" @click="editProfile()">
|
||||
<view class="flex items-center flex-grow">
|
||||
<view class="relative user-avatar mr-10">
|
||||
<image v-if="doctorInfo && doctorInfo.avatar" class="avatar-img rounded-full overflow-hidden"
|
||||
:src="doctorInfo.avatar" mode="aspectFill" />
|
||||
<image v-else class="avatar-img rounded-full overflow-hidden" src="/static/default-avatar.png"
|
||||
mode="aspectFill" />
|
||||
<view v-if="doctorInfo" class="edit-sub flex items-center justify-center rounded-full bg-primary">
|
||||
<image class="edit-icon" src="/static/work/pen.svg" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="status-tag tag-gray" @click="handleVerify">
|
||||
<text class="tag-text text-dark">未认证</text>
|
||||
</view>
|
||||
<view class="flex-col">
|
||||
<text v-if="doctorInfo && doctorInfo.anotherName" class="user-name text-dark text-lg font-semibold">
|
||||
{{ doctorInfo.anotherName }}
|
||||
</text>
|
||||
<text v-else class="user-name text-black text-lg font-semibold">请完善信息</text>
|
||||
<view class="flex items-center mt-5">
|
||||
<view v-if="!doctorInfo || !doctorInfo.anotherName" class="status-tag tag-orange mr-10">
|
||||
<text class="tag-text text-white">信息待完善</text>
|
||||
</view>
|
||||
<view v-if="certStatus" class="px-10 py-3 text-sm rounded-full" :class="certStatus.classnames"
|
||||
@click.stop="toCert()">
|
||||
{{ certStatus.text }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧:操作按钮 -->
|
||||
<view class="flex items-center">
|
||||
<view
|
||||
class="action-btn flex-col items-center mr-15"
|
||||
@click="handleInvitePatient"
|
||||
>
|
||||
<view class="qrcode-icon">
|
||||
<text class="qrcode-text">▦</text>
|
||||
<!-- 右侧:操作按钮 -->
|
||||
<view class="flex items-center">
|
||||
<view class="action-btn flex-col items-center mr-10" @click="handleInvitePatient">
|
||||
<image class="mb-5 qrcode-icon" src="/static/work/qrcode.svg" />
|
||||
<text class="action-text text-dark text-sm">邀请</text>
|
||||
</view>
|
||||
<view class="action-btn flex-col items-center" @click="handleMore">
|
||||
<image class="mb-5 qrcode-icon" src="/static/work/more.svg" />
|
||||
<text class="action-text text-dark text-sm">更多</text>
|
||||
</view>
|
||||
<text class="action-text text-black text-sm mt-5">邀请患者</text>
|
||||
</view>
|
||||
<view class="more-btn" @click="handleMore">
|
||||
<text class="more-icon">⋯</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 待办列表区域 -->
|
||||
<view class="todo-section px-15 mt-15">
|
||||
<view class="section-title mb-15">
|
||||
<text class="text-black text-lg font-semibold">待办列表11</text>
|
||||
<view class="mt-15 px-15 py-12 text-dark text-lg font-semibold bg-white border-b">
|
||||
待办列表11
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state">
|
||||
<empty-data text="暂无记录" />
|
||||
<scroll-view v-if="list.length" scroll-y="true" class="h-full bg-white">
|
||||
<view class="p-15">
|
||||
<view v-for="i in 10" class="p-15 bg-primary mb-10"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view v-else class="flex flex-col items-center justify-center h-full bg-white">
|
||||
<empty-data text="暂无记录" />
|
||||
</view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<view class="border-b"></view>
|
||||
</template>
|
||||
</full-page>
|
||||
<cert-popup :visible="visible" @close="visible = false" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { storeToRefs } from "pinia";
|
||||
import useGuard from "@/hooks/useGuard.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
|
||||
import certPopup from "./components/cert-popup.vue";
|
||||
import EmptyData from "@/components/empty-data.vue";
|
||||
import fullPage from '@/components/full-page.vue';
|
||||
|
||||
const { useLoad } = useGuard();
|
||||
|
||||
// 完善信息
|
||||
const handleCompleteInfo = () => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/work/profile",
|
||||
});
|
||||
const certConfig = {
|
||||
verified: { text: '已认证', classnames: 'bg-success text-white' },
|
||||
verifing: { text: '认证中', classnames: 'bg-orange text-white' },
|
||||
unverified: { text: '未认证', classnames: 'bg-gray text-dark' },
|
||||
};
|
||||
|
||||
const { useLoad, useShow } = useGuard();
|
||||
const { getDoctorInfo } = useAccountStore();
|
||||
const { doctorInfo } = storeToRefs(useAccountStore());
|
||||
const list = ref([1]);
|
||||
const visible = ref(false);
|
||||
|
||||
const certStatus = computed(() => doctorInfo.value?.verifyStatus ? certConfig[doctorInfo.value.verifyStatus] : certConfig.unverified)
|
||||
|
||||
// 认证
|
||||
const handleVerify = () => {
|
||||
uni.showToast({
|
||||
@ -101,15 +114,39 @@ const handleMore = () => {
|
||||
});
|
||||
};
|
||||
|
||||
function editProfile() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/work/profile",
|
||||
});
|
||||
}
|
||||
|
||||
function toCert() {
|
||||
// if (doctorInfo.value.verifyStatus === 'unverified') {
|
||||
visible.value = true
|
||||
// }
|
||||
}
|
||||
|
||||
useLoad(() => {
|
||||
console.log("工作台页面加载");
|
||||
});
|
||||
|
||||
useShow(() => {
|
||||
getDoctorInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.work-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
.edit-sub {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.user-header {
|
||||
@ -119,9 +156,6 @@ useLoad(() => {
|
||||
.user-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background: #e5e5e5;
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
@ -133,6 +167,11 @@ useLoad(() => {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
padding-top: 6rpx;
|
||||
padding-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
@ -162,17 +201,8 @@ useLoad(() => {
|
||||
justify-content: center;
|
||||
|
||||
.qrcode-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.qrcode-text {
|
||||
font-size: 40rpx;
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
}
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
@ -209,5 +239,4 @@ useLoad(() => {
|
||||
// align-items: center;
|
||||
// justify-content: center;
|
||||
// width: 100%;
|
||||
// }
|
||||
</style>
|
||||
// }</style>
|
||||
@ -1,20 +1,40 @@
|
||||
export default [
|
||||
{
|
||||
path: 'pages/message/message',
|
||||
meta: { title: '首页', login: false },
|
||||
style: { navigationStyle: 'custom' }
|
||||
meta: { title: '消息' }
|
||||
},
|
||||
{
|
||||
path: 'pages/message/index',
|
||||
meta: { title: '聊天' },
|
||||
style: { enablePullDownRefresh: false }
|
||||
},
|
||||
{
|
||||
path: 'pages/work/work',
|
||||
meta: { title: '工作台', login: false }
|
||||
meta: { title: '工作台' }
|
||||
},
|
||||
{
|
||||
path: 'pages/case/case',
|
||||
meta: { title: '病例' }
|
||||
},
|
||||
{
|
||||
path: 'pages/work/profile',
|
||||
meta: { title: '完善个人信息', login: false }
|
||||
meta: { title: '完善个人信息' }
|
||||
},
|
||||
{
|
||||
path: 'pages/work/department-select',
|
||||
meta: { title: '选择科室', login: false }
|
||||
}
|
||||
meta: { title: '选择科室' }
|
||||
},
|
||||
{
|
||||
path: 'pages/work/verify/assistant',
|
||||
meta: { title: '上传证照', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/work/verify/doctor',
|
||||
meta: { title: '上传证照', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/login/login',
|
||||
meta: { title: '授权登录' }
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
BIN
static/work/aIDCard.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
static/work/camera.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/work/cardBack.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
static/work/cardFront.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
static/work/error.png
Normal file
|
After Width: | Height: | Size: 960 B |
BIN
static/work/fIDCard.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
static/work/flashCard.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
static/work/hook.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
static/work/lackCard.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
static/work/licenseBack.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
static/work/licenseFront.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
1
static/work/more.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="1769069890058" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14332" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M378.112 481.536H159.488c-51.2 0-92.928-41.728-92.928-92.928V169.984c0-51.2 41.728-92.928 92.928-92.928h218.624c51.2 0 92.928 41.728 92.928 92.928v218.624c0 51.2-41.728 92.928-92.928 92.928zM159.488 138.752c-17.408 0-31.488 14.08-31.488 31.488v218.624c0 17.408 14.08 31.488 31.488 31.488h218.624c17.408 0 31.488-14.08 31.488-31.488V169.984c0-17.408-14.08-31.488-31.488-31.488H159.488zM378.112 949.504H159.488c-51.2 0-92.928-41.728-92.928-92.928v-218.624c0-51.2 41.728-92.928 92.928-92.928h218.624c51.2 0 92.928 41.728 92.928 92.928v218.624c0 51.456-41.728 92.928-92.928 92.928zM159.488 606.72c-17.408 0-31.488 14.08-31.488 31.488v218.624c0 17.408 14.08 31.488 31.488 31.488h218.624c17.408 0 31.488-14.08 31.488-31.488v-218.624c0-17.408-14.08-31.488-31.488-31.488H159.488zM680.448 480l-143.616-144.128c-33.536-33.792-33.536-88.576 0.256-122.112l144.128-143.616c33.792-33.536 88.576-33.536 122.112 0.256l143.616 144.128c33.536 33.792 33.536 88.576-0.256 122.112l-144.128 143.616c-33.792 33.536-88.32 33.536-122.112-0.256zM762.88 110.848c-11.264-11.52-29.952-11.52-41.216 0l-144.128 143.616c-11.52 11.264-11.52 29.952 0 41.216l143.616 144.128c11.264 11.52 29.952 11.52 41.216 0l144.128-143.616c11.52-11.264 11.52-29.952 0-41.216L762.88 110.848zM840.96 949.504l-218.624-0.512c-51.2 0-92.672-41.728-92.672-92.928l0.512-218.624c0-51.2 41.728-92.672 92.928-92.672l218.624 0.512c51.2 0 92.672 41.728 92.672 92.928l-0.512 218.624c0 51.2-41.728 92.928-92.928 92.672z m-217.856-343.04c-17.408 0-31.488 14.08-31.488 31.232l-0.512 218.624c0 17.408 14.08 31.488 31.232 31.488l218.624 0.512c17.408 0 31.488-14.08 31.488-31.232l0.512-218.624c0-17.408-14.08-31.488-31.232-31.488l-218.624-0.512z" fill="#2c2c2c" p-id="14333"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
static/work/pen.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="1769067872142" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10445" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M581.50172 186.48l256.04 256.04-555.98 555.98-228.28 25.2C22.72172 1027.08-3.09828 1001.24 0.30172 970.68l25.4-228.44 555.8-555.76z m414.4-38.12l-120.22-120.22c-37.5-37.5-98.32-37.5-135.82 0l-113.1 113.1 256.04 256.04 113.1-113.1c37.5-37.52 37.5-98.32 0-135.82z" fill="#ffffff" p-id="10446"></path></svg>
|
||||
|
After Width: | Height: | Size: 638 B |
1
static/work/qrcode.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="1769069475491" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11505" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M85.312 85.312V384H384V85.312H85.312zM0 0h469.248v469.248H0V0z m170.624 170.624h128v128h-128v-128zM0 554.624h469.248v469.248H0V554.624z m85.312 85.312v298.624H384V639.936H85.312z m85.312 85.312h128v128h-128v-128zM554.624 0h469.248v469.248H554.624V0z m85.312 85.312V384h298.624V85.312H639.936z m383.936 682.56H1024v85.376h-298.752V639.936H639.936V1023.872H554.624V554.624h255.936v213.248h128V554.624h85.312v213.248z m-298.624-597.248h128v128h-128v-128z m298.624 853.248h-85.312v-85.312h85.312v85.312z m-213.312 0h-85.312v-85.312h85.312v85.312z" fill="#262626" p-id="11506"></path></svg>
|
||||
|
After Width: | Height: | Size: 919 B |
BIN
static/work/vagueCard.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
@ -8,17 +8,28 @@ const env = __VITE_ENV__;
|
||||
|
||||
export default defineStore("accountStore", () => {
|
||||
const appid = env.MP_WX_APP_ID;
|
||||
const corpId = env.MP_CORP_ID;
|
||||
const account = ref(null);
|
||||
const loading = ref(false)
|
||||
const loading = ref(false);
|
||||
const loginPromise = ref(null);
|
||||
// IM 相关
|
||||
const openid = ref("");
|
||||
const isIMInitialized = ref(false);
|
||||
// 医生信息
|
||||
const doctorInfo = ref(null);
|
||||
|
||||
async function login(phoneCode = '') {
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
function getLoginPromise(phoneCode = '') {
|
||||
if (loginPromise.value) return loginPromise.value;
|
||||
loginPromise.value = loginByCode(phoneCode);
|
||||
return loginPromise.value;
|
||||
}
|
||||
|
||||
async function login(phoneCode) {
|
||||
await getLoginPromise(phoneCode);
|
||||
loginPromise.value = null;
|
||||
}
|
||||
|
||||
async function loginByCode(phoneCode = '') {
|
||||
try {
|
||||
const { code } = await uni.login({
|
||||
appid,
|
||||
@ -29,8 +40,8 @@ export default defineStore("accountStore", () => {
|
||||
const res = await api('wxAppLogin', {
|
||||
phoneCode,
|
||||
code,
|
||||
corpId,
|
||||
});
|
||||
loading.value = false;
|
||||
if (res.success && res.data) {
|
||||
if (!res.data.mobile) {
|
||||
const target = '/pages/login/login';
|
||||
@ -39,8 +50,6 @@ export default defineStore("accountStore", () => {
|
||||
}
|
||||
account.value = res.data;
|
||||
openid.value = res.data.openid;
|
||||
|
||||
|
||||
// 登录成功后初始化腾讯IM
|
||||
try {
|
||||
console.log('开始初始化腾讯IM,userID:', res.data.openid);
|
||||
@ -56,10 +65,11 @@ export default defineStore("accountStore", () => {
|
||||
}
|
||||
}
|
||||
toast('登录失败,请重新登录');
|
||||
|
||||
} catch (e) {
|
||||
toast('登录失败,请重新登录');
|
||||
}
|
||||
loading.value = false
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
async function getDoctorInfo() {
|
||||
|
||||
@ -11,7 +11,9 @@ const urlsConfig = {
|
||||
getHospitalList: 'getRealHospital',
|
||||
addCorpMember: 'addCorpMember',
|
||||
getCorpMemberData: 'getCorpMemberData',
|
||||
updateCorpMember: 'updateCorpMember'
|
||||
updateCorpMember: 'updateCorpMember',
|
||||
addCorpMemberFromWxapp: "addCorpMemberFromWxapp",
|
||||
updateCorpMemberFromWxapp: "updateCorpMemberFromWxapp"
|
||||
},
|
||||
|
||||
knowledgeBase: {
|
||||
|
||||
173
utils/http.js
@ -21,21 +21,21 @@ let retryQueue = [];
|
||||
* @param {String} token - 新的 token
|
||||
*/
|
||||
function processQueue(token) {
|
||||
retryQueue.forEach(({ resolve, reject, config }) => {
|
||||
if (token) {
|
||||
// 更新 token 并重试请求
|
||||
if (!config.header) config.header = {};
|
||||
config.header.Authorization = `Bearer ${token}`;
|
||||
uni.request({
|
||||
...config,
|
||||
success: (res) => resolve(res),
|
||||
fail: (err) => reject(err),
|
||||
});
|
||||
} else {
|
||||
reject(new Error("Token 刷新失败"));
|
||||
}
|
||||
});
|
||||
retryQueue = [];
|
||||
retryQueue.forEach(({ resolve, reject, config }) => {
|
||||
if (token) {
|
||||
// 更新 token 并重试请求
|
||||
if (!config.header) config.header = {};
|
||||
config.header.Authorization = `Bearer ${token}`;
|
||||
uni.request({
|
||||
...config,
|
||||
success: (res) => resolve(res),
|
||||
fail: (err) => reject(err),
|
||||
});
|
||||
} else {
|
||||
reject(new Error("Token 刷新失败"));
|
||||
}
|
||||
});
|
||||
retryQueue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,114 +43,114 @@ function processQueue(token) {
|
||||
* @returns {Promise<String|null>}
|
||||
*/
|
||||
async function refreshAccessToken() {
|
||||
const refreshToken = uni.getStorageSync("refreshToken");
|
||||
if (!refreshToken) {
|
||||
return null;
|
||||
}
|
||||
const refreshToken = uni.getStorageSync("refreshToken");
|
||||
if (!refreshToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isRefreshing) {
|
||||
// 等待刷新完成
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
return uni.getStorageSync("accessToken");
|
||||
}
|
||||
if (isRefreshing) {
|
||||
// 等待刷新完成
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
return uni.getStorageSync("accessToken");
|
||||
}
|
||||
|
||||
isRefreshing = true;
|
||||
isRefreshing = true;
|
||||
|
||||
try {
|
||||
const res = await uni.request({
|
||||
url: `${baseUrl}/auth/refresh`,
|
||||
method: "POST",
|
||||
header: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
data: {
|
||||
refreshToken,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const res = await uni.request({
|
||||
url: `${baseUrl}/auth/refresh`,
|
||||
method: "POST",
|
||||
header: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
data: {
|
||||
refreshToken,
|
||||
},
|
||||
});
|
||||
|
||||
if (res && res.data && res.data.success && res.data.data) {
|
||||
const newToken = res.data.data.accessToken;
|
||||
uni.setStorageSync("accessToken", newToken);
|
||||
processQueue(newToken);
|
||||
return newToken;
|
||||
} else {
|
||||
// 刷新失败,清空队列
|
||||
processQueue(null);
|
||||
// 清除登录状态
|
||||
uni.removeStorageSync("accessToken");
|
||||
uni.removeStorageSync("refreshToken");
|
||||
uni.removeStorageSync("jwtUserInfo");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Token 刷新异常:", error);
|
||||
processQueue(null);
|
||||
uni.removeStorageSync("accessToken");
|
||||
uni.removeStorageSync("refreshToken");
|
||||
uni.removeStorageSync("jwtUserInfo");
|
||||
return null;
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
if (res && res.data && res.data.success && res.data.data) {
|
||||
const newToken = res.data.data.accessToken;
|
||||
uni.setStorageSync("accessToken", newToken);
|
||||
processQueue(newToken);
|
||||
return newToken;
|
||||
} else {
|
||||
// 刷新失败,清空队列
|
||||
processQueue(null);
|
||||
// 清除登录状态
|
||||
uni.removeStorageSync("accessToken");
|
||||
uni.removeStorageSync("refreshToken");
|
||||
uni.removeStorageSync("jwtUserInfo");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Token 刷新异常:", error);
|
||||
processQueue(null);
|
||||
uni.removeStorageSync("accessToken");
|
||||
uni.removeStorageSync("refreshToken");
|
||||
uni.removeStorageSync("jwtUserInfo");
|
||||
return null;
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
const request = async (options = {}, showLoading = true) => {
|
||||
// 合并用户传入的配置和默认配置
|
||||
if (!options.data) options.data = {};
|
||||
if(!options.data.corpId) {
|
||||
if (!options.data.corpId) {
|
||||
options.data.corpId = env.MP_CORP_ID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const config = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
url: baseUrl + options.url,
|
||||
};
|
||||
|
||||
|
||||
// 添加 token 到请求头
|
||||
// const accessToken = uni.getStorageSync("accessToken");
|
||||
// if (accessToken) {
|
||||
// if (!config.header) config.header = {};
|
||||
// config.header.Authorization = `Bearer ${accessToken}`;
|
||||
// }
|
||||
|
||||
|
||||
const key = `${JSON.stringify(config)}_${Date.now()}`;
|
||||
if (showLoading) {
|
||||
recordTask(key)
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 发起请求
|
||||
const res = await uni.request(config);
|
||||
if (showLoading) {
|
||||
removeTask(key)
|
||||
}
|
||||
|
||||
|
||||
// 如果返回 401,尝试刷新 token
|
||||
// if (res.statusCode === 401) {
|
||||
// if (showLoading) {
|
||||
// removeTask(key)
|
||||
// }
|
||||
|
||||
|
||||
// // 尝试刷新 token
|
||||
// const newToken = await refreshAccessToken();
|
||||
|
||||
|
||||
// if (newToken) {
|
||||
// // 刷新成功,重试原请求
|
||||
// if (!config.header) config.header = {};
|
||||
// config.header.Authorization = `Bearer ${newToken}`;
|
||||
|
||||
|
||||
// if (showLoading) {
|
||||
// recordTask(key)
|
||||
// }
|
||||
|
||||
|
||||
// const retryRes = await uni.request(config);
|
||||
|
||||
|
||||
// if (showLoading) {
|
||||
// removeTask(key)
|
||||
// }
|
||||
|
||||
|
||||
// const success = retryRes && retryRes.data && retryRes.data.success === true;
|
||||
// if (success) {
|
||||
// return retryRes.data;
|
||||
@ -166,7 +166,7 @@ const request = async (options = {}, showLoading = true) => {
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
const success = res && res.data && res.data.success === true;
|
||||
if (success) {
|
||||
return res.data;
|
||||
@ -206,5 +206,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()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||