Showing
4 changed files
with
319 additions
and
2 deletions
| ... | @@ -24,6 +24,7 @@ declare module '@vue/runtime-core' { | ... | @@ -24,6 +24,7 @@ declare module '@vue/runtime-core' { |
| 24 | GroupField: typeof import('./src/components/GroupField/index.vue')['default'] | 24 | GroupField: typeof import('./src/components/GroupField/index.vue')['default'] |
| 25 | IdentityField: typeof import('./src/components/IdentityField/index.vue')['default'] | 25 | IdentityField: typeof import('./src/components/IdentityField/index.vue')['default'] |
| 26 | ImageUploaderField: typeof import('./src/components/ImageUploaderField/index.vue')['default'] | 26 | ImageUploaderField: typeof import('./src/components/ImageUploaderField/index.vue')['default'] |
| 27 | + LoginBox: typeof import('./src/components/LoginBox/index.vue')['default'] | ||
| 27 | MarqueeField: typeof import('./src/components/MarqueeField/index.vue')['default'] | 28 | MarqueeField: typeof import('./src/components/MarqueeField/index.vue')['default'] |
| 28 | MultiRuleField: typeof import('./src/components/MultiRuleField/index.vue')['default'] | 29 | MultiRuleField: typeof import('./src/components/MultiRuleField/index.vue')['default'] |
| 29 | MyComponent: typeof import('./src/components/AppointmentField/MyComponent.vue')['default'] | 30 | MyComponent: typeof import('./src/components/AppointmentField/MyComponent.vue')['default'] | ... | ... |
src/components/LoginBox/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="login-section"> | ||
| 3 | + <van-config-provider :theme-vars="themeVars"> | ||
| 4 | + <van-form ref="form" @submit="onSubmit"> | ||
| 5 | + <van-cell-group inset style="border: 1px solid #EAEAEA;"> | ||
| 6 | + <van-field v-if="use_widget" v-model="phone" name="phone" label="手机号" placeholder="手机号" readonly clickable | ||
| 7 | + :rules="[{ validator, message: '请输入正确手机号' }]" | ||
| 8 | + @touchstart.stop="showKeyboard" /> | ||
| 9 | + <van-field v-else v-model="phone" name="phone" label="手机号" placeholder="手机号" | ||
| 10 | + :rules="[{ validator, message: '请输入正确手机号' }]" /> | ||
| 11 | + <van-field v-model="code" center clearable name="code" type="digit" label="短信验证码" placeholder="请输入短信验证码" | ||
| 12 | + :formatter="formatter" :rules="[{ required: true, message: '请填写验证码' }]"> | ||
| 13 | + <template #button> | ||
| 14 | + <van-button @click="sendCode" v-if="countDown.current.value.total === limit" size="small" type="primary" | ||
| 15 | + :disabled="disabled"> | ||
| 16 | + <span>发送验证码</span> | ||
| 17 | + </van-button> | ||
| 18 | + <van-button v-else size="small" type="primary" :disabled="disabled"> | ||
| 19 | + <span>{{ countDown.current.value.seconds }} 秒重新发送</span> | ||
| 20 | + </van-button> | ||
| 21 | + </template> | ||
| 22 | + </van-field> | ||
| 23 | + </van-cell-group> | ||
| 24 | + </van-form> | ||
| 25 | + </van-config-provider> | ||
| 26 | + </div> | ||
| 27 | + | ||
| 28 | + <van-number-keyboard v-model="phone" :show="keyboard_show" :maxlength="11" @blur="onBlur" /> | ||
| 29 | +</template> | ||
| 30 | + | ||
| 31 | +<script setup> | ||
| 32 | + | ||
| 33 | +import { ref, onMounted } from 'vue' | ||
| 34 | +import { useRoute, useRouter } from 'vue-router' | ||
| 35 | +import { useCountDown } from '@vant/use'; | ||
| 36 | +import { wxInfo } from '@/utils/tools'; | ||
| 37 | +import { styleColor } from '@/constant.js'; | ||
| 38 | + | ||
| 39 | +import { Cookies, $, _, axios, storeToRefs, mainStore, Toast, useTitle } from '@/utils/generatePackage.js' | ||
| 40 | +//import { } from '@/utils/generateModules.js' | ||
| 41 | +//import { } from '@/utils/generateIcons.js' | ||
| 42 | +//import { } from '@/composables' | ||
| 43 | +const $route = useRoute(); | ||
| 44 | +const $router = useRouter(); | ||
| 45 | + | ||
| 46 | +const emit = defineEmits(['on-submit']) | ||
| 47 | + | ||
| 48 | +const form = ref(null); | ||
| 49 | + | ||
| 50 | +const submit = () => { | ||
| 51 | + let valid = form.value.validate(); | ||
| 52 | + valid | ||
| 53 | + .then(() => { | ||
| 54 | + form.value.submit(); | ||
| 55 | + }) | ||
| 56 | + .catch(error => { | ||
| 57 | + console.error(error); | ||
| 58 | + Toast({ | ||
| 59 | + message: '请检查后再次提交', | ||
| 60 | + icon: 'cross', | ||
| 61 | + }); | ||
| 62 | + }) | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +defineExpose({ | ||
| 66 | + submit | ||
| 67 | +}) | ||
| 68 | + | ||
| 69 | +const themeVars = { | ||
| 70 | + buttonPrimaryBackground: styleColor.baseColor, | ||
| 71 | + buttonPrimaryBorderColor: styleColor.baseColor, | ||
| 72 | + buttonPrimaryColor: styleColor.baseFontColor, | ||
| 73 | + CellVerticalPadding: '14px' | ||
| 74 | +}; | ||
| 75 | + | ||
| 76 | +const onSubmit = () => { | ||
| 77 | + emit('on-submit', { | ||
| 78 | + phone: phone.value, | ||
| 79 | + code: code.value, | ||
| 80 | + }) | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +// 判断是否显示控件 | ||
| 84 | +let use_widget = ref(true); | ||
| 85 | +/** | ||
| 86 | + * 手机号码校验 | ||
| 87 | + * 函数返回 true 表示校验通过,false 表示不通过 | ||
| 88 | + * @param {*} val | ||
| 89 | + */ | ||
| 90 | +const validator = (val) => { | ||
| 91 | + let flag = false; | ||
| 92 | + // 简单判断手机号位数 | ||
| 93 | + if (/1\d{10}/.test(val) && phone.value.length === 11) { | ||
| 94 | + disabled.value = false; | ||
| 95 | + flag = true; | ||
| 96 | + } else { | ||
| 97 | + disabled.value = true; | ||
| 98 | + flag = false; | ||
| 99 | + } | ||
| 100 | + return flag | ||
| 101 | +}; | ||
| 102 | + | ||
| 103 | + | ||
| 104 | +const phone = ref(''); | ||
| 105 | +const code = ref(''); | ||
| 106 | +// TAG: 开发环境测试数据 | ||
| 107 | +if (import.meta.env.DEV) { | ||
| 108 | + phone.value = import.meta.env.VITE_ID | ||
| 109 | + code.value = import.meta.env.VITE_PIN | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +onMounted(() => { | ||
| 113 | + /** | ||
| 114 | + * 判断微信环境看是否弹出控件框 | ||
| 115 | + * 桌面微信直接输入 | ||
| 116 | + * 其他环境弹出输入框 | ||
| 117 | + */ | ||
| 118 | + if (wxInfo().isiOS || wxInfo().isAndroid) { | ||
| 119 | + use_widget.value = true; | ||
| 120 | + } else { | ||
| 121 | + use_widget.value = false; | ||
| 122 | + } | ||
| 123 | +}) | ||
| 124 | + | ||
| 125 | +// 手机号输入控件控制 | ||
| 126 | +const keyboard_show = ref(false); | ||
| 127 | +const showKeyboard = () => { // 弹出数字弹框 | ||
| 128 | + keyboard_show.value = true; | ||
| 129 | +}; | ||
| 130 | +const onBlur = () => { // 数字键盘失焦回调 | ||
| 131 | + keyboard_show.value = false; | ||
| 132 | + if (phone.value.length === 11) { | ||
| 133 | + disabled.value = false; | ||
| 134 | + } | ||
| 135 | +}; | ||
| 136 | + | ||
| 137 | +// 设置发送短信倒计时 | ||
| 138 | +// TAG: vant 自带倒计时函数 | ||
| 139 | +const limit = ref(60000); // 配置倒计时秒数 | ||
| 140 | +const countDown = useCountDown({ | ||
| 141 | + // 倒计时 24 小时 | ||
| 142 | + time: limit.value, | ||
| 143 | + onFinish: () => { | ||
| 144 | + countDown.reset(); | ||
| 145 | + } | ||
| 146 | +}); | ||
| 147 | + | ||
| 148 | +const sendCode = () => { // 发送验证码 | ||
| 149 | + countDown.start(); | ||
| 150 | + axios.post('/srv/?a=bind_phone&t=get_code', { | ||
| 151 | + phone: phone.value | ||
| 152 | + }) | ||
| 153 | + .then(res => { | ||
| 154 | + if (res.data.code === 1) { | ||
| 155 | + Toast.success('发送成功'); | ||
| 156 | + } else { | ||
| 157 | + console.warn(res.data); | ||
| 158 | + if (!res.data.show) return false; | ||
| 159 | + Toast({ | ||
| 160 | + message: res.data.msg, | ||
| 161 | + icon: 'close', | ||
| 162 | + }); | ||
| 163 | + } | ||
| 164 | + }) | ||
| 165 | + .catch(err => { | ||
| 166 | + console.error(err); | ||
| 167 | + }) | ||
| 168 | +}; | ||
| 169 | + | ||
| 170 | +const disabled = ref(true); | ||
| 171 | +// 过滤输入的数字 只能四位 | ||
| 172 | +const formatter = (value) => value.substring(0, 4); | ||
| 173 | + | ||
| 174 | +</script> | ||
| 175 | + | ||
| 176 | +<style lang="less" scoped> | ||
| 177 | +</style> |
src/components/LoginBox/test.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <login-box ref="form" @on-submit="onSubmit"></login-box> | ||
| 3 | + <div class="btn" @click="submit"> | ||
| 4 | + 登 录 | ||
| 5 | + </div> | ||
| 6 | +</template> | ||
| 7 | + | ||
| 8 | +<script setup> | ||
| 9 | +import LoginBox from '@/components/LoginBox' | ||
| 10 | +import { onMounted, ref } from 'vue'; | ||
| 11 | +import { useRoute, useRouter } from 'vue-router'; | ||
| 12 | + | ||
| 13 | +import { | ||
| 14 | + Cookies, | ||
| 15 | + $, | ||
| 16 | + _, | ||
| 17 | + axios, | ||
| 18 | + storeToRefs, | ||
| 19 | + mainStore, | ||
| 20 | + Toast, | ||
| 21 | + useTitle, | ||
| 22 | +} from '@/utils/generatePackage.js'; | ||
| 23 | +//import { } from '@/utils/generateModules.js' | ||
| 24 | +//import { } from '@/utils/generateIcons.js' | ||
| 25 | +//import { } from '@/composables' | ||
| 26 | +const $route = useRoute(); | ||
| 27 | +const $router = useRouter(); | ||
| 28 | +useTitle($route.meta.title); | ||
| 29 | + | ||
| 30 | +const form = ref(null); | ||
| 31 | + | ||
| 32 | +const submit = () => { | ||
| 33 | + form.value.submit(); | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +const onSubmit = (values) => { | ||
| 37 | + axios.post('/srv/?a=b_login', { | ||
| 38 | + phone: values.phone, | ||
| 39 | + pin: values.code, | ||
| 40 | + }) | ||
| 41 | + .then(res => { | ||
| 42 | + if (res.data.code === 1) { | ||
| 43 | + $router.push({ | ||
| 44 | + path: '/business/index' | ||
| 45 | + }); | ||
| 46 | + } else { | ||
| 47 | + console.warn(res.data); | ||
| 48 | + Toast({ | ||
| 49 | + message: res.data.msg, | ||
| 50 | + icon: 'close', | ||
| 51 | + }); | ||
| 52 | + } | ||
| 53 | + }) | ||
| 54 | + .catch(err => { | ||
| 55 | + console.error(err); | ||
| 56 | + }) | ||
| 57 | +}; | ||
| 58 | +</script> | ||
| 59 | + | ||
| 60 | +<style | ||
| 61 | + lang="less" | ||
| 62 | + scoped> | ||
| 63 | +</style> |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-07-18 10:22:22 | 2 | * @Date: 2022-07-18 10:22:22 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2024-06-24 16:30:22 | 4 | + * @LastEditTime: 2024-06-26 10:29:41 |
| 5 | * @FilePath: /data-table/src/views/index.vue | 5 | * @FilePath: /data-table/src/views/index.vue |
| 6 | * @Description: 首页 | 6 | * @Description: 首页 |
| 7 | --> | 7 | --> |
| ... | @@ -113,6 +113,20 @@ | ... | @@ -113,6 +113,20 @@ |
| 113 | close-on-click-action | 113 | close-on-click-action |
| 114 | @cancel="onApprovalCancel" | 114 | @cancel="onApprovalCancel" |
| 115 | /> | 115 | /> |
| 116 | + | ||
| 117 | + <van-overlay :show="bind_tel_show" z-index="100"> | ||
| 118 | + <div class="bind-tel-wrapper"> | ||
| 119 | + <div class="block"> | ||
| 120 | + <div style="text-align: center; padding: 1rem; padding-top: 0; font-weight: bold;">手机号绑定</div> | ||
| 121 | + <login-box ref="bindForm" @on-submit="onBindSubmit"></login-box> | ||
| 122 | + <div style="padding: 1rem; padding-bottom: 0;"> | ||
| 123 | + <van-button round block type="primary" :color="styleColor.baseColor" @click="bindSubmit"> | ||
| 124 | + 绑 定 | ||
| 125 | + </van-button> | ||
| 126 | + </div> | ||
| 127 | + </div> | ||
| 128 | + </div> | ||
| 129 | + </van-overlay> | ||
| 116 | </template> | 130 | </template> |
| 117 | 131 | ||
| 118 | <script setup> | 132 | <script setup> |
| ... | @@ -131,11 +145,12 @@ import { | ... | @@ -131,11 +145,12 @@ import { |
| 131 | import { useRoute } from "vue-router"; | 145 | import { useRoute } from "vue-router"; |
| 132 | import { queryFormAPI, postVerifyPasswordAPI } from "@/api/form.js"; | 146 | import { queryFormAPI, postVerifyPasswordAPI } from "@/api/form.js"; |
| 133 | import { addFormDataAPI, queryFormDataAPI, modiFormDataAPI } from "@/api/data.js"; | 147 | import { addFormDataAPI, queryFormDataAPI, modiFormDataAPI } from "@/api/data.js"; |
| 134 | -import { showSuccessToast, showFailToast } from "vant"; | 148 | +import { showSuccessToast, showFailToast, showConfirmDialog } from "vant"; |
| 135 | import { wxInfo, getUrlParams } from "@/utils/tools"; | 149 | import { wxInfo, getUrlParams } from "@/utils/tools"; |
| 136 | import { styleColor } from "@/constant.js"; | 150 | import { styleColor } from "@/constant.js"; |
| 137 | import { sharePage } from '@/composables/useShare.js' | 151 | import { sharePage } from '@/composables/useShare.js' |
| 138 | import wx from 'weixin-js-sdk' | 152 | import wx from 'weixin-js-sdk' |
| 153 | +import LoginBox from '@/components/LoginBox/index.vue'; | ||
| 139 | 154 | ||
| 140 | // 获取表单设置 | 155 | // 获取表单设置 |
| 141 | const store = mainStore(); | 156 | const store = mainStore(); |
| ... | @@ -259,6 +274,53 @@ const onApprovalCancel = () => { | ... | @@ -259,6 +274,53 @@ const onApprovalCancel = () => { |
| 259 | 274 | ||
| 260 | } | 275 | } |
| 261 | 276 | ||
| 277 | +// TODO: 等待调试发送短信接口 | ||
| 278 | +const bind_tel_show = ref(false); | ||
| 279 | +// setTimeout(() => { | ||
| 280 | +// showConfirmDialog({ | ||
| 281 | +// title: '温馨提示', | ||
| 282 | +// message: | ||
| 283 | +// '您还没有绑定手机号,是否去绑定?', | ||
| 284 | +// confirmButtonColor: styleColor.baseColor | ||
| 285 | +// }) | ||
| 286 | +// .then(() => { | ||
| 287 | +// // on confirm | ||
| 288 | +// bind_tel_show.value = true; | ||
| 289 | +// }) | ||
| 290 | +// .catch(() => { | ||
| 291 | +// // on cancel | ||
| 292 | +// }); | ||
| 293 | +// }, 2000); | ||
| 294 | + | ||
| 295 | +const bindForm = ref(null); | ||
| 296 | + | ||
| 297 | +const bindSubmit = () => { | ||
| 298 | + bindForm.value.submit(); | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +const onBindSubmit = (values) => { | ||
| 302 | + axios.post('/srv/?a=b_login', { | ||
| 303 | + phone: values.phone, | ||
| 304 | + pin: values.code, | ||
| 305 | + }) | ||
| 306 | + .then(res => { | ||
| 307 | + if (res.data.code === 1) { | ||
| 308 | + $router.push({ | ||
| 309 | + path: '/business/index' | ||
| 310 | + }); | ||
| 311 | + } else { | ||
| 312 | + console.warn(res.data); | ||
| 313 | + Toast({ | ||
| 314 | + message: res.data.msg, | ||
| 315 | + icon: 'close', | ||
| 316 | + }); | ||
| 317 | + } | ||
| 318 | + }) | ||
| 319 | + .catch(err => { | ||
| 320 | + console.error(err); | ||
| 321 | + }) | ||
| 322 | +}; | ||
| 323 | + | ||
| 262 | onMounted(async () => { | 324 | onMounted(async () => { |
| 263 | // TAG: 全局背景色 | 325 | // TAG: 全局背景色 |
| 264 | document | 326 | document |
| ... | @@ -1024,6 +1086,20 @@ const onSubmit = async (values) => { | ... | @@ -1024,6 +1086,20 @@ const onSubmit = async (values) => { |
| 1024 | } | 1086 | } |
| 1025 | } | 1087 | } |
| 1026 | 1088 | ||
| 1089 | +.bind-tel-wrapper { | ||
| 1090 | + display: flex; | ||
| 1091 | + align-items: center; | ||
| 1092 | + justify-content: center; | ||
| 1093 | + height: 100%; | ||
| 1094 | + | ||
| 1095 | + .block { | ||
| 1096 | + width: 80vw; | ||
| 1097 | + background-color: #fff; | ||
| 1098 | + padding: 1rem; | ||
| 1099 | + border-radius: 5px; | ||
| 1100 | + } | ||
| 1101 | +} | ||
| 1102 | + | ||
| 1027 | .PHeader-Text { | 1103 | .PHeader-Text { |
| 1028 | padding: 1rem; | 1104 | padding: 1rem; |
| 1029 | font-weight: bold; | 1105 | font-weight: bold; | ... | ... |
-
Please register or login to post a comment