西藏巴青项目

index.vue 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. <template>
  2. <!-- 预约机构:见 doc/app/预约服务/预约服务接口说明.md -->
  3. <view :class="pageRootClass" class="tab-page bgo-page">
  4. <scroll-view scroll-y class="bgo-scroll" enable-back-to-top :show-scrollbar="false">
  5. <view class="bgo-card bgo-card--top">
  6. <view class="bgo-hero">
  7. <up-avatar
  8. v-if="photoSrc"
  9. shape="circle"
  10. :src="photoSrc"
  11. size="72"
  12. font-size="26"
  13. />
  14. <up-avatar
  15. v-else
  16. shape="circle"
  17. :text="avatarText"
  18. size="72"
  19. font-size="26"
  20. bg-color="#9ca3af"
  21. color="#ffffff"
  22. />
  23. <view class="bgo-hero__right">
  24. <text class="bgo-hero__title text-body">{{ orgTitle }}</text>
  25. <text class="bgo-hero__sub text-body">{{ orgSub }}</text>
  26. <text class="bgo-hero__contact text-body">{{ contactLine }}</text>
  27. </view>
  28. </view>
  29. <text class="bgo-line text-body"><text class="bgo-line__k">{{ $t('bookingOrgPage.labelIntro') }}</text>{{ introBody }}</text>
  30. <text class="bgo-line text-body"><text class="bgo-line__k">{{ $t('bookingOrgPage.labelAddress') }}</text>{{ detailAddress }}</text>
  31. <text class="bgo-line text-body"><text class="bgo-line__k">{{ $t('bookingOrgPage.labelHours') }}</text>{{ serviceHours }}</text>
  32. </view>
  33. <view class="bgo-card bgo-card--bottom">
  34. <text class="bgo-section-title text-body">{{ $t('bookingOrgPage.pickTimeTitle') }}</text>
  35. <scroll-view scroll-x class="bgo-date-scroll" :show-scrollbar="false" enable-flex>
  36. <view class="bgo-date-row">
  37. <view
  38. v-for="chip in dateChips"
  39. :key="chip.key"
  40. class="bgo-chip"
  41. :class="{
  42. 'bgo-chip--on': !chip.weekdayDisabled && selectedDateKey === chip.key,
  43. 'bgo-chip--disabled': chip.weekdayDisabled
  44. }"
  45. role="button"
  46. @tap="onChipTap(chip)"
  47. >
  48. <view class="bgo-chip__stack">
  49. <text class="bgo-chip__name text-body">{{ chip.weekdayLabel }}</text>
  50. <text v-if="!chip.weekdayDisabled" class="bgo-chip__date text-body">{{ chip.dateShort }}</text>
  51. <text v-else class="bgo-chip__full text-body">{{ $t('bookingOrgPage.dayNotAvailable') }}</text>
  52. </view>
  53. </view>
  54. </view>
  55. </scroll-view>
  56. <up-divider :hairline="true" line-color="#e5e7eb" margin="12rpx 0 8rpx" />
  57. <up-button
  58. type="success"
  59. shape="circle"
  60. :disabled="bookBtnDisabled"
  61. :text="bookBtnText"
  62. @click="openBookPopup"
  63. />
  64. </view>
  65. <view class="bgo-footer-spacer" />
  66. </scroll-view>
  67. <up-popup
  68. :show="bookPopupShow"
  69. mode="center"
  70. :round="16"
  71. :close-on-click-overlay="true"
  72. :safe-area-inset-bottom="true"
  73. :custom-style="bookPopupStyle"
  74. @update:show="bookPopupShow = $event"
  75. >
  76. <view class="bgo-popup">
  77. <scroll-view scroll-y class="bgo-popup__scroll" :show-scrollbar="false">
  78. <text class="bgo-popup__h1 text-body">{{ $t('bookingOrgPage.popupBookTitle') }}</text>
  79. <text class="bgo-popup__row text-body">{{ $t('bookingOrgPage.popupOrgName') }}{{ orgTitle }}</text>
  80. <text class="bgo-popup__row text-body">{{ $t('bookingOrgPage.popupBookDate') }}{{ popupDateLine }}</text>
  81. <text class="bgo-popup__h2 text-body">{{ $t('bookingOrgPage.popupServiceTitle') }}</text>
  82. <up-form
  83. ref="bookFormRef"
  84. label-position="top"
  85. :model="formModel"
  86. :rules="formRules"
  87. label-width="100%"
  88. :border-bottom="false"
  89. error-type="toast"
  90. >
  91. <up-form-item :label="$t('bookingOrgPage.formBooker')" prop="bookerName" required>
  92. <up-input v-model="formModel.bookerName" border="surround" clearable />
  93. </up-form-item>
  94. <up-form-item :label="$t('bookingOrgPage.formPhone')" prop="phone" required>
  95. <up-input v-model="formModel.phone" type="number" maxlength="11" border="surround" clearable />
  96. </up-form-item>
  97. </up-form>
  98. </scroll-view>
  99. <view class="bgo-popup__actions">
  100. <up-button
  101. class="bgo-popup__btn bgo-popup__btn--cancel"
  102. :text="$t('bookingOrgPage.btnCancel')"
  103. shape="circle"
  104. plain
  105. hairline
  106. @click="closeBookPopup"
  107. />
  108. <up-button
  109. class="bgo-popup__btn"
  110. type="success"
  111. :loading="submitting"
  112. :text="submitting ? $t('bookingOrgPage.submitting') : $t('bookingOrgPage.btnSubmit')"
  113. shape="circle"
  114. @click="onSubmitBook"
  115. />
  116. </view>
  117. </view>
  118. </up-popup>
  119. </view>
  120. </template>
  121. <script>
  122. import UAvatar from 'uview-plus/components/u-avatar/u-avatar.vue'
  123. import UButton from 'uview-plus/components/u-button/u-button.vue'
  124. import UDivider from 'uview-plus/components/u-divider/u-divider.vue'
  125. import UForm from 'uview-plus/components/u-form/u-form.vue'
  126. import UFormItem from 'uview-plus/components/u-form-item/u-form-item.vue'
  127. import UInput from 'uview-plus/components/u-input/u-input.vue'
  128. import UPopup from 'uview-plus/components/u-popup/u-popup.vue'
  129. import tabPage from '@/mixins/tabPage'
  130. import { resolveResourceUrl } from '@/utils/resourceUrl'
  131. import { ensureApiToken } from '@/utils/apiAuth'
  132. import { useUserStore } from '@/store/user'
  133. import {
  134. BOOKING_PROVIDER_TYPE,
  135. checkAppointmentBooked,
  136. listBookingDates,
  137. loadBookingResourceCache,
  138. submitBookingAppointment
  139. } from '@/api/bookingService'
  140. const ORG_RESOURCE_TYPE = '004003'
  141. function parseServiceWeekdays(raw) {
  142. if (raw == null || String(raw).trim() === '') return null
  143. const set = new Set()
  144. for (const part of String(raw).split(',')) {
  145. const n = parseInt(part.trim(), 10)
  146. if (n >= 1 && n <= 7) set.add(n)
  147. }
  148. return set.size ? set : null
  149. }
  150. export default {
  151. components: {
  152. 'up-avatar': UAvatar,
  153. 'up-button': UButton,
  154. 'up-divider': UDivider,
  155. 'up-form': UForm,
  156. 'up-form-item': UFormItem,
  157. 'up-input': UInput,
  158. 'up-popup': UPopup
  159. },
  160. mixins: [tabPage],
  161. data() {
  162. return {
  163. navTitleKey: 'bookingOrgPage.navTitle',
  164. orgId: '',
  165. resourceType: ORG_RESOURCE_TYPE,
  166. resource: null,
  167. apiDates: [],
  168. bookedDateMap: {},
  169. selectedDateKey: '',
  170. bookPopupShow: false,
  171. submitting: false,
  172. formModel: {
  173. bookerName: '',
  174. phone: ''
  175. },
  176. bookPopupStyle: {
  177. width: '90%',
  178. maxWidth: '680px',
  179. maxHeight: '72vh',
  180. overflow: 'hidden',
  181. display: 'flex',
  182. flexDirection: 'column'
  183. }
  184. }
  185. },
  186. computed: {
  187. allowedWeekdays() {
  188. return parseServiceWeekdays(this.resource && this.resource.serviceWeekdays)
  189. },
  190. dateChips() {
  191. return (this.apiDates || []).map((d) => {
  192. const key = d.appointDate || ''
  193. const weekday = d.weekday
  194. const allowed = this.allowedWeekdays
  195. const weekdayDisabled =
  196. allowed != null && weekday != null && !allowed.has(Number(weekday))
  197. return {
  198. key,
  199. dateShort: d.dateMmDd || '',
  200. weekday,
  201. weekdayLabel: this.weekdayLabelFor(d),
  202. weekdayDisabled
  203. }
  204. })
  205. },
  206. photoSrc() {
  207. const url = this.resource && this.resource.photoFileUrl
  208. return url ? resolveResourceUrl(url) : ''
  209. },
  210. selectedDateBooked() {
  211. return !!this.bookedDateMap[this.selectedDateKey]
  212. },
  213. bookBtnDisabled() {
  214. const chip = this.dateChips.find((c) => c.key === this.selectedDateKey)
  215. if (!chip || chip.weekdayDisabled) return true
  216. return this.selectedDateBooked
  217. },
  218. bookBtnText() {
  219. if (this.selectedDateBooked) return this.$t('bookingOrgPage.btnBooked')
  220. return this.$t('bookingOrgPage.btnBook')
  221. },
  222. avatarText() {
  223. const name = (this.resource && this.resource.resourceName) || ''
  224. return name.slice(0, 1) || '?'
  225. },
  226. orgTitle() {
  227. return (this.resource && this.resource.resourceName) || this.$t('bookingOrgPage.noResource')
  228. },
  229. orgSub() {
  230. return (this.resource && this.resource.affiliatedUnit) || this.$t('bookingServicePage.noUnit')
  231. },
  232. introBody() {
  233. const intro = (this.resource && this.resource.introduction) || ''
  234. return intro.trim() || this.$t('bookingOrgPage.noIntro')
  235. },
  236. contactLine() {
  237. const phone = this.resource && this.resource.contactPhone
  238. if (!phone) return this.$t('bookingOrgPage.contactLabel') + '—'
  239. return this.$t('bookingOrgPage.contactLabel') + phone
  240. },
  241. detailAddress() {
  242. const addr = (this.resource && this.resource.detailAddress) || ''
  243. return addr.trim() || this.$t('bookingOrgPage.noAddress')
  244. },
  245. serviceHours() {
  246. const start = this.resource && this.resource.serviceStartTime
  247. const end = this.resource && this.resource.serviceEndTime
  248. if (start && end) {
  249. return this.$t('bookingOrgPage.hoursTpl', { start, end })
  250. }
  251. return this.$t('bookingOrgPage.noHours')
  252. },
  253. defaultTimeSlot() {
  254. const start = this.resource && this.resource.serviceStartTime
  255. const end = this.resource && this.resource.serviceEndTime
  256. if (start && end) return `${start}~${end}`
  257. return '08:00~17:00'
  258. },
  259. popupDateLine() {
  260. const chip = this.dateChips.find((c) => c.key === this.selectedDateKey)
  261. if (!chip || chip.weekdayDisabled) return ''
  262. return this.formatMdWeekday(chip)
  263. },
  264. formRules() {
  265. return {
  266. bookerName: [
  267. {
  268. required: true,
  269. message: this.$t('bookingOrgPage.errBooker'),
  270. trigger: ['blur', 'change']
  271. }
  272. ],
  273. phone: [
  274. {
  275. required: true,
  276. message: this.$t('bookingOrgPage.errPhone'),
  277. trigger: ['blur', 'change']
  278. },
  279. {
  280. pattern: /^1[3-9]\d{9}$/,
  281. message: this.$t('bookingOrgPage.errPhoneFmt'),
  282. trigger: ['blur', 'change']
  283. }
  284. ]
  285. }
  286. }
  287. },
  288. onLoad(query) {
  289. if (!ensureApiToken()) return
  290. const q = query || {}
  291. this.orgId = q.id ? decodeURIComponent(String(q.id)) : ''
  292. if (q.resourceType) {
  293. try {
  294. this.resourceType = decodeURIComponent(String(q.resourceType))
  295. } catch (e) {
  296. this.resourceType = String(q.resourceType)
  297. }
  298. }
  299. this.loadResource()
  300. this.loadBookingDates()
  301. },
  302. methods: {
  303. weekdayLabelFor(d) {
  304. const w = d && d.weekday
  305. if (w != null && w >= 1 && w <= 7) {
  306. const dowKey = w === 7 ? 0 : w
  307. return this.$t(`bookingServicePage.wd${dowKey}`)
  308. }
  309. return (d && d.weekdayName) || ''
  310. },
  311. loadResource() {
  312. if (!this.orgId) {
  313. uni.showToast({ title: this.$t('bookingOrgPage.noResource'), icon: 'none' })
  314. setTimeout(() => uni.navigateBack(), 1500)
  315. return
  316. }
  317. this.resource = loadBookingResourceCache(this.resourceType, this.orgId)
  318. if (!this.resource) {
  319. uni.showToast({ title: this.$t('bookingOrgPage.noResource'), icon: 'none' })
  320. setTimeout(() => uni.navigateBack(), 1500)
  321. return
  322. }
  323. if (this.apiDates.length) {
  324. this.refreshBookedFlags()
  325. }
  326. },
  327. loadBookingDates() {
  328. listBookingDates()
  329. .then((res) => {
  330. this.apiDates = Array.isArray(res.data) ? res.data : []
  331. this.$nextTick(() => {
  332. this.ensureValidSelection()
  333. if (this.orgId) {
  334. this.refreshBookedFlags()
  335. }
  336. })
  337. })
  338. .catch(() => {
  339. this.apiDates = []
  340. })
  341. },
  342. refreshBookedFlags() {
  343. if (!this.orgId || !this.apiDates.length) return Promise.resolve()
  344. const providerId = Number(this.orgId) || this.orgId
  345. const dates = this.apiDates.map((d) => d.appointDate).filter(Boolean)
  346. return Promise.all(
  347. dates.map((appointDate) =>
  348. checkAppointmentBooked({
  349. providerType: BOOKING_PROVIDER_TYPE.ORG,
  350. providerId,
  351. appointDate
  352. })
  353. .then((res) => ({
  354. appointDate,
  355. booked: !!(res.data && res.data.booked)
  356. }))
  357. .catch(() => ({ appointDate, booked: false }))
  358. )
  359. ).then((results) => {
  360. const next = { ...this.bookedDateMap }
  361. results.forEach(({ appointDate, booked }) => {
  362. next[appointDate] = booked
  363. })
  364. this.bookedDateMap = next
  365. this.$nextTick(() => this.ensureValidSelection())
  366. })
  367. },
  368. fetchDateBooked(appointDate) {
  369. if (!this.orgId || !appointDate) return Promise.resolve(false)
  370. return checkAppointmentBooked({
  371. providerType: BOOKING_PROVIDER_TYPE.ORG,
  372. providerId: Number(this.orgId) || this.orgId,
  373. appointDate
  374. })
  375. .then((res) => {
  376. const booked = !!(res.data && res.data.booked)
  377. this.bookedDateMap = { ...this.bookedDateMap, [appointDate]: booked }
  378. return booked
  379. })
  380. .catch(() => false)
  381. },
  382. formatMdWeekday(chip) {
  383. if (!chip || !chip.key) return ''
  384. return `${chip.dateShort}(${chip.weekdayLabel})`
  385. },
  386. ensureValidSelection() {
  387. const cur = this.dateChips.find((c) => c.key === this.selectedDateKey && !c.weekdayDisabled)
  388. if (cur) return
  389. const ok = this.dateChips.find((c) => !c.weekdayDisabled)
  390. this.selectedDateKey = ok ? ok.key : ''
  391. },
  392. onChipTap(chip) {
  393. if (chip.weekdayDisabled) {
  394. uni.showToast({
  395. title: this.$t('bookingOrgPage.dayNotAvailable'),
  396. icon: 'none'
  397. })
  398. return
  399. }
  400. this.selectedDateKey = chip.key
  401. this.fetchDateBooked(chip.key)
  402. },
  403. openBookPopup() {
  404. if (!this.resource || !this.orgId) {
  405. uni.showToast({ title: this.$t('bookingOrgPage.noResource'), icon: 'none' })
  406. return
  407. }
  408. const sel = this.dateChips.find((c) => c.key === this.selectedDateKey)
  409. if (!sel || sel.weekdayDisabled) {
  410. uni.showToast({
  411. title: this.$t('bookingOrgPage.toastPickDate'),
  412. icon: 'none'
  413. })
  414. return
  415. }
  416. if (this.selectedDateBooked) return
  417. const addr = (this.resource.detailAddress || '').trim()
  418. if (!addr) {
  419. uni.showToast({ title: this.$t('bookingOrgPage.noAddress'), icon: 'none' })
  420. return
  421. }
  422. this.resetBookForm()
  423. this.bookPopupShow = true
  424. },
  425. closeBookPopup() {
  426. this.bookPopupShow = false
  427. },
  428. resetBookForm() {
  429. const store = useUserStore()
  430. const name = store.displayName()
  431. this.formModel = {
  432. bookerName: name || '',
  433. phone: ''
  434. }
  435. this.$nextTick(() => {
  436. this.$refs.bookFormRef?.clearValidate?.()
  437. })
  438. },
  439. onSubmitBook() {
  440. if (this.submitting) return
  441. this.$refs.bookFormRef
  442. ?.validate?.()
  443. .then(() => {
  444. const addr = (this.resource.detailAddress || '').trim()
  445. if (!addr) {
  446. uni.showToast({ title: this.$t('bookingOrgPage.noAddress'), icon: 'none' })
  447. return Promise.reject(new Error('no address'))
  448. }
  449. this.submitting = true
  450. return submitBookingAppointment({
  451. resourceType: this.resourceType,
  452. resourceId: Number(this.orgId) || this.orgId,
  453. appointDate: this.selectedDateKey,
  454. appointeeName: (this.formModel.bookerName || '').trim(),
  455. contactPhone: (this.formModel.phone || '').trim(),
  456. timeSlot: this.defaultTimeSlot,
  457. serviceAddress: addr
  458. })
  459. })
  460. .then(() => {
  461. this.bookedDateMap = { ...this.bookedDateMap, [this.selectedDateKey]: true }
  462. uni.showToast({
  463. title: this.$t('bookingOrgPage.toastSubmitOk'),
  464. icon: 'success'
  465. })
  466. this.bookPopupShow = false
  467. })
  468. .catch((err) => {
  469. const msg = err && (err.msg || err.message || '')
  470. if (String(msg).includes('已满')) {
  471. uni.showToast({ title: this.$t('bookingOrgPage.toastDayFull'), icon: 'none' })
  472. }
  473. })
  474. .finally(() => {
  475. this.submitting = false
  476. })
  477. }
  478. }
  479. }
  480. </script>
  481. <style lang="scss" scoped>
  482. @import '@/styles/morandi.scss';
  483. @import '@/styles/tab-page.scss';
  484. .bgo-page {
  485. display: flex;
  486. flex-direction: column;
  487. min-width: 0;
  488. min-height: 100%;
  489. box-sizing: border-box;
  490. background: $morandi-bg-page;
  491. }
  492. .bgo-scroll {
  493. flex: 1;
  494. min-height: 0;
  495. min-width: 0;
  496. height: 0;
  497. box-sizing: border-box;
  498. padding: 20rpx 24rpx 24rpx;
  499. }
  500. .bgo-card {
  501. background: #ffffff;
  502. border-radius: 16rpx;
  503. border: 1rpx solid $morandi-border-soft;
  504. padding: 24rpx;
  505. box-sizing: border-box;
  506. }
  507. .bgo-card--top {
  508. margin-bottom: 10rpx;
  509. }
  510. .bgo-hero {
  511. display: flex;
  512. flex-direction: row;
  513. align-items: center;
  514. gap: 20rpx;
  515. min-width: 0;
  516. margin-bottom: 20rpx;
  517. }
  518. .bgo-hero__right {
  519. flex: 1;
  520. min-width: 0;
  521. display: flex;
  522. flex-direction: column;
  523. gap: 8rpx;
  524. }
  525. .bgo-hero__title {
  526. font-size: 32rpx;
  527. font-weight: 600;
  528. color: #111827;
  529. line-height: 1.35;
  530. word-break: break-word;
  531. }
  532. .bgo-hero__sub {
  533. font-size: 24rpx;
  534. color: $morandi-text-secondary;
  535. line-height: 1.45;
  536. word-break: break-word;
  537. }
  538. .bgo-hero__contact {
  539. font-size: 22rpx;
  540. color: $morandi-text-muted;
  541. line-height: 1.45;
  542. word-break: break-word;
  543. }
  544. .bgo-line {
  545. display: block;
  546. font-size: 24rpx;
  547. line-height: 1.55;
  548. color: #111827;
  549. margin-top: 12rpx;
  550. word-break: break-word;
  551. }
  552. .bgo-line__k {
  553. color: $morandi-text-muted;
  554. margin-right: 4rpx;
  555. }
  556. .bgo-section-title {
  557. display: block;
  558. font-size: 28rpx;
  559. font-weight: 600;
  560. color: #111827;
  561. margin-bottom: 16rpx;
  562. }
  563. .bgo-date-scroll {
  564. width: 100%;
  565. white-space: nowrap;
  566. }
  567. .bgo-date-row {
  568. display: inline-flex;
  569. flex-direction: row;
  570. align-items: stretch;
  571. gap: 12rpx;
  572. padding: 4rpx 0 8rpx;
  573. min-width: min-content;
  574. }
  575. .bgo-chip {
  576. flex-shrink: 0;
  577. min-width: 96rpx;
  578. padding: 12rpx 16rpx;
  579. border-radius: 12rpx;
  580. border: 1rpx solid #e5e7eb;
  581. background: #ffffff;
  582. box-sizing: border-box;
  583. display: flex;
  584. flex-direction: row;
  585. align-items: center;
  586. justify-content: center;
  587. }
  588. .bgo-chip--on {
  589. background: #22c55e;
  590. border-color: #16a34a;
  591. }
  592. .bgo-chip--disabled {
  593. opacity: 0.75;
  594. background: #f9fafb;
  595. border-color: #e5e7eb;
  596. }
  597. .bgo-chip__stack {
  598. display: flex;
  599. flex-direction: column;
  600. align-items: center;
  601. justify-content: center;
  602. gap: 4rpx;
  603. }
  604. .bgo-chip__name {
  605. font-size: 24rpx;
  606. color: #111827;
  607. font-weight: 500;
  608. }
  609. .bgo-chip__date {
  610. font-size: 22rpx;
  611. color: #374151;
  612. }
  613. .bgo-chip__full {
  614. font-size: 22rpx;
  615. color: #9ca3af;
  616. font-weight: 500;
  617. }
  618. .bgo-chip--on .bgo-chip__name,
  619. .bgo-chip--on .bgo-chip__date {
  620. color: #ffffff;
  621. font-weight: 600;
  622. }
  623. .bgo-chip--disabled .bgo-chip__name {
  624. color: #9ca3af;
  625. }
  626. .bgo-footer-spacer {
  627. height: 32rpx;
  628. }
  629. .bgo-popup {
  630. display: flex;
  631. flex-direction: column;
  632. min-width: 0;
  633. max-height: 72vh;
  634. padding: 24rpx 24rpx 16rpx;
  635. box-sizing: border-box;
  636. }
  637. .bgo-popup__scroll {
  638. flex: 1;
  639. min-height: 0;
  640. max-height: 48vh;
  641. }
  642. .bgo-popup__h1 {
  643. display: block;
  644. font-size: 30rpx;
  645. font-weight: 600;
  646. color: #111827;
  647. margin-bottom: 20rpx;
  648. }
  649. .bgo-popup__h2 {
  650. display: block;
  651. font-size: 28rpx;
  652. font-weight: 600;
  653. color: #111827;
  654. margin: 24rpx 0 16rpx;
  655. }
  656. .bgo-popup__row {
  657. display: block;
  658. font-size: 26rpx;
  659. color: #374151;
  660. line-height: 1.5;
  661. margin-bottom: 8rpx;
  662. word-break: break-word;
  663. }
  664. .bgo-popup__actions {
  665. display: flex;
  666. flex-direction: row;
  667. gap: 20rpx;
  668. padding-top: 16rpx;
  669. border-top: 1rpx solid #e5e7eb;
  670. margin-top: 8rpx;
  671. }
  672. .bgo-popup__btn {
  673. flex: 1;
  674. min-width: 0;
  675. }
  676. .bgo-popup__btn--cancel {
  677. background: #ffffff !important;
  678. }
  679. .bgo-page.lang-bo {
  680. .bgo-hero__title {
  681. font-size: 28rpx;
  682. line-height: 1.75;
  683. }
  684. }
  685. </style>