猫妈狗爸伴宠师小程序前端代码
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.

492 lines
9.4 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. <template>
  2. <view class="containers po-r">
  3. <image src="" mode="" class="mainBg"></image>
  4. <view class="po-a content">
  5. <stepProgress :step="1"></stepProgress>
  6. <view class="bg-fff mt22 form ">
  7. <view class="title fw700 size-30 flex-rowl">
  8. 基本信息
  9. </view>
  10. <dForm ref="formRef" :list="state.list" labelWidth="220rpx" :isFooter="false" @input="onFormInput"></dForm>
  11. </view>
  12. <view class="license__view" v-if="form.isHave">
  13. <view class="license">
  14. <up-checkbox-group
  15. v-model="licenseData.selected"
  16. shape="circle"
  17. activeColor="#FFBF60"
  18. labelColor="#000000"
  19. labelSize="26rpx"
  20. >
  21. <view class="license-options">
  22. <up-checkbox
  23. v-for="item in licenseOptions"
  24. :key="`license-${item.id}`"
  25. :label="item.title"
  26. :name="item.id"
  27. >
  28. </up-checkbox>
  29. </view>
  30. </up-checkbox-group>
  31. <view class="tips">
  32. {{ configList.pet_news.paramValueText }}
  33. </view>
  34. <up-upload
  35. :fileList="licenseData.fileList"
  36. @afterRead="afterRead"
  37. @delete="deletePic"
  38. multiple
  39. >
  40. <image src="../static/list/icon-upload.png" style="width: 144rpx;height: 144rpx;"></image>
  41. </up-upload>
  42. </view>
  43. </view>
  44. <view class="bg-fff mt22 form bt120">
  45. <view class="title fw700 size-30 flex-rowl">
  46. 个人宠物类型
  47. </view>
  48. <view class="flex-between wrap mt32">
  49. <view class="type" v-for="item in petTypeOptions" :key="`petType-${item.id}`" @click="onSelectPetType(item.id)">
  50. <image :src="item.imageNo" mode="widthFix"></image>
  51. <template v-if="petType.includes(item.id)">
  52. <image class="active" :src="item.image" mode="widthFix"></image>
  53. <view class="active-icon">
  54. <up-icon name="checkmark-circle" color="#FFBF60" size="32rpx"></up-icon>
  55. </view>
  56. </template>
  57. </view>
  58. </view>
  59. </view>
  60. <view class="footer-btn" @click="toNext">
  61. <view class="btn">
  62. 下一步
  63. </view>
  64. </view>
  65. </view>
  66. <configPopup ref="configPopupRef" />
  67. </view>
  68. </template>
  69. <script setup>
  70. import { ref, reactive, computed } from "vue";
  71. import { useStore } from 'vuex'
  72. import { onLoad } from '@dcloudio/uni-app'
  73. import { ossUpload } from '@/utils/oss-upload/oss/index.js'
  74. import { getLicenseList } from '@/api/examination'
  75. import { insertUser, udpateUser, getUserOne } from '@/api/userTeacher'
  76. import dForm from "@/components/dForm/index.vue"
  77. import stepProgress from '../components/stepProgress.vue';
  78. import configPopup from '@/components/configPopup.vue'
  79. const configPopupRef = ref(null)
  80. const store = useStore()
  81. const configList = computed(() => {
  82. return store.getters.configList
  83. })
  84. const userId = computed(() => {
  85. return store.state.user.userInfo.userId
  86. })
  87. const id = ref(null)
  88. const state = reactive({
  89. list: [{
  90. type: "input",
  91. label: "姓名",
  92. key: "name",
  93. placeholder: "请输入您的真实姓名",
  94. },
  95. {
  96. type: "input",
  97. label: "身份证号",
  98. key: "idCard",
  99. placeholder: "请输入您的真实身份证号",
  100. },
  101. {
  102. type: "radio",
  103. label: "性别",
  104. key: "sex",
  105. options: [{
  106. name: "男",
  107. value: 0,
  108. },
  109. {
  110. name: "女",
  111. value: 1,
  112. }
  113. ]
  114. },
  115. {
  116. type: "input",
  117. label: "年龄",
  118. key: "age",
  119. placeholder: "请输入您的年龄",
  120. },
  121. {
  122. type: "input",
  123. label: "手机号",
  124. key: "phone",
  125. placeholder: "请输入您的手机号",
  126. },
  127. {
  128. type: "input",
  129. label: "养宠经验",
  130. key: "experience",
  131. placeholder: "请输入您的养宠年限",
  132. unit: "年"
  133. },
  134. {
  135. type: "radio",
  136. label: "是否有专业执照",
  137. key: "isHave",
  138. placeholder: "请选择",
  139. options: [{
  140. name: "是",
  141. value: 1,
  142. }, {
  143. name: "没有",
  144. value: 0,
  145. }]
  146. },
  147. ]
  148. })
  149. const formRef = ref()
  150. const form = ref({})
  151. const onFormInput = (e) => {
  152. form.value = e
  153. }
  154. const fetchUserInfo = async () => {
  155. try {
  156. const { userTelephone } = store.state.user.userInfo
  157. const data = await getUserOne(userId.value)
  158. if (data) {
  159. const {
  160. id: _id,
  161. status,
  162. name,
  163. idCard,
  164. sex,
  165. age,
  166. phone,
  167. experience,
  168. isHave,
  169. license,
  170. images,
  171. petType: _petType,
  172. } = data
  173. if ([1,2].includes(status)) { // status: 0-审核中 1-通过 2-不通过
  174. uni.navigateTo({
  175. url: `/otherPages/authentication/examination/trainCompleted/index?status=${status}`
  176. })
  177. return
  178. }
  179. id.value = _id
  180. formRef.value.setData({
  181. name,
  182. idCard,
  183. sex,
  184. age,
  185. phone: phone || userTelephone,
  186. experience,
  187. isHave,
  188. })
  189. licenseData.selected = license?.split?.(';').map(str => parseInt(str)) || []
  190. licenseData.fileList = images?.split?.(';').map(url => ({ url })) || []
  191. petType.value = _petType.split(',').map(n => parseInt(n))
  192. } else {
  193. formRef.value.setData({
  194. phone: userTelephone,
  195. })
  196. }
  197. store.dispatch('fetchPetTypeOptions')
  198. fetchLicenseOptions()
  199. } catch (err) {
  200. console.log('--err', err)
  201. }
  202. }
  203. const licenseData = reactive({
  204. selected: [],
  205. fileList: []
  206. })
  207. const licenseOptions = ref([])
  208. const fetchLicenseOptions = async () => {
  209. try {
  210. licenseOptions.value = await getLicenseList()
  211. } catch (err) {
  212. }
  213. }
  214. const afterRead = (event) => {
  215. event.file.forEach(n => {
  216. ossUpload(n.url)
  217. .then(url => {
  218. licenseData.fileList.push({
  219. url
  220. })
  221. })
  222. })
  223. };
  224. const deletePic = (event) => {
  225. licenseData.fileList.splice(event.index, 1);
  226. };
  227. const petType = ref([])
  228. const petTypeOptions = computed(() => {
  229. return store.getters.petTypeOptions
  230. })
  231. const onSelectPetType = (type) => {
  232. petType.value = petType.value.includes(type) ? petType.value.filter(n => n !== type) : [...petType.value, type]
  233. }
  234. const toNext = async () => {
  235. try {
  236. const {
  237. name,
  238. idCard,
  239. sex,
  240. age,
  241. phone,
  242. experience,
  243. isHave,
  244. } = form.value
  245. const data = {
  246. userId: userId.value,
  247. name,
  248. idCard,
  249. sex,
  250. age,
  251. phone,
  252. experience,
  253. isHave,
  254. petType: petType.value.join(','),
  255. }
  256. if (isHave) {
  257. const { selected, fileList } = licenseData
  258. data.license = selected.join(';')
  259. data.images = fileList.map(item => item.url).join(';')
  260. }
  261. // 效验字段必填
  262. const requiredFields = {
  263. name: '姓名',
  264. idCard: '身份证号',
  265. age: '年龄',
  266. phone: '手机号',
  267. experience: '养宠经验'
  268. }
  269. for (const [field, label] of Object.entries(requiredFields)) {
  270. if (!data[field]) {
  271. uni.showToast({
  272. title: `请填写${label}`,
  273. icon: 'none'
  274. })
  275. return
  276. }
  277. }
  278. // 性别需要单独判断,因为0是有效值(男性)
  279. if (data.sex != 0 && data.sex != 1) {
  280. uni.showToast({
  281. title: '请选择性别',
  282. icon: 'none'
  283. })
  284. return
  285. }
  286. if (petType.value.length === 0) {
  287. uni.showToast({
  288. title: '请选择宠物类型',
  289. icon: 'none'
  290. })
  291. return
  292. }
  293. if (data.isHave === 1) {
  294. if (licenseData.selected.length === 0) {
  295. uni.showToast({
  296. title: '请选择专业执照类型',
  297. icon: 'none'
  298. })
  299. return
  300. }
  301. if (licenseData.fileList.length === 0) {
  302. uni.showToast({
  303. title: '请上传执照图片',
  304. icon: 'none'
  305. })
  306. return
  307. }
  308. }
  309. if (id.value) {
  310. data.id = id.value
  311. await udpateUser(data)
  312. } else {
  313. await insertUser(data)
  314. }
  315. uni.navigateTo({
  316. url: `/otherPages/authentication/examination/start?petType=${petType.value.join(',')}`
  317. })
  318. } catch (err) {
  319. uni.navigateTo({
  320. url: `/otherPages/authentication/examination/start?petType=${petType.value.join(',')}`
  321. })
  322. }
  323. }
  324. onLoad(() => {
  325. fetchUserInfo()
  326. store.dispatch('fetchPetTypeOptions')
  327. })
  328. </script>
  329. <style lang="scss" scoped>
  330. .bt120 {
  331. margin-bottom: 120rpx;
  332. width: 716rpx;
  333. box-sizing: border-box;
  334. }
  335. .footer-btn {
  336. width: 100vw;
  337. height: 144rpx;
  338. background-color: #fff;
  339. display: flex;
  340. justify-content: center;
  341. position: fixed;
  342. bottom: 0;
  343. left: 0;
  344. align-items: center;
  345. .btn {
  346. font-size: 30rpx;
  347. color: #fff;
  348. display: flex;
  349. justify-content: center;
  350. align-items: center;
  351. width: 574rpx;
  352. height: 94rpx;
  353. border-radius: 94rpx;
  354. background-color: #FFBF60;
  355. }
  356. }
  357. .type {
  358. width: 190rpx;
  359. margin-bottom: 74rpx;
  360. position: relative;
  361. image {
  362. width: 100%;
  363. }
  364. .active {
  365. position: absolute;
  366. top: 0;
  367. left: 0;
  368. &-icon {
  369. position: absolute;
  370. top: 14rpx;
  371. right: 18rpx;
  372. }
  373. }
  374. }
  375. .form {
  376. padding: 40rpx 32rpx;
  377. box-sizing: border-box;
  378. width: 716rpx;
  379. }
  380. .title {
  381. &::before {
  382. content: "";
  383. display: block;
  384. width: 9rpx;
  385. height: 33rpx;
  386. background-color: #FFBF60;
  387. margin-right: 7rpx;
  388. }
  389. }
  390. .mb6 {
  391. margin-bottom: 6rpx;
  392. }
  393. .containers {
  394. .mainBg {
  395. width: 100vw;
  396. height: 442rpx;
  397. background-image: linear-gradient(to bottom, #FFBF60, #f5f5f5);
  398. }
  399. .content {
  400. top: 0;
  401. left: 0;
  402. padding: 16rpx;
  403. }
  404. }
  405. .license__view {
  406. width: 716rpx;
  407. padding-bottom: 40rpx;
  408. box-sizing: border-box;
  409. background-color: #FFFFFF;
  410. .license {
  411. width: 100%;
  412. padding: 13rpx 16rpx;
  413. box-sizing: border-box;
  414. background-color: #FFFCF1;
  415. &-options {
  416. display: grid;
  417. grid-template-columns: repeat(2, 1fr);
  418. }
  419. }
  420. .tips {
  421. margin: 22rpx 0 24rpx 0;
  422. color: #FFBF60;
  423. font-size: 22rpx;
  424. width: 100%;
  425. word-break: break-all;
  426. }
  427. }
  428. </style>