feat: 登录页密码+验证码双模式 / 首页岗位优化 / 法律页面 / 后端接口完善

- 前端:登录页重构,支持密码登录、验证码登录、注册三种模式
- 前端:首页热门岗位添加「参考示例」标签,去虚构数据
- 前端:面试页顶部优化,岗位名+状态标签展示
- 前端:新增用户协议、隐私政策页面及免责声明
- 后端:新增 POST /api/user/register 注册接口
- 后端:新增 POST /api/user/set-password 设置密码接口
- 后端:修复 user.schema.ts unique 索引导致 null 冲突问题
- 后端:新增 payment-order.schema、positions.schema、site-config.schema
- 后端:package.json 新增 postbuild 脚本自动复制证书
- 管理后台:新增订单管理 Tab
This commit is contained in:
yuzhiran
2026-06-09 15:39:17 +08:00
parent 511f60d0db
commit 37cfdfe93c
27 changed files with 1045 additions and 195 deletions
+25 -10
View File
@@ -74,19 +74,27 @@
<!-- 热门岗位 -->
<view class="section">
<view class="section-header">
<text class="section-title">热门岗位</text>
<view class="section-title-row">
<text class="section-title">热门岗位</text>
<text class="section-tag-demo">参考示例</text>
</view>
<text class="section-desc">点击直接面试</text>
</view>
<view class="position-list card" v-if="!positionsLoading">
<view class="pos-item" v-for="(pos, idx) in hotPositions" :key="idx" @click="startInterview(pos)">
<view class="pos-left">
<view class="pos-rank">{{ idx + 1 }}</view>
<text class="pos-icon">{{ posIcons[idx] || '💼' }}</text>
<view class="pos-body">
<text class="pos-name">{{ pos.name }}</text>
<text class="pos-company">{{ pos.company }}</text>
<view class="pos-meta-row">
<text class="pos-company">{{ pos.company || '参考公司' }}</text>
<text class="pos-salary">{{ pos.salary || '参考薪资' }}</text>
</view>
</view>
</view>
<text class="pos-salary">{{ pos.salary }}</text>
<view class="pos-action">
<text class="pos-action-text">立即模拟</text>
</view>
</view>
</view>
<view class="loading-tip" v-if="positionsLoading">加载岗位中...</view>
@@ -103,6 +111,7 @@ import { api } from '../../config'
const userInfo = ref(null)
const greeting = ref('')
const hotPositions = ref([])
const posIcons = ['💻', '⚙️', '🤖', '📊', '🎨', '🧪', '📱', '🔧']
const positionsLoading = ref(true)
const dailyQuestion = ref(null)
const showAnswer = ref(false)
@@ -175,8 +184,10 @@ const startInterview = (pos) => uni.navigateTo({ url: `/pages/interview/intervie
.section { padding: 32rpx 32rpx 0; }
.section:first-of-type { margin-top: -40rpx; padding-top: 0; }
.section-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 20rpx; }
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
.section-title { font-size: 30rpx; font-weight: 700; color: var(--color-text); }
.section-title-row { display: flex; align-items: center; gap: 12rpx; }
.section-tag-demo { font-size: 18rpx; color: #9CA3AF; background: #F3F4F6; padding: 2rpx 10rpx; border-radius: 6rpx; }
.section-desc { font-size: 22rpx; color: var(--color-primary); }
.feature-list { display: flex; flex-direction: column; gap: 16rpx; }
@@ -217,12 +228,16 @@ const startInterview = (pos) => uni.navigateTo({ url: `/pages/interview/intervie
.position-list { border-radius: var(--radius-lg); overflow: hidden; }
.pos-item { padding: 24rpx 28rpx; border-bottom: 1rpx solid var(--color-border); display: flex; justify-content: space-between; align-items: center; }
.pos-item:last-child { border-bottom: none; }
.pos-left { display: flex; align-items: center; gap: 16rpx; }
.pos-rank { width: 40rpx; height: 40rpx; border-radius: 10rpx; background: #F3F4F6; display: flex; align-items: center; justify-content: center; font-size: 22rpx; font-weight: 700; color: var(--color-primary); flex-shrink: 0; }
.pos-body { display: flex; flex-direction: column; }
.pos-item:active { background: var(--color-bg); }
.pos-left { display: flex; align-items: center; gap: 16rpx; flex: 1; min-width: 0; }
.pos-icon { font-size: 36rpx; width: 56rpx; height: 56rpx; display: flex; align-items: center; justify-content: center; background: #F3F4F6; border-radius: 14rpx; flex-shrink: 0; }
.pos-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.pos-name { font-size: 28rpx; font-weight: 600; color: var(--color-text); }
.pos-company { font-size: 22rpx; color: var(--color-text-tertiary); margin-top: 4rpx; }
.pos-salary { font-size: 24rpx; color: var(--color-primary); font-weight: 600; }
.pos-meta-row { display: flex; align-items: center; gap: 10rpx; margin-top: 4rpx; }
.pos-company { font-size: 20rpx; color: var(--color-text-tertiary); }
.pos-salary { font-size: 20rpx; color: var(--color-primary); background: #EEF2FF; padding: 2rpx 10rpx; border-radius: 6rpx; }
.pos-action { flex-shrink: 0; margin-left: 16rpx; }
.pos-action-text { font-size: 22rpx; color: var(--color-primary); font-weight: 600; }
.loading-tip { text-align: center; padding: 40rpx; font-size: 24rpx; color: var(--color-text-tertiary); background: #FFF; border-radius: var(--radius-lg); }
.bottom-spacer { height: 40rpx; }
</style>