普兆健康管家前端代码仓库
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.

573 lines
19 KiB

  1. <template>
  2. <view class="page__view">
  3. <navbar title="检测预约" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#F3F2F7" />
  4. <view class="main">
  5. <view class="card">
  6. <view class="card-header">{{ typeDesc }}</view>
  7. <view class="flex row">
  8. <view class="row-label">联系电话</view>
  9. <view class="row-content">{{ configList.customer_service_phone || '-' }}</view>
  10. </view>
  11. </view>
  12. <view class="card">
  13. <view class="card-header">预约信息</view>
  14. <view class="form">
  15. <uv-form
  16. ref="form"
  17. :model="form"
  18. errorType="toast"
  19. >
  20. <!-- 自采 -->
  21. <template v-if="type == 0">
  22. <view class="form-item">
  23. <uv-form-item prop="addressData" :customStyle="formItemStyle">
  24. <view class="form-item-label">寄送地址</view>
  25. <view class="form-item-content">
  26. <view class="flex row" @click="jumpToSelectAddress">
  27. <view v-if="form.addressData" class="text">{{ getAddressDesc(form.addressData) }}</view>
  28. <view v-else class="text placeholder">请选择内容</view>
  29. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  30. </view>
  31. </view>
  32. </uv-form-item>
  33. </view>
  34. </template>
  35. <!-- 上门 -->
  36. <template v-else-if="type == 1">
  37. <view class="form-item">
  38. <uv-form-item prop="addressData" :customStyle="formItemStyle">
  39. <view class="form-item-label">体检地址</view>
  40. <view class="form-item-content">
  41. <view class="flex row" @click="jumpToSelectAddress">
  42. <view v-if="form.addressData" class="text">{{ getAddressDesc(form.addressData) }}</view>
  43. <view v-else class="text placeholder">请选择内容</view>
  44. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  45. </view>
  46. </view>
  47. </uv-form-item>
  48. </view>
  49. <view class="form-item">
  50. <uv-form-item prop="date" :customStyle="formItemStyle">
  51. <view class="form-item-label">预约日期</view>
  52. <view class="form-item-content">
  53. <view class="flex row" @click="openDatePicker">
  54. <view v-if="form.date" class="text">{{ $dayjs(form.date).format('YYYY-MM-DD') }}</view>
  55. <view v-else class="text placeholder">请选择内容</view>
  56. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  57. </view>
  58. <uv-datetime-picker
  59. ref="datetimePicker"
  60. v-model="form.date"
  61. mode="date"
  62. title="预约日期"
  63. confirmColor="#7451DE"
  64. cancelColor="#8B8B8B"
  65. round="32rpx"
  66. :minDate="minTime"
  67. @confirm="onDateChange"
  68. ></uv-datetime-picker>
  69. </view>
  70. </uv-form-item>
  71. </view>
  72. <view class="form-item">
  73. <uv-form-item prop="timeRange" :customStyle="formItemStyle">
  74. <view class="form-item-label">预约时段</view>
  75. <view class="form-item-content">
  76. <view class="flex row" @click="openTimePicker">
  77. <view v-if="form.timeRange" class="text">{{ form.timeRange.join('') }}</view>
  78. <view v-else class="text placeholder">请选择内容</view>
  79. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  80. </view>
  81. <uv-picker
  82. ref="timeRangePicker"
  83. :columns="timeColumns"
  84. :defaultIndex="[0, 0]"
  85. title="预约时段"
  86. confirmColor="#7451DE"
  87. cancelColor="#8B8B8B"
  88. round="32rpx"
  89. @change="onTimeRangeColChange"
  90. @confirm="onTimeRangeChange"
  91. ></uv-picker>
  92. </view>
  93. </uv-form-item>
  94. </view>
  95. </template>
  96. <!-- 到店 -->
  97. <template v-else-if="type == 2">
  98. <view class="form-item">
  99. <uv-form-item prop="date" :customStyle="formItemStyle">
  100. <view class="form-item-label">预约日期</view>
  101. <view class="form-item-content">
  102. <view class="flex row" @click="openDatePicker">
  103. <view v-if="form.date" class="text">{{ $dayjs(form.date).format('YYYY-MM-DD') }}</view>
  104. <view v-else class="text placeholder">请选择内容</view>
  105. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  106. </view>
  107. <uv-datetime-picker
  108. ref="datetimePicker"
  109. v-model="form.date"
  110. mode="date"
  111. title="预约日期"
  112. confirmColor="#7451DE"
  113. cancelColor="#8B8B8B"
  114. round="32rpx"
  115. :minDate="minTime"
  116. @confirm="onDateChange"
  117. ></uv-datetime-picker>
  118. </view>
  119. </uv-form-item>
  120. </view>
  121. <view class="form-item">
  122. <uv-form-item prop="timeRange" :customStyle="formItemStyle">
  123. <view class="form-item-label">预约时段</view>
  124. <view class="form-item-content">
  125. <view class="flex row" @click="openTimePicker">
  126. <view v-if="form.timeRange" class="text">{{ form.timeRange.join('~') }}</view>
  127. <view v-else class="text placeholder">请选择内容</view>
  128. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  129. </view>
  130. <uv-picker
  131. ref="timeRangePicker"
  132. :columns="timeColumns"
  133. :defaultIndex="[0, 0]"
  134. title="预约时段"
  135. confirmColor="#7451DE"
  136. cancelColor="#8B8B8B"
  137. round="32rpx"
  138. @change="onTimeRangeColChange"
  139. @confirm="onTimeRangeChange"
  140. ></uv-picker>
  141. </view>
  142. </uv-form-item>
  143. </view>
  144. <view class="form-item">
  145. <uv-form-item prop="hospitalId" :customStyle="formItemStyle">
  146. <view class="form-item-label">预约医院</view>
  147. <view class="form-item-content">
  148. <view class="flex row" @click="openHospitalPicker">
  149. <view v-if="form.hospitalId" class="text">{{ getHospitalDesc(form.hospitalId) }}</view>
  150. <view v-else class="text placeholder">请选择内容</view>
  151. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  152. </view>
  153. <uv-picker
  154. ref="hospitalPicker"
  155. :columns="[hospitalOptions]"
  156. keyName="label"
  157. title="预约医院"
  158. confirmColor="#7451DE"
  159. cancelColor="#8B8B8B"
  160. round="32rpx"
  161. @confirm="onHospitalChange"
  162. ></uv-picker>
  163. </view>
  164. </uv-form-item>
  165. </view>
  166. <view class="form-item">
  167. <uv-form-item prop="name" :customStyle="formItemStyle">
  168. <view class="form-item-label">姓名</view>
  169. <view class="form-item-content input">
  170. <formInput v-model="form.name" placeholder="请输入内容"></formInput>
  171. </view>
  172. </uv-form-item>
  173. </view>
  174. <view class="form-item">
  175. <uv-form-item prop="phone" :customStyle="formItemStyle">
  176. <view class="form-item-label">联系方式</view>
  177. <view class="form-item-content input">
  178. <formInput v-model="form.phone" placeholder="请输入内容"></formInput>
  179. </view>
  180. </uv-form-item>
  181. </view>
  182. </template>
  183. </uv-form>
  184. </view>
  185. </view>
  186. </view>
  187. <view class="bottom">
  188. <button class="btn" @click="onSubmit">提交预约</button>
  189. </view>
  190. </view>
  191. </template>
  192. <script>
  193. import { mapState } from 'vuex'
  194. import formInput from '@/pages_order/components/formInput.vue'
  195. // 可选预约类型 0自采,1上门,2到店,3已取消
  196. const SUBSCRIBE_TYPE_AND_DESC_MAPPING = {
  197. 0: '自采',
  198. 1: '上门',
  199. 2: '到店',
  200. 3: '已取消',
  201. }
  202. export default {
  203. components: {
  204. formInput,
  205. },
  206. data() {
  207. return {
  208. id: null,
  209. productId: null,
  210. type: null,
  211. info: {},
  212. form: {
  213. addressData: null,
  214. date: null,
  215. timeRange: null,
  216. hospitalId: null,
  217. name: null,
  218. phone: null,
  219. },
  220. formItemStyle: { padding: 0 },
  221. minTime: new Date().getTime(),
  222. hours: [],
  223. timeColumns: [],
  224. hospitalOptions: []
  225. }
  226. },
  227. computed: {
  228. ...mapState(['configList', 'userInfo', 'payOrderProduct', 'addressInfo']),
  229. typeDesc() {
  230. const desc = SUBSCRIBE_TYPE_AND_DESC_MAPPING[this.type] || ''
  231. return `${desc}检测`
  232. },
  233. },
  234. onShow() {
  235. if (this.addressInfo) {
  236. const {
  237. id,
  238. name,
  239. phone,
  240. province,
  241. city,
  242. district,
  243. detail,
  244. } = this.addressInfo
  245. this.form.addressData = {
  246. id,
  247. name,
  248. phone,
  249. area: [province, city, district].filter(val => val),
  250. address: detail,
  251. }
  252. this.$store.commit('setAddressInfo', null)
  253. }
  254. },
  255. async onLoad(arg) {
  256. const { id, type } = arg
  257. this.id = id
  258. this.type = parseInt(type)
  259. await this.getData(id)
  260. if (this.type == 2) { // 到店
  261. this.fetchHospitalOptions()
  262. }
  263. },
  264. onReady() {
  265. this.setRules()
  266. },
  267. onUnload() {
  268. this.$store.commit('setAddressInfo', null)
  269. },
  270. methods: {
  271. async getData(id) {
  272. try {
  273. const result = await this.$fetch('getSubscribeDetail', { id })
  274. const {
  275. productId,
  276. name,
  277. phone,
  278. sendAddressId,
  279. sendAddress,
  280. sendAddressDetail,
  281. subscribeDate,
  282. subscribeTime,
  283. hospitalId,
  284. } = result
  285. this.form = {
  286. addressData: {
  287. id: sendAddressId,
  288. name,
  289. phone,
  290. area: [sendAddress],
  291. address: sendAddressDetail,
  292. },
  293. date: new Date(subscribeDate).getTime(),
  294. timeRange: subscribeTime ? JSON.parse(subscribeTime) : null,
  295. hospitalId,
  296. name,
  297. phone,
  298. }
  299. this.productId = productId
  300. } catch (err) {
  301. }
  302. },
  303. async fetchHospitalOptions() {
  304. try {
  305. const result = await this.$fetch('getSubscribeHospital', { productId: this.productId })
  306. this.hospitalOptions = result.map(item => {
  307. const { id, name } = item
  308. return { id, label: name }
  309. })
  310. } catch (err) {
  311. }
  312. },
  313. setRules() {
  314. let rules = {}
  315. switch(this.type) {
  316. case 0: // 自采
  317. rules = {
  318. 'addressData': {
  319. type: 'object',
  320. required: true,
  321. message: '请选择寄送地址',
  322. },
  323. }
  324. break
  325. case 1: // 上门
  326. rules = {
  327. 'addressData': {
  328. type: 'object',
  329. required: true,
  330. message: '请选择体检地址',
  331. },
  332. 'date': {
  333. type: 'number',
  334. required: true,
  335. message: '请选择预约日期',
  336. },
  337. 'timeRange': {
  338. type: 'array',
  339. required: true,
  340. message: '请选择预约时段',
  341. },
  342. }
  343. this.setTimeColumns()
  344. break
  345. case 2: // 到店
  346. rules = {
  347. 'date': {
  348. type: 'number',
  349. required: true,
  350. message: '请选择预约日期',
  351. },
  352. 'timeRange': {
  353. type: 'array',
  354. required: true,
  355. message: '请选择预约时段',
  356. },
  357. 'hospitalId': {
  358. type: 'string',
  359. required: true,
  360. message: '请选择预约医院',
  361. },
  362. 'name': {
  363. type: 'string',
  364. required: true,
  365. message: '请输入姓名',
  366. },
  367. 'phone': {
  368. type: 'string',
  369. required: true,
  370. message: '请输入联系方式',
  371. },
  372. }
  373. this.setTimeColumns()
  374. break
  375. default:
  376. break
  377. }
  378. this.$refs.form.setRules(rules);
  379. },
  380. getAddressDesc(data) {
  381. if (!data) {
  382. return ''
  383. }
  384. const { area, address } = data
  385. return `${area?.join?.('') || ''}${address}`
  386. },
  387. getHospitalDesc() {
  388. },
  389. fixedZero(num) {
  390. return `${num < 10 ? '0' + num : num}`
  391. },
  392. setTimeColumns(date) {
  393. let currentTime = this.$dayjs()
  394. let startHour = 8
  395. let endHour = 22
  396. if (date && this.$dayjs(date).isSame(currentTime, 'day')) {
  397. let currentHour = currentTime.hour()
  398. if (currentHour > endHour) {
  399. this.timeColumns = []
  400. return
  401. }
  402. if (currentHour > startHour) {
  403. startHour = currentHour
  404. }
  405. if (startHour == endHour) {
  406. this.timeColumns = []
  407. return
  408. }
  409. }
  410. this.hours = new Array(endHour - startHour + 1).fill(startHour).map((val, idx) => val + idx)
  411. let startCols = this.hours.slice(0, this.hours.length - 1).map(hour => `${this.fixedZero(hour)}:00`)
  412. let endCols = this.hours.slice(1).map(hour => `${this.fixedZero(hour)}:00`)
  413. this.timeColumns = [startCols, endCols]
  414. this.$refs.timeRangePicker.setColumnValues(0, startCols)
  415. this.$refs.timeRangePicker.setColumnValues(1, endCols)
  416. },
  417. openTimePicker() {
  418. this.$refs.timeRangePicker.open();
  419. },
  420. onTimeRangeColChange(e) {
  421. if (e.columnIndex == 0) {
  422. let startIdx = e.indexs[0]
  423. let endCols = startIdx == this.hours.length - 1 ? [] : this.hours.slice(startIdx + 1).map(hour => `${this.fixedZero(hour)}:00`)
  424. this.timeColumns[1] = endCols
  425. this.$refs.timeRangePicker.setColumnValues(1, endCols)
  426. }
  427. },
  428. onTimeRangeChange(e) {
  429. this.form.timeRange = e.value
  430. const [startTime, endTime] = this.form.timeRange
  431. let startIdx = startTime ? this.timeColumns[0].findIndex(item => item === startTime) : 0
  432. let endIdx = endTime ? this.timeColumns[1].findIndex(item => item === endTime) : 0
  433. this.$refs.timeRangePicker.setIndexs([startIdx, endIdx], true);
  434. },
  435. openDatePicker() {
  436. this.$refs.datetimePicker.open();
  437. },
  438. onDateChange(e) {
  439. const date = e.value
  440. this.setTimeColumns(date)
  441. const [startTime] = this.form.timeRange || []
  442. let dateStr = this.$dayjs(date).format('YYYY-MM-DD')
  443. if (startTime && this.$dayjs(`${dateStr} ${startTime}`).isBefore(this.$dayjs())) {
  444. this.form.timeRange = null
  445. this.$refs.datetimePicker.setIndexs([0, 0]);
  446. }
  447. },
  448. openHospitalPicker() {
  449. this.$refs.hospitalPicker.open();
  450. },
  451. onHospitalChange(e) {
  452. this.form.hospitalId = e.value[0].id
  453. },
  454. getHospitalDesc(id) {
  455. return this.hospitalOptions.find(item => item.id === id)?.label
  456. },
  457. jumpToSelectAddress() {
  458. this.$utils.navigateTo('/pages_order/address/addressList')
  459. },
  460. async onSubmit() {
  461. try {
  462. await this.$refs.form.validate()
  463. const params = {
  464. id: this.id,
  465. }
  466. if (this.type == 0) { // 自采
  467. const { addressData } = this.form
  468. const {
  469. id: sendAddressId,
  470. name,
  471. phone,
  472. area,
  473. address,
  474. } = addressData
  475. params.sendAddressId = sendAddressId || ''
  476. params.sendAddress = area.join('')
  477. params.sendAddressDetail = address
  478. params.name = name
  479. params.phone = phone
  480. } else if (this.type == 1) { // 上门
  481. const { addressData, date, timeRange } = this.form
  482. const {
  483. id: sendAddressId,
  484. name,
  485. phone,
  486. area,
  487. address,
  488. } = addressData
  489. params.sendAddressId = sendAddressId || ''
  490. params.sendAddress = area.join('')
  491. params.sendAddressDetail = address
  492. params.name = name
  493. params.phone = phone
  494. params.subscribeDate = this.$dayjs(date).format('YYYY-MM-DD')
  495. params.subscribeTime = JSON.stringify(timeRange)
  496. } else if (this.type == 2) { // 到店
  497. const { date, timeRange, hospitalId, name, phone } = this.form
  498. params.subscribeDate = this.$dayjs(date).format('YYYY-MM-DD')
  499. params.subscribeTime = JSON.stringify(timeRange)
  500. params.hospitalId = hospitalId
  501. params.name = name
  502. params.phone = phone
  503. }
  504. await this.$fetch('submitOrUpdateSubscribe', params)
  505. uni.showToast({
  506. icon: 'success',
  507. title: '提交成功',
  508. });
  509. setTimeout(() => {
  510. this.$utils.redirectTo(`/pages_order/checkup/checkupBook/detail?id=${this.id}`)
  511. }, 800)
  512. } catch (err) {
  513. console.log('onSubmit err', err)
  514. }
  515. },
  516. },
  517. }
  518. </script>
  519. <style scoped lang="scss">
  520. @import './style.scss';
  521. </style>