西藏巴青项目

index.vue 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <template>
  2. <!-- 兽医资料:列表项已含全部字段,见在线问诊接口说明 -->
  3. <view :class="pageRootClass" class="tab-page vp-page" :style="pageStyle">
  4. <scroll-view scroll-y class="vp-scroll" enable-back-to-top :show-scrollbar="false">
  5. <view class="vp-card">
  6. <view class="vp-hero">
  7. <up-avatar
  8. shape="circle"
  9. :src="photoSrc"
  10. :text="photoSrc ? '' : avatarText"
  11. size="48"
  12. font-size="18"
  13. bg-color="#9ca3af"
  14. color="#ffffff"
  15. />
  16. <view class="vp-hero__right">
  17. <text class="vp-hero__title text-body">{{ vetTitle }}</text>
  18. <text v-if="unitLine" class="vp-meta text-body">{{ unitLine }}</text>
  19. <text v-if="contactLine" class="vp-meta text-body">{{ contactLine }}</text>
  20. </view>
  21. </view>
  22. <text class="vp-line text-body">
  23. <text class="vp-line__k">{{ $t('vetProfilePage.labelIntro') }}</text>
  24. {{ introBody }}
  25. </text>
  26. <text class="vp-line text-body">
  27. <text class="vp-line__k">{{ $t('vetProfilePage.labelAddress') }}</text>
  28. {{ detailAddress }}
  29. </text>
  30. <text class="vp-line text-body">
  31. <text class="vp-line__k">{{ $t('vetProfilePage.labelHours') }}</text>
  32. {{ serviceHours }}
  33. </text>
  34. <up-button
  35. type="success"
  36. shape="circle"
  37. :text="$t('vetProfilePage.btnConsult')"
  38. :loading="consulting"
  39. :custom-style="{ width: '100%', marginTop: '8px' }"
  40. @click="onConsult"
  41. />
  42. </view>
  43. <view class="vp-footer-spacer" />
  44. </scroll-view>
  45. </view>
  46. </template>
  47. <script>
  48. import UAvatar from 'uview-plus/components/u-avatar/u-avatar.vue'
  49. import UButton from 'uview-plus/components/u-button/u-button.vue'
  50. import tabPage from '@/mixins/tabPage'
  51. import pageViewport from '@/mixins/pageViewport'
  52. import { resolveResourceUrl } from '@/utils/resourceUrl'
  53. import { ensureApiToken } from '@/utils/apiAuth'
  54. import {
  55. loadVetProfileCache,
  56. openOnlineConsultSession,
  57. saveVetProfileCache
  58. } from '@/api/onlineConsult'
  59. const CONSULT_DETAIL_PATH = '/package-a/consult-detail/index'
  60. export default {
  61. components: {
  62. 'up-avatar': UAvatar,
  63. 'up-button': UButton
  64. },
  65. mixins: [tabPage, pageViewport],
  66. data() {
  67. return {
  68. navTitleKey: 'vetProfilePage.navTitle',
  69. vetId: '',
  70. vet: null,
  71. consulting: false
  72. }
  73. },
  74. computed: {
  75. avatarText() {
  76. const name = (this.vet && this.vet.resourceName) || ''
  77. return name.slice(0, 1) || '兽'
  78. },
  79. photoSrc() {
  80. const url = this.vet && this.vet.photoFileUrl
  81. return url ? resolveResourceUrl(url) : ''
  82. },
  83. vetTitle() {
  84. return (this.vet && this.vet.resourceName) || this.$t('vetProfilePage.nameFallback')
  85. },
  86. unitLine() {
  87. const unit = this.vet && this.vet.affiliatedUnit
  88. return unit ? this.$t('vetProfilePage.unitTpl', { unit }) : ''
  89. },
  90. contactLine() {
  91. const phone = this.vet && this.vet.contactPhone
  92. return phone ? this.$t('vetProfilePage.contactTpl', { phone }) : ''
  93. },
  94. introBody() {
  95. return (this.vet && this.vet.introduction) || this.$t('vetProfilePage.noIntro')
  96. },
  97. detailAddress() {
  98. return (this.vet && this.vet.detailAddress) || this.$t('vetProfilePage.noAddress')
  99. },
  100. serviceHours() {
  101. const start = this.vet && this.vet.serviceStartTime
  102. const end = this.vet && this.vet.serviceEndTime
  103. if (start && end) {
  104. return this.$t('vetProfilePage.hoursTpl', { start, end })
  105. }
  106. return this.$t('vetProfilePage.noHours')
  107. }
  108. },
  109. onLoad(query) {
  110. this.vetId = query.id ? decodeURIComponent(query.id) : ''
  111. const cached = loadVetProfileCache(this.vetId)
  112. if (cached) {
  113. this.vet = cached
  114. }
  115. },
  116. onShow() {
  117. if (this.vet && this.vet.resourceName) {
  118. uni.setNavigationBarTitle({ title: this.vet.resourceName })
  119. }
  120. },
  121. methods: {
  122. subLine() {
  123. if (this.vet && this.vet.affiliatedUnit) {
  124. return this.vet.affiliatedUnit
  125. }
  126. if (this.vet && this.vet.serviceStartTime && this.vet.serviceEndTime) {
  127. return `${this.vet.serviceStartTime} - ${this.vet.serviceEndTime}`
  128. }
  129. return ''
  130. },
  131. onConsult() {
  132. if (!this.vetId || !ensureApiToken()) return
  133. this.consulting = true
  134. uni.showLoading({ title: this.$t('vetProfilePage.openingSession'), mask: true })
  135. openOnlineConsultSession({ vetResourceId: Number(this.vetId) || this.vetId })
  136. .then((res) => {
  137. const session = res.data || {}
  138. const sessionId = session.id != null ? String(session.id) : ''
  139. if (!sessionId) return
  140. if (this.vet) {
  141. saveVetProfileCache(this.vetId, this.vet)
  142. }
  143. const name = session.receiverName || (this.vet && this.vet.resourceName) || ''
  144. const q = [
  145. `sessionId=${encodeURIComponent(sessionId)}`,
  146. `vetResourceId=${encodeURIComponent(this.vetId)}`,
  147. `id=${encodeURIComponent(this.vetId)}`,
  148. `name=${encodeURIComponent(name)}`,
  149. `sub=${encodeURIComponent(this.subLine())}`,
  150. `intro=${encodeURIComponent((this.vet && this.vet.introduction) || '')}`,
  151. `avatar=${encodeURIComponent(this.avatarText)}`
  152. ].join('&')
  153. uni.navigateTo({ url: `${CONSULT_DETAIL_PATH}?${q}` })
  154. })
  155. .finally(() => {
  156. this.consulting = false
  157. uni.hideLoading()
  158. })
  159. }
  160. }
  161. }
  162. </script>
  163. <style lang="scss" scoped>
  164. @import '@/styles/morandi.scss';
  165. @import '@/styles/tab-page.scss';
  166. .vp-page {
  167. display: flex;
  168. flex-direction: column;
  169. min-width: 0;
  170. width: 100%;
  171. height: 100%;
  172. min-height: 100%;
  173. overflow: hidden;
  174. box-sizing: border-box;
  175. }
  176. .vp-scroll {
  177. flex: 1;
  178. min-height: 0;
  179. min-width: 0;
  180. height: 0;
  181. box-sizing: border-box;
  182. padding: 24rpx;
  183. }
  184. .vp-card {
  185. margin: 0;
  186. padding: 32rpx 16rpx 40rpx;
  187. box-sizing: border-box;
  188. background: #ffffff;
  189. min-width: 0;
  190. border-radius: 16rpx;
  191. }
  192. .vp-hero {
  193. display: flex;
  194. flex-direction: row;
  195. align-items: center;
  196. gap: 20rpx;
  197. margin-bottom: 20rpx;
  198. min-width: 0;
  199. }
  200. .vp-hero__right {
  201. flex: 1;
  202. min-width: 0;
  203. display: flex;
  204. flex-direction: column;
  205. gap: 8rpx;
  206. }
  207. .vp-hero__title {
  208. font-size: 32rpx;
  209. font-weight: 600;
  210. color: #111827;
  211. line-height: 1.4;
  212. word-break: break-word;
  213. }
  214. .vp-meta {
  215. font-size: 24rpx;
  216. color: $morandi-text-muted;
  217. line-height: 1.45;
  218. word-break: break-word;
  219. }
  220. .vp-line {
  221. display: block;
  222. font-size: 24rpx;
  223. color: $morandi-text-muted;
  224. line-height: 1.55;
  225. margin-top: 14rpx;
  226. word-break: break-word;
  227. }
  228. .vp-line__k {
  229. color: $morandi-text-muted;
  230. font-weight: 500;
  231. }
  232. .vp-footer-spacer {
  233. height: 48rpx;
  234. }
  235. .vp-page.lang-bo {
  236. .vp-hero__title {
  237. font-size: 28rpx;
  238. line-height: 1.65;
  239. }
  240. .vp-meta,
  241. .vp-line {
  242. font-size: 22rpx;
  243. line-height: 1.7;
  244. }
  245. }
  246. </style>