Просмотр исходного кода

feat: 更新优化注册流程代码

yezhihao 5 месяцев назад
Родитель
Сommit
948d275f01
90 измененных файлов с 7164 добавлено и 2230 удалено
  1. 4 3
      .hbuilderx/launch.json
  2. 29 13
      App.vue
  3. 8 10
      common/config.js
  4. 8 4
      common/httpRequest.js
  5. 90 0
      common/queue.js
  6. 126 0
      components/c-modal.vue
  7. 1 3
      components/mescroll-uni/good-list/good-list.vue
  8. 9 4
      components/nav-bar/index.vue
  9. 6 28
      components/resume/resume-card.vue
  10. 188 4
      manifest.json
  11. 1 3
      my/enterpriseInfo/enterpriseInfo.vue
  12. 151 287
      package/jobIntention/addAddress.vue
  13. 48 2
      package/jobIntention/basicInfo.vue
  14. 201 61
      package/jobIntention/companyImg.vue
  15. 173 207
      package/jobIntention/completeMsg.vue
  16. 307 150
      package/jobIntention/fund.vue
  17. 56 59
      package/jobIntention/preferenceSetting.vue
  18. 173 177
      package/jobIntention/underReview.vue
  19. 2 4
      package/list/baoming.vue
  20. 2 4
      package/list/studyUp.vue
  21. 2 4
      package/list/train.vue
  22. 2 4
      package/list/zhiCheng.vue
  23. 59 160
      package/my/hiddenManagement/hiddenCompany.vue
  24. 103 10
      package/my/hiddenManagement/index.vue
  25. 2 7
      package/search/search.vue
  26. 37 16
      pages.json
  27. 3 9
      pages/index/game/order.vue
  28. 4 10
      pages/index/game/orderDet.vue
  29. 426 374
      pages/index/index.vue
  30. 1 3
      pages/index/search/index.vue
  31. 312 131
      pages/jobManagement/jobManagement.vue
  32. 73 22
      pages/my/businessLicense.vue
  33. 36 17
      pages/my/index.vue
  34. 4 1
      pages/my/jobApplicant/guidePage.vue
  35. 8 3
      pages/my/jobApplicant/startPage.vue
  36. 150 13
      pages/my/jobPosting.vue
  37. 135 29
      pages/my/jobPostingSecond.vue
  38. 56 0
      pages/my/joinCompany.vue
  39. 179 15
      pages/my/onlineResume.vue
  40. 5 5
      pages/my/setup.vue
  41. 1 3
      pages/my/switchRoles.vue
  42. 42 104
      pages/my/userinfo.vue
  43. 73 2
      pages/public/improvePrompt.vue
  44. 160 160
      pages/public/login.vue
  45. 326 101
      pages/public/loginV2.vue
  46. 231 0
      pages/public/phoneLoginOrRegister.vue
  47. 141 0
      pages/public/verify.vue
  48. 2 4
      pages/talentSearch/index.vue
  49. 0 0
      static/checked.png
  50. BIN
      static/close.png
  51. BIN
      static/images/oneKeyLogin/checked.png
  52. BIN
      static/images/oneKeyLogin/close.png
  53. BIN
      static/images/oneKeyLogin/logo.png
  54. 0 0
      static/images/oneKeyLogin/phone.png
  55. 0 0
      static/images/oneKeyLogin/unchecked.png
  56. 0 0
      static/images/oneKeyLogin/wechat.png
  57. 8 0
      static/images/svg/add-border.svg
  58. 4 0
      static/images/svg/refresh-arrow.svg
  59. 11 0
      static/images/svg/tie.svg
  60. BIN
      static/invite.png
  61. BIN
      static/login-btn.png
  62. BIN
      static/logo.png
  63. BIN
      static/phone.png
  64. BIN
      static/unchecked.png
  65. BIN
      static/wechat.png
  66. 4 0
      uni_modules/sr-file-choose/changelog.md
  67. 87 0
      uni_modules/sr-file-choose/package.json
  68. 52 0
      uni_modules/sr-file-choose/readme.md
  69. 12 0
      uni_modules/sr-file-choose/utssdk/app-android/AndroidManifest.xml
  70. 3 0
      uni_modules/sr-file-choose/utssdk/app-android/config.json
  71. 81 0
      uni_modules/sr-file-choose/utssdk/app-android/filePicker.kt
  72. 212 0
      uni_modules/sr-file-choose/utssdk/app-android/index.uts
  73. 72 0
      uni_modules/sr-file-choose/utssdk/app-harmony/index.uts
  74. 3 0
      uni_modules/sr-file-choose/utssdk/app-ios/config.json
  75. 93 0
      uni_modules/sr-file-choose/utssdk/app-ios/filePicker.swift
  76. 69 0
      uni_modules/sr-file-choose/utssdk/app-ios/index.uts
  77. 16 0
      uni_modules/sr-file-choose/utssdk/app-ios/info.plist
  78. 49 0
      uni_modules/sr-file-choose/utssdk/interface.uts
  79. 39 0
      uni_modules/sr-file-choose/utssdk/unierror.uts
  80. 91 0
      uni_modules/uni-file-picker/changelog.md
  81. 287 0
      uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js
  82. 680 0
      uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue
  83. 323 0
      uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue
  84. 285 0
      uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue
  85. 110 0
      uni_modules/uni-file-picker/components/uni-file-picker/utils.js
  86. 105 0
      uni_modules/uni-file-picker/package.json
  87. 10 0
      uni_modules/uni-file-picker/readme.md
  88. 169 0
      utils/jVerificationLogin.js
  89. 101 0
      utils/login.js
  90. 32 0
      utils/util.js

+ 4 - 3
.hbuilderx/launch.json

@@ -2,9 +2,10 @@
     "version" : "1.0",
     "configurations" : [
         {
-            "customPlaygroundType" : "device",
-            "packageName" : "io.dcloud.HBuilder",
-            "playground" : "standard",
+            "customPlaygroundType" : "local",
+            "localRepoPath" : "E:/projects/yizhizan-h5",
+            "packageName" : "uni.app.UNI0B0096D",
+            "playground" : "custom",
             "type" : "uni-app:app-android"
         },
         {

+ 29 - 13
App.vue

@@ -8,20 +8,36 @@ export default {
     // #ifdef APP
     // 检查是否首次启动 - 引导页逻辑
     let firstUpload = uni.getStorageSync("firstUpload") || false;
+	let token = this.$queue.getData('token')
+	let userType = this.$queue.getData('userType')
+	let companyStatus = this.$queue.getData('companyStatus')
     if (!firstUpload) {
-      // 标记已经显示过引导页
-      uni.setStorageSync("firstUpload", true);
-      // 跳转到引导页
-      uni.navigateTo({
-        url: "/pages/my/jobApplicant/startPage",
-        success: () => {
-          // 跳转成功后,不再执行后面的逻辑
-          return;
-        },
-      });
-      // 这里需要return,避免执行后面的代码
-      return;
-    }
+		// 跳转到引导页
+		uni.reLaunch({
+			url: "/pages/my/jobApplicant/startPage",
+			success: () => {
+				// 标记已经显示过引导页
+				uni.setStorageSync("firstUpload", true);
+			}
+		});
+		// 这里需要return,避免执行后面的代码
+		return;
+    } else if (
+		token && 
+		(!userType || (userType == 2 && companyStatus != 2))
+	) {
+		// 有登录token,但是没有userType,跳转选择角色页面,强制用户选择
+		// 有登录token,并且userType为2,但是公司状态没有通过
+		uni.reLaunch({
+			url: '/pages/my/jobApplicant/guidePage',
+		})
+		return
+	} else if (token && userType) {
+		// 已经登录,且有userType
+		uni.reLaunch({
+			url: '/pages/index/index',
+		})
+	}
     // #endif
 
     // #ifdef APP || H5

+ 8 - 10
common/config.js

@@ -1,10 +1,7 @@
 const h5Url='https://h5.bosszan.com'
 const ossUrl='https://yi-zhipin.oss-cn-hangzhou.aliyuncs.com/emoji/'
-let mobile=0;
-// #ifdef APP-PLUS
-mobile=1
-//#endif
-if (process.env.NODE_ENV !== 'development'||mobile) {
+let isProduction = false // 判断是否是生产环境
+if (isProduction) {
 	//正式环境
 	const ROOTPATH = "https://www.bosszan.com/sqx_fast";
 	const ROOTPATH1 = "https://www.bosszan.com/sqx_fast";
@@ -19,7 +16,8 @@ if (process.env.NODE_ENV !== 'development'||mobile) {
 		WSHOST1: ROOTPATH3,
 		WSHOST4: ROOTPATH4,
 		h5Url,
-		ossUrl
+		ossUrl,
+		isProduction
 	};
 }else{
 	//测试环境
@@ -27,8 +25,8 @@ if (process.env.NODE_ENV !== 'development'||mobile) {
 	const ROOTPATH = "https://yizhizan.edccc.cn/sqx_fast";
 	// const ROOTPATH1 = "/sqx_fast";
 	const ROOTPATH1 = "https://yizhizan.edccc.cn/sqx_fast";
-	const ROOTPATH2 = "wss://www.bosszan.com/wss/websocket/"; //联系客服
-	const ROOTPATH3 = "ws://192.168.0.10:7155/sqx_fast/chatSocket/"; //聊天
+	const ROOTPATH2 = "wss://yizhizan.edccc.cn/wss/websocket/"; //联系客服
+	const ROOTPATH3 = "wss://yizhizan.edccc.cn/wss/chatSocket/"; //聊天
 	const ROOTPATH4 = "";
 	module.exports = {
 		APIHOST: ROOTPATH,
@@ -37,7 +35,7 @@ if (process.env.NODE_ENV !== 'development'||mobile) {
 		WSHOST1: ROOTPATH3,
 		WSHOST4: ROOTPATH4,
 		h5Url,
-		ossUrl
-	
+		ossUrl,
+		isProduction
 	};
 }

+ 8 - 4
common/httpRequest.js

@@ -1,5 +1,6 @@
 import configdata from './config'
 import cache from './cache'
+import queue from './queue'
 
 module.exports = {
 	config: function(name) {
@@ -58,9 +59,10 @@ module.exports = {
 						   setTimeout(() => {
 						      // 选择合适的路由方法(根据需求切换)
 						      // 1. 跳转到登录页,关闭当前页面(推荐,避免回退到失效页面)
-						      uni.redirectTo({
-						        url: '/pages/public/login' // 替换为你的登录页路径
-						      });
+							  queue.toLogin()
+						    //   uni.redirectTo({
+						    //     url: '/pages/public/login' // 替换为你的登录页路径
+						    //   });
 						
 						      // 2. 如果需要关闭所有页面,强制跳转到登录页(比如退出登录场景)
 						      // uni.reLaunch({
@@ -82,16 +84,18 @@ module.exports = {
 			})
 		})
 	},
-	postT: function(url, data, header) {
+	postT: function(url, data, header, timeout) {
 		header = header || "application/x-www-form-urlencoded";
 		url = this.config("APIHOST1") + url;
 		let token = uni.getStorageSync("token");
+		console.log('timeout', timeout)
 		if (token) {
 			return new Promise((succ, error) => {
 				uni.request({
 					url: url,
 					data: data,
 					method: "POST",
+					timeout: timeout ? timeout : 60000,
 					header: {
 						"content-type": header,
 						"token": token

+ 90 - 0
common/queue.js

@@ -24,6 +24,20 @@ module.exports = {
 	publicYuMing() {
 		return configdata.h5Url
 	},
+	// 登录判断
+	toLogin(type) {
+		let url = '/pages/public/loginV2'
+		// #ifdef H5
+		url = '/pages/public/login'
+		// #endif
+		switch (type) {
+			case 'navigateTo':
+				uni.navigateTo({ url })
+				break
+			default:
+				uni.reLaunch({ url })
+		}
+	},
 	logout() {
 		this.remove("token");
 		this.remove("userId");
@@ -47,6 +61,9 @@ module.exports = {
 		this.remove('vipDueTimes')
 		this.remove('msgNum')
 		this.remove('msgPrice')
+		this.remove('invitationCode')
+		this.remove('inviterCode')
+		this.remove('hadShowPoster')
 	},
 	loginClear() {
 		this.remove("token");
@@ -237,6 +254,47 @@ module.exports = {
 			}
 		})
 	},
+	/**
+	 * 上传文件
+	 * @param {Object} params 参数
+	 *  url:服务器地址
+	 *  path:文件本地地址
+	 *  name:文件名称
+	 *  formData:上传参数
+	 * @param {Object} callback
+	 */
+	uploadFiles: function(params, callback) {
+		let url = params.url ? params.url : ''
+		let filePath = params.path ? params.path : ''
+		let name = params.name ? params.name : ''
+		let formData = params.data ? params.data : {}
+		const uploadTask = uni.uploadFile({ // 上传接口
+			url: `${configdata.APIHOST}${url}`, //真实的接口地址
+			filePath,
+			name,
+			formData,
+			header: {
+				token: uni.getStorageSync('token')
+			},
+			success: (uploadFileRes) => {
+				let content = JSON.parse(uploadFileRes.data)
+				uni.hideLoading();
+				callback(content)
+			},
+			fail: (err) => {
+				callback(false)
+				console.log(err)
+			}
+		});
+		uploadTask.onProgressUpdate((res) => {
+			uni.showLoading({
+				title: '上传进度' + res.progress + '%',
+			})
+			if (res.progress >= 100) {
+				uni.hideLoading()
+			}
+		})
+	},
 	array_column: function(arr, column_key, index_key = null) {
 		if (index_key === null) {
 			return arr.map(item => item[column_key]);
@@ -711,5 +769,37 @@ module.exports = {
 				}
 			})
 		})
+	},
+	
+	/**
+	 * 深合并两个对象:将前一个对象的数据合并到后一个对象(支持嵌套对象)
+	 * @param {Object} sourceObj - 源对象(提供数据的对象,前一个对象)
+	 * @param {Object} targetObj - 目标对象(被合并的对象,后一个对象)
+	 * @returns {Object} 合并后的目标对象
+	 */
+	deepMergeObject(sourceObj, targetObj) {
+		// 1. 参数校验
+		const isValidObj = (obj) => obj !== null && typeof obj === 'object' && !Array.isArray(obj);
+		if (!isValidObj(sourceObj)) return isValidObj(targetObj) ? targetObj : {};
+		if (!isValidObj(targetObj)) return JSON.parse(JSON.stringify(sourceObj)); // 简单深拷贝源对象
+	
+		// 2. 遍历源对象属性
+		Object.keys(sourceObj).forEach((key) => {
+			const sourceValue = sourceObj[key];
+			const targetValue = targetObj[key];
+	
+			// 3. 处理嵌套对象(递归合并)
+			if (isValidObj(sourceValue) && isValidObj(targetValue)) {
+				deepMergeObject(sourceValue, targetValue); // 递归处理嵌套对象
+				return; // 嵌套对象处理完毕,跳过后续逻辑
+			}
+		
+			// 4. 非嵌套对象,按照原规则处理
+			if (!(key in targetObj) || targetValue === null || targetValue === undefined) {
+				targetObj[key] = sourceValue;
+			}
+		});
+	
+		return targetObj;
 	}
 };

+ 126 - 0
components/c-modal.vue

@@ -0,0 +1,126 @@
+<template>
+	<u-modal
+		v-model="visible"
+		:show-confirm-button="false"
+		:show-cancel-button="false"
+		:show-title="false"
+	>
+		<view class="modal-container">
+			<view class="wrapper">
+				<view class="title" v-if="showTitle">{{ title }}</view>
+				<view class="content">
+					<slot></slot>
+				</view>
+			</view>
+			
+			<view class="buttons flex align-center justify-between">
+				<view class="button cancel-button" v-if="showConfirmButton" @click="handleCancel">{{ cancelButtonText }}</view>
+				<view class="button confirm-button" v-if="showCancelButton" @click="handleConfirm">{{ confirmButtonText }}</view>
+			</view>
+		</view>		
+	</u-modal>
+</template>
+
+<script>
+	export default {
+		name:"c-modal",
+		props: {
+			value: {
+				type: Boolean,
+				default: false
+			},
+			confirmButtonText: {
+				type: String,
+				default: '确定'
+			},
+			cancelButtonText: {
+				type: String,
+				default: '取消'
+			},
+			showConfirmButton: {
+				type: Boolean,
+				default: true
+			},
+			showCancelButton: {
+				type: Boolean,
+				default: true
+			},
+			title: {
+				type: String,
+				default: '提示'
+			},
+			showTitle: {
+				type: Boolean,
+				default: true
+			}
+		},
+		computed: {
+		    // 处理 v-model 双向绑定
+		    visible: {
+				get() {
+					return this.value;
+				},
+				set(val) {
+					this.$emit("input", val);
+				}
+		    }
+		},
+		data() {
+			return {};
+		},
+		methods: {
+			handleCancel() {
+				this.$emit('cancel')
+			},
+			handleConfirm() {
+				this.$emit('confirm')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.modal-container {
+		font-family: DM Sans;
+		padding: 38rpx 40rpx 28rpx;
+		
+		.wrapper {
+			.title {
+				color: rgba(31, 44, 55, 1);
+				font-size: 28rpx;
+				font-weight: 500;
+				line-height: 44rpx;
+				margin-bottom: 16rpx;
+			}
+			.content {
+				color: rgba(158, 158, 158, 1);
+				font-size: 28rpx;
+				font-weight: 400;
+				line-height: 36rpx;
+				min-height: 120rpx;
+				margin-bottom: 40rpx;
+			}
+		}
+		
+		.buttons {
+			gap: 16rpx;
+			.button {
+				flex: 1;
+				height: 80rpx;
+				border-radius: 80rpx;
+				font-size: 32rpx;
+				font-weight: 400;
+				line-height: 80rpx;
+				text-align: center;
+			}
+			.cancel-button {
+				color: rgba(1, 107, 246, 1);
+				background: rgba(1, 107, 246, 0.1);
+			}
+			.confirm-button {
+				color: #fff;
+				background: linear-gradient(90.00deg, rgba(13, 39, 247, 1),rgba(19, 193, 234, 1));
+			}
+		}
+	}
+</style>

+ 1 - 3
components/mescroll-uni/good-list/good-list.vue

@@ -57,9 +57,7 @@
 						url: "/pages/locality/houseDet?id=" + e.id + "&name=" + e.classifyName
 					})
 				} else {
-					uni.navigateTo({
-						url: '/pages/public/login'
-					});
+					this.$queue.toLogin('navigateTo')
 				}
 
 			}

+ 9 - 4
components/nav-bar/index.vue

@@ -1,7 +1,9 @@
 <template>
 	<view class="nav-bar" :style="{ paddingTop: 12 + BarHeight + 'px' }">
 		<view class="go-back">
-			<u-icon name="arrow-leftward" :color="color" size="32" @click="goBack" v-if="isBack"></u-icon>
+			<slot name="left">
+				<u-icon name="arrow-left" :color="color" size="32" @click="goBack" v-if="isBack"></u-icon>
+			</slot>
 		</view>
 		<view class="nav-title" :style="{ color: color }">
 			{{ title }}
@@ -15,7 +17,10 @@
 export default {
 	props: {
 		title: "",
-		color: '',
+		color: {
+			type: String,
+			default: 'rgba(51, 51, 51, 1)'
+		},
 		backUrl: {
 			type: String,
 			default: ''
@@ -82,8 +87,8 @@ export default {
 	box-sizing: border-box;
 	color: rgba(255, 255, 255, 1);
 	font-family: DM Sans;
-	font-size: 30rpx;
-	font-weight: 700;
+	font-size: 32rpx;
+	font-weight: 400;
 	line-height: 26px;
 	display: flex;
 	// justify-content: center;

+ 6 - 28
components/resume/resume-card.vue

@@ -10,7 +10,7 @@
 				<view class="user-info-box">
 					<view class="flex">
 						<text class="name">{{ userName }}</text>
-						<view class="online-status" :class="{ online: lineValue == ONLINE, offline: lineValue == OFFLINE }">{{ lineText }}</view>
+						<view class="online-status" :class="{ online: lineValue == onlineStr, offline: lineValue == offlineStr }">{{ lineText }}</view>
 					</view>
 					<view class="base-info flex align-center">
 						<text v-for="(item, index) in baseInfoArr" :key="index">{{ item }}</text>
@@ -38,8 +38,7 @@
 </template>
 
 <script>
-	const ONLINE = 'ONLINE'
-	const OFFLINE = 'OFFLINE'
+	import { formatNumberToK } from '@/utils/util.js'
 	
 	export default {
 		data() {
@@ -56,13 +55,15 @@
 				companyTime: '', // 当前公司工作时间
 				ruleClassifyName: '', // 期望岗位
 				skills: [], // 技能标签
+				onlineStr: 'ONLINE',
+				offlineStr: 'OFFLINE'
 			};
 		},
 		computed: {
 			lineText() {
-				if (this.lineValue === ONLINE) {
+				if (this.lineValue === this.onlineStr) {
 					return '在线'
-				} else if (this.lineValue === OFFLINE) {
+				} else if (this.lineValue === this.offlineStr) {
 					return '离线'
 				}
 				return ''
@@ -113,29 +114,6 @@
 				}
 				return ''
 			},
-			formatNumberToK(numStr) {
-				// 1. 异常处理:空值、非字符串、非数字字符串直接返回原内容
-				if (typeof numStr !== 'string' || numStr.trim() === '') {
-				    return numStr || '';
-				}
-				
-				// 2. 将字符串转为数字(自动忽略首尾空格)
-				const num = Number(numStr.trim());
-				
-				// 3. 非有效数字的情况,返回原字符串(如"abc"、"12a3")
-				if (isNaN(num)) {
-				    return numStr;
-				}
-				
-				// 4. 小于1000的数字,直接返回原数字的字符串(无格式变化)
-				if (num < 1000) {
-				    return String(Math.floor(num)); // 确保无小数(比如"999.9"转"999")
-				}
-				
-				// 5. 大于等于1000的数字,除以1000取整后拼接K
-				const kNum = Math.floor(num / 1000);
-				return `${kNum}K`;
-			}
 		}
 	}
 </script>

+ 188 - 4
manifest.json

@@ -2,8 +2,8 @@
     "name" : "亿职赞",
     "appid" : "__UNI__0B0096D",
     "description" : "",
-    "versionName" : "1.2.4",
-    "versionCode" : 124,
+    "versionName" : "1.2.5",
+    "versionCode" : 125,
     "transformPx" : false,
     "sassImplementationName" : "node-sass",
     /* 5+App特有相关 */
@@ -57,7 +57,8 @@
                     "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
                     "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
                     "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
-                    "<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>"
+                    "<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
+                    "<uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"/>"
                 ],
                 "autoSdkPermissions" : true,
                 "targetSdkVersion" : 33,
@@ -75,7 +76,8 @@
                     "NSCameraUsageDescription" : "于拍摄简历头像、作品照片,完成个人资料的编辑与上传",
                     "NSContactsUsageDescription" : "用于导入通讯录中的求职联系人,快速分享招聘信息",
                     "NSCalendarsUsageDescription" : "用于同步面试时间到日历,提醒您及时参加面试",
-                    "NSLocationAlwaysAndWhenInUseUsageDescription" : "用于获取您的当前位置,推荐同城的求职岗位与招聘信息"
+                    "NSLocationAlwaysAndWhenInUseUsageDescription" : "用于获取您的当前位置,推荐同城的求职岗位与招聘信息",
+                    "NSDocumentDirectoryUsageDescription" : "需要访问你的文档目录以选择文件进行上传"
                 }
             },
             /* SDK配置 */
@@ -181,6 +183,188 @@
                     "pid" : "7774",
                     "parameters" : {}
                 }
+            },
+            "JG-JPush" : {
+                "JPUSH_ADVERTISINGID_IOS" : "",
+                "JPUSH_DEFAULTINITJPUSH_IOS" : "",
+                "JPUSH_GOOGLE_API_KEY" : "",
+                "JPUSH_GOOGLE_APP_ID" : "",
+                "JPUSH_GOOGLE_PROJECT_ID" : "",
+                "JPUSH_GOOGLE_PROJECT_NUMBER" : "",
+                "JPUSH_GOOGLE_STORAGE_BUCKET" : "",
+                "JPUSH_HONOR_APPID" : "",
+                "JPUSH_HUAWEI_APPID" : "",
+                "JPUSH_ISPRODUCTION_IOS" : "",
+                "JPUSH_MEIZU_APPID" : "",
+                "JPUSH_MEIZU_APPKEY" : "",
+                "JPUSH_OPPO_APPID" : "",
+                "JPUSH_OPPO_APPKEY" : "",
+                "JPUSH_OPPO_APPSECRET" : "",
+                "JPUSH_VIVO_APPID" : "",
+                "JPUSH_VIVO_APPKEY" : "",
+                "JPUSH_XIAOMI_APPID" : "",
+                "JPUSH_XIAOMI_APPKEY" : "",
+                "__plugin_info__" : {
+                    "name" : "极光推送 JPush 官方 SDK",
+                    "description" : "极光推送JPush官方SDK HBuilder插件版本",
+                    "platforms" : "Android,iOS",
+                    "url" : "https://ext.dcloud.net.cn/plugin?id=4035",
+                    "android_package_name" : "uni.app.UNI0B0096D",
+                    "ios_bundle_id" : "com.yizhizan.app",
+                    "isCloud" : true,
+                    "bought" : 1,
+                    "pid" : "4035",
+                    "parameters" : {
+                        "JPUSH_ADVERTISINGID_IOS" : {
+                            "des" : "[iOS]广告标识符(IDFA)如果不需要使用IDFA,可不填",
+                            "key" : "JPush:ADVERTISINGID",
+                            "value" : ""
+                        },
+                        "JPUSH_DEFAULTINITJPUSH_IOS" : {
+                            "des" : "[iOS]是否默认初始化,是填true,不是填false或者不填",
+                            "key" : "JPush:DEFAULTINITJPUSH",
+                            "value" : ""
+                        },
+                        "JPUSH_GOOGLE_API_KEY" : {
+                            "des" : "厂商google api_key,示例:g-12346578",
+                            "key" : "google_api_key",
+                            "value" : ""
+                        },
+                        "JPUSH_GOOGLE_APP_ID" : {
+                            "des" : "厂商google mobilesdk_app_id,示例:g-12346578",
+                            "key" : "google_app_id",
+                            "value" : ""
+                        },
+                        "JPUSH_GOOGLE_PROJECT_ID" : {
+                            "des" : "厂商google project_id ,示例:g-12346578",
+                            "key" : "project_id",
+                            "value" : ""
+                        },
+                        "JPUSH_GOOGLE_PROJECT_NUMBER" : {
+                            "des" : "厂商google project_number,示例:g-12346578",
+                            "key" : "gcm_defaultSenderId",
+                            "value" : ""
+                        },
+                        "JPUSH_GOOGLE_STORAGE_BUCKET" : {
+                            "des" : "厂商google storage_bucket,示例:g-12346578",
+                            "key" : "google_storage_bucket",
+                            "value" : ""
+                        },
+                        "JPUSH_HONOR_APPID" : {
+                            "des" : "厂商HONOR-appId,示例:12346578",
+                            "key" : "com.hihonor.push.app_id",
+                            "value" : ""
+                        },
+                        "JPUSH_HUAWEI_APPID" : {
+                            "des" : "厂商HUAWEI-appId,示例:appid=12346578",
+                            "key" : "com.huawei.hms.client.appid",
+                            "value" : ""
+                        },
+                        "JPUSH_ISPRODUCTION_IOS" : {
+                            "des" : "[iOS]是否是生产环境,是填true,不是填false或者不填",
+                            "key" : "JPush:ISPRODUCTION",
+                            "value" : ""
+                        },
+                        "JPUSH_MEIZU_APPID" : {
+                            "des" : "厂商MEIZU-appId,示例:MZ-12345678",
+                            "key" : "MEIZU_APPID",
+                            "value" : ""
+                        },
+                        "JPUSH_MEIZU_APPKEY" : {
+                            "des" : "厂商MEIZU-appKey,示例:MZ-12345678",
+                            "key" : "MEIZU_APPKEY",
+                            "value" : ""
+                        },
+                        "JPUSH_OPPO_APPID" : {
+                            "des" : "厂商OPPO-appId,示例:OP-12345678",
+                            "key" : "OPPO_APPID",
+                            "value" : ""
+                        },
+                        "JPUSH_OPPO_APPKEY" : {
+                            "des" : "厂商OPPO-appkey,示例:OP-12345678",
+                            "key" : "OPPO_APPKEY",
+                            "value" : ""
+                        },
+                        "JPUSH_OPPO_APPSECRET" : {
+                            "des" : "厂商OPPO-appSecret,示例:OP-12345678",
+                            "key" : "OPPO_APPSECRET",
+                            "value" : ""
+                        },
+                        "JPUSH_VIVO_APPID" : {
+                            "des" : "厂商VIVO-appId,示例:12345678",
+                            "key" : "com.vivo.push.app_id",
+                            "value" : ""
+                        },
+                        "JPUSH_VIVO_APPKEY" : {
+                            "des" : "厂商VIVO-appkey,示例:12345678",
+                            "key" : "com.vivo.push.api_key",
+                            "value" : ""
+                        },
+                        "JPUSH_XIAOMI_APPID" : {
+                            "des" : "厂商XIAOMI-appId,示例:MI-12345678",
+                            "key" : "XIAOMI_APPID",
+                            "value" : ""
+                        },
+                        "JPUSH_XIAOMI_APPKEY" : {
+                            "des" : "厂商XIAOMI-appKey,示例:MI-12345678",
+                            "key" : "XIAOMI_APPKEY",
+                            "value" : ""
+                        }
+                    }
+                }
+            },
+            "JG-JCore" : {
+                "JPUSH_APPKEY_ANDROID" : "ac83014f08ba7a0ef7cad5ce",
+                "JPUSH_APPKEY_IOS" : "ac83014f08ba7a0ef7cad5ce",
+                "JPUSH_CHANNEL_ANDROID" : "developer-default",
+                "JPUSH_CHANNEL_IOS" : "developer-default",
+                "__plugin_info__" : {
+                    "name" : "极光推送 JCore 官方 SDK",
+                    "description" : "极光推送 JCore 官方 SDK HBuilder 插件版本",
+                    "platforms" : "Android,iOS",
+                    "url" : "https://ext.dcloud.net.cn/plugin?id=4028",
+                    "android_package_name" : "uni.app.UNI0B0096D",
+                    "ios_bundle_id" : "com.yizhizan.app",
+                    "isCloud" : true,
+                    "bought" : 1,
+                    "pid" : "4028",
+                    "parameters" : {
+                        "JPUSH_APPKEY_ANDROID" : {
+                            "des" : "[Android]极光portal配置应用信息时分配的AppKey",
+                            "key" : "JPUSH_APPKEY",
+                            "value" : ""
+                        },
+                        "JPUSH_APPKEY_IOS" : {
+                            "des" : "[iOS]极光portal配置应用信息时分配的AppKey",
+                            "key" : "JCore:APP_KEY",
+                            "value" : ""
+                        },
+                        "JPUSH_CHANNEL_ANDROID" : {
+                            "des" : "[Android]用于统计分发渠道,不需要可填默认值developer-default",
+                            "key" : "JPUSH_CHANNEL",
+                            "value" : ""
+                        },
+                        "JPUSH_CHANNEL_IOS" : {
+                            "des" : "[iOS]用于统计分发渠道,不需要可填默认值developer-default",
+                            "key" : "JCore:CHANNEL",
+                            "value" : ""
+                        }
+                    }
+                }
+            },
+            "JG-JVerification" : {
+                "__plugin_info__" : {
+                    "name" : "极光认证官方 SDK",
+                    "description" : "极光认证官方SDK HBuilder插件版本",
+                    "platforms" : "Android,iOS",
+                    "url" : "https://ext.dcloud.net.cn/plugin?id=1356",
+                    "android_package_name" : "uni.app.UNI0B0096D",
+                    "ios_bundle_id" : "com.yizhizan.app",
+                    "isCloud" : true,
+                    "bought" : 1,
+                    "pid" : "1356",
+                    "parameters" : {}
+                }
             }
         }
     },

+ 1 - 3
my/enterpriseInfo/enterpriseInfo.vue

@@ -410,9 +410,7 @@ export default {
 				confirmText: '去登录',
 				complete: (res) => {
 					if (res.confirm) {
-						uni.navigateTo({
-							url: '/pages/public/login'
-						})
+						this.$queue.toLogin('navigateTo')
 					}
 
 				}

+ 151 - 287
package/jobIntention/addAddress.vue

@@ -1,95 +1,60 @@
 <template>
   <view class="switch-roles">
-    <nav-bar title="添加地址" color="#000"></nav-bar>
+    <nav-bar title="添加地址"></nav-bar>
     <view class="roles-content">
       <view class="content">
-        <!-- 地图选点入口(辅助填充) -->
-        <view class="map-selector" @click="openMapSelector">
-          <u-icon name="map" size="36rpx" color="#007AFF" marginRight="16rpx"></u-icon>
-          <text class="map-text">通过地图选择地址(可选)</text>
-        </view>
-
-        <!-- 省/市/区三级联动选择器 -->
-        <view class="form-item">
-          <view class="item-label">所在省份 <text class="required">*</text></view>
-          <picker :range="provinces" :range-key="'name'" v-model="provinceIndex" @change="onProvinceChange">
-            <view class="picker-view">
-              {{ provinceIndex !== null ? provinces[provinceIndex].name : '请选择省份' }}
-            </view>
-          </picker>
-        </view>
-
-        <view class="form-item">
-          <view class="item-label">所在城市 <text class="required">*</text></view>
-          <picker :range="cities" :range-key="'name'" v-model="cityIndex" @change="onCityChange"
-            :disabled="provinceIndex === null">
-            <view class="picker-view">
-              {{ cityIndex !== null && cities.length > 0 ? cities[cityIndex].name : '请先选择省份' }}
-            </view>
-          </picker>
-        </view>
-
-        <view class="form-item">
-          <view class="item-label">所在区域 <text class="required">*</text></view>
-          <picker :range="districts" :range-key="'name'" v-model="districtIndex" @change="onDistrictChange"
-            :disabled="cityIndex === null || cities.length === 0">
-            <view class="picker-view">
-              {{ districtIndex !== null && districts.length > 0 ? districts[districtIndex].name : '请先选择城市' }}
-            </view>
-          </picker>
-        </view>
-
-        <!-- 详细地址信息 -->
-        <view class="form-item">
-          <view class="item-label">街道/楼宇 <text class="required">*</text></view>
-          <u-input placeholder="请输入街道或楼宇名称,例:科技园路1号" v-model="address" clearable class="custom-input">
-            <template #prefix>
-              <u-icon name="map" size="36rpx" color="#999" marginRight="20rpx"></u-icon>
-            </template>
-          </u-input>
-        </view>
-
-        <view class="form-item">
-          <view class="item-label">楼层/单元室</view>
-          <u-input placeholder="例:3层302室(选填)" v-model="addressDetail" clearable class="custom-input">
-            <template #prefix>
-              <u-icon name="home" size="36rpx" color="#999" marginRight="20rpx"></u-icon>
-            </template>
-          </u-input>
-        </view>
-
-        <!-- 地图选点后显示经纬度(可选) -->
-        <view class="location-info" v-if="latitude && longitude">
-          <text>经纬度:{{ latitude }}, {{ longitude }}</text>
-        </view>
+		<!-- 城市 -->
+		<view class="form-item">
+			<view class="item-label"><text class="required">*</text>所在城市</view>
+			<view class="item-content flex align-center justify-between" @click="toCityPage">
+				<view class="content-value">
+					<text v-if="city">{{ city }}</text>
+					<text class="placeholder" v-else>请选择城市</text>
+				</view>
+				<u-icon name="arrow-down" color="rgba(153, 153, 153, 1)"></u-icon>
+			</view>
+		</view>
+		
+		<!-- 区域 -->
+		<view class="form-item">
+			<view class="item-label"><text class="required">*</text>所在区域</view>
+			<view class="item-content flex align-center justify-between" @click="toCountyPage">
+				<view class="content-value">
+					<text v-if="district">{{ district }}</text>
+					<text class="placeholder" v-else>请选择区域</text>
+				</view>
+				<u-icon name="arrow-down" color="rgba(153, 153, 153, 1)"></u-icon>
+			</view>
+		</view>
+		
+		<!-- 详细地址 -->
+		<view class="form-item">
+			<view class="item-label"><text class="required">*</text>详细地址</view>
+			<view class="item-content">
+				<input v-model="addr" placeholder="请输入公司详细地址" class="input" placeholder-class="input-placeholder" />
+			</view>
+		</view>
       </view>
     </view>
-    <view class="submit-btn" @click="confirmAddress">确定</view>
+	<view class="button-section">
+		<view class="submit-btn" @click="confirmAddress">确定</view>
+	</view>
   </view>
 </template>
 
 <script>
 import navBar from "@/components/nav-bar/index.vue";
-import addr from "@/utils/addr.json";
+// import addr from "@/utils/addr.json";
 
 export default {
   data() {
     return {
-      // 三级联动数据
-      provinces: [],       // 所有省份
-      cities: [],          // 选中省份对应的城市
-      districts: [],       // 选中城市对应的区域
-
-      // 选中索引
-      provinceIndex: null, // 选中的省份索引
-      cityIndex: null,     // 选中的城市索引
-      districtIndex: null, // 选中的区域索引
-
-      // 地址信息
-      address: "",        // 街道/楼宇(必填)
-      addressDetail: '',  // 楼层/单元室(选填)
-      latitude: '',       // 纬度(地图选点获取)
-      longitude: ''       // 经度(地图选点获取)
+      latitude: '', // 纬度(地图选点获取)
+      longitude: '', // 经度(地图选点获取)
+	  province: '', // 省份
+	  city: '', // 城市
+	  district: '', // 区域
+	  addr: '', // 详情地址
     };
   },
   components: {
@@ -100,171 +65,89 @@ export default {
   //   this.provinces = addr;
   // },
   onLoad(options) {
-    this.provinces = addr;
     if (options.address) {
       let address = JSON.parse(options.address);
-      // 回显上一页传递的地址信息
-      // this.address = decodeURIComponent(address.address);
-      // this.addressDetail = decodeURIComponent(address.addressDetail);
       this.latitude = address.latitude;
       this.longitude = address.longitude;
-      // 如果有省市区信息,进行回显
-      this.setAddressValue(
-        decodeURIComponent(address.province),
-        decodeURIComponent(address.city),
-        decodeURIComponent(address.district)
-      );
+	  this.province = decodeURIComponent(address.province) || ''
+	  this.city = decodeURIComponent(address.city)
+	  this.district = decodeURIComponent(address.district)
+	  this.addr = this.setAddress(decodeURIComponent(address.fullText))
     }
+	
+	uni.$on('city', data => {
+		if (data.city) {
+			this.city = data.city
+		}
+	})
+	uni.$on('filterCity', data => {
+		this.city = data.city
+		this.district = data.county
+	})
+  },
+  onUnload() {
+	  uni.$off('city')
+	  uni.$off('filterCity')
   },
   methods: {
-    // 省份选择改变
-    onProvinceChange(e) {
-      const index = e.detail.value;
-      this.provinceIndex = index;
-
-      // 重置城市和区域选择
-      this.cityIndex = null;
-      this.districtIndex = null;
-
-      // 加载对应省份的城市
-      this.cities = this.provinces[index].children || [];
-
-      // 如果有城市数据,自动加载第一个城市的区域
-      if (this.cities.length > 0) {
-        this.districts = this.cities[0].children || [];
-      } else {
-        this.districts = [];
-      }
-    },
-
-    // 城市选择改变
-    onCityChange(e) {
-      const index = e.detail.value;
-      this.cityIndex = index;
-
-      // 重置区域选择
-      this.districtIndex = null;
-
-      // 加载对应城市的区域
-      this.districts = this.cities[index].children || [];
-    },
-
-    // 区域选择改变
-    onDistrictChange(e) {
-      this.districtIndex = e.detail.value;
-    },
-
-    // 根据省市区名称设置选择器值(用于回显)
-    setAddressValue(provinceName, cityName, districtName) {
-      // 查找省份索引
-      const provinceIndex = this.provinces.findIndex(item => item.name === provinceName);
-      if (provinceIndex !== -1) {
-        this.provinceIndex = provinceIndex;
-        this.cities = this.provinces[provinceIndex].children || [];
-
-        // 查找城市索引
-        const cityIndex = this.cities.findIndex(item => item.name === cityName);
-        if (cityIndex !== -1) {
-          this.cityIndex = cityIndex;
-          this.districts = this.cities[cityIndex].children || [];
-
-          // 查找区域索引
-          const districtIndex = this.districts.findIndex(item => item.name === districtName);
-          if (districtIndex !== -1) {
-            this.districtIndex = districtIndex;
-          }
-        }
-      }
-    },
-
-    // 打开地图选择地址
-    openMapSelector() {
-      uni.chooseLocation({
-        success: (res) => {
-          this.address = res.name || res.address;
-          this.latitude = res.latitude;
-          this.longitude = res.longitude;
-
-          uni.showToast({
-            title: '请确认并补充省/市/区信息',
-            icon: 'none',
-            duration: 2000
-          });
-        },
-        fail: (err) => {
-          if (err.errMsg.includes('auth')) {
-            uni.showToast({ title: '请开启位置权限', icon: 'none' });
-          }
-        }
-      });
-    },
+	// 设置详细地址
+	setAddress(address) {
+		let preStr = `${this.province}${this.city}${this.district}`
+		return address.replace(preStr, '')
+	},
+	
+	// 跳转选择城市页面
+	toCityPage() {
+		uni.navigateTo({
+			url: '/package/jobIntention/city'
+		})
+	},
+	
+	// 跳转选择区域页面
+	toCountyPage() {
+		if (!this.city) {
+			this.$queue.showToast('请先选择城市')
+			return
+		}
+		uni.navigateTo({
+			url: `/package/screen/city?city=${this.city}&county=&type=search`
+		})
+	},
 
     // 确认地址并回传
     confirmAddress() {
-      // 获取选中的省市区信息
-      const province = this.provinceIndex !== null ? this.provinces[this.provinceIndex].name : '';
-      const city = this.cityIndex !== null && this.cities.length > 0 ? this.cities[this.cityIndex].name : '';
-      const district = this.districtIndex !== null && this.districts.length > 0 ? this.districts[this.districtIndex].name : '';
-
-      // 验证必填项
-      if (!province) {
-        return uni.showToast({ title: '请选择省份', icon: 'none' });
-      }
-      if (!city) {
-        return uni.showToast({ title: '请选择城市', icon: 'none' });
-      }
-      if (!district) {
-        return uni.showToast({ title: '请选择区域', icon: 'none' });
-      }
-      if (!this.address.trim()) {
-        return uni.showToast({ title: '请输入街道或楼宇名称', icon: 'none' });
-      }
-
-      // 合并完整地址信息
-      const fullAddress = {
-        province,
-        city,
-        district,
-        address: this.address.trim(),
-        addressDetail: this.addressDetail.trim(),
-        latitude: this.latitude || '',
-        longitude: this.longitude || '',
-        fullText: `${province}${city}${district}${this.address.trim()}${this.addressDetail.trim() ? ' ' + this.addressDetail.trim() : ''}`
-      };
-
-      // 回传上一页
-      uni.$emit('addressData', fullAddress);
-      uni.navigateBack();
+		let city = this.city
+		let district = this.district
+		let address = this.addr.trim()
+		if (!city) {
+			return uni.showToast({ title: '请选择城市', icon: 'none' });
+		}
+		if (!district) {
+			return uni.showToast({ title: '请选择区域', icon: 'none' });
+		}
+		if (!address) {
+			return uni.showToast({ title: '请输入详细地址', icon: 'none' });
+		}
+
+		// 合并完整地址信息
+		const fullAddress = {
+			province: this.province,
+			city,
+			district,
+			latitude: this.latitude || '',
+			longitude: this.longitude || '',
+			fullText: `${this.province}${city}${district}${address}`
+		};
+
+		// 回传上一页
+		uni.$emit('addressData', fullAddress);
+		uni.navigateBack();
     }
   }
 };
 </script>
 
 <style lang="scss" scoped>
-/* 原有样式保持不变,添加以下选择器样式 */
-.picker-view {
-  box-sizing: border-box;
-  width: 100%;
-  border: 2rpx solid #9ea1a8;
-  border-radius: 100rpx;
-  padding: 8rpx 36rpx;
-  height: 96rpx;
-  display: flex;
-  align-items: center;
-  font-size: 28rpx;
-  color: #333;
-}
-
-picker {
-  width: 100%;
-}
-
-/* 禁用状态样式 */
-picker:disabled .picker-view {
-  color: #999;
-  background-color: #f5f5f5;
-}
-
 /* 其他原有样式保持不变 */
 .switch-roles {
   background-color: #fff;
@@ -285,55 +168,33 @@ picker:disabled .picker-view {
       padding: 40rpx;
       box-sizing: border-box;
 
-      .map-selector {
-        display: flex;
-        align-items: center;
-        padding: 20rpx 24rpx;
-        background-color: #f5f7fa;
-        border-radius: 16rpx;
-        margin-bottom: 30rpx;
-        color: #007AFF;
-        font-size: 28rpx;
-        cursor: pointer;
-        transition: background-color 0.2s;
-
-        &:active {
-          background-color: #e8ebf0;
-        }
-      }
-
-      .location-info {
-        margin-top: 10rpx;
-        font-size: 24rpx;
-        color: #999;
-        padding: 10rpx 0;
-      }
-
       .required {
-        color: #ff4d4f;
+        color: #FF0027;
         /* 红色必填标记 */
-        font-size: 32rpx;
-        margin-left: 4rpx;
+        font-size: 28rpx;
       }
     }
   }
 
+  .button-section {
+  	padding: 28rpx 40rpx;
+  	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+  }
   .submit-btn {
-    margin: 60rpx 20rpx;
-    height: 96rpx;
-    border-radius: 999px;
-    background: linear-gradient(90deg, #0d27f7, #13c1ea);
-    color: #fff;
-    font-size: 32rpx;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    transition: all 0.2s ease;
-
-    &:active {
-      transform: scale(0.98);
-      opacity: 0.9;
-    }
+  	flex-shrink: 0;
+  	border-radius: 999px;
+  	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+  	background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
+  	color: rgba(255, 255, 255, 1);
+  	font-family: DM Sans;
+  	font-size: 32rpx;
+  	font-weight: 400;
+  	line-height: 48rpx;
+  	display: flex;
+  	justify-content: center;
+  	align-items: center;
+  	padding: 16rpx 32rpx;
+  	box-sizing: border-box;
   }
 }
 
@@ -345,33 +206,36 @@ picker:disabled .picker-view {
   height: 96rpx;
 }
 
-::v-deep .u-input__prefix {
-  margin-right: 24rpx !important;
-}
-
-::v-deep .u-input input {
-  padding-left: 32rpx;
-}
-
 .form-item {
-  margin-bottom: 26rpx;
+  margin-bottom: 20rpx;
   width: 100%;
+  margin-bottom: 20rpx;
 }
 
 .item-label {
-  color: #121a2c;
-  font-size: 32rpx;
-  padding: 10rpx 0;
+  color: #1F2C37;
+  font-size: 28rpx;
+  line-height: 44rpx;
   display: flex;
   align-items: center;
+  margin-bottom: 20rpx;
 }
 
-::v-deep .u-input input:focus {
-  border-color: #007AFF !important;
-  outline: none;
-}
-
-::v-deep .u-input {
-  text-align: left !important;
+.item-content {
+	box-sizing: border-box;
+	border: 1px solid rgba(227, 231, 236, 1);
+	border-radius: 24px;
+	background: Colors/White, rgba(255, 255, 255, 1);
+	padding: 15rpx 32rpx;
+	line-height: 40rpx;
+	.placeholder {
+		color: rgba(153, 153, 153, 1);
+	}
+	.input {
+		font-size: 28rpx;
+	}
+	.input-placeholder {
+		color: rgba(153, 153, 153, 1);
+	}
 }
 </style>

+ 48 - 2
package/jobIntention/basicInfo.vue

@@ -99,6 +99,7 @@
 
 		<!-- 保存按钮 -->
 		<view class="button-section">
+			<view class="link-btn" v-if="!hasUserType" @click="handleIgnore">稍后填写</view>
 			<view class="btn" @click="messagebtn">继续完善</view>
 		</view>
 
@@ -188,12 +189,16 @@
 					}
 				],
 				isAnonymous: false, // 匿名展示
-				scene: 0, // 0.默认 1.H5邀请
+				scene: 0, // 0.默认 1.H5邀请 2.注册进入
 			}
 		},
 		computed: {
 			hidePhoneNumber() {
 				return String(this.phone).replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
+			},
+			// 判断是否有用户类型userType
+			hasUserType() {
+				return this.$queue.getData('userType')
 			}
 		},
 		onLoad(options) {
@@ -279,8 +284,10 @@
 				// 老接口 /app/user/selectUserById
 				// 新接口 /app/user/getUserInfo
 				this.$Request.get("/app/user/getUserInfo").then(res => {
+					// 获取稍后填写的缓存数据
+					const temp_baseInfo = this.$queue.getData('temp_baseInfo') || {}
 					if (res.code == 0) {
-						const data = res.data
+						const data = this.$queue.deepMergeObject(temp_baseInfo, res.data)
 						this.weChatNum = data.weChatNum
 						this.sex = data.sex
 						this.avatar = data.avatar
@@ -334,6 +341,9 @@
 											uni.navigateTo({
 												url: `/pages/public/improvePrompt?scene=${this.scene}`
 											});
+										} else if (!this.$queue.getData('userType')) {
+											// 没有角色类型
+											this.toSelectUserType()
 										} else {
 											// 实际开发中可以在这里添加跳转逻辑
 											uni.$emit('updateResume')
@@ -342,6 +352,11 @@
 											}, 1000)							
 										}
 										// this.setResume()
+										
+										// 保存成功后,删除首次进入“稍后填写”保存的数据
+										if (this.$queue.getData('temp_baseInfo')) {
+											this.$queue.remove('temp_baseInfo')
+										}
 									} else {
 										uni.showToast({
 											title: res.msg,
@@ -354,6 +369,30 @@
 					});
 				}
 			},
+			
+			// 注册时跳转选择角色页面
+			toSelectUserType() {
+				uni.reLaunch({
+					url: '/pages/my/jobApplicant/guidePage'
+				})
+			},
+			
+			// 稍后填写
+			handleIgnore() {
+				this.$queue.setData('temp_baseInfo', {
+					userName: this.userName,
+					avatar: this.avatar,
+					sex: this.sex,
+					weChatNum: this.weChatNum,
+					isAnonymous: this.isAnonymous ? 1 : 0,
+					resumesStatus: this.selectedJobStatus,
+					resumesPhone: this.phone,
+					birthday: this.birthDateText,
+					resumesEmail: this.email,
+					ifExp: this.ifExp
+				})
+				this.toSelectUserType()
+			},
 			// setResume() {
 			// 	let data = {
 			// 		resumesStatus: this.selectedJobStatus,
@@ -553,6 +592,13 @@
 		// height: 210rpx;
 		box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
 		background: rgba(255, 255, 255, 1);
+		.link-btn {
+			font-size: 32rpx;
+			line-height: 1;
+			text-align: center;
+			color: rgba(1, 107, 246, 1);
+			padding-top: 28rpx;
+		}
 		.btn {
 			width: 600rpx;
 			height: 88rpx;

+ 201 - 61
package/jobIntention/companyImg.vue

@@ -5,63 +5,115 @@
 			<!-- 营业执照上传 -->
 			<view class="company-title">上传公司营业执照</view>
 			<view class="company-desc">请上传“{{ companyName }}”的营业执照</view>
-			<view class="upload-img">
-				<image v-if="filePath" :src="filePath" mode="aspectFit"
-					style="height:100%; width:100%; border-radius:8rpx"></image>
-				<view v-else class="text-item" @click="chooseImage">
-					<u-icon name="plus" color="#999" size="40" style="margin-bottom: 8rpx;"></u-icon>
-					上传营业执照
+			
+			<view class="form-item">
+				<view class="form-label"><text class="required">*</text>营业执照</view>
+				<view class="upload-img">
+					<image v-if="filePath" :src="filePath" mode="aspectFit"
+						style="height:100%; width:100%; border-radius:8rpx"></image>
+					<view v-else class="text-item" @click="chooseImage">
+						<u-icon name="plus" color="#999" size="40" style="margin-bottom: 8rpx;"></u-icon>
+						上传营业执照
+					</view>
+					<view v-if="filePath" class="delete-btn" @click="deleteImage">×</view>
+				</view>
+				<view class="warning-title">注意事项</view>
+				<view class="warning-desc">1.请上传所填公司一致的营业执照上传</view>
+				<view class="warning-desc">2.营业执照信息和公章清晰可辨</view>				
+				<view class="warning-desc">3.公司信息可通过图片一键识别</view>				
+			</view>
+			
+			<view class="form-item">
+				<view class="form-label"><text class="required">*</text>企业名称</view>
+				<view class="form-content">
+					<input v-model="companyName" placeholder="请输入企业名称" placeholder-class="placeholder" />
+				</view>
+			</view>
+			
+			<view class="form-item">
+				<view class="form-label"><text class="required">*</text>企业法人</view>
+				<view class="form-content">
+					<input v-model="companyLegalPerson" placeholder="请输入企业法人" placeholder-class="placeholder" />
+				</view>
+			</view>
+			
+			<view class="form-item">
+				<view class="form-label"><text class="required">*</text>企业信用代码</view>
+				<view class="form-content">
+					<input v-model="companyDutyParagraph" placeholder="请输入企业信用代码" placeholder-class="placeholder" />
+				</view>
+			</view>
+			
+			<view class="form-item">
+				<view class="form-label">注册时间</view>
+				<view class="form-content" @click="showDateModal = true">
+					<template v-if="companyCreateTime">{{ companyCreateTime }}</template>
+					<text v-else>请选择注册时间</text>
+				</view>
+			</view>
+			
+			<view class="form-item">
+				<view class="form-label">注册资本</view>
+				<view class="form-content">
+					<input v-model="companyRegisteredFund" placeholder="请输入注册资本" placeholder-class="placeholder" />
 				</view>
-				<view v-if="filePath" class="delete-btn" @click="deleteImage">×</view>
 			</view>
-			<view class="warning-title">注意事项</view>
-			<view class="warning-desc">1.请上传所填公司一致的营业执照</view>
-			<view class="warning-desc">2.营业执照信息和公章清晰可辨</view>
 
 			<!-- 在职证明上传 -->
-			<view class="upload-img">
-				<view v-if="pdfFilePath" class="text-item">
-					<template v-if="isImageFile(pdfFilePath)">
-					    <image 
-					      :src="pdfFilePath" 
-					      mode="aspectFill" 
-					      class="preview-img"
-					    ></image>
-					    <view class="delete-btn" @click="deletePDF">×</view>
-					  </template>
-					
-					<template v-else>
-					    <u-icon name="file-text" color="#016bf6" size="40"></u-icon>
-					    <text style="margin-top: 8rpx;">已选择 PDF</text>
-					    <view class="delete-btn" @click="deletePDF">×</view>
-					</template>
+			<view class="form-item">
+				<view class="form-label"><text class="required">*</text>在职证明</view>
+				<view class="upload-img">
+					<view v-if="pdfFilePath" class="text-item">
+						<template v-if="isImageFile(pdfFilePath)">
+							<image 
+							  :src="pdfFilePath" 
+							  mode="aspectFill" 
+							  class="preview-img"
+							></image>
+							<view class="delete-btn" @click="deletePDF">×</view>
+						  </template>
+						
+						<template v-else>
+							<u-icon name="file-text" color="#016bf6" size="40"></u-icon>
+							<text style="margin-top: 8rpx;">已选择 PDF</text>
+							<view class="delete-btn" @click="deletePDF">×</view>
+						</template>
+					</view>
+					<fileSelector v-else :isShowStyle="false" title="上传在职证明" allowType=".pdf,image/*" @fileSelected="choosePDF" />
 				</view>
-				<fileSelector v-else :isShowStyle="false" title="上传在职证明" allowType=".pdf,image/*" @fileSelected="choosePDF" />
-			</view>
-			<!-- 模板下载部分 -->
-			<view class="warning-title" style="margin-top: 20rpx;">
-				<text style="color: #016bf6; margin-left: 8rpx;" @click="downloadTemplate">点击下载模板</text>
+				<!-- 模板下载部分 -->
+				<view class="warning-title" style="margin-top: 20rpx;">
+					<text style="color: #016bf6; margin-left: 8rpx;" @click="downloadTemplate">下载亿职赞模板</text>
+				</view>
+				<view class="warning-title">注意事项</view>
+				<view class="warning-desc">1.请上传加盖公章的在职证明</view>
 			</view>
-			<view class="warning-title">注意事项</view>
-			<view class="warning-desc">1.请上传加盖公章的在职证明(仅限 PDF 文件 或者 图片)</view>
 
 			<!-- 公司证明上传 -->
-			<view class="upload-group">
-				<view v-for="(img, index) in companyProofList" :key="index" class="upload-img">
-					<image :src="img" mode="aspectFill" style="width:100%;height:100%;border-radius:8rpx;"></image>
-					<view class="delete-btn" @click="deleteCompanyProof(index)">×</view>
-				</view>
-				<view v-if="companyProofList.length < 4" class="upload-img text-item" @click="chooseCompanyProof">
-					<u-icon name="plus" color="#999" size="40" style="margin-bottom: 8rpx;"></u-icon>
-					上传公司证明
+			<view class="form-item">
+				<view class="form-label"><text class="required">*</text>公司证明</view>
+				<view class="upload-group">
+					<view v-for="(img, index) in companyProofList" :key="index" class="upload-img">
+						<image :src="img" mode="aspectFill" style="width:100%;height:100%;border-radius:8rpx;"></image>
+						<view class="delete-btn" @click="deleteCompanyProof(index)">×</view>
+					</view>
+					<view v-if="companyProofList.length < 4" class="upload-img text-item" @click="chooseCompanyProof">
+						<u-icon name="plus" color="#999" size="40" style="margin-bottom: 8rpx;"></u-icon>
+						上传公司证明
+					</view>
 				</view>
+				<view class="warning-title">注意事项</view>
+				<view class="warning-desc">1.请上传清晰且完整的公司前台LOGO照1-2张</view>
+				<view class="warning-desc">2.请上传公司办公区照片1-2张</view>
 			</view>
-			<view class="warning-title">注意事项</view>
-			<view class="warning-desc">1.请上传公司前台LOGO照1-2张</view>
-			<view class="warning-desc">2.请上传公司办公区照片1-2张</view>
 		</view>
+		
+		<u-calendar v-model="showDateModal" mode="date" @change="handleChangeDate"></u-calendar>
 
-		<view class="bottom-btn" @click="goUnderReview">确认并提交</view>
+		<view class="button-section">
+			<view class="link-btn" @click="handleAfterVerify">稍后认证</view>
+			<view class="submit-btn" @click="goUnderReview">确认并提交</view>
+		</view>
 	</view>
 </template>
 
@@ -76,6 +128,10 @@
 			return {
 				companyName: "",
 				companyId: "",
+				companyLegalPerson: '', // 企业法人
+				companyDutyParagraph: '', // 信用代码
+				companyCreateTime: '', // 注册时间
+				companyRegisteredFund: '', // 注册资本
 				filePath: "", // 营业执照本地路径
 				licenseUrl: "", // 营业执照上传后URL
 
@@ -85,7 +141,11 @@
 				companyProofList: [], // 公司证明本地预览图
 				companyProofUrls: [], // 上传后URL列表
 
-				action: configdata.APIHOST1 + '/alioss/upload'
+				action: configdata.APIHOST1 + '/alioss/upload',
+				
+				actionBusinessLicense: configdata.APIHOST1 + '/alioss/uploadBusinessLicense',
+				
+				showDateModal: false
 			};
 		},
 		components: { navBar,fileSelector},
@@ -129,16 +189,30 @@
 				});
 			},
 			// 通用上传
-			async uploadSingleFile(path) {
+			async uploadSingleFile(path, url) {
 				return new Promise((resolve, reject) => {
 					uni.uploadFile({
-						url: this.action,
+						url: url ? url : this.action,
 						filePath: path,
 						name: 'file',
+						formData: {
+							isBusinessLicense: url ? true : false
+						},
 						success: (res) => {
 							const data = JSON.parse(res.data);
-							if (data.code === 0 && data.data) resolve(data.data);
-							else reject(data.msg || '上传失败');
+							if (data.code === 0 && data.data) {
+								if (data.data.src) {
+									this.companyName = data.data.companyName
+									this.companyCreateTime = data.data.companyCreateTime?.slice(0, 10)
+									this.companyDutyParagraph = data.data.companyDutyParagraph
+									this.companyLegalPerson = data.data.companyLegalPerson
+									this.companyLegalPerson = data.data.companyLegalPerson
+								}
+								let imgUrl = url ? data.data.src : data.data
+								resolve(imgUrl);
+							} else {
+								reject(data.msg || '上传失败');
+							}
 						},
 						fail: (err) => reject(err)
 					});
@@ -165,7 +239,7 @@
 						// 自动上传
 						uni.showLoading({ title: '上传营业执照中...' });
 						try {
-							this.licenseUrl = await this.uploadSingleFile(tempFilePath);
+							this.licenseUrl = await this.uploadSingleFile(tempFilePath, this.actionBusinessLicense);
 							uni.showToast({ title: '上传成功', icon: 'success' });
 						} catch (e) {
 							uni.showToast({ title: '上传失败', icon: 'none' });
@@ -266,11 +340,19 @@
 			// ======= 提交 =======
 			async goUnderReview() {
 				if (!this.licenseUrl) return uni.showToast({ title: '请上传营业执照', icon: 'none' });
+				if (!this.companyName) return uni.showToast({ title: '请输入企业名称', icon: 'none' });
+				if (!this.companyLegalPerson) return uni.showToast({ title: '请输入企业法人', icon: 'none' });
+				if (!this.companyDutyParagraph) return uni.showToast({ title: '请输入企业信用代码', icon: 'none' });
 				if (!this.employmentVerification) return uni.showToast({ title: '请上传在职证明PDF或者图片', icon: 'none' });
 				if (this.companyProofUrls.length === 0) return uni.showToast({ title: '请上传公司证明图片', icon: 'none' });
 
 				const data = {
 					companyId: this.companyId,
+					companyName: this.companyName,
+					companyLegalPerson: this.companyLegalPerson,
+					companyDutyParagraph: this.companyDutyParagraph,
+					companyCreateTime: this.companyCreateTime,
+					companyRegisteredFund: this.companyRegisteredFund,
 					companyCertification: this.licenseUrl,
 					employmentVerification: this.employmentVerification,
 					companyVerification: this.companyProofUrls.join(',')
@@ -281,7 +363,8 @@
 					uni.hideLoading();
 					if (res.code === 0) {
 						uni.showToast({ title: '提交成功', icon: 'success' });
-						uni.navigateTo({ url: "/package/jobIntention/completeMsg" });
+						// uni.navigateTo({ url: "/package/jobIntention/completeMsg" });
+						uni.navigateTo({ url: "/package/jobIntention/underReview" });
 					} else {
 						uni.showToast({ title: res.msg || '提交失败', icon: 'none' });
 					}
@@ -290,6 +373,16 @@
 					console.error('提交失败', e);
 					uni.showToast({ title: '网络异常,请重试', icon: 'none' });
 				}
+			},
+			
+			// 选择注册时间
+			handleChangeDate(e) {
+				this.companyCreateTime = e.result
+			},
+			
+			// 稍后认证
+			handleAfterVerify() {
+				uni.navigateTo({ url: "/package/jobIntention/completeMsg" });
 			}
 		}
 	};
@@ -316,8 +409,9 @@
 
 			.company-title {
 				color: #333;
-				font-size: 40rpx;
-				font-weight: 600;
+				font-size: 48rpx;
+				font-weight: 700;
+				line-height: 60rpx;
 				margin-bottom: 20rpx;
 			}
 
@@ -325,6 +419,37 @@
 				color: #666;
 				font-size: 24rpx;
 				line-height: 32rpx;
+				margin-bottom: 20rpx;
+			}
+			
+			.form-item {
+				margin-bottom: 20rpx;
+				.form-label {
+					font-size: 28rpx;
+					font-weight: 500;
+					line-height: 44rpx;
+					color: #1F2C37;
+					margin-bottom: 20rpx;
+					.required {
+						color: #FF0000;
+					}
+				}
+				.form-content {
+					box-sizing: border-box;
+					border: 1px solid rgba(227, 231, 236, 1);
+					border-radius: 48rpx;
+					background: rgba(255, 255, 255, 1);
+					padding: 15rpx 32rpx;
+					text,
+					.placeholder {
+						color: rgba(153, 153, 153, 1);
+					}
+					input {
+						font-size: 28rpx;
+						font-weight: 400;
+						line-height: 40rpx;
+					}
+				}
 			}
 
 			.upload-img {
@@ -333,7 +458,6 @@
 				display: flex;
 				justify-content: center;
 				align-items: center;
-				margin-top: 20rpx;
 				position: relative;
 			}
 
@@ -386,17 +510,33 @@
 			}
 		}
 
-		.bottom-btn {
+		.button-section {
+			padding: 28rpx 40rpx;
+			box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+		}
+		.submit-btn {
+			flex-shrink: 0;
 			border-radius: 999px;
-			background: #016bf6;
+			box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+			background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
+			color: rgba(255, 255, 255, 1);
+			font-family: DM Sans;
+			font-size: 32rpx;
+			font-weight: 400;
+			line-height: 48rpx;
 			display: flex;
 			justify-content: center;
 			align-items: center;
-			color: #fff;
-			font-size: 32rpx;
 			padding: 16rpx 32rpx;
-			margin: 20rpx;
-			margin-bottom: 110rpx;
+			box-sizing: border-box;
+		}
+		.link-btn {
+			text-align: center;
+			color: rgba(1, 107, 246, 1);
+			font-size: 16px;
+			font-weight: 400;
+			line-height: 16px;
+			margin-bottom: 32rpx;
 		}
 	}
 </style>

+ 173 - 207
package/jobIntention/completeMsg.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="company">
-		<nav-bar title="完善信息" color="#000"></nav-bar>
+		<nav-bar title="完善招聘名片"></nav-bar>
 		<view class="company-content">
 			<view class="company-title">完善你的招聘名片</view>
 			<view class="company-desc">求职者更愿意主动联系名片信息完整的HR</view>
@@ -13,32 +13,33 @@
 						<image v-else src="@/static/images/jobApplicant/touxiang.svg" mode="scaleToFill"
 							class="user-img" />
 					</view>
-					<view class="upload-view">上传头像</view>
+					<view class="upload-view">修改头像</view>
 				</view>
 			</view>
 			<!-- 姓名 -->
 			<view class="form-item">
 				<view class="form-label">填写职务</view>
-				<u-input placeholder="真实的职务,更能赢得牛人的信任" v-model="name" :border="false" class="form-input"></u-input>
+				<view class="input-wrapper">
+					<input placeholder="真实的职务,更能赢得牛人的信任" v-model="name" class="form-input" placeholder-class="placeholder" />
+				</view>
 			</view>
 			<!-- email -->
-			<view class="form-item">
+			<!-- <view class="form-item">
 				<view class="form-label">填写email</view>
 				<u-input placeholder="填写email,方便牛人联系" v-model="email" :border="false"
 					:class="{ 'form-input': true, 'error': emailError }" @blur="validateEmailOnBlur"></u-input>
-				<!-- 添加错误提示 -->
 				<view v-if="emailError" class="error-message">
 					{{ emailError }}
 				</view>
-			</view>
-			<view class="form-item">
+			</view> -->
+			<view class="form-item list">
 				<view class="form-label">求职者视角中的效果</view>
 				<view class="form-border" v-for="(item, inx) in userpost" :key="inx">
 					<view class="form-border-top">
 						<view class="form-border-title">{{ item.ruleClassifyName }}</view>
-						<view class="form-border-money">{{ item.salaryRange }}</view>
+						<view class="form-border-money">{{ item.salaryRange }} <text v-if="item.salaryTimes">· {{ item.salaryTimes }}</text></view>
 					</view>
-					<view class="form-border-desc" v-if="item.company">{{ item.company.companyName || item.company.companyAllName}} {{ item.company.companyPeople }}</view>
+					<view class="form-border-desc" v-if="item.company">{{ item.company.companyName || item.company.companyAllName}} {{ item.company.companyPeople }}</view>
 					<view class="form-border-img-name">
 						<view class="people-img">
 							<image v-if="avatar" :src="avatar" class="avatar-image"
@@ -50,18 +51,31 @@
 					</view>
 				</view>
 			</view>
-			<view class="bottom-btn" @click="submithr">开始招聘</view>
+			<view class="fixed-section">
+				<view class="link-btn" v-if="showAfterBtn" @click="handleAfterFinished">稍后完善</view>
+				<view class="submit-btn" @click="submithr">开始招聘</view>
+			</view>
 		</view>
+		<!-- 弹窗说明 -->
+		<c-modal
+			:value="showModal"
+			title="企业介绍"
+			confirm-button-text="完善信息"
+			cancel-button-text="稍后完善"
+			@cancel="handleAfterFinished"
+			@confirm="handleConfirm"
+		>为了让更多的求职者关注您的招聘岗位,建议您直接完善公司介绍</c-modal>
 		<!-- 权限说明弹窗 -->
-				<u-popup mode="top" ref="permission">
-					<view class="popup-content">
-						<view class="popup-text-permission">选择/拍摄照片需要相机/相册权限,用于上传用户头像</view>
-					</view>
-				</u-popup>
+		<u-popup mode="top" ref="permission">
+			<view class="popup-content">
+				<view class="popup-text-permission">选择/拍摄照片需要相机/相册权限,用于上传用户头像</view>
+			</view>
+		</u-popup>
 	</view>
 </template>
 <script>
 import navBar from "@/components/nav-bar/index.vue";
+import cModal from '@/components/c-modal.vue';
 export default {
 	data() {
 		return {
@@ -70,34 +84,25 @@ export default {
 			isok: false,
 			email: '',
 			emailError: "", // 添加错误提示
-			userpost: []
+			userpost: [],
+			showAfterBtn: false, // 显示页面的稍后完善按钮,用户没有职务时显示
+			showModal: false,
+			companyStatus: 1, // 公司状态 1审核中 2审核通过 3审核拒绝
+			isAdmin: false, // 是否是管理员
+			hasReviewPosition: false, // 判断是否有审核中的岗位
+			companyId: ''
 		};
 	},
 	components: {
 		navBar,
+		cModal,
 	},
 	onShow() {
+		this.getPositionInfo()
 		this.getmypost()
-		// this.HrFirst()
+		this.getcompanystatus()
 	},
 	methods: {
-		goUploadImg() {
-			uni.navigateTo({
-				url: "/package/jobIntention/companyImg"
-			});
-		},
-		goCompanyReview() {
-			if (this.isok) {
-				uni.navigateTo({
-					url: "/package/jobIntention/companyReview",
-				});
-			}
-			return uni.showToast({
-				title: "请补全资料",
-				icon: "none"
-			});
-
-		},
 		// 选择头像
 		async chooseAvatar() {
 			var that = this
@@ -131,34 +136,7 @@ export default {
 				},
 			});
 		},
-		//提交hr 信息
-		// 邮箱验证方法
-		validateEmail(email) {
-			const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
-			return emailRegex.test(email);
-		},
-		// 邮箱失焦验证
-		validateEmailOnBlur() {
-			if (!this.email) {
-				this.emailError = "邮箱不能为空";
-				return;
-			}
-
-			if (!this.validateEmail(this.email)) {
-				this.emailError = "邮箱格式不正确";
-			} else {
-				this.emailError = "";
-			}
-		},
-
-		// 实时验证(可选)
-		onEmailInput(e) {
-			this.email = e;
-			// 如果之前有错误,输入时清除错误提示
-			if (this.emailError && this.email) {
-				this.emailError = "";
-			}
-		},
+		
 		// 验证所有表单字段
 		validateForm() {
 			let isValid = true;
@@ -181,25 +159,25 @@ export default {
 				return false;
 			}
 
-			// 验证邮箱
-			if (!this.email) {
-				this.emailError = "邮箱不能为空";
-				uni.showToast({
-					title: "请输入邮箱",
-					icon: "none"
-				});
-				return false;
-			}
-
-			// 验证邮箱格式
-			if (!this.validateEmail(this.email)) {
-				this.emailError = "邮箱格式不正确";
-				uni.showToast({
-					title: "邮箱格式不正确",
-					icon: "none"
-				});
-				return false;
-			}
+			// // 验证邮箱
+			// if (!this.email) {
+			// 	this.emailError = "邮箱不能为空";
+			// 	uni.showToast({
+			// 		title: "请输入邮箱",
+			// 		icon: "none"
+			// 	});
+			// 	return false;
+			// }
+
+			// // 验证邮箱格式
+			// if (!this.validateEmail(this.email)) {
+			// 	this.emailError = "邮箱格式不正确";
+			// 	uni.showToast({
+			// 		title: "邮箱格式不正确",
+			// 		icon: "none"
+			// 	});
+			// 	return false;
+			// }
 
 			this.emailError = ""; // 清除错误提示
 			return true;
@@ -228,14 +206,12 @@ export default {
 					uni.hideLoading();
 					if (res.code === 0) {
 						this.isok = true;
-						uni.showToast({
-							title: "提交成功,请开始招聘吧。",
-							icon: "success"
-						});
-						// uni.switchTab({
-						// 	url: '/pages/index/index'
-						// });
-						uni.navigateTo({ url: '/package/jobIntention/underReview' })
+						// 判断是否是管理员
+						if (this.isAdmin && this.companyStatus != 2) {
+							this.showModal = true
+						} else {
+							this.handleAfterFinished()							
+						}
 					} else {
 						uni.showToast({
 							title: res.msg || "提交失败",
@@ -253,64 +229,6 @@ export default {
 				});
 		},
 
-		// submithr() {
-		// 	// 验证必填项
-		// 	if (!this.avatar) {
-		// 		return uni.showToast({
-		// 			title: "请上传头像",
-		// 			icon: "none"
-		// 		});
-		// 	}
-		// 	if (!this.name) {
-		// 		return uni.showToast({
-		// 			title: "请输入职务",
-		// 			icon: "none"
-		// 		});
-		// 	}
-		// 	// 构造提交数据
-		// 	const data = {
-		// 		hrImg: this.avatar, //头像
-		// 		hrPosition: this.name, //职位
-		// 	};
-
-		// 	// 调用接口提交
-		// 	uni.showLoading({
-		// 		title: "提交中..."
-		// 	});
-		// 	this.$Request.postJson("/app/HrFirst/addHr", data)
-		// 		.then((res) => {
-		// 			uni.hideLoading();
-		// 			if (res.code === 0) {
-		// 				this.isok = true
-		// 				uni.showToast({
-		// 					title: "提交成功,请开始招聘吧。",
-		// 					icon: "success"
-		// 				});
-		// 				// uni.navigateTo({
-		// 				// 	url: "/package/jobIntention/companyReview",
-		// 				// });
-		// 				uni.switchTab({
-		// 					url: '/pages/index/index'
-		// 				})
-
-		// 			} else {
-		// 				uni.showToast({
-		// 					title: res.msg || "提交失败",
-		// 					icon: "none"
-		// 				});
-		// 			}
-		// 		})
-		// 		.catch((err) => {
-		// 			uni.hideLoading();
-		// 			console.error("提交失败:", err);
-		// 			uni.showToast({
-		// 				title: "网络异常",
-		// 				icon: "none"
-		// 			});
-		// 		});
-
-		// },
-
 		//获取我发布的岗位列表
 		getmypost() {
 			this.$Request.get("/app/postPush/getMyPostPush", { status: "" })
@@ -323,9 +241,15 @@ export default {
 						return;
 					}
 					this.userpost = res.data.records || [];
-					if (this.userpost[0].hr) {
-						this.name = this.userpost[0].hr.hrPosition || ""
-						this.avatar = this.userpost[0].hr.hrImg || ""
+					if (this.userpost.length && this.userpost[0].hr) {
+						// this.name = this.userpost[0].hr.hrPosition || ""
+						// this.avatar = this.userpost[0].hr.hrImg || ""
+						this.showAfterBtn = this.userpost[0]?.hr?.hrPosition ? false : true
+					}
+					
+					// 判断是否有审核中职位
+					if (this.userpost.length == 1) {
+						this.hasReviewPosition = this.userpost[0].status == 1
 					}
 
 					console.log("查询我的岗位列表状态", res)
@@ -338,33 +262,58 @@ export default {
 					});
 				});
 		},
-		//Hr 名片
-		// HrFirst() {
-		// 	this.$Request.get("/app/HrFirst", "")
-		// 		.then((res) => {
-		// 			if (res.code != 0) {
-		// 				uni.showToast({
-		// 					title: res.msg || "查询失败",
-		// 					icon: "none"
-		// 				});
-		// 				return;
-		// 			}
-
-		// 			console.log("Hr 名片", res)
-		// 		})
-		// 		.catch((err) => {
-		// 			console.error("查询失败:", err);
-		// 			uni.showToast({
-		// 				title: "查询失败",
-		// 				icon: "none"
-		// 			});
-		// 		});
-		// },
-
+		
+		// 查询用户企业状态
+		getcompanystatus() {
+			this.$Request.get('/app/company/selectCompanyByUserId')
+				.then((res) => {
+					if (res.code == 0) {
+						// 判断是否是管理员
+						if (res.data?.companyUser?.isAdmin == 1) {
+							this.isAdmin = true
+						}
+						// 公司状态
+						if (res.data.status) {
+							this.companyStatus = res.data.status
+						}
+						this.companyId = res.data.companyId
+					}
+				})
+		},
+		
+		// 获取职位信息
+		getPositionInfo() {
+			this.$Request.getT('/app/HrFirst/getUserHr')
+				.then(res => {
+					if (res.code == 0) {
+						this.avatar = res.data.HrEntity.hrImg
+						this.name = res.data.HrEntity.hrPosition
+						this.email = res.data.HrEntity.hrEmail
+					}
+				})
+		},
+		
+		handleConfirm() {
+			this.showModal = false
+			uni.navigateTo({
+				url: `/my/renzheng/editCompany?companyId=${this.companyId}`
+			})
+		},
+		
+		// 稍后完善,如果没有职位等待审核中,则跳转到陈列页
+		handleAfterFinished() {
+			if (this.hasReviewPosition) {
+				// 第一次发布岗位,并且正在审核中
+				uni.reLaunch({ url: '/pages/index/index' })
+			} else {
+				uni.navigateTo({ url: '/pages/jobManagement/jobManagement?type=canClose' })
+			}
+		}
 	},
 };
 </script>
 <style scoped lang="scss">
+@import '@/common.scss';
 .company {
 	position: absolute;
 	left: 0;
@@ -377,15 +326,24 @@ export default {
 
 	.company-content {
 		flex: 1;
-		padding: 40rpx;
+		padding: 40rpx 40rpx 180rpx;
 		box-sizing: border-box;
 		overflow: hidden;
 		overflow-y: auto;
 
 		.company-title {
 			color: #333;
-			font-size: 40rpx;
-			font-weight: 600;
+			font-size: 48rpx;
+			font-weight: 700;
+			line-height: 60rpx;
+			margin-bottom: 20rpx;
+		}
+		
+		.company-desc {
+			color: rgba(102, 102, 102, 1);
+			font-size: 24rpx;
+			font-weight: 400;
+			line-height: 32rpx;
 			margin-bottom: 20rpx;
 		}
 
@@ -395,13 +353,13 @@ export default {
 			font-size: 28rpx;
 			font-weight: 500;
 			line-height: 44rpx;
-			padding-top: 30rpx;
-			padding-bottom: 27rpx;
+			margin-bottom: 20rpx;
 			box-sizing: border-box;
 		}
 
 		.avatar-section {
 			display: flex;
+			margin-bottom: 36rpx;
 		}
 
 		.avatar-upload {
@@ -451,6 +409,7 @@ export default {
 				background: rgba(253, 253, 253, 1);
 				padding: 36rpx;
 				box-sizing: border-box;
+				margin-bottom: 20rpx;
 
 				.form-border-top {
 					display: flex;
@@ -509,6 +468,10 @@ export default {
 		.form-item:last-child {
 			border-bottom: none;
 		}
+		
+		.list {
+			padding-bottom: 100rpx;
+		}
 
 		.form-label {
 			color: #1f2c37;
@@ -516,34 +479,37 @@ export default {
 			font-size: 28rpx;
 			font-weight: 500;
 			line-height: 44rpx;
-			padding-top: 30rpx;
-			padding-bottom: 27rpx;
+			margin-bottom: 20rpx;
 			box-sizing: border-box;
 		}
 
-		.form-input {
+		.input-wrapper {
 			flex: 1;
-			box-sizing: border-box;
-			border: 2rpx solid #9ea1a8;
-			border-radius: 100rpx;
-			background: rgba(255, 255, 255, 1);
-			padding: 0rpx 24rpx !important;
+			border: 1px solid rgba(227, 231, 236, 1);
+			padding: 11rpx 32rpx;
+			border-radius: 30rpx;
+		}
+		.form-input {
+			font-size: 24rpx;
+			line-height: 48rpx;
+		}
+		.placeholder {
+			color: rgba(153, 153, 153, 1);
 		}
 	}
 
-	.bottom-btn {
-		border-radius: 999px;
-		background: rgba(255, 102, 0, 1);
-		display: flex;
-		justify-content: center;
-		align-items: center;
-		color: rgba(255, 255, 255, 1);
-		font-family: DM Sans;
-		font-size: 32rpx;
+	.submit-btn {
+		text-align: center;
+		line-height: 90rpx;
+		color: #fff;
+	}
+	.link-btn {
+		text-align: center;
+		color: rgba(1, 107, 246, 1);
+		font-size: 16px;
 		font-weight: 400;
-		line-height: 48rpx;
-		padding: 16rpx 32rpx;
-		box-sizing: border-box;
+		line-height: 16px;
+		margin-bottom: 32rpx;
 	}
 }
 
@@ -551,15 +517,15 @@ export default {
 	text-align: left !important;
 }
 
-.error-message {
-	color: #ff4d4f;
-	font-size: 24rpx;
-	margin-top: 8rpx;
-	padding-left: 24rpx;
-}
+// .error-message {
+// 	color: #ff4d4f;
+// 	font-size: 24rpx;
+// 	margin-top: 8rpx;
+// 	padding-left: 24rpx;
+// }
 
 /* 如果有错误,可以修改输入框边框颜色 */
-.form-input.error {
-	border-color: #ff4d4f !important;
-}
+// .form-input.error {
+// 	border-color: #ff4d4f !important;
+// }
 </style>

+ 307 - 150
package/jobIntention/fund.vue

@@ -1,29 +1,60 @@
 <template>
   <view class="switch-roles">
-    <nav-bar title="福利待遇" color="#000"></nav-bar>
+    <nav-bar title="福利待遇"></nav-bar>
     <view class="roles-content">
       <view class="content-card">
         <view class="card-title">福利待遇</view>
+		
+		<view class="other-input-area">
+			<textarea v-model="otherWelfare" placeholder="请详细描述公司的福利待遇" maxlength="5000" class="other-textarea" />
+			<view class="input-count flex align-center justify-end">
+				<text class="hight-light">0</text>
+				<text>/5000</text>
+			</view>
+		</view>
+		
+		<!-- 福利标签 -->
+		<view class="tag-list">
+			<view
+				class="tag flex"
+				:class="{ 'tag-active': selectedTags.includes(item) }"
+				v-for="(item, index) in fundList"
+				:key="index + item"
+				@click="toggleTag(item)"
+			>
+				{{ item }}
+				<view style="margin-left: 2px" v-if="index > 11" @click.stop="removeCustomTag(item, index)">
+					<u-icon name="close" size="16"></u-icon>
+				</view>
+			</view>
+		</view>
+		<view class="add-tag-wrapper flex">
+			<view class="tag tag-active flex" @click="showAddModal = true">
+				<text>自定义标签</text>
+				<image src="/static/images/svg/add-border.svg" class="add-icon"></image>
+			</view>
+		</view>
+		<view class="tag-count-wrapper">已选 {{ selectedTags.length }} 个</view>
 
         <!-- 已选标签展示区(核心新增) -->
-        <view class="selected-tags" v-if="selectedTags.length > 0">
+        <!-- <view class="selected-tags" v-if="selectedTags.length > 0">
           <view class="selected-tag-item" v-for="(tag, index) in selectedTags" :key="index">
             <text>{{ tag }}</text>
             <u-icon name="close" size="24rpx" color="#999" @click="removeTag(index)"></u-icon>
           </view>
-        </view>
+        </view> -->
 
         <!-- 自定义输入 + 添加按钮区域 -->
-        <view class="input-area">
+        <!-- <view class="input-area">
           <textarea v-model="customWelfare" placeholder="请输入自定义福利" maxlength="500" class="custom-textarea"></textarea>
           <button class="add-btn" @click="addCustomTag">添加</button>
           <view class="word-count">
             <text>{{ customWelfare.length }}</text> /500
           </view>
-        </view>
+        </view> -->
 
         <!-- 常用标签区域 -->
-        <view class="tag-section">
+        <!-- <view class="tag-section">
           <view class="section-title">常用福利标签</view>
           <view class="tag-list">
             <view class="tag-item" :class="{ 'tag-selected': selectedTags.includes(item) }" v-for="item in fundList"
@@ -32,10 +63,20 @@
             </view>
           </view>
           <view class="selected-count">已选 {{ selectedTags.length }} 个福利</view>
-        </view>
+        </view> -->
       </view>
     </view>
-    <view class="submit-btn" @click="submitWelfare">确定</view>
+	
+	<!-- 添加自定义弹窗 -->
+	<u-modal v-model="showAddModal" show-cancel-button title="添加自定义" @confirm="confirmAddCustomeWelfare">
+		<view class="custom-input">
+			<input v-model="addCustomWelfare" placeholder="请输入自定义福利" maxlength="50" class="input" />
+		</view>
+	</u-modal>
+	
+	<view class="button-section">
+		<view class="submit-btn" @click="submitWelfare">确定</view>
+	</view>
   </view>
 </template>
 
@@ -44,20 +85,26 @@ import navBar from "@/components/nav-bar/index.vue";
 export default {
   data() {
     return {
-      fundList: [
-        "五险一金", "定期体检", "年终奖", "带薪年假",
-        "员工旅游", "节日福利", "绩效奖金", "零食下午茶",
-        "住房补贴", "交通补贴", "餐饮补贴", "通讯补贴"
-      ],
-      customWelfare: "",
-      selectedTags: [], // 存储所有选中的标签(系统+自定义)
+		fundList: [
+			"五险一金", "定期体检", "年终奖", "带薪年假",
+			"员工旅游", "节日福利", "绩效奖金", "零食下午茶",
+			"住房补贴", "交通补贴", "餐饮补贴", "通讯补贴"
+		],
+		customWelfare: "",
+		selectedTags: [], // 存储所有选中的标签(系统+自定义)
+		otherWelfare: '', // 福利描述
+		showAddModal: false,
+		addCustomWelfare: ''
     };
   },
   components: { navBar },
   onLoad(options) {
     if (options.tag&&options.tag.length>0&&options.tag!='undefined') {
-      this.selectedTags = options.tag.split(';')
+      this.selectedTags = decodeURIComponent(options.tag).split(';')
     }
+	if (options.text) {
+		this.otherWelfare = decodeURIComponent(options.text)
+	}
   },
   methods: {
     // 切换系统标签选中状态
@@ -73,35 +120,74 @@ export default {
       }
     },
 
-    // 移除已选标签(系统或自定义)
-    removeTag(index) {
-      this.selectedTags.splice(index, 1);
-    },
+    // // 移除已选标签(系统或自定义)
+    // removeTag(index) {
+    //   this.selectedTags.splice(index, 1);
+    // },
 
-    // 添加自定义标签
-    addCustomTag() {
-      const tag = this.customWelfare.trim();
-      if (!tag) {
-        uni.showToast({ title: '请输入福利内容', icon: 'none' });
-        return;
-      }
-      if (this.selectedTags.includes(tag)) {
-        uni.showToast({ title: '该福利已添加', icon: 'none' });
-        return;
-      }
-      if (this.selectedTags.length >= 10) {
-        uni.showToast({ title: '最多选择10个福利', icon: 'none' });
-        return;
-      }
-      this.selectedTags.push(tag); // 添加到选中列表
-      this.customWelfare = ""; // 清空输入框
-    },
+    // // 添加自定义标签
+    // addCustomTag() {
+    //   const tag = this.customWelfare.trim();
+    //   if (!tag) {
+    //     uni.showToast({ title: '请输入福利内容', icon: 'none' });
+    //     return;
+    //   }
+    //   if (this.selectedTags.includes(tag)) {
+    //     uni.showToast({ title: '该福利已添加', icon: 'none' });
+    //     return;
+    //   }
+    //   if (this.selectedTags.length >= 10) {
+    //     uni.showToast({ title: '最多选择10个福利', icon: 'none' });
+    //     return;
+    //   }
+    //   this.selectedTags.push(tag); // 添加到选中列表
+    //   this.customWelfare = ""; // 清空输入框
+    // },
+	
+	// 移除自定义福利
+	removeCustomTag(tag, index) {
+		this.fundList.splice(index, 1)
+		
+		const list = []
+		this.selectedTags.forEach(item => {
+			if (item != tag) {
+				list.push(item)
+			}
+		})
+		console.log(list)
+		this.selectedTags = list
+		console.log(this.selectedTags)
+	},
+	
+	// 添加自定义福利
+	confirmAddCustomeWelfare() {
+		const tag = this.addCustomWelfare.trim();
+		if (!tag) {
+			uni.showToast({ title: '请输入福利内容', icon: 'none' });
+			return;
+		}
+		if (this.fundList.includes(tag)) {
+			uni.showToast({ title: '该福利已添加', icon: 'none' });
+			return;
+		}
+		if (this.fundList.length >= 22) {
+			uni.showToast({ title: '最多选择10个福利', icon: 'none' });
+			return;
+		}
+		this.fundList.push(tag)
+		this.selectedTags.push(tag) // 添加到选中列表
+		this.addCustomWelfare = ''
+		this.showAddModal = false
+	},
 
     // 提交回传
     submitWelfare() {
-      const welfareData = this.selectedTags.join(';');
-      uni.$emit("fundData", welfareData);
-      uni.navigateBack();
+		const welfareData = this.selectedTags.join(';');
+		uni.$emit("fundData", {
+			text: this.otherWelfare,
+			tagText: welfareData
+		});
+		uni.navigateBack();
     },
   },
 };
@@ -122,138 +208,209 @@ export default {
     width: 100%;
     flex: 1;
     overflow-y: auto;
-    padding: 20rpx;
-  }
-
-  .content-card {
-    background-color: #fff;
-    border-radius: 16rpx;
-    padding: 30rpx;
-    margin-bottom: 20rpx;
-    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+    padding: 32rpx 40rpx;
   }
 
   .card-title {
-    font-size: 32rpx;
-    font-weight: 600;
+    font-size: 48rpx;
+    font-weight: 700;
+	line-height: 60rpx;
     color: #333;
     margin-bottom: 20rpx;
   }
+  
+	.other-input-area {
+		padding: 40rpx 32rpx 20rpx;
+		border-radius: 6px;
+		background: rgba(153, 153, 153, 0.05);
+		margin-bottom: 20rpx;
+		.other-textarea {
+			width: 100%;
+			height: 340rpx;
+			font-size: 20rpx;
+			line-height: 26rpx;
+		}
+		.input-count {
+			font-size: 20rpx;
+			margin-top: 20rpx;
+			color: #999;
+			.hight-light {
+				color: #016BF6;
+			}
+		}
+	}
+  
+	// 福利标签
+	.tag-list {
+		display: flex;
+		flex-wrap: wrap;
+	}
+	.tag {
+		color: rgba(102, 102, 102, 1);
+		font-size: 20rpx;
+		line-height: 1;
+		border-radius: 8rpx;
+		background: rgba(153, 153, 153, 0.1);
+		margin-right: 12rpx;
+		margin-bottom: 8rpx;
+		padding: 8rpx 9rpx;
+		&.tag-active {
+			color: rgba(1, 107, 246, 1);
+			background: rgba(1, 107, 246, 0.1);
+		}
+	}
+	.add-tag-wrapper {
+		margin: 20rpx 0 10rpx;
+		.add-icon {
+			display: inline-block;
+			width: 20rpx;
+			height: 20rpx;
+			margin-left: 4rpx;
+		}
+	}
+	.tag-count-wrapper {
+		color: #016BF6;
+		font-size: 20rpx;
+		line-height: 26rpx;
+	}
+	
+	// 添加自定义弹窗
+	.custom-input {
+		margin: 40rpx 60rpx;
+		border: 1px solid #ddd;
+		border-radius: 4px;
+		padding: 0 20rpx;
+		.input {
+			height: 60rpx;
+			line-height: 40rpx;
+			margin: 0 auto;
+		}
+	}
 
   /* 已选标签展示区样式 */
-  .selected-tags {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 16rpx;
-    padding: 10rpx 0;
-    margin-bottom: 20rpx;
-    min-height: 60rpx;
-    border-bottom: 1rpx solid #f0f0f0;
-    padding-bottom: 15rpx;
+  // .selected-tags {
+  //   display: flex;
+  //   flex-wrap: wrap;
+  //   gap: 16rpx;
+  //   padding: 10rpx 0;
+  //   margin-bottom: 20rpx;
+  //   min-height: 60rpx;
+  //   border-bottom: 1rpx solid #f0f0f0;
+  //   padding-bottom: 15rpx;
 
-    .selected-tag-item {
-      display: inline-flex;
-      align-items: center;
-      background-color: #f0f7ff;
-      border-radius: 60rpx;
-      padding: 8rpx 20rpx;
-      padding-right: 12rpx;
-      font-size: 28rpx;
-      color: #007aff;
-      gap: 8rpx;
+  //   .selected-tag-item {
+  //     display: inline-flex;
+  //     align-items: center;
+  //     background-color: #f0f7ff;
+  //     border-radius: 60rpx;
+  //     padding: 8rpx 20rpx;
+  //     padding-right: 12rpx;
+  //     font-size: 28rpx;
+  //     color: #007aff;
+  //     gap: 8rpx;
 
-      u-icon {
-        margin-left: 5rpx;
-        padding: 2rpx;
+  //     u-icon {
+  //       margin-left: 5rpx;
+  //       padding: 2rpx;
 
-        &:active {
-          background-color: rgba(0, 0, 0, 0.05);
-          border-radius: 50%;
-        }
-      }
-    }
-  }
+  //       &:active {
+  //         background-color: rgba(0, 0, 0, 0.05);
+  //         border-radius: 50%;
+  //       }
+  //     }
+  //   }
+  // }
 
-  .input-area {
-    display: flex;
-    flex-direction: column;
-    margin-bottom: 30rpx;
+  // .input-area {
+  //   display: flex;
+  //   flex-direction: column;
+  //   margin-bottom: 30rpx;
 
-    .custom-textarea {
-      width: 100%;
-      min-height: 120rpx;
-      border: 1rpx solid #e5e7eb;
-      border-radius: 8rpx;
-      padding: 20rpx;
-      font-size: 28rpx;
-      margin-bottom: 16rpx;
-    }
+  //   .custom-textarea {
+  //     width: 100%;
+  //     min-height: 120rpx;
+  //     border: 1rpx solid #e5e7eb;
+  //     border-radius: 8rpx;
+  //     padding: 20rpx;
+  //     font-size: 28rpx;
+  //     margin-bottom: 16rpx;
+  //   }
 
-    .add-btn {
-      align-self: flex-end;
-      background-color: #007aff;
-      color: #fff;
-      border-radius: 8rpx;
-      font-size: 28rpx;
-      padding: 10rpx 24rpx;
-      margin-bottom: 10rpx;
-    }
+  //   .add-btn {
+  //     align-self: flex-end;
+  //     background-color: #007aff;
+  //     color: #fff;
+  //     border-radius: 8rpx;
+  //     font-size: 28rpx;
+  //     padding: 10rpx 24rpx;
+  //     margin-bottom: 10rpx;
+  //   }
 
-    .word-count {
-      font-size: 26rpx;
-      color: #999;
-      text-align: right;
+  //   .word-count {
+  //     font-size: 26rpx;
+  //     color: #999;
+  //     text-align: right;
 
-      text {
-        color: #007aff;
-      }
-    }
-  }
+  //     text {
+  //       color: #007aff;
+  //     }
+  //   }
+  // }
 
-  .tag-section {
-    .section-title {
-      font-size: 30rpx;
-      color: #666;
-      margin-bottom: 20rpx;
-    }
+  // .tag-section {
+  //   .section-title {
+  //     font-size: 30rpx;
+  //     color: #666;
+  //     margin-bottom: 20rpx;
+  //   }
 
-    .tag-list {
-      display: flex;
-      flex-wrap: wrap;
-      gap: 16rpx;
-      margin-bottom: 20rpx;
+  //   .tag-list {
+  //     display: flex;
+  //     flex-wrap: wrap;
+  //     gap: 16rpx;
+  //     margin-bottom: 20rpx;
 
-      .tag-item {
-        padding: 12rpx 24rpx;
-        border: 1rpx solid #d1d5db;
-        border-radius: 60rpx;
-        color: #4b5563;
-        font-size: 28rpx;
-        background-color: #fff;
-      }
+  //     .tag-item {
+  //       padding: 12rpx 24rpx;
+  //       border: 1rpx solid #d1d5db;
+  //       border-radius: 60rpx;
+  //       color: #4b5563;
+  //       font-size: 28rpx;
+  //       background-color: #fff;
+  //     }
 
-      .tag-selected {
-        border-color: #007aff;
-        color: #007aff;
-        background-color: #e0f2ff;
-      }
-    }
+  //     .tag-selected {
+  //       border-color: #007aff;
+  //       color: #007aff;
+  //       background-color: #e0f2ff;
+  //     }
+  //   }
 
-    .selected-count {
-      font-size: 26rpx;
-      color: #666;
-    }
+  //   .selected-count {
+  //     font-size: 26rpx;
+  //     color: #666;
+  //   }
+  // }
+  
+  .button-section {
+  	padding: 28rpx 40rpx;
+  	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
   }
-
   .submit-btn {
-    margin: 30rpx;
-    padding: 24rpx 0;
-    border-radius: 100rpx;
-    background: linear-gradient(90deg, #005eff, #00bfff);
-    color: #fff;
-    font-size: 32rpx;
-    font-weight: 500;
-    text-align: center;
+  	flex-shrink: 0;
+  	border-radius: 999px;
+  	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+  	background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
+  	color: rgba(255, 255, 255, 1);
+  	font-family: DM Sans;
+  	font-size: 32rpx;
+  	font-weight: 400;
+  	line-height: 48rpx;
+  	display: flex;
+  	justify-content: center;
+  	align-items: center;
+  	padding: 16rpx 32rpx;
+  	box-sizing: border-box;
   }
 }
 </style>

+ 56 - 59
package/jobIntention/preferenceSetting.vue

@@ -1,51 +1,51 @@
 <template>
 	<view class="preference-setting">
-		<!-- 自定义导航栏 -->
-		<view class="custom-navbar" :style="{ paddingTop: (12 + statusBarHeight) + 'px' }">
-			<view class="navbar-content">
-				<view class="nav-left" @click="goBack">
-					<u-icon name="close" color="#333" size="32"></u-icon>
+		<view class="container">
+			<!-- 自定义导航栏 -->
+			<view class="custom-navbar" :style="{ paddingTop: (12 + statusBarHeight) + 'px' }">
+				<view class="navbar-content">
+					<view class="nav-left" @click="goBack">
+						<u-icon name="close" color="#333" size="32"></u-icon>
+					</view>
+					<view class="nav-title">跨境职业标签</view>
+					<view class="nav-right"></view>
 				</view>
-				<view class="nav-title">跨境职业标签</view>
-				<view class="nav-right"></view>
 			</view>
-		</view>
 
-		<!-- 主要内容 -->
-		<view class="main-content">
-			<view class="job-title">{{ jobTitle }}-职位偏好</view>
-			<view class="description">根据你的偏好设置,为你推荐更匹配的职位</view>
+			<!-- 主要内容 -->
+			<view class="main-content">
+				<view class="job-title">{{ jobTitle }}-职位偏好</view>
+				<view class="description">根据你的偏好设置,为你推荐更匹配的职位</view>
 
-			<view class="preference-section">
-				<view class="section-title">请选择你的从事偏好</view>
+				<view class="preference-section">
+					<view class="section-title">请选择你的从事偏好</view>
 
-				<!-- 偏好标签网格 -->
-				<view class="preference-grid">
-					<view v-for="(parent, pIndex) in preferenceOptions" :key="pIndex" class="preference-group">
-						<!-- 一级标题 -->
-						<view class="preference-title">
-							<text>{{ parent.postSkillName }}</text>
-						</view>
+					<!-- 偏好标签网格 -->
+					<view class="preference-grid">
+						<view v-for="(parent, pIndex) in preferenceOptions" :key="pIndex" class="preference-group">
+							<!-- 一级标题 -->
+							<view class="preference-title">
+								<text>{{ parent.postSkillName }}</text>
+							</view>
 
-						<!-- 二级可选项 -->
-						<view class="preference-children">
-							<view v-for="(child, cIndex) in parent.childrenList" :key="cIndex" class="preference-tag"
-								:class="{ active: selectedPreferences.includes(child.postSkillName) }"
-								@click="togglePreference(pIndex,child.postSkillName)">
-								<text>{{ child.postSkillName }}</text>
+							<!-- 二级可选项 -->
+							<view class="preference-children">
+								<view v-for="(child, cIndex) in parent.childrenList" :key="cIndex" class="preference-tag"
+									:class="{ active: selectedPreferences.includes(child.postSkillName) }"
+									@click="togglePreference(pIndex,child.postSkillName)">
+									<text>{{ child.postSkillName }}</text>
+								</view>
 							</view>
 						</view>
 					</view>
-				</view>
 
-			</view>
+				</view>
+			</view>			
 		</view>
 
 		<!-- 底部确定按钮 -->
-		<view class="bottom-btn-container">
-			<view class="confirm-btn" @click="confirmSelection">
-				<text>确定</text>
-			</view>
+		<view class="button-section">
+			<view class="submit-btn" @click="confirmSelection">确定</view>
 		</view>
 	</view>
 </template>
@@ -151,7 +151,13 @@ export default {
 	background: #fff;
 	display: flex;
 	flex-direction: column;
-	padding: 0 40rpx 40rpx 40rpx;
+	// padding: 0 40rpx 40rpx 40rpx;
+	padding-bottom: 40rpx;
+}
+
+.container {
+	flex: 1;
+	padding: 0 40rpx;
 }
 
 .navbar-content {
@@ -276,33 +282,24 @@ export default {
 	}
 }
 
-.bottom-btn-container {
-	// position: fixed;
-	// bottom: 0;
-	// left: 0;
-	// right: 0;
-	padding: 32rpx;
-	background: #fff;
-	border-top: 1px solid #f0f0f0;
+.button-section {
+	padding: 28rpx 40rpx;
+	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
 }
-
-.confirm-btn {
-	width: 100%;
-	height: 88rpx;
-	background: #007AFF;
-	border-radius: 44rpx;
+.submit-btn {
+	flex-shrink: 0;
+	border-radius: 999px;
+	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+	background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
+	color: rgba(255, 255, 255, 1);
+	font-family: DM Sans;
+	font-size: 32rpx;
+	font-weight: 400;
+	line-height: 48rpx;
 	display: flex;
-	align-items: center;
 	justify-content: center;
-
-	text {
-		font-size: 32rpx;
-		font-weight: 600;
-		color: #fff;
-	}
-
-	&:active {
-		background: #0056CC;
-	}
+	align-items: center;
+	padding: 16rpx 32rpx;
+	box-sizing: border-box;
 }
 </style>

+ 173 - 177
package/jobIntention/underReview.vue

@@ -1,185 +1,181 @@
 <template>
-  <view class="company">
-    <view class="company-content">
-      <view class="company-title" v-if="companyinfo.status==1">
-        <u-icon
-          name="clock-fill"
-          color="#016BF6"
-          size="40"
-          style="margin-right: 16rpx"
-        ></u-icon>
-		认证审核中
-       
-      </view>
-	  <view class="company-title" v-else-if="companyinfo.status==2">
-	    <u-icon
-	      name="clock-fill"
-	      color="#016BF6"
-	      size="40"
-	      style="margin-right: 16rpx"
-	    ></u-icon>
-	  		认证审核已通过
-	  </view>
-	  <view class="company-title" v-else >
-	    <u-icon
-	      name="clock-fill"
-	      color="#016BF6"
-	      size="40"
-	      style="margin-right: 16rpx"
-	    ></u-icon>
-	  		认证审核被拒绝
-	  </view>
-	  
-	  
-      <view class="review-desc" v-if="companyinfo.status!=2">
-        您的企业【{{companyinfo.companyAllName}}】于[{{companyinfo.companyCreateTime}}]提交了企业认证申请,
-        {{companyinfo.status==3 ? '审核未通过,理由如下:'+companyinfo.message : '请耐心等待认证结果'}}
-      </view>
-	  <view class="review-desc" v-else>
-	    您的企业【{{companyinfo.companyAllName}}】于[{{companyinfo.companyCreateTime}}]提交了企业认证申请,
-		已经通过审核。
-	  </view>
-	  
-      
-      <!-- 审核拒绝理由区域 -->
-      <view class="reject-reason" v-if="companyinfo.status==3">
-        {{companyinfo.auditContent}}
-      </view>
-      
-      <view class="review-btn" @click="goCompleteMsg">
-        {{companyinfo.status==3 ? '重新提交' : '我知道了'}}
-      </view>
-    </view>
-  </view>
+	<view class="company" :style="{ paddingTop: (12 + statusBarHeight) + 'px' }">
+		<view class="company-content">
+			<view class="company-title">
+				<u-icon name="clock-fill" color="#016BF6" size="40" style="margin-right: 16rpx"></u-icon>
+				<text>{{ statusText }}</text>
+			</view>
+
+			<view class="review-desc"  v-if="companyinfo.status == 1">您于[{{companyinfo.createTime}}]提交了企业认证申请,请耐心等待预计1-3个工作日将会反馈认证结果</view>
+			<view class="review-desc" v-else-if="companyinfo.status == 2">
+				您的企业【{{companyinfo.companyAllName}}】于[{{companyinfo.createTime}}]提交了企业认证申请,
+				已经通过审核。
+			</view>
+			<view class="review-desc" v-else>
+				您的企业【{{companyinfo.companyAllName}}】于[{{companyinfo.createTime}}]提交了企业认证申请,
+				{{companyinfo.status== 3 ? '审核未通过,理由如下:' + companyinfo.message : '请耐心等待认证结果'}}
+			</view>
+
+			<image src="/static/invite.png" class="slogan"></image>
+
+			<!-- 审核拒绝理由区域 -->
+			<!-- <view class="reject-reason" v-if="companyinfo.status==3">
+				{{companyinfo.auditContent}}
+			</view> -->
+
+		</view>
+		<view class="section-btn">
+			<view class="submit-btn" @click="goCompleteMsg">
+				{{companyinfo.status==3 ? '重新提交' : '我知道了'}}
+			</view>			
+		</view>
+	</view>
 </template>
 
 <script>
-export default {
-  data() {
-    return {
-      companyinfo: {} // 改为对象初始值更合适
-    };
-  },
-  
-  onShow() {
-    this.getcompanystatus()
-  },
-  
-  methods: {
-    goCompleteMsg() {
-		// if(this.companyinfo.status!=2){
-		// 	//  uni.navigateTo({ url: "/my/renzheng/editCompany" });
-		// 	 uni.switchTab({ url: "/pages/my/index" });
-		// }else{
-		// 	 uni.navigateTo({ url: "/package/jobIntention/completeMsg" });
-		// }
-		uni.switchTab({ url: "/pages/my/index" });
-    },
-    
-    // 查询用户企业状态
-    getcompanystatus() {
-      this.$Request.get("/app/company/selectCompanyByUserId", "")
-        .then((res) => {
-          if (res.code != 0) {
-            uni.showToast({
-              title: res.msg || "查询状态失败",
-              icon: "none"
-            });
-            return;
-          }
-          this.companyinfo = res.data || {};
-          console.log("查询用户企业状态", res)
-        })
-        .catch((err) => {
-          console.error("查询失败:", err);
-          uni.showToast({
-            title: "网络异常",
-            icon: "none"
-          });
-        });
-    }
-  },
-};
+	export default {
+		data() {
+			return {
+				statusBarHeight: 0, // 状态栏高度
+				companyinfo: {} // 改为对象初始值更合适
+			};
+		},
+		
+		computed: {
+			statusText() {
+				let text = '认证审核被拒绝'
+				let status = this.companyinfo?.status || 3
+				switch (status) {
+					case 1:
+						text = '认证审核中'
+						break
+					case 2:
+						text = '认证审核已通过'
+						break
+				}
+				return text
+			}
+		},
+		
+		onLoad() {
+			// 获取状态栏高度
+			let systemInfo = uni.getSystemInfoSync();
+			this.statusBarHeight = systemInfo.statusBarHeight || 0;
+		},
+
+		onShow() {
+			this.getcompanystatus()
+		},
+
+		methods: {
+			goCompleteMsg() {
+				if (this.companyinfo.status == 1) {
+					// 审核中,跳转到完善信息页面
+					uni.navigateTo({
+						url: '/package/jobIntention/completeMsg'
+					})
+				} else {
+					uni.switchTab({
+						url: "/pages/my/index"
+					});					
+				}
+			},
+
+			// 查询用户企业状态
+			getcompanystatus() {
+				this.$Request.get("/app/company/selectCompanyByUserId", "")
+					.then((res) => {
+						if (res.code != 0) {
+							uni.showToast({
+								title: res.msg || "查询状态失败",
+								icon: "none"
+							});
+							return;
+						}
+						this.companyinfo = res.data || {};
+						console.log("查询用户企业状态", res)
+					})
+					.catch((err) => {
+						console.error("查询失败:", err);
+						uni.showToast({
+							title: "网络异常",
+							icon: "none"
+						});
+					});
+			}
+		},
+	};
 </script>
 
 <style scoped lang="scss">
-.company {
-  position: absolute;
-  left: 0;
-  right: 0;
-  top: 0;
-  bottom: 0;
-  display: flex;
-  flex-direction: column;
-  font-family: DM Sans;
-  
-  .company-content {
-    flex: 1;
-    padding: 40rpx;
-    box-sizing: border-box;
-    overflow: hidden;
-    overflow-y: auto;
-    padding-top: 200rpx;
-    
-    .company-title {
-      color: rgba(29, 33, 41, 1);
-      font-family: DM Sans;
-      font-size: 36rpx;
-      font-weight: 500;
-      line-height: 44rpx;
-      text-align: center;
-    }
-    
-    .review-desc {
-      color: rgba(102, 102, 102, 1);
-      font-family: DM Sans;
-      font-size: 24rpx;
-      font-weight: 400;
-      line-height: 32rpx;
-      text-align: center;
-      padding-top: 22rpx;
-      padding-bottom: 20rpx;
-    }
-    
-    // 拒绝理由样式
-    .reject-reason {
-      color: #ff4d4f; /* 红色表示错误/拒绝 */
-      font-family: DM Sans;
-      font-size: 26rpx;
-      line-height: 36rpx;
-      text-align: center;
-      padding: 20rpx;
-      margin: 0 22rpx 50rpx;
-      background-color: #fff8f8;
-      border-radius: 8rpx;
-      border: 1px solid #ffebe9;
-    }
-    
-    .review-btn {
-      padding: 16rpx 32rpx;
-      box-sizing: border-box;
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      border-radius: 999px;
-      background: rgba(255, 102, 0, 1);
-      color: rgba(255, 255, 255, 1);
-      font-family: DM Sans;
-      font-size: 32rpx;
-      font-weight: 400;
-      margin: 0 22rpx;
-    }
-  }
-  
-  .bottom-btn {
-    flex-shrink: 0;
-    padding-bottom: 50rpx;
-    text-align: center;
-    color: #016bf6;
-    font-family: DM Sans;
-    font-size: 28rpx;
-    font-weight: 400;
-    line-height: 36rpx;
-  }
-}
+	.company {
+		height: 100vh;
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+		font-family: DM Sans;
+		box-sizing: border-box;
+
+		.company-content {
+			flex: 1;
+			padding: 96rpx 50rpx 40rpx;
+			box-sizing: border-box;
+
+			.company-title {
+				color: rgba(29, 33, 41, 1);
+				font-size: 36rpx;
+				font-weight: 500;
+				line-height: 44rpx;
+				text-align: center;
+				margin-bottom: 100rpx;
+			}
+
+			.review-desc {
+				height: 280rpx;
+				color: rgba(102, 102, 102, 1);
+				font-size: 28rpx;
+				font-weight: 400;
+				line-height: 36rpx;
+				text-align: center;
+			}
+			
+			.slogan {
+				display: block;
+				width: 400rpx;
+				height: 400rpx;
+				margin: 20rpx auto;
+			}
+
+			// 拒绝理由样式
+			.reject-reason {
+				color: #ff4d4f;
+				/* 红色表示错误/拒绝 */
+				font-family: DM Sans;
+				font-size: 26rpx;
+				line-height: 36rpx;
+				text-align: center;
+				padding: 20rpx;
+				margin: 0 22rpx 50rpx;
+				background-color: #fff8f8;
+				border-radius: 8rpx;
+				border: 1px solid #ffebe9;
+			}
+		}
+
+		.section-btn {
+			padding: 40rpx;
+			box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+			background-color: #fff;
+			.submit-btn {
+				height: 90rpx;
+				font-size: 32rpx;
+				font-weight: 500;
+				color: #fff;
+				text-align: center;
+				line-height: 90rpx;
+				border-radius: 90rpx;
+				background: linear-gradient(90.00deg, rgba(13, 39, 247, 1),rgba(19, 193, 234, 1));
+			}
+		}
+	}
 </style>

+ 2 - 4
package/list/baoming.vue

@@ -95,12 +95,10 @@
 						title: '提示',
 						content: '您还未登录,请先登录',
 						confirmColor:'#016BF6',
-						success: function(res) {
+						success: (res) => {
 							if (res.confirm) {
 								console.log('用户点击确定');
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							} else if (res.cancel) {
 								console.log('用户点击取消');
 							}

+ 2 - 4
package/list/studyUp.vue

@@ -112,12 +112,10 @@
 					uni.showModal({
 						title: '提示',
 						content: '您还未登录,请先登录',
-						success: function(res) {
+						success: (res) => {
 							if (res.confirm) {
 								console.log('用户点击确定');
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							} else if (res.cancel) {
 								console.log('用户点击取消');
 							}

+ 2 - 4
package/list/train.vue

@@ -119,12 +119,10 @@
 						title: '提示',
 						content: '您还未登录,请先登录',
 						confirmColor:'#016BF6',
-						success: function(res) {
+						success: (res) => {
 							if (res.confirm) {
 								console.log('用户点击确定');
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							} else if (res.cancel) {
 								console.log('用户点击取消');
 							}

+ 2 - 4
package/list/zhiCheng.vue

@@ -120,12 +120,10 @@
 						title: '提示',
 						content: '您还未登录,请先登录',
 						confirmColor:'#016BF6',
-						success: function(res) {
+						success: (res) => {
 							if (res.confirm) {
 								console.log('用户点击确定');
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							} else if (res.cancel) {
 								console.log('用户点击取消');
 							}

+ 59 - 160
package/my/hiddenManagement/hiddenCompany.vue

@@ -7,16 +7,16 @@
 					<image src="@/static/images/svg/search.svg" class="search-icon"></image>
 					<input v-model="searchValue" type="text" placeholder="输入的是一个公司的名称,全模糊搜索" class="input" />
 				</view>
-				<view class="search-btn" @click="handleSearch">搜索</view>
+				<view class="search-btn" @click="getData">搜索</view>
 			</view>
 			<scroll-view :scroll-y="true" class="scroll-view">
 				<view class="scroll-wrapper">
-					<view class="company-item" v-for="company in companys" :key="company.id">
+					<view class="company-item" v-for="company in companys" :key="company.companyId">
 						<u-checkbox 
 							v-model="company.checked" 
-							:name="company.id"
+							:name="company.companyId"
 							@change="checkboxChange"
-						>{{company.name}}</u-checkbox>						
+						>{{company.companyName}}</u-checkbox>						
 					</view>
 				</view>
 			</scroll-view>			
@@ -38,168 +38,39 @@
 			return {
 				searchValue: '',
 				selectCount: 0,
-				companys: [
-					{
-						name: 'Apple',
-						id: '1',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Google',
-						id: '2',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '特斯拉',
-						id: '3',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '英伟达',
-						id: '4',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Meta',
-						id: '5',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'X',
-						id: '6',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '甲骨文',
-						id: '7',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '微软',
-						id: '8',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'OpenIA',
-						id: '9',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Space X',
-						id: '10',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Meta',
-						id: '11',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'X',
-						id: '12',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '甲骨文',
-						id: '13',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '微软',
-						id: '14',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'OpenIA',
-						id: '15',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Space X',
-						id: '16',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Meta',
-						id: '17',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'X',
-						id: '18',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '甲骨文',
-						id: '19',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '微软',
-						id: '20',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'OpenIA',
-						id: '21',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Space X',
-						id: '22',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: '微软',
-						id: '23',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'OpenIA',
-						id: '24',
-						checked: false,
-						disabled: false
-					},
-					{
-						name: 'Space X',
-						id: '25',
-						checked: false,
-						disabled: false
-					},
-				],
+				companys: [],
 				selectCompanyIds: [],
+				selectCompanyList: [],
+				searching: false,
+				loading: false
 			};
 		},
 		methods: {
-			// 搜索
-			handleSearch() {
-				console.log(this.searchValue)
+			getData() {
+				if (this.searching) return
+				this.searching = true
+				uni.showLoading({
+					title: '搜索中'
+				})
+				this.$Request
+					.getT('/app/shieldCompany/selectCompanyListByName', {
+						companyName: this.searchValue
+					})
+					.then(res => {
+						if (res.code === 0) {
+							this.selectCompanyIds = []
+							this.selectCompanyList = []
+							this.companys = res.data
+						}
+					})
+					.finally(() => {
+						this.searching = false
+						uni.hideLoading()
+					})
 			},
 			// 选择公司
 			checkboxChange(item) {
+				const company = this.companys.find(c => c.companyId == item.name)
 				if (!item.value) {
 					// 删除
 					let deleteId = 0
@@ -209,16 +80,44 @@
 						}
 					})
 					this.selectCompanyIds.splice(deleteId, 1)
+					this.selectCompanyList.splice(deleteId, 1)
 					this.selectCount--
 				} else {
 					// 新增
 					this.selectCompanyIds.push(item.name)
+					this.selectCompanyList.push(company)
 					this.selectCount++
 				}
 			},
 			// 提交数据
 			handleSave() {
-				console.log(this.selectCompanyIds)
+				if (this.loading) return
+				if (!this.selectCompanyIds.length) {
+					return uni.showToast({
+						title: '请选择屏蔽公司',
+						icon: 'none'
+					})
+					return
+				}
+				
+				this.loading = true
+				uni.showLoading({
+					title: '保存中'
+				})
+				this.$Request
+					.postJson('/app/shieldCompany/insertShieldCompanys', {
+						shieldCompanyList: this.selectCompanyList
+					})
+					.then(res => {
+						if (res.code === 0) {
+							uni.navigateBack()
+							uni.$emit('refreshList')
+						}
+					})
+					.finally(() => {
+						this.loading = false
+						uni.hideLoading()
+					})
 			}
 		}
 	}

+ 103 - 10
package/my/hiddenManagement/index.vue

@@ -11,21 +11,38 @@
 				</view>
 			</view>
 		</view>
-		<scroll-view :scroll-y="true" class="company-list">
+		<scroll-view
+			class="company-list"
+			:scroll-y="true"
+			:refresher-threshold="80"
+			:refresher-enabled="true"
+			:refresher-triggered="isRefreshing"
+			scroll-with-animation="true"
+			@refresherrefresh="handleRefresh"
+			@scrolltolower="loadMore"
+		>
 			<view class="scroll-wrapper">
-				<view class="company-item" v-for="item in 30" :key="item">
-					<siwper-delete @delete="handleDelete">
+				<view class="company-item" v-for="item in hideCompanys" :key="item.companyId">
+					<siwper-delete @delete="handleDelete(item)">
 						<view class="company-content flex align-center">
-							<image src="@/static/images/logo.jpg" class="company-icon"></image>
+							<image :src="item.company.companyLogo ? item.company.companyLogo : '/static/images/logo.jpg'" class="company-icon"></image>
 							<view class="company-info">
-								<view class="company-name">深圳市四海新舟科技有限公司</view>
-								<view class="text">100-400 · 跨境电商</view>
+								<view class="company-name">{{ item.companyName }}</view>
+								<view class="text">{{ item.company.companyPeople }} <template v-if="item.company.type == '0'">· 跨境电商</template></view>
 							</view>
 						</view>
 					</siwper-delete>
 				</view>				
 			</view>
-		</scroll-view>			
+		</scroll-view>
+		
+		<u-modal
+			v-model="showDeleteModal"
+			:async-close="true"
+			:content="content"
+			:show-cancel-button="true"
+			@confirm="confirm"
+		></u-modal>
 	</view>
 </template>
 
@@ -40,17 +57,93 @@
 		},
 		data() {
 			return {
-				
+				hideCompanys: [],
+				loading: false,
+				page: 1,
+				isRefreshing: false,
+				showDeleteModal: false,
+				selectCompany: {},
+				content: '是否确定删除屏蔽',
+				total: 0
 			};
 		},
+		onLoad() {
+			this.getHideCompany()
+			
+			// 添加屏蔽后返回刷新数据
+			uni.$on('refreshList', () => {
+				this.page = 1
+				this.getHideCompany()
+			})
+		},
+		onUnload() {
+			uni.$off('refreshList')
+		},
 		methods: {
-			handleDelete() {
-				console.log(1123)
+			// 获取屏蔽公司列表数据
+			getHideCompany() {
+				uni.showLoading({
+					title: '加载中'
+				})
+				this.loading = true
+				this.$Request
+					.getT('/app/shieldCompany/selectShieldList', {
+						page: this.page,
+						limit: 10,
+					})
+					.then(res => {
+						if (res.code === 0) {
+							this.hideCompanys = this.page != 1 ? [...this.hideCompanys, ...res.data.list] : res.data.list
+							this.total = res.data.totalCount
+						}
+					})
+					.finally(() => {
+						this.isRefreshing = false
+						this.loading = false
+						uni.hideLoading()
+					})
+			},
+			// 删除屏蔽
+			deleteCompany() {
+				uni.showLoading({
+					title: '删除中'
+				})
+				this.$Request
+					.postJson('/app/shieldCompany/deleteShieldCompany', {
+						shieldCompanyId: this.selectCompany.shieldCompanyId,
+					})
+					.then(res => {
+						this.$queue.showToast('删除成功')
+						this.page = 1
+						this.getHideCompany()
+					})
+					.finally(() => {
+						this.showDeleteModal = false
+						uni.hideLoading()
+					})
+			},
+			handleDelete(item) {
+				this.showDeleteModal = true
+				this.selectCompany = item
+			},
+			confirm() {
+				this.deleteCompany()
 			},
 			toHiddenCompany() {
 				uni.navigateTo({
 					url: '/package/my/hiddenManagement/hiddenCompany'
 				})
+			},
+			handleRefresh() {
+				if (this.isRefreshing) return
+				this.isRefreshing = true
+				this.page = 1
+				this.getHideCompany()
+			},
+			loadMore() {
+				if (this.loading || this.hideCompanys.length >= this.total) return
+				this.page++
+				this.getHideCompany()
 			}
 		}
 	}

+ 2 - 7
package/search/search.vue

@@ -624,15 +624,10 @@ export default {
 				title: '提示',
 				content: '您还未登录,请先登录',
 				confirmColor: '#016BF6',
-				success: function (res) {
+				success: (res) => {
 					if (res.confirm) {
 						console.log('用户点击确定');
-						// uni.reLaunch({
-						// 	url:'/pages/public/login'
-						// })
-						uni.navigateTo({
-							url: '/pages/public/login'
-						})
+						this.$queue.toLogin('navigateTo')
 					} else if (res.cancel) {
 						console.log('用户点击取消');
 					}

+ 37 - 16
pages.json

@@ -4,16 +4,22 @@
 	},
 	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
 		{
-		"path": "pages/index/index",
-		"style": {
-			"navigationBarTitleText": "首页",
-			// "navigationBarBackgroundColor": "#E5EEFF",
-			"navigationBarBackgroundColor": "#00DD9A",
-			"navigationBarTextStyle": "white",
-			"enablePullDownRefresh": false,
-			"navigationStyle": "custom"
-		}
-	},
+			"path": "pages/public/loginV2",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "首页",
+				// "navigationBarBackgroundColor": "#E5EEFF",
+				"navigationBarBackgroundColor": "#00DD9A",
+				"navigationBarTextStyle": "white",
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
+			}
+		},
 		{
 			"path": "pages/my/switchRoles",
 			"style": {
@@ -537,12 +543,6 @@
 				"navigationStyle": "custom"
 			}
 		},
-		{
-			"path": "pages/public/loginV2",
-			"style": {
-				"navigationStyle": "custom"
-			}
-		},
 		{
 			"path": "pages/public/invite/index",
 			"style": {
@@ -562,6 +562,27 @@
 				"navigationBarTitleText": "下载",
 				"navigationStyle": "custom"
 			}
+		},
+		{
+			"path": "pages/public/phoneLoginOrRegister",
+			"style": {
+				"navigationBarTitleText": "注册登录",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/public/verify",
+			"style": {
+				"navigationBarTitleText": "验证登录",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/my/joinCompany",
+			"style": {
+				"navigationBarTitleText": "加入公司",
+				"navigationStyle": "custom"
+			}
 		}
 	],
 	"subPackages": [

+ 3 - 9
pages/index/game/order.vue

@@ -475,9 +475,7 @@
 					confirmText: '去登录',
 					complete: (res) => {
 						if (res.confirm) {
-							uni.navigateTo({
-								url: '/pages/public/login'
-							})
+							this.$queue.toLogin('navigateTo')
 						}
 
 					}
@@ -570,9 +568,7 @@
 						confirmText: '去登录',
 						complete: (res) => {
 							if (res.confirm) {
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							}
 
 						}
@@ -709,9 +705,7 @@
 						confirmText: '去登录',
 						complete: (res) => {
 							if (res.confirm) {
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							}
 						}
 					});

+ 4 - 10
pages/index/game/orderDet.vue

@@ -294,9 +294,7 @@
 						confirmColor: '#016BF6',
 						complete: (res) => {
 							if (res.confirm) {
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							}
 
 						}
@@ -441,9 +439,7 @@
 						confirmColor: '#016BF6',
 						complete: (res) => {
 							if (res.confirm) {
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							}
 
 						}
@@ -731,12 +727,10 @@
 						title: '提示',
 						content: '您还未登录,请先登录',
 						confirmColor: '#016BF6',
-						success: function(res) {
+						success: (res) => {
 							if (res.confirm) {
 								console.log('用户点击确定');
-								uni.navigateTo({
-									url: '/pages/public/login'
-								})
+								this.$queue.toLogin('navigateTo')
 							} else if (res.cancel) {
 								console.log('用户点击取消');
 							}

Разница между файлами не показана из-за своего большого размера
+ 426 - 374
pages/index/index.vue


+ 1 - 3
pages/index/search/index.vue

@@ -303,9 +303,7 @@
 						url: '/pages/index/game/order?id=' + e.id
 					});
 				} else {
-					uni.navigateTo({
-						url: '/pages/public/login'
-					});
+					this.$queue.toLogin('navigateTo')
 				}
 			},
 

+ 312 - 131
pages/jobManagement/jobManagement.vue

@@ -1,7 +1,11 @@
 <template>
 	<view class="job-management-page">
 		<view class="fixed-header">
-			<navBar title="职位管理" color="#000" :backUrl="backUrl" goMy="/pages/my/index"/>
+			<nav-bar title="职位管理" :backUrl="backUrl" goMy="/pages/my/index">
+				<view slot="left" v-if="canClose" @click="handleToIndex">
+					<u-icon name="close" color="#333"></u-icon>
+				</view>
+			</nav-bar>
 			<!-- 标签页导航 - 使用uview的u-tabs组件 -->
 			<view class="sticky-tabs">
 				<u-tabs 
@@ -31,27 +35,42 @@
 			  @scrolltolower="loadMore"
 			  lower-threshold="50"
 			>
-				 <view 
-				    class="job-card" 
-				    :class="getJobStatusClass(job)"
-				    v-for="(job, index) in jobList" 
-				    :key="index"
-				    @click="goNav('/my/order/pay?postPushId='+job.postPushId)"
-				  >
-					<view class="job-card-content">
-						<view class="job-header">
-							<view class="job-title-section">
-								<text class="job-title">{{job.stationName || job.ruleClassifyName}}</text>
-								<view class="urgent-tag" v-if="job.isDue">
-									<text class="urgent-text">急聘</text>
+				<view class="list">
+					<view 
+						class="job-card"
+						v-for="(job, index) in jobList" 
+						:key="index"
+						@click="goNav('/my/order/pay?postPushId='+job.postPushId)"
+					>
+						<view class="job-card-content">
+							<view class="job-header">
+								<view class="job-title-section">
+									<text class="job-title">{{job.stationName || job.ruleClassifyName}}</text>
+									<view class="urgent-tag" v-if="job.isDue">急聘</view>
+								</view>
+								<view class="job-status" :class="job.status | statusClassName">
+									{{job.status | statusText}}
 								</view>
 							</view>
-							<view class="job-status" :class="getJobStatusClass(job)">
-								<text class="status-text">{{job.status | statusText}}</text>
+							<view class="base-info">
+								<text class="info-item color-clip">{{ job.city }}</text>
+								<text class="info-item color-clip">{{ job.education }}</text>
+								<text class="info-item color-clip">{{ job.experience }}</text>
+								<text class="info-item color-clip">{{ job.salaryRange }}·{{ job.salaryTimes }}</text>
+							</view>
+							<view class="tags">
+								<view class="tag" v-for="(tag, i) in job.positionTags" :key="i">{{ tag }}</view>
+							</view>
+							<view class="company-info flex align-center">
+								<image :src="job.company.companyLogo ? job.company.companyLogo : '/static/logo.png'" class="logo"></image>
+								<text class="company-name">{{ job.company.companyName }}</text>
+							</view>
+							<view class="buttons flex align-center">
+								<view class="button primary" v-if="job.status == 5" @click.stop="handleOpenPosition('open', job)">开启岗位</view>
+								<view class="button plain" v-if="job.status != 1" @click.stop="handleEditPosition(job)">{{ job.status == 2 ? '下架并编辑' : '编辑修改' }}</view>
+								<view class="button info" v-if="job.status == 2" @click.stop="handleOpenPosition('close', job)">关闭职位</view>
+								<view class="button info" v-else @click.stop="handleDeletePosition(job)">删除</view>
 							</view>
-						</view>
-						<view class="job-info">
-							<text class="job-details">{{job.city}}-{{job.county}} {{job.education}} {{job.experience}} {{job.salaryRange}} {{job.postType}}</text>
 						</view>
 					</view>
 				</view>
@@ -66,16 +85,14 @@
 		</view>
 		
 		<!-- 底部发布按钮 -->
-		<view class="bottom-action">
-			<view class="publish-btn" @click="publishNewJob">
-				<text class="publish-text">发布新职位</text>
-			</view>
+		<view class="button-section">
+			<view class="submit-btn" @click="publishNewJob">发布新职位</view>
 		</view>
 	</view>
 </template>
 
 <script>
-	import navBar from "@/components/nav-bar/index.vue";
+import navBar from "@/components/nav-bar/index.vue";
 import empty from '@/components/empty.vue'
 
 export default {
@@ -90,7 +107,7 @@ export default {
         { name: '待审核', status: 1 },
         { name: '招聘中', status: 2 },
         { name: '已拒绝', status: 3 },
-        { name: '已取消', status: 4 },
+        // { name: '已取消', status: 4 },
         { name: '已关闭', status: 5 },
         // { name: '已拒绝', status: 6 }
       ],
@@ -102,22 +119,37 @@ export default {
       isLoading: false, // 防止重复请求标记
 	  backUrl:'',
 	  status:'',
+	  canClose: false, // 判断页面是否是关闭,如果是从“完善招聘名片”页面进入,则是可以关闭,反之则不可以
+	  loading: false,
     }
   },
   filters: {
       statusText(value) {
         switch (value) {
           case 1: return '待审核'
-          case 2: return '审核通过'
-          case 3: return '审核不通过'
+          case 2: return '招聘中'
+          case 3: return '已拒绝'
 		  case 4: return '已取消'
 		  case 5: return '已关闭'
 		  case 6: return '暂存'
           default: return '暂无数据'
         }
-      }
+      },
+		statusClassName(value) {
+			switch (value) {
+				case 1: return ''
+				case 2: return 'primary'
+				case 3: return 'info'
+				case 4: return 'error'
+				case 5: return 'error'
+				default: return ''
+			}
+		}
     },
   onLoad(options) {
+	  if (options.type && options.type == 'canClose') {
+		  this.canClose = true
+	  }
 	if(options.status){
 		this.status=options.status
 	}
@@ -163,6 +195,7 @@ export default {
             } else if (ret.status == 5) {
               ret.statusName = '已关闭'
             }
+			ret.positionTags = ret.positionTag.split(',')
             this.jobList.push(ret)
           })
           this.count = res.data.total; // 更新总条数
@@ -207,8 +240,133 @@ export default {
     goNav(url) { uni.navigateTo({ url }); },
    // publishNewJob() { uni.navigateTo({ url: '/package/addJob/addJob' }); },
 	 publishNewJob() { uni.navigateTo({ url: `/pages/my/jobPosting?status=${this.status}&companyId=${this.companyId}` }); },
-    getJobStatusClass(job) { /* 原有逻辑不变 */ },
+	
     setBodyHeight() { /* 原有逻辑不变 */ },
+	
+	// 跳转至首页
+	handleToIndex() {
+		uni.reLaunch({
+			url: '/pages/index/index'
+		})
+	},
+	
+	// 开启职位
+	handleOpenPosition(type, item) {
+		uni.showModal({
+			title: '提示',
+			content: type == 'close' ? '关闭岗位后,该岗位不会再推荐给用户,是否关闭?' : '开启岗位后,该岗位会重新推荐给用户,是否开启?',
+			complete: (ret) => {
+				if (ret.confirm) {
+					this.closePosition(item)
+				}
+			}
+		})
+	},
+	
+	// 开启关闭岗位
+	closePosition(item, callback) {
+		if (this.loading) return
+		this.loading = true
+		
+		uni.showLoading({
+			title: '处理中'
+		})
+		
+		this.$Request.getT('/app/postPush/closeOrOpen', {
+			postPushId: item.postPushId
+		}).then(res => {
+			if (res.code == 0) {
+				uni.showToast({
+					title: '操作成功',
+					duration: 1500,
+					complete: () => {
+						this.page = 1
+						this.getJobList()
+						if (typeof callback == 'function') {
+							callback()
+						}
+					}
+				})
+			} else {
+				uni.showToast({
+					title: res.msg,
+					icon: 'none'
+				})
+			}
+		}).finally(() => {
+			uni.hideLoading()
+			this.loading = false
+		})
+	},
+	
+	// 编辑职位
+	handleEditPosition(item) {
+		// 如果是招聘中,则是下架并编辑,需要先下架再编辑
+		if (item.status == 2) {
+			uni.showModal({
+				content: '确认下架并编辑该岗位?',
+				title: '提示',
+				confirmColor: '#016BF6',
+				success: (it) => {
+					if (it.confirm) {
+						this.closePosition(item, () => {
+							uni.navigateTo({
+								url: `/pages/my/jobPosting?postPushId=${item.postPushId}&type=update`
+							})	
+						})
+					}
+				}
+			})
+		} else {
+			uni.navigateTo({
+				url: `/pages/my/jobPosting?postPushId=${item.postPushId}&type=update`
+			})			
+		}
+	},
+	
+	// 删除职位
+	handleDeletePosition(item) {
+		uni.showModal({
+			content: '确认删除该岗位吗?',
+			title: '提示',
+			confirmColor: '#016BF6',
+			success: (iet) => {
+				if (iet.confirm) {
+					this.deletePosition(item)
+				}
+			}
+		})
+	},
+	
+	// 删除职位
+	deletePosition(item) {
+		if (this.loading) return
+		this.loading = true
+		
+		uni.showLoading({
+			title: '处理中'
+		})
+		this.$Request.post('/app/postPush/deletePostPush', {
+			postPushId: item.postPushId,
+		}).then(res => {
+			if (res.code == 0) {
+				uni.showToast({
+					title: '删除成功',
+					duration: 1500
+				})
+				this.page = 1
+				this.getJobList()
+			} else {
+				uni.showToast({
+					title: res.msg,
+					icon: 'none'
+				})
+			}
+		}).finally(() => {
+			uni.hideLoading()
+			this.loading = false
+		})
+	}
   }
 }
 </script>
@@ -236,6 +394,8 @@ page {
 	display: flex;
 	flex-direction: column;
 	height: 100vh;
+	font-family: DM Sans;
+	box-sizing: border-box;
 }
 
 .fixed-header {
@@ -247,27 +407,30 @@ page {
 	flex: 1;
 	display: flex;
 	flex-direction: column;
+	overflow: auto;
+	
+	.list {
+		padding-bottom: 30rpx;
+	}
 	
 	.job-card {
 		background: #ffffff;
 		border-radius: 12rpx;
 		margin-bottom: 20rpx;
-		padding: 32rpx;
+		padding: 16rpx 36rpx;
         border: 0.5px solid rgba(227, 231, 236, 1);
-		// box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.05);
 		
 		.job-card-content {
 			.job-header {
 				display: flex;
 				justify-content: space-between;
 				align-items: flex-start;
-				margin-bottom: 16rpx;
+				margin-bottom: 8rpx;
 				
 				.job-title-section {
 					display: flex;
 					align-items: center;
 					flex: 1;
-                    gap: 28rpx;
 					
 					.job-title {
                         color: rgba(23, 23, 37, 1);
@@ -281,102 +444,124 @@ page {
 					
 					.urgent-tag {
                         border-radius: 8rpx;
-                        background: rgba(252, 233, 220, 1);
-						padding: 4rpx 8rpx;
-						
-						.urgent-text {
-                            color: rgba(1, 107, 246, 1);
-                            font-family: DM Sans;
-                            font-size: 16rpx;
-                            font-weight: 400;
-                            // line-height: 20rpx;
-                            // letter-spacing: -0.5px;
-                            text-align: left;
-						}
+						padding: 8rpx;
+						margin-left: 20rpx;
+						background: radial-gradient(87.28% 72.40% at 49% 66%,rgba(255, 160, 162, 0.2),rgba(255, 246, 247, 1));
+						font-size: 20rpx;
+						line-height: 1;
+						color: rgba(237, 66, 69, 1);
 					}
 				}
 				
 				.job-status {
-					&.status-trial {
-						color: rgba(1, 107, 246, 1);
-					}
+					color: rgba(1, 107, 246, 1);
+					padding: 8rpx;
+					font-size: 20rpx;
+					line-height: 1;
+					border-radius: 4px;
 					
-					&.status-review {
+					&.primary {
 						color: rgba(1, 107, 246, 1);
+						background: rgba(1, 107, 246, 0.1);
 					}
 					
-					&.status-failed {
-						color: rgba(153, 153, 153, 1);
-						
-						.status-text {
-							color: rgba(153, 153, 153, 1) !important;
-							background: none !important;
-							-webkit-background-clip: unset !important;
-							-webkit-text-fill-color: rgba(153, 153, 153, 1) !important;
-							background-clip: unset !important;
-							text-fill-color: rgba(153, 153, 153, 1) !important;
-						}
-					}
-					
-					&.status-closed {
+					&.info {
 						color: rgba(153, 153, 153, 1);
-						
-						.status-text {
-							color: rgba(153, 153, 153, 1) !important;
-							background: none !important;
-							-webkit-background-clip: unset !important;
-							-webkit-text-fill-color: rgba(153, 153, 153, 1) !important;
-							background-clip: unset !important;
-							text-fill-color: rgba(153, 153, 153, 1) !important;
-						}
 					}
 					
-					.status-text {
-                        background: linear-gradient(132.53deg, rgba(106.94185638427734, 84.63434600830078, 214.0178680419922, 0.96),rgba(144.87640380859375, 87.8011474609375, 191.25, 1) 95%);
-                        -webkit-background-clip: text;
-                        -webkit-text-fill-color: transparent;
-                        background-clip: text;
-                        text-fill-color: transparent;
-                        font-family: DM Sans;
-                        font-size: 18rpx;
-                        font-weight: 400;
-                        line-height: 20rpx;
-                        letter-spacing: -0.5px;
-                        text-align: right;
+					&.error {
+						color: rgba(144.88, 87.8, 191.25, 1);
 					}
 				}
 			}
 			
-			.job-info {
-				.job-details {
-                    color: rgba(120, 130, 138, 1);
-                    font-family: DM Sans;
-                    font-size: 28rpx;
-                    font-weight: 400;
-                    line-height: 36rpx;
-                    letter-spacing: 0.5%;
-                    text-align: left;
+			.base-info {
+				display: flex;
+				flex-wrap: wrap;
+				margin-bottom: 8rpx;
+				.info-item {
+					margin-right: 12rpx;
+				}
+				.color-clip {
+					background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
+					-webkit-background-clip: text;
+					-webkit-text-fill-color: transparent;
+					background-clip: text;
+					text-fill-color: transparent;
+					font-size: 28rpx;
+					font-weight: 400;
+					line-height: 36rpx;
 				}
-			}
-		}
-		
-		&.status-failed {
-			.job-title {
-				color: rgba(153, 153, 153, 1) !important;
 			}
 			
-			.job-details {
-				color: rgba(153, 153, 153, 1) !important;
+			.tags {
+				display: flex;
+				flex-wrap: wrap;
+				.tag {
+					padding: 8rpx;
+					border-radius: 8rpx;
+					background: rgba(153, 153, 153, 0.1);
+					margin-right: 4px;
+					margin-bottom: 4px;
+					color: rgba(102, 102, 102, 1);
+					font-size: 20rpx;
+					font-weight: 400;
+					line-height: 1;
+				}
 			}
-		}
-		
-		&.status-closed {
-			.job-title {
-				color: rgba(153, 153, 153, 1) !important;
+			
+			.company-info {
+				.logo {
+					display: block;
+					width: 28rpx;
+					height: 28rpx;
+					border: 1px solid rgba(240, 240, 240, 1);
+					border-radius: 28rpx;
+					margin-right: 8rpx;
+				}
+				.company-name {
+					flex: 1;
+					width: 0;
+					color: rgba(156, 164, 171, 1);
+					font-size: 20rpx;
+					font-weight: 400;
+					line-height: 40rpx;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
 			}
 			
-			.job-details {
-				color: rgba(153, 153, 153, 1) !important;
+			.buttons {
+				margin-top: 12rpx;
+				.button {
+					height: 60rpx;
+					padding: 16rpx 32rpx;
+					box-sizing: border-box;
+					border-radius: 60rpx;
+					border: 1px solid #fff;
+					font-size: 24rpx;
+					font-weight: 500;
+					line-height: 24rpx;
+					margin-right: 16rpx;
+					
+					&.primary {
+						color: #fff;
+						background: rgba(1, 107, 246, 1);
+						border-color: rgba(1, 107, 246, 1);
+					}
+					
+					&.plain {
+						color: rgba(1, 107, 246, 1);
+						background-color:  rgba(1, 107, 246, 0.1);
+						border-color:  rgba(1, 107, 246, 0.1);
+					}
+					
+					&.info {
+						color: #999;
+						border-color: #E8E8E8;
+					}
+				}
 			}
 		}
 	}
@@ -409,28 +594,24 @@ page {
 	}
 }
 
-.bottom-action {
-	padding: 30rpx 40rpx;
-	background: #ffffff;
-	
-	.publish-btn {
-		width: 100%;
-		height: 88rpx;
-		background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
-		border-radius: 44rpx;
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		
-		.publish-text {
-            color: rgba(255, 255, 255, 1);
-            font-family: DM Sans;
-            font-size: 32rpx;
-            font-weight: 400;
-            line-height: 48rpx;
-            letter-spacing: 0%;
-            text-align: center;
-		}
-	}
+.button-section {
+	padding: 28rpx 40rpx;
+	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+}
+.submit-btn {
+	flex-shrink: 0;
+	border-radius: 999px;
+	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+	background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
+	color: rgba(255, 255, 255, 1);
+	font-family: DM Sans;
+	font-size: 32rpx;
+	font-weight: 400;
+	line-height: 48rpx;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	padding: 16rpx 32rpx;
+	box-sizing: border-box;
 }
 </style>

+ 73 - 22
pages/my/businessLicense.vue

@@ -3,26 +3,28 @@
     <view class="roles-content">
       <view class="content">
         <view class="title">Hi,欢迎加入亿职赞</view>
-        <view class="desc">填写招聘信息,开启高效招聘方式</view>
+        <view class="desc">填写企业信息,开启高效招聘方式</view>
         <view class="title-second">公司营业执照名称</view>
         <view class="input-box">
-          <u-input
-            placeholder="请输入您所属公司营业执照上的公司名称"
-            v-model="companyName"
-            clearable
-            class="custom-input"
-            maxlength="46"
-			@input="handleInput"
-          >
-			<template #prefix>
-	              <u-icon name="phone" size="36rpx" color="#999" marginRight="20rpx"></u-icon>
+			<u-input
+				placeholder="请输入您所属公司营业执照上的公司名称"
+				v-model="companyName"
+				clearable
+				class="custom-input"
+				maxlength="46"
+				@input="handleInput"
+			>
+				<template #prefix>
+					<u-icon name="phone" size="36rpx" color="#999" marginRight="20rpx"></u-icon>
 	            </template>
-          </u-input>
-		 	    <view class="word-count" style="margin-right: 20rpx; color: #999; font-size: 22rpx;">
-		 			         {{ currentLength }} / 46
-		 	 </view>
-	
+			</u-input>
+		 	<view class="word-count" style="margin-right: 20rpx; color: #999; font-size: 22rpx;">
+		 		{{ currentLength }} / 46
+		 	</view>
         </view>
+		
+		<view class="join-btn">加入已认证的公司</view>
+		
         <view class="desc-txt">
           <view class="desc-txt-item">注意事项:</view>
           <view class="desc-txt-item">1.请输入您所述公司营业执照上的公司名称</view>
@@ -33,7 +35,10 @@
         <image src="@/static/images/my/join-yzz.svg" mode="scaleToFill" />
       </view>
     </view>
-    <view class="next-btn" @click="goPeopleNumber">下一步</view>
+	
+	<view class="fixed-section">
+		<u-button type="primary" class="submit-btn" @click="goPeopleNumber">下一步</u-button>
+	</view>
   </view>
   
 </template>
@@ -43,7 +48,8 @@ export default {
   data() {
     return {
 		companyName: '', 
-		currentLength: 0 
+		currentLength: 0,
+		loading: false,
 	};
   },
   components: {
@@ -58,14 +64,12 @@ export default {
       }
     },
   methods: {
-    goBusinessLicense() {
-      uni.navigateTo({ url: "/pages/my/businessLicense" });
-    },
     goPeopleNumber(){
 		console.log(this.companyName)
 		
 		if(this.companyName&& this.currentLength>6){
-			uni.navigateTo({ url: '/pages/my/peopleNumber?companyName='+this.companyName})
+			// uni.navigateTo({ url: '/pages/my/peopleNumber?companyName='+this.companyName})
+			this.createCompany()
 		}else{
 			uni.showToast({
 				title: '请完整输入营业执照名称',
@@ -79,10 +83,49 @@ export default {
 	handleInput(value) {
 	  this.currentLength = value.length;
 	},
+	// 创建公司
+	createCompany() {
+		if (this.loading) return
+		this.loading = true
+		
+		uni.showLoading({
+			title: '创建中'
+		})
+		this.$Request
+			.postJson('/app/company/insertCompany', {
+				companyAllName: this.companyName,
+				companyPeople: '',
+			})
+			.then(res => {
+				if (res.code === 0) {
+					// 跳转至招聘岗位页面
+					uni.navigateTo({
+						url: `/pages/my/jobPosting?companyName=${this.companyName}&companyId=${res.data.companyId}&isBack=${false}&status=${res.data.status}`
+					})
+				} else {
+					uni.showToast({
+						title: res.msg || '提交失败,请重试',
+						icon: 'none'
+					})
+				}
+				uni.hideLoading()
+				this.loading = false
+			})
+			.catch(() => {
+				uni.hideLoading()
+				this.loading = false
+				uni.showToast({
+					title: '网络异常,请稍后重试',
+					icon: 'none'
+				})
+			})
+	}
   },
 };
 </script>
 <style lang="scss" scoped>
+@import '@/common.scss';
+	
 .input-box{
     width: 100%;
     padding: 16rpx 0 32rpx 0;
@@ -213,4 +256,12 @@ export default {
     align-items: center;
   }
 }
+
+.join-btn {
+	width: 100%;
+	color: rgba(1, 107, 246, 1);
+	font-size: 32rpx;
+	line-height: 1;
+	margin-bottom: 32rpx;
+}
 </style>

+ 36 - 17
pages/my/index.vue

@@ -22,11 +22,12 @@
 					<view class="info flex justify-center"
 						:style="{ paddingTop: token && XCXIsSelect != '否' ? '0' : '100rpx' }">
 						<view class="info-box">
-							<view @click="goNav('/pages/my/onlineResume')"
+							<view @click="goNav('/package/jobIntention/basicInfo')"
 								class="info-box-header flex justify-between align-end">
 								<view class="flex align-center">
 									<view class="info-box-header-l">
-										<image :src="avatar ? avatar : '@/static/images/logo.jpg'" mode="aspectFill"></image>
+										<image :src="avatar ? avatar : '../../static/images/logo.jpg'" mode="aspectFill"></image>
+										<view class="info-tip" v-if="score < 20">待完善</view>
 									</view>
 									<view class="info-box-header-r">
 										<view class="info-box-header-r-name flex align-center">
@@ -45,7 +46,7 @@
 									</view>
 								</view>
 								<!-- 简历完善度 -->
-								<view class="resume-progress">
+								<view class="resume-progress" v-if="token">
 									<view class="text-tip flex align-center justify-between">
 										<text>简历完善度</text>
 										<text>{{ score }}%</text>
@@ -172,6 +173,7 @@
 										@click="goNav('/pages/my/onlineResume')">
 										<image src="../../static/images/index/qwzn.svg" mode=""></image>
 										<view class=""> 在线简历 </view>
+										<view class="info-tip" v-if="score < 20">待完善</view>
 									</view>
 									<view class="jobServer-box-btn-item flex justify-center flex-wrap"
 										@click="goNav('/pages/my/attachment')">
@@ -337,7 +339,7 @@
 							</view>
 							<view class="info-box-header flex align-center" @click="goNav('/pages/my/userinfo')">
 								<view class="info-box-header-l">
-									<image :src="avatar ? avatar : '../../static/logo.png'" mode="aspectFill"></image>
+									<image :src="avatar ? avatar : '../../static/images/logo.jpg'" mode="aspectFill"></image>
 								</view>
 								<view class="info-box-header-r">
 									<view class="info-box-header-r-name flex align-center">
@@ -761,7 +763,7 @@
 				<view class="pop-image" v-if="userType == 2">
 					<image src="/static/images/qy.jpg"></image>
 				</view>
-				<u-button @click="showNotice = false" class="pop-btn">确认</u-button>
+				<u-button @click="handleCloseNotice" class="pop-btn">确认</u-button>
 			</view>
 		</u-popup>
 	</view>
@@ -822,7 +824,7 @@ export default {
 				} */
 			],
 			userType: 1,
-			avatar: "../../static/logo.png",
+			avatar: "",
 			userName: "登录",
 			XCXIsSelect: "否",
 			userId: "",
@@ -882,17 +884,15 @@ export default {
 			that.token = "";
 			that.isLogin = true;
 			that.userName = "登录";
-			that.avatar = "../../static/logo.png";
+			that.avatar = "";
 			that.isVip = false;
 			that.companyInfo = '';
 		})
 		console.log(uni.getStorageSync("userType"))
 		this.$queue.changeTabbar(uni.getStorageSync("userType") || 1);
-		setTimeout(()=>{
-			if(this.userType){
-				this.showNotice = true;
-			}
-		},1000)
+		if (!this.$queue.getData('hadShowPoster')) {
+			this.showNotice = true
+		}
 	},
 	onUnload() {
 		uni.$off('changeRole')
@@ -1362,7 +1362,7 @@ export default {
 						});
 						that.isLogin = true;
 						that.userName = "登录";
-						that.avatar = "../../static/logo.png";
+						that.avatar = "";
 						that.isVip = false;
 						// uni.reLaunch({
 						// 	url: '/pages/public/selectIdentity/selectIdentity'
@@ -1702,10 +1702,14 @@ export default {
 			// 		}
 			// 	}
 			// })
-			uni.navigateTo({
-				url: "/pages/public/login",
-			});
+			this.$queue.toLogin()
 		},
+		
+		// 关闭海报弹窗
+		handleCloseNotice() {
+			this.$queue.setData('hadShowPoster', '1')
+			this.showNotice = false
+		}
 	},
 };
 </script>
@@ -1715,6 +1719,19 @@ page {
 	background-color: #f5f7fa;
 }
 
+// 完善提示
+.info-tip {
+	position: absolute;
+	top: 0;
+	right: -30rpx;
+	padding: 8rpx;
+	border-radius: 4px;
+	background: rgba(255, 0, 0, 1);
+	font-size: 20rpx;
+	line-height: 1;
+	color: #fff;
+}
+
 .report-content {
 	/* content */
 	width: 540rpx;
@@ -2052,7 +2069,8 @@ page {
 			// height: 90rpx;
 
 			.info-box-header-l {
-				margin-right: 20rpx;
+				position: relative;
+				margin-right: 32rpx;
 
 				image {
 					width: 128rpx;
@@ -2212,6 +2230,7 @@ page {
 			}
 
 			.jobServer-box-btn-item {
+				position: relative;
 				width: 120rpx;
 				color: rgba(56, 58, 63, 1);
 				font-family: DM Sans;

+ 4 - 1
pages/my/jobApplicant/guidePage.vue

@@ -36,7 +36,8 @@
 						});
 					}
 					uni.navigateTo({
-						url: '/pages/my/jobApplicant/welcomePage'
+						// url: '/pages/my/jobApplicant/welcomePage'
+						url: '/pages/public/improvePrompt'
 					})
 				})
 				
@@ -66,6 +67,8 @@
 					});
 			},
 			gotocompany() {
+				// 获取公司状态,用户新用户进入APP判断是否进入选择角色页面
+				this.$queue.setData('companyStatus', this.companyinfo.status)
 
 				var that=this
 				//修改当前用户身份

+ 8 - 3
pages/my/jobApplicant/startPage.vue

@@ -97,9 +97,14 @@ export default {
       // uni.navigateTo({
       //   url: "/pages/my/jobApplicant/login",
       // });
-      uni.navigateTo({
-        url: "/pages/public/login",
-      });
+	  // #ifdef H5
+		  this.$queue.toLogin('navigateTo')
+	  // #endif
+	  // #ifdef APP-PLUS
+		  uni.navigateTo({
+			url: '/pages/public/loginV2?isFirst=1'
+		  })
+	  // #endif
     },
     // 暂停自动播放
     pauseAutoplay() {

+ 150 - 13
pages/my/jobPosting.vue

@@ -5,8 +5,24 @@
 			<view class="content">
 				<view class="title">{{ isDisabled ? '编辑【' + jobInfo.ruleClassifyName + '】' : '发布社招' }}岗位</view>
 				<view class="desc">{{ companyName }}</view>
-				<view class="step">
+				<!-- <view class="step">
 					<u-steps :list="numList" mode="number" :current="0"></u-steps>
+				</view> -->
+				<view class="step-progress flex align-center">
+					<view class="circle-wrapper">
+						<view class="check-icon check-active">
+							<u-icon name="checkbox-mark" color="#fff" size="20"></u-icon>
+						</view>
+						<view class="step-progress-text">填写基本信息</view>
+					</view>
+					<view class="line blue"></view>
+					<view class="line"></view>
+					<view class="circle-wrapper">
+						<view class="check-icon">
+							<u-icon name="checkbox-mark" color="#fff" size="20"></u-icon>
+						</view>
+						<view class="step-progress-text">填写岗位要求</view>
+					</view>
 				</view>
 
 				<view class="check-title-big">招聘模式</view>
@@ -33,18 +49,28 @@
 				</view>
 
 
-				<view class="check-title">招聘类型</view>
+				<!-- <view class="check-title">招聘类型</view>
 				<view class="check-box" :class="{ disabled: isDisabled }">
 					<view class="check-item" :class="{ 'is-check': check == index }" v-for="(item, index) in peopleList"
 						:key="index" @click="!isDisabled && checkPeople(index)">{{ item }}</view>
-				</view>
+				</view> -->
 				<view class="check-title-big">招聘职位</view>
 				<view class="check-select" @click="!isDisabledid && goJob()" :class="{ disabled: isDisabledid }">
 					<view class="select-txt">{{ jobInfo.ruleClassifyName }}</view>
 					<u-icon name="arrow-down" color="#D3D3D6" size="22"></u-icon>
 				</view>
 				<view class="check-title-big">岗位描述</view>
-				<view class="check-select" @click="goJobContent">
+				<view class="check-sub-title">招聘岗位、招聘类型等发布后不可修改</view>
+				<view class="job-desc-wrapper">
+					<view class="placeholder" v-if="!text">
+						<view class="placeholder-text">请勿填写QQ、微信、电话等联系方式及特殊符号,性别歧视词、违反劳动法相关内容,否则有可能导致账号封禁。</view>
+						<view class="placeholder-text">1. 工作内容</view>
+						<view class="placeholder-text">2. 任务要求</view>
+						<view class="placeholder-text">3. 特别说明</view>
+					</view>
+					<textarea type="text" v-model="text" maxlength="2000" class="job-desc-input" />
+				</view>
+				<!-- <view class="check-select" @click="goJobContent">
 				    <view class="select-txt" v-if="textList.length > 0">
 				      <view v-for="(item, index) in textList" :key="index">
 				        {{ item }}
@@ -52,15 +78,18 @@
 				    </view>
 					<view class="select-txt" v-else>介绍工作内容、职位要求</view>
 				    <u-icon name="arrow-down" color="#D3D3D6" size="22"></u-icon>
-				  </view>
+				  </view> -->
 				<!-- <view class="check-select" @click="goJobContent">
 					<view class="select-txt">{{ this.text ? this.text : "介绍工作内容、职位要求" }}</view>
 					<u-icon name="arrow-down" color="#D3D3D6" size="22"></u-icon>
 				</view> -->
-				<view class="txt-desc">注:职位名称、职位类型等发布后不可修改</view>
+				<!-- <view class="txt-desc">注:职位名称、职位类型等发布后不可修改</view> -->
 			</view>
 		</view>
-		<view class="submit-btn" @click="goJobPostingSecond">下一步</view>
+		<view class="button-section">
+			<view class="link-btn" @click="handleToIndex">稍后发布</view>
+			<view class="submit-btn" @click="goJobPostingSecond">下一步</view>
+		</view>
 	</view>
 </template>
 <script>
@@ -441,6 +470,13 @@ export default {
 			// 	});
 
 
+		},
+		
+		// 跳转至公司认证页面
+		handleToIndex() {
+			uni.navigateTo({
+				url: `/package/jobIntention/companyImg?companyName=${this.companyName}&companyId=${this.companyId}`
+			})
 		}
 	},
 };
@@ -469,13 +505,54 @@ export default {
 			flex-direction: column;
 			align-items: center;
 			justify-content: center;
+			
+			// 步骤条
+			.step-progress {
+				padding: 20rpx 0 60rpx;
+				.circle-wrapper {
+					position: relative;
+					.check-icon {
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						width: 40rpx;
+						height: 40rpx;
+						border: 1px solid rgba(198, 198, 198, 1);
+						border-radius: 40rpx;
+					}
+					.check-active {
+						background: #016bf6;
+						border-color: #016bf6;
+					}
+					.step-progress-text {
+						position: absolute;
+						left: 50%;
+						bottom: -36rpx;
+						width: 150rpx;
+						font-size: 20rpx;
+						font-weight: 500;
+						line-height: 32rpx;
+						color: rgba(111, 111, 111, 1);
+						transform: translateX(-50%);
+						text-align: center;
+					}
+				}
+				.line {
+					width: 220rpx;
+					height: 1px;
+					background: rgba(198, 198, 198, 1);
+				}
+				.blue {
+					background: rgba(1, 107, 246, 1);
+				}
+			}
 
 			.title {
 				color: #333;
 				width: 100%;
 				font-family: DM Sans;
-				font-size: 40rpx;
-				font-weight: 600;
+				font-size: 48rpx;
+				font-weight: 700;
 			}
 
 			.desc {
@@ -503,11 +580,11 @@ export default {
 			}
 
 			.check-title-big {
-				color: rgba(58, 57, 67, 1);
+				color: rgba(31, 44, 55, 1);
 				font-family: DM Sans;
 				font-size: 36rpx;
 				font-weight: 500;
-				line-height: 48rpx;
+				line-height: 44rpx;
 				width: 100%;
 				padding: 20rpx 0;
 				box-sizing: border-box;
@@ -526,6 +603,49 @@ export default {
 
 				.select-txt {}
 			}
+			
+			// 岗位描述
+			.check-sub-title {
+				width: 100%;
+				color: rgba(102, 102, 102, 1);
+				font-size: 20rpx;
+				font-weight: 400;
+				line-height: 26rpx;
+				margin-bottom: 20rpx;
+			}
+			.job-desc-wrapper {
+				position: relative;
+				width: 100%;
+				height: 220rpx;
+				padding: 20rpx 28rpx;
+				border-radius: 6px;
+				background: rgba(153, 153, 153, 0.05);
+				.job-desc-input {
+					position: relative;
+					width: 100%;
+					height: 180rpx;
+					font-size: 20rpx;
+					line-height: 26rpx;
+				}
+				.placeholder {
+					position: absolute;
+					left: 0;
+					top: 0;
+					width: 100%;
+					height: 200rpx;
+					padding: 24rpx 28rpx;
+					box-sizing: border-box;
+					.placeholder-text {
+						color: rgba(153, 153, 153, 1);
+						font-size: 20rpx;
+						font-weight: 400;
+						line-height: 26rpx;
+						&:first-child {
+							margin-bottom: 20rpx;
+						}
+					}
+				}
+			}
 
 			/* 禁用状态样式 */
 			.check-select.disabled {
@@ -563,8 +683,14 @@ export default {
 
 			.radioseletvi {
 				position: relative;
+				padding: 0;
 				::v-deep uni-radio-group{
 					flex: 1;
+					.radio {
+						&:first-child {
+							margin-top: 0;
+						}
+					}
 				}
 			}
 
@@ -625,10 +751,14 @@ export default {
 		box-sizing: border-box;
 	}
 
+	.button-section {
+		padding: 28rpx 40rpx;
+		box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+	}
 	.submit-btn {
 		flex-shrink: 0;
 		border-radius: 999px;
-		box-shadow: 0px 2px 4px 0px rgba(9, 196, 116, 0.3);
+		box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
 		background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
 		color: rgba(255, 255, 255, 1);
 		font-family: DM Sans;
@@ -640,7 +770,14 @@ export default {
 		align-items: center;
 		padding: 16rpx 32rpx;
 		box-sizing: border-box;
-		margin: 60rpx 20rpx;
+	}
+	.link-btn {
+		text-align: center;
+		color: rgba(1, 107, 246, 1);
+		font-size: 16px;
+		font-weight: 400;
+		line-height: 16px;
+		margin-bottom: 32rpx;
 	}
 
 	.txt-desc {

+ 135 - 29
pages/my/jobPostingSecond.vue

@@ -1,12 +1,24 @@
 <template>
 	<view class="switch-roles">
-		<nav-bar title="选择职位要求" color="#000"></nav-bar>
+		<nav-bar title="位要求" color="#000"></nav-bar>
 		<view class="roles-content">
 			<view class="content">
 				<view class="title">继续填写</view>
-				<view class="desc">“{{ companyName }}”的职位要求</view>
-				<view class="step">
-					<u-steps :list="numList" mode="number" :current="2"></u-steps>
+				<view class="step-progress flex align-center">
+					<view class="circle-wrapper">
+						<view class="check-icon check-active">
+							<u-icon name="checkbox-mark" color="#fff" size="20"></u-icon>
+						</view>
+						<view class="step-progress-text">填写基本信息</view>
+					</view>
+					<view class="line blue"></view>
+					<view class="line blue"></view>
+					<view class="circle-wrapper">
+						<view class="check-icon check-active">
+							<u-icon name="checkbox-mark" color="#fff" size="20"></u-icon>
+						</view>
+						<view class="step-progress-text">填写岗位要求</view>
+					</view>
 				</view>
 
 				<!-- 经验要求 -->
@@ -26,18 +38,14 @@
 				<!-- 薪资范围 -->
 				<view class="check-title-big">薪资范围</view>
 				<view class="check-select" @click="showMoney = true">
-					<view class="select-txt">{{ selectedSalary + '——' + salaryTimes || "请选择合理的薪资范围" }}</view>
+					<view class="select-txt">
+						<template v-if="selectedSalary">{{ selectedSalary + ' · ' + salaryTimes }}</template>
+						<template v-else>请选择合理的薪资范围</template>
+					</view>
 					<u-icon name="arrow-down" color="#D3D3D6" size="22"></u-icon>
 				</view>
-
-				<!-- 福利待遇 -->
-				<view class="check-title-big">福利待遇(选填)</view>
-				<view class="check-select" @click="goFund">
-					<view class="select-txt">{{ welfareTag || "请填写奖金绩效" }}</view>
-					<u-icon name="arrow-down" color="#D3D3D6" size="22"></u-icon>
-				</view>
-
-				<!-- 职位关键词 -->
+				
+				<!-- 跨境标签 -->
 				<view class="check-title-big">跨境标签</view>
 				<view class="check-select" @click="goJobSkill">
 					<view class="select-txt">{{ positionTag && positionTag.length > 0 ? positionTag.join(',') :
@@ -45,6 +53,13 @@
 					<u-icon name="arrow-down" color="#D3D3D6" size="22"></u-icon>
 				</view>
 
+				<!-- 福利待遇 -->
+				<view class="check-title-big">福利待遇(选填)</view>
+				<view class="check-select" @click="goFund">
+					<view class="select-txt">{{ welfareValue || "请填写奖金绩效" }}</view>
+					<u-icon name="arrow-down" color="#D3D3D6" size="22"></u-icon>
+				</view>
+
 				<!-- 工作地址 -->
 				<view class="check-title-big">工作地址</view>
 				<view class="check-select" @click="goAddAddress">
@@ -59,7 +74,10 @@
 			</view>
 		</view>
 
-		<view class="submit-btn" @click="submitPost">发布</view>
+		<view class="button-section">
+			<view class="link-btn" @click="handleToIndex">稍后发布</view>
+			<view class="submit-btn" @click="submitPost">发布</view>
+		</view>
 
 		<!-- 经验选择器 -->
 		<u-select v-model="showExper" :list="listExper" title="请选择经验" @confirm="onExperConfirm"
@@ -85,6 +103,7 @@ export default {
 			selectedLevel: "", // 学历
 			selectedSalary: "", // 薪资范围
 			welfareTag: "", // 福利待遇
+			welfareText: '', // 福利待遇描述
 			positionTag: "", // 职位关键词
 			stationName: [],
 			companyName: "",
@@ -243,6 +262,11 @@ export default {
 	components: {
 		navBar,
 	},
+	computed: {
+		welfareValue() {
+			return this.welfareTag + this.welfareText
+		}
+	},
 	onLoad(options) {
 		console.log(options)
 		if(options.dataInfo){
@@ -277,6 +301,7 @@ export default {
 			this.selectedSalary = workData.salaryRange || '';
 			this.salaryTimes = workData.salaryTimes || '';
 			this.welfareTag = workData.welfareTag || '';
+			this.welfareText = workData.welfareText || '';
 			this.positionTag = workData.positionTag ? workData.positionTag.split (',') : [];
 			this.address = workData.address || this.companyAddressInfo.address;
 			this.stationName.fullText = workData.address || this.companyAddressInfo.fullText, //地址
@@ -291,7 +316,8 @@ export default {
 		}
 		this.fundListener = uni.$on("fundData", (data) => {
 			console.log("接收到的福利数据:", data);
-			this.welfareTag = data; // 赋值给当前页面变量
+			this.welfareText = data.text
+			this.welfareTag = data.tagText; // 赋值给当前页面变量
 		});
 		this.fundListener = uni.$on("skillsUpdated", (data) => {
 			console.log("接收到的职位技能数据:", data);
@@ -326,7 +352,7 @@ export default {
 	onUnload() {
 		// 页面关闭时移除监听,避免内存泄漏
 		if (this.fundListener) {
-			uni.$off("fundData", this.fundListener);
+			uni.$off("fundData");
 		}
 	},
 	methods: {
@@ -365,6 +391,7 @@ export default {
 					this.selectedLevel = res.data.education
 					this.selectedSalary = res.data.salaryRange
 					this.welfareTag = res.data.welfareTag
+					this.welfareText = res.data.welfareText
 					this.positionTag = res.data.positionTag
 					this.address = res.data.address
 					this.stationName.fullText = res.data.address
@@ -417,6 +444,11 @@ export default {
 
 		// 处理薪资范围选择(核心修复)
 		onMoneyConfirm(selectedItems) {
+			let minSalary = selectedItems[0].value.replace('k', '')
+			let maxSalary = selectedItems[1].value.replace('k', '')
+			if (Number(minSalary) > Number(maxSalary)) {
+				return this.$queue.showToast('最低薪资不能大于最高薪资')
+			}
 			// 多列选择器返回数组,取每列选中的label拼接
 			console.log(selectedItems)
 			if (Array.isArray(selectedItems) && selectedItems.length >= 2) {
@@ -431,12 +463,16 @@ export default {
 
 		// 跳转页面并接收参数(福利待遇)
 		goFund() {
-			let tag;
+			let tag = ''
+			let text = ''
 			if (this.welfareTag) {
 				tag = this.welfareTag;
 			}
+			if (this.welfareText) {
+				text = this.welfareText
+			}
 			uni.navigateTo({
-				url: "/package/jobIntention/fund?tag=" + tag,
+				url: `/package/jobIntention/fund?tag=${encodeURIComponent(tag)}&text=${encodeURIComponent(text)}`,
 				events: {
 					// 监听子页面发送的福利数据
 					fundData: (data) => {
@@ -533,6 +569,7 @@ export default {
 				education: this.selectedLevel, //学历
 				salaryRange: this.selectedSalary, //薪资范围
 				welfareTag: this.welfareTag, //福利相关标签
+				welfareText: this.welfareText,
 				positionTag: str, //职位技能
 				address: this.stationName.fullText || this.address, //地址
 				lat: this.stationName.latitude,
@@ -558,7 +595,7 @@ export default {
 						//这里要选择跳转,第一次是要跳公司页,如果是二次就到职位管理页
 						if (this.status == 1) { //审核中
 							uni.navigateTo({
-								url: "/package/jobIntention/company?companyName=" + this.companyName +
+								url: "/package/jobIntention/companyImg?companyName=" + this.companyName +
 									'&companyId=' + this.companyId
 							});
 						} else if (this.status == 2) {//审核通过
@@ -594,6 +631,13 @@ export default {
 					});
 				});
 		},
+		
+		// 跳转至公司认证页面
+		handleToIndex() {
+			uni.navigateTo({
+				url: `/package/jobIntention/companyImg?companyName=${this.companyName}&companyId=${this.companyId}`
+			})
+		}
 	},
 };
 </script>
@@ -616,13 +660,14 @@ export default {
 		overflow: auto;
 
 		.content {
-			padding: 40rpx;
+			padding: 32rpx 40rpx;
 
 			.title {
 				color: #333;
-				font-size: 40rpx;
-				font-weight: 600;
-				margin-bottom: 10rpx;
+				font-size: 48rpx;
+				font-weight: 700;
+				margin-bottom: 40rpx;
+				line-height: 60rpx;
 			}
 
 			.desc {
@@ -630,6 +675,49 @@ export default {
 				font-size: 28rpx;
 				margin-bottom: 20rpx;
 			}
+			
+			// 步骤条
+			.step-progress {
+				display: flex;
+				justify-content: center;
+				padding: 20rpx 0 60rpx;
+				.circle-wrapper {
+					position: relative;
+					.check-icon {
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						width: 40rpx;
+						height: 40rpx;
+						border: 1px solid rgba(198, 198, 198, 1);
+						border-radius: 40rpx;
+					}
+					.check-active {
+						background: #016bf6;
+						border-color: #016bf6;
+					}
+					.step-progress-text {
+						position: absolute;
+						left: 50%;
+						bottom: -36rpx;
+						width: 150rpx;
+						font-size: 20rpx;
+						font-weight: 500;
+						line-height: 32rpx;
+						color: rgba(111, 111, 111, 1);
+						transform: translateX(-50%);
+						text-align: center;
+					}
+				}
+				.line {
+					width: 220rpx;
+					height: 1px;
+					background: rgba(198, 198, 198, 1);
+				}
+				.blue {
+					background: rgba(1, 107, 246, 1);
+				}
+			}
 
 			.check-title-big {
 				color: #3a3943;
@@ -668,15 +756,33 @@ export default {
 		padding: 32rpx 0;
 	}
 
+	.button-section {
+		padding: 28rpx 40rpx;
+		box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+	}
 	.submit-btn {
-		margin: 60rpx 20rpx;
-		padding: 16rpx 32rpx;
+		flex-shrink: 0;
 		border-radius: 999px;
-		background: linear-gradient(90deg, #0d27f7, #13c1ea);
-		color: #fff;
+		box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+		background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
+		color: rgba(255, 255, 255, 1);
+		font-family: DM Sans;
 		font-size: 32rpx;
-		text-align: center;
+		font-weight: 400;
 		line-height: 48rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 16rpx 32rpx;
+		box-sizing: border-box;
+	}
+	.link-btn {
+		text-align: center;
+		color: rgba(1, 107, 246, 1);
+		font-size: 16px;
+		font-weight: 400;
+		line-height: 16px;
+		margin-bottom: 32rpx;
 	}
 }
 </style>

+ 56 - 0
pages/my/joinCompany.vue

@@ -0,0 +1,56 @@
+<template>
+	<view class="page-container">
+		<view class="content">
+			<view class="title">Hi,欢迎加入亿职赞</view>
+			<view class="desc">填写企业信息,开启高效招聘方式</view>
+			<view class="sub-title">公司名称</view>
+		</view>
+		
+		<view class="fixed-section">
+			<u-button type="primary" class="submit-btn" @click="handleNext">下一步</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			};
+		},
+		methods: {
+			handleNext() {}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/common.scss';
+	.page-container {
+		font-family: DM Sans;
+		.content {
+			padding: 60rpx 40rpx;
+			.title {
+				color: rgba(29, 33, 41, 1);
+				font-size: 36rpx;
+				font-weight: 500;
+				line-height: 44rpx;
+				margin-bottom: 20rpx;
+			}
+			.desc {
+				color: rgba(102, 102, 102, 1);
+				font-size: 24rpx;
+				font-weight: 400;
+				line-height: 32rpx;
+				margin-bottom: 20rpx;
+			}
+			.sub-title {
+				color: rgba(31, 44, 55, 1);
+				font-size: 28rpx;
+				font-weight: 500;
+				line-height: 44rpx;
+			}
+		}
+	}
+</style>

+ 179 - 15
pages/my/onlineResume.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="online-resume" :class="{ pb : scene == 1 }">
-		<navBar title="在线简历" color="#000" />
+		<navBar title="完善在线简历" />
 		<view class="page-content">
 			<!-- 用户信息区域 -->
 			<view class="user-profile">
@@ -17,7 +17,7 @@
 					<view class="contact-item">
 						<image src="../../static/images/index/phone.svg" style="width: 24rpx;height: 24rpx;" mode="">
 						</image>
-						<text class="contact-text">{{ detail.resumeList && detail.resumeList.resumesPhone }}</text>
+						<text class="contact-text">{{ detail.resumesPhone }}</text>
 					</view>
 					<view class="contact-item">
 						<image src="../../static/images/index/wx.svg" style="width: 24rpx;height: 24rpx;" mode="">
@@ -281,12 +281,30 @@
 				<view class="button" @click="toDownload">保存并开启求职之旅</view>
 			</view>
 			<view class="button-section" v-else>
-				<view class="text-btn">稍后填写</view>
+				<view class="text-btn" v-if="score < 20 && score !== -1" @click="handleStartWork">稍后填写</view>
 				<view class="flex buttons">
 					<view class="btn primary-btn" @click="toPreviewResume">预览/导出</view>
-					<view class="btn main-btn">开启求职之旅</view>
+					<view class="btn main-btn" @click="handleStartWork">开启求职之旅</view>
 				</view>
 			</view>
+			
+			<u-popup v-model="showResumesAnalysis" mode="bottom" border-radius="14" :mask-close-able="false">
+				<view class="resume-popup">
+					<view class="close-button flex align-center justify-center" @click="showResumesAnalysis = false">
+						<u-icon name="close" color="#fff" size="22" />
+					</view>
+					<view class="title">从附件简历中解析出<text>5段</text>有效信息</view>
+					<view class="tip">无需手动填写,可直接添加到在线简历中</view>
+					<view class="info-list">
+						<view class="info-item flex align-center" v-for="(item, index) in analysisList" :key="index">
+							<view class="label">{{ item.title }}</view>
+							<view class="border"></view>
+							<view class="content">{{ item.value }}</view>
+						</view>
+					</view>
+					<view class="button">立即同步</view>
+				</view>
+			</u-popup>
 		</view>
 	</view>
 </template>
@@ -335,7 +353,27 @@ export default {
 			showAllExpToggle: false,
 			showChecked: false, // 是否对外展示简历
 			loadingSwitch: false,
-			controlStatus: false
+			controlStatus: false,
+			score: -1,
+			showResumesAnalysis: false,
+			analysisList: [
+				{
+					title: '基本信息',
+					value: '噶撒旦发生的噶手动阀手动阀手动阀噶撒旦发生的噶撒旦发生的噶手动阀手动阀手动阀噶撒旦发生的',
+				},
+				{
+					title: '工作经历',
+					value: '噶撒旦发生的噶手动阀手动阀手动阀噶撒旦发生的',
+				},
+				{
+					title: '教育经历',
+					value: '无法覆盖噶撒旦发生',
+				},
+				{
+					title: '个人优势',
+					value: '刚刚噶士大夫撒打赏',
+				},
+			]
 		}
 	},
 	computed: {
@@ -370,10 +408,13 @@ export default {
 		if (options.scene) {
 			this.scene = options.scene
 		}
+		// 判断是否有fileId,如果有则是从上传简历文件页面跳转
+		if (options.fileId) {
+			// this.getAiResumesAnalysis(options.fileId)
+		}
 		this.getData()
 		var that = this
 		uni.$on('updateResume', () => {
-			console.log(2222222222222)
 			that.getData()
 		})
 
@@ -431,6 +472,7 @@ export default {
 					that.hasEcommerceExperience = res.data.resumeList && res.data.resumeList.ifExp ? true :
 						false
 					that.detail.degree = that.$queue.getHighestEducation(res.data.eduList)
+					this.score = res.data?.resumeList?.score || 0
 
 					res.data.workExps.forEach(function (item) {
 						console.log(item.type);
@@ -596,8 +638,8 @@ export default {
 			});
 		},
 		changePreference(preference) {
-			if (intentionId == 0)
-				return;
+			// if (intentionId == 0)
+			// 	return;
 			var industry = preference == '' || preference == null ? '' : preference.join('/')
 			let data = {
 				intentionId,
@@ -712,6 +754,53 @@ export default {
 				.finally(() => {
 					uni.hideLoading()
 				})
+		},
+		
+		// 跳转首页
+		handleStartWork() {
+			uni.reLaunch({
+				url: '/pages/index/index'
+			})
+		},
+		
+		// 获取简历解析数据
+		getAiResumesAnalysis(fileId) {
+			this.$queue.showLoading('解析中')
+			// this.$Request
+			// 	.postT('/app/resumes/aiResumesAnalysis', {
+			// 		workflowId: '7594327567932309547',
+			// 		fileId
+			// 	}, '', 240000)
+			// 	.then(res => {
+			// 		console.log(res)
+			// 		uni.hideLoading()
+			// 	})
+			// 	.catch(err => {
+			// 		console.log(err)
+			// 		uni.hideLoading()
+			// 	})
+			uni.request({
+				url: 'https://yizhizan.edccc.cn/sqx_fast/app/resumes/aiResumesAnalysis',
+				header: {
+					'content-type': 'application/x-www-form-urlencoded', // 模拟 form-data
+					token: uni.getStorageSync("token"),
+				},
+				method: 'post',
+				data: {
+					workflowId: '7594327567932309547',
+					fileId
+				},
+				timeout: 120000,
+				success: (res) => {
+					console.log(res)
+					uni.hideLoading()
+				},
+				fail: (err) => {
+					console.log('0000000000')
+					console.log(err)
+					uni.hideLoading()
+				}
+			})
 		}
 	},
 	watch: {
@@ -733,7 +822,7 @@ export default {
 	height: 100vh;
 	display: flex;
 	flex-direction: column;
-	padding-bottom: 182rpx;
+	padding-bottom: 100rpx;
 }
 .pb {
 	padding-bottom: 160rpx;
@@ -765,16 +854,16 @@ export default {
 	text-align: center;
 
 	.avatar-container {
-		width: 196rpx;
-		height: 196rpx;
+		width: 98px;
+		height: 98px;
 		padding: 8rpx;
 		background-image: linear-gradient(90.00deg, rgba(13, 39, 247, 1),rgba(19, 193, 234, 1));
-		margin: 0 auto 8rpx;
+		margin: 0 auto 4rpx;
 		border-radius: 50%;
 
 		.user-avatar {
-			width: 180rpx;
-			height: 180rpx;
+			width: 90px;
+			height: 90px;
 			border-radius: 50%;
 		}
 	}
@@ -818,7 +907,7 @@ export default {
 // 内容区域
 .content-section {
 	font-family: DM Sans;
-	padding: 40rpx 30rpx 30rpx;
+	padding: 40rpx 30rpx 130rpx;
 	
 	.common-section {
 		background: #fff;
@@ -928,6 +1017,7 @@ export default {
 					font-size: 28rpx;
 					line-height: 32rpx;
 					color: #78828a;
+					word-break: break-all;
 				}
 			}
 			// 个人技能
@@ -1309,4 +1399,78 @@ export default {
 		}
 	}
 }
+
+// 解析简历popup
+.resume-popup {
+	position: relative;
+	padding: 70rpx 40rpx 40rpx;
+	.title {
+		font-size: 40rpx;
+		font-weight: 700;
+		line-height: 52rpx;
+		color: #333;
+		margin-bottom: 12rpx;
+		text {
+			color: #016bf6;
+		}
+	}
+	.tip {
+		font-size: 28rpx;
+		line-height: 36rpx;
+		color: #999999;
+	}
+	.info-list {
+		.info-item {
+			box-sizing: border-box;
+			border: 1px solid rgba(242, 242, 242, 1);
+			border-radius: 6px;
+			background: rgba(250, 250, 250, 1);
+			padding: 26rpx 24rpx;
+			margin-top: 40rpx;
+			.label {
+				font-size: 28rpx;
+				font-weight: 500;
+				line-height: 1;
+				color: rgba(51, 51, 51, 1);
+			}
+			.border {
+				width: 1px;
+				height: 20rpx;
+				margin: 0 20rpx;
+				background: rgba(234, 234, 234, 1);
+			}
+			.content {
+				flex: 1;
+				width: 0;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+				color: rgba(153, 153, 153, 1);
+				font-size: 28rpx;
+				line-height: 1;
+			}
+		}
+	}
+	.button {
+		width: 600rpx;
+		height: 88rpx;
+		border-radius: 88rpx;
+		background: linear-gradient(90.00deg, rgba(13, 39, 247, 1),rgba(19, 193, 234, 1));
+		font-size: 32rpx;
+		font-weight: 500;
+		line-height: 88rpx;
+		text-align: center;
+		color: #fff;
+		margin: 60rpx auto 0;
+	}
+	.close-button {
+		position: absolute;
+		top: 30rpx;
+		right: 40rpx;
+		width: 40rpx;
+		height: 40rpx;
+		border-radius: 40rpx;
+		background: rgba(230, 230, 230, 1);
+	}
+}
 </style>

+ 5 - 5
pages/my/setup.vue

@@ -356,7 +356,7 @@ export default {
 				title: "提示",
 				content: "确定退出登录吗?",
 				confirmColor: "#016BF6",
-				success: function (res) {
+				success: (res) => {
 					if (res.confirm) {
 						console.log("用户点击确定");
 						that.$queue.logout()
@@ -364,8 +364,8 @@ export default {
 						uni.showToast({
 							title: "退出成功!",
 							icon: "none",
-							success() {
-								uni.navigateBack()
+							success: () => {
+								this.$queue.toLogin()
 							}
 						});
 						/* that.isLogin = true;
@@ -420,8 +420,8 @@ export default {
 					uni.showToast({
 						title: "注销成功!",
 						icon: "none",
-						success() {
-							uni.navigateBack()
+						success: () => {
+							this.$queue.toLogin()
 						}
 					});
 				}else{

+ 1 - 3
pages/my/switchRoles.vue

@@ -116,9 +116,7 @@ export default {
 		// 		}
 		// 	}
 		// })
-		uni.navigateTo({
-			url: "/pages/public/login",
-		});
+		this.$queue.toLogin('navigateTo')
 	},
   },
 };

+ 42 - 104
pages/my/userinfo.vue

@@ -1,40 +1,26 @@
 <template>
-	<view>
+	<view class="page-container" :style="{ paddingTop: statusBarHeight + 'px' }">
 		<!-- 顶部导航栏 -->
-		<view class="navbar" :style="{ paddingTop: (12 + statusBarHeight) + 'px' }">
-			<view class="navbar-content">
-				<view class="navbar-left" @click="goBack">
-					<u-icon name="arrow-leftward" size="36" color="#333"></u-icon>
-				</view>
-				<view class="navbar-title">基本信息</view>
-				<view class="navbar-right"></view>
-			</view>
-		</view>
+		<navBar title="基本信息"></navBar>
 
 		<!-- <nav-bar title="基本信息"></nav-bar> -->
 
 		<view class="usermain">
 			<view class="usermain-item ">
-				<view class="usermain-item-title">头像(为了更换头像需要访问您的相册和摄像头)</view>
-				<view class="avatar-wrapper">
-					<!-- #ifdef MP-WEIXIN -->
-					<button open-type="chooseAvatar" class="avatar-button" @chooseavatar="onChooseAvatar">
-						<image style="width: 78rpx;height: 78rpx;border-radius: 50%"
-							:src="avatar ? avatar : '../../static/logo.png'"></image>
-					</button>
-					<!-- #endif -->
+				<view class="form-label">
+					<text class="required-mark">*</text>
+					<text class="usermain-item-title">头像</text>
+				</view>
+				<view class="avatar-wrapper flex align-center" @click="uploadImg()">
 					<!-- #ifndef MP-WEIXIN -->
-					<view @click="uploadImg()">
+					<view>
 						<image src="../../static/logo.png" v-if="avatar == null" mode=""
 							style="width: 78rpx;height: 78rpx;border-radius: 50%;"></image>
 						<image v-else :src="avatar" mode="" style="width: 78rpx;height: 78rpx;border-radius: 50%;">
 						</image>
 					</view>
 					<!-- #endif -->
-					<view class="edit-avatar-icon">
-						<image src="/static/images/index/Combined-Shape.svg" style="width: 32rpx;height: 32rpx;"
-							mode="scaleToFill"></image>
-					</view>
+					<view class="tip">重新上传</view>
 				</view>
 			</view>
 			<view class="usermain-item item-padding ">
@@ -73,7 +59,7 @@
 				<view class="usermain-item-title">联系方式</view>
 				<view class="contact-wrapper">
 					<text class="phone-display">{{ phoneWithMask }}</text>
-					<text class="modify-link" @click="modifyPhone">修改手机号</text>
+					<!-- <text class="modify-link" @click="modifyPhone">修改手机号</text> -->
 				</view>
 			</view>
 			<!-- <view class="usermain-item item-padding">
@@ -127,8 +113,8 @@
 				</view>
 			</view>
 		</view>
-		<view class="footer-btn">
-			<view class="usermain-btn" @click="messagebtn()">保存</view>
+		<view class="button-section">
+			<view class="submit-btn" @click="messagebtn()">保存</view>
 		</view>
 		<!-- 权限说明弹窗 -->
 		<u-popup mode="top" ref="permission">
@@ -473,49 +459,11 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-page {
-	/* background: #1c1b20; */
-}
-
-.navbar {
-	position: fixed;
-	top: 0;
-	left: 0;
-	right: 0;
-	z-index: 999;
-	background: #fff;
-	padding: 0 0 40rpx 20rpx;
-
-	.navbar-content {
-		display: flex;
-		align-items: center;
-		justify-content: space-between;
-		padding: 0 30rpx;
-		height: 60rpx;
-
-		.navbar-left {
-			width: 60rpx;
-			height: 60rpx;
-			display: flex;
-			align-items: center;
-			justify-content: center;
-		}
-
-		.navbar-title {
-			color: rgba(23, 23, 37, 1);
-			font-family: DM Sans;
-			font-size: 36rpx;
-			font-weight: 700;
-			line-height: 52rpx;
-			letter-spacing: 0%;
-			text-align: center;
-		}
-
-		.navbar-right {
-			width: 60rpx;
-			height: 60rpx;
-		}
-	}
+.page-container {
+	height: 100vh;
+	display: flex;
+	flex-direction: column;
+	box-sizing: border-box;
 }
 
 button::after {
@@ -542,13 +490,9 @@ button {
 }
 
 .usermain {
-	background: #ffffff;
-	/* color: #fff; */
-	margin-top: 120rpx;
-	/* 为固定导航栏留出空间 */
-	/* #ifdef APP*/
-	margin-top: calc(120rpx + var(--status-bar-height));
-	/* #endif*/
+	flex: 1;
+	overflow: auto;
+	padding-bottom: 40rpx;
 }
 
 .usermain-item {
@@ -670,22 +614,12 @@ button {
 
 /* 头像编辑图标样式 */
 .avatar-wrapper {
-	position: relative;
-	display: inline-block;
-}
-
-.edit-avatar-icon {
-	position: absolute;
-	bottom: 10rpx;
-	left: 60rpx;
-	width: 24rpx;
-	height: 24rpx;
-	// background: #fff;
-	border-radius: 50%;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+	.tip {
+		font-size: 20rpx;
+		line-height: 44rpx;
+		color: rgba(1, 107, 246, 1);
+		margin-left: 24rpx;
+	}
 }
 
 .avatar-button {
@@ -696,21 +630,25 @@ button {
 	border: none;
 }
 
-.footer-btn {
-	margin-top: 40rpx;
-	padding-bottom: 40rpx;
+.button-section {
+	padding: 28rpx 40rpx;
+	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
 }
-
-.footer-btn .usermain-btn {
+.submit-btn {
+	flex-shrink: 0;
+	border-radius: 999px;
+	box-shadow: 0px -4px 16px 0px rgba(0, 0, 0, 0.04);
+	background: linear-gradient(90deg, rgba(13, 39, 247, 1), rgba(19, 193, 234, 1) 100%);
 	color: rgba(255, 255, 255, 1);
-	background: rgba(1, 107, 246, 1);
-	text-align: center;
-	width: 90%;
-	height: 80rpx;
+	font-family: DM Sans;
 	font-size: 32rpx;
-	line-height: 80rpx;
-	margin: 20rpx auto;
-	border-radius: 40rpx;
+	font-weight: 400;
+	line-height: 48rpx;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	padding: 16rpx 32rpx;
+	box-sizing: border-box;
 }
 
 /* 选择器样式 - 参考workExperience页面 */

+ 73 - 2
pages/public/improvePrompt.vue

@@ -25,14 +25,17 @@
 				<view class="white-btn" @click="toOnlineResume">完善在线简历</view>
 			</template>
 			<template v-else>
-				<view class="link-btn">手动完善简历</view>
-				<view class="white-btn">附件简历一键识别</view>				
+				<view class="link-btn" @click="handleSubmit">手动完善简历</view>
+				<view class="white-btn" @click="handlUploadFile">附件简历一键识别</view>
 			</template>
 		</view>
 	</view>
 </template>
 
 <script>
+	// #ifdef APP-PLUS
+	import { chooseFileFromModule } from '@/uni_modules/sr-file-choose'
+	// #endif
 	export default {
 		data() {
 			return {
@@ -49,6 +52,74 @@
 				uni.navigateTo({
 					url: `/pages/my/onlineResume?scene=${this.scene}`
 				})
+			},
+			// 手动完善
+			handleSubmit() {
+				uni.navigateTo({
+					url: `/pages/my/onlineResume?scene=${this.scene}`
+				})
+			},
+			// 附件一键识别
+			handlUploadFile() {
+				// #ifdef APP-PLUS
+				chooseFileFromModule({
+					complete: res => {
+						let path = res.path
+						let name = res.name
+						let fileType = ''
+						// 如果没有name,默认为:截取最后一个/之后的内容
+						if (!name) {
+							let lastIndex = path.lastIndexOf('/');
+							if (lastIndex !== -1) {
+								name = path.substring(lastIndex + 1);
+							} else {
+								name = Math.random().toString(36).substr(2) + Date.now();
+							}
+						}
+						// 使用 lastIndexOf 方法找到最后一个 . 的位置
+						let lastDotIndex = name.lastIndexOf('.');
+						if (lastDotIndex !== -1) {
+							fileType = name.substring(lastDotIndex + 1);
+						} else {
+							console.log('文件路径中没有 .');
+						}
+						let file = {
+							size: res.size,
+							path,
+							fileType,
+							name
+						}
+						this.uploadFile(file)
+					}
+				})
+				// #endif
+				// #ifdef H5
+				uni.chooseFile({
+					count: 1,
+					success: res => {
+						this.uploadFile({
+							path: res.tempFilePaths[0],
+							name: 'file'
+						})
+					}
+				})
+				// #endif
+			},
+			// 上传文件
+			uploadFile(file) {
+				this.$queue.uploadFiles({
+					url: '/app/resumes/aiResumesUpload',
+					path: file.path,
+					name: 'file'
+				}, (res) => {
+					if (res && res.code === 0) {
+						uni.reLaunch({
+							url: `/pages/my/onlineResume?fileId=${res.data.id}`
+						})
+					} else {
+						this.$queue.showToast(res ? res.msg : '文件上传失败')
+					}
+				})
 			}
 		}
 	}

+ 160 - 160
pages/public/login.vue

@@ -222,138 +222,138 @@
 					}
 				}
 			},
-			//微信公众号登录
-			weixinLo() {
-				if (this.checked == false) {
-					uni.showToast({
-						title: "请先同意《隐私政策》和《用户服务协议》",
-						icon: "none",
-					});
-					return;
-				}
-				let ua = navigator.userAgent.toLowerCase();
-				if (ua.indexOf("micromessenger") !== -1) {
-					let openid = uni.getStorageSync("openid");
-					let userId = uni.getStorageSync("userId");
-
-					if (!openid) {
-						if (
-							window.location.href.indexOf("?code=") !== -1 ||
-							window.location.href.indexOf("&code=") !== -1
-						) {
-							let code;
-							if (window.location.href.indexOf("?code=") !== -1) {
-								code = window.location.href.split("?code=")[1].split("&")[0];
-							} else {
-								code = window.location.href.split("&code=")[1].split("&")[0];
-							}
-							let data = {
-								code: code,
-							};
-							this.$Request.getT("/app/Login/getOpenId", data).then((res) => {
-								if (ret.code == 0) {
-									uni.setStorageSync("openid", ret.data);
-									let datas = {
-										openId: ret.data,
-									};
-									this.$Request.getT("/app/Login/openid/login", datas).then((res) => {
-										if (res.code == 0) {
-											uni.setStorageSync("token", res.token);
-											uni.setStorageSync("userName", res.user.userName);
-											uni.setStorageSync("avatar", res.user.avatar);
-											uni.setStorageSync("phone", res.user.phone);
-											uni.setStorageSync("invitationCode", res.user.invitationCode);
-											this.$queue.setData("inviterCode", res.user.inviterCode);
-											uni.setStorageSync("sex", res.user.sex);
-											uni.setStorageSync("userId", res.user.userId);
-											uni.setStorageSync("openId", res.user.openId);
-											uni.setStorageSync("userType", res.user.userType);
-											this.$Request.get("/app/UserVip/isUserVip").then((res) => {
-												if (res.code == 0 && res.data && res.data.isVip ==
-													2) {
-													uni.setStorageSync("isVIP", true);
-												} else {
-													uni.setStorageSync("isVIP", false);
-												}
-											});
-											//判断是否开启身份选择 是/否
-											this.$Request.get("/app/common/type/339").then((rest) => {
-												if (rest.code == 0) {
-													if (rest.data.value == "是") {
-														uni.reLaunch({
-															url: "/pages/public/selectIdentity/selectIdentity",
-														});
-													} else {
-														uni.navigateBack();
-													}
-												} else {
-													uni.navigateBack();
-												}
-											});
-										} else {
-											uni.navigateTo({
-												url: "/pages/public/bind",
-											});
-										}
-									});
-								}
-							});
-						} else {
-							let that = this;
-							window.location.href =
-								"https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +
-								that.$queue.getWxAppid() +
-								"&redirect_uri=" +
-								window.location.href.split("#")[0] +
-								"&response_type=code&scope=snsapi_userinfo#wechat_redirect";
-						}
-					} else {
-						this.$Request
-							.getT("/app/Login/openid/login", {
-								openId: openid,
-							})
-							.then((res) => {
-								if (res.code == 0) {
-									uni.setStorageSync("token", res.token);
-									uni.setStorageSync("userName", res.user.userName);
-									uni.setStorageSync("avatar", res.user.avatar);
-									uni.setStorageSync("phone", res.user.phone);
-									uni.setStorageSync("invitationCode", res.user.invitationCode);
-									this.$queue.setData("inviterCode", res.user.inviterCode);
-									uni.setStorageSync("sex", res.user.sex);
-									uni.setStorageSync("userId", res.user.userId);
-									uni.setStorageSync("openId", res.user.openId);
-									uni.setStorageSync("userType", res.user.userType);
-									this.$Request.get("/app/UserVip/isUserVip").then((res) => {
-										if (res.code == 0 && res.data && res.data.isVip == 2) {
-											uni.setStorageSync("isVIP", true);
-										} else {
-											uni.setStorageSync("isVIP", false);
-										}
-									});
-									//判断是否开启身份选择 是/否
-									this.$Request.get("/app/common/type/339").then((rest) => {
-										if (rest.code == 0) {
-											if (rest.data.value == "是") {
-												uni.reLaunch({
-													url: "/pages/public/selectIdentity/selectIdentity",
-												});
-											} else {
-												uni.navigateBack();
-											}
-										} else {
-											uni.navigateBack();
-										}
-									});
-								} else {
-									uni.navigateTo({
-										url: "/pages/public/bind",
-									});
-								}
-							});
-					}
-				}
-			},
+			// //微信公众号登录
+			// weixinLo() {
+			// 	if (this.checked == false) {
+			// 		uni.showToast({
+			// 			title: "请先同意《隐私政策》和《用户服务协议》",
+			// 			icon: "none",
+			// 		});
+			// 		return;
+			// 	}
+			// 	let ua = navigator.userAgent.toLowerCase();
+			// 	if (ua.indexOf("micromessenger") !== -1) {
+			// 		let openid = uni.getStorageSync("openid");
+			// 		let userId = uni.getStorageSync("userId");
+
+			// 		if (!openid) {
+			// 			if (
+			// 				window.location.href.indexOf("?code=") !== -1 ||
+			// 				window.location.href.indexOf("&code=") !== -1
+			// 			) {
+			// 				let code;
+			// 				if (window.location.href.indexOf("?code=") !== -1) {
+			// 					code = window.location.href.split("?code=")[1].split("&")[0];
+			// 				} else {
+			// 					code = window.location.href.split("&code=")[1].split("&")[0];
+			// 				}
+			// 				let data = {
+			// 					code: code,
+			// 				};
+			// 				this.$Request.getT("/app/Login/getOpenId", data).then((res) => {
+			// 					if (ret.code == 0) {
+			// 						uni.setStorageSync("openid", ret.data);
+			// 						let datas = {
+			// 							openId: ret.data,
+			// 						};
+			// 						this.$Request.getT("/app/Login/openid/login", datas).then((res) => {
+			// 							if (res.code == 0) {
+			// 								uni.setStorageSync("token", res.token);
+			// 								uni.setStorageSync("userName", res.user.userName);
+			// 								uni.setStorageSync("avatar", res.user.avatar);
+			// 								uni.setStorageSync("phone", res.user.phone);
+			// 								uni.setStorageSync("invitationCode", res.user.invitationCode);
+			// 								this.$queue.setData("inviterCode", res.user.inviterCode);
+			// 								uni.setStorageSync("sex", res.user.sex);
+			// 								uni.setStorageSync("userId", res.user.userId);
+			// 								uni.setStorageSync("openId", res.user.openId);
+			// 								uni.setStorageSync("userType", res.user.userType);
+			// 								this.$Request.get("/app/UserVip/isUserVip").then((res) => {
+			// 									if (res.code == 0 && res.data && res.data.isVip ==
+			// 										2) {
+			// 										uni.setStorageSync("isVIP", true);
+			// 									} else {
+			// 										uni.setStorageSync("isVIP", false);
+			// 									}
+			// 								});
+			// 								//判断是否开启身份选择 是/否
+			// 								this.$Request.get("/app/common/type/339").then((rest) => {
+			// 									if (rest.code == 0) {
+			// 										if (rest.data.value == "是") {
+			// 											uni.reLaunch({
+			// 												url: "/pages/public/selectIdentity/selectIdentity",
+			// 											});
+			// 										} else {
+			// 											uni.navigateBack();
+			// 										}
+			// 									} else {
+			// 										uni.navigateBack();
+			// 									}
+			// 								});
+			// 							} else {
+			// 								uni.navigateTo({
+			// 									url: "/pages/public/bind",
+			// 								});
+			// 							}
+			// 						});
+			// 					}
+			// 				});
+			// 			} else {
+			// 				let that = this;
+			// 				window.location.href =
+			// 					"https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +
+			// 					that.$queue.getWxAppid() +
+			// 					"&redirect_uri=" +
+			// 					window.location.href.split("#")[0] +
+			// 					"&response_type=code&scope=snsapi_userinfo#wechat_redirect";
+			// 			}
+			// 		} else {
+			// 			this.$Request
+			// 				.getT("/app/Login/openid/login", {
+			// 					openId: openid,
+			// 				})
+			// 				.then((res) => {
+			// 					if (res.code == 0) {
+			// 						uni.setStorageSync("token", res.token);
+			// 						uni.setStorageSync("userName", res.user.userName);
+			// 						uni.setStorageSync("avatar", res.user.avatar);
+			// 						uni.setStorageSync("phone", res.user.phone);
+			// 						uni.setStorageSync("invitationCode", res.user.invitationCode);
+			// 						this.$queue.setData("inviterCode", res.user.inviterCode);
+			// 						uni.setStorageSync("sex", res.user.sex);
+			// 						uni.setStorageSync("userId", res.user.userId);
+			// 						uni.setStorageSync("openId", res.user.openId);
+			// 						uni.setStorageSync("userType", res.user.userType);
+			// 						this.$Request.get("/app/UserVip/isUserVip").then((res) => {
+			// 							if (res.code == 0 && res.data && res.data.isVip == 2) {
+			// 								uni.setStorageSync("isVIP", true);
+			// 							} else {
+			// 								uni.setStorageSync("isVIP", false);
+			// 							}
+			// 						});
+			// 						//判断是否开启身份选择 是/否
+			// 						this.$Request.get("/app/common/type/339").then((rest) => {
+			// 							if (rest.code == 0) {
+			// 								if (rest.data.value == "是") {
+			// 									uni.reLaunch({
+			// 										url: "/pages/public/selectIdentity/selectIdentity",
+			// 									});
+			// 								} else {
+			// 									uni.navigateBack();
+			// 								}
+			// 							} else {
+			// 								uni.navigateBack();
+			// 							}
+			// 						});
+			// 					} else {
+			// 						uni.navigateTo({
+			// 							url: "/pages/public/bind",
+			// 						});
+			// 					}
+			// 				});
+			// 		}
+			// 	}
+			// },
 			// wxGetUserInfo(e) {
 			// 	if (this.checked) {
 			// 		wx.getUserProfile({
@@ -467,34 +467,34 @@
 					},
 				});
 			},
-			appleLogin(){
-				if (!this.checked) {
-					uni.showToast({
-						title: "请阅读并同意《隐私协议》和《用户服务协议》",
-						icon: "none",
-					});
-					return;
-				}
-				uni.login({
-				    provider: 'apple',
-				    success: function (loginRes) {
-				        // 登录成功
-				        uni.getUserInfo({
-				            provider: 'apple',
-				            success: function(info) {
-				                // 获取用户信息成功, info.authResult中保存登录认证数据
-								uni.navigateTo({
-									url: `/pages/public/loginphone?type=1`,
-								});
-				            }
-				        })
-				    },
-				    fail: function (err) {
-				        // 登录授权失败
-				        // err.code错误码参考`授权失败错误码(code)说明`
-				    }
-				});
-			},
+			// appleLogin(){
+			// 	if (!this.checked) {
+			// 		uni.showToast({
+			// 			title: "请阅读并同意《隐私协议》和《用户服务协议》",
+			// 			icon: "none",
+			// 		});
+			// 		return;
+			// 	}
+			// 	uni.login({
+			// 	    provider: 'apple',
+			// 	    success: function (loginRes) {
+			// 	        // 登录成功
+			// 	        uni.getUserInfo({
+			// 	            provider: 'apple',
+			// 	            success: function(info) {
+			// 	                // 获取用户信息成功, info.authResult中保存登录认证数据
+			// 					uni.navigateTo({
+			// 						url: `/pages/public/loginphone?type=1`,
+			// 					});
+			// 	            }
+			// 	        })
+			// 	    },
+			// 	    fail: function (err) {
+			// 	        // 登录授权失败
+			// 	        // err.code错误码参考`授权失败错误码(code)说明`
+			// 	    }
+			// 	});
+			// },
 			//小程序登录
 			login(nickName, avatarUrl, sex) {
 				if (!this.checked) {

+ 326 - 101
pages/public/loginV2.vue

@@ -1,9 +1,20 @@
 <template>
-	<view class="container">
-		<view class="main-container">
-			<view class="logo"></view>
-			<view class="main-img"></view>
-			<view class="phone-number">13588888888</view>
+	<view class="container" :style="{ paddingTop: (12 + statusBarHeight) + 'px' }">
+		<view class="main-container" v-if="!initing">
+			<image src="/static/images/logo-long.png" class="logo"></image>
+			<image src="/static/images/invite.png" class="main-img"></image>
+			<view class="input-container">
+				<view class="input-wrapper flex align-center">
+					<text class="country-code">{{ countryCode }}</text>
+					<u-icon name="arrow-down" color="rgba(216, 216, 216, 1)"></u-icon>
+					<text class="border">|</text>
+					<input v-model="phoneNum" class="input" type="number" placeholder="请输入手机号" maxlength="11" placeholder-class="placeholder" />
+				</view>
+				<view class="input-wrapper flex align-center justify-between">
+					<input v-model="verifyCode" type="number" placeholder="请输入验证码" maxlength="6" placeholder-class="placeholder" />
+					<view class="verify-code" @click="getVerifyCode">{{ disabled ? `${countdown}s` : '获取验证码' }}</view>
+				</view>
+			</view>
 			<view class="agreement">
 				<view class="check-box" @click="checked = !checked">
 					<image src="@/static/images/jobApplicant/check.svg" v-if="checked" mode="scaleToFill" />
@@ -14,49 +25,163 @@
 					和<navigator url="/my/setting/mimi">《隐私协议》</navigator>,允许亿职赞统一管理本人账号信息					
 				</view>
 			</view>
-			<u-button class="login-btn" shape="circle" :hairline="false" @click="login">一键登录</u-button>
+			<view class="login-btn" @click="handleLogin">注册登录</view>
+			<!-- #ifdef APP-PLUS -->
 			<view class="other-login">
-				<view class="other-type">
-					<view class="icon"></view>
+				<view class="other-type" @click="handleWxLogin">
+					<image src="/static/wechat.png" class="icon"></image>
 					<view class="text">微信登录</view>
 				</view>
-				<view class="other-type">
-					<view class="icon"></view>
-					<view class="text">其他手机登录</view>
+				<view class="other-type" @click="handlOneKeyLogin">
+					<image src="/static/phone.png" class="icon"></image>
+					<view class="text">一键登录</view>
 				</view>
 			</view>
+			<!-- #endif -->
 		</view>
-		<view class="footer">
-			<view class="link">企业套餐咨询</view>
-			<view class="link">投诉举报</view>
-			<view class="link">资质信息</view>
+		<!-- <view class="footer">
+			<view class="link" v-for="item in infos" :key="item.type" @click="showPopup(item.type)">{{ item.name }}</view>
 		</view>
+		
+		<u-popup v-model="show" mode="bottom" border-radius="14">
+			<view class="popup-content">
+				<view v-if="currentType == 1">企业套餐咨询</view>
+				<view v-if="currentType == 2">投诉举报</view>
+				<view v-if="currentType == 3">资质信息</view>				
+			</view>
+		</u-popup> -->
 	</view>
 </template>
 
 <script>
+	import { jVerificationLogin, closeVerifyView } from '@/utils/jVerificationLogin'
 	export default {
 		data() {
 			return {
-				checked: false
+				initing: true, // 初始化中
+				statusBarHeight: 0,
+				checked: false,
+				countryCode: '+86',
+				phoneNum: '',
+				verifyCode: '',
+				countdown: 60,
+				timer: null,
+				disabled: false,
+				loading: false,
+				isFirst: false,
+				// show: false,
+				// currentType: 1,
+				// infos: [
+				// 	{
+				// 		type: 1,
+				// 		name: '企业套餐咨询',
+				// 	},
+				// 	{
+				// 		type: 2,
+				// 		name: '投诉举报',
+				// 	},
+				// 	{
+				// 		type: 3,
+				// 		name: '资质信息',
+				// 	},
+				// ],
 			}
 		},
-		onLoad() {
-			// this.handleLogin()
-			// uni.getPhoneNumber({
-			// 	provider: 'univerify',
-			// 	success: (res) => {
-			// 		console.log('fail----------------')
-			// 		console.log(JSON.parse(JSON.stringify(err)))
-			// 	},
-			// 	fail: (err) => {
-			// 		console.log('fail----------------')
-			// 		console.log(JSON.parse(JSON.stringify(err)))
-			// 	}
-			// })
+		onLoad(options) {
+			// #ifdef H5
+			this.initing = false
+			// #endif
+			// #ifdef APP-PLUS
+			this.checkLoginStatus()
+			// #endif
+			
+			// 判断是否是第一次
+			if (options.isFirst) {
+				this.isFirst = true
+			}
+			
+			// 获取状态栏高度
+			let systemInfo = uni.getSystemInfoSync();
+			this.statusBarHeight = systemInfo.statusBarHeight || 0;
 		},
 		methods: {
-			login() {
+			// 获取验证码
+			getVerifyCode() {
+				if (!this.phoneNum) {
+					uni.showToast({
+						title: '请输入手机号',
+						icon: 'none'
+					})
+					return
+				}
+				if (!/^1[3-9]\d{9}$/.test(this.phoneNum)) {
+					uni.showToast({
+						title: '请输入正确的手机号',
+						icon: 'none'
+					})
+					return
+				}
+				
+				if (this.disabled || this.loading) return
+				uni.showLoading({
+					title: '正在发送验证码'
+				})
+				this.loading = true
+				this.$Request
+					.getT('/app/Login/sendMsg/' + this.phoneNum + '/bind')
+					.then((res) => {
+						if (res.code === 0) {
+							this.startCountdown()
+						} else {
+							uni.showModal({
+								showCancel: false,
+								title: '短信发送失败',
+								content: res.msg ? res.msg : '请一分钟后再获取验证码',
+							});
+						}
+					})
+					.finally(() => {
+						this.loading = false
+						uni.hideLoading();
+					})
+			},
+			startCountdown() {
+				this.disabled = true
+				this.timer = setInterval(() => {
+					this.countdown--
+					if (this.countdown <= 0) {
+					    this.clearCountdownTimer()
+					    this.countdown = 60
+					    this.disabled = false
+					}
+				}, 1000)
+			},
+			clearCountdownTimer() {
+			    if (this.timer) {
+					clearInterval(this.timer)
+					this.timer = null // 重置定时器实例
+				}
+			},
+			// showPopup(type) {
+			// 	this.currentType = type
+			// 	this.show = true
+			// },
+			// 登录
+			handleLogin() {
+				if (!this.phoneNum) {
+					uni.showToast({
+						title: '请输入手机号',
+						icon: 'none'
+					})
+					return
+				}
+				if (!this.verifyCode) {
+					uni.showToast({
+						title: '请输入验证码',
+						icon: 'none'
+					})
+					return
+				}
 				if (!this.checked) {
 					uni.showToast({
 						title: '请阅读并同意协议',
@@ -64,62 +189,137 @@
 					})
 					return
 				}
-				this.handleLogin()
-				console.log('点击登录')
+				
+				// 避免出现多个loading
+				uni.hideLoading()
+				uni.showLoading({
+					title: '登录中'
+				})
+				let platform = 'app'
+				// #ifdef H5
+					platform = 'h5'
+				// #endif
+				this.$Request
+					.post('/app/Login/registerAndLogin', {
+						phone: this.phoneNum,
+						msg: this.verifyCode,
+						platform
+					})
+					.then((res) => {
+						this.loginFinished(res)
+					})
+					.finally(() => {
+						uni.hideLoading()
+					})
 			},
-			handleLogin() {
+			// 微信登录
+			handleWxLogin() {
+				uni.showLoading({
+					title: '登录中'
+				})
 				uni.login({
-					provider: 'univerify',
-					univerifyStyle: {
-					    fullScreen: true,
-						icon: {
-							path: 'static/images/logo.jpg',
-						},
-						authButton: {
-							normalColor: '#016BF6'
-							// "normalColor": "#3479f5", // 授权按钮正常状态背景颜色 默认值:#3479f5
-							// "highlightColor": "#2861c5",  // 授权按钮按下状态背景颜色 默认值:#2861c5(仅ios支持)
-							// "disabledColor": "#73aaf5",  // 授权按钮不可点击时背景颜色 默认值:#73aaf5(仅ios支持)
-						},
-						otherLoginButton: {
-							visible: false,
-						},
-						privacyTerms: {
-							defaultCheckBoxState: false,
-							uncheckedImage: 'static/images/onKeyLogin/unchecked.png',
-							checkedImage: 'static/images/onKeyLogin/checked.png',
-							privacyItems: [
-								{
-									"url": "https://h5.bosszan.com/my/setting/xieyi", // 点击跳转的协议详情页面
-									"title": "亿职赞用户协议" // 协议名称
-								},
-								{
-									"url": "https://h5.bosszan.com/my/setting/mimi", // 点击跳转的协议详情页面
-									"title": "隐私协议" // 协议名称
-								},
-							]
-						},
-						buttons: {
-							iconWidth: '45px',
-							list: [
-								{
-									provider: 'weixin',
-									iconPath: 'static/images/onKeyLogin/wechat.png'
-								},
-								{
-									provider: 'phone',
-									iconPath: 'static/images/onKeyLogin/phone.png'
-								}
-							]
+					provider: 'weixin',
+					success: (wxRes) => {
+						this.$queue.setData('openId', wxRes.authResult.openid)
+						this.$queue.setData('unionId', wxRes.authResult.unionId)
+						let data = {
+							wxOpenId: loginRes.authResult.openid,
+							token: loginRes.authResult.access_token
 						}
+						this.$Request
+							.postT('/app/Login/wxAppLogin', data)
+							.then(res => {
+								this.loginFinished(res)
+							})
+							.catch(() => {
+								this.$queue.showToast('登录失败,请稍后重试或更换其他登录方式')
+								uni.hideLoading()
+							})
 					},
-					success: (res) => {
-						console.log(JSON.parse(JSON.stringify(res)))
-					},
-					fail: (err) => {
-						console.log(JSON.parse(JSON.stringify(err)))
+					fail: () => {
+						this.$queue.showToast('登录失败,请稍后重试或更换其他登录方式')
+						uni.hideLoading()
 					}
 				})
+			},
+			// 一键登录
+			handlOneKeyLogin() {
+				jVerificationLogin((res) => {
+					this.loginFinished(res)
+				})
+			},
+			// 登录处理
+			loginFinished(res) {
+				if (res.code == 0) {
+					this.$queue.setData("userId", res.user.userId)
+					this.$queue.setData("token", res.token)
+					this.$queue.setData("phone", res.user.phone)
+					this.$queue.setData("userName", res.user.userName)
+					this.$queue.setData("avatar", res.user.avatar)
+					this.$queue.setData("invitationCode", res.user.invitationCode)
+					this.$queue.setData("inviterCode", res.user.inviterCode)
+					this.getIsVip()
+					if (res.user.userType) {
+						this.$queue.setData("userType", res.user.userType)
+						uni.reLaunch({
+							url: '/pages/my/index',
+							success: closeVerifyView
+						})
+					} else if (this.isFirst) {
+						// 首次登录,跳转至基本详情页
+						uni.reLaunch({
+							url: '/package/jobIntention/basicInfo',
+							success: closeVerifyView
+						})
+					} else {
+						uni.reLaunch({
+							url: '/pages/my/jobApplicant/guidePage',
+							success: closeVerifyView
+						})
+					}
+				} else {
+					uni.showToast({
+						title: res.msg,
+						icon: 'none'
+					})
+				}
+			},
+			// 获取vip数据
+			getIsVip() {
+				this.$Request.get("/app/UserVip/isUserVip").then((res) => {
+					if (res.code == 0) {
+						this.$queue.setData("isVip", res.data);
+					}
+				});
+			},
+			// 检查更新
+			checkLoginStatus() {
+				let firstUpload = uni.getStorageSync("firstUpload") || false;
+				let token = this.$queue.getData('token')
+				let userType = this.$queue.getData('userType')
+				let companyStatus = this.$queue.getData('companyStatus')
+				if (!firstUpload) {
+					// 第一次登录
+					// 跳转至引导页
+					return
+				}
+				
+				if (
+					token &&
+					(!userType || (userType == 2 && companyStatus != 2))
+				) {
+					// 有登录token,但是没有userType,跳转选择角色页面,强制用户选择
+					// 有登录token,并且userType为2,但是公司状态没有通过
+					// 跳转至选择角色页
+					return
+				}
+				
+				if (token && userType) {
+					// 跳转至首页
+					return
+				}
+				
+				this.initing = false
 			}
 		}
 	}
@@ -133,29 +333,51 @@
 		justify-content: space-between;
 		padding: 0 70rpx;
 		.logo {
+			display: block;
 			width: 204rpx;
 			height: 56rpx;
-			background-color: gray;
 			margin: 72rpx auto 92rpx;
 		}
 		.main-img {
+			display: block;
 			width: 400rpx;
 			height: 400rpx;
-			background-color: gray;
 			margin: 0 auto 34rpx;
 		}
-		.phone-number {
-			font-size: 56rpx;
-			font-weight: bold;
-			line-height: 68rpx;
-			text-align: center;
+		.input-container {
+			.input-wrapper {
+				margin-top: 28rpx;
+				box-sizing: border-box;
+				border: 1px solid rgba(242, 242, 242, 1);
+				border-radius: 12rpx;
+				background: rgba(250, 250, 250, 1);
+				padding: 24rpx;
+				.country-code {
+					color: rgba(51, 51, 51, 1);
+					margin-right: 24rpx;
+				}
+				.border {
+					margin: 0 24rpx;
+					color: rgba(234, 234, 234, 1);
+				}
+				.input {
+					font-size: 28rpx;
+				}
+				.placeholder {
+					color: rgba(153, 153, 153, 1);
+				}
+				.verify-code {
+					color: rgba(1, 107, 246, 1);
+					line-height: 36rpx;
+				}
+			}
 		}
 		.agreement {
 			display: flex;
 			align-items: flex-start;
 			font-size: 24rpx;
 			color: #605d67;
-			margin: 40rpx auto 68rpx;
+			margin: 52rpx auto 40rpx;
 			.check-box {
 				display: flex;
 				justify-content: center;
@@ -182,12 +404,12 @@
 			width: 600rpx;
 			height: 88rpx;
 			border-radius: 100px;
-			background: var(--线性渐变, linear-gradient(90.00deg, rgba(13, 39, 247, 1),rgba(19, 193, 234, 1) 100%));
+			background: linear-gradient(90.00deg, rgba(13, 39, 247, 1),rgba(19, 193, 234, 1) 100%);
 			text-align: center;
 			line-height: 88rpx;
 			color: #fff;
 			font-size: 32rpx;
-			font-weight: bold;
+			font-weight: 500;
 		}
 		.other-login {
 			display: flex;
@@ -197,9 +419,9 @@
 				margin: 0 24rpx;
 			}
 			.icon {
+				display: block;
 				width: 60rpx;
 				height: 60rpx;
-				background-color: gray;
 				margin: 0 auto 8rpx;
 			}
 			.text {
@@ -209,15 +431,18 @@
 				color: rgb(153, 153, 153);
 			}
 		}
-		.footer {
-			display: flex;
-			justify-content: center;
-			margin-bottom: 40rpx;
-			.link {
-				font-size: 28rpx;
-				color: #999999;
-				margin: 0 14rpx;
-			}
-		}
+		// .footer {
+		// 	display: flex;
+		// 	justify-content: center;
+		// 	margin-bottom: 40rpx;
+		// 	.link {
+		// 		font-size: 28rpx;
+		// 		color: #999999;
+		// 		margin: 0 14rpx;
+		// 	}
+		// }
+		// .popup-content {
+		// 	padding: 40rpx;
+		// }
 	}
 </style>

+ 231 - 0
pages/public/phoneLoginOrRegister.vue

@@ -0,0 +1,231 @@
+<template>
+	<view class="login-container">
+		<view>
+			<view class="title-wrapper">
+				<view class="title">手机号登录/注册</view>
+				<view class="sub-title">未注册的手机号将自动完成账号注册</view>
+			</view>
+			
+			<view class="input-wrapper flex align-center">
+				<text class="country-code">{{ countryCode }}</text>
+				<u-icon name="arrow-down" color="rgba(216, 216, 216, 1)"></u-icon>
+				<text class="border">|</text>
+				<input v-model="phoneNum" class="input" type="number" placeholder="请输入手机号" maxlength="11" placeholder-class="placeholder" />
+			</view>
+			
+			<view class="protocol-wrapper flex">
+				<view class="protocol-icon" @click="checked = !checked">
+					<image src="@/static/images/jobApplicant/check.svg" v-if="checked" mode="scaleToFill" class="icon" />
+					<image src="@/static/images/jobApplicant/border.svg" v-else mode="scaleToFill" class="icon" />
+				</view>
+				<view class="protocol-content">
+					已阅读并同意<navigator url="/my/setting/xieyi" class="link">《亿职赞用户协议》</navigator>
+					和<navigator url="/my/setting/mimi" class="link">《隐私协议》</navigator>,允许亿职赞统一管理本人账号信息					
+				</view>
+			</view>
+			
+			<view class="button" :class="{ active: checked }" @click="handleSubmit">下一步</view>			
+		</view>
+		
+		<view class="infos flex align-center justify-between">
+			<view class="info-button" v-for="item in infos" :key="item.type" @click="showPopup(item.type)">{{ item.name }}</view>
+		</view>
+		
+		<u-popup v-model="show" mode="bottom" border-radius="14">
+			<view class="popup-content">
+				<view v-if="currentType == 1">企业套餐咨询</view>
+				<view v-if="currentType == 2">投诉举报</view>
+				<view v-if="currentType == 3">资质信息</view>				
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	import { jVerificationLogin } from '@/utils/jVerificationLogin'
+	import { login } from '@/utils/login'
+	
+	export default {
+		data() {
+			return {
+				countryCode: '+86',
+				phoneNum: '',
+				checked: false,
+				show: false,
+				infos: [
+					{
+						type: 1,
+						name: '企业套餐咨询',
+					},
+					{
+						type: 2,
+						name: '投诉举报',
+					},
+					{
+						type: 3,
+						name: '资质信息',
+					},
+				],
+				currentType: 1,
+				loading: false
+			};
+		},
+		onLoad(options) {
+			if (options.init) {
+				jVerificationLogin((res) => {
+					login(res)
+				})
+			}
+		},
+		methods: {
+			showPopup(type) {
+				this.currentType = type
+				this.show = true
+			},
+			handleSubmit() {
+				if (!this.checked || this.loading) {
+					return
+				}
+				if (!this.phoneNum) {
+					uni.showToast({
+						title: '请输入手机号',
+						icon: 'none'
+					})
+					return
+				}
+				if (!/^1[3-9]\d{9}$/.test(this.phoneNum)) {
+					uni.showToast({
+						title: '请输入正确的手机号',
+						icon: 'none'
+					})
+					return
+				}
+				
+				this.loading = true
+				uni.showLoading({
+					title: '正在发送验证码'
+				})
+				this.$Request
+					.getT('/app/Login/sendMsg/' + this.phoneNum + '/bind')
+					.then((res) => {
+						if (res.code === 0) {
+							uni.navigateTo({
+								url: `/pages/public/verify?phoneNum=${this.phoneNum}&countryCode=${encodeURIComponent(this.countryCode)}`
+							})
+						} else {
+							uni.showModal({
+								showCancel: false,
+								title: '短信发送失败',
+								content: res.msg ? res.msg : '请一分钟后再获取验证码',
+							});
+						}
+					})
+					.finally(() => {
+						this.loading = false
+						uni.hideLoading();
+					})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.login-container {
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+		height: 100vh;
+		padding: 0 40rpx;
+		
+		.title-wrapper {
+			padding-top: 132rpx;
+			.title {
+				font-size: 40rpx;
+				font-weight: 700;
+				line-height: 52rpx;
+				color: rgba(51, 51, 51, 1);
+				margin-bottom: 12rpx;
+			}
+			.sub-title {
+				font-size: 28rpx;
+				line-height: 36rpx;
+				color: rgba(153, 153, 153, 1);
+			}
+		}
+		
+		.input-wrapper {
+			margin-top: 120rpx;
+			box-sizing: border-box;
+			border: 1px solid rgba(242, 242, 242, 1);
+			border-radius: 12rpx;
+			background: rgba(250, 250, 250, 1);
+			padding: 24rpx;
+			.country-code {
+				color: rgba(51, 51, 51, 1);
+				margin-right: 24rpx;
+			}
+			.border {
+				margin: 0 24rpx;
+				color: rgba(234, 234, 234, 1);
+			}
+			.input {
+				font-size: 28rpx;
+			}
+			.placeholder {
+				color: rgba(153, 153, 153, 1);
+			}
+		}
+		
+		.protocol-wrapper {
+			margin-top: 40rpx;
+			.protocol-icon {
+				width: 24rpx;
+				margin-right: 16rpx;
+				.icon {
+					display: block;
+					width: 24rpx;
+					height: 24rpx;
+				}
+			}
+			.protocol-content {
+				flex: 1;
+				display: flex;
+				flex-wrap: wrap;
+				font-size: 24rpx;
+				line-height: 28rpx;
+				color: rgba(96, 93, 103, 1);
+				.link {
+					color: #016BF6;
+				}
+			}
+		}
+		
+		.button {
+			width: 600rpx;
+			height: 88rpx;
+			text-align: center;
+			color: #fff;
+			line-height: 88rpx;
+			font-size: 32rpx;
+			font-weight: 500;
+			border-radius: 100rpx;
+			background: rgba(0, 0, 0, 0.08);
+			margin: 40rpx auto;
+			&.active {
+				background: linear-gradient(90.00deg, rgba(13, 39, 247, 1),rgba(19, 193, 234, 1));
+			}
+		}
+		
+		.infos {
+			width: 400rpx;
+			margin: 0 auto 100rpx;
+			color: rgba(153, 153, 153, 1);
+			font-size: 24rpx;
+			line-height: 28rpx;
+		}
+		
+		.popup-content {
+			padding: 40rpx;
+		}
+	}
+</style>

+ 141 - 0
pages/public/verify.vue

@@ -0,0 +1,141 @@
+<template>
+	<view class="verify-container">
+		<view class="title-wrapper">
+			<view class="title">输入验证码</view>
+			<view class="sub-title">验证码已发送到{{ countryCode }} {{ phoneNum }}</view>
+		</view>
+		
+		<view class="verify-wrapper">
+			<u-message-input
+				:maxlength="6"
+				active-color="#f2f2f2"
+				inactive-color="#f2f2f2"
+				:breathe="false"
+				@finish="handleFinish"
+			></u-message-input>
+		</view>
+		
+		<view class="count-down" :class="{ disabled: disabled }" @click="getVerifyCode">重新获取 <template v-if="disabled">{{ countdown }}s</template></view>
+	</view>
+</template>
+
+<script>
+	import { phoneLogin } from '@/utils/login'
+	
+	export default {
+		data() {
+			return {
+				phoneNum: '',
+				countryCode: '+86',
+				disabled: true,
+				countdown: 60,
+				timer: null,
+				loading: false
+			};
+		},
+		onLoad(options) {
+			if (options.phoneNum) {
+				this.phoneNum = options.phoneNum
+			}
+			if (options.countryCode) {
+				this.countryCode = decodeURIComponent(options.countryCode)
+			}
+			this.startCountdown()
+		},
+		onUnload() {
+			this.clearCountdownTimer()
+		},
+		methods: {
+			getVerifyCode() {
+				if (this.disabled || this.loading) return
+				uni.showLoading({
+					title: '正在发送验证码'
+				})
+				this.loading = true
+				this.$Request
+					.getT('/app/Login/sendMsg/' + this.phoneNum + '/bind')
+					.then((res) => {
+						if (res.code === 0) {
+							this.startCountdown()
+						} else {
+							uni.showModal({
+								showCancel: false,
+								title: '短信发送失败',
+								content: res.msg ? res.msg : '请一分钟后再获取验证码',
+							});
+						}
+					})
+					.finally(() => {
+						this.loading = false
+						uni.hideLoading();
+					})
+			},
+			startCountdown() {
+				this.disabled = true
+				this.timer = setInterval(() => {
+					this.countdown--
+					if (this.countdown <= 0) {
+					    this.clearCountdownTimer()
+					    this.countdown = 60
+					    this.disabled = false
+					}
+				}, 1000)
+			},
+			clearCountdownTimer() {
+			    if (this.timer) {
+					clearInterval(this.timer)
+					this.timer = null // 重置定时器实例
+				}
+			},
+			handleFinish(value) {
+				phoneLogin({
+					phone: this.phoneNum,
+					msg: value
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.verify-container {
+		padding: 0 40rpx;
+		.title-wrapper {
+			padding-top: 132rpx;
+			.title {
+				font-size: 40rpx;
+				font-weight: 700;
+				line-height: 52rpx;
+				color: rgba(51, 51, 51, 1);
+				margin-bottom: 12rpx;
+			}
+			.sub-title {
+				font-size: 28rpx;
+				line-height: 36rpx;
+				color: rgba(153, 153, 153, 1);
+			}
+		}
+		.verify-wrapper {
+			margin: 120rpx auto 40rpx;
+			::v-deep.u-char-flex {
+				justify-content: space-between;
+				.u-box {
+					width: 40rpx;
+					height: 40rpx;
+					box-sizing: border-box;
+					border-radius: 12rpx;
+					background: rgba(250, 250, 250, 1);
+					color: rgba(51, 51, 51, 1) !important;
+				}
+			}
+		}
+		.count-down {
+			color: rgba(1, 107, 246, 1);
+			font-size: 24rpx;
+			line-height: 32rpx;
+			&.disabled {
+				color: rgba(1, 107, 246, 0.6);
+			}
+		}
+	}
+</style>

+ 2 - 4
pages/talentSearch/index.vue

@@ -561,11 +561,9 @@
 					title: '提示',
 					content: '您还未登录,请先登录',
 					confirmColor: '#016BF6',
-					success: function(res) {
+					success: (res) => {
 						if (res.confirm) {
-							uni.navigateTo({
-								url: '/pages/public/login'
-							})
+							this.$queue.toLogin('navigateTo')
 						}
 					}
 				})

+ 0 - 0
static/images/onKeyLogin/checked.png → static/checked.png


BIN
static/close.png


BIN
static/images/oneKeyLogin/checked.png


BIN
static/images/oneKeyLogin/close.png


BIN
static/images/oneKeyLogin/logo.png


+ 0 - 0
static/images/onKeyLogin/phone.png → static/images/oneKeyLogin/phone.png


+ 0 - 0
static/images/onKeyLogin/unchecked.png → static/images/oneKeyLogin/unchecked.png


+ 0 - 0
static/images/onKeyLogin/wechat.png → static/images/oneKeyLogin/wechat.png


+ 8 - 0
static/images/svg/add-border.svg

@@ -0,0 +1,8 @@
+<svg viewBox="0 0 10 11" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="10.000000" height="11.000000" fill="none">
+	<rect id="svg 40" width="10.000000" height="11.000000" x="0.000000" y="0.000000" />
+	<g id="组合 74">
+		<path id="矢量 36" d="M8.2 9.6185L1.424 9.6185C0.9125 9.6185 0.5 9.206 0.5 8.6945L0.5 1.924C0.5 1.4125 0.9125 1 1.424 1L8.1945 1C8.706 1 9.1185 1.4125 9.1185 1.924L9.1185 8.6945C9.124 9.206 8.7115 9.6185 8.2 9.6185L8.2 9.6185ZM1.424 1.4785C1.1765 1.4785 0.9785 1.6765 0.9785 1.924L0.9785 8.6945C0.9785 8.942 1.1765 9.14 1.424 9.14L8.1945 9.14C8.442 9.14 8.64 8.942 8.64 8.6945L8.64 1.924C8.64 1.6765 8.442 1.4785 8.1945 1.4785L1.424 1.4785Z" fill="rgb(1,107,246)" fill-rule="nonzero" />
+		<path id="矢量 37" d="M7.66119 5.54858L1.96319 5.54858C1.83119 5.54858 1.72119 5.44408 1.72119 5.30658C1.72119 5.17458 1.82569 5.06458 1.96319 5.06458L7.66119 5.06458C7.79319 5.06458 7.90319 5.16908 7.90319 5.30658C7.90319 5.44408 7.79319 5.54858 7.66119 5.54858Z" fill="rgb(1,107,246)" fill-rule="nonzero" />
+		<path id="矢量 38" d="M4.81231 8.39757C4.68031 8.39757 4.57031 8.29307 4.57031 8.15557L4.57031 2.46307C4.57031 2.33107 4.67481 2.22107 4.81231 2.22107C4.94981 2.22107 5.05431 2.32557 5.05431 2.46307L5.05431 8.16107C5.05431 8.29307 4.94431 8.39757 4.81231 8.39757L4.81231 8.39757Z" fill="rgb(1,107,246)" fill-rule="nonzero" />
+	</g>
+</svg>

+ 4 - 0
static/images/svg/refresh-arrow.svg

@@ -0,0 +1,4 @@
+<svg viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15.000000" height="15.000000" fill="none">
+	<rect id="svg 15" width="15.000001" height="15.000000" x="0.000000" y="0.000000" />
+	<path id="矢量 20" d="M7.49387 0.928711C3.88379 0.928711 0.946655 3.86585 0.946655 7.47593C0.946655 11.086 3.88379 14.0232 7.49389 14.0232C11.104 14.0232 14.0411 11.086 14.0411 7.47593C14.0411 3.86583 11.104 0.928711 7.49387 0.928711ZM7.45607 11.0134C6.01293 11.0134 4.7832 10.096 4.31 8.81528L4.3148 8.81332C4.28539 8.75419 4.26524 8.68994 4.26524 8.61941C4.26524 8.37556 4.46291 8.17787 4.70678 8.17787C4.90292 8.17787 5.0636 8.30791 5.12117 8.48483L5.12737 8.4823C5.46824 9.44093 6.38194 10.1303 7.45607 10.1303C8.81993 10.1303 9.92939 9.02084 9.92939 7.65697C9.92939 6.29311 8.81993 5.18365 7.45607 5.18365C7.15676 5.18365 6.87197 5.24196 6.60626 5.33994L6.91753 5.93676C6.95395 5.97598 6.97638 6.02879 6.97638 6.0868C6.97638 6.21011 6.85327 6.30539 6.75129 6.30756C6.74979 6.30756 6.74826 6.30756 6.74676 6.30756C6.74354 6.30756 6.7405 6.30756 6.73727 6.30736L4.53152 6.21269C4.45282 6.20926 4.38167 6.1642 4.34524 6.09413C4.30882 6.02427 4.31247 5.94019 4.35473 5.87359L5.53965 4.01061C5.5819 3.9442 5.65693 3.90584 5.73543 3.90865C5.81413 3.91208 5.88525 3.95717 5.92168 4.02723L6.19385 4.54909C6.58393 4.39007 7.00952 4.3006 7.45608 4.3006C9.30674 4.3006 10.8125 5.80633 10.8125 7.657C10.8125 9.50768 9.30673 11.0134 7.45607 11.0134Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
+</svg>

+ 11 - 0
static/images/svg/tie.svg

@@ -0,0 +1,11 @@
+<svg viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12.000000" height="12.000000" fill="none">
+	<defs>
+		<linearGradient id="paint_linear_0" x1="0" x2="11.9999981" y1="6.00000048" y2="6.00000048" gradientUnits="userSpaceOnUse">
+			<stop stop-color="rgb(13,39,247)" offset="0" stop-opacity="1" />
+			<stop stop-color="rgb(19,193,234)" offset="1" stop-opacity="1" />
+		</linearGradient>
+	</defs>
+	<rect id="svg 76" width="12.000000" height="12.000000" x="0.000000" y="0.000000" />
+	<path id="矢量 67" d="M3.6 3.57628e-07L8.4 3.57628e-07C10.8 3.57628e-07 12 1.2 12 3.6L12 8.4C12 10.8 10.8 12 8.4 12L3.6 12C1.2 12 0 10.8 0 8.4L0 3.6C0 1.2 1.2 0 3.6 0L3.6 3.57628e-07Z" fill="url(#paint_linear_0)" fill-rule="nonzero" />
+	<path id="矢量 68" d="M4.71959 2.54981L7.34099 2.54981C7.39781 2.54959 7.45376 2.56379 7.5036 2.59107C7.55344 2.61835 7.59555 2.65783 7.62599 2.70581C7.68719 2.80301 7.69259 2.92421 7.64039 3.02621L7.04459 4.19441C7.00069 4.27964 6.93408 4.35109 6.85213 4.40085C6.77018 4.45062 6.67607 4.47676 6.58019 4.47641L5.48039 4.47641C5.3845 4.47683 5.29036 4.45071 5.20839 4.40094C5.12643 4.35117 5.05984 4.27969 5.01599 4.19441L4.42019 3.02561C4.39472 2.97558 4.38271 2.91979 4.38534 2.86372C4.38797 2.80765 4.40515 2.75323 4.43519 2.70581C4.46557 2.65791 4.50759 2.61849 4.55732 2.59121C4.60705 2.56394 4.66287 2.54969 4.71959 2.54981L4.71959 2.54981ZM3.96059 8.04221L5.01119 5.15501C5.04823 5.05519 5.11508 4.96919 5.20266 4.90865C5.29024 4.84812 5.39432 4.81598 5.50079 4.81661L6.56039 4.81661C6.77999 4.81661 6.97619 4.95221 7.04999 5.15501L8.09999 8.04221C8.1331 8.13322 8.13922 8.23187 8.11761 8.32628C8.09601 8.42069 8.04759 8.50685 7.97819 8.57441L6.26699 10.2538C6.22516 10.295 6.17332 10.3246 6.11657 10.3396C6.05983 10.3547 6.00015 10.3547 5.9434 10.3396C5.88666 10.3246 5.83482 10.295 5.79299 10.2538L4.08299 8.57441C4.01355 8.50688 3.96506 8.42075 3.94335 8.32635C3.92164 8.23195 3.92762 8.13329 3.96059 8.04221L3.96059 8.04221Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
+</svg>

BIN
static/invite.png


BIN
static/login-btn.png


BIN
static/logo.png


BIN
static/phone.png


BIN
static/unchecked.png


BIN
static/wechat.png


+ 4 - 0
uni_modules/sr-file-choose/changelog.md

@@ -0,0 +1,4 @@
+## 1.0.1(2025-08-17)
+插件优化
+## 1.0.0(2025-08-15)
+首次发布,实现安卓、苹果和鸿蒙的文件选择功能

+ 87 - 0
uni_modules/sr-file-choose/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "sr-file-choose",
+  "displayName": "文件选择App端通用插件,支持Android、iOS和鸿蒙(Harmony)",
+  "version": "1.0.1",
+  "description": "一个插件实现安卓、苹果和鸿蒙的文件选择功能。uniapp编译到App时,选择文件的方法不够全面,只支持图片和视频。  通过uts插件,实现了uniapp编译到安卓、苹果和鸿蒙App之后的文件选择功能。",
+  "keywords": [
+    "文件选择",
+    "File",
+    "Android",
+    "iOS",
+    "Harmony"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^4.08"
+  },
+  "dcloudext": {
+    "type": "uts",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": "2294810367"
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "文件读取权限"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+            "app-android": "y",
+            "app-ios": "y",
+            "app-harmony": "y"
+        },
+        "H5-mobile": {
+          "Safari": "u",
+          "Android Browser": "u",
+          "微信浏览器(Android)": "u",
+          "QQ浏览器(Android)": "u"
+        },
+        "H5-pc": {
+          "Chrome": "u",
+          "IE": "u",
+          "Edge": "u",
+          "Firefox": "u",
+          "Safari": "u"
+        },
+        "小程序": {
+          "微信": "u",
+          "阿里": "u",
+          "百度": "u",
+          "字节跳动": "u",
+          "QQ": "u",
+          "钉钉": "u",
+          "快手": "u",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 52 - 0
uni_modules/sr-file-choose/readme.md

@@ -0,0 +1,52 @@
+# sr-file-choose
+
+## 功能
+本地文件选择,例如.txt文件、.pdf文件等
+uniapp编译到App时,选择文件的方法不够全面,只支持图片和视频,想要选择本地其他格式的文件,目前需要自己去实现。
+
+通过uts插件,实现了uniapp编译到安卓、苹果和鸿蒙App之后的文件选择功能。
+
+
+## 使用方法
+
+1. 通过插件市场下载并导入项目,在uni_modules 目录下
+```javascript
+  import { chooseFileFromModule } from '../../uni_modules/sr-file-choose'
+```
+
+2. 在需要的地方直接调用`chooseFileFromModule`方法,示例中只用了`complete`完成回调,如何需要区分成功失败,可以自行实现`success`和`fail`回调。
+```javascript
+  chooseFileFromModule({
+	complete: (res) => {
+		console.log(res)
+	    let path = res.path							   
+	    let name = res.name
+	    let fileType = ''
+	   // 如果没有name,默认为:截取最后一个/之后的内容
+	   if(!name) {
+		   let lastIndex = path.lastIndexOf('/');
+		   if (lastIndex !== -1) {
+			   name = path.substring(lastIndex + 1);
+		   } else {
+			   name = Math.random().toString(36).substr(2) + Date.now();
+		   }
+	   }
+	   // 使用 lastIndexOf 方法找到最后一个 . 的位置
+	   let lastDotIndex = name.lastIndexOf('.');
+	   if (lastDotIndex !== -1) {
+		   fileType = name.substring(lastDotIndex + 1);
+	   } else {
+		   console.log('文件路径中没有 .');
+	   }
+	   let file = {
+		   size: res.size,
+		   path,
+		   fileType,
+		   name
+	   }
+	   console.log('根据需求构造的数据', file)
+	}
+})
+}
+```
+

+ 12 - 0
uni_modules/sr-file-choose/utssdk/app-android/AndroidManifest.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
+  package="uts.sdk.modules.srFilePicker">
+
+   <!--创建前台服务权限-->
+   <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <application>
+	   
+    </application>
+</manifest>

+ 3 - 0
uni_modules/sr-file-choose/utssdk/app-android/config.json

@@ -0,0 +1,3 @@
+{
+  "minSdkVersion": "21"
+}

+ 81 - 0
uni_modules/sr-file-choose/utssdk/app-android/filePicker.kt

@@ -0,0 +1,81 @@
+package uts.sdk.modules.srFilePicker
+
+import android.content.Intent
+import android.net.Uri
+import android.provider.OpenableColumns
+import io.dcloud.uts.UTSAndroid
+import io.dcloud.uts.console
+
+object NativeCode {
+
+    private const val REQUEST_CODE_PICK_FILE = 1001
+
+    // 定义一个回调接口,用于返回文件路径
+    private var filePickerCallback: ((String?) -> Unit)? = null
+
+    /**
+     * 打开文件选择器
+     * @param callback 回调函数,用于返回选中的文件路径
+     */
+    fun openFilePicker(callback: (String?) -> Unit) {
+		console.log("open filepicker")
+        // 保存回调函数
+        filePickerCallback = callback
+
+        // 创建文件选择 Intent
+        val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
+            type = "*/*" // 选择所有类型的文件
+            addCategory(Intent.CATEGORY_OPENABLE)
+        }
+
+        // 启动文件选择器
+        val activity = UTSAndroid.getUniActivity()
+        activity?.startActivityForResult(intent, REQUEST_CODE_PICK_FILE)
+    }
+
+    /**
+     * 处理文件选择结果
+     */
+	
+    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+		console.log("Selected file")
+        if (requestCode == REQUEST_CODE_PICK_FILE && resultCode == android.app.Activity.RESULT_OK) {
+            val uri: Uri? = data?.data
+            if (uri != null) {
+                // 获取文件路径
+                val filePath = getFilePathFromUri(uri)
+                console.log("Selected file path: ", filePath)
+
+                // 通过回调返回文件路径
+                filePickerCallback?.invoke(filePath)
+            } else {
+                // 如果没有选中文件,返回 null
+                filePickerCallback?.invoke(null)
+            }
+        } else {
+            // 用户取消选择或选择失败,返回 null
+            filePickerCallback?.invoke(null)
+        }
+
+        // 清空回调,避免内存泄漏
+        filePickerCallback = null
+    }
+
+    /**
+     * 从 Uri 中获取文件路径
+     */
+    private fun getFilePathFromUri(uri: Uri): String? {
+        val context = UTSAndroid.getUniActivity()
+        val cursor = context?.contentResolver?.query(uri, null, null, null, null)
+        return cursor?.use {
+            if (it.moveToFirst()) {
+                // 获取文件名
+                val displayName = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
+                // 获取文件路径(实际路径可能无法直接获取,返回 Uri 的字符串形式)
+                uri.toString()
+            } else {
+                null
+            }
+        }
+    }
+}

+ 212 - 0
uni_modules/sr-file-choose/utssdk/app-android/index.uts

@@ -0,0 +1,212 @@
+/**
+ * 引用 Android 系统库,示例如下:
+ * import { Context } from "android.content.Context";
+ * [可选实现,按需引入]
+ */
+
+/* 引入 interface.uts 文件中定义的变量 */
+import { ChooseFileOptions, ChooseFileResult, ChooseFile, ChooseFileSync } from '../interface.uts';
+
+/* 引入 unierror.uts 文件中定义的变量 */
+import { ChooseFileFailImpl } from '../unierror';
+
+import Context from "android.content.Context";
+import Intent from 'android.content.Intent'
+
+import Activity from 'android.app.Activity'
+import Cursor from 'android.database.Cursor';
+import Uri from 'android.net.Uri'
+import InputStream from 'java.io.InputStream'
+  
+/**
+ * 引入三方库
+ * [可选实现,按需引入]
+ *
+ * 在 Android 平台引入三方库有以下两种方式:
+ * 1、[推荐] 通过 仓储 方式引入,将 三方库的依赖信息 配置到 config.json 文件下的 dependencies 字段下。详细配置方式[详见](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html#dependencies)
+ * 2、直接引入,将 三方库的aar或jar文件 放到libs目录下。更多信息[详见](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html#android%E5%B9%B3%E5%8F%B0%E5%8E%9F%E7%94%9F%E9%85%8D%E7%BD%AE)
+ *
+ * 在通过上述任意方式依赖三方库后,使用时需要在文件中 import,如下示例:
+ * import { LottieAnimationView } from 'com.airbnb.lottie.LottieAnimationView'
+ */
+
+/**
+ * UTSAndroid 为平台内置对象,不需要 import 可直接调用其API,[详见](https://uniapp.dcloud.net.cn/uts/utsandroid.html#utsandroid)
+ */
+
+
+/**
+ * 异步方法
+ *
+ * uni-app项目中(vue/nvue)调用示例:
+ * 1、引入方法声明 import { ChooseFile } from "@/uni_modules/uts-api"
+ * 2、方法调用
+ * ChooseFile({
+ *   paramA: false,
+ *   complete: (res) => {
+ *      console.log(res)
+ *   }
+ * });
+ * uni-app x项目(uvue)中调用示例:
+ * 1、引入方法及参数声明 import { ChooseFile, ChooseFileOptions } from "@/uni_modules/uts-api";
+ * 2、方法调用
+ * let options = {
+ *   paramA: false,
+ *   complete: (res : any) => {
+ *     console.log(res)
+ *   }
+ * } as ChooseFileOptions;
+ * ChooseFile(options);
+ *
+ */
+
+// 全局保存回调的Map,键为请求码
+const callbackMap = new Map<number, ChooseFileOptions>();
+
+export const chooseFileFromModule : ChooseFile = function (options : ChooseFileOptions) {
+	
+	// 获取当前Activity实例
+	    const activity: Activity = UTSAndroid.getUniActivity() as Activity
+	    
+	    // 创建Intent
+	    const intent = new Intent(Intent.ACTION_GET_CONTENT)
+	    
+	    // 设置类型为所有文件类型
+	    intent.setType("*/*")
+	    
+	    // 添加类别,可选
+	    intent.addCategory(Intent.CATEGORY_OPENABLE)
+	    
+	    // 启动文件选择器
+	    activity.startActivityForResult(intent, 100) // 100是请求码,可以自定义
+		
+		UTSAndroid.onAppActivityResult((requestCode: Int, resultCode: Int, data?: Intent) => {
+			if(requestCode == 100){
+				// 我们发起的请求
+				// let eventName = "onAppActivityResult  -  requestCode:" + requestCode + " -resultCode:"+resultCode + " -data:"+JSON.stringify(data);
+		    	// console.log(eventName);
+				
+				// 必须检查结果是否成功
+				if (resultCode == Activity.RESULT_OK) { // RESULT_OK = -1
+					// 1. 获取URI
+					const uri = data?.getData();
+					let fileSize = 0;
+					let fileName = '';
+					if (uri != null) {
+						const fileUri = uri.toString();
+						console.log("Selected File URI:" + fileUri);
+						// console.log("Selected File Path:" + data?.getPath());
+						// 2. 通过URI获取文件信息(需要处理权限)
+						try {
+							const activity: Activity = UTSAndroid.getUniActivity()!!  // 强制断言非空
+							// 获取ContentResolver
+							const contentResolver = activity.getContentResolver()!!;
+							// 修复:显式声明 projection 类型
+							const projection = [android.provider.OpenableColumns.DISPLAY_NAME].toTypedArray()
+							// 查询文件名
+							const cursor = contentResolver.query(
+								uri,
+								projection,
+								null,
+								null,
+								null
+							);
+							if (cursor != null && cursor.moveToFirst()) {
+								const nameIndex = cursor.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME);
+								const sizeIndex = cursor.getColumnIndex(android.provider.OpenableColumns.SIZE);
+								fileName = cursor.getString(nameIndex);
+								if (sizeIndex != -1) {  // 确保列存在
+									fileSize = cursor.getLong(sizeIndex)
+								} else {
+									let inputStream: InputStream | null = null;
+									try {
+										inputStream = contentResolver.openInputStream(uri)!!;
+										let totalBytes = 0;
+										// const buffer = new Array<Byte>(1024); // 1KB 缓冲区
+										const buffer = ByteArray(1024)
+										while (true) {
+											const bytesRead = inputStream.read(buffer);
+											if (bytesRead == -1) break; // 读取结束
+											totalBytes += bytesRead;
+										}
+										fileSize = totalBytes;
+									} catch (e) {
+										console.error("Failed to calculate file size via InputStream:", e);
+										
+									} finally {
+										inputStream?.close();
+									}
+								}
+								console.log("File Size:" + fileSize + " bytes");
+								console.log("File Name:" + fileName);
+								cursor.close();
+							}
+							
+							// 返回数据
+							const res : ChooseFileResult = {
+							  size: fileSize,
+							  path: fileUri,
+							  name: fileName
+							};
+							options.success?.(res);
+							options.complete?.(res); 
+							// 读取文件内容(示例) ...这里可以处理文件流...
+							// const inputStream = contentResolver.openInputStream(uri);
+
+						} catch (e: Throwable) {
+							console.error("File Read Error:" + e);
+						}
+					}
+				} else {
+					console.log("User canceled or failed")
+				}
+		  
+			}else{
+				// 别的代码发起的请求,不要处理
+			}
+			// 移除全部监听
+			UTSAndroid.offAppActivityResult();
+		});
+		
+	
+  // if (options.paramA == true) {
+  //   // 返回数据
+  //   const res : ChooseFileResult = {
+  //     fieldA: 85,
+  //     fieldB: true,
+  //     fieldC: 'some message'
+  //   };
+  //   options.success?.(res);
+  //   options.complete?.(res);
+  // } else {
+  //   // 返回错误
+  //   const err = new ChooseFileFailImpl(9010001);
+  //   options.fail?.(err)
+  //   options.complete?.(err)
+  // }
+}
+
+/**
+ * 同步方法
+ *
+ * uni-app项目中(vue/nvue)调用示例:
+ * 1、引入方法声明 import { ChooseFileSync } from "@/uni_modules/uts-api"
+ * 2、方法调用 ChooseFileSync(true)
+ *
+ * uni-app x项目(uvue)中调用示例:
+ * 1、引入方法及参数声明 import { ChooseFileSync } from "@/uni_modules/uts-api";
+ * 2、方法调用 ChooseFileSync(true)
+ */
+export const ChooseFileSync : ChooseFileSync = function (paramA : boolean) : ChooseFileResult {
+  // 返回数据,根据插件功能获取实际的返回值
+  const res : ChooseFileResult = {
+    fieldA: 85,
+    fieldB: paramA,
+    fieldC: 'some message'
+  };
+  return res;
+}
+
+/**
+ * 更多插件开发的信息详见:https://uniapp.dcloud.net.cn/plugin/uts-plugin.html
+ */

+ 72 - 0
uni_modules/sr-file-choose/utssdk/app-harmony/index.uts

@@ -0,0 +1,72 @@
+/* 引入 interface.uts 文件中定义的变量 */
+import { ChooseFileOptions, ChooseFileResult, ChooseFile, ChooseFileSync } from '../interface.uts';
+
+/* 引入 unierror.uts 文件中定义的变量 */
+import { ChooseFileFailImpl } from '../unierror.uts';
+
+import picker from '@ohos.file.picker'
+import { fileIo as fs } from '@kit.CoreFileKit';
+import uri from '@ohos.uri'
+import { common } from '@kit.AbilityKit';
+import { BusinessError } from '@ohos.base'
+
+
+export const chooseFileFromModule : ChooseFile = function (options : ChooseFileOptions) {
+	const documentSelectOptions = new picker.DocumentSelectOptions();
+	// 选择文档的最大数目 暂时只选一个。
+	documentSelectOptions.maxSelectNumber = 1;
+	let uris: Array<string> = [];
+	let context = getContext() as common.Context; // 请确保 getContext(this) 返回结果为 UIAbilityContext
+	// 创建文件选择器实例
+	const documentViewPicker = new picker.DocumentViewPicker(context);
+	documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
+	  //文件选择成功后,返回被选中文档的URI结果集。
+	  uris = documentSelectResult;
+	  let uri: string = '';
+	  let filePath: string = '';
+	  let totalSize = 0;
+	  const BUFFER_SIZE = 4096; // 可调整的常量
+	  if(uris.length > 0) {
+		  uri = uris[0]
+		  // 解码中文路径
+		  filePath = decodeURIComponent(uri);
+		  console.info('filePath are:' + filePath);
+		  
+		  // 获取描述符 这里需要注意接口权限参数是fs.OpenMode.READ_ONLY。
+		  let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);		  
+		  // 获取文件大小 更完整的读取示例(循环读取直到文件结束)		  
+		  let buffer = new ArrayBuffer(BUFFER_SIZE);		  
+		  while (true) {
+		    let readLen = fs.readSync(file.fd, buffer);
+		    if (readLen <= 0) break; // 读取结束
+		    totalSize += readLen;
+		    // 处理buffer中的数据...
+		  }
+		  
+		  fs.closeSync(file);
+		  console.log(`文件总大小: ${totalSize}字节`);
+	  }
+	  console.info('documentViewPicker.select to file succeed and uris are:' + uris);	  
+	  // 返回数据
+	  const res : ChooseFileResult = {
+	    size: totalSize,
+	    path: filePath,
+		name: ''
+	  };
+	  options.success?.(res);
+	  options.complete?.(res);  
+	}).catch((err: BusinessError) => {
+	  console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
+	})
+	
+}
+
+async function getFileSize(filePath: string): Promise<number> {
+    try {
+        const stat = await fileio.stat(filePath)
+        return stat.size  // 返回文件大小(字节)
+    } catch (err) {
+        console.error('获取文件大小失败:', err)
+        return -1
+    }
+}

+ 3 - 0
uni_modules/sr-file-choose/utssdk/app-ios/config.json

@@ -0,0 +1,3 @@
+{
+  "deploymentTarget": "12"
+}

+ 93 - 0
uni_modules/sr-file-choose/utssdk/app-ios/filePicker.swift

@@ -0,0 +1,93 @@
+import Foundation
+import UIKit
+import MobileCoreServices
+// UTS内置对象的引用
+import DCloudUTSFoundation
+
+@objc public class NativeCode: NSObject {
+    
+    /// 当前视图控制器,用于弹出文件选择器
+    @objc static var currentViewController: UIViewController?
+    /// 文件选择器代理对象
+    private static var filePickerDelegate: FilePickerDelegate?
+    /// 打开文件选择器
+    @objc static func openFilePicker(_ callback: @escaping (_ fileURL: String?, _ size: Int) -> Void) {
+        // 确保当前视图控制器存在
+        guard let viewController = currentViewController else {
+            callback(nil, 0)
+            return
+        }
+		
+        // 创建文件选择器
+        let documentPicker = UIDocumentPickerViewController(documentTypes: [String(kUTTypeData)], in: .open)
+		filePickerDelegate = FilePickerDelegate(callback: callback) // 强引用持有 delegate
+        documentPicker.delegate = filePickerDelegate
+        documentPicker.modalPresentationStyle = .formSheet
+		
+        // 在主线程中执行 UI 操作
+        DispatchQueue.main.async {                        
+            // 弹出文件选择器
+            viewController.present(documentPicker, animated: true, completion: nil)
+        }
+    }
+}
+
+// MARK: - 文件选择器代理
+class FilePickerDelegate: NSObject, UIDocumentPickerDelegate {
+    private var callback: (_ fileURL: String?, _ size: Int) -> Void
+    
+    init(callback: @escaping (_ fileURL: String?, _ size: Int) -> Void) {
+        self.callback = callback
+    }
+    
+    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
+		// 用户选择了文件
+		console.log(urls);
+		if let fileURL = urls.first {
+			// 1. 获取安全访问权限
+			let accessing = fileURL.startAccessingSecurityScopedResource()
+			
+			do {
+				// 2. 将文件复制到应用的临时目录(获取可用的文件路径)
+				let tempDirectoryURL = FileManager.default.temporaryDirectory
+				let destinationURL = tempDirectoryURL.appendingPathComponent(fileURL.lastPathComponent)
+				
+				// 删除已存在的文件(如果有)
+				if FileManager.default.fileExists(atPath: destinationURL.path) {
+					try FileManager.default.removeItem(at: destinationURL)
+				}
+				
+				// 复制文件到临时目录
+				try FileManager.default.copyItem(at: fileURL, to: destinationURL)
+				
+				// 3. 获取文件属性
+				let fileAttributes = try destinationURL.resourceValues(forKeys: [.fileSizeKey])
+				let fileSize = fileAttributes.fileSize ?? 0
+						
+				// 在主线程中执行回调
+				DispatchQueue.main.async {
+					console.log(destinationURL.relativePath);
+					self.callback(destinationURL.path, fileSize) // 返回文件路径 path relativePath absoluteString
+				}
+			} catch {
+				console.log("Error getting file size: \(error.localizedDescription)")
+				// DispatchQueue.main.async {
+				// 	self.callback(nil, 0)
+				// }
+			}
+			// 4. 释放访问权限
+			if accessing {
+				fileURL.stopAccessingSecurityScopedResource()
+			}
+		} else {
+		    DispatchQueue.main.async {
+		        self.callback(nil, 0)
+		    }
+		}    
+    }
+    
+    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
+        // 用户取消了文件选择
+        // self.callback(nil, 0)
+    }
+}

+ 69 - 0
uni_modules/sr-file-choose/utssdk/app-ios/index.uts

@@ -0,0 +1,69 @@
+/**
+ * 引用 iOS 系统库,示例如下:
+ * import { UIDevice } from "UIKit";
+ * [可选实现,按需引入]
+ */
+
+// 导入 iOS 原生框架
+import AVFoundation from "AVFoundation"
+import Foundation from "Foundation"
+import UIKit from "UIKit"
+
+/* 引入 interface.uts 文件中定义的变量 */
+import { ChooseFileOptions, ChooseFileResult, ChooseFile, ChooseFileSync } from '../interface.uts';
+
+/* 引入 unierror.uts 文件中定义的变量 */
+import { ChooseFileFailImpl } from '../unierror';
+
+/**
+ * 引入三方库
+ * [可选实现,按需引入]
+ *
+ * 在 iOS 平台引入三方库有以下两种方式:
+ * 1、通过引入三方库framework 或者.a 等方式,需要将 .framework 放到 ./Frameworks 目录下,将.a 放到 ./Libs 目录下。更多信息[详见](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html#ios-平台原生配置)
+ * 2、通过 cocoaPods 方式引入,将要引入的 pod 信息配置到 config.json 文件下的 dependencies-pods 字段下。详细配置方式[详见](https://uniapp.dcloud.net.cn/plugin/uts-ios-cocoapods.html)
+ *
+ * 在通过上述任意方式依赖三方库后,使用时需要在文件中 import:
+ * 示例:import { LottieLoopMode	} from 'Lottie'
+ */
+
+/**
+ * UTSiOS 为平台内置对象,不需要 import 可直接调用其API,[详见](https://uniapp.dcloud.net.cn/uts/utsios.html)
+ */
+
+/**
+ * 异步方法
+ *
+ * uni-app项目中(vue/nvue)调用示例:
+ * 1、引入方法声明 import { ChooseFile } from "@/uni_modules/uts-api"
+ * 2、方法调用
+ * ChooseFile({
+ *   paramA: false,
+ *   complete: (res) => {
+ *      console.log(res)
+ *   }
+ * });
+ *
+ */
+
+export const chooseFileFromModule : ChooseFile = function (options : ChooseFileOptions) {
+	
+	// 设置当前视图控制器
+	NativeCode.currentViewController = UTSiOS.getCurrentViewController()
+	
+	// 调用文件选择器
+	NativeCode.openFilePicker((fileURL: String | null, size: Int) => {
+		// 返回数据
+		const res : ChooseFileResult = {
+		  size: size,
+		  path: 'file://' + (fileURL ?? ''),
+		  name: ''
+		};
+		options.success?.(res);
+		options.complete?.(res);
+	});
+}
+
+/**
+ * 更多插件开发的信息详见:https://uniapp.dcloud.net.cn/plugin/uts-plugin.html
+ */

+ 16 - 0
uni_modules/sr-file-choose/utssdk/app-ios/info.plist

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+	<key>NSPhotoLibraryUsageDescription</key>
+	<string>申请使用相册权限,用于选择照片</string>
+	<key>NSDocumentsFolderUsageDescription</key>
+	<string>申请使用文件选择权限,用于选择文件</string>
+	<key>NSDownloadsFolderUsageDescription</key>
+	<string>App需要访问下载文件夹以获取文件</string>
+	<key>UIFileSharingEnabled</key>
+	<true/>
+	<key>LSSupportsOpeningDocumentsInPlace</key>
+	<true/>
+  </dict>
+</plist>

+ 49 - 0
uni_modules/sr-file-choose/utssdk/interface.uts

@@ -0,0 +1,49 @@
+/**
+ * interface.uts
+ * uts插件接口定义文件,按规范定义接口文件可以在HBuilderX中更好的做到语法提示
+ */
+
+/**
+ * myApi 异步函数的参数,在type里定义函数需要的参数以及api成功、失败的相关回调函数。
+ */
+
+export type ChooseFileOptions = {
+	success ?: (res : ChooseFileResult) => void
+	fail ?: (res : ChooseFileFail) => void
+	complete ?: (res : any) => void
+}
+
+/**
+ * 函数返回结果
+ * 可以是void, 基本数据类型,自定义type, 或者其他类型。
+ * [可选实现]
+ */
+
+// 返回文件的路径、名称、大小
+export type ChooseFileResult = {
+  size : number,
+  path : string,
+  name : string
+}
+
+/**
+ * 错误码
+ * 根据uni错误码规范要求,建议错误码以90开头,以下是错误码示例:
+ * - 9010001 错误信息1
+ * - 9010002 错误信息2
+ */
+export type ChooseFileErrorCode = 9010001 | 9010002;
+/**
+ * ChooseFile 的错误回调参数
+ */
+export interface ChooseFileFail extends IUniError {
+  errCode : ChooseFileErrorCode
+};
+
+/* 
+  异步函数定义 
+  interface中的方法 如ChooseFile 提供给各端组件去使用
+*/
+
+// 选择文件
+export type ChooseFile = (options : ChooseFileOptions) => void

+ 39 - 0
uni_modules/sr-file-choose/utssdk/unierror.uts

@@ -0,0 +1,39 @@
+/* 此规范为 uni 规范,可以按照自己的需要选择是否实现 */
+import { ChooseFileErrorCode, ChooseFileFail } from "./interface.uts"
+/**
+ * 错误主题
+ * 注意:错误主题一般为插件名称,每个组件不同,需要使用时请更改。
+ * [可选实现]
+ */
+export const UniErrorSubject = 'uts-api';
+
+
+/**
+ * 错误信息
+ * @UniError
+ * [可选实现]
+ */
+export const ChooseFileErrors : Map<ChooseFileErrorCode, string> = new Map([
+  /**
+   * 错误码及对应的错误信息
+   */
+  [9010001, 'custom error mseeage1'],
+  [9010002, 'custom error mseeage2'],
+]);
+
+
+/**
+ * 错误对象实现
+ */
+export class ChooseFileFailImpl extends UniError implements ChooseFileFail {
+
+  /**
+   * 错误对象构造函数
+   */
+  constructor(errCode : ChooseFileErrorCode) {
+    super();
+    this.errSubject = UniErrorSubject;
+    this.errCode = errCode;
+    this.errMsg = ChooseFileErrors.get(errCode) ?? "";
+  }
+}

+ 91 - 0
uni_modules/uni-file-picker/changelog.md

@@ -0,0 +1,91 @@
+## 1.1.3(2025-12-03)
+- 修复: 腾讯云目录错误导致的上传错误问题
+## 1.1.2(2025-09-17)
+- 修复 设置readonly属性后内容插槽失效的问题。
+## 1.1.1(2025-09-03)
+- 修复 动态dir目录,不生效的问题
+## 1.1.0(2025-09-02)
+- 新增 dir 属性,可以选择上传目录
+## 1.0.13(2025-08-18)
+- 修复 删除文件后,返回信息不包含file对象的问题
+## 1.0.12(2025-04-14)
+- 修复 支付宝小程序 上传样式问题
+## 1.0.10(2024-07-09)
+- 优化 vue3兼容性
+## 1.0.9(2024-07-09)
+- 修复 value 属性不兼容vue3的bug
+## 1.0.8(2024-03-20)
+- 补充 删除文件时返回文件下标
+## 1.0.7(2024-02-21)
+- 新增 微信小程序选择视频时改用chooseMedia,并返回视频缩略图
+## 1.0.6(2024-01-06)
+- 新增 微信小程序不再调用chooseImage,而是调用chooseMedia
+## 1.0.5(2024-01-03)
+- 新增 上传文件至云存储携带本地文件名称
+## 1.0.4(2023-03-29)
+- 修复 手动上传删除一个文件后不能再上传的bug
+## 1.0.3(2022-12-19)
+- 新增 sourceType 属性, 可以自定义图片和视频选择的来源
+## 1.0.2(2022-07-04)
+- 修复 在uni-forms下样式不生效的bug
+## 1.0.1(2021-11-23)
+- 修复 参数为对象的情况下,url在某些情况显示错误的bug
+## 1.0.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker)
+## 0.2.16(2021-11-08)
+- 修复 传入空对象 ,显示错误的Bug
+## 0.2.15(2021-08-30)
+- 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug
+## 0.2.14(2021-08-23)
+- 新增 参数中返回 fileID 字段
+## 0.2.13(2021-08-23)
+- 修复 腾讯云传入fileID 不能回显的bug
+- 修复 选择图片后,不能放大的问题
+## 0.2.12(2021-08-17)
+- 修复 由于 0.2.11 版本引起的不能回显图片的Bug
+## 0.2.11(2021-08-16)
+- 新增 clearFiles(index) 方法,可以手动删除指定文件
+- 修复 v-model 值设为 null 报错的Bug
+## 0.2.10(2021-08-13)
+- 修复 return-type="object" 时,无法删除文件的Bug
+## 0.2.9(2021-08-03)
+- 修复 auto-upload 属性失效的Bug
+## 0.2.8(2021-07-31)
+- 修复 fileExtname属性不指定值报错的Bug
+## 0.2.7(2021-07-31)
+- 修复 在某种场景下图片不回显的Bug
+## 0.2.6(2021-07-30)
+- 修复 return-type为object下,返回值不正确的Bug
+## 0.2.5(2021-07-30)
+- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题
+## 0.2.3(2021-07-28)
+- 优化 调整示例代码
+## 0.2.2(2021-07-27)
+- 修复 vue3 下赋值错误的Bug
+- 优化 h5平台下上传文件导致页面卡死的问题
+## 0.2.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 0.1.1(2021-07-02)
+- 修复 sourceType 缺少默认值导致 ios 无法选择文件
+## 0.1.0(2021-06-30)
+- 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改
+## 0.0.11(2021-06-30)
+- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题
+## 0.0.10(2021-06-29)
+- 优化 文件上传后进度条消失时机
+## 0.0.9(2021-06-29)
+- 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug
+## 0.0.8(2021-06-15)
+- 修复 删除文件时无法触发 v-model 的Bug
+## 0.0.7(2021-05-12)
+- 新增 组件示例地址
+## 0.0.6(2021-04-09)
+- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug
+## 0.0.5(2021-04-09)
+- 优化 更新组件示例
+## 0.0.4(2021-04-09)
+- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔
+## 0.0.3(2021-02-05)
+- 调整为uni_modules目录规范
+- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug

+ 287 - 0
uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js

@@ -0,0 +1,287 @@
+'use strict';
+
+const ERR_MSG_OK = 'chooseAndUploadFile:ok';
+const ERR_MSG_FAIL = 'chooseAndUploadFile:fail';
+
+function chooseImage(opts) {
+	const {
+		count,
+		sizeType = ['original', 'compressed'],
+		sourceType,
+		extension
+	} = opts
+	return new Promise((resolve, reject) => {
+		// 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口
+		// #ifdef MP-WEIXIN
+		uni.chooseMedia({
+			count,
+			sizeType,
+			sourceType,
+			mediaType: ['image'],
+			extension,
+			success(res) {
+				res.tempFiles.forEach(item => {
+					item.path = item.tempFilePath;
+				})
+				resolve(normalizeChooseAndUploadFileRes(res, 'image'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
+				});
+			},
+		})
+		// #endif
+		// #ifndef MP-WEIXIN
+		uni.chooseImage({
+			count,
+			sizeType,
+			sourceType,
+			extension,
+			success(res) {
+				resolve(normalizeChooseAndUploadFileRes(res, 'image'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
+				});
+			},
+		});
+		// #endif
+
+	});
+}
+
+function chooseVideo(opts) {
+	const {
+		count,
+		camera,
+		compressed,
+		maxDuration,
+		sourceType,
+		extension
+	} = opts;
+	return new Promise((resolve, reject) => {
+		// 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口
+		// #ifdef MP-WEIXIN
+		uni.chooseMedia({
+			count,
+			compressed,
+			maxDuration,
+			sourceType,
+			extension,
+			mediaType: ['video'],
+			success(res) {
+				const {
+					tempFiles,
+				} = res;
+				resolve(normalizeChooseAndUploadFileRes({
+					errMsg: 'chooseVideo:ok',
+					tempFiles: tempFiles.map(item => {
+						return {
+							name: item.name || '',
+							path: item.tempFilePath,
+							thumbTempFilePath: item.thumbTempFilePath,
+							size:item.size,
+							type: (res.tempFile && res.tempFile.type) || '',
+							width:item.width,
+							height:item.height,
+							duration:item.duration,
+							fileType: 'video',
+							cloudPath: '',
+						}
+					}),
+				}, 'video'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
+				});
+			},
+		})
+		// #endif
+		// #ifndef MP-WEIXIN
+		uni.chooseVideo({
+			camera,
+			compressed,
+			maxDuration,
+			sourceType,
+			extension,
+			success(res) {
+				const {
+					tempFilePath,
+					duration,
+					size,
+					height,
+					width
+				} = res;
+				resolve(normalizeChooseAndUploadFileRes({
+					errMsg: 'chooseVideo:ok',
+					tempFilePaths: [tempFilePath],
+					tempFiles: [{
+						name: (res.tempFile && res.tempFile.name) || '',
+						path: tempFilePath,
+						size,
+						type: (res.tempFile && res.tempFile.type) || '',
+						width,
+						height,
+						duration,
+						fileType: 'video',
+						cloudPath: '',
+					}, ],
+				}, 'video'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
+				});
+			},
+		});
+		// #endif
+	});
+}
+
+function chooseAll(opts) {
+	const {
+		count,
+		extension
+	} = opts;
+	return new Promise((resolve, reject) => {
+		let chooseFile = uni.chooseFile;
+		if (typeof wx !== 'undefined' &&
+			typeof wx.chooseMessageFile === 'function') {
+			chooseFile = wx.chooseMessageFile;
+		}
+		if (typeof chooseFile !== 'function') {
+			return reject({
+				errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。',
+			});
+		}
+		chooseFile({
+			type: 'all',
+			count,
+			extension,
+			success(res) {
+				resolve(normalizeChooseAndUploadFileRes(res));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL),
+				});
+			},
+		});
+	});
+}
+
+function normalizeChooseAndUploadFileRes(res, fileType) {
+	res.tempFiles.forEach((item, index) => {
+		if (!item.name) {
+			item.name = item.path.substring(item.path.lastIndexOf('/') + 1);
+		}
+		if (fileType) {
+			item.fileType = fileType;
+		}
+		item.cloudPath =
+			Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.'));
+	});
+	if (!res.tempFilePaths) {
+		res.tempFilePaths = res.tempFiles.map((file) => file.path);
+	}
+	return res;
+}
+
+function uploadCloudFiles(files, max = 5, onUploadProgress) {
+	files = JSON.parse(JSON.stringify(files))
+	const len = files.length
+	let count = 0
+	let self = this
+	return new Promise(resolve => {
+		while (count < max) {
+			next()
+		}
+
+		function next() {
+			let cur = count++
+			if (cur >= len) {
+				!files.find(item => !item.url && !item.errMsg) && resolve(files)
+				return
+			}
+			const fileItem = files[cur]
+			const index = self.files.findIndex(v => v.uuid === fileItem.uuid)
+			fileItem.url = ''
+			delete fileItem.errMsg
+
+			uniCloud
+				.uploadFile({
+					filePath: fileItem.path,
+					cloudPath: fileItem.cloudPath,
+					fileType: fileItem.fileType,
+					onUploadProgress: res => {
+						res.index = index
+						onUploadProgress && onUploadProgress(res)
+					}
+				})
+				.then(res => {
+					fileItem.url = res.fileID
+					fileItem.index = index
+					if (cur < len) {
+						next()
+					}
+				})
+				.catch(res => {
+					fileItem.errMsg = res.errMsg || res.message
+					fileItem.index = index
+					if (cur < len) {
+						next()
+					}
+				})
+		}
+	})
+}
+
+
+
+
+
+function uploadFiles(choosePromise, {
+	onChooseFile,
+	onUploadProgress
+}) {
+	return choosePromise
+		.then((res) => {
+			if (onChooseFile) {
+				const customChooseRes = onChooseFile(res);
+				if (typeof customChooseRes !== 'undefined') {
+					return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ?
+						res : chooseRes);
+				}
+			}
+			return res;
+		})
+		.then((res) => {
+			if (res === false) {
+				return {
+					errMsg: ERR_MSG_OK,
+					tempFilePaths: [],
+					tempFiles: [],
+				};
+			}
+			return res
+		})
+}
+
+function chooseAndUploadFile(opts = {
+	type: 'all'
+}) {
+	if (opts.type === 'image') {
+		return uploadFiles(chooseImage(opts), opts);
+	} else if (opts.type === 'video') {
+		return uploadFiles(chooseVideo(opts), opts);
+	}
+	return uploadFiles(chooseAll(opts), opts);
+}
+
+export {
+	chooseAndUploadFile,
+	uploadCloudFiles
+};

+ 680 - 0
uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue

@@ -0,0 +1,680 @@
+<template>
+	<view class="uni-file-picker">
+		<view v-if="title" class="uni-file-picker__header">
+			<text class="file-title">{{ title }}</text>
+			<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
+		</view>
+		<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly" :image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview" :delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
+			<slot>
+				<view class="icon-add"></view>
+				<view class="icon-add rotate"></view>
+			</slot>
+		</upload-image>
+		<upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly" :list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
+			<slot><button type="primary" size="mini">选择文件</button></slot>
+		</upload-file>
+	</view>
+</template>
+
+<script>
+	import {
+		chooseAndUploadFile,
+		uploadCloudFiles
+	} from './choose-and-upload-file.js'
+	import {
+		get_file_ext,
+		get_extname,
+		get_files_and_is_max,
+		get_file_info,
+		get_file_data
+	} from './utils.js'
+	import uploadImage from './upload-image.vue'
+	import uploadFile from './upload-file.vue'
+	let fileInput = null
+	/**
+	 * FilePicker 文件选择上传
+	 * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=4079
+	 * @property {Object|Array}	value	组件数据,通常用来回显 ,类型由return-type属性决定
+	 * @property {Boolean}	disabled = [true|false]	组件禁用
+	 * 	@value true 	禁用
+	 * 	@value false 	取消禁用
+	 * @property {Boolean}	readonly = [true|false]	组件只读,不可选择,不显示进度,不显示删除按钮
+	 * 	@value true 	只读
+	 * 	@value false 	取消只读
+	 * @property {String}	return-type = [array|object]	限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖
+	 * 	@value array	规定 value 属性的类型为数组
+	 * 	@value object	规定 value 属性的类型为对象
+	 * @property {Boolean}	disable-preview = [true|false]	禁用图片预览,仅 mode:grid 时生效
+	 * 	@value true 	禁用图片预览
+	 * 	@value false 	取消禁用图片预览
+	 * @property {Boolean}	del-icon = [true|false]	是否显示删除按钮
+	 * 	@value true 	显示删除按钮
+	 * 	@value false 	不显示删除按钮
+	 * @property {Boolean}	auto-upload = [true|false]	是否自动上传,值为true则只触发@select,可自行上传
+	 * 	@value true 	自动上传
+	 * 	@value false 	取消自动上传
+	 * @property {Number|String}	limit	最大选择个数 ,h5 会自动忽略多选的部分
+	 * @property {String}	title	组件标题,右侧显示上传计数
+	 * @property {String}	mode = [list|grid]	选择文件后的文件列表样式
+	 * 	@value list 	列表显示
+	 * 	@value grid 	宫格显示
+	 * @property {String}	file-mediatype = [image|video|all]	选择文件类型
+	 * 	@value image	只选择图片
+	 * 	@value video	只选择视频
+	 * 	@value all		选择所有文件
+	 * @property {Array}	file-extname	选择文件后缀,根据 file-mediatype 属性而不同
+	 * @property {Object}	list-style	mode:list 时的样式
+	 * @property {Object}	image-styles	选择文件后缀,根据 file-mediatype 属性而不同
+	 * @event {Function} select 	选择文件后触发
+	 * @event {Function} progress 文件上传时触发
+	 * @event {Function} success 	上传成功触发
+	 * @event {Function} fail 		上传失败触发
+	 * @event {Function} delete 	文件从列表移除时触发
+	 */
+	export default {
+		name: 'uniFilePicker',
+		components: {
+			uploadImage,
+			uploadFile
+		},
+		options: {
+			virtualHost: true
+		},
+		emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
+		props: {
+			modelValue: {
+				type: [Array, Object],
+				default () {
+					return []
+				}
+			},
+			value: {
+				type: [Array, Object],
+				default () {
+					return []
+				}
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			disablePreview: {
+				type: Boolean,
+				default: false
+			},
+			delIcon: {
+				type: Boolean,
+				default: true
+			},
+			// 自动上传
+			autoUpload: {
+				type: Boolean,
+				default: true
+			},
+			// 最大选择个数 ,h5只能限制单选或是多选
+			limit: {
+				type: [Number, String],
+				default: 9
+			},
+			// 列表样式 grid | list | list-card
+			mode: {
+				type: String,
+				default: 'grid'
+			},
+			// 选择文件类型  image/video/all
+			fileMediatype: {
+				type: String,
+				default: 'image'
+			},
+			// 文件类型筛选
+			fileExtname: {
+				type: [Array, String],
+				default () {
+					return []
+				}
+			},
+			title: {
+				type: String,
+				default: ''
+			},
+			listStyles: {
+				type: Object,
+				default () {
+					return {
+						// 是否显示边框
+						border: true,
+						// 是否显示分隔线
+						dividline: true,
+						// 线条样式
+						borderStyle: {}
+					}
+				}
+			},
+			imageStyles: {
+				type: Object,
+				default () {
+					return {
+						width: 'auto',
+						height: 'auto'
+					}
+				}
+			},
+			readonly: {
+				type: Boolean,
+				default: false
+			},
+			returnType: {
+				type: String,
+				default: 'array'
+			},
+			sizeType: {
+				type: Array,
+				default () {
+					return ['original', 'compressed']
+				}
+			},
+			sourceType: {
+				type: Array,
+				default () {
+					return ['album', 'camera']
+				}
+			},
+			provider: {
+				type: String,
+				default: '' // 默认上传到 unicloud 内置存储 extStorage 扩展存储
+			},
+			dir: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				files: [],
+				localValue: [],
+				dirPath: ''
+			}
+		},
+		watch: {
+			value: {
+				handler(newVal, oldVal) {
+					this.setValue(newVal, oldVal)
+				},
+				immediate: true
+			},
+			modelValue: {
+				handler(newVal, oldVal) {
+					this.setValue(newVal, oldVal)
+				},
+				immediate: true
+			},
+			dir: {
+				handler(newVal) {
+					this.dirPath = newVal
+				},
+				immediate: true
+			},
+		},
+		computed: {
+			filesList() {
+				let files = []
+				this.files.forEach(v => {
+					files.push(v)
+				})
+				return files
+			},
+			showType() {
+				if (this.fileMediatype === 'image') {
+					return this.mode
+				}
+				return 'list'
+			},
+			limitLength() {
+				if (this.returnType === 'object') {
+					return 1
+				}
+				if (!this.limit) {
+					return 1
+				}
+				if (this.limit >= 9) {
+					return 9
+				}
+				return this.limit
+			}
+		},
+		created() {
+			// TODO 兼容不开通服务空间的情况
+			if (!(uniCloud.config && uniCloud.config.provider)) {
+				this.noSpace = true
+				uniCloud.chooseAndUploadFile = chooseAndUploadFile
+			}
+			this.form = this.getForm('uniForms')
+			this.formItem = this.getForm('uniFormsItem')
+			if (this.form && this.formItem) {
+				if (this.formItem.name) {
+					this.rename = this.formItem.name
+					this.form.inputChildrens.push(this)
+				}
+			}
+		},
+		methods: {
+			/**
+			 * 公开用户使用,清空文件
+			 * @param {Object} index
+			 */
+			clearFiles(index) {
+				if (index !== 0 && !index) {
+					this.files = []
+					this.$nextTick(() => {
+						this.setEmit()
+					})
+				} else {
+					this.files.splice(index, 1)
+				}
+				this.$nextTick(() => {
+					this.setEmit()
+				})
+			},
+			/**
+			 * 公开用户使用,继续上传
+			 */
+			upload() {
+				let files = []
+				this.files.forEach((v, index) => {
+					if (v.status === 'ready' || v.status === 'error') {
+						files.push(Object.assign({}, v))
+					}
+				})
+				return this.uploadFiles(files)
+			},
+			async setValue(newVal, oldVal) {
+				const newData = async (v) => {
+					const reg = /cloud:\/\/([\w.]+\/?)\S*/
+					let url = ''
+					if (v.fileID) {
+						url = v.fileID
+					} else {
+						url = v.url
+					}
+					if (reg.test(url)) {
+						v.fileID = url
+						v.url = await this.getTempFileURL(url)
+					}
+					if (v.url) v.path = v.url
+					return v
+				}
+				if (this.returnType === 'object') {
+					if (newVal) {
+						await newData(newVal)
+					} else {
+						newVal = {}
+					}
+				} else {
+					if (!newVal) newVal = []
+					for (let i = 0; i < newVal.length; i++) {
+						let v = newVal[i]
+						await newData(v)
+					}
+				}
+				this.localValue = newVal
+				if (this.form && this.formItem && !this.is_reset) {
+					this.is_reset = false
+					this.formItem.setValue(this.localValue)
+				}
+				let filesData = Object.keys(newVal).length > 0 ? newVal : [];
+				this.files = [].concat(filesData)
+			},
+
+			/**
+			 * 选择文件
+			 */
+			choose() {
+				if (this.disabled) return
+				if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType ===
+					'array') {
+					uni.showToast({
+						title: `您最多选择 ${this.limitLength} 个文件`,
+						icon: 'none'
+					})
+					return
+				}
+				this.chooseFiles()
+			},
+
+			/**
+			 * 选择文件并上传
+			 */
+			chooseFiles() {
+				const _extname = get_extname(this.fileExtname)
+				// 获取后缀
+				uniCloud
+					.chooseAndUploadFile({
+						type: this.fileMediatype,
+						compressed: false,
+						sizeType: this.sizeType,
+						sourceType: this.sourceType,
+						// TODO 如果为空,video 有问题
+						extension: _extname.length > 0 ? _extname : undefined,
+						count: this.limitLength - this.files.length, //默认9
+						onChooseFile: this.chooseFileCallback,
+						onUploadProgress: progressEvent => {
+							this.setProgress(progressEvent, progressEvent.index)
+						}
+					})
+					.then(result => {
+						this.setSuccessAndError(result.tempFiles)
+					})
+					.catch(err => {
+						console.log('选择失败', err)
+					})
+			},
+
+			/**
+			 * 选择文件回调
+			 * @param {Object} res
+			 */
+			async chooseFileCallback(res) {
+
+				const _extname = get_extname(this.fileExtname)
+
+				const is_one = (Number(this.limitLength) === 1 &&
+						this.disablePreview &&
+						!this.disabled) ||
+					this.returnType === 'object'
+				// 如果这有一个文件 ,需要清空本地缓存数据
+				if (is_one) {
+					this.files = []
+				}
+
+				let {
+					filePaths,
+					files
+				} = get_files_and_is_max(res, _extname)
+				if (!(_extname && _extname.length > 0)) {
+					filePaths = res.tempFilePaths
+					files = res.tempFiles
+				}
+
+				let currentData = []
+				for (let i = 0; i < files.length; i++) {
+					if (this.limitLength - this.files.length <= 0) break
+					files[i].uuid = Date.now()
+					let filedata = await get_file_data(files[i], this.fileMediatype)
+					filedata.progress = 0
+					filedata.status = 'ready'
+					// fix by mehaotian ,统一返回,删除也包含file对象
+					let fileTempData = {
+						...filedata,
+						file: files[i]
+					}
+					this.files.push(fileTempData)
+					currentData.push(fileTempData)
+				}
+				this.$emit('select', {
+					tempFiles: currentData,
+					tempFilePaths: filePaths
+				})
+				res.tempFiles = files
+				// 停止自动上传
+				if (!this.autoUpload || this.noSpace) {
+					res.tempFiles = []
+				}
+				res.tempFiles.map((fileItem, index) => {
+					this.provider && (fileItem.provider = this.provider);
+					const fileNameSplit = fileItem.name.split('.')
+					const ext = fileNameSplit.pop()
+					const fileName = fileNameSplit.join('.').replace(/[\s\/\?<>\\:\*\|":]/g, '_')
+					// 选择文件目录上传
+					let dir = this.dirPath || ''; // 防止用户传入的 dir 不正常
+					// 检查最后一个字符是否为 '/'(同时处理空字符串情况)
+					if (dir && dir[dir.length - 1] !== '/') {
+						dir += '/';
+					}
+
+					fileItem.cloudPath = dir + fileName + '_' + Date.now() + '_' + index + '.' + ext
+					fileItem.cloudPathAsRealPath = true
+
+					return fileItem
+				})
+				return res
+			},
+
+			/**
+			 * 批传
+			 * @param {Object} e
+			 */
+			uploadFiles(files) {
+				files = [].concat(files)
+				return uploadCloudFiles.call(this, files, 5, res => {
+						this.setProgress(res, res.index, true)
+					})
+					.then(result => {
+						this.setSuccessAndError(result)
+						return result;
+					})
+					.catch(err => {
+						console.log(err)
+					})
+			},
+
+			/**
+			 * 成功或失败
+			 */
+			async setSuccessAndError(res, fn) {
+				let successData = []
+				let errorData = []
+				let tempFilePath = []
+				let errorTempFilePath = []
+				for (let i = 0; i < res.length; i++) {
+					const item = res[i]
+					const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index
+
+					if (index === -1 || !this.files) break
+					if (item.errMsg === 'request:fail') {
+						this.files[index].url = item.path
+						this.files[index].status = 'error'
+						this.files[index].errMsg = item.errMsg
+						// this.files[index].progress = -1
+						errorData.push(this.files[index])
+						errorTempFilePath.push(this.files[index].url)
+					} else {
+						this.files[index].errMsg = ''
+						this.files[index].fileID = item.url
+						const reg = /cloud:\/\/([\w.]+\/?)\S*/
+						if (reg.test(item.url)) {
+							this.files[index].url = await this.getTempFileURL(item.url)
+						} else {
+							this.files[index].url = item.url
+						}
+
+						this.files[index].status = 'success'
+						this.files[index].progress += 1
+						successData.push(this.files[index])
+						tempFilePath.push(this.files[index].fileID)
+					}
+				}
+
+				if (successData.length > 0) {
+					this.setEmit()
+					// 状态改变返回
+					this.$emit('success', {
+						tempFiles: this.backObject(successData),
+						tempFilePaths: tempFilePath
+					})
+				}
+
+				if (errorData.length > 0) {
+					this.$emit('fail', {
+						tempFiles: this.backObject(errorData),
+						tempFilePaths: errorTempFilePath
+					})
+				}
+			},
+
+			/**
+			 * 获取进度
+			 * @param {Object} progressEvent
+			 * @param {Object} index
+			 * @param {Object} type
+			 */
+			setProgress(progressEvent, index, type) {
+				const fileLenth = this.files.length
+				const percentNum = (index / fileLenth) * 100
+				const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
+				let idx = index
+				if (!type) {
+					idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid)
+				}
+				if (idx === -1 || !this.files[idx]) return
+				// fix by mehaotian 100 就会消失,-1 是为了让进度条消失
+				this.files[idx].progress = percentCompleted - 1
+				// 上传中
+				this.$emit('progress', {
+					index: idx,
+					progress: parseInt(percentCompleted),
+					tempFile: this.files[idx]
+				})
+			},
+
+			/**
+			 * 删除文件
+			 * @param {Object} index
+			 */
+			delFile(index) {
+				this.$emit('delete', {
+					index,
+					tempFile: this.files[index],
+					tempFilePath: this.files[index].url
+				})
+				this.files.splice(index, 1)
+				this.$nextTick(() => {
+					this.setEmit()
+				})
+			},
+
+			/**
+			 * 获取文件名和后缀
+			 * @param {Object} name
+			 */
+			getFileExt(name) {
+				const last_len = name.lastIndexOf('.')
+				const len = name.length
+				return {
+					name: name.substring(0, last_len),
+					ext: name.substring(last_len + 1, len)
+				}
+			},
+
+			/**
+			 * 处理返回事件
+			 */
+			setEmit() {
+				let data = []
+				if (this.returnType === 'object') {
+					data = this.backObject(this.files)[0]
+					this.localValue = data ? data : null
+				} else {
+					data = this.backObject(this.files)
+					if (!this.localValue) {
+						this.localValue = []
+					}
+					this.localValue = [...data]
+				}
+				// #ifdef VUE3
+				this.$emit('update:modelValue', this.localValue)
+				// #endif
+				// #ifndef VUE3
+				this.$emit('input', this.localValue)
+				// #endif
+			},
+
+			/**
+			 * 处理返回参数
+			 * @param {Object} files
+			 */
+			backObject(files) {
+				let newFilesData = []
+				files.forEach(v => {
+					newFilesData.push({
+						extname: v.extname,
+						fileType: v.fileType,
+						image: v.image,
+						name: v.name,
+						path: v.path,
+						size: v.size,
+						fileID: v.fileID,
+						url: v.url,
+						// 修改删除一个文件后不能再上传的bug, #694
+						uuid: v.uuid,
+						status: v.status,
+						cloudPath: v.cloudPath
+					})
+				})
+				return newFilesData
+			},
+			async getTempFileURL(fileList) {
+				fileList = {
+					fileList: [].concat(fileList)
+				}
+				const urls = await uniCloud.getTempFileURL(fileList)
+				return urls.fileList[0].tempFileURL || ''
+			},
+			/**
+			 * 获取父元素实例
+			 */
+			getForm(name = 'uniForms') {
+				let parent = this.$parent;
+				let parentName = parent.$options.name;
+				while (parentName !== name) {
+					parent = parent.$parent;
+					if (!parent) return false;
+					parentName = parent.$options.name;
+				}
+				return parent;
+			}
+		}
+	}
+</script>
+
+<style>
+	.uni-file-picker {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		overflow: hidden;
+		width: 100%;
+		/* #endif */
+		flex: 1;
+	}
+
+	.uni-file-picker__header {
+		padding-top: 5px;
+		padding-bottom: 10px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: space-between;
+	}
+
+	.file-title {
+		font-size: 14px;
+		color: #333;
+	}
+
+	.file-count {
+		font-size: 14px;
+		color: #999;
+	}
+
+	.icon-add {
+		width: 50px;
+		height: 5px;
+		background-color: #f1f1f1;
+		border-radius: 2px;
+	}
+
+	.rotate {
+		position: absolute;
+		transform: rotate(90deg);
+	}
+</style>

+ 323 - 0
uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue

@@ -0,0 +1,323 @@
+<template>
+	<view class="uni-file-picker__files">
+		<view v-if="!readonly" class="files-button" @click="choose">
+			<slot></slot>
+		</view>
+		<!-- :class="{'is-text-box':showType === 'list'}" -->
+		<view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle">
+			<!-- ,'is-list-card':showType === 'list-card' -->
+
+			<view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{
+				'files-border':index !== 0 && styles.dividline}" :style="index !== 0 && styles.dividline &&borderLineStyle">
+				<view class="uni-file-picker__item">
+					<!-- :class="{'is-text-image':showType === 'list'}" -->
+					<!-- 	<view class="files__image is-text-image">
+						<image class="header-image" :src="item.logo" mode="aspectFit"></image>
+					</view> -->
+					<view class="files__name">{{item.name}}</view>
+					<view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)">
+						<view class="icon-del icon-files"></view>
+						<view class="icon-del rotate"></view>
+					</view>
+				</view>
+				<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
+					<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4" :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
+				</view>
+				<view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
+					点击重试
+				</view>
+			</view>
+
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "uploadFile",
+		emits: ['uploadFiles', 'choose', 'delFile'],
+		props: {
+			filesList: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			delIcon: {
+				type: Boolean,
+				default: true
+			},
+			limit: {
+				type: [Number, String],
+				default: 9
+			},
+			showType: {
+				type: String,
+				default: ''
+			},
+			listStyles: {
+				type: Object,
+				default () {
+					return {
+						// 是否显示边框
+						border: true,
+						// 是否显示分隔线
+						dividline: true,
+						// 线条样式
+						borderStyle: {}
+					}
+				}
+			},
+			readonly: {
+				type: Boolean,
+				default: false
+			}
+		},
+		computed: {
+			list() {
+				let files = []
+				this.filesList.forEach(v => {
+					files.push(v)
+				})
+				return files
+			},
+			styles() {
+				let styles = {
+					border: true,
+					dividline: true,
+					'border-style': {}
+				}
+				return Object.assign(styles, this.listStyles)
+			},
+			borderStyle() {
+				let {
+					borderStyle,
+					border
+				} = this.styles
+				let obj = {}
+				if (!border) {
+					obj.border = 'none'
+				} else {
+					let width = (borderStyle && borderStyle.width) || 1
+					width = this.value2px(width)
+					let radius = (borderStyle && borderStyle.radius) || 5
+					radius = this.value2px(radius)
+					obj = {
+						'border-width': width,
+						'border-style': (borderStyle && borderStyle.style) || 'solid',
+						'border-color': (borderStyle && borderStyle.color) || '#eee',
+						'border-radius': radius
+					}
+				}
+				let classles = ''
+				for (let i in obj) {
+					classles += `${i}:${obj[i]};`
+				}
+				return classles
+			},
+			borderLineStyle() {
+				let obj = {}
+				let {
+					borderStyle
+				} = this.styles
+				if (borderStyle && borderStyle.color) {
+					obj['border-color'] = borderStyle.color
+				}
+				if (borderStyle && borderStyle.width) {
+					let width = borderStyle && borderStyle.width || 1
+					let style = borderStyle && borderStyle.style || 0
+					if (typeof width === 'number') {
+						width += 'px'
+					} else {
+						width = width.indexOf('px') ? width : width + 'px'
+					}
+					obj['border-width'] = width
+
+					if (typeof style === 'number') {
+						style += 'px'
+					} else {
+						style = style.indexOf('px') ? style : style + 'px'
+					}
+					obj['border-top-style'] = style
+				}
+				let classles = ''
+				for (let i in obj) {
+					classles += `${i}:${obj[i]};`
+				}
+				return classles
+			}
+		},
+
+		methods: {
+			uploadFiles(item, index) {
+				this.$emit("uploadFiles", {
+					item,
+					index
+				})
+			},
+			choose() {
+				this.$emit("choose")
+			},
+			delFile(index) {
+				this.$emit('delFile', index)
+			},
+			value2px(value) {
+				if (typeof value === 'number') {
+					value += 'px'
+				} else {
+					value = value.indexOf('px') !== -1 ? value : value + 'px'
+				}
+				return value
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.uni-file-picker__files {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: flex-start;
+	}
+
+	.files-button {
+		// border: 1px red solid;
+	}
+
+	.uni-file-picker__lists {
+		position: relative;
+		margin-top: 5px;
+		overflow: hidden;
+	}
+
+	.file-picker__mask {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		color: #fff;
+		font-size: 14px;
+		background-color: rgba(0, 0, 0, 0.4);
+	}
+
+	.uni-file-picker__lists-box {
+		position: relative;
+	}
+
+	.uni-file-picker__item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		padding: 8px 10px;
+		padding-right: 5px;
+		padding-left: 10px;
+	}
+
+	.files-border {
+		border-top: 1px #eee solid;
+	}
+
+	.files__name {
+		flex: 1;
+		font-size: 14px;
+		color: #666;
+		margin-right: 25px;
+		/* #ifndef APP-NVUE */
+		word-break: break-all;
+		word-wrap: break-word;
+		/* #endif */
+	}
+
+	.icon-files {
+		/* #ifndef APP-NVUE */
+		position: static;
+		background-color: initial;
+		/* #endif */
+	}
+
+	// .icon-files .icon-del {
+	// 	background-color: #333;
+	// 	width: 12px;
+	// 	height: 1px;
+	// }
+
+
+	.is-list-card {
+		border: 1px #eee solid;
+		margin-bottom: 5px;
+		border-radius: 5px;
+		box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1);
+		padding: 5px;
+	}
+
+	.files__image {
+		width: 40px;
+		height: 40px;
+		margin-right: 10px;
+	}
+
+	.header-image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.is-text-box {
+		border: 1px #eee solid;
+		border-radius: 5px;
+	}
+
+	.is-text-image {
+		width: 25px;
+		height: 25px;
+		margin-left: 5px;
+	}
+
+	.rotate {
+		position: absolute;
+		transform: rotate(90deg);
+	}
+
+	.icon-del-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		margin: auto 0;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+		position: absolute;
+		top: 0px;
+		bottom: 0;
+		right: 5px;
+		height: 26px;
+		width: 26px;
+		// border-radius: 50%;
+		// background-color: rgba(0, 0, 0, 0.5);
+		z-index: 2;
+		transform: rotate(-45deg);
+	}
+
+	.icon-del {
+		width: 15px;
+		height: 1px;
+		background-color: #333;
+		// border-radius: 1px;
+	}
+
+	/* #ifdef H5 */
+	@media all and (min-width: 768px) {
+		.uni-file-picker__files {
+			max-width: 375px;
+		}
+	}
+
+	/* #endif */
+</style>

+ 285 - 0
uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue

@@ -0,0 +1,285 @@
+<template>
+	<view class="uni-file-picker__container">
+		<view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle">
+			<view class="file-picker__box-content" :style="borderStyle">
+				<image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image>
+				<view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)">
+					<view class="icon-del"></view>
+					<view class="icon-del rotate"></view>
+				</view>
+				<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
+					<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
+					 :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
+				</view>
+				<view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
+					点击重试
+				</view>
+			</view>
+		</view>
+		<view v-if="filesList.length < limit" class="file-picker__box" :style="boxStyle">
+			<view class="file-picker__box-content is-add" :style="borderStyle" @click="choose">
+				<slot></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "uploadImage",
+		emits:['uploadFiles','choose','delFile'],
+		props: {
+			filesList: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			disabled:{
+				type: Boolean,
+				default: false
+			},
+			disablePreview: {
+				type: Boolean,
+				default: false
+			},
+			limit: {
+				type: [Number, String],
+				default: 9
+			},
+			imageStyles: {
+				type: Object,
+				default () {
+					return {
+						width: 'auto',
+						height: 'auto',
+						border: {}
+					}
+				}
+			},
+			delIcon: {
+				type: Boolean,
+				default: true
+			},
+			readonly:{
+				type:Boolean,
+				default:false
+			}
+		},
+		computed: {
+			styles() {
+				let styles = {
+					width: 'auto',
+					height: 'auto',
+					border: {}
+				}
+				return Object.assign(styles, this.imageStyles)
+			},
+			boxStyle() {
+				const {
+					width = 'auto',
+						height = 'auto'
+				} = this.styles
+				let obj = {}
+				if (height === 'auto') {
+					if (width !== 'auto') {
+						obj.height = this.value2px(width)
+						obj['padding-top'] = 0
+					} else {
+						obj.height = 0
+					}
+				} else {
+					obj.height = this.value2px(height)
+					obj['padding-top'] = 0
+				}
+
+				if (width === 'auto') {
+					if (height !== 'auto') {
+						obj.width = this.value2px(height)
+					} else {
+						obj.width = '33.3%'
+					}
+				} else {
+					obj.width = this.value2px(width)
+				}
+
+				let classles = ''
+				for(let i in obj){
+					classles+= `${i}:${obj[i]};`
+				}
+				return classles
+			},
+			borderStyle() {
+				let {
+					border
+				} = this.styles
+				let obj = {}
+				const widthDefaultValue = 1
+				const radiusDefaultValue = 3
+				if (typeof border === 'boolean') {
+					obj.border = border ? '1px #eee solid' : 'none'
+				} else {
+					let width = (border && border.width) || widthDefaultValue
+					width = this.value2px(width)
+					let radius = (border && border.radius) || radiusDefaultValue
+					radius = this.value2px(radius)
+					obj = {
+						'border-width': width,
+						'border-style': (border && border.style) || 'solid',
+						'border-color': (border && border.color) || '#eee',
+						'border-radius': radius
+					}
+				}
+				let classles = ''
+				for(let i in obj){
+					classles+= `${i}:${obj[i]};`
+				}
+				return classles
+			}
+		},
+		methods: {
+			uploadFiles(item, index) {
+				this.$emit("uploadFiles", item)
+			},
+			choose() {
+				if(this.readonly) return
+				this.$emit("choose")
+			},
+			delFile(index) {
+				if(this.readonly) return
+				this.$emit('delFile', index)
+			},
+			prviewImage(img, index) {
+				if(this.readonly) return
+				let urls = []
+				if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
+					this.$emit("choose")
+				}
+				if(this.disablePreview) return
+				this.filesList.forEach(i => {
+					urls.push(i.url)
+				})
+
+				uni.previewImage({
+					urls: urls,
+					current: index
+				});
+			},
+			value2px(value) {
+				if (typeof value === 'number') {
+					value += 'px'
+				} else {
+					if (value.indexOf('%') === -1) {
+						value = value.indexOf('px') !== -1 ? value : value + 'px'
+					}
+				}
+				return value
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.uni-file-picker__container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		box-sizing: border-box;
+		/* #endif */
+		flex-wrap: wrap;
+		margin: -5px;
+	}
+
+	.file-picker__box {
+		position: relative;
+		// flex: 0 0 33.3%;
+		width: 33.3%;
+		height: 0;
+		padding-top: 33.33%;
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		/* #endif */
+	}
+
+	.file-picker__box-content {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		margin: 5px;
+		border: 1px #eee solid;
+		border-radius: 5px;
+		overflow: hidden;
+	}
+
+	.file-picker__progress {
+		position: absolute;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		/* border: 1px red solid; */
+		z-index: 2;
+	}
+
+	.file-picker__progress-item {
+		width: 100%;
+	}
+
+	.file-picker__mask {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		color: #fff;
+		font-size: 12px;
+		background-color: rgba(0, 0, 0, 0.4);
+	}
+
+	.file-image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.is-add {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+	}
+
+	.rotate {
+		position: absolute;
+		transform: rotate(90deg);
+	}
+
+	.icon-del-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+		position: absolute;
+		top: 3px;
+		right: 3px;
+		height: 26px;
+		width: 26px;
+		border-radius: 50%;
+		background-color: rgba(0, 0, 0, 0.5);
+		z-index: 2;
+		transform: rotate(-45deg);
+	}
+
+	.icon-del {
+		width: 15px;
+		height: 2px;
+		background-color: #fff;
+		border-radius: 2px;
+	}
+</style>

+ 110 - 0
uni_modules/uni-file-picker/components/uni-file-picker/utils.js

@@ -0,0 +1,110 @@
+/**
+ * 获取文件名和后缀
+ * @param {String} name
+ */
+export const get_file_ext = (name) => {
+	const last_len = name.lastIndexOf('.')
+	const len = name.length
+	return {
+		name: name.substring(0, last_len),
+		ext: name.substring(last_len + 1, len)
+	}
+}
+
+/**
+ * 获取扩展名
+ * @param {Array} fileExtname
+ */
+export const get_extname = (fileExtname) => {
+	if (!Array.isArray(fileExtname)) {
+		let extname = fileExtname.replace(/(\[|\])/g, '')
+		return extname.split(',')
+	} else {
+		return fileExtname
+	}
+	return []
+}
+
+/**
+ * 获取文件和检测是否可选
+ */
+export const get_files_and_is_max = (res, _extname) => {
+	let filePaths = []
+	let files = []
+	if(!_extname || _extname.length === 0){
+		return {
+			filePaths,
+			files
+		}
+	}
+	res.tempFiles.forEach(v => {
+		let fileFullName = get_file_ext(v.name)
+		const extname = fileFullName.ext.toLowerCase()
+		if (_extname.indexOf(extname) !== -1) {
+			files.push(v)
+			filePaths.push(v.path)
+		}
+	})
+	if (files.length !== res.tempFiles.length) {
+		uni.showToast({
+			title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`,
+			icon: 'none',
+			duration: 5000
+		})
+	}
+
+	return {
+		filePaths,
+		files
+	}
+}
+
+
+/**
+ * 获取图片信息
+ * @param {Object} filepath
+ */
+export const get_file_info = (filepath) => {
+	return new Promise((resolve, reject) => {
+		uni.getImageInfo({
+			src: filepath,
+			success(res) {
+				resolve(res)
+			},
+			fail(err) {
+				reject(err)
+			}
+		})
+	})
+}
+/**
+ * 获取封装数据
+ */
+export const get_file_data = async (files, type = 'image') => {
+	// 最终需要上传数据库的数据
+	let fileFullName = get_file_ext(files.name)
+	const extname = fileFullName.ext.toLowerCase()
+	let filedata = {
+		name: files.name,
+		uuid: files.uuid,
+		extname: extname || '',
+		cloudPath: files.cloudPath,
+		fileType: files.fileType,
+		thumbTempFilePath: files.thumbTempFilePath,
+		url: files.path || files.path,
+		size: files.size, //单位是字节
+		image: {},
+		path: files.path,
+		video: {}
+	}
+	if (type === 'image') {
+		const imageinfo = await get_file_info(files.path)
+		delete filedata.video
+		filedata.image.width = imageinfo.width
+		filedata.image.height = imageinfo.height
+		filedata.image.location = imageinfo.path
+	} else {
+		delete filedata.image
+	}
+	return filedata
+}

+ 105 - 0
uni_modules/uni-file-picker/package.json

@@ -0,0 +1,105 @@
+{
+  "id": "uni-file-picker",
+  "displayName": "uni-file-picker 文件选择上传",
+  "version": "1.1.3",
+  "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "图片上传",
+    "文件上传"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "",
+    "uni-app": "^4.33",
+    "uni-app-x": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue",
+    "darkmode": "x",
+    "i18n": "x",
+    "widescreen": "x"
+  },
+  "uni_modules": {
+    "dependencies": [
+      "uni-scss"
+    ],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "√",
+        "aliyun": "√",
+        "alipay": "√"
+      },
+      "client": {
+        "uni-app": {
+          "vue": {
+            "vue2": "√",
+            "vue3": "√"
+          },
+          "web": {
+            "safari": "√",
+            "chrome": "√"
+          },
+          "app": {
+            "vue": "√",
+            "nvue": "-",
+            "android": "√",
+            "ios": "√",
+            "harmony": "√"
+          },
+          "mp": {
+            "weixin": "√",
+            "alipay": "√",
+            "toutiao": "√",
+            "baidu": "√",
+            "kuaishou": "√",
+            "jd": "-",
+            "harmony": "-",
+            "qq": "√",
+            "lark": "-"
+          },
+          "quickapp": {
+            "huawei": "√",
+            "union": "√"
+          }
+        },
+        "uni-app-x": {
+          "web": {
+            "safari": "-",
+            "chrome": "-"
+          },
+          "app": {
+            "android": "-",
+            "ios": "-",
+            "harmony": "-"
+          },
+          "mp": {
+            "weixin": "-"
+          }
+        }
+      }
+    }
+  }
+}

+ 10 - 0
uni_modules/uni-file-picker/readme.md

@@ -0,0 +1,10 @@
+
+## FilePicker 文件选择上传
+
+> **组件名:uni-file-picker**
+>  代码块: `uFilePicker`
+
+
+文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker)

+ 169 - 0
utils/jVerificationLogin.js

@@ -0,0 +1,169 @@
+/**
+ * 极光一键登录封装
+ * 功能:1. 判断登录态 2. 未登录则弹出极光一键登录界面 3. 登录成功后执行自定义回调
+ */
+import { isProduction } from '@/common/config'
+import HttpRequest from '@/common/httpRequest'
+
+// 1. 引入极光插件
+const jv = uni.requireNativePlugin('JG-JVerification');
+
+// 2. 全局状态(避免重复初始化、保存协议勾选状态)
+const jvState = {
+	isInit: false, // SDK 是否已初始化
+};
+
+/**
+ * 初始化极光 SDK(仅初始化一次)
+ */
+function initJVerification() {
+	return new Promise((resolve, reject) => {
+		// 已初始化,直接返回成功
+		if (jvState.isInit) {
+			resolve(true);
+			return;
+		}
+
+		// 初始化 SDK
+		jv.init({
+			timeout: 7000,
+			isProduction: isProduction // 生产环境改为 true
+		}, (result) => {
+			if (result.code === 8000) { // 初始化成功(极光返回 code=0 为成功)
+				jvState.isInit = true;
+				console.log('极光 SDK 初始化成功');
+				resolve(true);
+			} else {
+				reject(new Error(result.content || '初始化失败'));
+			}
+		});
+	});
+}
+
+/**
+ * 配置极光一键登录自定义 UI
+ */
+function configJVerificationUI() {
+	if (uni.getSystemInfoSync().platform == "ios") {
+		// iOS平台
+	} else {
+		// Android平台
+		jv.setCustomUIWithConfigAndroid({
+			setLogoImgPath: 'static/invite.png',
+			setLogoWidth: 200,
+			setLogoHeight: 200,
+			setNumFieldOffsetY: 340,
+			setSloganOffsetY: 380,
+			setLogBtnText: '一键登录',
+			setLogBtnImgPath: 'static/login-btn.png',
+			setLogBtnHeight: 40,
+			setLogBtnOffsetY: 418,
+			setNavReturnImgPath: 'static/close.png',
+			setNavReturnBtnWidth: 20,
+			setNavReturnBtnHeight: 20,
+			setNavReturnBtnOffsetX: 20,
+			setNavReturnBtnOffsetY: 18,
+			setNavTransparent: true,
+			setNumberColor: 0xff333333,
+			setNumberSize: 28,
+			setPrivacyOffsetX: 30,
+			setPrivacyTextSize: 12,
+			setCheckedImgPathFromJs: 'static/checked.png',
+			setUncheckedImgPathFromJs: 'static/unchecked.png',
+			setPrivacyCheckboxSize: 12,
+			setPrivacyWithBookTitleMark: true,
+			setAppPrivacyColor: [0xff605D67, 0xff016BF6],
+			enableHintToast: true,
+			enableHintToastText: '请点击同意协议',
+			setPrivacyNameAndUrlBeanList: [{
+					name: '亿职赞用户协议',
+					url: 'https://h5.bosszan.com/my/setting/xieyi',
+					beforeName: '、'
+				},
+				{
+					name: '隐私协议',
+					url: 'https://h5.bosszan.com/my/setting/mimi',
+					beforeName: '和'
+				},
+			],
+			setPrivacyNavColor: 0xff016BF6,
+		});
+	}
+}
+
+/**
+ * 执行极光一键登录核心逻辑
+ */
+function doJVerificationLogin(callback) {
+	return new Promise((resolve, reject) => {
+		// 预登录(提升登录速度,极光推荐)
+		jv.preLogin(7000, (preResult) => {
+			if (preResult.code !== 7000) {
+				// 预取号失败
+				plus.nativeUI.toast('一键登录失败')
+				reject(new Error(preResult.content || '预取号失败'));
+				return;
+			}
+
+			// 配置自定义 UI
+			configJVerificationUI();
+
+			// 显示一键登录界面
+			jv.loginAuth({
+				autoFinish: false,
+				timeout: 5000
+			}, (loginRes) => {
+				console.log(' loginRes ', loginRes)
+				if (loginRes.code == 6000) {
+					// 成功获取运营商的loginToken
+					// 请求后端接口进行登录
+					const params = {
+						...loginRes,
+						platform: 'app'
+					}
+					HttpRequest
+						.postJson('/app/Login/jpushLogin', params)
+						.then((response) => {
+							if (response.code === 0) {
+								if (typeof callback === 'function') {
+									callback(response);
+								}
+								// 返回 accessToken,供调用方后续换取手机号
+								resolve(true);
+								return
+							}
+							plus.nativeUI.toast('登录失败:', response.msg)
+						})
+				} else if (loginRes.code == 6002) {
+					// 用户取消了一键登录
+				} else {
+					// 其他情况则跳转到手机验证码登录
+					plus.nativeUI.toast('一键登录失败')
+				}
+			}, (event) => {
+				// 监听协议勾选/取消事件
+			});
+		});
+	});
+}
+
+/**
+ * 关闭授权弹窗
+ */
+export function closeVerifyView() {
+	// #ifdef APP-PLUS
+	jv.dismissLoginAuth(true);
+	// #endif
+}
+
+/**
+ * 对外暴露的核心方法:极光一键登录入口
+ * @param {Function} callback // 成功后执行
+ */
+export function jVerificationLogin(callback) {
+	initJVerification()
+		.then(() => doJVerificationLogin(callback))
+		.catch((err) => {
+			console.error('极光一键登录失败:', err.message);
+		});
+}

+ 101 - 0
utils/login.js

@@ -0,0 +1,101 @@
+import HttpRequest from '@/common/httpRequest'
+import queue from '@/common/queue'
+
+/**
+ * 处理微信登录
+ */
+export function wxLogin(callback) {
+	uni.showLoading({
+		title: '登录中'
+	})
+	uni.login({
+		provider: 'weixin',
+		success: (wxRes) => {
+			queue.setData('openId', wxRes.authResult.openid)
+			queue.setData('unionId', wxRes.authResult.unionId)
+			let data = {
+				wxOpenId: loginRes.authResult.openid,
+				token: loginRes.authResult.access_token
+			}
+			HttpRequest
+				.postT('/app/Login/wxAppLogin', data)
+				.then(res => {
+					uni.hideLoading()
+					if (typeof callback == 'function') {
+						callback(res)
+					}
+					login(res)
+				})
+				.catch(() => {
+					if (typeof callback == 'function') {
+						callback(false)
+					}
+					uni.hideLoading()
+				})
+		},
+		fail: () => {
+			uni.hideLoading()
+		}
+	})
+}
+
+/**
+ * 正常登录处理
+ */
+export function phoneLogin(params) {
+	uni.showLoading({
+		title: '登录中'
+	})
+	HttpRequest
+		.post('/app/Login/registerAndLogin', {
+			phone: params.phone,
+			msg: params.msg,
+			platform: 'app'
+		})
+		.then(res => {
+			login(res)
+		})
+		.finally(() => {
+			uni.hideLoading()
+		})
+}
+
+/**
+ * 处理登录逻辑
+ */
+export function login(res) {
+	if (res.code == 0) {
+		queue.setData("userId", res.user.userId)
+		queue.setData("token", res.token)
+		queue.setData("phone", res.user.phone)
+		queue.setData("userName", res.user.userName)
+		queue.setData("avatar", res.user.avatar)
+		queue.setData("invitationCode", res.user.invitationCode)
+		queue.setData("inviterCode", res.user.inviterCode)
+		getIsVip()
+		uni.hideLoading()
+		if (res.user.userType) {
+			queue.setData("userType", res.user.userType)
+			uni.reLaunch({
+				url: '/pages/my/index',
+			})
+		} else {
+			uni.reLaunch({
+				url: '/pages/my/jobApplicant/guidePage',
+			})
+		}
+	} else {
+		plus.nativeUI.toast('出错了:', res.msg)
+	}
+}
+
+/**
+ * 获取vip数据
+ */
+function getIsVip() {
+	HttpRequest.get("/app/UserVip/isUserVip").then((res) => {
+		if (res.code == 0) {
+			queue.setData("isVip", res.data);
+		}
+	});
+}

+ 32 - 0
utils/util.js

@@ -0,0 +1,32 @@
+// 金额格式化成K格式
+export const formatNumberToK = (param) => {
+    // 1. 异常处理:空值(null/undefined)直接返回空字符串
+    if (param === null || param === undefined) {
+        return '';
+    }
+
+    // 2. 统一预处理:无论参数是数字/字符串,都转为字符串并去除首尾空格
+    let numStr = String(param).trim();
+    
+    // 3. 空字符串情况直接返回
+    if (numStr === '') {
+        return '';
+    }
+
+    // 4. 将处理后的字符串转为数字
+    const num = Number(numStr);
+    
+    // 5. 非有效数字的情况,返回原始参数的字符串形式(保持输入原貌)
+    if (isNaN(num)) {
+        return String(param);
+    }
+
+    // 6. 小于1000的数字,返回整数形式的字符串(无小数)
+    if (num < 1000) {
+        return String(Math.floor(num));
+    }
+
+    // 7. 大于等于1000的数字,除以1000取整后拼接K
+    const kNum = Math.floor(num / 1000);
+    return `${kNum}K`;
+};

Некоторые файлы не были показаны из-за большого количества измененных файлов