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

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