Appointment.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <template>
  2. <ScreenLayout :show-back-btn="true" back-text="返回登记" back-target="/visitor">
  3. <div class="page-appointment">
  4. <h1 class="page-title">预约核验</h1>
  5. <div class="verify-options">
  6. <!-- 身份证读取 -->
  7. <div class="verify-card" @click="handleIdCard">
  8. <div class="verify-icon">
  9. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  10. <rect x="2" y="4" width="20" height="16" rx="2" />
  11. <line x1="6" y1="8" x2="10" y2="8" />
  12. <line x1="6" y1="12" x2="18" y2="12" />
  13. <line x1="6" y1="16" x2="14" y2="16" />
  14. </svg>
  15. </div>
  16. <h3>身份证读取</h3>
  17. <p>请将身份证放置读卡区</p>
  18. </div>
  19. <!-- 手机号查询 -->
  20. <div class="verify-card" @click="showPhoneInput = true">
  21. <div class="verify-icon">
  22. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  23. <rect x="5" y="2" width="14" height="20" rx="2" ry="2" />
  24. <line x1="12" y1="18" x2="12.01" y2="18" />
  25. </svg>
  26. </div>
  27. <h3>手机号查询</h3>
  28. <p>输入预约手机号查询</p>
  29. </div>
  30. </div>
  31. <!-- 手机号输入弹窗 -->
  32. <div v-if="showPhoneInput" class="phone-modal">
  33. <div class="modal-content">
  34. <h2>请输入预约手机号</h2>
  35. <NumericKeyboard
  36. v-model="phoneNumber"
  37. label="手机号"
  38. placeholder="请输入11位手机号"
  39. :max-length="11"
  40. type="phone"
  41. @confirm="handlePhoneConfirm"
  42. />
  43. <div class="modal-actions">
  44. <button class="btn-cancel" @click="showPhoneInput = false">取消</button>
  45. <button class="btn-confirm" @click="handlePhoneConfirm(phoneNumber)">查询</button>
  46. </div>
  47. </div>
  48. </div>
  49. </div>
  50. </ScreenLayout>
  51. </template>
  52. <script setup>
  53. import { ref } from 'vue'
  54. import { useRouter } from 'vue-router'
  55. import { useScreenStore } from '@/stores/screen'
  56. import { useVisitorStore } from '@/stores/visitor'
  57. import ScreenLayout from '@/layouts/ScreenLayout.vue'
  58. import NumericKeyboard from '@/components/NumericKeyboard.vue'
  59. const router = useRouter()
  60. const screenStore = useScreenStore()
  61. const visitorStore = useVisitorStore()
  62. const showPhoneInput = ref(false)
  63. const phoneNumber = ref('')
  64. const handleIdCard = async () => {
  65. try {
  66. const result = await visitorStore.readIdCard()
  67. screenStore.showAlert({
  68. type: 'success',
  69. message: '身份证读取成功'
  70. })
  71. // 查询预约
  72. if (result.idCardNo) {
  73. try {
  74. await visitorStore.queryAppointment({ idCardNo: result.idCardNo })
  75. router.push('/visitor/appointment-confirm')
  76. } catch {
  77. screenStore.showAlert({
  78. type: 'info',
  79. message: '未查询到预约信息,请选择现场登记'
  80. })
  81. }
  82. }
  83. } catch {
  84. screenStore.showAlert({
  85. type: 'error',
  86. message: '身份证读取失败,请重试或选择其他方式'
  87. })
  88. }
  89. }
  90. const handlePhoneConfirm = async (phone) => {
  91. if (!phone || phone.length !== 11) {
  92. screenStore.showAlert({
  93. type: 'warning',
  94. message: '请输入正确的11位手机号'
  95. })
  96. return
  97. }
  98. try {
  99. await visitorStore.queryAppointment({ mobile: phone })
  100. showPhoneInput.value = false
  101. router.push('/visitor/appointment-confirm')
  102. } catch {
  103. screenStore.showAlert({
  104. type: 'info',
  105. message: '未查询到预约信息'
  106. })
  107. }
  108. }
  109. </script>
  110. <style scoped>
  111. .page-appointment {
  112. width: 100%;
  113. height: 100%;
  114. display: flex;
  115. flex-direction: column;
  116. align-items: center;
  117. padding: 20px 0;
  118. }
  119. .page-title {
  120. font-size: 32px;
  121. font-weight: 600;
  122. color: var(--text-primary);
  123. margin: 0 0 40px;
  124. }
  125. .verify-options {
  126. display: grid;
  127. grid-template-columns: repeat(2, 1fr);
  128. gap: 40px;
  129. max-width: 700px;
  130. width: 100%;
  131. }
  132. .verify-card {
  133. display: flex;
  134. flex-direction: column;
  135. align-items: center;
  136. gap: 16px;
  137. padding: 40px 24px;
  138. background: var(--bg-card);
  139. border-radius: var(--radius-xl);
  140. box-shadow: var(--shadow-md);
  141. cursor: pointer;
  142. transition: all var(--transition-normal);
  143. }
  144. .verify-card:hover {
  145. transform: translateY(-6px);
  146. box-shadow: var(--shadow-xl);
  147. }
  148. .verify-icon {
  149. width: 64px;
  150. height: 64px;
  151. display: flex;
  152. align-items: center;
  153. justify-content: center;
  154. background: var(--primary-soft);
  155. border-radius: 16px;
  156. color: var(--primary);
  157. }
  158. .verify-icon svg {
  159. width: 36px;
  160. height: 36px;
  161. }
  162. .verify-card h3 {
  163. font-size: 24px;
  164. font-weight: 600;
  165. color: var(--text-primary);
  166. margin: 0;
  167. }
  168. .verify-card p {
  169. font-size: 15px;
  170. color: var(--text-muted);
  171. margin: 0;
  172. }
  173. /* 手机号输入弹窗 */
  174. .phone-modal {
  175. position: fixed;
  176. top: 0;
  177. left: 0;
  178. right: 0;
  179. bottom: 0;
  180. background: rgba(0, 0, 0, 0.5);
  181. display: flex;
  182. align-items: center;
  183. justify-content: center;
  184. z-index: 100;
  185. animation: fadeIn 0.2s ease-out;
  186. }
  187. .modal-content {
  188. width: 90%;
  189. max-width: 450px;
  190. padding: 32px;
  191. background: var(--bg-card);
  192. border-radius: var(--radius-xl);
  193. box-shadow: var(--shadow-xl);
  194. }
  195. .modal-content h2 {
  196. font-size: 24px;
  197. font-weight: 600;
  198. color: var(--text-primary);
  199. text-align: center;
  200. margin: 0 0 24px;
  201. }
  202. .modal-actions {
  203. display: grid;
  204. grid-template-columns: 1fr 1fr;
  205. gap: 16px;
  206. margin-top: 24px;
  207. }
  208. .btn-cancel,
  209. .btn-confirm {
  210. height: 56px;
  211. font-size: 18px;
  212. border-radius: var(--radius-lg);
  213. cursor: pointer;
  214. transition: all var(--transition-fast);
  215. }
  216. .btn-cancel {
  217. background: var(--bg-page);
  218. color: var(--text-secondary);
  219. border: 2px solid var(--border-light);
  220. }
  221. .btn-cancel:hover {
  222. border-color: var(--text-muted);
  223. }
  224. .btn-confirm {
  225. background: var(--primary);
  226. color: white;
  227. border: none;
  228. }
  229. .btn-confirm:hover {
  230. background: var(--primary-dark);
  231. }
  232. @keyframes fadeIn {
  233. from {
  234. opacity: 0;
  235. }
  236. to {
  237. opacity: 1;
  238. }
  239. }
  240. </style>