index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. <template>
  2. <view class="detail-container">
  3. <scroll-view scroll-y class="scroll-wrapper">
  4. <!-- 顶部基础信息 -->
  5. <view class="section header-section">
  6. <view class="title-line">
  7. <view class="left-box">
  8. <text class="job-name">{{ jobInfo.postName || '' }}</text>
  9. <text class="urgent-tag" v-if="jobInfo.isUrgent === 1">急招</text>
  10. </view>
  11. <text class="salary">{{ jobInfo.salaryRange || '面议' }}</text>
  12. </view>
  13. <view class="meta-list">
  14. <view class="meta-item">
  15. <image src="/static/icons/location.svg" class="icon" mode="aspectFit"></image>
  16. <text>{{ jobInfo.workProvince || '' }}{{ jobInfo.workCity ? '·' + jobInfo.workCity : '' }}{{ jobInfo.workDistrict ? '·' + jobInfo.workDistrict : '' }}</text>
  17. </view>
  18. <view class="meta-item">
  19. <image src="/static/icons/user.svg" class="icon" mode="aspectFit"></image>
  20. <text>招录 {{ jobInfo.recruitNum || 1 }} 人</text>
  21. </view>
  22. <view class="meta-item">
  23. <image src="/static/icons/time.svg" class="icon" mode="aspectFit"></image>
  24. <text>截止时间:{{ jobInfo.registrationEndDate ? jobInfo.registrationEndDate.split(' ')[0] : '长期有效' }}</text>
  25. </view>
  26. </view>
  27. </view>
  28. <!-- HR 信息 -->
  29. <view class="section hr-section">
  30. <image :src="jobInfo.hrAvatar || jobInfo.companyAvatar || '/static/images/hr_avatar.svg'" class="hr-avatar" mode="aspectFill"></image>
  31. <view class="hr-content">
  32. <text class="hr-name">{{ jobInfo.companyName || '平台推荐' }}</text>
  33. <text class="hr-desc">人事负责人</text>
  34. </view>
  35. </view>
  36. <!-- 岗位详情内容 -->
  37. <view class="section detail-content">
  38. <view class="section-title">岗位详情</view>
  39. <!-- 标签云 -->
  40. <view class="tag-cloud">
  41. <text class="tag-label" v-for="tag in tags" :key="tag">{{ tag }}</text>
  42. </view>
  43. <!-- 岗位描述(富文本,来自数据库 post_description) -->
  44. <view class="rich-text-wrapper" v-if="jobInfo.postDescription">
  45. <rich-text :nodes="jobInfo.postDescription"></rich-text>
  46. </view>
  47. <view class="info-block" v-else>
  48. <text class="block-text">暂无岗位详情</text>
  49. </view>
  50. </view>
  51. <!-- 办公地址 - 接入动态地图并使用气泡显示地址 -->
  52. <view class="section address-section">
  53. <view class="section-title">办公地址</view>
  54. <view class="map-wrapper">
  55. <map
  56. class="map-view"
  57. :latitude="latitude"
  58. :longitude="longitude"
  59. :markers="markers"
  60. :scale="16"
  61. show-location
  62. ></map>
  63. </view>
  64. </view>
  65. <!-- 公司概况 -->
  66. <view class="section company-section">
  67. <view class="company-header">
  68. <image :src="jobInfo.companyAvatar || '/static/images/logo1.png'" class="company-logo" mode="aspectFit"></image>
  69. <text class="company-name">{{ jobInfo.companyName || '未知企业' }}</text>
  70. </view>
  71. <text class="company-desc">
  72. 这是一段公司的概览信息,暂无详细内容。
  73. </text>
  74. </view>
  75. <!-- 到底提示 -->
  76. <view class="end-tip">
  77. <text>— 已到底啦~ —</text>
  78. </view>
  79. <view class="safe-bottom-holder"></view>
  80. </scroll-view>
  81. <!-- 底部操作栏 -->
  82. <view class="bottom-action-bar">
  83. <view class="collect-box" @click="toggleCollect">
  84. <image :src="isCollected ? '/static/icons/star_filled.svg' : '/static/icons/star_hollow.svg'"
  85. class="star-icon"
  86. mode="aspectFit"></image>
  87. <view class="collect-text">{{ isCollected ? '已收藏' : '收藏' }}</view>
  88. </view>
  89. <view
  90. class="consult-btn"
  91. :class="[jobState]"
  92. @click="handleMainAction"
  93. >
  94. <text>{{ btnText }}</text>
  95. </view>
  96. </view>
  97. </view>
  98. </template>
  99. <script setup lang="js">
  100. import { ref, computed, onUnmounted } from 'vue';
  101. import { onShow, onLoad } from '@dcloudio/uni-app';
  102. import { getPositionDetail } from '../../api/position.js';
  103. import { createOrGetSession } from '../../api/message.js';
  104. import { addCollection, delCollection, checkCollection } from '../../api/collection.js';
  105. import { getAssessmentRecordList, getAssessmentList } from '../../api/assessment.js';
  106. import { listOrder } from '../../api/order.js';
  107. const isCollected = ref(false);
  108. const collectionId = ref(null);
  109. const jobInfo = ref({});
  110. const tags = ref([]);
  111. // 岗位状态: 'initial', 'paid', 'assessed', 'added'
  112. const jobState = ref('initial');
  113. const btnText = computed(() => {
  114. switch (jobState.value) {
  115. case 'initial': return '咨询';
  116. case 'paid': return '开始测评';
  117. case 'assessed': return '投递简历';
  118. case 'added': return '已投递';
  119. default: return '咨询';
  120. }
  121. });
  122. const positionId = ref(null);
  123. const checkState = async () => {
  124. console.log('Checking job state for position:', positionId.value);
  125. const userInfo = uni.getStorageSync('userInfo');
  126. if (!userInfo || !userInfo.studentId || !positionId.value) {
  127. jobState.value = 'initial';
  128. return;
  129. }
  130. // ① 已投递过 → 直接显示已投递
  131. const appliedKey = `candidate_applied_${positionId.value}`;
  132. if (uni.getStorageSync(appliedKey)) {
  133. jobState.value = 'added';
  134. return;
  135. }
  136. try {
  137. // 先确保岗位基础信息已加载,避免异步竞态导致提取 postName 失败
  138. if (!jobInfo.value || !jobInfo.value.postName) {
  139. const jobRes = await getPositionDetail(positionId.value);
  140. if (jobRes.code === 200 && jobRes.data) {
  141. jobInfo.value = jobRes.data;
  142. }
  143. }
  144. // ② 查询该岗位关联的测评列表
  145. const evalRes = await getAssessmentList({ positionId: positionId.value, pageNum: 1, pageSize: 100 });
  146. // 兜底 key:chat.vue 在 assessmentId 为空时会写入此 key
  147. const fallbackPaidKey = `audit_paid_pos_${positionId.value}`;
  148. const isFallbackPaid = !!uni.getStorageSync(fallbackPaidKey);
  149. // 检查该岗位下是否有测评已支付:
  150. let isAnyEvalPaid = (evalRes.code === 200 && evalRes.rows)
  151. ? evalRes.rows.some(e => uni.getStorageSync(`audit_paid_${e.id}`)) || isFallbackPaid
  152. : isFallbackPaid || !!uni.getStorageSync(`audit_paid_${positionId.value}`);
  153. // 【新增后端订单校验】如果缓存没有记录为支付,调用后端订单接口二次确认,防止由于换机或缓存释放造成的遗漏
  154. if (!isAnyEvalPaid) {
  155. try {
  156. const orderRes = await listOrder({
  157. orderStatus: 1,
  158. buyerId: userInfo.studentId
  159. });
  160. if (orderRes.code === 200 && orderRes.rows) {
  161. isAnyEvalPaid = orderRes.rows.some(order => {
  162. const remark = order.remark || '';
  163. const matchPost = jobInfo.value?.postName && remark.includes(jobInfo.value.postName);
  164. const matchEval = evalRes.rows && evalRes.rows.some(e => e.evaluationName && remark.includes(e.evaluationName));
  165. return matchPost || matchEval;
  166. });
  167. }
  168. } catch (e) {
  169. console.error('[JobDetail] 订单校验失败:', e);
  170. }
  171. }
  172. console.log('[JobDetail] 支付状态最终综合判定:', isAnyEvalPaid);
  173. if (evalRes.code !== 200 || !evalRes.rows || evalRes.rows.length === 0) {
  174. // 无关联测评,检查是否已支付
  175. jobState.value = isAnyEvalPaid ? 'paid' : 'initial';
  176. return;
  177. }
  178. // 保存第一个关联测评ID(用于“开始测评”跳转时传参,避免 remind 页匹配错误)
  179. linkedAssessmentId.value = evalRes.rows[0].id;
  180. console.log('[JobDetail] 关联测评ID:', linkedAssessmentId.value);
  181. // ③ 获取用户的测评记录
  182. const recordRes = await getAssessmentRecordList(userInfo.studentId);
  183. if (recordRes.code !== 200 || !recordRes.data) {
  184. jobState.value = isAnyEvalPaid ? 'paid' : 'initial';
  185. return;
  186. }
  187. // ④ 检查用户是否通过了该岗位关联的任一测评
  188. const evaluationIds = evalRes.rows.map(e => e.id);
  189. const passedRecord = recordRes.data.find(r =>
  190. r.finalResult === '1' && evaluationIds.includes(r.evaluationId)
  191. );
  192. if (passedRecord) {
  193. // 测评通过 → 显示"投递简历"
  194. jobState.value = 'assessed';
  195. } else if (isAnyEvalPaid) {
  196. // 已付款但未通过测评 → 显示"开始测评"
  197. jobState.value = 'paid';
  198. } else {
  199. // 未支付测评费用,只显示咨询按钮
  200. jobState.value = 'initial';
  201. }
  202. } catch (err) {
  203. console.error('检查状态失败:', err);
  204. jobState.value = 'initial';
  205. }
  206. };
  207. const latitude = ref(31.22863);
  208. const longitude = ref(121.45039);
  209. const markers = ref([]);
  210. const fetchDetail = async (id) => {
  211. console.log('fetchDetail called with id:', id); // 调试日志
  212. uni.showLoading({ title: '加载中...' });
  213. try {
  214. const res = await getPositionDetail(id);
  215. console.log('getPositionDetail响应:', res); // 调试日志
  216. if (res.code === 200 && res.data) {
  217. jobInfo.value = res.data;
  218. positionId.value = res.data.id;
  219. const data = res.data;
  220. // 地图坐标赋值
  221. if (data.latitude && data.longitude) {
  222. latitude.value = parseFloat(data.latitude);
  223. longitude.value = parseFloat(data.longitude);
  224. markers.value = [{
  225. id: 1,
  226. latitude: latitude.value,
  227. longitude: longitude.value,
  228. title: data.postName || '办公地点',
  229. iconPath: '/static/icons/location.svg',
  230. width: 32,
  231. height: 32,
  232. callout: {
  233. content: data.workAddress || (data.workProvince + data.workCity + data.workDistrict),
  234. color: '#333333',
  235. fontSize: 12,
  236. borderRadius: 8,
  237. padding: 8,
  238. bgColor: '#ffffff',
  239. display: 'ALWAYS',
  240. boxShadow: '0 4rpx 12rpx rgba(0,0,0,0.1)'
  241. }
  242. }];
  243. } else {
  244. // 如果没有经纬度,显示一个默认位置或清空标记
  245. markers.value = [];
  246. }
  247. let t = [];
  248. if(data.postTypeLabel) t.push(data.postTypeLabel);
  249. if(data.schoolRequirementLabel) t.push(data.schoolRequirementLabel);
  250. if(data.gradeRequirementLabel) t.push(data.gradeRequirementLabel);
  251. if(data.welfareTags) {
  252. t.push(...data.welfareTags.split(',').filter(Boolean));
  253. }
  254. tags.value = t;
  255. // 检查是否已收藏
  256. console.log('准备检查收藏状态, id:', id);
  257. checkCollectionStatus(id);
  258. }
  259. } catch(err) {
  260. console.error('获取岗位详情失败:', err);
  261. } finally {
  262. uni.hideLoading();
  263. }
  264. };
  265. // 检查收藏状态
  266. const checkCollectionStatus = async (id) => {
  267. console.log('checkCollectionStatus called with id:', id); // 调试日志
  268. const userInfo = uni.getStorageSync('userInfo');
  269. console.log('checkCollectionStatus userInfo:', userInfo); // 调试日志
  270. if (!userInfo || !userInfo.studentId) {
  271. console.log('checkCollectionStatus: 用户未登录');
  272. return;
  273. }
  274. try {
  275. console.log('调用checkCollection API, studentId:', userInfo.studentId, 'targetId:', id, 'type: job'); // 调试日志
  276. const res = await checkCollection(userInfo.studentId, id, 'job');
  277. console.log('checkCollection API响应:', res); // 调试日志
  278. if (res.code === 200 && res.data) {
  279. isCollected.value = true;
  280. collectionId.value = res.data.id; // 保存收藏记录ID
  281. console.log('设置为已收藏, collectionId:', res.data.id);
  282. } else {
  283. isCollected.value = false;
  284. collectionId.value = null;
  285. console.log('设置为未收藏');
  286. }
  287. } catch (err) {
  288. console.error('检查收藏状态失败', err);
  289. }
  290. };
  291. // 切换收藏状态
  292. const toggleCollect = async () => {
  293. console.log('toggleCollect clicked'); // 调试日志
  294. const userInfo = uni.getStorageSync('userInfo');
  295. console.log('userInfo:', userInfo); // 调试日志
  296. if (!userInfo || !userInfo.studentId) {
  297. console.log('用户未登录'); // 调试日志
  298. uni.showToast({ title: '请先登录', icon: 'none' });
  299. setTimeout(() => {
  300. uni.navigateTo({ url: '/pages/login/login' });
  301. }, 1000);
  302. return;
  303. }
  304. if (!jobInfo.value.id) {
  305. console.log('jobInfo.value.id 为空:', jobInfo.value); // 调试日志
  306. return;
  307. }
  308. console.log('准备发送收藏请求, jobInfo.value.id:', jobInfo.value.id); // 调试日志
  309. uni.showLoading({ title: isCollected.value ? '取消收藏中...' : '收藏中...' });
  310. try {
  311. if (isCollected.value) {
  312. // 取消收藏
  313. console.log('取消收藏, collectionId:', collectionId.value); // 调试日志
  314. if (collectionId.value) {
  315. const res = await delCollection(collectionId.value);
  316. console.log('取消收藏响应:', res); // 调试日志
  317. if (res.code === 200) {
  318. isCollected.value = false;
  319. collectionId.value = null;
  320. uni.showToast({ title: '已取消收藏', icon: 'none' });
  321. }
  322. }
  323. } else {
  324. // 添加收藏
  325. const requestData = {
  326. studentId: userInfo.studentId,
  327. targetId: jobInfo.value.id,
  328. type: 'job'
  329. };
  330. console.log('添加收藏请求数据:', requestData); // 调试日志
  331. const res = await addCollection(requestData);
  332. console.log('添加收藏响应:', res); // 调试日志
  333. if (res.code === 200) {
  334. isCollected.value = true;
  335. // 添加成功后重新检查获取完整的收藏记录ID
  336. checkCollectionStatus(jobInfo.value.id);
  337. uni.showToast({ title: '收藏成功', icon: 'success' });
  338. } else {
  339. uni.showToast({ title: res.msg || '收藏失败', icon: 'none' });
  340. }
  341. }
  342. } catch (err) {
  343. console.error('操作收藏失败', err);
  344. uni.showToast({ title: '操作失败', icon: 'none' });
  345. } finally {
  346. uni.hideLoading();
  347. }
  348. };
  349. onLoad((options) => {
  350. if (options.id) {
  351. positionId.value = options.id;
  352. fetchDetail(options.id);
  353. }
  354. // 监听支付完成事件(chat.vue 支付成功后会 emit)
  355. uni.$on('payment_done', (data) => {
  356. console.log('收到 payment_done 事件:', data);
  357. checkState();
  358. });
  359. // 监听简历投递完成事件(select-resume.vue 投递成功后会 emit)
  360. uni.$on('resume_delivered', (data) => {
  361. console.log('收到 resume_delivered 事件:', data);
  362. if (data && data.postId == positionId.value) {
  363. jobState.value = 'added';
  364. }
  365. });
  366. });
  367. onShow(() => {
  368. if (positionId.value) {
  369. checkState();
  370. }
  371. });
  372. onUnmounted(() => {
  373. uni.$off('payment_done');
  374. uni.$off('resume_delivered');
  375. });
  376. const handleMainAction = async () => {
  377. if (jobState.value === 'initial') {
  378. try {
  379. uni.showLoading({ title: '正在连接客服...' });
  380. const userInfo = uni.getStorageSync('userInfo') || {};
  381. const userId = userInfo.studentId || null;
  382. const userName = userInfo.name || '用户';
  383. const userAvatar = userInfo.avatarUrl || '/static/images/user_avatar.svg';
  384. const res = await createOrGetSession({
  385. sessionType: 1,
  386. fromUserId: userId,
  387. fromUserName: userName,
  388. fromUserAvatar: userAvatar,
  389. sourceId: 'job_' + positionId.value
  390. });
  391. uni.hideLoading();
  392. if (res.data) {
  393. const session = res.data;
  394. uni.navigateTo({
  395. url: `/pages/chat/chat?sessionId=${session.sessionId}&sessionNo=${session.sessionNo || ''}&fromUserId=${userId}&userName=${encodeURIComponent(userName)}&jobName=${encodeURIComponent(jobInfo.value.postName || '')}&type=job&positionId=${positionId.value}&salaryRange=${encodeURIComponent(jobInfo.value.salaryRange || '')}&companyName=${encodeURIComponent(jobInfo.value.companyName || '')}&workCity=${encodeURIComponent(jobInfo.value.workCity || '')}&workDistrict=${encodeURIComponent(jobInfo.value.workDistrict || '')}`
  396. });
  397. } else {
  398. uni.showToast({ title: '创建会话失败', icon: 'none' });
  399. }
  400. } catch (err) {
  401. uni.hideLoading();
  402. console.error('创建会话失败:', err);
  403. uni.showToast({ title: '连接失败,请重试', icon: 'none' });
  404. }
  405. } else if (jobState.value === 'paid') {
  406. uni.navigateTo({
  407. url: `/pages/assessment/remind?family=audit&id=${linkedAssessmentId.value || ''}`
  408. });
  409. } else if (jobState.value === 'assessed') {
  410. // 投递简历:跳转到选择简历页面
  411. const userInfo = uni.getStorageSync('userInfo') || {};
  412. if (!userInfo.studentId) {
  413. uni.showToast({ title: '请先登录', icon: 'none' });
  414. setTimeout(() => {
  415. uni.navigateTo({ url: '/pages/login/login' });
  416. }, 1000);
  417. return;
  418. }
  419. uni.navigateTo({
  420. url: `/pages/my/select-resume?postId=${positionId.value}`
  421. });
  422. }
  423. };
  424. const updateState = (state) => {
  425. jobState.value = state;
  426. };
  427. // 状态逻辑已移至 checkState 内部
  428. </script>
  429. <style lang="scss" scoped>
  430. .detail-container {
  431. width: 100%;
  432. height: 100vh;
  433. background-color: #FFFFFF;
  434. display: flex;
  435. flex-direction: column;
  436. }
  437. .scroll-wrapper {
  438. flex: 1;
  439. }
  440. .section {
  441. padding: 30rpx 40rpx;
  442. background-color: #FFFFFF;
  443. }
  444. .section-title {
  445. font-size: 34rpx;
  446. font-weight: bold;
  447. color: #1A1A1A;
  448. margin-bottom: 30rpx;
  449. }
  450. /* 头部样式 */
  451. .header-section {
  452. padding-top: 20rpx;
  453. .title-line {
  454. display: flex;
  455. justify-content: space-between;
  456. align-items: center;
  457. margin-bottom: 24rpx;
  458. .left-box {
  459. display: flex;
  460. align-items: center;
  461. .job-name {
  462. font-size: 44rpx;
  463. font-weight: bold;
  464. color: #1A1A1A;
  465. margin-right: 16rpx;
  466. }
  467. .urgent-tag {
  468. font-size: 20rpx;
  469. color: #FF4D4F;
  470. border: 1rpx solid #FF4D4F;
  471. padding: 2rpx 10rpx;
  472. border-radius: 6rpx;
  473. }
  474. }
  475. .salary {
  476. font-size: 38rpx;
  477. font-weight: bold;
  478. color: #1F6CFF;
  479. }
  480. }
  481. .meta-list {
  482. .meta-item {
  483. display: flex;
  484. align-items: center;
  485. margin-bottom: 12rpx;
  486. font-size: 26rpx;
  487. color: #777777;
  488. .icon {
  489. width: 30rpx;
  490. height: 30rpx;
  491. margin-right: 12rpx;
  492. opacity: 0.7;
  493. }
  494. .warning-text {
  495. color: #FF4D4F;
  496. margin-left: 16rpx;
  497. }
  498. }
  499. }
  500. }
  501. /* HR 区域 */
  502. .hr-section {
  503. border-top: 1rpx solid #F8F9FB;
  504. display: flex;
  505. align-items: center;
  506. padding: 30rpx 40rpx;
  507. .hr-avatar {
  508. width: 100rpx;
  509. height: 100rpx;
  510. border-radius: 50%;
  511. margin-right: 24rpx;
  512. background-color: #F0F0F0;
  513. }
  514. .hr-content {
  515. .hr-name {
  516. display: block;
  517. font-size: 30rpx;
  518. font-weight: bold;
  519. color: #1A1A1A;
  520. margin-bottom: 4rpx;
  521. }
  522. .hr-desc {
  523. font-size: 24rpx;
  524. color: #888888;
  525. }
  526. }
  527. }
  528. /* 岗位详情描述 */
  529. .detail-content {
  530. border-top: 10rpx solid #F8F9FB;
  531. .tag-cloud {
  532. display: flex;
  533. flex-wrap: wrap;
  534. gap: 16rpx;
  535. margin-bottom: 40rpx;
  536. .tag-label {
  537. background-color: #F5F7FA;
  538. color: #888888;
  539. font-size: 24rpx;
  540. padding: 10rpx 24rpx;
  541. border-radius: 6rpx;
  542. }
  543. }
  544. .rich-text-wrapper {
  545. font-size: 28rpx;
  546. color: #555555;
  547. line-height: 1.8;
  548. }
  549. .info-block {
  550. margin-bottom: 24rpx;
  551. .block-label {
  552. display: block;
  553. font-size: 28rpx;
  554. font-weight: bold;
  555. color: #1A1A1A;
  556. margin-bottom: 8rpx;
  557. }
  558. .block-text {
  559. font-size: 28rpx;
  560. color: #555555;
  561. line-height: 1.6;
  562. }
  563. .list-item {
  564. display: flex;
  565. margin-bottom: 8rpx;
  566. .dot {
  567. font-weight: bold;
  568. margin-right: 12rpx;
  569. color: #1A1A1A;
  570. }
  571. .list-text {
  572. font-size: 28rpx;
  573. color: #555555;
  574. line-height: 1.6;
  575. }
  576. }
  577. }
  578. }
  579. /* 办公地址区 - 动态地图 */
  580. .address-section {
  581. border-top: 10rpx solid #F8F9FB;
  582. .map-wrapper {
  583. position: relative;
  584. width: 100%;
  585. height: 360rpx;
  586. border-radius: 12rpx;
  587. overflow: hidden;
  588. box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.06);
  589. .map-view {
  590. width: 100%;
  591. height: 100%;
  592. }
  593. }
  594. }
  595. /* 公司概况区 */
  596. .company-section {
  597. border-top: 10rpx solid #F8F9FB;
  598. .company-header {
  599. display: flex;
  600. align-items: center;
  601. margin-bottom: 24rpx;
  602. .company-logo {
  603. width: 80rpx;
  604. height: 80rpx;
  605. border-radius: 12rpx;
  606. background: #f0f0f0;
  607. margin-right: 20rpx;
  608. }
  609. .company-name {
  610. font-size: 32rpx;
  611. font-weight: bold;
  612. color: #1A1A1A;
  613. }
  614. }
  615. .company-desc {
  616. font-size: 28rpx;
  617. color: #666666;
  618. line-height: 1.6;
  619. }
  620. }
  621. .end-tip {
  622. padding: 60rpx 0;
  623. text-align: center;
  624. text {
  625. font-size: 24rpx;
  626. color: #CCCCCC;
  627. }
  628. }
  629. .safe-bottom-holder {
  630. height: 140rpx;
  631. }
  632. /* 底部栏 */
  633. .bottom-action-bar {
  634. position: fixed;
  635. bottom: 0;
  636. left: 0;
  637. right: 0;
  638. height: 120rpx;
  639. background-color: #FFFFFF;
  640. display: flex;
  641. align-items: center;
  642. padding: 0 40rpx;
  643. padding-bottom: env(safe-area-inset-bottom);
  644. box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.08);
  645. z-index: 999;
  646. .collect-box {
  647. display: flex;
  648. flex-direction: column;
  649. align-items: center;
  650. width: 80rpx;
  651. margin-right: 40rpx;
  652. background: transparent; /* 强制背景透明 */
  653. .star-icon {
  654. width: 44rpx;
  655. height: 44rpx;
  656. margin-bottom: 4rpx;
  657. background: transparent;
  658. display: block;
  659. }
  660. .collect-text {
  661. font-size: 20rpx;
  662. color: #999999;
  663. }
  664. }
  665. .consult-btn {
  666. flex: 1;
  667. background-color: #FFB700;
  668. height: 88rpx;
  669. border-radius: 44rpx;
  670. display: flex;
  671. align-items: center;
  672. justify-content: center;
  673. transition: all 0.3s;
  674. text {
  675. color: #FFFFFF;
  676. font-size: 32rpx;
  677. font-weight: bold;
  678. }
  679. &.initial, &.paid {
  680. background-color: #FFB700;
  681. box-shadow: 0 8rpx 16rpx rgba(255, 183, 0, 0.2);
  682. }
  683. &.assessed {
  684. background-color: #1F6CFF; // 测评通过后变为蓝色,投递简历
  685. box-shadow: 0 8rpx 16rpx rgba(31, 108, 255, 0.2);
  686. }
  687. &.added {
  688. background-color: #E0E0E0 !important; // 已投递后禁用灰色
  689. box-shadow: none !important;
  690. pointer-events: none;
  691. text {
  692. color: #999 !important;
  693. }
  694. }
  695. &:active {
  696. opacity: 0.9;
  697. }
  698. }
  699. }
  700. </style>