index.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  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); }
  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 = []; }
  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. if (res.code === 200 && res.data) {
  310. this.agreementTitle = res.data.title; this.agreementContent = res.data.content; this.showPrivacy = true;
  311. } else { uni.showToast({ title: res.msg || '获取协议失败', icon: 'none' }); }
  312. } catch (err) { console.error('获取协议详情失败:', err); }
  313. finally { uni.hideLoading(); }
  314. },
  315. goToAuth() {
  316. if (!this.isAgreed) { uni.showToast({ title: '请勾选协议', icon: 'none' }); return; }
  317. if (!this.formData.mobile || this.formData.mobile.length !== 11) { uni.showToast({ title: '请输入正确的手机号', icon: 'none' }); return; }
  318. if (!this.formData.name) { uni.showToast({ title: '请输入姓名', icon: 'none' }); return; }
  319. if (this.formData.serviceType.length === 0) { uni.showToast({ title: '请选择服务类型', icon: 'none' }); return; }
  320. if (!this.formData.stationId) { uni.showToast({ title: '请选择所属站点', icon: 'none' }); return; }
  321. uni.setStorageSync('recruit_form_data', JSON.stringify({ ...this.formData, _selectedPathway: this.selectedPathway }));
  322. const selectedServices = this.serviceTypes.filter(s => this.formData.serviceType.includes(s.id));
  323. uni.navigateTo({ url: `/pages/recruit/auth/index?services=${encodeURIComponent(JSON.stringify(selectedServices))}` });
  324. }
  325. }
  326. }
  327. </script>
  328. <style>
  329. page {
  330. background-color: #F5F6F8;
  331. }
  332. .container {
  333. padding: 20rpx;
  334. }
  335. .card {
  336. background-color: #fff;
  337. border-radius: 20rpx;
  338. padding: 30rpx;
  339. margin-bottom: 20rpx;
  340. }
  341. .form-item {
  342. display: flex;
  343. align-items: center;
  344. margin-bottom: 30rpx;
  345. }
  346. .form-item:last-child {
  347. margin-bottom: 0;
  348. }
  349. .label {
  350. width: 120rpx;
  351. font-size: 30rpx;
  352. font-weight: bold;
  353. color: #333;
  354. margin-right: 20rpx;
  355. flex-shrink: 0;
  356. }
  357. .input-box {
  358. flex: 1;
  359. height: 80rpx;
  360. background-color: #F8F8F8;
  361. border-radius: 8rpx;
  362. display: flex;
  363. align-items: center;
  364. padding: 0 20rpx;
  365. font-size: 28rpx;
  366. color: #333;
  367. }
  368. .input {
  369. flex: 1;
  370. height: 100%;
  371. font-size: 28rpx;
  372. }
  373. .station-display {
  374. display: flex;
  375. flex-direction: column;
  376. justify-content: center;
  377. line-height: 1.4;
  378. padding: 10rpx 0;
  379. }
  380. .area-tag {
  381. font-size: 22rpx;
  382. color: #999;
  383. margin-bottom: 4rpx;
  384. }
  385. .station-name {
  386. font-size: 28rpx;
  387. color: #333;
  388. font-weight: 500;
  389. }
  390. .prefix-area {
  391. display: flex;
  392. align-items: center;
  393. margin-right: 20rpx;
  394. height: 100%;
  395. }
  396. .prefix {
  397. color: #333;
  398. margin-right: 5rpx;
  399. }
  400. .arrow-down {
  401. font-size: 20rpx;
  402. color: #999;
  403. line-height: 1;
  404. }
  405. .get-code-text {
  406. color: #FF5722;
  407. font-size: 28rpx;
  408. font-weight: bold;
  409. margin-left: 20rpx;
  410. }
  411. .gender-group {
  412. display: flex;
  413. align-items: center;
  414. }
  415. .radio-item {
  416. display: flex;
  417. align-items: center;
  418. margin-right: 40rpx;
  419. font-size: 30rpx;
  420. color: #333;
  421. }
  422. .radio-icon {
  423. margin-right: 10rpx;
  424. font-size: 32rpx;
  425. color: #ccc;
  426. }
  427. .radio-icon.active {
  428. color: #FF5722;
  429. }
  430. .radio-label.active {
  431. color: #FF5722;
  432. font-weight: bold;
  433. }
  434. .arrow-right {
  435. color: #ccc;
  436. font-size: 24rpx;
  437. margin-left: auto;
  438. }
  439. .eye-icon {
  440. padding: 10rpx;
  441. color: #ccc;
  442. }
  443. .section-title {
  444. font-size: 30rpx;
  445. font-weight: bold;
  446. color: #333;
  447. margin-bottom: 20rpx;
  448. }
  449. .service-types {
  450. display: flex;
  451. flex-wrap: wrap;
  452. gap: 20rpx;
  453. margin-bottom: 30rpx;
  454. }
  455. .type-btn {
  456. width: calc((100% - 40rpx) / 3);
  457. height: 80rpx;
  458. background-color: #F8F8F8;
  459. border-radius: 8rpx;
  460. display: flex;
  461. align-items: center;
  462. justify-content: center;
  463. font-size: 28rpx;
  464. color: #333;
  465. font-weight: 500;
  466. }
  467. .type-btn.selected {
  468. background-color: #FFF3E0;
  469. color: #FF5722;
  470. font-weight: bold;
  471. }
  472. .footer-actions {
  473. margin-top: 40rpx;
  474. padding: 0 10rpx;
  475. padding-bottom: 60rpx;
  476. }
  477. .agreement-row {
  478. display: flex;
  479. align-items: center;
  480. margin-bottom: 30rpx;
  481. }
  482. .checkbox {
  483. width: 32rpx;
  484. height: 32rpx;
  485. border: 2rpx solid #ccc;
  486. border-radius: 4rpx;
  487. margin-right: 15rpx;
  488. display: flex;
  489. align-items: center;
  490. justify-content: center;
  491. }
  492. .checkbox.checked {
  493. border-color: #FF5722;
  494. background-color: #FF5722;
  495. }
  496. .check-mark {
  497. color: #fff;
  498. font-size: 22rpx;
  499. }
  500. .agree-text {
  501. font-size: 26rpx;
  502. color: #999;
  503. }
  504. .footer-btn-area {
  505. width: 100%;
  506. margin-top: 40rpx;
  507. }
  508. .submit-btn {
  509. background: linear-gradient(90deg, #FF6F00 0%, #FF5722 100%);
  510. color: #fff;
  511. font-size: 28rpx;
  512. font-weight: bold;
  513. height: 90rpx;
  514. line-height: 90rpx;
  515. border-radius: 45rpx;
  516. box-shadow: 0 10rpx 20rpx rgba(255, 87, 34, 0.2);
  517. }
  518. .submit-btn::after {
  519. border: none;
  520. }
  521. .picker-mask {
  522. position: fixed;
  523. top: 0;
  524. left: 0;
  525. right: 0;
  526. bottom: 0;
  527. background-color: rgba(0, 0, 0, 0.5);
  528. z-index: 999;
  529. visibility: hidden;
  530. opacity: 0;
  531. transition: all 0.3s;
  532. }
  533. .picker-mask.show {
  534. visibility: visible;
  535. opacity: 1;
  536. }
  537. .picker-content {
  538. position: absolute;
  539. bottom: 0;
  540. left: 0;
  541. width: 100%;
  542. background-color: #fff;
  543. border-radius: 20rpx 20rpx 0 0;
  544. transform: translateY(100%);
  545. transition: all 0.3s;
  546. height: 800rpx;
  547. display: flex;
  548. flex-direction: column;
  549. }
  550. .picker-mask.show .picker-content {
  551. transform: translateY(0);
  552. }
  553. .picker-header {
  554. display: flex;
  555. justify-content: space-between;
  556. align-items: center;
  557. padding: 30rpx;
  558. border-bottom: 1px solid #eee;
  559. font-size: 32rpx;
  560. }
  561. .picker-btn-cancel {
  562. color: #999;
  563. }
  564. .picker-title {
  565. font-weight: bold;
  566. color: #333;
  567. }
  568. .picker-btn-confirm {
  569. color: #FF5722;
  570. font-weight: bold;
  571. }
  572. .picker-view {
  573. width: 100%;
  574. height: 400rpx;
  575. }
  576. .picker-item {
  577. line-height: 50px;
  578. text-align: center;
  579. font-size: 32rpx;
  580. color: #333;
  581. }
  582. .monkey-icon {
  583. padding: 10rpx;
  584. display: flex;
  585. align-items: center;
  586. justify-content: center;
  587. }
  588. .svg-icon {
  589. width: 40rpx;
  590. height: 40rpx;
  591. }
  592. .picker-body {
  593. flex: 1;
  594. display: flex;
  595. overflow: hidden;
  596. padding: 0 30rpx 30rpx;
  597. }
  598. .timeline-area {
  599. width: 200rpx;
  600. border-right: 1px solid #f5f5f5;
  601. padding-right: 20rpx;
  602. display: flex;
  603. flex-direction: column;
  604. }
  605. .timeline-item {
  606. position: relative;
  607. padding-bottom: 40rpx;
  608. padding-left: 30rpx;
  609. color: #333;
  610. font-size: 28rpx;
  611. }
  612. .timeline-item:last-child {
  613. padding-bottom: 0;
  614. }
  615. .timeline-dot {
  616. position: absolute;
  617. left: 0;
  618. top: 12rpx;
  619. width: 14rpx;
  620. height: 14rpx;
  621. border-radius: 50%;
  622. background-color: #eee;
  623. }
  624. .timeline-item.active .timeline-dot {
  625. background-color: #FF5722;
  626. box-shadow: 0 0 0 4rpx rgba(255, 87, 34, 0.2);
  627. }
  628. .timeline-item.active {
  629. font-weight: bold;
  630. color: #FF5722;
  631. }
  632. .timeline-item:not(:last-child)::after {
  633. content: '';
  634. position: absolute;
  635. left: 6rpx;
  636. top: 30rpx;
  637. bottom: -10rpx;
  638. width: 2rpx;
  639. background-color: #f0f0f0;
  640. }
  641. .list-area {
  642. flex: 1;
  643. padding-left: 30rpx;
  644. overflow-y: auto;
  645. }
  646. .list-item {
  647. padding: 24rpx 0;
  648. border-bottom: 1px solid #f9f9f9;
  649. font-size: 28rpx;
  650. color: #666;
  651. }
  652. .list-item:active {
  653. background-color: #f5f5f5;
  654. }
  655. .picker-list {
  656. max-height: 600rpx;
  657. overflow-y: auto;
  658. padding: 20rpx 0;
  659. }
  660. .city-item {
  661. display: flex;
  662. align-items: center;
  663. padding: 24rpx 40rpx;
  664. font-size: 30rpx;
  665. color: #333;
  666. }
  667. .city-item.active {
  668. color: #FF5722;
  669. font-weight: bold;
  670. }
  671. .dot-radio {
  672. width: 16rpx;
  673. height: 16rpx;
  674. border-radius: 50%;
  675. background-color: #eee;
  676. margin-right: 20rpx;
  677. position: relative;
  678. }
  679. .city-item.active .dot-radio {
  680. background-color: #FF5722;
  681. box-shadow: 0 0 0 6rpx rgba(255, 87, 34, 0.2);
  682. }
  683. .station-item {
  684. padding: 30rpx 40rpx;
  685. font-size: 30rpx;
  686. color: #333;
  687. border-bottom: 1px solid #f9f9f9;
  688. }
  689. .station-item:last-child {
  690. border-bottom: none;
  691. }
  692. </style>