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

532 lines
18 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">{{ info.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 == 1">
  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 == 2">
  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 == 3">
  98. <view class="form-item">
  99. <uv-form-item prop="addressData" :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="hospital" :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.hospital" class="text">{{ getHospitalDesc(form.hospital) }}</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. const TYPE_AND_DESC_MAPPING = {
  196. 1: '自采',
  197. 2: '上门',
  198. 3: '到店',
  199. }
  200. export default {
  201. components: {
  202. formInput,
  203. },
  204. data() {
  205. return {
  206. id: null,
  207. type: null,
  208. info: {},
  209. form: {
  210. addressData: null,
  211. date: null,
  212. timeRange: null,
  213. hospital: null,
  214. name: null,
  215. phone: null,
  216. },
  217. formItemStyle: { padding: 0 },
  218. minTime: new Date().getTime(),
  219. hours: [],
  220. timeColumns: [],
  221. hospitalOptions: []
  222. }
  223. },
  224. computed: {
  225. ...mapState(['configList', 'userInfo', 'payOrderProduct', 'addressInfo']),
  226. typeDesc() {
  227. const desc = TYPE_AND_DESC_MAPPING[this.type] || ''
  228. return `${desc}检测`
  229. },
  230. },
  231. onShow() {
  232. console.log('onShow')
  233. console.log('address', this.addressInfo)
  234. this.form.addressData = this.addressInfo || null
  235. },
  236. onLoad(arg) {
  237. const { id, type } = arg
  238. this.id = id
  239. this.type = parseInt(type)
  240. // todo: fetch data
  241. this.fetchCheckUpPackageInfo()
  242. this.fetchCheckUpBookInfo()
  243. },
  244. onReady() {
  245. this.setRules()
  246. },
  247. onUnload() {
  248. this.$store.commit('setAddressInfo', null)
  249. },
  250. methods: {
  251. setRules() {
  252. let rules = {}
  253. switch(this.type) {
  254. case 1: // 自采
  255. rules = {
  256. 'addressData': {
  257. type: 'object',
  258. required: true,
  259. message: '请选择寄送地址',
  260. },
  261. }
  262. break
  263. case 2: // 上门
  264. rules = {
  265. 'addressData': {
  266. type: 'object',
  267. required: true,
  268. message: '请选择体检地址',
  269. },
  270. 'date': {
  271. type: 'string',
  272. required: true,
  273. message: '请选择预约日期',
  274. },
  275. 'timeRange': {
  276. type: 'array',
  277. required: true,
  278. message: '请选择预约时段',
  279. },
  280. }
  281. this.setTimeColumns()
  282. break
  283. case 3: // 到店
  284. rules = {
  285. 'date': {
  286. type: 'string',
  287. required: true,
  288. message: '请选择预约日期',
  289. },
  290. 'timeRange': {
  291. type: 'array',
  292. required: true,
  293. message: '请选择预约时段',
  294. },
  295. 'hospital': {
  296. type: 'string',
  297. required: true,
  298. message: '请选择预约医院',
  299. },
  300. 'name': {
  301. type: 'string',
  302. required: true,
  303. message: '请输入姓名',
  304. },
  305. 'phone': {
  306. type: 'string',
  307. required: true,
  308. message: '请输入联系方式',
  309. },
  310. }
  311. this.setTimeColumns()
  312. this.hospitalOptions = [
  313. { id: '001', label: '湖南省长沙市湘雅第一医院' },
  314. { id: '002', label: '湖南省长沙市湘雅第二医院' },
  315. { id: '003', label: '湖南省长沙市湘雅第三医院' },
  316. { id: '004', label: '湖南省长沙市湘雅第四医院' },
  317. ]
  318. break
  319. default:
  320. break
  321. }
  322. this.$refs.form.setRules(rules);
  323. },
  324. getAddressDesc(data) {
  325. if (!data) {
  326. return ''
  327. }
  328. const { area, address } = data
  329. return `${area.join('')}${address}`
  330. },
  331. getHospitalDesc() {
  332. },
  333. fixedZero(num) {
  334. return `${num < 10 ? '0' + num : num}`
  335. },
  336. setTimeColumns(date) {
  337. let currentTime = this.$dayjs()
  338. let startHour = 8
  339. let endHour = 22
  340. if (date && this.$dayjs(date).isSame(currentTime, 'day')) {
  341. let currentHour = currentTime.hour()
  342. console.log('currentHour', currentHour)
  343. console.log('startHour', startHour)
  344. console.log('endHour', endHour)
  345. console.log('date', this.$dayjs(date))
  346. console.log('currentTime', currentTime)
  347. console.log('isToday', this.$dayjs(date).isSame(currentTime, 'day'))
  348. if (currentHour > endHour) {
  349. this.timeColumns = []
  350. return
  351. }
  352. if (currentHour > startHour) {
  353. // todo: check
  354. startHour = currentHour
  355. }
  356. if (startHour == endHour) {
  357. this.timeColumns = []
  358. return
  359. }
  360. }
  361. this.hours = new Array(endHour - startHour + 1).fill(startHour).map((val, idx) => val + idx)
  362. let startCols = this.hours.slice(0, this.hours.length - 1).map(hour => `${this.fixedZero(hour)}:00`)
  363. let endCols = this.hours.slice(1).map(hour => `${this.fixedZero(hour)}:00`)
  364. this.timeColumns = [startCols, endCols]
  365. this.$refs.timeRangePicker.setColumnValues(0, startCols)
  366. this.$refs.timeRangePicker.setColumnValues(1, endCols)
  367. },
  368. openTimePicker() {
  369. this.$refs.timeRangePicker.open();
  370. },
  371. onTimeRangeColChange(e) {
  372. console.log('onTimeRangeColChange', e)
  373. if (e.columnIndex == 0) {
  374. let startIdx = e.indexs[0]
  375. let endCols = startIdx == this.hours.length - 1 ? [] : this.hours.slice(startIdx + 1).map(hour => `${this.fixedZero(hour)}:00`)
  376. console.log('endCols', endCols)
  377. this.timeColumns[1] = endCols
  378. this.$refs.timeRangePicker.setColumnValues(1, endCols)
  379. }
  380. },
  381. onTimeRangeChange(e) {
  382. console.log('onTimeRangeChange', e)
  383. this.form.timeRange = e.value
  384. const [startTime, endTime] = this.form.timeRange
  385. let startIdx = startTime ? this.timeColumns[0].findIndex(item => item === startTime) : 0
  386. let endIdx = endTime ? this.timeColumns[1].findIndex(item => item === endTime) : 0
  387. console.log('setIndexs', [startIdx, endIdx])
  388. this.$refs.timeRangePicker.setIndexs([startIdx, endIdx], true);
  389. },
  390. openDatePicker() {
  391. this.$refs.datetimePicker.open();
  392. },
  393. onDateChange(e) {
  394. const date = e.value
  395. this.setTimeColumns(date)
  396. const [startTime] = this.form.timeRange || []
  397. let dateStr = this.$dayjs(date).format('YYYY-MM-DD')
  398. if (startTime && this.$dayjs(`${dateStr} ${startTime}`).isBefore(this.$dayjs())) {
  399. this.form.timeRange = null
  400. this.$refs.datetimePicker.setIndexs([0, 0]);
  401. }
  402. },
  403. openHospitalPicker() {
  404. this.$refs.hospitalPicker.open();
  405. },
  406. onHospitalChange(e) {
  407. console.log('onHospitalChange', e)
  408. this.form.hospital = e.value[0].id
  409. console.log('form.hospital', this.form.hospital)
  410. },
  411. getHospitalDesc(id) {
  412. console.log('getHospitalDesc', id)
  413. return this.hospitalOptions.find(item => item.id === id)?.label
  414. },
  415. fetchCheckUpPackageInfo() {
  416. // todo: fetch
  417. this.info = {
  418. phone: '0745-5982433',
  419. }
  420. },
  421. fetchCheckUpBookInfo() {
  422. // todo: fetch
  423. const detail = {
  424. id: '003',
  425. url: '',
  426. title: '孕产妇体检套餐',
  427. userName: '周小艺',
  428. phone: '15558661691',
  429. amount: 688,
  430. area: ['海南省', '海口市', '秀英区'],
  431. address: '秀英街道5单元183室',
  432. hospital: '001',
  433. createTime: '2025-04-28 08:14',
  434. appointmentDate: '2025-04-28',
  435. appointmentTime: ['08:00', '09:00'],
  436. type: 2,
  437. status: 2,
  438. }
  439. const {
  440. userName,
  441. phone,
  442. area,
  443. address,
  444. appointmentDate,
  445. appointmentTime,
  446. hospital,
  447. } = detail
  448. this.form = {
  449. addressData: {
  450. id: '001',
  451. name: userName,
  452. phone,
  453. area,
  454. address,
  455. },
  456. date: appointmentDate,
  457. timeRange: appointmentTime,
  458. hospital,
  459. name: userName,
  460. phone,
  461. }
  462. },
  463. jumpToSelectAddress() {
  464. this.$utils.navigateTo('/pages_order/address/addressList')
  465. },
  466. async onSubmit() {
  467. try {
  468. const res = await this.$refs.form.validate()
  469. console.log('onSubmit res', res)
  470. // todo
  471. setTimeout(() => {
  472. // todo: check
  473. // this.$utils.navigateBack()
  474. let id = '001'
  475. this.$utils.redirectTo(`/pages_order/checkup/checkupBook/detail?id=${id}`)
  476. }, 800)
  477. } catch (err) {
  478. console.log('onSubmit err', err)
  479. }
  480. },
  481. },
  482. }
  483. </script>
  484. <style scoped lang="scss">
  485. @import './style.scss';
  486. </style>