爱简收旧衣按件回收前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

785 lines
24 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. <template>
  2. <view class="container" :style="{paddingTop: (statusBarHeight + 88) + 'rpx'}">
  3. <!-- 顶部导航 -->
  4. <view class="nav-bar" :style="{height: (statusBarHeight + 88) + 'rpx', paddingTop: statusBarHeight + 'px'}">
  5. <view class="back" @tap="goBack">
  6. <uni-icons type="left" size="20"></uni-icons>
  7. </view>
  8. <text class="title">选择寄件地址</text>
  9. </view>
  10. <!-- 地址列表 -->
  11. <view class="address-list">
  12. <view
  13. class="address-item"
  14. @click="selectAddress(item)"
  15. v-for="(item, index) in addressList"
  16. :key="index"
  17. >
  18. <view class="address-header">
  19. <text class="name">{{item.name}}</text>
  20. <text class="phone">{{item.phone}}</text>
  21. <text v-if="item.defaultFlag === 'Y'" class="default-tag">默认</text>
  22. </view>
  23. <view class="address-text">{{item.address}} {{item.addressDetails}}</view>
  24. <view class="dashed-line"></view>
  25. <view class="address-actions">
  26. <view class="action" @tap="setDefault(item.id)">
  27. <view class="circle" :class="{ active: item.defaultFlag === 'Y' }">
  28. <text v-if="item.defaultFlag === 'Y'" class="check"></text>
  29. </view>
  30. <text :class="{ 'active-text': item.defaultFlag === 'Y' }">默认地址</text>
  31. </view>
  32. <view class="action" @tap="editAddress(item)">
  33. <uni-icons type="compose" size="22" color="#bbb" />
  34. <text>编辑</text>
  35. </view>
  36. <view class="action" @tap="deleteAddress(item.id)">
  37. <uni-icons type="trash" size="22" color="#bbb" />
  38. <text>删除</text>
  39. </view>
  40. </view>
  41. </view>
  42. </view>
  43. <!-- 新建地址按钮 -->
  44. <view class="bottom-bar">
  45. <button class="main-btn" @tap="goToAddAddress">新建地址</button>
  46. </view>
  47. <!-- 遮罩 -->
  48. <view v-if="showAddressModal" class="modal-mask" @tap="closeAddressModal"></view>
  49. <!-- 新建地址弹窗 -->
  50. <view v-if="showAddressModal" class="address-modal">
  51. <view class="modal-header">
  52. <text class="close-btn" @tap="closeAddressModal">关闭</text>
  53. <text class="modal-title">{{form.id ? '编辑地址' : '新建地址'}}</text>
  54. </view>
  55. <view class="modal-form">
  56. <view class="form-item">
  57. <text class="label">联系人</text>
  58. <input class="input" v-model="form.name" placeholder="请输入" />
  59. </view>
  60. <view class="form-item">
  61. <text class="label">手机号</text>
  62. <input class="input" v-model="form.phone" placeholder="请输入" />
  63. </view>
  64. <view class="form-item" @tap="selectRegion">
  65. <text class="label">所在地区</text>
  66. <view class="input region-input">
  67. <text :class="{placeholder: !form.address}">
  68. {{ form.address || '选择省市区街道' }}
  69. </text>
  70. <text class="arrow">></text>
  71. </view>
  72. </view>
  73. <view class="form-item">
  74. <text class="label">详细地址</text>
  75. <input class="input" v-model="form.addressDetails" placeholder="小区楼栋、门牌号、村等" />
  76. </view>
  77. </view>
  78. <button class="main-btn" @tap="saveAddress">保存</button>
  79. </view>
  80. <!-- 地区选择器弹窗 -->
  81. <view v-if="showRegionPicker" class="region-modal">
  82. <view class="modal-header">
  83. <text class="close-btn" @tap="showRegionPicker = false">关闭</text>
  84. <text class="modal-title">手动选择地区</text>
  85. </view>
  86. <view class="region-tip">
  87. <text>请手动选择准确的省市区信息</text>
  88. </view>
  89. <view class="region-picker">
  90. <picker-view style="height:300px;" :value="regionIndex" @change="onRegionChange">
  91. <picker-view-column >
  92. <view v-for="(item,index) in provinces" :key="index" class="item">{{item.name}}</view>
  93. </picker-view-column>
  94. <picker-view-column>
  95. <view v-for="(item,index) in cities" :key="index" class="item">{{item.name}}</view>
  96. </picker-view-column>
  97. <picker-view-column>
  98. <view v-for="(item,index) in districts" :key="index" class="item">{{item.name}}</view>
  99. </picker-view-column>
  100. </picker-view>
  101. </view>
  102. <button class="main-btn" @tap="confirmRegion">确认</button>
  103. </view>
  104. </view>
  105. </template>
  106. <script>
  107. import regionData from '@/pages/subcomponent/region-data.js'
  108. import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
  109. export default {
  110. mixins: [pullRefreshMixin],
  111. data() {
  112. return {
  113. statusBarHeight: 0,
  114. addressList: [
  115. ],
  116. batchMode: false,
  117. selectedIndexes: [],
  118. showAddressModal: false,
  119. showRegionPicker: false,
  120. form: {
  121. name: '',
  122. phone: '',
  123. region: [],
  124. address: '',
  125. addressDetails: '',
  126. latitude: '',
  127. longitude: ''
  128. },
  129. provinces: regionData,
  130. cities: [],
  131. districts: [],
  132. regionIndex: [0, 0, 0],
  133. mode: ''
  134. }
  135. },
  136. watch: {
  137. regionIndex: {
  138. handler(val) {
  139. let pIdx = val[0] < this.provinces.length ? val[0] : 0
  140. let cIdx = val[1] < (this.provinces[pIdx]?.children?.length || 0) ? val[1] : 0
  141. this.cities = this.provinces[pIdx]?.children || []
  142. this.districts = this.cities[cIdx]?.children || []
  143. },
  144. immediate: true
  145. }
  146. },
  147. onPullDownRefresh() {
  148. this.getAddressList(() => {
  149. uni.stopPullDownRefresh();
  150. });
  151. },
  152. onLoad(options) {
  153. this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
  154. this.cities = this.provinces[0]?.children || []
  155. this.districts = this.cities[0]?.children || []
  156. this.regionIndex = [0, 0, 0]
  157. this.getAddressList();
  158. this.mode = options.mode || ''
  159. },
  160. methods: {
  161. async onRefresh() {
  162. // 模拟刷新数据
  163. await new Promise(resolve => setTimeout(resolve, 1000))
  164. uni.stopPullRefresh()
  165. },
  166. getAddressList(callback) {
  167. this.$api('getAddressList', {}, res => {
  168. if (res && res.result && res.result.records) {
  169. this.addressList = res.result.records;
  170. }
  171. if (typeof callback === 'function') callback();
  172. });
  173. },
  174. goBack() {
  175. uni.navigateBack()
  176. },
  177. startBatchDelete() {
  178. this.batchMode = true
  179. this.selectedIndexes = []
  180. },
  181. cancelBatchDelete() {
  182. this.batchMode = false
  183. this.selectedIndexes = []
  184. },
  185. selectItem(index) {
  186. if (!this.batchMode) return
  187. const idx = this.selectedIndexes.indexOf(index)
  188. if (idx > -1) {
  189. this.selectedIndexes.splice(idx, 1)
  190. } else {
  191. this.selectedIndexes.push(index)
  192. }
  193. },
  194. confirmDelete() {
  195. if (this.selectedIndexes.length === 0) return
  196. uni.showModal({
  197. title: '提示',
  198. content: '确定要删除选中的地址吗?',
  199. success: (res) => {
  200. if (res.confirm) {
  201. // 从后往前删除,避免索引变化的问题
  202. this.selectedIndexes.sort((a, b) => b - a).forEach(index => {
  203. this.addressList.splice(index, 1)
  204. })
  205. this.batchMode = false
  206. this.selectedIndexes = []
  207. }
  208. }
  209. })
  210. },
  211. editAddress(item) {
  212. this.showAddressModal = true
  213. this.form = {
  214. id: item.id,
  215. name: item.name,
  216. phone: item.phone,
  217. region: [],
  218. address: item.address,
  219. addressDetails: item.addressDetails,
  220. defaultFlag: item.defaultFlag,
  221. latitude: item.latitude || '',
  222. longitude: item.longitude || ''
  223. }
  224. this.regionIndex = [0, 0, 0]
  225. this.cities = this.provinces[0]?.children || []
  226. this.districts = this.cities[0]?.children || []
  227. },
  228. goToAddAddress() {
  229. this.showAddressModal = true
  230. this.form = {
  231. id: undefined,
  232. name: '',
  233. phone: '',
  234. region: [],
  235. address: '',
  236. addressDetails: '',
  237. latitude: '',
  238. longitude: ''
  239. }
  240. this.regionIndex = [0, 0, 0]
  241. this.cities = this.provinces[0]?.children || []
  242. this.districts = this.cities[0]?.children || []
  243. },
  244. selectAddress(address) {
  245. if (this.mode === 'select') {
  246. uni.$emit('addressSelected', {
  247. id: address.id,
  248. name: address.name,
  249. phone: address.phone,
  250. address: address.address,
  251. addressDetails: address.addressDetails
  252. })
  253. uni.navigateBack()
  254. }
  255. },
  256. closeAddressModal() {
  257. this.showAddressModal = false
  258. },
  259. saveAddress() {
  260. if (!this.form.name) return uni.showToast({ title: '请输入联系人', icon: 'none' })
  261. if (!this.form.phone) return uni.showToast({ title: '请输入手机号', icon: 'none' })
  262. if (!this.form.address) return uni.showToast({ title: '请选择地区', icon: 'none' })
  263. if (!this.form.addressDetails) return uni.showToast({ title: '请输入详细地址', icon: 'none' })
  264. // 判断手机号是否合法
  265. if (!/^1[3-9]\d{9}$/.test(this.form.phone)) return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
  266. const params = {
  267. name: this.form.name,
  268. phone: this.form.phone,
  269. address: this.form.address,
  270. addressDetails: this.form.addressDetails,
  271. defaultFlag: this.form.defaultFlag,
  272. latitude: this.form.latitude,
  273. longitude: this.form.longitude
  274. }
  275. if (this.form.id) params.id = this.form.id
  276. this.$api('saveOrUpdateAddress', params, (res) => {
  277. if(res.code == 200){
  278. uni.showToast({ title: '保存成功', icon: 'success' })
  279. this.closeAddressModal()
  280. // 保存成功后刷新地址列表
  281. this.getAddressList()
  282. }
  283. })
  284. },
  285. onRegionChange(e) {
  286. let [pIdx, cIdx, dIdx] = e.detail.value
  287. if (pIdx !== this.regionIndex[0]) {
  288. cIdx = 0
  289. dIdx = 0
  290. } else if (cIdx !== this.regionIndex[1]) {
  291. dIdx = 0
  292. }
  293. this.regionIndex = [pIdx, cIdx, dIdx]
  294. },
  295. // 选择地区
  296. selectRegion() {
  297. // 首先检查位置权限
  298. uni.getSetting({
  299. success: (res) => {
  300. if (res.authSetting['scope.userLocation'] === false) {
  301. // 用户之前拒绝了位置权限,引导用户去设置
  302. uni.showModal({
  303. title: '位置权限',
  304. content: '需要获取您的位置信息来选择地址,请在设置中开启位置权限',
  305. confirmText: '去设置',
  306. success: (modalRes) => {
  307. if (modalRes.confirm) {
  308. uni.openSetting()
  309. } else {
  310. // 用户拒绝去设置,直接打开区域选择器
  311. this.showRegionPicker = true
  312. }
  313. },
  314. fail: () => {
  315. this.showRegionPicker = true
  316. }
  317. })
  318. return
  319. }
  320. // 调用位置选择
  321. uni.chooseLocation({
  322. success: res => {
  323. console.log(res);
  324. this.form.latitude = res.latitude
  325. this.form.longitude = res.longitude
  326. // 改进的正则表达式,更精确地匹配省市区结构
  327. var provinceReg = /(.+?(省|自治区|特别行政区))/;
  328. var cityReg = /(.+?(市|自治州|地区|盟))/;
  329. var districtReg = /(.+?(区|县|市|旗))/;
  330. let address = ''
  331. if (!res.address && res.name) { //用户直接选择城市的逻辑
  332. address = res.name
  333. }
  334. if (res.address || res.name) {
  335. address = res.address + res.name
  336. }
  337. if(!address){
  338. this.showRegionPicker = true
  339. return
  340. }
  341. // 使用更严格的省市区结构验证
  342. let province = ''
  343. let city = ''
  344. let district = ''
  345. // 提取省份
  346. let provinceMatch = address.match(provinceReg)
  347. if (provinceMatch) {
  348. province = provinceMatch[1]
  349. address = address.replace(province, '')
  350. }
  351. // 提取城市
  352. let cityMatch = address.match(cityReg)
  353. if (cityMatch) {
  354. city = cityMatch[1]
  355. address = address.replace(city, '')
  356. }
  357. // 提取区县
  358. let districtMatch = address.match(districtReg)
  359. if (districtMatch) {
  360. district = districtMatch[1]
  361. address = address.replace(district, '')
  362. }
  363. // 验证是否符合省市区/县的基本结构
  364. // 至少需要有省份和市/区县其中之一
  365. if (!province || (!city && !district)) {
  366. // 地址结构不完整,直接打开区域选择器
  367. uni.showToast({
  368. title: '地址信息不完整,请手动选择',
  369. icon: 'none'
  370. })
  371. this.showRegionPicker = true
  372. return
  373. }
  374. // 如果只有省市没有区县,也提示手动选择
  375. if (province && city && !district) {
  376. uni.showToast({
  377. title: '请选择具体的区县',
  378. icon: 'none'
  379. })
  380. this.showRegionPicker = true
  381. return
  382. }
  383. // 设置详细地址(剩余部分)
  384. this.form.addressDetails = address.trim()
  385. this.form.address = `${province}${city}${district}`
  386. // 尝试匹配到region数据中对应的省市区
  387. this.matchRegionData(province, city, district)
  388. },
  389. fail(e) {
  390. console.log("获取位置信息失败!", e)
  391. // 根据错误类型给出不同提示
  392. if (e.errMsg && e.errMsg.includes('auth deny')) {
  393. uni.showModal({
  394. title: '位置权限',
  395. content: '需要获取您的位置信息来选择地址,请允许位置权限',
  396. confirmText: '重新授权',
  397. success: (modalRes) => {
  398. if (modalRes.confirm) {
  399. uni.openSetting()
  400. } else {
  401. this.showRegionPicker = true
  402. }
  403. }
  404. })
  405. } else if (e.errMsg && e.errMsg.includes('cancel')) {
  406. // 用户取消选择位置,直接打开区域选择器
  407. this.showRegionPicker = true
  408. } else {
  409. // 其他错误,提示并打开区域选择器
  410. uni.showToast({
  411. title: '获取位置失败,请手动选择',
  412. icon: 'none'
  413. })
  414. this.showRegionPicker = true
  415. }
  416. }
  417. })
  418. },
  419. fail: () => {
  420. // 获取设置失败,直接打开区域选择器
  421. this.showRegionPicker = true
  422. }
  423. })
  424. },
  425. // 匹配region数据
  426. matchRegionData(provinceName, cityName, districtName) {
  427. // 查找省份
  428. const provinceIndex = this.provinces.findIndex(p =>
  429. provinceName.includes(p.name) || p.name.includes(provinceName.replace(/省|市|自治区|自治州/g, ''))
  430. )
  431. if (provinceIndex === -1) {
  432. this.regionIndex = [0, 0, 0]
  433. return
  434. }
  435. this.cities = this.provinces[provinceIndex]?.children || []
  436. // 查找城市
  437. const cityIndex = this.cities.findIndex(c =>
  438. cityName.includes(c.name) || c.name.includes(cityName.replace(/市|县|区/g, ''))
  439. )
  440. if (cityIndex === -1) {
  441. this.regionIndex = [provinceIndex, 0, 0]
  442. this.districts = this.cities[0]?.children || []
  443. return
  444. }
  445. this.districts = this.cities[cityIndex]?.children || []
  446. // 查找区县
  447. const districtIndex = this.districts.findIndex(d =>
  448. districtName.includes(d.name) || d.name.includes(districtName.replace(/县|区/g, ''))
  449. )
  450. this.regionIndex = [
  451. provinceIndex,
  452. cityIndex,
  453. districtIndex > -1 ? districtIndex : 0
  454. ]
  455. // 更新form.region
  456. this.form.region = [
  457. this.provinces[provinceIndex]?.code || '',
  458. this.cities[cityIndex]?.code || '',
  459. this.districts[districtIndex > -1 ? districtIndex : 0]?.code || ''
  460. ]
  461. },
  462. confirmRegion() {
  463. const province = this.provinces[this.regionIndex[0]]
  464. const city = this.cities[this.regionIndex[1]]
  465. const district = this.districts[this.regionIndex[2]]
  466. this.form.region = [
  467. province?.code || '',
  468. city?.code || '',
  469. district?.code || ''
  470. ]
  471. this.form.address = [
  472. province?.name || '',
  473. city?.name || '',
  474. district?.name || ''
  475. ].filter(Boolean).join(' ')
  476. this.showRegionPicker = false
  477. },
  478. setDefault(id) {
  479. // 找到当前地址项
  480. const address = this.addressList.find(item => item.id === id)
  481. // 如果已经是默认地址,则取消默认
  482. const defaultFlag = address.defaultFlag === 'Y' ? 'N' : 'Y'
  483. this.$api('updateDefaultAddress', {
  484. id: id
  485. }, (res) => {
  486. if(res.code == 200) {
  487. uni.showToast({
  488. title: defaultFlag === 'Y' ? '设置成功' : '已取消默认',
  489. icon: 'success'
  490. })
  491. // 设置成功后刷新地址列表
  492. this.getAddressList()
  493. }
  494. })
  495. },
  496. deleteAddress(id) {
  497. uni.showModal({
  498. title: '提示',
  499. content: '确定要删除该地址吗?',
  500. success: (res) => {
  501. if (res.confirm) {
  502. this.$api('deleteAddress', {id:id}, res => {
  503. if(res.code == 200) {
  504. uni.showToast({
  505. title: '删除成功',
  506. icon: 'success'
  507. })
  508. // 删除成功后刷新地址列表
  509. this.getAddressList()
  510. }
  511. })
  512. }
  513. }
  514. })
  515. },
  516. initRegionPicker() {
  517. if (this.form.region.length > 0) {
  518. const [provinceCode, cityCode, districtCode] = this.form.region
  519. const provinceIndex = this.provinces.findIndex(p => p.code === provinceCode)
  520. if (provinceIndex > -1) {
  521. this.cities = this.provinces[provinceIndex].children || []
  522. if (this.cities.length === 0) {
  523. this.cities = [{ code: '', name: '请选择' }]
  524. }
  525. const cityIndex = this.cities.findIndex(c => c.code === cityCode)
  526. if (cityIndex > -1) {
  527. this.districts = this.cities[cityIndex].children || []
  528. if (this.districts.length === 0) {
  529. this.districts = [{ code: '', name: '请选择' }]
  530. }
  531. const districtIndex = this.districts.findIndex(d => d.code === districtCode)
  532. this.regionIndex = [
  533. provinceIndex,
  534. cityIndex,
  535. districtIndex > -1 ? districtIndex : 0
  536. ]
  537. return
  538. }
  539. }
  540. }
  541. this.cities = this.provinces[0]?.children || []
  542. if (this.cities.length === 0) {
  543. this.cities = [{ code: '', name: '请选择' }]
  544. }
  545. this.districts = this.cities[0]?.children || []
  546. if (this.districts.length === 0) {
  547. this.districts = [{ code: '', name: '请选择' }]
  548. }
  549. this.regionIndex = [0, 0, 0]
  550. }
  551. },
  552. }
  553. </script>
  554. <style lang="scss" scoped>
  555. .container {
  556. min-height: 100vh;
  557. background: #f8f8f8;
  558. padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
  559. }
  560. .nav-bar {
  561. display: flex;
  562. align-items: center;
  563. background: #fff;
  564. padding: 0 30rpx;
  565. position: fixed;
  566. top: 0;
  567. left: 0;
  568. right: 0;
  569. z-index: 999;
  570. .back {
  571. padding: 20rpx;
  572. margin-left: -20rpx;
  573. }
  574. .title {
  575. flex: 1;
  576. text-align: center;
  577. font-size: 34rpx;
  578. font-weight: 500;
  579. color: #222;
  580. }
  581. .right-btns {
  582. display: flex;
  583. align-items: center;
  584. gap: 30rpx;
  585. .more, .target {
  586. font-size: 40rpx;
  587. color: #333;
  588. }
  589. }
  590. }
  591. .address-list {
  592. padding: 32rpx;
  593. margin-top: calc(38rpx + var(--status-bar-height));
  594. height: calc(100vh - 88rpx - var(--status-bar-height) - 140rpx - env(safe-area-inset-bottom));
  595. box-sizing: border-box;
  596. overflow-y: auto;
  597. -webkit-overflow-scrolling: touch;
  598. .address-item {
  599. background: #fff;
  600. border-radius: 24rpx;
  601. margin-bottom: 24rpx;
  602. padding: 32rpx 28rpx 0 28rpx;
  603. box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.04);
  604. .address-header {
  605. display: flex;
  606. align-items: center;
  607. .name { font-size: 32rpx; color: #222; font-weight: 500; margin-right: 20rpx; }
  608. .phone { font-size: 32rpx; color: #222; }
  609. .default-tag {
  610. margin-left: 18rpx;
  611. font-size: 24rpx;
  612. color: #FF9500;
  613. border: 1rpx solid #FF9500;
  614. border-radius: 8rpx;
  615. padding: 2rpx 12rpx;
  616. background: #FFF7E6;
  617. vertical-align: middle;
  618. }
  619. }
  620. .address-text {
  621. font-size: 28rpx;
  622. color: #888;
  623. margin: 16rpx 0 0 0;
  624. line-height: 1.5;
  625. }
  626. .dashed-line {
  627. border-bottom: 1rpx dashed #eee;
  628. margin: 24rpx 0 0 0;
  629. }
  630. .address-actions {
  631. display: flex;
  632. align-items: center;
  633. justify-content: space-around;
  634. padding: 0 0 18rpx 0;
  635. .action {
  636. display: flex;
  637. align-items: center;
  638. margin-right: 48rpx;
  639. .circle {
  640. width: 36rpx; height: 36rpx; border-radius: 50%;
  641. border: 2rpx solid #FF9500; margin-right: 8rpx;
  642. display: flex; align-items: center; justify-content: center;
  643. &.active {
  644. background: #FF9500; border: none;
  645. .check { color: #fff; font-size: 24rpx; }
  646. }
  647. }
  648. text { font-size: 26rpx; color: #bbb; }
  649. .active-text { color: #FF9500; }
  650. }
  651. }
  652. }
  653. }
  654. .bottom-bar {
  655. position: fixed;
  656. left: 0; right: 0; bottom: 0;
  657. background: #fff;
  658. padding: 24rpx 32rpx env(safe-area-inset-bottom);
  659. z-index: 10;
  660. margin-bottom: calc(40rpx );
  661. }
  662. .main-btn {
  663. width: 100%;
  664. height: 90rpx;
  665. background: linear-gradient(90deg, #FFB74D 0%, #FF9500 100%);
  666. color: #fff;
  667. font-size: 32rpx;
  668. border-radius: 45rpx;
  669. font-weight: bold;
  670. margin: 0 auto;
  671. margin: calc(40rpx) 0 40rpx 0;
  672. display: flex; align-items: center; justify-content: center;
  673. box-shadow: 0 4rpx 16rpx rgba(255, 149, 0, 0.08);
  674. }
  675. .modal-mask {
  676. position: fixed; left: 0; right: 0; top: 0; bottom: 0;
  677. background: rgba(0,0,0,0.5); z-index: 1000;
  678. }
  679. .address-modal, .region-modal {
  680. position: fixed; left: 0; right: 0; bottom: 0; z-index: 1001;
  681. background: #fff; border-radius: 32rpx 32rpx 0 0;
  682. box-shadow: 0 -4rpx 32rpx rgba(0,0,0,0.08);
  683. padding-bottom: env(safe-area-inset-bottom);
  684. animation: slideUp 0.2s;
  685. }
  686. @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
  687. .modal-header {
  688. display: flex; align-items: center; justify-content: space-between;
  689. padding: 32rpx 32rpx 0 32rpx;
  690. .close-btn { color: #bbb; font-size: 30rpx; }
  691. .modal-title { flex: 1; text-align: center; font-size: 34rpx; font-weight: bold; color: #222; }
  692. }
  693. .modal-form {
  694. padding: 0 32rpx;
  695. .form-item {
  696. border-bottom: 1rpx solid #f2f2f2;
  697. padding: 28rpx 0 10rpx 0;
  698. .label { color: #222; font-size: 28rpx; margin-bottom: 8rpx; }
  699. .input, .region-input {
  700. width: 100%; font-size: 28rpx; color: #222; background: none; border: none; outline: none;
  701. &.placeholder { color: #ccc; }
  702. }
  703. .region-input { display: flex; align-items: center; justify-content: space-between; }
  704. .arrow { color: #ccc; font-size: 28rpx; margin-left: 10rpx; }
  705. }
  706. }
  707. .region-tip {
  708. padding: 0 32rpx 16rpx 32rpx;
  709. text-align: center;
  710. text {
  711. font-size: 26rpx;
  712. color: #999;
  713. }
  714. }
  715. .region-picker {
  716. padding: 16rpx 0 0 0;
  717. .picker-view {
  718. width: 100%; height: 300rpx; display: flex; justify-content: center; align-items: center;
  719. .active { color: #222; font-weight: bold; }
  720. view { color: #ccc; font-size: 28rpx; text-align: center; }
  721. }
  722. .item {
  723. // line-height: 100upx;
  724. text-align: center;
  725. display: flex;
  726. align-items: center;
  727. justify-content: center;
  728. // height: 100upx;
  729. font-size: 30rpx;
  730. color: #222;
  731. }
  732. }
  733. </style>