西藏巴青项目

index.vue 6.8KB

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