index.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. <template>
  2. <view class="container">
  3. <!-- 个人信息卡片 -->
  4. <view class="card">
  5. <!-- 手机号 -->
  6. <view class="form-item">
  7. <text class="label">手机号</text>
  8. <view class="input-box">
  9. <view class="prefix-area">
  10. <text class="prefix">+86</text>
  11. <text class="arrow-down">﹀</text>
  12. </view>
  13. <input class="input" type="number" v-model="formData.mobile" />
  14. </view>
  15. </view>
  16. <!-- 验证码 -->
  17. <!-- <view class="form-item">
  18. <text class="label">验证码</text>
  19. <view class="input-box">
  20. <input class="input" type="number" v-model="formData.code" placeholder="验证码" />
  21. <text class="get-code-text" @click="getVerifyCode">{{ countDown > 0 ? countDown + 's' : '获取验证码' }}</text>
  22. </view>
  23. </view> -->
  24. <!-- 姓名 -->
  25. <view class="form-item">
  26. <text class="label">姓名</text>
  27. <view class="input-box">
  28. <input class="input" type="text" v-model="formData.name" />
  29. </view>
  30. </view>
  31. <!-- 性别 -->
  32. <view class="form-item">
  33. <text class="label">性别</text>
  34. <view class="gender-group">
  35. <view class="radio-item" @click="formData.gender = 1">
  36. <text class="radio-icon" :class="{ active: formData.gender === 1 }">{{ formData.gender === 1 ? '♂' : '○'
  37. }}</text>
  38. <text class="radio-label" :class="{ active: formData.gender === 1 }"> 男</text>
  39. </view>
  40. <view class="radio-item" @click="formData.gender = 2">
  41. <text class="radio-icon" :class="{ active: formData.gender === 2 }">{{ formData.gender === 2 ? '♀' : '○'
  42. }}</text>
  43. <text class="radio-label" :class="{ active: formData.gender === 2 }"> 女</text>
  44. </view>
  45. </view>
  46. </view>
  47. <!-- 生日 -->
  48. <view class="form-item">
  49. <text class="label">生日</text>
  50. <view class="input-box" @click="openPicker">
  51. <text>{{ formData.birthday || '请选择生日' }}</text>
  52. <!-- 灰色箭头 SVG -->
  53. <svg class="arrow-right" style="width:24rpx; height:24rpx; margin-left: auto;" viewBox="0 0 1024 1024"
  54. version="1.1" xmlns="http://www.w3.org/2000/svg">
  55. <path
  56. d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-45.056L382.592 149.312a29.12 29.12 0 0 0-41.728 0z"
  57. fill="#CCCCCC"></path>
  58. </svg>
  59. </view>
  60. </view>
  61. <!-- 密码 -->
  62. <view class="form-item">
  63. <text class="label">密码</text>
  64. <view class="input-box">
  65. <input class="input" :password="!showPwd" v-model="formData.password" placeholder="设置登录密码" />
  66. <view class="monkey-icon" @click="showPwd = !showPwd">
  67. <template v-if="showPwd">
  68. <!-- 睁眼线框图标 -->
  69. <svg class="svg-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  70. <path
  71. d="M12 4.5C7 4.5 2.73 7.61 1 12C2.73 16.39 7 19.5 12 19.5C17 19.5 21.27 16.39 23 12C21.27 7.61 17 4.5 12 4.5ZM12 17C9.24 17 7 14.76 7 12C7 9.24 9.24 7 12 7C14.76 7 17 9.24 17 12C17 14.76 14.76 17 12 17ZM12 9C10.34 9 9 10.34 9 12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12C15 10.34 13.66 9 12 9Z"
  72. fill="#CCCCCC" />
  73. </svg>
  74. </template>
  75. <template v-else>
  76. <!-- 闭眼线框图标 (带睫毛) -->
  77. <svg class="svg-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  78. <path d="M12 7C7 7 2.73 10.11 1 14.5" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" />
  79. <path d="M23 14.5C21.27 10.11 17 7 12 7" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" />
  80. <path d="M12 7V4" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" />
  81. <path d="M16 8L18 5" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" />
  82. <path d="M8 8L6 5" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" />
  83. <path d="M20 10L22 8" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" />
  84. <path d="M4 10L2 8" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" />
  85. </svg>
  86. </template>
  87. </view>
  88. </view>
  89. </view>
  90. </view>
  91. <!-- 服务类型卡片 -->
  92. <view class="card">
  93. <view class="section-title">服务类型</view>
  94. <view class="service-types">
  95. <view class="type-btn" v-for="(item, index) in serviceTypes" :key="item.id"
  96. :class="{ selected: formData.serviceType.includes(item.id) }" @click="toggleService(item)">
  97. {{ item.name }}
  98. </view>
  99. </view>
  100. <!-- 所属站点 -->
  101. <view class="form-item">
  102. <text class="label">所属站点</text>
  103. <view class="input-box" @click="openStationPickerCascader">
  104. <view class="station-display" v-if="formData.station">
  105. <text class="area-tag" v-if="formData.areaPath">{{ formData.areaPath }}</text>
  106. <text class="station-name">{{ formData.station }}</text>
  107. </view>
  108. <text v-else style="color: #ccc">请选择所属站点</text>
  109. <!-- 灰色箭头 SVG -->
  110. <svg class="arrow-right" style="width:24rpx; height:24rpx; margin-left: auto;" viewBox="0 0 1024 1024"
  111. version="1.1" xmlns="http://www.w3.org/2000/svg">
  112. <path
  113. d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-45.056L382.592 149.312a29.12 29.12 0 0 0-41.728 0z"
  114. fill="#CCCCCC"></path>
  115. </svg>
  116. </view>
  117. </view>
  118. </view>
  119. <!-- ... (Bottom content) ... -->
  120. <!-- 自定义日期选择器 (已存在) -->
  121. <!-- ... (Picker content) ... -->
  122. <!-- 自定义站点选择器 (级联版) -->
  123. <view class="picker-mask" :class="{ show: showStationPickerCascader }" @click="closeStationPickerCascader">
  124. <view class="picker-content" @click.stop>
  125. <view class="picker-header">
  126. <text class="picker-btn-cancel" @click="closeStationPickerCascader">取消</text>
  127. <text class="picker-title">请选择所属站点</text>
  128. <text class="picker-btn-confirm" @click="closeStationPickerCascader">关闭</text>
  129. </view>
  130. <view class="picker-body">
  131. <!-- 左侧:垂直路径 -->
  132. <view class="timeline-area">
  133. <!-- 已选节点 -->
  134. <view class="timeline-item" v-for="(item, index) in selectedPathway" :key="index"
  135. @click="jumpToStep(index)">
  136. <view class="timeline-dot"></view>
  137. <text>{{ item.name }}</text>
  138. </view>
  139. <!-- 当前正在选的提示 (如果还没选完) -->
  140. <view class="timeline-item active" v-if="selectStep === selectedPathway.length">
  141. <view class="timeline-dot"></view>
  142. <text>请选择</text>
  143. </view>
  144. </view>
  145. <!-- 右侧:待选项列表 -->
  146. <scroll-view scroll-y class="list-area">
  147. <view class="list-item" v-for="(item, index) in currentList" :key="item.id"
  148. @click="selectStationItem(item)">
  149. {{ item.name }}
  150. </view>
  151. <view v-if="currentList.length === 0" style="padding:20rpx;color:#999">
  152. 无数据
  153. </view>
  154. </scroll-view>
  155. </view>
  156. </view>
  157. </view>
  158. <!-- 底部协议与按钮 -->
  159. <view class="footer-actions">
  160. <view class="agreement-row">
  161. <view class="checkbox" :class="{ checked: isAgreed }" @click="isAgreed = !isAgreed">
  162. <text v-if="isAgreed" class="check-mark">✓</text>
  163. </view>
  164. <text class="agree-text">我已阅读并同意 <text style="color:#2979ff" @click.stop="openPrivacy">《宠宝履约者说明》</text></text>
  165. </view>
  166. <view class="footer-btn-area">
  167. <button class="submit-btn" @click="goToAuth">下一步,实名认证</button>
  168. </view>
  169. </view>
  170. <!-- 自定义日期选择器 -->
  171. <view class="picker-mask" :class="{ show: showPicker }" @click="closePicker">
  172. <view class="picker-content" @click.stop>
  173. <view class="picker-header">
  174. <text class="picker-btn-cancel" @click="closePicker">取消</text>
  175. <text class="picker-title">选择出生日期</text>
  176. <text class="picker-btn-confirm" @click="confirmPicker">确定</text>
  177. </view>
  178. <picker-view class="picker-view" indicator-style="height: 50px;" :value="pickerValue" @change="onPickerChange">
  179. <picker-view-column>
  180. <view class="picker-item" v-for="(item, index) in years" :key="index">{{ item }}年</view>
  181. </picker-view-column>
  182. <picker-view-column>
  183. <view class="picker-item" v-for="(item, index) in months" :key="index">{{ item }}月</view>
  184. </picker-view-column>
  185. <picker-view-column>
  186. <view class="picker-item" v-for="(item, index) in days" :key="index">{{ item }}日</view>
  187. </picker-view-column>
  188. </picker-view>
  189. </view>
  190. </view>
  191. <!-- 协议弹窗 公共组件 -->
  192. <agreement :visible="showPrivacy" :title="agreementTitle" :content="agreementContent" @close="showPrivacy = false">
  193. </agreement>
  194. </view>
  195. </template>
  196. <script>
  197. import { sendSmsCode } from '@/api/resource/sms'
  198. import { getAreaStationList } from '@/api/system/areaStation'
  199. import { listAllService } from '@/api/service/list'
  200. import { getAgreement } from '@/api/system/agreement'
  201. import Agreement from '@/components/agreement/index.vue'
  202. export default {
  203. components: { Agreement },
  204. data() {
  205. return {
  206. formData: {
  207. mobile: '', code: '', name: '', gender: 1, birthday: '',
  208. password: '', serviceType: [], station: '', stationId: null, areaPath: ''
  209. },
  210. showPwd: false, isAgreed: false, serviceTypes: [],
  211. countDown: 0, timer: null,
  212. showPicker: false, years: [], months: [], days: [],
  213. pickerValue: [0, 0, 0], tempYear: 0, tempMonth: 0, tempDay: 0,
  214. showStationPickerCascader: false, selectStep: 0,
  215. selectedPathway: [], currentList: [], fullStationData: [],
  216. selectedStationId: null,
  217. showPrivacy: false, agreementTitle: '', agreementContent: '', currentAgreementId: ''
  218. }
  219. },
  220. onLoad() {
  221. this.initDateData(); this.loadServiceTypes();
  222. this.loadAreaStationData(); this.restoreFormData();
  223. },
  224. beforeDestroy() { if (this.timer) clearInterval(this.timer); },
  225. methods: {
  226. async loadAreaStationData() {
  227. try { const res = await getAreaStationList(); this.fullStationData = res.data || []; }
  228. catch (err) { console.error('加载站点列表失败:', err); uni.showToast({ title: err.message || err.msg || '获取站点失败', icon: 'none' }); }
  229. },
  230. restoreFormData() {
  231. try {
  232. const saved = uni.getStorageSync('recruit_form_data');
  233. if (saved) {
  234. const d = JSON.parse(saved);
  235. Object.assign(this.formData, d);
  236. if (d._selectedPathway) { this.selectedPathway = d._selectedPathway; this.selectStep = this.selectedPathway.length; }
  237. if (this.selectedPathway.length > 0) {
  238. const last = this.selectedPathway[this.selectedPathway.length - 1];
  239. if (last) this.loadStations(last.id);
  240. }
  241. }
  242. } catch (e) { console.error('恢复表单数据失败', e); }
  243. },
  244. initDateData() {
  245. const currentYear = new Date().getFullYear();
  246. for (let i = 1980; i <= currentYear + 5; i++) this.years.push(i);
  247. for (let i = 1; i <= 12; i++) this.months.push(i);
  248. for (let i = 1; i <= 31; i++) this.days.push(i);
  249. },
  250. openPicker() {
  251. const dateStr = this.formData.birthday || '2000-01-01';
  252. const [y, m, d] = dateStr.split('-').map(Number);
  253. const yIndex = this.years.indexOf(y); const mIndex = this.months.indexOf(m); const dIndex = this.days.indexOf(d);
  254. this.pickerValue = [yIndex > -1 ? yIndex : 0, mIndex > -1 ? mIndex : 0, dIndex > -1 ? dIndex : 0];
  255. this.tempYear = this.years[this.pickerValue[0]]; this.tempMonth = this.months[this.pickerValue[1]]; this.tempDay = this.days[this.pickerValue[2]];
  256. this.showPicker = true;
  257. },
  258. closePicker() { this.showPicker = false; },
  259. onPickerChange(e) {
  260. const val = e.detail.value;
  261. this.tempYear = this.years[val[0]]; this.tempMonth = this.months[val[1]]; this.tempDay = this.days[val[2]];
  262. },
  263. confirmPicker() {
  264. const mStr = this.tempMonth < 10 ? '0' + this.tempMonth : this.tempMonth;
  265. const dStr = this.tempDay < 10 ? '0' + this.tempDay : this.tempDay;
  266. this.formData.birthday = `${this.tempYear}-${mStr}-${dStr}`; this.closePicker();
  267. },
  268. async loadServiceTypes() {
  269. try {
  270. const res = await listAllService();
  271. this.serviceTypes = (res.data || []).map(item => ({ id: item.id, name: item.name }));
  272. } catch (err) { console.error('加载服务类型失败:', err); this.serviceTypes = []; uni.showToast({ title: err.message || err.msg || '获取服务失败', icon: 'none' }); }
  273. },
  274. toggleService(item) {
  275. const idx = this.formData.serviceType.indexOf(item.id);
  276. if (idx > -1) this.formData.serviceType.splice(idx, 1);
  277. else this.formData.serviceType.push(item.id);
  278. },
  279. async openStationPickerCascader() {
  280. this.showStationPickerCascader = true;
  281. if (this.selectedPathway.length === 0) await this.resetStationPicker();
  282. },
  283. async resetStationPicker() { this.selectStep = 0; this.selectedPathway = []; this.filterLocalChildren(0); },
  284. closeStationPickerCascader() { this.showStationPickerCascader = false; },
  285. filterLocalChildren(parentId) { this.currentList = this.fullStationData.filter(item => item.parentId == parentId); },
  286. async selectStationItem(item) {
  287. this.selectedPathway[this.selectStep] = item;
  288. const sons = this.fullStationData.filter(i => i.parentId == item.id);
  289. if (sons.length > 0) {
  290. this.selectStep++; this.selectedPathway = this.selectedPathway.slice(0, this.selectStep); this.currentList = sons;
  291. } else { this.confirmStation(); }
  292. },
  293. async jumpToStep(step) {
  294. this.selectStep = step;
  295. if (step === 0) this.filterLocalChildren(0);
  296. else { const parent = this.selectedPathway[step - 1]; if (parent) this.filterLocalChildren(parent.id); }
  297. },
  298. confirmStation() {
  299. const path = this.selectedPathway.map(i => i.name);
  300. this.formData.station = path[path.length - 1];
  301. this.formData.stationId = this.selectedPathway[this.selectedPathway.length - 1].id;
  302. this.formData.areaPath = path.slice(0, -1).join(' ');
  303. this.closeStationPickerCascader();
  304. },
  305. async openPrivacy() {
  306. try {
  307. uni.showLoading({ title: '加载中...' });
  308. const res = await getAgreement(3);
  309. this.agreementTitle = res.data.title; this.agreementContent = res.data.content; this.showPrivacy = true;
  310. } catch (err) { console.error('获取协议详情失败:', err); uni.showToast({ title: err.message || err.msg || '获取协议失败', icon: 'none' }); }
  311. finally { uni.hideLoading(); }
  312. },
  313. goToAuth() {
  314. if (!this.isAgreed) { uni.showToast({ title: '请勾选协议', icon: 'none' }); return; }
  315. if (!this.formData.mobile || this.formData.mobile.length !== 11) { uni.showToast({ title: '请输入正确的手机号', icon: 'none' }); return; }
  316. if (!this.formData.name) { uni.showToast({ title: '请输入姓名', icon: 'none' }); return; }
  317. if (this.formData.serviceType.length === 0) { uni.showToast({ title: '请选择服务类型', icon: 'none' }); return; }
  318. if (!this.formData.stationId) { uni.showToast({ title: '请选择所属站点', icon: 'none' }); return; }
  319. uni.setStorageSync('recruit_form_data', JSON.stringify({ ...this.formData, _selectedPathway: this.selectedPathway }));
  320. const selectedServices = this.serviceTypes.filter(s => this.formData.serviceType.includes(s.id));
  321. uni.navigateTo({ url: `/pages/recruit/auth/index?services=${encodeURIComponent(JSON.stringify(selectedServices))}` });
  322. }
  323. }
  324. }
  325. </script>
  326. <style>
  327. page {
  328. background-color: #F5F6F8;
  329. }
  330. .container {
  331. padding: 20rpx;
  332. }
  333. .card {
  334. background-color: #fff;
  335. border-radius: 20rpx;
  336. padding: 30rpx;
  337. margin-bottom: 20rpx;
  338. }
  339. .form-item {
  340. display: flex;
  341. align-items: center;
  342. margin-bottom: 30rpx;
  343. }
  344. .form-item:last-child {
  345. margin-bottom: 0;
  346. }
  347. .label {
  348. width: 120rpx;
  349. font-size: 30rpx;
  350. font-weight: bold;
  351. color: #333;
  352. margin-right: 20rpx;
  353. flex-shrink: 0;
  354. }
  355. .input-box {
  356. flex: 1;
  357. height: 80rpx;
  358. background-color: #F8F8F8;
  359. border-radius: 8rpx;
  360. display: flex;
  361. align-items: center;
  362. padding: 0 20rpx;
  363. font-size: 28rpx;
  364. color: #333;
  365. }
  366. .input {
  367. flex: 1;
  368. height: 100%;
  369. font-size: 28rpx;
  370. }
  371. .station-display {
  372. display: flex;
  373. flex-direction: column;
  374. justify-content: center;
  375. line-height: 1.4;
  376. padding: 10rpx 0;
  377. }
  378. .area-tag {
  379. font-size: 22rpx;
  380. color: #999;
  381. margin-bottom: 4rpx;
  382. }
  383. .station-name {
  384. font-size: 28rpx;
  385. color: #333;
  386. font-weight: 500;
  387. }
  388. .prefix-area {
  389. display: flex;
  390. align-items: center;
  391. margin-right: 20rpx;
  392. height: 100%;
  393. }
  394. .prefix {
  395. color: #333;
  396. margin-right: 5rpx;
  397. }
  398. .arrow-down {
  399. font-size: 20rpx;
  400. color: #999;
  401. line-height: 1;
  402. }
  403. .get-code-text {
  404. color: #FF5722;
  405. font-size: 28rpx;
  406. font-weight: bold;
  407. margin-left: 20rpx;
  408. }
  409. .gender-group {
  410. display: flex;
  411. align-items: center;
  412. }
  413. .radio-item {
  414. display: flex;
  415. align-items: center;
  416. margin-right: 40rpx;
  417. font-size: 30rpx;
  418. color: #333;
  419. }
  420. .radio-icon {
  421. margin-right: 10rpx;
  422. font-size: 32rpx;
  423. color: #ccc;
  424. }
  425. .radio-icon.active {
  426. color: #FF5722;
  427. }
  428. .radio-label.active {
  429. color: #FF5722;
  430. font-weight: bold;
  431. }
  432. .arrow-right {
  433. color: #ccc;
  434. font-size: 24rpx;
  435. margin-left: auto;
  436. }
  437. .eye-icon {
  438. padding: 10rpx;
  439. color: #ccc;
  440. }
  441. .section-title {
  442. font-size: 30rpx;
  443. font-weight: bold;
  444. color: #333;
  445. margin-bottom: 20rpx;
  446. }
  447. .service-types {
  448. display: flex;
  449. flex-wrap: wrap;
  450. gap: 20rpx;
  451. margin-bottom: 30rpx;
  452. }
  453. .type-btn {
  454. width: calc((100% - 40rpx) / 3);
  455. height: 80rpx;
  456. background-color: #F8F8F8;
  457. border-radius: 8rpx;
  458. display: flex;
  459. align-items: center;
  460. justify-content: center;
  461. font-size: 28rpx;
  462. color: #333;
  463. font-weight: 500;
  464. }
  465. .type-btn.selected {
  466. background-color: #FFF3E0;
  467. color: #FF5722;
  468. font-weight: bold;
  469. }
  470. .footer-actions {
  471. margin-top: 40rpx;
  472. padding: 0 10rpx;
  473. padding-bottom: 60rpx;
  474. }
  475. .agreement-row {
  476. display: flex;
  477. align-items: center;
  478. margin-bottom: 30rpx;
  479. }
  480. .checkbox {
  481. width: 32rpx;
  482. height: 32rpx;
  483. border: 2rpx solid #ccc;
  484. border-radius: 4rpx;
  485. margin-right: 15rpx;
  486. display: flex;
  487. align-items: center;
  488. justify-content: center;
  489. }
  490. .checkbox.checked {
  491. border-color: #FF5722;
  492. background-color: #FF5722;
  493. }
  494. .check-mark {
  495. color: #fff;
  496. font-size: 22rpx;
  497. }
  498. .agree-text {
  499. font-size: 26rpx;
  500. color: #999;
  501. }
  502. .footer-btn-area {
  503. width: 100%;
  504. margin-top: 40rpx;
  505. }
  506. .submit-btn {
  507. background: linear-gradient(90deg, #FF6F00 0%, #FF5722 100%);
  508. color: #fff;
  509. font-size: 28rpx;
  510. font-weight: bold;
  511. height: 90rpx;
  512. line-height: 90rpx;
  513. border-radius: 45rpx;
  514. box-shadow: 0 10rpx 20rpx rgba(255, 87, 34, 0.2);
  515. }
  516. .submit-btn::after {
  517. border: none;
  518. }
  519. .picker-mask {
  520. position: fixed;
  521. top: 0;
  522. left: 0;
  523. right: 0;
  524. bottom: 0;
  525. background-color: rgba(0, 0, 0, 0.5);
  526. z-index: 999;
  527. visibility: hidden;
  528. opacity: 0;
  529. transition: all 0.3s;
  530. }
  531. .picker-mask.show {
  532. visibility: visible;
  533. opacity: 1;
  534. }
  535. .picker-content {
  536. position: absolute;
  537. bottom: 0;
  538. left: 0;
  539. width: 100%;
  540. background-color: #fff;
  541. border-radius: 20rpx 20rpx 0 0;
  542. transform: translateY(100%);
  543. transition: all 0.3s;
  544. height: 800rpx;
  545. display: flex;
  546. flex-direction: column;
  547. }
  548. .picker-mask.show .picker-content {
  549. transform: translateY(0);
  550. }
  551. .picker-header {
  552. display: flex;
  553. justify-content: space-between;
  554. align-items: center;
  555. padding: 30rpx;
  556. border-bottom: 1px solid #eee;
  557. font-size: 32rpx;
  558. }
  559. .picker-btn-cancel {
  560. color: #999;
  561. }
  562. .picker-title {
  563. font-weight: bold;
  564. color: #333;
  565. }
  566. .picker-btn-confirm {
  567. color: #FF5722;
  568. font-weight: bold;
  569. }
  570. .picker-view {
  571. width: 100%;
  572. height: 400rpx;
  573. }
  574. .picker-item {
  575. line-height: 50px;
  576. text-align: center;
  577. font-size: 32rpx;
  578. color: #333;
  579. }
  580. .monkey-icon {
  581. padding: 10rpx;
  582. display: flex;
  583. align-items: center;
  584. justify-content: center;
  585. }
  586. .svg-icon {
  587. width: 40rpx;
  588. height: 40rpx;
  589. }
  590. .picker-body {
  591. flex: 1;
  592. display: flex;
  593. overflow: hidden;
  594. padding: 0 30rpx 30rpx;
  595. }
  596. .timeline-area {
  597. width: 200rpx;
  598. border-right: 1px solid #f5f5f5;
  599. padding-right: 20rpx;
  600. display: flex;
  601. flex-direction: column;
  602. }
  603. .timeline-item {
  604. position: relative;
  605. padding-bottom: 40rpx;
  606. padding-left: 30rpx;
  607. color: #333;
  608. font-size: 28rpx;
  609. }
  610. .timeline-item:last-child {
  611. padding-bottom: 0;
  612. }
  613. .timeline-dot {
  614. position: absolute;
  615. left: 0;
  616. top: 12rpx;
  617. width: 14rpx;
  618. height: 14rpx;
  619. border-radius: 50%;
  620. background-color: #eee;
  621. }
  622. .timeline-item.active .timeline-dot {
  623. background-color: #FF5722;
  624. box-shadow: 0 0 0 4rpx rgba(255, 87, 34, 0.2);
  625. }
  626. .timeline-item.active {
  627. font-weight: bold;
  628. color: #FF5722;
  629. }
  630. .timeline-item:not(:last-child)::after {
  631. content: '';
  632. position: absolute;
  633. left: 6rpx;
  634. top: 30rpx;
  635. bottom: -10rpx;
  636. width: 2rpx;
  637. background-color: #f0f0f0;
  638. }
  639. .list-area {
  640. flex: 1;
  641. padding-left: 30rpx;
  642. overflow-y: auto;
  643. }
  644. .list-item {
  645. padding: 24rpx 0;
  646. border-bottom: 1px solid #f9f9f9;
  647. font-size: 28rpx;
  648. color: #666;
  649. }
  650. .list-item:active {
  651. background-color: #f5f5f5;
  652. }
  653. .picker-list {
  654. max-height: 600rpx;
  655. overflow-y: auto;
  656. padding: 20rpx 0;
  657. }
  658. .city-item {
  659. display: flex;
  660. align-items: center;
  661. padding: 24rpx 40rpx;
  662. font-size: 30rpx;
  663. color: #333;
  664. }
  665. .city-item.active {
  666. color: #FF5722;
  667. font-weight: bold;
  668. }
  669. .dot-radio {
  670. width: 16rpx;
  671. height: 16rpx;
  672. border-radius: 50%;
  673. background-color: #eee;
  674. margin-right: 20rpx;
  675. position: relative;
  676. }
  677. .city-item.active .dot-radio {
  678. background-color: #FF5722;
  679. box-shadow: 0 0 0 6rpx rgba(255, 87, 34, 0.2);
  680. }
  681. .station-item {
  682. padding: 30rpx 40rpx;
  683. font-size: 30rpx;
  684. color: #333;
  685. border-bottom: 1px solid #f9f9f9;
  686. }
  687. .station-item:last-child {
  688. border-bottom: none;
  689. }
  690. </style>