diff --git a/.env.localhost b/.env.localhost
index 29dbff8..f379283 100644
--- a/.env.localhost
+++ b/.env.localhost
@@ -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
diff --git a/App.vue b/App.vue
index 1010425..0ea7c2d 100644
--- a/App.vue
+++ b/App.vue
@@ -1,369 +1,368 @@
-
-
-
\ No newline at end of file
+
+
+
diff --git a/components/button-footer.vue b/components/button-footer.vue
index b2e924f..2677c92 100644
--- a/components/button-footer.vue
+++ b/components/button-footer.vue
@@ -1,5 +1,5 @@
-
diff --git a/components/form-template/form-cell/form-select.vue b/components/form-template/form-cell/form-select.vue
index d261253..5cdc4cc 100644
--- a/components/form-template/form-cell/form-select.vue
+++ b/components/form-template/form-cell/form-select.vue
@@ -1,5 +1,5 @@
-
+
@@ -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
})
}
diff --git a/components/form-template/form-cell/form-textarea.vue b/components/form-template/form-cell/form-textarea.vue
index 3b6b40b..73342d0 100644
--- a/components/form-template/form-cell/form-textarea.vue
+++ b/components/form-template/form-cell/form-textarea.vue
@@ -4,8 +4,9 @@
{{ name }}
-
+
{{ value && value.length ? value.length : 0 }} / {{ wordLimit }}
@@ -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;
diff --git a/components/full-page.vue b/components/full-page.vue
index 0fa427f..45eefc8 100644
--- a/components/full-page.vue
+++ b/components/full-page.vue
@@ -1,9 +1,9 @@
-
+
-
+
@@ -16,7 +16,7 @@
-
+
@@ -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);
diff --git a/hooks/useGuard.js b/hooks/useGuard.js
index 7d0f41b..f027e2d 100644
--- a/hooks/useGuard.js
+++ b/hooks/useGuard.js
@@ -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);
diff --git a/pages.json b/pages.json
index a97b558..55a5b8a 100644
--- a/pages.json
+++ b/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": "授权登录"
}
}
],
diff --git a/pages/login/login.vue b/pages/login/login.vue
index ee59991..e0a5c39 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -1,5 +1,5 @@
-
+
+
柚健康
生命全周期健康管理伙伴
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
diff --git a/pages/work/verify/assistant.vue b/pages/work/verify/assistant.vue
new file mode 100644
index 0000000..eb45ac0
--- /dev/null
+++ b/pages/work/verify/assistant.vue
@@ -0,0 +1,164 @@
+
+
+
+
+ 请上传
+ 身份证正反面
+
+
+
+
+
+
+
+
+ 上传人像面
+
+
+
+
+
+
+
+
+
+ 上传国徽面
+
+
+
+
+
+ 身份证示例
+
+
+
+
+
+ 拍摄须知
+
+
+
+
+ 标准
+
+
+
+
+ 缺边
+
+
+
+
+ 模糊
+
+
+
+
+ 闪光
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/work/verify/doctor.vue b/pages/work/verify/doctor.vue
new file mode 100644
index 0000000..d13ab60
--- /dev/null
+++ b/pages/work/verify/doctor.vue
@@ -0,0 +1,126 @@
+
+
+
+ 请填写执业医院
+
+
+ {{ formData.hospitalName || '请填写执业医院' }}
+
+
+
+
+ 请上传
+ 医师执业资格证
+
+
+
+
+
+
+ 上传第一页
+
+
+
+
+
+
+ 上传第二页
+
+
+
+
+ 证书示例
+
+ 1、确保姓名、照片、编号、执业范围、签发机关等清晰可见
+ 2、需上传证书第一、第二页,图片仅供参考,以实际证书为准
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/work/work.vue b/pages/work/work.vue
index 0b2dce0..d2055f0 100644
--- a/pages/work/work.vue
+++ b/pages/work/work.vue
@@ -1,80 +1,93 @@
-
-
-
\ No newline at end of file
+// }
\ No newline at end of file
diff --git a/routes/index.js b/routes/index.js
index 97275ac..270d820 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -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: '授权登录' }
+ },
]
diff --git a/static/work/aIDCard.png b/static/work/aIDCard.png
new file mode 100644
index 0000000..66bdc4f
Binary files /dev/null and b/static/work/aIDCard.png differ
diff --git a/static/work/camera.png b/static/work/camera.png
new file mode 100644
index 0000000..61ede37
Binary files /dev/null and b/static/work/camera.png differ
diff --git a/static/work/cardBack.png b/static/work/cardBack.png
new file mode 100644
index 0000000..337dd1f
Binary files /dev/null and b/static/work/cardBack.png differ
diff --git a/static/work/cardFront.png b/static/work/cardFront.png
new file mode 100644
index 0000000..30d923f
Binary files /dev/null and b/static/work/cardFront.png differ
diff --git a/static/work/error.png b/static/work/error.png
new file mode 100644
index 0000000..1d2dbe0
Binary files /dev/null and b/static/work/error.png differ
diff --git a/static/work/fIDCard.png b/static/work/fIDCard.png
new file mode 100644
index 0000000..99e5b40
Binary files /dev/null and b/static/work/fIDCard.png differ
diff --git a/static/work/flashCard.png b/static/work/flashCard.png
new file mode 100644
index 0000000..0b3a4ed
Binary files /dev/null and b/static/work/flashCard.png differ
diff --git a/static/work/hook.png b/static/work/hook.png
new file mode 100644
index 0000000..c337dc7
Binary files /dev/null and b/static/work/hook.png differ
diff --git a/static/work/lackCard.png b/static/work/lackCard.png
new file mode 100644
index 0000000..a14d205
Binary files /dev/null and b/static/work/lackCard.png differ
diff --git a/static/work/licenseBack.png b/static/work/licenseBack.png
new file mode 100644
index 0000000..bd10e67
Binary files /dev/null and b/static/work/licenseBack.png differ
diff --git a/static/work/licenseFront.png b/static/work/licenseFront.png
new file mode 100644
index 0000000..ec36c2e
Binary files /dev/null and b/static/work/licenseFront.png differ
diff --git a/static/work/more.svg b/static/work/more.svg
new file mode 100644
index 0000000..dae9067
--- /dev/null
+++ b/static/work/more.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/work/pen.svg b/static/work/pen.svg
new file mode 100644
index 0000000..89fe9b4
--- /dev/null
+++ b/static/work/pen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/work/qrcode.svg b/static/work/qrcode.svg
new file mode 100644
index 0000000..565f66a
--- /dev/null
+++ b/static/work/qrcode.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/work/vagueCard.png b/static/work/vagueCard.png
new file mode 100644
index 0000000..cfad2d3
Binary files /dev/null and b/static/work/vagueCard.png differ
diff --git a/store/account.js b/store/account.js
index 44e6e47..a4ce42f 100644
--- a/store/account.js
+++ b/store/account.js
@@ -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() {
diff --git a/utils/api.js b/utils/api.js
index 243ca6b..db1e8aa 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -11,7 +11,9 @@ const urlsConfig = {
getHospitalList: 'getRealHospital',
addCorpMember: 'addCorpMember',
getCorpMemberData: 'getCorpMemberData',
- updateCorpMember: 'updateCorpMember'
+ updateCorpMember: 'updateCorpMember',
+ addCorpMemberFromWxapp: "addCorpMemberFromWxapp",
+ updateCorpMemberFromWxapp: "updateCorpMemberFromWxapp"
},
knowledgeBase: {
diff --git a/utils/http.js b/utils/http.js
index e29328e..6a77cd2 100644
--- a/utils/http.js
+++ b/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}
*/
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()
+ }
+ })
+ })
+}
+