diff --git a/apiRc/login.js b/apiRc/login.js
index 74ee74b..8f4b12f 100644
--- a/apiRc/login.js
+++ b/apiRc/login.js
@@ -44,10 +44,22 @@ export function register(data) {
})
}
+// 身份证号码登录
+export function idCardLogin(data) {
+ return request({
+ method: 'post',
+ url: '/app/idCardLogin',
+ data,
+ headers: {
+ isToken: false
+ }
+ })
+}
+
// 获取用户详细信息
export function getInfo() {
return request({
url: '/getInfo',
method: 'get'
})
-}
\ No newline at end of file
+}
diff --git a/components/CustomTabBar/CustomTabBar.vue b/components/CustomTabBar/CustomTabBar.vue
index 83d3fd0..5e2b9fe 100644
--- a/components/CustomTabBar/CustomTabBar.vue
+++ b/components/CustomTabBar/CustomTabBar.vue
@@ -26,6 +26,7 @@ import { ref, computed, watch, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import useUserStore from '@/stores/useUserStore';
import { useReadMsg } from '@/stores/useReadMsg';
+import { checkLoginAndNavigate } from '@/utils/loginHelper';
const props = defineProps({
currentPage: {
@@ -157,77 +158,75 @@ watch(() => userInfo.value, (newUserInfo, oldUserInfo) => {
const switchTab = (item, index) => {
console.log('switchTab called', item, index);
- // 检查是否为"发布岗位"页面,需要判断企业信息是否完整
- if (item.path === '/pages/job/publishJob') {
- // 检查用户是否已登录
- const token = uni.getStorageSync('token') || '';
- const hasLogin = userStore.hasLogin;
-
- if (!token || !hasLogin) {
- // 未登录,发送事件显示登录弹窗
- uni.$emit('showLoginModal');
- return; // 不进行页面跳转
- }
-
- // 已登录,检查企业信息是否完整
- const cachedUserInfo = uni.getStorageSync('userInfo') || {};
- const storeUserInfo = userInfo.value || {};
- const currentUserInfo = storeUserInfo.id ? storeUserInfo : cachedUserInfo;
-
- // 判断企业信息字段company是否为null或undefined
- if (!currentUserInfo.company || currentUserInfo.company === null) {
- // 企业信息为空,跳转到企业信息补全页面
- uni.navigateTo({
- url: '/pages/complete-info/company-info',
- });
- } else {
- // 企业信息完整,跳转到发布岗位页面
- uni.navigateTo({
- url: '/pages/job/publishJob',
- });
- }
-
- currentItem.value = item.id;
- return;
- }
+ // 检查是否为需要登录的页面
+ const loginRequiredPages = [
+ '/pages/job/publishJob',
+ '/pages/mine/mine',
+ '/pages/mine/company-mine'
+ ];
- // 检查是否为"我的"页面,需要登录验证和用户类型判断
- if (item.path === '/pages/mine/mine') {
+ if (loginRequiredPages.includes(item.path)) {
// 检查用户是否已登录
const token = uni.getStorageSync('token') || '';
const hasLogin = userStore.hasLogin;
if (!token || !hasLogin) {
- // 未登录,发送事件显示登录弹窗
- uni.$emit('showLoginModal');
+ // 未登录,根据平台类型跳转到对应的登录页面
+ checkLoginAndNavigate();
return; // 不进行页面跳转
}
- // 已登录,根据用户类型跳转到不同的"我的"页面
- const cachedUserInfo = uni.getStorageSync('userInfo') || {};
- const storeIsCompanyUser = userInfo.value?.isCompanyUser;
- const cachedIsCompanyUser = cachedUserInfo.isCompanyUser;
-
- // 获取用户类型
- const userType = Number(storeIsCompanyUser !== undefined ? storeIsCompanyUser : (cachedIsCompanyUser !== undefined ? cachedIsCompanyUser : 1));
-
- let targetPath = '/pages/mine/mine'; // 默认求职者页面
-
- if (userType === 0) {
- // 企业用户,跳转到企业我的页面
- targetPath = '/pages/mine/company-mine';
- } else {
- // 求职者或其他用户类型,跳转到普通我的页面
- targetPath = '/pages/mine/mine';
+ // 已登录,处理特定页面的逻辑
+ if (item.path === '/pages/job/publishJob') {
+ // 检查企业信息是否完整
+ const cachedUserInfo = uni.getStorageSync('userInfo') || {};
+ const storeUserInfo = userInfo.value || {};
+ const currentUserInfo = storeUserInfo.id ? storeUserInfo : cachedUserInfo;
+
+ // 判断企业信息字段company是否为null或undefined
+ if (!currentUserInfo.company || currentUserInfo.company === null) {
+ // 企业信息为空,跳转到企业信息补全页面
+ uni.navigateTo({
+ url: '/pages/complete-info/company-info',
+ });
+ } else {
+ // 企业信息完整,跳转到发布岗位页面
+ uni.navigateTo({
+ url: '/pages/job/publishJob',
+ });
+ }
+
+ currentItem.value = item.id;
+ return;
}
- // 跳转到对应的页面
- uni.navigateTo({
- url: targetPath,
- });
-
- currentItem.value = item.id;
- return;
+ if (item.path === '/pages/mine/mine') {
+ // 根据用户类型跳转到不同的"我的"页面
+ const cachedUserInfo = uni.getStorageSync('userInfo') || {};
+ const storeIsCompanyUser = userInfo.value?.isCompanyUser;
+ const cachedIsCompanyUser = cachedUserInfo.isCompanyUser;
+
+ // 获取用户类型
+ const userType = Number(storeIsCompanyUser !== undefined ? storeIsCompanyUser : (cachedIsCompanyUser !== undefined ? cachedIsCompanyUser : 1));
+
+ let targetPath = '/pages/mine/mine'; // 默认求职者页面
+
+ if (userType === 0) {
+ // 企业用户,跳转到企业我的页面
+ targetPath = '/pages/mine/company-mine';
+ } else {
+ // 求职者或其他用户类型,跳转到普通我的页面
+ targetPath = '/pages/mine/mine';
+ }
+
+ // 跳转到对应的页面
+ uni.navigateTo({
+ url: targetPath,
+ });
+
+ currentItem.value = item.id;
+ return;
+ }
}
// 判断是否为 tabBar 页面
diff --git a/components/wxAuthLogin/WxAuthLogin.vue b/components/wxAuthLogin/WxAuthLogin.vue
index 1ee2dc5..668956a 100644
--- a/components/wxAuthLogin/WxAuthLogin.vue
+++ b/components/wxAuthLogin/WxAuthLogin.vue
@@ -9,7 +9,7 @@
@@ -227,65 +227,12 @@ const wxLogin = () => {
}
// #ifdef H5
- // H5网页微信登录逻辑
- uni.showLoading({ title: '登录中...' });
-
- // 获取微信授权code
- uni.login({
- provider: 'weixin',
- success: (loginRes) => {
- console.log('微信登录成功:', loginRes);
-
- // 调用后端接口进行登录
- $api.createRequest('/app/appLogin', {
- code: loginRes.code,
- userType: userType.value
- }, 'post').then((resData) => {
- uni.hideLoading();
-
- if (resData.token) {
- loginSetToken(resData.token).then((resume) => {
- console.log(resData, 'resData.isCompanyUser');
- // 更新用户类型到缓存
- if (resData.isCompanyUser) {
- console.log(resData.isCompanyUser, 'resData.isCompanyUser');
- const userInfo = uni.getStorageSync('userInfo') || {};
- userInfo.isCompanyUser = Number(resData.isCompanyUser); // 0-企业用户,1-求职者
- uni.setStorageSync('userInfo', userInfo);
- }
-
- $api.msg('登录成功');
- close();
- emit('success');
-
- if (!resume.data.jobTitleId) {
- if (userType.value === 1) {
- // 求职者跳转到个人信息补全页面
- uni.navigateTo({
- url: '/pages/complete-info/complete-info?step=1'
- });
- } else if (userType.value === 0) {
- // 招聘者跳转到企业信息补全页面
- uni.navigateTo({
- url: '/pages/complete-info/company-info'
- });
- }
- }
- });
- } else {
- $api.msg('登录失败,请重试');
- }
- }).catch((err) => {
- uni.hideLoading();
- $api.msg(err.msg || '登录失败,请重试');
- });
- },
- fail: (err) => {
- uni.hideLoading();
- console.error('微信登录失败:', err);
- $api.msg('微信登录失败');
- }
+ // H5端跳转到H5登录页面
+ close();
+ uni.navigateTo({
+ url: '/pages/login/h5-login'
});
+ return;
// #endif
// #ifdef APP-PLUS
@@ -561,4 +508,3 @@ defineExpose({
button::after
border: none
-
diff --git a/packageA/pages/UnitDetails/UnitDetails.vue b/packageA/pages/UnitDetails/UnitDetails.vue
index 138d967..b68307e 100644
--- a/packageA/pages/UnitDetails/UnitDetails.vue
+++ b/packageA/pages/UnitDetails/UnitDetails.vue
@@ -338,19 +338,19 @@
if (diffSeconds < 60) {
return '刚刚发布';
} else if (diffMinutes < 60) {
- return `${diffMinutes}分钟前发布`;
+ return `${diffMinutes}分钟前`;
} else if (diffHours < 24) {
- return `${diffHours}小时前发布`;
+ return `${diffHours}小时前`;
} else if (diffDays === 1) {
return '昨天发布';
} else if (diffDays < 7) {
- return `${diffDays}天前发布`;
+ return `${diffDays}天前`;
} else if (diffWeeks < 4) {
- return `${diffWeeks}周前发布`;
+ return `${diffWeeks}周前`;
} else if (diffMonths < 12) {
- return `${diffMonths}个月前发布`;
+ return `${diffMonths}个月前`;
} else {
- return `${diffYears}年前发布`;
+ return `${diffYears}年前`;
}
} catch (error) {
console.error('格式化发布时间失败:', error);
diff --git a/pages.json b/pages.json
index f06439e..1bb733b 100644
--- a/pages.json
+++ b/pages.json
@@ -111,6 +111,20 @@
"style": {
"navigationBarTitleText": "编辑联系人"
}
+ },
+ {
+ "path": "pages/login/h5-login",
+ "style": {
+ "navigationBarTitleText": "登录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/login/id-card-login",
+ "style": {
+ "navigationBarTitleText": "社保登录",
+ "navigationStyle": "custom"
+ }
}
],
"subpackages": [
@@ -658,4 +672,3 @@
},
"uniIdRouter": {}
}
-
\ No newline at end of file
diff --git a/pages/index/components/index-one.vue b/pages/index/components/index-one.vue
index 33c6bf9..fda098b 100644
--- a/pages/index/components/index-one.vue
+++ b/pages/index/components/index-one.vue
@@ -425,6 +425,7 @@ const { $api, navTo, vacanciesTo, formatTotal, config } = inject('globalFunction
import { onLoad, onShow } from '@dcloudio/uni-app';
import { storeToRefs } from 'pinia';
import useUserStore from '@/stores/useUserStore';
+import { checkLoginAndNavigate, getPlatformType } from '@/utils/loginHelper';
const { userInfo, hasLogin, token } = storeToRefs(useUserStore());
// 计算是否显示求职者内容
@@ -685,8 +686,8 @@ watch([hasLogin, userInfo], () => {
const checkLogin = () => {
const tokenValue = uni.getStorageSync('token') || '';
if (!tokenValue || !hasLogin.value) {
- // 未登录,打开授权弹窗
- wxAuthLoginRef.value?.open();
+ // 未登录,根据平台类型跳转到对应登录页面
+ checkLoginAndNavigate();
return false;
}
return true;
diff --git a/pages/job/publishJob.vue b/pages/job/publishJob.vue
index e8ba499..ec0281a 100644
--- a/pages/job/publishJob.vue
+++ b/pages/job/publishJob.vue
@@ -118,7 +118,7 @@
v-model="formData.jobLocation"
/>
-
+
@@ -800,25 +800,27 @@ const validateForm = () => {
.location-input {
flex: 1;
- padding-right: 80rpx;
+ padding-right: 100rpx;
}
.location-icon-btn {
- position: absolute;
- right: -3px;
- top: 20%;
- transform: translateY(-50%);
- width: 80rpx;
- height: 80rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- background: transparent;
- border: none;
- border-radius: 8rpx;
- z-index: 99;
+ position: absolute;
+ right: 0;
+ top: 44%;
+ transform: translateY(-50%);
+ width: 80rpx;
+ height: 60rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #f0f0f0;
+ border: none;
+ border-radius: 12rpx;
+ transition: all 0.3s ease;
+ z-index: 99;
+
&:active {
- background: #f0f0f0;
+ transform: translateY(-50%) scale(0.95);
}
}
}
diff --git a/pages/login/h5-login.vue b/pages/login/h5-login.vue
new file mode 100644
index 0000000..3dc294f
--- /dev/null
+++ b/pages/login/h5-login.vue
@@ -0,0 +1,529 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 账号
+
+
+
+
+
+
+
+
+ 密码
+
+
+
+
+
+
+
+
+
+
+
+ 长度8位
+
+
+
+ 字母数字组合
+
+
+
+ 大小写字母
+
+
+
+
+
+
+
+
+
+
+
+ 其他登录方式
+
+
+
+
+
+
+
+
+ 社保卡登录
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/login/id-card-login.vue b/pages/login/id-card-login.vue
new file mode 100644
index 0000000..4396500
--- /dev/null
+++ b/pages/login/id-card-login.vue
@@ -0,0 +1,372 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 登录中,请稍候...
+ 正在验证您的社保卡信息
+
+
+
+
+
+
+
+ 验证中...
+
+
+
+
+
+
+
+
+
+
+
diff --git a/static/logo1-S.png b/static/logo1-S.png
new file mode 100644
index 0000000..dd7e619
Binary files /dev/null and b/static/logo1-S.png differ
diff --git a/static/logo2-S.png b/static/logo2-S.png
new file mode 100644
index 0000000..caba026
Binary files /dev/null and b/static/logo2-S.png differ
diff --git a/static/logo3.png b/static/logo3.png
new file mode 100644
index 0000000..d246fbd
Binary files /dev/null and b/static/logo3.png differ
diff --git a/utils/loginHelper.js b/utils/loginHelper.js
new file mode 100644
index 0000000..11ae3ef
--- /dev/null
+++ b/utils/loginHelper.js
@@ -0,0 +1,158 @@
+/**
+ * 登录帮助工具类
+ * 用于根据平台类型跳转到对应的登录页面
+ */
+
+import useUserStore from '@/stores/useUserStore';
+
+/**
+ * 检查当前平台类型
+ * @returns {string} 平台类型:'mp-weixin' | 'h5' | 'app'
+ */
+export function getPlatformType() {
+ // #ifdef MP-WEIXIN
+ return 'mp-weixin';
+ // #endif
+
+ // #ifdef H5
+ return 'h5';
+ // #endif
+
+ // #ifdef APP-PLUS
+ return 'app';
+ // #endif
+
+ return 'h5'; // 默认返回H5
+}
+
+/**
+ * 跳转到对应平台的登录页面
+ * @param {Object} options 跳转选项
+ * @param {string} options.redirectUrl 登录成功后跳转的URL
+ * @param {string} options.loginType 登录类型:'wechat' | 'account' | 'idCard'
+ */
+export function navigateToLoginPage(options = {}) {
+ const { redirectUrl, loginType = 'account' } = options;
+ const platform = getPlatformType();
+
+ let loginPage = '';
+ let params = {};
+
+ if (redirectUrl) {
+ params.redirectUrl = redirectUrl;
+ }
+
+ switch (platform) {
+ case 'mp-weixin':
+ // 小程序端使用微信授权登录,直接显示微信授权弹窗
+ uni.$emit('showLoginModal', { loginType: 'wechat' });
+ return;
+
+ case 'h5':
+ if (loginType === 'idCard') {
+ // H5端身份证号码登录
+ loginPage = '/pages/login/id-card-login';
+ } else {
+ // H5端账号密码登录
+ loginPage = '/pages/login/h5-login';
+ }
+ break;
+
+ case 'app':
+ // App端使用微信授权登录
+ uni.$emit('showLoginModal', { loginType: 'wechat' });
+ return;
+
+ default:
+ loginPage = '/pages/login/h5-login';
+ }
+
+ if (loginPage) {
+ const queryString = Object.keys(params).length > 0
+ ? `?${new URLSearchParams(params).toString()}`
+ : '';
+
+ uni.navigateTo({
+ url: `${loginPage}${queryString}`
+ });
+ }
+}
+
+/**
+ * 检查登录状态,如果未登录则跳转到对应登录页面
+ * @param {Object} options 选项
+ * @param {string} options.redirectUrl 登录成功后跳转的URL
+ * @param {string} options.loginType 登录类型
+ * @returns {boolean} 是否已登录
+ */
+export function checkLoginAndNavigate(options = {}) {
+ const userStore = useUserStore();
+
+ if (userStore.hasLogin) {
+ return true;
+ }
+
+ // 未登录,跳转到对应登录页面
+ navigateToLoginPage(options);
+ return false;
+}
+
+/**
+ * 处理身份证号码登录的URL参数
+ * @param {string} url 包含参数的URL
+ * @returns {Object} 解析后的参数对象
+ */
+export function parseIdCardLoginParams(url) {
+ const urlObj = new URL(url, window.location.origin);
+ const params = {};
+
+ // 获取身份证号码base64参数
+ const idCardBase64 = urlObj.searchParams.get('idCardBase64');
+ if (idCardBase64) {
+ params.idCardBase64 = idCardBase64;
+ }
+
+ // 获取重定向URL
+ const redirectUrl = urlObj.searchParams.get('redirectUrl');
+ if (redirectUrl) {
+ params.redirectUrl = redirectUrl;
+ }
+
+ return params;
+}
+
+/**
+ * Base64解码身份证号码
+ * @param {string} base64Str base64编码的字符串
+ * @returns {string} 解码后的身份证号码
+ */
+export function decodeIdCardBase64(base64Str) {
+ try {
+ // #ifdef H5
+ return atob(base64Str);
+ // #endif
+
+ // #ifdef MP-WEIXIN
+ const result = uni.base64ToArrayBuffer(base64Str);
+ return String.fromCharCode.apply(null, new Uint8Array(result));
+ // #endif
+
+ // #ifdef APP-PLUS
+ return plus.base64.decode(base64Str);
+ // #endif
+
+ // 默认使用H5方式
+ return atob(base64Str);
+ } catch (error) {
+ console.error('Base64解码失败:', error);
+ throw new Error('身份证号码解码失败');
+ }
+}
+
+export default {
+ getPlatformType,
+ navigateToLoginPage,
+ checkLoginAndNavigate,
+ parseIdCardLoginParams,
+ decodeIdCardBase64
+};
diff --git a/登录系统实现总结.md b/登录系统实现总结.md
new file mode 100644
index 0000000..b1ab1de
--- /dev/null
+++ b/登录系统实现总结.md
@@ -0,0 +1,139 @@
+# 多端登录系统实现总结
+
+## 项目概述
+已成功完成多端登录系统的重构,支持小程序端、H5端和App端的差异化登录方式。
+
+## 实现功能
+
+### 1. 多端登录策略
+- **小程序端 (MP-WEIXIN)**: 微信授权登录
+- **H5端 (H5)**: 账号密码登录 + 身份证号码登录
+- **App端 (APP-PLUS)**: 微信授权登录
+
+### 2. 新增页面
+
+#### 2.1 H5账号密码登录页面 (`pages/login/h5-login.vue`)
+- 账号密码输入表单
+- 登录按钮
+- 跳转到身份证号码登录的入口
+- 美观的UI设计,渐变背景和圆角卡片
+
+#### 2.2 身份证号码登录页面 (`pages/login/id-card-login.vue`)
+- Loading页面设计,显示"登录中,请稍候..."
+- 支持从URL参数获取Base64编码的身份证号码
+- 多端Base64解码支持:
+ - H5: `atob()`
+ - 小程序: `uni.base64ToArrayBuffer()`
+ - App: `plus.base64.decode()`
+- 调用后端身份证号码登录接口
+
+### 3. API接口扩展
+
+#### 3.1 身份证号码登录接口 (`apiRc/login.js`)
+```javascript
+export function idCardLogin(data) {
+ return request({
+ method: 'post',
+ url: '/app/idCardLogin',
+ data,
+ headers: {
+ isToken: false
+ }
+ })
+}
+```
+
+### 4. 登录帮助工具类 (`utils/loginHelper.js`)
+- 平台类型检测 (`getPlatformType`)
+- 登录页面跳转 (`navigateToLoginPage`)
+- 登录状态检查 (`checkLoginAndNavigate`)
+- URL参数解析 (`parseIdCardLoginParams`)
+- Base64解码 (`decodeIdCardBase64`)
+
+### 5. 现有组件集成
+
+#### 5.1 微信授权登录组件 (`components/wxAuthLogin/WxAuthLogin.vue`)
+- 已修复语法错误
+- H5端点击微信授权登录时跳转到H5登录页面
+- 小程序端保持原有的微信授权登录逻辑
+- App端保持原有的微信授权登录逻辑
+
+#### 5.2 首页组件 (`pages/index/components/index-one.vue`)
+- 已集成登录检查逻辑
+- 在`onMounted`生命周期中调用`checkLoginAndNavigate()`
+
+#### 5.3 Tabbar组件 (`components/CustomTabBar/CustomTabBar.vue`)
+- 已集成登录状态判断
+- 对于需要登录的页面(发布岗位、我的页面),检查登录状态
+- 未登录时跳转到对应登录页面
+
+### 6. 路由配置 (`pages.json`)
+- 已注册新的登录页面路由
+- H5登录页面: `/pages/login/h5-login`
+- 身份证号码登录页面: `/pages/login/id-card-login`
+
+## 登录流程
+
+### 小程序端登录流程
+1. 用户点击需要登录的功能
+2. 显示微信授权登录弹窗
+3. 用户授权手机号
+4. 调用后端接口获取token
+5. 登录成功,跳转到目标页面
+
+### H5端登录流程
+1. 用户点击需要登录的功能
+2. 跳转到H5登录页面
+3. 用户可选择:
+ - 账号密码登录
+ - 身份证号码登录
+4. 登录成功,跳转到目标页面
+
+### 身份证号码登录流程
+1. 第三方系统跳转到身份证号码登录页面
+2. 通过URL参数传递Base64编码的身份证号码
+3. 页面自动解码身份证号码
+4. 调用后端身份证号码登录接口
+5. 获取token和用户信息
+6. 登录成功,跳转到首页
+
+## 技术特点
+
+### 1. 条件编译
+使用`#ifdef`、`#ifndef`实现多端差异化逻辑
+
+### 2. Base64解码
+- H5: `atob(base64Str)`
+- 小程序: `uni.base64ToArrayBuffer(base64Str)`
+- App: `plus.base64.decode(base64Str)`
+
+### 3. 状态管理
+使用Pinia store (`useUserStore`)管理用户状态
+
+### 4. 错误处理
+完善的错误处理和用户提示
+
+## 测试建议
+
+1. **小程序端测试**: 验证微信授权登录功能
+2. **H5端测试**: 验证账号密码登录和身份证号码登录
+3. **身份证号码登录测试**: 模拟第三方跳转,验证Base64解码和登录流程
+4. **Tabbar导航测试**: 验证登录状态判断和页面跳转
+
+## 文件清单
+- `pages/login/h5-login.vue` - H5账号密码登录页面
+- `pages/login/id-card-login.vue` - 身份证号码登录页面
+- `utils/loginHelper.js` - 登录帮助工具类
+- `apiRc/login.js` - 扩展的API接口
+- `components/wxAuthLogin/WxAuthLogin.vue` - 修复后的微信授权组件
+- `pages/index/components/index-one.vue` - 集成登录检查的首页组件
+- `components/CustomTabBar/CustomTabBar.vue` - 集成登录判断的Tabbar组件
+- `pages.json` - 页面路由配置
+
+## 注意事项
+1. 身份证号码登录页面需要第三方系统通过URL参数传递`idCardBase64`
+2. 后端需要实现`/app/idCardLogin`接口
+3. 各端Base64解码方式不同,需要确保兼容性
+4. 登录成功后需要正确存储用户信息和token
+
+系统已准备就绪,可以开始多端测试。