index.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <template>
  2. <hc-dialog v-model="showModal" ui="hc-sms-auth-dialog" widths="30rem" :padding="false" :footer="false" :is-close="false" @close="cancelClick">
  3. <hc-tab-card :tabs="tabsData" :tab-key="tabsKey" is-action-btn contents @change="tabsChange">
  4. <div v-if="tabsKey === '1'" class="relative p-[14px]">
  5. <el-form ref="reportFormRef" :model="reportModel" :rules="reportRules" label-width="auto">
  6. <el-form-item label="手机号码">
  7. <el-input v-model="phoneVal" disabled placeholder="手机号码" />
  8. </el-form-item>
  9. <el-form-item class="hc-input-button-group" label="验证码" prop="code">
  10. <el-input v-model="reportModel.code" placeholder="请输入验证码" />
  11. <el-button :disabled="isDisabled" type="primary" @click="getCodeClick">
  12. {{ isDisabled ? `倒计时${currentTime}s` : '获取验证码' }}
  13. </el-button>
  14. </el-form-item>
  15. </el-form>
  16. </div>
  17. <div v-if="tabsKey === '2'" class="relative p-[14px]">
  18. <div class="relative mb-24px text-center">
  19. <el-button v-if="isUKey" type="warning" size="large" @click="linkUkeyClick">已连接UKey</el-button>
  20. <el-button v-else type="success" size="large" @click="linkUkeyClick">点击连接UKey</el-button>
  21. </div>
  22. <div class="mb-10px text-red-6">操作步骤:</div>
  23. <div class="mb-10px">第一步、插入UKey在电脑上(一定要先插入密钥UKey),点击上方按钮连接配对</div>
  24. <div class="mb-10px">第二步、选择【Utap】,点击【连接】,当页面显示【已配对】</div>
  25. <div class="relative">
  26. <img :src="HcImgUKey" class="w-full" alt="">
  27. </div>
  28. </div>
  29. <template #action>
  30. <el-button @click="cancelClick">
  31. <hc-icon name="close" />
  32. <span>取消</span>
  33. </el-button>
  34. <el-button :loading="isLoading" type="primary" @click="confirmClick">
  35. <hc-icon name="check" />
  36. <span>确认</span>
  37. </el-button>
  38. </template>
  39. </hc-tab-card>
  40. </hc-dialog>
  41. </template>
  42. <script setup>
  43. import { ref, watch } from 'vue'
  44. import { useAppStore } from '~src/store'
  45. import { formValidate } from 'js-fast-way'
  46. import { saveSmsTimeout, sendNotice } from '~api/other'
  47. import config from '~src/config/index'
  48. import HcImgUKey from '~src/assets/view/ukey.png'
  49. //参数
  50. const props = defineProps({
  51. show: {
  52. type: Boolean,
  53. default: false,
  54. },
  55. tabKey: {
  56. type: String,
  57. default: '1',
  58. },
  59. })
  60. //事件
  61. const emit = defineEmits(['cancel', 'confirm'])
  62. //变量
  63. const userStore = useAppStore()
  64. const userInfo = ref(userStore.getUserInfo)
  65. const projectId = ref(userStore.getProjectId)
  66. const phoneVal = ref(config.smsPhone + '' || userInfo.value.phone + '')
  67. const showModal = ref(props.show)
  68. //Ukey是否存在
  69. const isUKey = ref(userStore.getIsUKey)
  70. watch(() => userStore.getIsUKey, (ukey) => {
  71. isUKey.value = ukey
  72. }, { immediate: true, deep: true })
  73. //监听
  74. watch(() => [props.show, userStore.getUserInfo], ([show, user]) => {
  75. userInfo.value = user
  76. showModal.value = show
  77. if (show) setDataApi()
  78. })
  79. //处理数据
  80. const setDataApi = () => {
  81. tabsKey.value = props.tabKey
  82. }
  83. //tab
  84. const tabsKey = ref('1')
  85. const tabsData = ref([
  86. { key: '1', name: '个人认证' },
  87. { key: '2', name: '公章认证' },
  88. ])
  89. const tabsChange = ({ key }) => {
  90. tabsKey.value = key
  91. }
  92. //返回的验证码
  93. const resCode = ref('')
  94. //表单
  95. const reportFormRef = ref(null)
  96. const reportModel = ref({ code: null })
  97. const reportRules = ref({
  98. code: {
  99. required: true,
  100. validator: (rule, value, callback) => {
  101. const code = resCode.value ?? ''
  102. if (!code) {
  103. callback(new Error('请先获取验证码'))
  104. } else if (!value) {
  105. callback(new Error('请输入验证码'))
  106. } else if (code !== value) {
  107. callback(new Error('验证码错误'))
  108. } else {
  109. callback()
  110. }
  111. },
  112. trigger: 'blur',
  113. },
  114. })
  115. //短信验证码
  116. const isDisabled = ref(false) //是否开启倒计时
  117. const totalTime = 60 //总时间,单位秒
  118. const recordingTime = ref(0) //记录时间变量
  119. const currentTime = ref(0) //显示时间变量
  120. //获取短信验证码
  121. const getCodeClick = async () => {
  122. const { error, code, msg } = await sendNotice({
  123. phone: phoneVal.value,
  124. }, false)
  125. //处理数据
  126. if (!error && code === 200 && msg) {
  127. resCode.value = msg
  128. //把显示时间设为总时间
  129. currentTime.value = totalTime
  130. //开始倒计时
  131. isDisabled.value = true
  132. //执行倒计时
  133. checkingTime()
  134. window?.$message?.success('发送成功')
  135. } else {
  136. resCode.value = ''
  137. window?.$message?.error(msg || '请求异常')
  138. }
  139. }
  140. //倒计时
  141. const checkingTime = () => {
  142. //判断是否开启
  143. if (isDisabled.value) {
  144. //判断显示时间是否已到0,判断记录时间是否已到总时间
  145. if (currentTime.value > 0 && recordingTime.value <= totalTime) {
  146. //记录时间增加 1
  147. recordingTime.value++
  148. //显示时间,用总时间 - 记录时间
  149. currentTime.value = totalTime - recordingTime.value
  150. //1秒钟后,再次执行本方法
  151. setTimeout(() => {
  152. checkingTime()
  153. }, 1000)
  154. } else {
  155. //时间已完成,还原相关变量
  156. isDisabled.value = false //关闭倒计时
  157. recordingTime.value = 0 //记录时间为0
  158. currentTime.value = totalTime //显示时间为总时间
  159. }
  160. } else {
  161. //倒计时未开启,初始化默认变量
  162. isDisabled.value = false
  163. recordingTime.value = 0
  164. currentTime.value = totalTime
  165. }
  166. }
  167. //取消
  168. const cancelClick = () => {
  169. emit('cancel')
  170. }
  171. //确认
  172. const isLoading = ref(false)
  173. const confirmClick = async () => {
  174. if (tabsKey.value === '1') {
  175. const validate = await formValidate(reportFormRef.value)
  176. if (validate) {
  177. await saveSmsTimeoutApi()
  178. emit('confirm', true)
  179. }
  180. } else if (tabsKey.value === '2') {
  181. if (isUKey.value) {
  182. emit('confirm', false)
  183. } else {
  184. window?.$message?.warning('请先连接UKey')
  185. }
  186. }
  187. }
  188. //验证码过期时间
  189. const saveSmsTimeoutApi = async () => {
  190. isLoading.value = true
  191. await saveSmsTimeout({
  192. code: resCode.value,
  193. projectId: projectId.value,
  194. })
  195. isLoading.value = false
  196. }
  197. //连接UKey
  198. const linkUkeyClick = () => {
  199. navigator.usb.requestDevice({
  200. filters: [{ vendorId: 10367 }],
  201. }).then(data => {
  202. if (data.manufacturerName === 'CFIST') {
  203. userStore.setIsUKey(true)
  204. isUKey.value = true
  205. }
  206. }).catch(() => {})
  207. }
  208. </script>
  209. <style lang="scss">
  210. .el-overlay-dialog .el-dialog.hc-new-dialog.hc-sms-auth-dialog {
  211. --el-dialog-padding-primary: 2px;
  212. .el-dialog__header {
  213. display: none;
  214. }
  215. .el-card.hc-card-box.hc-new-card-box .hc-card-action.is-action-btn {
  216. border-top: 1px solid #E9E9E9;
  217. padding-top: 14px;
  218. }
  219. }
  220. </style>