西藏巴青项目

index.vue 20KB

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