前端-胡立永 10 months ago
parent
commit
d834f6474c
66 changed files with 2075 additions and 4416 deletions
  1. +12
    -0
      api/amount/index.js
  2. +45
    -0
      components/CustomerService/index.vue
  3. +10
    -0
      main.js
  4. +172
    -13
      otherPages/authentication/components/questionCard.vue
  5. +2
    -0
      otherPages/authentication/connectAddress/detail.vue
  6. +2
    -0
      otherPages/authentication/connectAddress/index.vue
  7. +78
    -29
      otherPages/authentication/examination/base.vue
  8. +21
    -2
      otherPages/authentication/examination/baseCompleted.vue
  9. +57
    -27
      otherPages/authentication/examination/end.vue
  10. +0
    -206
      otherPages/authentication/examination/errorDetail - 副本.vue
  11. +59
    -36
      otherPages/authentication/examination/errorDetail.vue
  12. +3
    -0
      otherPages/authentication/examination/start.vue
  13. +82
    -12
      otherPages/authentication/examination/train.vue
  14. +2
    -0
      otherPages/authentication/examination/trainCompleted/index.vue
  15. +0
    -8
      otherPages/authentication/index.vue
  16. +7
    -4
      otherPages/authentication/list/index.vue
  17. +14
    -5
      otherPages/authentication/serve/index.vue
  18. +29
    -3
      otherPages/authentication/serve/popupServeSelect.vue
  19. +2
    -0
      otherPages/binding/share/index.vue
  20. +3
    -0
      otherPages/binding/wallet/index.vue
  21. +79
    -22
      otherPages/binding/withdrawal/index.vue
  22. +0
    -462
      otherPages/myOrdersManage/clock/index - 副本.vue
  23. +2
    -0
      otherPages/myOrdersManage/clock/index.vue
  24. +0
    -66
      otherPages/myOrdersManage/list/index.vue
  25. +0
    -754
      otherPages/myOrdersManage/service/detail - 副本.vue
  26. +2
    -0
      otherPages/myOrdersManage/service/detail.vue
  27. +0
    -80
      otherPages/myOrdersManage/service/index - 副本.vue
  28. +317
    -15
      otherPages/myOrdersManage/transaction/index.vue
  29. +3
    -0
      otherPages/orderTakingManage/detail/index.vue
  30. +2
    -1
      otherPages/orderTakingManage/evaluate/index.vue
  31. +0
    -66
      otherPages/orderTakingManage/list/index.vue
  32. +0
    -9
      otherPages/otherPages/myOrdersManage/bond/detail.vue
  33. +0
    -9
      otherPages/otherPages/myOrdersManage/bond/index.vue
  34. +0
    -9
      otherPages/otherPages/myOrdersManage/detail/index.vue
  35. +0
    -9
      otherPages/otherPages/myOrdersManage/withdrawal/index.vue
  36. +0
    -66
      otherPages/userManage/list/index.vue
  37. +0
    -316
      otherPages/userManage/pet/components/petBaseInfo.vue
  38. +0
    -253
      otherPages/userManage/pet/components/petHealthInfo.vue
  39. +2
    -0
      otherPages/userManage/pet/index.vue
  40. +738
    -23
      otherPages/userManage/pet/petInfo.vue
  41. +0
    -36
      otherPages/userManage/record/index.scss
  42. +0
    -52
      otherPages/userManage/record/index.vue
  43. +2
    -0
      otherPages/userManage/userInfo.vue
  44. +0
    -193
      otherPages/workbenchManage/bindUser/sharing - 副本.vue
  45. +3
    -0
      otherPages/workbenchManage/feedingStaff/index.vue
  46. +0
    -66
      otherPages/workbenchManage/list/index.vue
  47. +10
    -12
      pages.json
  48. +2
    -0
      pages/login/index.vue
  49. +2
    -0
      pages/login/wxUserInfo.vue
  50. +0
    -277
      pages/myOrdersManage/components/petServicePopup - 副本.vue
  51. +0
    -636
      pages/myOrdersManage/components/timelineService - 副本.vue
  52. +3
    -0
      pages/myOrdersManage/index.vue
  53. +3
    -1
      pages/myOrdersManage/index1.vue
  54. +3
    -0
      pages/orderTakingManage/index.vue
  55. +0
    -79
      pages/test/index.vue
  56. +4
    -0
      pages/userManage/index.scss
  57. +25
    -10
      pages/userManage/index.vue
  58. +29
    -0
      pages/userManage/mine/home_agreement.vue
  59. +29
    -0
      pages/userManage/mine/platformManual.vue
  60. +9
    -1
      pages/workbenchManage/index.vue
  61. +1
    -0
      store/modules/system.js
  62. +0
    -256
      uni_modules/uview-plus/components/u-swipe-action-item/index - backup.wxs
  63. +0
    -270
      uni_modules/uview-plus/components/u-swipe-action-item/nvue - backup.js
  64. +103
    -21
      utils/exam.js
  65. +1
    -1
      utils/getUrl.js
  66. +101
    -0
      utils/shareMixin.js

+ 12
- 0
api/amount/index.js View File

@ -47,4 +47,16 @@ export const teacherPayPrice = (data) => {
method: "post",
data
})
}
// 提现成功
export const withdrawalSuccessful = (params) => {
return request({
url: '/applet/amount/withdrawalSuccessful',
headers: {
isToken: true
},
method: "post",
params
})
}

+ 45
- 0
components/CustomerService/index.vue View File

@ -0,0 +1,45 @@
<template>
<button plain class="customer-service-btn" open-type="contact">
<image src="@/static/images/ydd/chat.png" mode="widthFix"></image>
</button>
</template>
<script setup>
// - props
const props = defineProps({
//
right: {
type: String,
default: '20rpx'
},
//
bottom: {
type: String,
default: '400rpx'
},
//
width: {
type: String,
default: '97rpx'
}
})
</script>
<style lang="scss" scoped>
.customer-service-btn {
position: fixed;
right: v-bind(right);
bottom: v-bind(bottom);
z-index: 9999;
border: none;
padding: 0;
width: v-bind(width);
height: auto;
background: transparent;
image {
width: 100%;
height: auto;
}
}
</style>

+ 10
- 0
main.js View File

@ -4,11 +4,15 @@ import store from './store'
// #ifndef VUE3
import Vue from 'vue'
import App from './App'
import shareMixin from '@/utils/shareMixin.js'
Vue.config.productionTip = false
App.mpType = 'app'
// 全局混入分享功能
Vue.mixin(shareMixin)
const app = new Vue({
...App
})
@ -21,11 +25,17 @@ import {
createSSRApp
} from 'vue'
import App from './App.vue'
import CustomerService from '@/components/CustomerService/index.vue'
import shareMixin from '@/utils/shareMixin.js'
export function createApp() {
const app = createSSRApp(App)
app.use(uviewPlus)
app.use(store)
// 全局注册客服按钮组件
app.component('CustomerService', CustomerService)
// 全局混入分享功能
app.mixin(shareMixin)
return {
app
}


+ 172
- 13
otherPages/authentication/components/questionCard.vue View File

@ -2,12 +2,19 @@
<view class="mt32 question__view" :class="[props.mode]">
<view class="size-28 mb20 question">
{{ props.data.title }}
<text class="question-type" v-if="props.data.typeAnswer == 0">
{{ isMultipleChoice ? '(多选题)' : '(单选题)' }}
</text>
<text class="question-type" v-else-if="props.data.typeAnswer == 1">
(填空题)
</text>
<!-- {{ `${props.index + 1}${props.data.title}` }} -->
</view>
<template v-if="props.mode === 'edit'">
<template v-if="props.data.options?.length">
<template v-if="props.data.typeAnswer == 0">
<view class="size-28 option" v-for="(option, oIdx) in props.data.options"
:key="`${props.index}-option-${oIdx}`" :class="[value === option.id ? 'is-selected' : '']"
:key="`${props.index}-option-${oIdx}`"
:class="[isOptionSelected(option.id) ? 'is-selected' : '']"
@click="onChange(option.id)">
{{ option.title }}
</view>
@ -20,25 +27,39 @@
</template>
</template>
<template v-else>
<template v-if="props.data.options?.length">
<template v-if="props.data.typeAnswer == 0">
<view class="size-28 option" v-for="(option, oIdx) in props.data.options"
:key="`${props.index}-option-${oIdx}`" :class="[
props.data.answer === option.id ? 'is-correct' : '',
props.data.value === option.id && props.data.answer !== option.id ? 'is-error' : '',
]">
option.isTrue ? 'is-correct' : '',
isDisplayOptionSelected(option.id) && !option.isTrue ? 'is-error' : '',
isDisplayOptionSelected(option.id) && option.isTrue ? 'is-correct-selected' : ''
]">
{{ option.title }}
<view class="icon icon-correct">
<!-- 正确答案标识 -->
<view class="icon icon-correct" v-if="option.isTrue">
<up-icon name="checkmark" color="#05C160" size="35rpx"></up-icon>
</view>
<view class="icon icon-error">
<!-- 错误选择标识 -->
<view class="icon icon-error" v-if="isDisplayOptionSelected(option.id) && !option.isTrue">
<up-icon name="close" color="#FF2A2A" size="35rpx"></up-icon>
</view>
<!-- 用户选择标识和正确答案标识 -->
<view class="answer-tags" v-if="isDisplayOptionSelected(option.id) || option.isTrue">
<view class="user-choice-tag" v-if="isDisplayOptionSelected(option.id)">
我的选择
</view>
<view class="correct-answer-tag" v-if="option.isTrue">
正确答案
</view>
</view>
</view>
</template>
<template v-else>
<view class="textarea">
<view>{{ props.data.value }}</view>
<view class="highlight">{{ props.data.answer }}</view>
<view class="user-answer-label">我的答案</view>
<view class="user-answer">{{ props.data.value || '未作答' }}</view>
<view class="correct-answer-label" v-if="props.data.answer">正确答案</view>
<view class="highlight" v-if="props.data.answer">{{ props.data.answer }}</view>
</view>
</template>
</template>
@ -74,7 +95,7 @@
}
},
modelValue: {
type: [String, Number],
type: [String, Number, Array],
default: null,
},
mode: {
@ -91,17 +112,92 @@
const emit = defineEmits(['update:modelValue'])
//
const isMultipleChoice = computed(() => {
if (!props.data.options || !props.data.options.length) return false
const correctAnswers = props.data.options.filter(option => option.isTrue === 1 || option.isTrue === true)
return correctAnswers.length > 1
})
const value = computed({
set(val) {
emit('update:modelValue', val)
},
get() {
return props.modelValue
if (isMultipleChoice.value) {
//
return Array.isArray(props.modelValue) ? props.modelValue : (props.modelValue ? [props.modelValue] : [])
} else {
//
return Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue
}
}
})
//
const isOptionSelected = (optionId) => {
if (isMultipleChoice.value) {
return Array.isArray(value.value) && value.value.includes(optionId)
} else {
return value.value === optionId
}
}
//
const isDisplayOptionSelected = (optionId) => {
if (props.mode !== 'display') return false
// 1: props.data.value
if (props.data.value !== undefined && props.data.value !== null) {
if (Array.isArray(props.data.value)) {
return props.data.value.includes(optionId)
} else {
return props.data.value === optionId
}
}
// 2: userAnswerBaseList
if (props.data.userAnswerBaseList && props.data.userAnswerBaseList.length > 0) {
return props.data.userAnswerBaseList.some(answer => answer.answerId === optionId)
}
// 3: modelValue
if (props.modelValue !== undefined && props.modelValue !== null) {
if (Array.isArray(props.modelValue)) {
return props.modelValue.includes(optionId)
} else {
return props.modelValue === optionId
}
}
return false
}
const onChange = (val) => {
value.value = val
if (props.data.typeAnswer == 1) {
//
value.value = val
} else if (props.data.typeAnswer == 0) {
//
if (isMultipleChoice.value) {
//
let currentValue = Array.isArray(value.value) ? [...value.value] : []
const index = currentValue.indexOf(val)
if (index > -1) {
//
currentValue.splice(index, 1)
} else {
//
currentValue.push(val)
}
value.value = currentValue
} else {
//
value.value = val
}
}
// const data = {
// userId: userId.value,
@ -121,6 +217,12 @@
<style lang="scss" scoped>
.question {
color: #000000;
.question-type {
color: #FFBF60;
font-size: 22rpx;
margin-left: 10rpx;
}
}
.option {
@ -182,6 +284,63 @@
display: block;
}
}
&.is-correct-selected {
background-color: rgba($color: #05C160, $alpha: 0.15);
color: #05C160;
border: 2px solid #05C160;
}
}
.answer-tags {
margin-top: 10rpx;
display: flex;
gap: 10rpx;
flex-wrap: wrap;
}
.user-choice-tag {
background-color: #FFBF60;
color: white;
font-size: 20rpx;
padding: 6rpx 12rpx;
border-radius: 12rpx;
display: inline-block;
}
.correct-answer-tag {
background-color: #05C160;
color: white;
font-size: 20rpx;
padding: 6rpx 12rpx;
border-radius: 12rpx;
display: inline-block;
}
}
.textarea {
.user-answer-label, .correct-answer-label {
font-size: 24rpx;
color: #999;
margin-bottom: 10rpx;
}
.user-answer {
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
padding: 10rpx;
background-color: rgba($color: #FFBF60, $alpha: 0.1);
border-radius: 8rpx;
}
.highlight {
color: #05C160;
font-size: 28rpx;
font-weight: bold;
padding: 10rpx;
background-color: rgba($color: #05C160, $alpha: 0.1);
border-radius: 8rpx;
}
}
</style>

+ 2
- 0
otherPages/authentication/connectAddress/detail.vue View File

@ -73,6 +73,8 @@
</view>
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 2
- 0
otherPages/authentication/connectAddress/index.vue View File

@ -86,6 +86,8 @@
</view>
</template>
</Modal>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 78
- 29
otherPages/authentication/examination/base.vue View File

@ -12,10 +12,10 @@
<view class="content bg-fff">
<view>
<view class="label size-22">
选择题
混合题型
</view>
</view>
<view >
<view>
<questionCard
v-for="(item, qIdx) in list"
:key="`question-${qIdx}`"
@ -34,6 +34,8 @@
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>
@ -67,6 +69,7 @@ const initQuestion = async () => {
title: item.title,
value: null,
answerList : item.answerList,
...item,
}))
// todo:
@ -75,8 +78,20 @@ const initQuestion = async () => {
// questions[i].options = options
questions[i].options = questions[i].answerList
if (questions[i].typeAnswer == 0) {
//
questions[i].options = questions[i].answerList
// value
const correctAnswers = questions[i].answerList.filter(option => option.isTrue === 1 || option.isTrue === true)
const isMultipleChoice = correctAnswers.length > 1
// null
questions[i].value = isMultipleChoice ? [] : null
} else if (questions[i].typeAnswer == 1) {
//
questions[i].value = null
}
}
list.value = questions
@ -94,11 +109,25 @@ const answered = computed(() => {
return false
}
// if(typeof item.value == 'string'){
// return item.value.length >= 700
// }
if (item.typeAnswer == 0) {
//
//
if(Array.isArray(item.value)){
return item.value.length > 0
}
//
return item.value !== null && item.value !== undefined
} else if (item.typeAnswer == 1) {
//
if(typeof item.value == 'string'){
return item.value.length >= (item.numberWords || 0)
}
return false
}
return true
return false
}).length
})
@ -109,35 +138,55 @@ const progress = computed(() => {
const toNext = async () => {
const data = []
const answerBase = []
const answerTrains = []
uni.showLoading({
title: '提交中...'
})
// list.value.forEach(n => {
// data.push(addBaseAnswer({
// userId: userId.value,
// questionId: n.id,
// answerId : n.value,
// }))
// })
// await Promise.all(data)
// uni.hideLoading()
list.value.forEach(n => {
data.push({
userId: userId.value,
questionId: n.id,
answerId : n.value,
})
if (n.typeAnswer == 0) {
// -
if (Array.isArray(n.value)) {
//
n.value.forEach(answerId => {
answerBase.push({
userId: userId.value,
questionId: n.id,
answerId: answerId,
})
})
} else {
// 使
answerBase.push({
userId: userId.value,
questionId: n.id,
answerId: n.value,
})
}
} else if (n.typeAnswer == 1) {
// -
answerTrains.push({
userId: userId.value,
questionId: n.id,
answer: n.value,
})
}
})
await addBaseAnswer({
answerBase : data
})
// type
const hasBaseQuestions = list.value.some(item => item.typeAnswer == 0)
const hasTrainQuestions = list.value.some(item => item.typeAnswer == 1)
//
const submitData = {
answerBase: answerBase,
answerTrains: answerTrains,
type: hasBaseQuestions ? 0 : 1 // type01
}
await addBaseAnswer(submitData)
const res = await getAnswerBuildData(userId.value)


+ 21
- 2
otherPages/authentication/examination/baseCompleted.vue View File

@ -28,8 +28,9 @@
</view>
<view class="info-box-content">
<view class="flex-colc info-item"
v-for="item in serviceList"
:key="`service-${item.id}`"
v-for="item in serviceList"
v-if="configList.pet_base_test.paramValueText == 1"
:key="`service-${item.id}`"
>
<view class="video">
<video
@ -42,6 +43,22 @@
</view>
</view>
</view>
<view class="info-box">
<view class="info-box-title">
{{ configList.train_assessment_text.paramValueText }}
</view>
<view style="padding: 20rpx;">
<up-parse :content="configList.train_assessment_text.paramValueArea" containerStyle="{
color: '#707070',
fontSize: '22rpx',
lineHeight: '29rpx',
}"></up-parse>
</view>
</view>
<!-- <view class="info-box">
<view class="info-box-title">
服务培训
@ -81,6 +98,8 @@
</view>
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 57
- 27
otherPages/authentication/examination/end.vue View File

@ -2,7 +2,7 @@
<view>
<view class="containers po-r">
<image src="" mode="" class="mainBg"></image>
<view class="w-100 po-a content">
<view class="po-a content">
<stepProgress :step="3"></stepProgress>
@ -45,8 +45,8 @@
<image class="img" src="@/static/images/ydd/end13.png" mode="widthFix"></image>
<text class="size-30 color-000 fw700">履约保证金缴纳</text>
</view>
<view class="size-22 color-ffb jiao" @click="jumpToBond">
去缴纳
<view class="size-22 color-999 jiao-disabled">
暂未开放
</view>
<!-- todo -->
<!-- <view class="size-22 highlight">
@ -77,8 +77,10 @@
</view>
<view class="info color-777 size-22 flex-rowl">
<text class="size-22 color-777">微信二维码</text>
<image :src="configList.wx_image.paramValueImage" mode="widthFix"
style="width: 150rpx;height: 150rpx;"></image>
<image :src="configList.wx_image.paramValueImage"
@click="previewQRCode"
mode="widthFix"
style="width: 150rpx;height: 150rpx;cursor: pointer;"></image>
</view>
</view>
</view>
@ -160,9 +162,8 @@
</up-modal>
</view>
<button plain class="chat" open-type="contact">
<image src="@/static/images/ydd/chat.png" mode="widthFix"></image>
</button>
<!-- 使用全局客服组件 -->
<CustomerService />
</view>
</template>
@ -246,11 +247,7 @@ import { appletUsersTeacher } from '@/api/examination'
initData()
})
const jumpToBond = () => {
uni.navigateTo({
url: "/otherPages/myOrdersManage/bond/index"
})
}
// jumpToBond
const showToolsInfoModal = ref(false)
const openToolsInfoModal = () => {
@ -260,6 +257,44 @@ import { appletUsersTeacher } from '@/api/examination'
showToolsInfoModal.value = false
}
//
const previewQRCode = () => {
if (configList.value.wx_image && configList.value.wx_image.paramValueImage) {
uni.previewImage({
urls: [configList.value.wx_image.paramValueImage],
current: 0,
longPressActions: {
itemList: ['保存图片'],
success: function(data) {
if (data.tapIndex === 0) {
//
uni.saveImageToPhotosAlbum({
filePath: configList.value.wx_image.paramValueImage,
success: function() {
uni.showToast({
title: '保存成功',
icon: 'success'
})
},
fail: function() {
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
})
}
}
}
})
} else {
uni.showToast({
title: '二维码图片加载失败',
icon: 'none'
})
}
}
const setAddress = (res) => {
//
form.latitude = res.latitude
@ -429,20 +464,7 @@ import { appletUsersTeacher } from '@/api/examination'
</script>
<style lang="scss" scoped>
.chat {
position: fixed;
right: 20rpx;
bottom: 400rpx;
z-index: 9999;
border: none;
padding: 0;
width: 97rpx;
height: auto;
image {
width: 100%;
}
}
//
.line {
width: 3rpx;
@ -459,6 +481,14 @@ import { appletUsersTeacher } from '@/api/examination'
color: #fff;
}
.jiao-disabled {
padding: 5px 20rpx;
border-radius: 30rpx;
background-color: #CCCCCC;
color: #999999;
cursor: not-allowed;
}
.li {
width: 710rpx;


+ 0
- 206
otherPages/authentication/examination/errorDetail - 副本.vue View File

@ -1,206 +0,0 @@
<template>
<!-- <view>伴宠师认证</view> -->
<view class="page">
<view class="box">
<view class="content bg-fff">
<view class="group" v-for="(group, gIdx) in list" :key="`group-${gIdx}`">
<view class="label size-22 level" :style="{borderRadius:'10rpx'}">
{{ group.title }}
</view>
<view >
<questionCard
v-for="(item, qIdx) in group.children"
:key="`${gIdx}-question-${qIdx}`"
:index="qIdx"
:data="item"
:type="item.type"
mode="display"
></questionCard>
</view>
</view>
</view>
</view>
<view class="footer-btn flex-colc">
<view class="size-22 color-777 tips-rest">
<!-- todo -->
剩余考试机会<text class="highlight">{{ restTimes }}</text>
</view>
<view class="btn" @click="onClick">
重新考试
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getQuestionList, getQuestionOptions, answeBaseByQuestionId, answeTrainByQuestionId, retakeExam } from '@/api/examination'
import { useStore } from 'vuex'
import questionCard from '../components/questionCard.vue';
const store = useStore()
const configList = computed(() => {
return store.getters.configList
})
const userInfo = computed(() => {
return store.state.user.userInfo
})
const list = ref([
{
title: '选择题',
children: [],
},
{
title: '主观题',
children: [],
},
])
const initList = async () => {
try {
let groups = [
{ title: '选择题', children: [], },
{ title: '主观题', children: [], },
]
let questions = await getQuestionList()
for (let i = 0; i < questions.length; i++) {
const { id, title, type } = questions[i]
let data = { id, title, type }
if (type === '基本') {
const options = (await getQuestionOptions({ questionId: id })).map(item => ({ id: item.id, title: item.title}))
data.options = options
// todo:
const { answer, answerId } = await answeBaseByQuestionId({ questionId: id })
if (answer.isTrue) {
continue
}
data.answer = answer.id
data.value = answerId
data.isTrue = answer.isTrue
groups[0].children.push(data)
} else {
// todo:
const { answer, remark } = await answeTrainByQuestionId({ questionId: id })
if (!remark) {
continue
}
data.answer = remark
data.value = answer
groups[1].children.push(data)
}
}
list.value = groups.filter(group => group.children.length > 0)
console.log('--list', list.value)
} catch (err) {
console.log('--initList--err', err)
}
}
const restTimes = ref()
onShow(() => {
// todo: fetch
restTimes.value = parseInt(configList.value.pet_work_num.paramValueText || 0)
initList()
})
const onClick = () => {
retakeExam({
userId : userInfo.value.userId
}).then(res => {
uni.reLaunch({
url: "/otherPages/authentication/examination/start"
// todo: check
// url: "/otherPages/authentication/list/index"
})
})
}
</script>
<style lang="scss" scoped>
$bar-height: 163rpx;
.page {
padding-bottom: $bar-height;
}
.box {
// background-image: linear-gradient(to bottom, #ffbf60, #f2f2f2);
padding: 16rpx;
.step {
width: 720rpx;
height: 32rpx;
border-radius: 32rpx;
background-color: #D9D9D9;
.in {
width: 50%;
height: 32rpx;
background-color: #ffbf60;
border-radius: 32rpx;
}
}
}
.content {
border-radius: 20rpx;
padding: 15rpx 20rpx;
.label {
width: 86rpx;
padding: 5rpx 15rpx;
color: #fff;
background-color: #FFBF60;
justify-content: center;
}
}
.group + .group {
margin-top: 68rpx;
}
.level {
display: flex;
}
.footer-btn {
height: $bar-height;
}
.tips {
&-rest {
color: #707070;
font-size: 22rpx;
margin: 9rpx 0 13rpx 0;
.highlight {
color: #FF8DC6;
}
}
}
</style>

+ 59
- 36
otherPages/authentication/examination/errorDetail.vue View File

@ -3,42 +3,30 @@
<view class="page">
<view class="box">
<view class="content bg-fff">
<!-- 选择题部分 -->
<!-- 基本题部分 - 基本考核不通过时显示 -->
<view class="question-section" v-if="basicQuestions.length > 0 && !baseSuccess">
<view class="section-title">选择题</view>
<view class="question__view display" v-for="(question, index) in basicQuestions" :key="index">
<view class="size-28 mb20 question">
{{ `${question.title}` }}
</view>
<view class="size-28 option" v-for="(option, oIdx) in question.answerList"
:key="`${index}-option-${oIdx}`"
:class="[
option.isTrue ? 'is-correct' : '',
isOptionSelected(question, option.id) && !option.isTrue ? 'is-error' : ''
]">
{{ option.title }}
<view class="icon icon-correct" v-if="option.isTrue">
<up-icon name="checkmark" color="#05C160" size="35rpx"></up-icon>
</view>
<view class="icon icon-error" v-if="isOptionSelected(question, option.id) && !option.isTrue">
<up-icon name="close" color="#FF2A2A" size="35rpx"></up-icon>
</view>
</view>
</view>
<view class="section-title">基本题</view>
<questionCard
v-for="(question, index) in basicQuestions"
:key="`question-${index}`"
:index="index"
:data="prepareQuestionData(question)"
mode="display"
type="基本"
/>
</view>
<!-- 培训题部分 -->
<view class="question-section" v-if="trainQuestions.length > 0">
<!-- 培训题部分 - 后台审核不通过时显示 -->
<view class="question-section" v-if="trainQuestions.length > 0 && baseSuccess">
<view class="section-title">培训题</view>
<view class="question__view display" v-for="(question, index) in trainQuestions" :key="index">
<view class="size-28 mb20 question">
{{ `${question.title}` }}
</view>
<view class="textarea">
<view class="answer-content">{{ question.userAppletAnswerTrain?.answer || '未作答' }}</view>
<view class="highlight" v-if="question.answer">{{ question.answer }}</view>
</view>
</view>
<questionCard
v-for="(question, index) in trainQuestions"
:key="`train-question-${index}`"
:index="index"
:data="prepareTrainQuestionData(question)"
mode="display"
type="培训"
/>
</view>
<view class="question-section" v-if="detail.reason">
@ -58,6 +46,9 @@
重新考试
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>
@ -68,6 +59,7 @@
import { getAnswerBuildData, retakeExam } from '@/api/examination'
import { useStore } from 'vuex'
import { isOptionSelected, isAnswerCorrect, isQuestionAnswered, isSuccessPrecision } from '@/utils/exam.js'
import questionCard from '../components/questionCard.vue'
const store = useStore()
const questionList = ref([])
@ -84,14 +76,14 @@
const restTimes = ref(0)
const examNumber = ref(0)
//
// (type)
const basicQuestions = computed(() => {
return questionList.value.filter(item => item.type === '基本')
})
//
// (type)
const trainQuestions = computed(() => {
return questionList.value.filter(item => item.type === '培训' && isQuestionAnswered(item))
return questionList.value.filter(item => item.type === '培训')
})
onMounted(() => {
@ -104,10 +96,41 @@
questionList.value = res.answerList
examNumber.value = res.examNumber || 0
baseSuccess.value = isSuccessPrecision(res.answerList)
// 使
const basicQuestionsList = res.answerList.filter(item => item.type === '基本')
baseSuccess.value = isSuccessPrecision(basicQuestionsList)
})
}
//
const prepareQuestionData = (question) => {
return {
id: question.id,
title: question.title,
typeAnswer: question.typeAnswer,
options: question.answerList || [],
userAnswerBaseList: question.userAnswerBaseList || [],
//
rawQuestion: question
}
}
//
const prepareTrainQuestionData = (question) => {
return {
id: question.id,
title: question.title,
typeAnswer: question.typeAnswer,
options: question.answerList || [], //
value: question.userAppletAnswerTrain?.answer || '',
answer: question.answer || '',
numberWords: question.numberWords || 0,
userAnswerBaseList: question.userAnswerBaseList || [], //
//
rawQuestion: question
}
}
onShow(() => {
restTimes.value = parseInt(configList.value.pet_work_num.paramValueText || 0)
})


+ 3
- 0
otherPages/authentication/examination/start.vue View File

@ -44,6 +44,9 @@
{{ canTakeExam ? '开始考试' : '暂无考试机会' }}
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>


+ 82
- 12
otherPages/authentication/examination/train.vue View File

@ -14,7 +14,7 @@
<view class="content bg-fff">
<view>
<view class="label size-22">
主观题
混合题型
</view>
</view>
<view>
@ -28,6 +28,9 @@
提交
</button>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>
@ -70,9 +73,28 @@
id: item.id,
title: item.title,
value: null,
numberWords: item.numberWords
numberWords: item.numberWords,
...item,
}))
// value
for (let i = 0; i < questions.length; i++) {
if (questions[i].typeAnswer == 0) {
//
questions[i].options = questions[i].answerList
// value
const correctAnswers = questions[i].answerList.filter(option => option.isTrue === 1 || option.isTrue === true)
const isMultipleChoice = correctAnswers.length > 1
// null
questions[i].value = isMultipleChoice ? [] : null
} else if (questions[i].typeAnswer == 1) {
//
questions[i].value = null
}
}
list.value = questions
total.value = questions.length
@ -89,11 +111,25 @@
return false
}
if (typeof item.value == 'string') {
return item.value.length >= (item.numberWords || 0)
if (item.typeAnswer == 0) {
//
//
if(Array.isArray(item.value)){
return item.value.length > 0
}
//
return item.value !== null && item.value !== undefined
} else if (item.typeAnswer == 1) {
//
if (typeof item.value == 'string') {
return item.value.length >= (item.numberWords || 0)
}
return false
}
return true
return false
}).length
@ -106,17 +142,51 @@
const toNext = async () => {
const data = []
const answerBase = []
const answerTrains = []
list.value.forEach(n => {
data.push(addTrainAnswer({
userId: userId.value,
questionId: n.id,
answer : n.value,
}))
if (n.typeAnswer == 0) {
// -
if (Array.isArray(n.value)) {
//
n.value.forEach(answerId => {
answerBase.push({
userId: userId.value,
questionId: n.id,
answerId: answerId,
})
})
} else {
// 使
answerBase.push({
userId: userId.value,
questionId: n.id,
answerId: n.value,
})
}
} else if (n.typeAnswer == 1) {
// -
answerTrains.push({
userId: userId.value,
questionId: n.id,
answer: n.value,
})
}
})
await Promise.all(data)
// type
const hasBaseQuestions = list.value.some(item => item.typeAnswer == 0)
const hasTrainQuestions = list.value.some(item => item.typeAnswer == 1)
//
const submitData = {
answerBase: answerBase,
answerTrains: answerTrains,
type: hasTrainQuestions ? 1 : 0 // type10
}
await addBaseAnswer(submitData)
uni.navigateTo({
url: "/otherPages/authentication/examination/trainCompleted/index"


+ 2
- 0
otherPages/authentication/examination/trainCompleted/index.vue View File

@ -11,6 +11,8 @@
<waitingView v-else></waitingView>
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 0
- 8
otherPages/authentication/index.vue View File

@ -1,8 +0,0 @@
<template>
</template>
<script>
</script>
<style>
</style>

+ 7
- 4
otherPages/authentication/list/index.vue View File

@ -73,6 +73,8 @@
<configPopup ref="configPopupRef" />
<!-- 客服组件 -->
<CustomerService />
</view>
</template>
@ -386,16 +388,17 @@
data.id = id.value
await udpateUser(data)
} else {
await insertUser(data)
let r = await insertUser(data)
if(r.code != 200){
return
}
}
uni.navigateTo({
url: `/otherPages/authentication/examination/start?petType=${petType.value.join(',')}`
})
} catch (err) {
uni.navigateTo({
url: `/otherPages/authentication/examination/start?petType=${petType.value.join(',')}`
})
}
}


+ 14
- 5
otherPages/authentication/serve/index.vue View File

@ -111,7 +111,10 @@
<popupTypeSelect ref="popupTypeSelectRef" v-model="form.type" @confirm="fetchUpdate" ></popupTypeSelect>
<popupServeSelect ref="popupServeSelectRef" v-model="serveFormData.serve" @confirm="fetchUpdate" ></popupServeSelect>
<popupServeSelect ref="popupServeSelectRef"
:certificate="tData.license"
v-model="serveFormData.serve"
@confirm="fetchUpdate" ></popupServeSelect>
<popupSexSelect ref="popupSexSelectRef" v-model="baseFormData.sex" @confirm="fetchUpdate" ></popupSexSelect>
@ -121,6 +124,9 @@
</view>
<configPopup ref="configPopupRef" />
<!-- 客服组件 -->
<CustomerService />
</view>
</template>
@ -182,6 +188,7 @@
}
const baseFormRef = ref(null)
const tData = ref({})
const formData = reactive({
introduction: '',
@ -222,8 +229,10 @@
const fetchUserInfo = async () => {
try {
const data = await getUserOne(userInfo.value.userId)
if (data) {
tData.value = data
//
baseFormData.userName = userInfo.value.userName || ''
baseFormData.userTelephone = data.phone || userInfo.value.userTelephone || ''
@ -242,8 +251,8 @@
}
//
if (data.license) {
serveFormData.serve = data.license.split(';').map(item => parseInt(item))
if (data.addServices) {
serveFormData.serve = data.addServices.split(';').map(item => parseInt(item))
}
}
} catch (err) {
@ -343,7 +352,7 @@
userBrief: formData.userBrief,
experience: formData.experience,
petType: form.type.length > 0 ? form.type.join(',') : null,
license: serveFormData.serve.join(';'),
addServices: serveFormData.serve.join(';'),
}
if (form.headImage) {


+ 29
- 3
otherPages/authentication/serve/popupServeSelect.vue View File

@ -7,14 +7,16 @@
<view class="content">
<view class="flex-between option"
v-for="(option, index) in increaseServiceOptions"
:key="`option-${index}`"
:key="index"
@click="onSelectServe(option.id)"
>
<view class="option-info">
<view class="option-info"
v-if="option && isNeedCertificate(option.needCertificate)">
<view class="option-title">{{ option.title }}</view>
<view class="option-desc">{{ option.detail }}</view>
</view>
<up-icon
v-if="option && isNeedCertificate(option.needCertificate)"
name="checkmark-circle-fill"
:color="serves.includes(option.id) ? '#FFBF60' : '#BDBDBD'"
size="46rpx"
@ -39,6 +41,10 @@
return []
}
},
certificate: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue', 'confirm'])
@ -46,7 +52,7 @@
const serves = ref([])
const increaseServiceOptions = computed(() => {
return store.getters.increaseServiceOptions
return store.getters.increaseServiceOptions || []
})
const onSelectServe = (serve) => {
@ -62,6 +68,26 @@
serves.value = newServes
}
function isNeedCertificate(needCertificate) {
if (!needCertificate || typeof needCertificate !== 'string') {
return true
}
let a = false
const needCertificateArr = needCertificate.split(';')
const certificateArr = props.certificate ? props.certificate.split(';') : []
needCertificateArr.forEach(item => {
if (certificateArr.includes(item)) {
a = true
}
})
return a
}
const popupRef = ref()
const open = () => {


+ 2
- 0
otherPages/binding/share/index.vue View File

@ -10,6 +10,8 @@
</view>
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 3
- 0
otherPages/binding/wallet/index.vue View File

@ -104,6 +104,9 @@
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 79
- 22
otherPages/binding/withdrawal/index.vue View File

@ -27,16 +27,16 @@
lineHeight: '45rpx',
}"></up-parse>
</view>
<up-button class="buttom" type="warning" text="暂未开放" shape="circle"
style="color: #fff;background-color: #999;border: none;"
></up-button>
<!-- <up-button class="buttom" type="warning" text="提现" shape="circle" @click="cashClick"></up-button> -->
<up-button class="buttom" type="warning" text="提现" shape="circle" @click="cashClick"></up-button>
</view>
<uni-popup ref="inputDialogRef" type="dialog">
<uni-popup-dialog ref="inputClose" mode="input" title="提现" v-model="fullName" placeholder="请输入姓名"
@confirm="dialogInputConfirm" @close="close" :before-close="true"></uni-popup-dialog>
</uni-popup>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>
@ -46,9 +46,14 @@
} from "vue"
import { useStore } from 'vuex'
import {
cashOut
cashOut,
withdrawalSuccessful,
} from "@/api/amount/index.js"
import {
onLoad
} from '@dcloudio/uni-app'
const store = useStore()
const configList = computed(() => {
@ -59,46 +64,98 @@
const fullName = ref('')
const inputDialogRef = ref(null)
const inputClose = ref(null)
const type = ref(null)
onLoad((options) => {
console.log('options', options);
type.value = options.type || 0;
});
const cashClick = () => {
if (num.value) {
fullName.value = ''
inputDialogRef.value.open()
} else {
//
if (!num.value || num.value.trim() === '') {
uni.showToast({
icon: 'error',
title: '金额不能为空',
duration: 2000
})
return
}
const amount = parseFloat(num.value)
if (isNaN(amount) || amount <= 0) {
uni.showToast({
icon: 'error',
title: '请输入有效的金额',
duration: 2000
})
return
}
if (amount < 0.01) {
uni.showToast({
icon: 'error',
title: '提现金额不能小于0.01元',
duration: 2000
})
return
}
// 65
// withdrawalSuccessful({ id: 65 })
fullName.value = ''
inputDialogRef.value.open()
}
//
const dialogInputConfirm = (val) => {
if (val) {
//
cashOut({
userId: store.state.user.userInfo.userId,
amount:num.value,
nameValue:val,
moneyType:0,
amount: num.value,
nameValue: val,
moneyType: type.value,
}).then(res => {
if (res.code == 200) {
store.dispatch("getUserInfo")
fullName.value = ''
num.value = ''
//
uni.showToast({
title: '提现申请已提交,等待审核',
icon: 'none',
duration: 1500,
success() {
setTimeout(() => {
uni.navigateTo({
url: '/otherPages/myOrdersManage/transaction/index?type=1'
})
}, 1500)
}
})
} else {
uni.showToast({
icon: 'success',
title: '提现成功',
icon: 'error',
title: res.msg || '提现申请失败',
duration: 2000
})
uni.navigateBack(-1)
}
}).catch(error => {
console.error('提现申请失败:', error)
uni.showToast({
icon: 'error',
title: '提现申请失败,请重试',
duration: 2000
})
})


+ 0
- 462
otherPages/myOrdersManage/clock/index - 副本.vue View File

@ -1,462 +0,0 @@
<template>
<!-- <view>打卡</view> -->
<view class="box box-size">
<view class="top box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
个人准备
</view>
<view class="mt32 ml10">
<span :style="{fontSize:'30rpx',fontWeight:'400'}">手套照片</span>
<span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(至少1张)</span>
</view>
<view class="level">
<view class="mt20 ml10">
<up-upload :fileList="fileList.glove" @afterRead="afterRead" @delete="deletePic" name="glove"
multiple :maxCount="2" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
</view>
</view>
<view class="mt32 ml10">
<span :style="{fontSize:'30rpx',fontWeight:'400'}">鞋套照片</span>
<span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(至少1张)</span>
</view>
<view class="level">
<view class="mt20 ml10">
<up-upload :fileList="fileList.ShoeCover" @afterRead="afterRead" @delete="deletePic"
name="ShoeCover" multiple :maxCount="2" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
</view>
</view>
</view>
<view class="top mt24 box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
宠物状态记录
</view>
<view v-for="(pet, index) in petList" :key="index">
<view class="mt32 ml10">
<span :style="{fontSize:'30rpx',fontWeight:'400'}">{{ pet.title }}照片</span>
<span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(至少2张)</span>
</view>
<view class="level">
<view class="mt20 ml10 mr20">
<up-upload :fileList="fileList['pet' + index]" @afterRead="afterRead" @delete="deletePic"
:name="'pet' + index" multiple :maxCount="2" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode=""
:style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
</view>
</view>
</view>
</view>
<view class="top mt24 box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
基础服务记录
</view>
<view class="mt32 ml10" :style="{fontSize:'30rpx',fontWeight:'400'}">粮碗前后对比 </view>
<view class="level">
<view class="mt20 ml10">
<up-upload :fileList="fileList.foodA" @afterRead="afterRead" @delete="deletePic" name="foodA"
multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
<view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}"></view>
</view>
<view class="mt20 ml10 ml28">
<up-upload :fileList="fileList.foodB" @afterRead="afterRead" @delete="deletePic" name="foodB"
multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
<view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}"></view>
</view>
</view>
<view class="mt32 ml10" :style="{fontSize:'30rpx',fontWeight:'400'}">水碗前后对比 </view>
<view class="level">
<view class="mt20 ml10">
<up-upload :fileList="fileList.waterA" @afterRead="afterRead" @delete="deletePic" name="waterA"
multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
<view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}"></view>
</view>
<view class="mt20 ml10 ml28">
<up-upload :fileList="fileList.waterB" @afterRead="afterRead" @delete="deletePic" name="waterB"
multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
<view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}"></view>
</view>
</view>
<view class="mt32 ml10" :style="{fontSize:'30rpx',fontWeight:'400'}">猫砂盆尿垫前后对比 </view>
<view class="level">
<view class="mt20 ml10">
<up-upload :fileList="fileList.urinalA" @afterRead="afterRead" @delete="deletePic" name="urinalA"
multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
<view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}"></view>
</view>
<view class="mt20 ml10 ml28">
<up-upload :fileList="fileList.urinalB" @afterRead="afterRead" @delete="deletePic" name="urinalB"
multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
<view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}"></view>
</view>
</view>
</view>
<view class="top mt24 box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
定制服务记录
</view>
<view class="mt32 ml10">
<span :style="{fontSize:'30rpx',fontWeight:'400'}">遛狗</span>
<span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(2-3)</span>
</view>
<view class="level">
<view class="mt20 ml10 mr20">
<up-upload :fileList="fileList.testa" @afterRead="afterRead" @delete="deletePic" name="testa"
multiple :maxCount="3" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
</view>
</view>
<view class="mt32 ml10">
<span :style="{fontSize:'30rpx',fontWeight:'400'}">陪玩</span>
<span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(2-3)</span>
</view>
<view class="level">
<view class="mt20 ml10 mr20">
<up-upload :fileList="fileList.testb" @afterRead="afterRead" @delete="deletePic" name="testb"
multiple :maxCount="3" width="131rpx" height="131rpx" :disabled="isRead">
<image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}">
</image>
</up-upload>
</view>
</view>
</view>
<view class="top mt24 box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
其他补充信息(必填)
</view>
<view class="mt32 ml10" :style="{color:'#999999',fontSize:'30rpx'}">
可记录一下今日趣事宠物状况提醒事项等
</view>
<view class="mt24">
<textarea cols="30" rows="10"
placeholder="请输入内容"
v-model="content"
:style="{color:'#999999',fontSize:'30rpx',backgroundColor:'#F5F5F5',borderRadius:'16rpx',width:'681rpx',height:'180rpx'}"
class="pd20 box-size" :disabled="isRead"></textarea>
</view>
</view>
<view class="buttom mt60 box-size"
v-if="!isRead"
style="display: flex;gap: 20rpx;"
>
<view class="buttom-item buttom-item-2 level size-30"
@click="saveDraft(false)"
:style="{borderRadius:'41rpx',color:'#ff842c'}">
保存草稿
</view>
<view class="buttom-item level size-30"
@click="submit"
:style="{borderRadius:'41rpx',color:'#fff'}">
确定提交
</view>
</view>
</view>
</template>
<script setup>
import {
onMounted,
reactive,
ref,
} from "vue";
import {
onLoad
} from '@dcloudio/uni-app'
import {
ossUpload
} from '@/utils/oss-upload/oss/index.js'
import { orderItemLogGetByOrderId } from '@/api/order/order.js'
import {
getOrderPetById,
orderItemLogAddOrUpdate,
} from "@/api/order/order.js";
onLoad((options) => {
orderId.value = options.id || null;
isRead.value = options.isRead || false;
itemOrderID.value = options.itemID
serviceId.value = options.serviceId;
GetByOrderId()
});
const orderId = ref(0)
const itemOrderID = ref(0)
const serviceId = ref(0)
const isRead = ref(false)
const content = ref('')
const fileList = reactive({
// glove: [],//
// ShoeCover: [],//
// food: [],//
// water: [],//
// urinal: [],//尿
})
const form = ref({})
const petList = ref([
// {
// title: ''
// },
// {
// title: ''
// },
])
//
const deletePic = (event) => {
if (fileList[event.name]) {
fileList[event.name].splice(event.index, 1);
}
};
//
const afterRead = (event) => {
if (!fileList[event.name]) {
fileList[event.name] = []
}
event.file.forEach(n => {
ossUpload(n.url)
.then(url => {
fileList[event.name].push({
url
})
})
})
};
function submit() {
//
if (!fileList.glove || fileList.glove.length == 0) {
return msg('请上传鞋套照片')
}
if (!fileList.ShoeCover || fileList.ShoeCover.length == 0) {
return msg('请上传鞋套照片')
}
for (let i = 0; i < petList.value.length; i++) {
if (!fileList['pet' + i] || fileList['pet' + i].length < 2) {
return msg(`请上传${petList.value[i].title}照片`)
}
}
if (!fileList.foodA || fileList.foodA.length < 1 ||
!fileList.foodB || fileList.foodB.length < 1) {
return msg('请上传粮碗前后照片')
}
if (!fileList.waterA || fileList.waterA.length < 1 ||
!fileList.waterB || fileList.waterB.length < 1) {
return msg('请上传水碗前后照片')
}
if (!fileList.urinalA || fileList.urinalA.length < 1 ||
!fileList.urinalB || fileList.urinalB.length < 1) {
return msg('请上传猫砂盆、尿盆照片')
}
if(!content.value){
return msg('请填写补充信息')
}
saveDraft(true)
}
function GetByOrderId(){
orderItemLogGetByOrderId(serviceId.value)
.then(res => {
if(res.code == 200 && res.data) {
const data = res.data;
form.value = data
//
if(data.glovePhoto) {
fileList.glove = data.glovePhoto.split(',').map(url => ({ url }));
}
//
if(data.shoeCoverPhoto) {
fileList.ShoeCover = data.shoeCoverPhoto.split(',').map(url => ({ url }));
}
//
if(data.petPhoto) {
const pets = JSON.parse(data.petPhoto);
petList.value = pets;
pets.forEach((pet, index) => {
if(pet.fileList) {
fileList['pet' + index] = pet.fileList.split(',').map(url => ({ url }));
}
});
}
//
if(data.grainBowlFront) fileList.foodA = data.grainBowlFront.split(',').map(url => ({ url }));
if(data.grainBowlAfter) fileList.foodB = data.grainBowlAfter.split(',').map(url => ({ url }));
//
if(data.waterBowlFront) fileList.waterA = data.waterBowlFront.split(',').map(url => ({ url }));
if(data.waterBowlAfter) fileList.waterB = data.waterBowlAfter.split(',').map(url => ({ url }));
// /尿
if(data.basinFront) fileList.urinalA = data.basinFront.split(',').map(url => ({ url }));
if(data.basinAfter) fileList.urinalB = data.basinAfter.split(',').map(url => ({ url }));
//
if(data.workDogImage) fileList.testa = data.workDogImage.split(',').map(url => ({ url }));
if(data.workPalyImage) fileList.testb = data.workPalyImage.split(',').map(url => ({ url }));
}else{
getOrderPetByIdFN()
}
})
}
function getOrderPetByIdFN(){
getOrderPetById(orderId.value)
.then(res => {
if(res.code == 200){
res.data.forEach((n, i) => {
fileList['pet' + i] = []
})
res.data.forEach((n, i) => {
petList.value.push({
title : n.name,
id : n.id,
})
})
}
})
}
function saveDraft(flag) {
//
const params = {
orderId: orderId.value,
itemOrderId : itemOrderID.value,
itemDateId : serviceId.value,
glovePhoto: fileList.glove?.map(item => item.url).join(',') || '',
shoeCoverPhoto: fileList.ShoeCover?.map(item => item.url).join(',') || '',
//
petPhoto: JSON.stringify(
petList.value.map((pet, index) => {
return {
id : pet.id,
title : pet.title,
fileList: fileList['pet' + index]?.map(item => item.url).join(',') || ''
};
})
),
//
grainBowlFront: fileList.foodA?.map(item => item.url).join(',') || '',
grainBowlAfter: fileList.foodB?.map(item => item.url).join(',') || '',
//
waterBowlFront: fileList.waterA?.map(item => item.url).join(',') || '',
waterBowlAfter: fileList.waterB?.map(item => item.url).join(',') || '',
// /尿
basinFront: fileList.urinalA?.map(item => item.url).join(',') || '',
basinAfter: fileList.urinalB?.map(item => item.url).join(',') || '',
//
workDogImage: fileList.testa?.map(item => item.url).join(',') || '',
workPalyImage: fileList.testb?.map(item => item.url).join(',') || '',
//
notes: form.value.notes || '',
}
if(form.value.id) {
params.id = form.value.id
}
if(flag) {
params.submitFlag = 2
}
orderItemLogAddOrUpdate(params)
.then(res => {
if(res.code === 200) {
uni.showToast({
title: '提交成功',
icon: 'success'
})
// 稿
setTimeout(() => {
uni.navigateBack()
}, 1500)
} else {
uni.showToast({
title: res.msg || '提交失败',
icon: 'none'
})
}
})
.catch(err => {
uni.showToast({
title: '提交失败',
icon: 'none'
})
})
}
function msg(content) {
uni.showToast({
title: content,
icon: 'none'
})
}
</script>
<style scoped lang="scss">
@import"index.scss"
</style>

+ 2
- 0
otherPages/myOrdersManage/clock/index.vue View File

@ -226,6 +226,8 @@
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>


+ 0
- 66
otherPages/myOrdersManage/list/index.vue View File

@ -1,66 +0,0 @@
<template>
<view class="container">
<view v-for="(item,index) in questions" :key="index">
<view>{{ item.name }}</view>
<up-radio-group
v-model="radioValue[index]"
placement="column"
iconPlacement="right"
>
<up-radio
:customStyle="{marginBottom: '8px'}"
v-for="(item, index) in item.option"
:key="index"
:label="item.name"
:name="item.value"
@change="radioChange"
>
</up-radio>
</up-radio-group>
</view>
<up-button text="提交1" open-type="getPhoneNumber"
@getphonenumber="handleSubmit"></up-button>
</view>
</template>
<script setup>
import {useMixin} from "@/utils/useMixin";
import {ref} from "vue";
const {questions} = useMixin()
const radioValue = ref([])
const handleSubmit = (e) => {
wx.login({
success: res => {
if (res.code) {
// code
wx.request({
url: 'https://yourserver.com/getPhoneNumber', //
method: 'POST',
data: {
code: res.code,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv
},
success: function(response) {
//
console.log(response.data.phoneNumber);
}
});
} else {
console.log('登录失败!' + res.errMsg);
}
}
});
}
</script>
<style scoped lang="scss">
.container {
padding: 20px;
font-size: 14px;
line-height: 24px;
background: #FFFFFF;
}
</style>

+ 0
- 754
otherPages/myOrdersManage/service/detail - 副本.vue View File

@ -1,754 +0,0 @@
<template>
<!-- <div>服务档案</div> -->
<view class="box box-size">
<view style="margin: 32rpx 0;">
<up-steps :current="currentStep" activeColor="#FFA042" inactiveColor="#E5E5E5" class="up-steps-custom">
<up-steps-item title="喂养要求" :titleStyle="{fontSize: '32rpx', fontWeight: 'bold', color: currentStep === 0 ? '#FFA042' : '#999999'}"></up-steps-item>
<up-steps-item title="清洁要求" :titleStyle="{fontSize: '32rpx', fontWeight: 'bold', color: currentStep === 1 ? '#FFA042' : '#999999'}"></up-steps-item>
<up-steps-item title="附加服务" :titleStyle="{fontSize: '32rpx', fontWeight: 'bold', color: currentStep === 2 ? '#FFA042' : '#999999'}"></up-steps-item>
</up-steps>
</view>
<!-- v-if="detail.includeDryFood
|| detail.includeWetFood
|| detail.includeHomemadeFood
|| detail.includeRawMeat
|| detail.includeHealthSupplements
|| detail.includeSnacks
" -->
<!-- 喂养要求内容 -->
<view v-if="currentStep === 0">
<view class="top box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
本次喂养过程中您需要喂养的食品包括
</view>
<view v-if="!detail.includeDryFood && !detail.includeWetFood && !detail.includeHomemadeFood && !detail.includeRawMeat && !detail.includeHealthSupplements && !detail.includeSnacks" class="empty-state-full">
<up-icon name="info-circle" size="60rpx" color="#999999"></up-icon>
<text class="empty-text-full">暂无喂养食品</text>
</view>
<view class=" mt32 ml10" v-if="detail.includeDryFood">
<view class="color-A55 size-30 fw400">
干粮(主粮冻干等)
</view>
<view class="size-30 mt32 fw400">
干粮摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.dryFoodLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.dryFoodLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24 flex-between">
<view :style="{color:'#999999'}">干粮喂养重量</view>
<view class="level">
<view class="input_ mr10">
<input type="text" v-model="detail.dryFoodFeedWeight" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view>/(单位:)</view>
</view>
</view>
<view class="size-30 mt24 flex-between">
<view :style="{color:'#999999'}">干粮喂养频率</view>
<view class="level">
<view class="input_ mr10">
<input type="text" v-model="detail.dryFoodFeedFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
<view class="input_ mr10">
<input type="text" v-model="detail.dryFoodFeedFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
备注信息
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.dryFoodRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeDryFood"></view>
<view class=" mt32 ml10" v-if="detail.includeWetFood">
<view class="color-A55 size-30 fw400">
湿粮(罐头等)
</view>
<view class="size-30 mt32 fw400">
湿粮摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.wetFoodLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.wetFoodLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24 flex-between">
<view :style="{color:'#999999'}">湿粮喂养重量</view>
<view class="level">
<view class="input_ mr10">
<input type="text" disabled v-model="detail.wetFoodFeedWeight" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view>/(单位:)</view>
</view>
</view>
<view class="size-30 mt24 flex-between">
<view :style="{color:'#999999'}">湿粮喂养频率</view>
<view class="level">
<view class="input_ mr10">
<input type="text" disabled v-model="detail.wetFoodFeedFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
<view class="input_ mr10">
<input type="text" disabled v-model="detail.wetFoodFeedFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
备注信息
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.wetFoodRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeWetFood"></view>
<view class=" mt32 ml10" v-if="detail.includeHomemadeFood">
<view class="color-A55 size-30 fw400">
自制食品
</view>
<view class="size-30 mt32 fw400">
自制食品摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.homemadeFoodLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.homemadeFoodLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
自制饭制作和喂食说明
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.homemadeFoodInstructions" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeHomemadeFood"></view>
<view class=" mt32 ml10" v-if="detail.includeRawMeat">
<view class="color-A55 size-30 fw400">
生骨肉
</view>
<view class="size-30 mt32 fw400">
生骨肉摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.rawMeatLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.rawMeatLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
生骨肉喂食说明
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.rawMeatFeedingInstructions" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeRawMeat"></view>
<view class=" mt32 ml10" v-if="detail.includeHealthSupplements">
<view class="color-A55 size-30 fw400">
保健品
</view>
<view class="size-30 mt32 fw400">
保健品摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.healthSupplementsLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.healthSupplementsLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
保健品喂养方式
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.healthSupplementsFeedingInstructions" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeHealthSupplements"></view>
<view class=" mt32 ml10" v-if="detail.includeSnacks">
<view class="color-A55 size-30 fw400">
零食
</view>
<view class="size-30 mt32 fw400">
零食摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.snacksLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.snacksLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
零食喂养方式
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.snacksFeedingMethod" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeSnacks"></view>
</view>
<view class="top mt24" :style="{borderRadius:'16rpx'}">
<view class="form-title">
食盆/自动喂食器
</view>
<view class=" mt32 ml10">
<view class="color-A55 size-30 fw400">
喂食器具摆放位置
</view>
<view class="mt20">
<template v-if="handleImageUrl(detail.feedingTrayAutomaticFeederLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.feedingTrayAutomaticFeederLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
备注信息
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.feedingTrayAutomaticFeederRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
</view>
<view class="top mt24" :style="{borderRadius:'16rpx'}">
<view class="form-title">
饮用水
</view>
<view class=" mt32 ml10">
<view class="color-A55 size-30 fw400">
饮用水摆放位置
</view>
<view class="mt20">
<template v-if="handleImageUrl(detail.waterBowlLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.waterBowlLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
饮用水添加要求
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.waterBowlRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
</view>
<view class="top mt24" :style="{borderRadius:'16rpx'}">
<view class="form-title">
其他补充信息(非必填)
</view>
<view class=" mt32 ml10">
<view class="size-30 mt24" :style="{color:'#999999'}">
备注信息
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.otherRemark" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
</view>
</view>
<!-- 清洁要求内容 -->
<view v-if="currentStep === 1">
<view class="top box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
您需要的清洁内容
</view>
<view v-if="!detail.includeCatLitter && !detail.includeUrinePad" class="empty-state-full">
<up-icon name="info-circle" size="60rpx" color="#999999"></up-icon>
<text class="empty-text-full">暂无清洁内容</text>
</view>
<view class="mt32 ml10" v-if="detail.includeCatLitter">
<view class="color-A55 size-30 fw400">
猫砂
</view>
<view class="size-30 mt32 fw400">
猫砂摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.catLitterLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.catLitterLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24 flex-between">
<view :style="{color:'#999999'}">猫砂更换频率</view>
<view class="level">
<view class="input_ mr10">
<input type="text" disabled v-model="detail.catLitterChangeFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
<view class="input_ mr10">
<input type="text" disabled v-model="detail.catLitterChangeFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
备注信息
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.catLitterRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeCatLitter"></view>
<view class="mt32 ml10" v-if="detail.includeUrinePad">
<view class="color-A55 size-30 fw400">
尿垫
</view>
<view class="size-30 mt32 fw400">
尿垫摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.urinePadLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.urinePadLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24 flex-between">
<view :style="{color:'#999999'}">更换尿垫</view>
<view class="level">
<view class="input_ mr10">
<input type="text" v-model="detail.urinePadChangeFrequency" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
</view>
</view>
<view class="size-30 mt24 flex-between">
<view :style="{color:'#999999'}">尿垫更换频率</view>
<view class="level">
<view class="input_ mr10">
<input type="text" v-model="detail.urinePadChangeFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
<view class="input_ mr10">
<input type="text" v-model="detail.urinePadChangeFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}">
</view>
<view></view>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
备注信息
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.urinePadRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
</view>
</view>
<!-- 附加服务内容 -->
<view v-if="currentStep === 2">
<view class="top box-size" :style="{borderRadius:'16rpx'}">
<view class="form-title">
您需要的附加服务
</view>
<view v-if="!detail.additionalService || (!detail.playtimeService && !detail.furCleaningService && !detail.groomingService && !detail.deepCleaningService)" class="empty-state-full">
<up-icon name="info-circle" size="60rpx" color="#999999"></up-icon>
<text class="empty-text-full">暂无附加服务</text>
</view>
<!-- 陪玩服务 -->
<view class="mt32 ml10" v-if="detail.playtimeService">
<view class="color-A55 size-30 fw400">
陪玩
</view>
<view class="size-30 mt32 fw400">
陪玩用具摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.playtimeToolsLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.playtimeToolsLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
您希望的互动方式和要求
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.playtimeRequirements" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.playtimeService"></view>
<!-- 活动区吸毛 -->
<view class="mt32 ml10" v-if="detail.furCleaningService">
<view class="color-A55 size-30 fw400">
活动区吸毛
</view>
<view class="size-30 mt32 fw400">
吸毛用品摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.furCleaningToolsLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.furCleaningToolsLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
请说明吸毛区域及要求
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.furCleaningRequirements" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.furCleaningService"></view>
<!-- 毛发梳理 -->
<view class="mt32 ml10" v-if="detail.groomingService">
<view class="color-A55 size-30 fw400">
毛发梳理
</view>
<view class="size-30 mt32 fw400">
毛发梳理用品摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.groomingToolsLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.groomingToolsLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
请说明毛发梳理要求(: 天数,次数等)
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.groomingRequirements" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.groomingService"></view>
<!-- 食具深度清洁 -->
<view class="mt32 ml10" v-if="detail.deepCleaningService">
<view class="color-A55 size-30 fw400">
食具深度清洁
</view>
<view class="size-30 mt32 fw400">
需深度清洁的用具及消毒剂摆放位置
</view>
<view class="img mt20">
<template v-if="handleImageUrl(detail.deepCleaningToolsLocation).length > 0">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.deepCleaningToolsLocation)" :key="index"></u-image>
</template>
<view v-else class="empty-state">
<up-icon name="info-circle" size="40rpx" color="#999999"></up-icon>
<text class="empty-text">暂无图片</text>
</view>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
请说明食具/饮水机深度清洁要求(如清洁方式消毒剂使用方式频率等)
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.deepCleaningRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.deepCleaningService"></view>
<!-- 喂药上药 -->
<view class="mt32 ml10" v-if="detail.administerMedication">
<view class="color-A55 size-30 fw400">
喂药上药
</view>
<view class="size-30 mt32 fw400">
药品摆放位置
</view>
<view class="img mt20">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.administerMedicationLocation)" :key="index"></u-image>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
请说明药品使用方式
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" disabled v-model="detail.administerMedicationRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.administerMedication"></view>
<!-- 遛狗服务 -->
<view class="mt32 ml10" v-if="detail.walkDog">
<view class="color-A55 size-30 fw400">
遛狗服务
</view>
<view class="size-30 mt32 fw400">
遛狗用品摆放位置
</view>
<view class="img mt20">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.walkDogToolsLocation)" :key="index"></u-image>
</view>
<!-- 雨天遛狗 -->
<view v-if="detail.walkDogRainyDay">
<view class="size-30 mt32 fw400">
狗狗雨具位置
</view>
<view class="img mt20">
<u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.dogRainGearLocation)" :key="index"></u-image>
</view>
<view class="size-30 mt24" :style="{color:'#999999'}">
下雨天遛狗要求
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.walkDogRainRemark" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<!-- 遛狗时长 -->
<view v-if="detail.isWalkDogDuration">
<view class="size-30 mt24" :style="{color:'#999999'}">
遛狗时长
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.walkDogDuration" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<!-- 狗狗行为 -->
<view v-if="detail.isDogBehavior">
<view class="size-30 mt24" :style="{color:'#999999'}">
狗狗行为
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.dogBehavior" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<!-- 偏好区域 -->
<view v-if="detail.isFavoriteRegion">
<view class="size-30 mt24" :style="{color:'#999999'}">
偏好区域
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.favoriteRegion" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
<!-- 偏好路线 -->
<view v-if="detail.isFavoriteRoute">
<view class="size-30 mt24" :style="{color:'#999999'}">
偏好路线
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.favoriteRoute" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
</view>
<!-- 附加服务备注 -->
<view class="mt32 ml10">
<view class="size-30 mt24" :style="{color:'#999999'}">
附加服务备注
</view>
<view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}">
<input type="text" v-model="detail.additionalServicesRemarks" :style="{height:'50rpx',fontSize:'30rpx'}">
</view>
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="buttom_ mt60 box-size level" v-if="currentStep > 0">
<view class="buttom_item level size-30" :style="{borderRadius:'41rpx',color:'#fff'}" @click="prevStep">
上一页
</view>
<view class="buttom_item level size-30" :style="{borderRadius:'41rpx',color:'#fff'}" @click="nextStep">
{{ currentStep === 2 ? '完成' : '下一页' }}
</view>
</view>
<view class="buttom mt60 box-size" v-else>
<view class="buttom-item level size-30" :style="{borderRadius:'41rpx',color:'#fff'}" @click="nextStep">
下一页
</view>
</view>
</view>
</template>
<script>
import {
getpetList
} from "@/api/pet/index.js"
import {
getDictList
} from "@/api/system/user.js"
import {
getPetCareByPetId
} from "@/api/order/order.js";
export default {
data() {
return {
detail: {},
show: false,
petTypes: [],
petType: '',
orderId: 0,
currentStep: 0, // 0:1:2:
}
},
onLoad(options) {
this.orderId = options.petId
this.getPetCareByPetId()
//
if (options.step) {
this.currentStep = parseInt(options.step)
}
},
methods: {
getPetCareByPetId() {
getPetCareByPetId(this.orderId).then(res => {
this.detail = res.data || {}
})
},
handleImageUrl(url) {
if (!url) return []
return url.split(',')
},
previewImage(url) {
uni.previewImage({
urls: [url]
})
},
//
prevStep() {
if (this.currentStep > 0) {
this.currentStep--;
}
},
//
nextStep() {
if (this.currentStep < 2) {
this.currentStep++;
} else {
//
uni.navigateBack();
}
},
}
}
</script>
<style scoped lang="scss">
@import"detail.scss";
@import"steps-custom.scss";
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 173rpx;
height: 230rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
}
.empty-text {
margin-top: 10rpx;
font-size: 24rpx;
color: #999999;
}
.empty-state-full {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 200rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
margin-top: 20rpx;
}
.empty-text-full {
margin-top: 20rpx;
font-size: 28rpx;
color: #999999;
}
/* 自定义步骤条样式 */
:deep(.up-steps-custom) {
.u-steps-item__wrapper {
width: 40rpx !important;
height: 40rpx !important;
&__circle {
width: 40rpx !important;
height: 40rpx !important;
background: #E5E5E5;
&__text {
font-size: 24rpx;
}
}
}
.u-steps-item__line {
top: 20rpx !important;
height: 4rpx !important;
background: #E5E5E5 !important;
}
/* 激活状态样式 */
.u-steps-item--active {
.u-steps-item__wrapper__circle {
background: linear-gradient(90deg, #FFA042, #FFD591) !important;
}
}
/* 已完成步骤前的线条样式 */
.u-steps-item--process {
.u-steps-item__line {
background: linear-gradient(90deg, #FFA042, #FFD591) !important;
}
}
}
</style>

+ 2
- 0
otherPages/myOrdersManage/service/detail.vue View File

@ -701,6 +701,8 @@
下一页
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>


+ 0
- 80
otherPages/myOrdersManage/service/index - 副本.vue View File

@ -1,80 +0,0 @@
<template>
<!-- <div>服务档案</div> -->
<view class="box box-size">
<view class="container box-size level"
@click="toDetail" :style="{borderRadius:'16rpx'}">
<view class="level">
<view class="img">
<image src="" mode=""></image>
</view>
<view class="text">
查看猫猫小咪的服务信息
</view>
</view>
<view class="img_">
<image src="/static/images/ydd/right.png" mode=""></image>
</view>
</view>
</view>
</template>
<script setup>
function toDetail() {
uni.navigateTo({
url: "/otherPages/myOrdersManage/service/detail"
})
}
</script>
<style scoped lang="scss">
.box {
width: 100vw;
height: 100vh;
padding: 2% 3%;
}
.box-size {
box-sizing: border-box;
}
.level {
display: flex;
}
.container {
width: 710rpx;
height: 121rpx;
background-color: #fff;
padding: 1% 4% 3% 2%;
justify-content: space-between;
margin-bottom: 20rpx;
}
.img image {
width: 104rpx;
height: 104rpx;
border-radius: 100rpx;
background-color: red;
margin-right: 20rpx;
}
.text {
font-size: 30rpx;
display: grid;
place-items: center;
}
.img_ {
display: grid;
place-items: center;
}
.img_ image {
width: 40rpx;
height: 40rpx;
/* background-color: red; */
}
</style>

+ 317
- 15
otherPages/myOrdersManage/transaction/index.vue View File

@ -12,21 +12,41 @@
</view>
</view>
<view v-for="(item,index) in list" :key="index" class="Recharge flex">
<view v-for="(item,index) in list" :key="index" class="Recharge">
<view class="flex">
<image style="width: 80rpx;height: 80rpx" src="https://img1.baidu.com/it/u=3034232350,1041791648&fm=253&fmt=auto&app=138&f=PNG?w=500&h=500"
mode="aspectFill"></image>
<view class="text1">
<view class="text2">
{{ item.title }}
<!-- 金额类型标签 -->
<text v-if="item.moneyType !== undefined" class="money-type-tag" :class="getMoneyTypeClass(item.moneyType)">
{{ getMoneyTypeText(item.moneyType) }}
</text>
</view>
<view>
{{ item.createTime }}
</view>
<!-- 审核状态显示 - 只在支出明细中显示 -->
<view v-if="type === 1 && item.auditStatus !== undefined" class="audit-status">
<text v-if="item.auditStatus === 0" class="status-pending">待审核</text>
<text v-if="item.auditStatus === 1" class="status-approved">审核通过</text>
<text v-if="item.auditStatus === 2" class="status-rejected">审核不通过</text>
</view>
<!-- 审核不通过时显示备注 - 只在支出明细中显示 -->
<view v-if="type === 1 && item.auditStatus === 2 && item.remark" class="remark">
备注{{ item.remark }}
</view>
</view>
</view>
<view class="text3">
{{ item.amount }}
<view class="right-content">
<view class="text3">
{{ item.amount }}
</view>
<!-- 审核通过的提现显示领取按钮 -->
<view v-if="item.auditStatus === 1 && type === 1 && item.state == 0" class="receive-btn" @click="receiveWithdrawal(item)">
领取
</view>
</view>
</view>
</view>
@ -36,18 +56,31 @@
import {
ref
} from "vue"
import { useStore } from 'vuex'
import {
amountLogList
} from "@/api/amount/index.js"
import {
withdrawalSuccessful
} from "@/api/amount/index.js"
import {
onShow
onShow,
onLoad
} from "@dcloudio/uni-app"
onLoad((options) => {
// typetab
if (options.type) {
type.value = parseInt(options.type)
}
})
onShow(() => {
getRunningWater()
})
const store = useStore()
const list = ref([]);
const type = ref(0);
@ -55,7 +88,8 @@
const getRunningWater = async () => {
let response = await amountLogList({
type: type.value,
moneyType:1
moneyType:1,
userId: store.state.user.userInfo.userId
});
if (response.code == 200) {
list.value = response.data
@ -66,14 +100,156 @@
type.value = index;
getRunningWater();
}
//
const getMoneyTypeText = (moneyType) => {
switch (moneyType) {
case 0:
return '合伙人余额'
case 1:
return '伴宠师余额'
case 2:
return '保证金余额'
default:
return ''
}
}
//
const getMoneyTypeClass = (moneyType) => {
switch (moneyType) {
case 0:
return 'money-type-partner'
case 1:
return 'money-type-sitter'
case 2:
return 'money-type-deposit'
default:
return ''
}
}
//
const receiveWithdrawal = async (item) => {
try {
console.log('开始领取提现,item:', item)
//
const confirmResult = await new Promise((resolve) => {
uni.showModal({
title: '确认领取',
content: `确定要领取¥${item.amount}吗?`,
success: (res) => {
resolve(res.confirm)
}
})
})
if (!confirmResult) {
return
}
uni.showLoading({
title: '处理中...'
})
//
if (typeof wx === 'undefined' || !wx.canIUse('requestMerchantTransfer')) {
console.log('微信API不可用,尝试直接调用后端接口')
// API
const result = await withdrawalSuccessful({
id: item.id,
userId: store.state.user.userInfo.userId
})
console.log('withdrawalSuccessful result:', result)
uni.hideLoading()
if (result.code === 200) {
uni.showToast({
title: '领取成功',
icon: 'success',
duration: 1500,
success() {
//
getRunningWater()
}
})
} else {
uni.showToast({
title: result.msg || '领取失败',
icon: 'error',
duration: 2000
})
}
return
}
// API
wx.requestMerchantTransfer({
mchId: '1665639691', // ID
appId: wx.getAccountInfoSync().miniProgram.appId,
package: item.packageInfo || '', //
success: async (res) => {
console.log('微信转账成功:', res)
//
try {
const result = await withdrawalSuccessful({
id: item.id,
userId: store.state.user.userInfo.userId
})
console.log('withdrawalSuccessful result:', result)
uni.hideLoading()
if (result.code === 200) {
uni.showToast({
title: '领取成功',
icon: 'success',
duration: 1500,
success() {
//
getRunningWater()
}
})
} else {
uni.showToast({
title: result.msg || '领取失败',
icon: 'error',
duration: 2000
})
}
} catch (error) {
console.error('调用withdrawalSuccessful失败:', error)
uni.hideLoading()
uni.showToast({
title: '网络异常,请重试',
icon: 'error',
duration: 2000
})
}
},
fail: (res) => {
}
})
} catch (error) {
uni.hideLoading()
console.error('领取提现异常:', error)
uni.showToast({
title: '网络异常,请重试',
icon: 'error',
duration: 2000
})
}
}
</script>
<style scoped lang="scss">
.box {
width: 750rpx;
height: 1452rpx;
min-height: 100vh;
background-color: #FFFFFF;
padding: 2% 2% 0 2%;
padding: 32rpx 24rpx 0 24rpx;
box-sizing: border-box;
}
@ -86,22 +262,33 @@
.active {
background-color: #FFBF60;
color: white;
font-weight: 600;
box-shadow: 0 2rpx 8rpx rgba(255, 191, 96, 0.3);
}
.income {
width: 361rpx;
height: 63rpx;
line-height: 63rpx;
font-size: 30rpx;
font-size: 28rpx;
font-weight: 500;
justify-content: center;
transition: all 0.3s ease;
}
.Recharge {
padding: 3% 3% 0 2%;
padding: 32rpx 24rpx;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: flex-start;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
.flex {
@ -109,22 +296,137 @@
}
.text1 {
font-size: 30rpx;
color: #949494;
font-size: 26rpx;
color: #666666;
line-height: 1.4;
margin-left: 20rpx;
}
.Recharge image {
width: 56rpx;
height: 56rpx;
margin-top: 0.5%;
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
margin-top: 0;
}
.text2 {
font-weight: 700;
font-weight: 500;
color: #333333;
font-size: 28rpx;
line-height: 40rpx;
margin-bottom: 8rpx;
display: flex;
align-items: center;
}
.text3 {
color: #FF2A2A;
font-size: 30rpx;
font-weight: 600;
line-height: 44rpx;
}
.right-content {
display: flex;
flex-direction: column;
align-items: flex-end;
min-width: 120rpx;
}
.right-content > .text3 {
margin-bottom: 16rpx;
}
.audit-status {
margin-top: 12rpx;
font-size: 22rpx;
}
.status-pending {
color: #FF9800;
background-color: #FFF3E0;
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-size: 20rpx;
font-weight: 500;
display: inline-block;
}
.status-approved {
color: #4CAF50;
background-color: #E8F5E8;
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-size: 20rpx;
font-weight: 500;
display: inline-block;
}
.status-rejected {
color: #F44336;
background-color: #FFEBEE;
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-size: 20rpx;
font-weight: 500;
display: inline-block;
}
.remark {
margin-top: 12rpx;
color: #666;
font-size: 24rpx;
line-height: 36rpx;
background-color: #f8f8f8;
padding: 12rpx;
border-radius: 8rpx;
border-left: 4rpx solid #F44336;
}
.receive-btn {
background-color: #FFBF60;
color: #FFFFFF;
padding: 12rpx 24rpx;
border-radius: 24rpx;
font-size: 26rpx;
font-weight: 500;
text-align: center;
min-width: 80rpx;
cursor: pointer;
box-shadow: 0 4rpx 12rpx rgba(255, 191, 96, 0.3);
transition: all 0.3s ease;
}
.receive-btn:active {
background-color: #E6A84D;
transform: translateY(2rpx);
box-shadow: 0 2rpx 8rpx rgba(255, 191, 96, 0.2);
}
/* 金额类型标签样式 */
.money-type-tag {
font-size: 20rpx;
font-weight: 500;
padding: 4rpx 8rpx;
border-radius: 8rpx;
margin-left: 12rpx;
display: inline-block;
line-height: 1;
flex-shrink: 0;
}
.money-type-partner {
color: #1976D2;
background-color: #E3F2FD;
}
.money-type-sitter {
color: #388E3C;
background-color: #E8F5E8;
}
.money-type-deposit {
color: #F57C00;
background-color: #FFF3E0;
}
</style>

+ 3
- 0
otherPages/orderTakingManage/detail/index.vue View File

@ -252,6 +252,9 @@
</view>
</up-popup>
<configPopup ref="configPopupRef" />
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 2
- 1
otherPages/orderTakingManage/evaluate/index.vue View File

@ -32,7 +32,8 @@
</view>
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 0
- 66
otherPages/orderTakingManage/list/index.vue View File

@ -1,66 +0,0 @@
<template>
<view class="container">
<view v-for="(item,index) in questions" :key="index">
<view>{{ item.name }}</view>
<up-radio-group
v-model="radioValue[index]"
placement="column"
iconPlacement="right"
>
<up-radio
:customStyle="{marginBottom: '8px'}"
v-for="(item, index) in item.option"
:key="index"
:label="item.name"
:name="item.value"
@change="radioChange"
>
</up-radio>
</up-radio-group>
</view>
<up-button text="提交1" open-type="getPhoneNumber"
@getphonenumber="handleSubmit"></up-button>
</view>
</template>
<script setup>
import {useMixin} from "@/utils/useMixin";
import {ref} from "vue";
const {questions} = useMixin()
const radioValue = ref([])
const handleSubmit = (e) => {
wx.login({
success: res => {
if (res.code) {
// code
wx.request({
url: 'https://yourserver.com/getPhoneNumber', //
method: 'POST',
data: {
code: res.code,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv
},
success: function(response) {
//
console.log(response.data.phoneNumber);
}
});
} else {
console.log('登录失败!' + res.errMsg);
}
}
});
}
</script>
<style>
.container {
padding: 20px;
font-size: 14px;
line-height: 24px;
background: #FFFFFF;
}
</style>

+ 0
- 9
otherPages/otherPages/myOrdersManage/bond/detail.vue View File

@ -1,9 +0,0 @@
<template>
<div>保证金明细</div>
</template>
<script>
</script>
<style>
</style>

+ 0
- 9
otherPages/otherPages/myOrdersManage/bond/index.vue View File

@ -1,9 +0,0 @@
<template>
<div>保证金/保证金退还</div>
</template>
<script>
</script>
<style>
</style>

+ 0
- 9
otherPages/otherPages/myOrdersManage/detail/index.vue View File

@ -1,9 +0,0 @@
<template>
<div>交易明细</div>
</template>
<script>
</script>
<style>
</style>

+ 0
- 9
otherPages/otherPages/myOrdersManage/withdrawal/index.vue View File

@ -1,9 +0,0 @@
<template>
<div>提现</div>
</template>
<script>
</script>
<style>
</style>

+ 0
- 66
otherPages/userManage/list/index.vue View File

@ -1,66 +0,0 @@
<template>
<view class="container">
<view v-for="(item,index) in questions" :key="index">
<view>{{ item.name }}</view>
<up-radio-group
v-model="radioValue[index]"
placement="column"
iconPlacement="right"
>
<up-radio
:customStyle="{marginBottom: '8px'}"
v-for="(item, index) in item.option"
:key="index"
:label="item.name"
:name="item.value"
@change="radioChange"
>
</up-radio>
</up-radio-group>
</view>
<up-button text="提交1" open-type="getPhoneNumber"
@getphonenumber="handleSubmit"></up-button>
</view>
</template>
<script setup>
import {useMixin} from "@/utils/useMixin";
import {ref} from "vue";
const {questions} = useMixin()
const radioValue = ref([])
const handleSubmit = (e) => {
wx.login({
success: res => {
if (res.code) {
// code
wx.request({
url: 'https://yourserver.com/getPhoneNumber', //
method: 'POST',
data: {
code: res.code,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv
},
success: function(response) {
//
console.log(response.data.phoneNumber);
}
});
} else {
console.log('登录失败!' + res.errMsg);
}
}
});
}
</script>
<style>
.container {
padding: 20px;
font-size: 14px;
line-height: 24px;
background: #FFFFFF;
}
</style>

+ 0
- 316
otherPages/userManage/pet/components/petBaseInfo.vue View File

@ -1,316 +0,0 @@
<template>
<view>
<view class="personal-pet-basic-info">
<view class="personal-pet-info-title border-bottom">
宠物基本信息
</view>
<u--form labelPosition="left" :model="model" ref="uForm">
<u-form-item required label="昵称" :prop="`petBaseInfo.nickName`" labelWidth="80" borderBottom>
<u--input v-model="petBaseInfo.nickName" placeholder="请输入宠物昵称" @change="nameChange"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
</u-form-item>
<u-form-item label="性别" :prop="`petBaseInfo.sex`" labelWidth="80" borderBottom @click="showSex = true">
<u--input v-model="sexText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<!-- 后台获取品种数据 赶进度这里先注释 -->
<!-- <u-form-item required label="品种" :prop="`petBaseInfo.breed`" labelWidth="80" borderBottom
@click="breedSelectOpen"> -->
<u-form-item required label="品种" :prop="`petBaseInfo.type`" labelWidth="80" borderBottom>
<u--input v-model="petBaseInfo.type" disabledColor="#ffffff" placeholder="请输入"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<!-- 赶进度先注释 -->
<!-- <u-form-item required label="体重" :prop="`petBaseInfo.weight`" labelWidth="80" borderBottom
@click="showWeight = true" ref="item1"> -->
<u-form-item required label="体重" :prop="`petBaseInfo.weight`" labelWidth="80" borderBottom ref="item1">
<u--input v-model="petBaseInfo.weight" disabledColor="#ffffff" placeholder="请输入(单位kg)"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right" type="number"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item label="出生年月" :prop="`petBaseInfo.birthday`" labelWidth="80" borderBottom
@click="yearMonthOpen">
<u--input v-model="petBaseInfo.birthday" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<!-- 后台获取性格选择数据 感进度这里先注释 -->
<!-- <u-form-item required label="性格" :prop="`petBaseInfo.personality`" labelWidth="80" borderBottom
@click="dispositionSelectOpen"> -->
<u-form-item required label="性格" :prop="`petBaseInfo.personality`" labelWidth="80" borderBottom>
<u--input v-model="petBaseInfo.personality" disabledColor="#ffffff" placeholder="请输入"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
</u--form>
</view>
<!-- 后台获取性格选择数据 感进度这里先注释 -->
<!-- <view style="background-color: #fffcf2;padding: 10px 20px;" v-show="showDisposition">
<view style="height: 85%;">
<u-checkbox-group v-model="petBaseInfo.personality" @change="checkboxChange" iconPlacement="left"
placement="row" style="flex-wrap: wrap;" activeColor="#ffbf60">
<u-checkbox :customStyle="{margin: '8px'}" v-for="(item, index) in dispositionActions" :key="item"
:label="item" :name="item">
</u-checkbox>
</u-checkbox-group>
</view>
</view> -->
<u-picker :show="showSex" :showToolbar='false' :columns="sexActions" @cancel="showSex = false"
:immediateChange="true" @change="sexSelect"></u-picker>
<u-picker :show="showWeight" :showToolbar='false' :columns="weightActions" @cancel="showWeight = false"
:immediateChange="true" @change="weightSelect"></u-picker>
<u-overlay :show="showBreed">
<view class="breed-select" style="height: 60%;">
<u-search shape="round" :show-action="false" v-model="searchValue" @change="searchBreed"></u-search>
<view style="padding: 10px;height: 80%;overflow: auto;">
<u-radio-group v-model="tempBreed" placement="column">
<u-radio v-for="(item,index) in searchBreedData" :customStyle="{marginBottom: '8px'}"
activeColor="#ffbf60" :label="item" :name="item" :key="item"></u-radio>
</u-radio-group>
</view>
<view class="personal-pet-breed-btns">
<view class="personal-pet-breed-btn">
<u-button color="#FFF4E4" @click="breedSelectClose">
<view style="color: #A9A9A9;">
取消
</view>
</u-button>
</view>
<view class="personal-pet-breed-btn" @click="breedSelectConfirm">
<u-button color="#FFBF60">
<view style="color: #fff;">
确定
</view>
</u-button>
</view>
</view>
</view>
</u-overlay>
<u-datetime-picker :maxDate='getMaxDate()' :minDate="getMinDate()" :show="showBirthday" v-model="tempBirthday"
mode="year-month" @confirm="yearMonthConfirm" @cancel="yearMonthClose"></u-datetime-picker>
</view>
</template>
<script setup>
import {
ref,
onMounted,
watch,
computed
} from 'vue';
// props
const props = defineProps({
petType: {
type: String,
default: 'dog'
},
petBaseInfo: {
type: Object,
default: () => ({
nickName: '',
sex: '',
type: '',
weight: '',
birthday: '',
personality: ''
})
}
});
// emits
const emits = defineEmits(['update:petBaseInfo']);
//
const showSex = ref(false);
const showBreed = ref(false);
const showWeight = ref(false);
const showBirthday = ref(false);
const showDisposition = ref(false);
const sexActions = ref([
['男生', '女生']
]);
const weightActions = ref([
['小型(<10 KG)', '中型(10~20KG)', '大型(20KG以上)']
]);
const dispositionActions = ref([]);
const breedData = ref([]);
const searchBreedData = ref([]);
const tempBreed = ref('');
const searchValue = ref('');
const tempBirthday = ref('');
const tempDisposition = ref('');
const uForm = ref(null);
const item1 = ref(null);
const sexText = computed(() => {
const sexMap = {
0: '男生',
1: '女生'
};
return sexMap[props.petBaseInfo.sex] || '';
});
//
const nameChange = () => {
updatePetBaseInfo();
};
const sexSelect = (e) => {
props.petBaseInfo.sex = e.index;
showSex.value = false;
updatePetBaseInfo();
};
const weightSelect = (e) => {
props.petBaseInfo.weight = e.value[0];
showWeight.value = false;
updatePetBaseInfo();
};
const breedSelectOpen = () => {
searchBreedData.value = breedData.value;
searchValue.value = '';
tempBreed.value = props.petBaseInfo.breed;
showBreed.value = true;
};
const searchBreed = () => {
if (searchValue.value && searchValue.value !== '') {
searchBreedData.value = breedData.value.filter(e => e.includes(searchValue.value));
} else {
searchBreedData.value = breedData.value;
}
};
const breedSelectClose = () => {
tempBreed.value = '';
showBreed.value = false;
};
const breedSelectConfirm = () => {
props.petBaseInfo.type = tempBreed.value;
showBreed.value = false;
updatePetBaseInfo();
};
const yearMonthOpen = () => {
if (props.petBaseInfo.birthday) {
tempBirthday.value = Number(new Date(props.petBaseInfo.birthday));
} else {
tempBirthday.value = Number(new Date());
}
showBirthday.value = true;
};
const yearMonthClose = () => {
showBirthday.value = false;
};
const yearMonthConfirm = (e) => {
const timeFormat = uni.$u.timeFormat;
props.petBaseInfo.birthday = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss');
showBirthday.value = false;
updatePetBaseInfo();
};
const dispositionSelectOpen = () => {
tempDisposition.value = props.petBaseInfo.personality;
showDisposition.value = true;
};
const dispositionSelectClose = () => {
tempDisposition.value = '';
showDisposition.value = false;
};
const dispositionSelectConfirm = () => {
props.petBaseInfo.personality = tempDisposition.value;
showDisposition.value = false;
updatePetBaseInfo();
};
const checkboxChange = (n) => {
updatePetBaseInfo();
};
const updatePetBaseInfo = () => {
// update petBaseInfo
emits('update:petBaseInfo', props.petBaseInfo);
};
const getPersonalityDataList = () => {
// getDictList('pet_personality').then(res => {
// if (res.code === 200) {
// dispositionActions.value = Array.from(new Set(res.data.map(e => e.dictLabel)));
// } else {
// uni.showToast({
// title: '',
// icon: 'none'
// });
// }
// });
};
const getPetBreed = () => {
let petBreedType = props.petType === 'cat' ? 'pet_brand_cat' : 'pet_brand_dog';
// getDictList(petBreedType).then(res => {
// if (res.code === 200) {
// breedData.value = Array.from(new Set(res.data.map(e => e.dictLabel)));
// } else {
// uni.showToast({
// title: '',
// icon: 'none'
// });
// }
// });
};
const getMaxDate = () => {
return Date.now();
};
const getMinDate = () => {
let dt = new Date();
dt.setFullYear(dt.getFullYear() - 30);
return Date.parse(dt);
};
//
onMounted(() => {
// getPersonalityDataList();
// getPetBreed();
});
</script>
<style>
.personal-pet-basic-info {
background-color: #fff;
margin-top: 10px;
padding: 10px 20px;
}
.personal-pet-breed-btns {
display: flex;
justify-content: space-between;
box-sizing: border-box;
background-color: #FFFFFF;
padding: 20rpx 20rpx 40rpx 20rpx;
left: 0;
width: 100%;
height: 160rpx;
position: fixed;
bottom: 0;
z-index: 100;
}
.personal-pet-breed-btn {
width: 40%;
}
</style>

+ 0
- 253
otherPages/userManage/pet/components/petHealthInfo.vue View File

@ -1,253 +0,0 @@
<template>
<view>
<view class="personal-pet-health-info">
<view class="personal-pet-info-title border-bottom">
宠物健康情况
</view>
<u--form labelPosition="left" :model="petHealthInfo" ref="uForm">
<u-form-item required label="疫苗" :prop="`petHealthInfo.vaccine`" labelWidth="80" borderBottom
@click="showVaccine = true">
<u--input v-model="vaccineText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item required label="驱虫" :prop="`petHealthInfo.deworm`" labelWidth="80" borderBottom
@click="showExpelling = true">
<u--input v-model="dewormText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item label="绝育" :prop="`petHealthInfo.neutered`" labelWidth="80" borderBottom
@click="showSterilization = true" ref="item1">
<u--input v-model="neuteredText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item v-if="petType === '1' || petType === '狗狗' " label="狗证" :prop="`petHealthInfo.petCard`"
labelWidth="80" borderBottom @click="showDog = true" ref="item1">
<u--input v-model="petCartText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<view class="dog-tips" v-if="petType === 'dog' || petType === '狗狗'">
<u-icon name="info-circle" color="#A94F20" size="12"></u-icon>
<view style="margin-left: 3px;">
未办理养犬许可证且需要外出遛狗, 犬只存在被相关单位收缴
甚至捕杀的可能请您遵守当地养犬规范合法文明养犬具体
请您查看当地养犬条例
</view>
</view>
<!-- 赶进度先注释 -->
<!-- <u-form-item required label="健康" :prop="`petHealthInfo.healths`" labelWidth="80" @click="healthsSelect"> -->
<u-form-item required label="健康" :prop="`petHealthInfo.health`" labelWidth="80">
<u--input disabledColor="#ffffff" placeholder="请输入" v-model="petHealthInfo.health"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<view slot="right">
<u-icon v-if="showHealths" name="arrow-down" color="#AAA"></u-icon>
<u-icon v-else name="arrow-right" color="#AAA"></u-icon>
</view>
</u-form-item>
</u--form>
</view>
<!-- 赶进度先注释 -->
<!-- <view class="health-select" v-show="showHealths">
<view style="padding: 10px;height: 85%;">
<u-checkbox-group v-model="petHealthInfo.healthStatus" @change="checkboxChange" placement="column"
activeColor="#ffbf60">
<u-checkbox :customStyle="{marginBottom: '8px'}" v-for="(item, index) in healthData" :key="index"
:label="item" :name="item">
</u-checkbox>
</u-checkbox-group>
<u--input placeholder="请输入其他健康特征"
:disabled="!(petHealthInfo.healthStatus && petHealthInfo.healthStatus.includes('其他'))"
border="surround" maxlength='20' :customStyle="{backgroundColor: '#fff'}"
@change="updatePetHealthInfo()" v-model="petHealthInfo.remark"></u--input>
</view>
</view> -->
<u-picker :show="showVaccine" :columns="vaccineActions" @cancel="showVaccine = false"
:immediateChange="true" @confirm="vaccineSelect"></u-picker>
<u-picker :show="showExpelling" :columns="expellingActions" @cancel="showExpelling = false"
:immediateChange="true" @confirm="expellingSelect"></u-picker>
<u-picker :show="showSterilization" :columns="sterilizationActions"
@cancel="showSterilization = false" :immediateChange="true" @confirm="sterilizationSelect"></u-picker>
<u-picker :show="showDog" :columns="dogActions" @cancel="showDog = false"
:immediateChange="true" @confirm="dogSelect"></u-picker>
</view>
</template>
<script setup>
import {
ref,
onMounted,
watch,
computed
} from 'vue';
// props
const props = defineProps({
petType: {
type: String,
default: 'dog'
},
petHealthInfo: {
type: Object,
default: () => ({
vaccine: '',
deworm: '',
neutered: '',
petCard: '',
health: [],
remark: ''
})
}
});
// emits
const emits = defineEmits(['update:petHealthInfo', 'updatePetHealthInfo']);
//
const showVaccine = ref(false);
const showExpelling = ref(false);
const showSterilization = ref(false);
const showDog = ref(false);
const showHealths = ref(false);
const vaccineActions = ref([
['每年都免疫', '有免疫史', '未免疫']
]);
const expellingActions = ref([
['未驱虫', '定期驱虫', '有驱虫史']
]);
const sterilizationActions = ref([
['已绝育', '未绝育']
]);
const dogActions = ref([
['是', '否']
]);
const healthData = ref([]);
const tempHealths = ref([]);
const uForm = ref(null);
const item1 = ref(null);
const vaccineText = computed(() => {
const vaccineMap = {
0: '每年都免疫',
1: '有免疫史',
2: '未免疫'
};
return vaccineMap[props.petHealthInfo.vaccine] || ''
});
const dewormText = computed(() => {
const dewormMap = {
0: '未驱虫',
1: '定期驱虫',
2: '有驱虫史'
};
return dewormMap[props.petHealthInfo.deworm] || ''
});
const neuteredText = computed(() => {
const neuteredMap = {
0: '已绝育',
1: '未绝育'
};
return neuteredMap[props.petHealthInfo.neutered] || ''
});
const petCartText = computed(() => {
const petCartMap = {
0: '是',
1: '否'
};
return petCartMap[props.petHealthInfo.petCard] || ''
});
//
const getHealthDataList = () => {
// getDictList('pet_health_status').then(res => {
// if (res.code === 200) {
// healthData.value = res.data.map(e => e.dictLabel);
// } else {
// uni.showToast({
// title: '',
// icon: 'none'
// });
// }
// });
};
const vaccineSelect = (e) => {
props.petHealthInfo.vaccine = e.indexs[0];
showVaccine.value = false;
updatePetHealthInfo();
};
const expellingSelect = (e) => {
props.petHealthInfo.deworm = e.indexs[0];
showExpelling.value = false;
updatePetHealthInfo();
};
const sterilizationSelect = (e) => {
props.petHealthInfo.neutered = e.indexs[0];
showSterilization.value = false;
updatePetHealthInfo();
};
const dogSelect = (e) => {
props.petHealthInfo.petCard = e.indexs[0];
showDog.value = false;
updatePetHealthInfo();
};
const healthsSelect = () => {
showHealths.value = !showHealths.value;
uni.pageScrollTo({
scrollTop: 2000,
duration: 300
});
};
const checkboxChange = (n) => {
updatePetHealthInfo();
};
const changeRemark = (e) => {
props.petHealthInfo.remark = e;
updatePetHealthInfo();
};
const updatePetHealthInfo = (map) => {
// update petHealthInfo
emits('update:petHealthInfo', props.petHealthInfo);
};
//
onMounted(() => {
// getHealthDataList();
// defaultVaccine.value = vaccineActions.value[0].indexOf(props.petHealthInfo.vaccineStatus);
// defaultDeworming.value = expellingActions.value[0].indexOf(props.petHealthInfo.dewormingStatus);
// defaultSterilization.value = sterilizationActions.value[0].indexOf(props.petHealthInfo.sterilization);
// defaultDoglicense.value = dogActions.value[0].indexOf(props.petHealthInfo.doglicenseStatus);
});
</script>
<style lang="scss" scoped>
.personal-pet-health-info {
background-color: #fff;
padding: 10px 20px 0 20px;
margin-top: 10px;
}
.health-select {
background-color: #fffcf2;
padding: 10px 20px;
}
.dog-tips {
color: #A94F20;
font-size: 12px;
display: flex;
align-items: baseline;
margin-top: 5px;
}
</style>

+ 2
- 0
otherPages/userManage/pet/index.vue View File

@ -72,6 +72,8 @@
<u-modal :show="showDel" @confirm="confirmDel" @cancel="cancelDel" ref="uModal" showCancelButton
:asyncClose="true" :content="delContent"></u-modal>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>


+ 738
- 23
otherPages/userManage/pet/petInfo.vue View File

@ -13,9 +13,171 @@
</view>
</view>
<PetBaseInfo :petType="petType" v-model:petBaseInfo="petBaseInfo" />
<PetHealthInfo :petType="petType" v-model:petHealthInfo="petHealthInfo"
@upPetHealthInfo="updatePetHealthInfo"/>
<!-- 宠物基本信息 -->
<view class="personal-pet-basic-info">
<view class="personal-pet-info-title border-bottom">
宠物基本信息
</view>
<u--form labelPosition="left" :model="petBaseInfo" ref="uForm">
<u-form-item required label="昵称" :prop="`petBaseInfo.nickName`" labelWidth="80" borderBottom>
<u--input v-model="petBaseInfo.nickName" placeholder="请输入宠物昵称"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
</u-form-item>
<u-form-item label="性别" :prop="`petBaseInfo.sex`" labelWidth="80" borderBottom @click="showSex = true">
<u--input v-model="sexText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item required label="品种" :prop="`petBaseInfo.type`" labelWidth="80" borderBottom>
<u--input v-model="petBaseInfo.type" placeholder="请输入"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item required label="体重" :prop="`petBaseInfo.weight`" labelWidth="80" borderBottom ref="item1">
<u--input v-model="petBaseInfo.weight" placeholder="请输入(单位kg)"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right" type="number"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item label="出生年月" :prop="`petBaseInfo.birthday`" labelWidth="80" borderBottom
@click="yearMonthOpen">
<u--input v-model="petBaseInfo.birthday" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item required label="性格" :prop="`petBaseInfo.personality`" labelWidth="80" borderBottom
@click="dispositionSelectOpen">
<u--input v-model="personalityText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
</u--form>
</view>
<!-- 性格选择弹窗 -->
<view style="background-color: #fffcf2;padding: 10px 20px;" v-show="showDisposition">
<view style="height: 85%;">
<u-checkbox-group v-model="tempPersonality" @change="personalityCheckboxChange" iconPlacement="left"
placement="row" style="flex-wrap: wrap;" activeColor="#ffbf60">
<u-checkbox :customStyle="{margin: '8px'}" v-for="(item, index) in dispositionActions" :key="item"
:label="item" :name="item">
</u-checkbox>
</u-checkbox-group>
<view class="personal-pet-disposition-btns">
<view class="personal-pet-disposition-btn">
<u-button color="#FFF4E4" @click="dispositionSelectClose">
<view style="color: #A9A9A9;">
取消
</view>
</u-button>
</view>
<view class="personal-pet-disposition-btn" @click="dispositionSelectConfirm">
<u-button color="#FFBF60">
<view style="color: #fff;">
确定
</view>
</u-button>
</view>
</view>
</view>
</view>
<!-- 宠物健康信息 -->
<view class="personal-pet-health-info">
<view class="personal-pet-info-title border-bottom">
宠物健康情况
</view>
<u--form labelPosition="left" :model="petHealthInfo" ref="uHealthForm">
<u-form-item required label="疫苗" :prop="`petHealthInfo.vaccine`" labelWidth="80" borderBottom
@click="showVaccine = true">
<u--input v-model="vaccineText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item required label="驱虫" :prop="`petHealthInfo.deworm`" labelWidth="80" borderBottom
@click="showExpelling = true">
<u--input v-model="dewormText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item label="绝育" :prop="`petHealthInfo.neutered`" labelWidth="80" borderBottom
@click="showSterilization = true">
<u--input v-model="neuteredText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<u-form-item v-if="petType === '1' || petType === '狗狗' " label="狗证" :prop="`petHealthInfo.petCard`"
labelWidth="80" borderBottom @click="showDog = true">
<u--input v-model="petCartText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<u-icon slot="right" name="arrow-right" color="#AAA"></u-icon>
</u-form-item>
<view class="dog-tips" v-if="petType === 'dog' || petType === '狗狗'">
<u-icon name="info-circle" color="#A94F20" size="12"></u-icon>
<view style="margin-left: 3px;">
未办理养犬许可证且需要外出遛狗, 犬只存在被相关单位收缴
甚至捕杀的可能请您遵守当地养犬规范合法文明养犬具体
请您查看当地养犬条例
</view>
</view>
<u-form-item required label="健康" :prop="`petHealthInfo.health`" labelWidth="80" @click="healthsSelect">
<u--input v-model="healthText" disabled disabledColor="#ffffff" placeholder="请选择"
placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input>
<view slot="right">
<u-icon v-if="showHealths" name="arrow-down" color="#AAA"></u-icon>
<u-icon v-else name="arrow-right" color="#AAA"></u-icon>
</view>
</u-form-item>
</u--form>
</view>
<!-- 健康选择弹窗 -->
<view class="health-select" v-show="showHealths">
<view style="padding: 10px;height: 85%;">
<u-checkbox-group v-model="tempHealthStatus" @change="healthCheckboxChange" placement="column"
activeColor="#ffbf60">
<u-checkbox :customStyle="{marginBottom: '8px'}" v-for="(item, index) in healthData" :key="index"
:label="item" :name="item">
</u-checkbox>
</u-checkbox-group>
<!-- 只有选择了"其他"才显示输入框 -->
<view v-if="tempHealthStatus && tempHealthStatus.includes('其他')" style="margin-top: 10px;">
<u--input placeholder="请输入其他健康特征"
border="surround" maxlength='20'
:customStyle="{backgroundColor: '#fff', borderColor: '#ffbf60'}"
v-model="tempRemark"></u--input>
</view>
<view class="health-select-btns">
<view class="health-select-btn">
<u-button color="#FFF4E4" @click="healthSelectClose">
<view style="color: #A9A9A9;">
取消
</view>
</u-button>
</view>
<view class="health-select-btn" @click="healthSelectConfirm">
<u-button color="#FFBF60">
<view style="color: #fff;">
确定
</view>
</u-button>
</view>
</view>
</view>
</view>
<!-- 各种选择器 -->
<u-picker :show="showSex" :columns="sexActions" @cancel="showSex = false" @confirm="sexSelect"></u-picker>
<u-picker :show="showVaccine" :columns="vaccineActions" @cancel="showVaccine = false"
:immediateChange="true" @confirm="vaccineSelect"></u-picker>
<u-picker :show="showExpelling" :columns="expellingActions" @cancel="showExpelling = false"
:immediateChange="true" @confirm="expellingSelect"></u-picker>
<u-picker :show="showSterilization" :columns="sterilizationActions"
@cancel="showSterilization = false" :immediateChange="true" @confirm="sterilizationSelect"></u-picker>
<u-picker :show="showDog" :columns="dogActions" @cancel="showDog = false"
:immediateChange="true" @confirm="dogSelect"></u-picker>
<u-datetime-picker :maxDate='getMaxDate()' :minDate="getMinDate()" :show="showBirthday" v-model="tempBirthday"
mode="year-month" @confirm="yearMonthConfirm" @cancel="yearMonthClose"></u-datetime-picker>
<view class="personal-pet-info-btns">
<view class="personal-pet-btns">
@ -44,7 +206,8 @@
<script setup>
import {
ref,
onMounted
onMounted,
computed
} from 'vue';
import {
addPet,
@ -52,8 +215,6 @@
updatePet,
delByPetId
} from '@/api/pet/index.js';
import PetBaseInfo from './components/petBaseInfo.vue';
import PetHealthInfo from './components/petHealthInfo.vue';
import {
useRoute,
useRouter
@ -74,6 +235,8 @@
const isNewOrder = ref(false);
const delContent = ref('');
const headImage = ref('');
//
const petBaseInfo = ref({
nickName: '',
sex: '',
@ -82,6 +245,8 @@
birthday: '',
personality: ''
});
//
const petHealthInfo = ref({
vaccine: '',
deworm: '',
@ -90,8 +255,133 @@
health: '',
remark: ''
});
const showDel = ref(false);
const uModal = ref(null);
//
const showSex = ref(false);
const showBirthday = ref(false);
const showDisposition = ref(false);
const sexActions = ref([
['男生', '女生']
]);
const dispositionActions = ref([
'友善热情', '害羞胆怯', '比较顽皮', '粘人乖巧', '顽皮捣蛋', '内向胆小', '有攻击性'
]);
const tempBirthday = ref('');
const tempPersonality = ref([]);
const uForm = ref(null);
const item1 = ref(null);
//
const showVaccine = ref(false);
const showExpelling = ref(false);
const showSterilization = ref(false);
const showDog = ref(false);
const showHealths = ref(false);
const vaccineActions = ref([
['每年都免疫', '有免疫史', '未免疫']
]);
const expellingActions = ref([
['未驱虫', '定期驱虫', '有驱虫史']
]);
const sterilizationActions = ref([
['已绝育', '未绝育']
]);
const dogActions = ref([
['是', '否']
]);
const healthData = ref([
'身体健康,无异常',
'近3个月有做过手术',
'有过往病史、过敏史',
'宠物食欲及肠胃不佳',
'宠物部分身体部位有受伤',
'宠物皮肤敏感或正患有皮肤疾病',
'其他'
]);
const tempHealthStatus = ref([]);
const tempRemark = ref('');
const uHealthForm = ref(null);
//
const sexText = computed(() => {
const sexMap = {
0: '男生',
1: '女生'
};
return sexMap[petBaseInfo.value.sex] || '';
});
const personalityText = computed(() => {
if (petBaseInfo.value.personality) {
//
if (typeof petBaseInfo.value.personality === 'string') {
return petBaseInfo.value.personality;
}
//
if (Array.isArray(petBaseInfo.value.personality) && petBaseInfo.value.personality.length > 0) {
return petBaseInfo.value.personality.join(',');
}
}
return '';
});
const vaccineText = computed(() => {
const vaccineMap = {
0: '每年都免疫',
1: '有免疫史',
2: '未免疫'
};
return vaccineMap[petHealthInfo.value.vaccine] || ''
});
const dewormText = computed(() => {
const dewormMap = {
0: '未驱虫',
1: '定期驱虫',
2: '有驱虫史'
};
return dewormMap[petHealthInfo.value.deworm] || ''
});
const neuteredText = computed(() => {
const neuteredMap = {
0: '已绝育',
1: '未绝育'
};
return neuteredMap[petHealthInfo.value.neutered] || ''
});
const petCartText = computed(() => {
const petCartMap = {
0: '是',
1: '否'
};
return petCartMap[petHealthInfo.value.petCard] || ''
});
const healthText = computed(() => {
if (petHealthInfo.value.health) {
let healthDisplay = '';
// 使
if (typeof petHealthInfo.value.health === 'string') {
healthDisplay = petHealthInfo.value.health;
}
//
else if (Array.isArray(petHealthInfo.value.health) && petHealthInfo.value.health.length > 0) {
healthDisplay = petHealthInfo.value.health.join(',');
}
// ""
if (healthDisplay.includes('其他') && petHealthInfo.value.remark) {
healthDisplay = healthDisplay.replace('其他', `其他:${petHealthInfo.value.remark}`);
}
return healthDisplay;
}
return '';
});
onLoad((option) => {
petType.value = option.petType;
@ -132,13 +422,6 @@
}
};
function updatePetHealthInfo(data){
// petHealthInfo.value = {
// ...petHealthInfo.value,
// ...data
// }
}
const uploadFilePromise = (url) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
@ -181,25 +464,65 @@
health,
remark
} = res.data;
console.log('=== 后端返回的原始数据 ===');
console.log('birthday:', birthday);
console.log('personality:', personality);
console.log('health:', health);
console.log('remark:', remark);
// "yyyy-MM-dd HH:mm:ss" "yyyy-MM"
let processedBirthday = '';
if (birthday) {
if (typeof birthday === 'string' && birthday.length >= 7) {
processedBirthday = birthday.substring(0, 7); // 7 "yyyy-MM"
} else {
processedBirthday = birthday;
}
}
//
let processedPersonality = '';
if (personality) {
//
processedPersonality = personality.replace(/,/g, ',');
}
//
let processedHealth = '';
if (health) {
//
processedHealth = health.replace(/,/g, ',');
}
console.log('=== 处理后的数据 ===');
console.log('processedBirthday:', processedBirthday);
console.log('processedPersonality:', processedPersonality);
console.log('processedHealth:', processedHealth);
petBaseInfo.value = {
nickName,
sex,
type,
weight,
birthday,
personality
birthday: processedBirthday,
personality: processedPersonality
};
petHealthInfo.value = {
vaccine,
deworm,
neutered,
petCard,
health,
health: processedHealth,
remark
};
fileList.value = [{
url: headImage
}];
console.log('=== 最终设置的数据 ===');
console.log('petBaseInfo:', petBaseInfo.value);
console.log('petHealthInfo:', petHealthInfo.value);
} else {
uni.showToast({
title: '获取pet失败',
@ -210,7 +533,260 @@
});
};
//
const sexSelect = (e) => {
petBaseInfo.value.sex = e.indexs[0];
showSex.value = false;
console.log('性别选择:', petBaseInfo.value.sex);
};
const yearMonthOpen = () => {
if (petBaseInfo.value.birthday) {
tempBirthday.value = Number(new Date(petBaseInfo.value.birthday));
} else {
tempBirthday.value = Number(new Date());
}
showBirthday.value = true;
};
const yearMonthClose = () => {
showBirthday.value = false;
};
const yearMonthConfirm = (e) => {
const timeFormat = uni.$u.timeFormat;
petBaseInfo.value.birthday = timeFormat(e.value, 'yyyy-mm');
showBirthday.value = false;
console.log('生日选择:', petBaseInfo.value.birthday);
};
const dispositionSelectOpen = () => {
// 使
console.log('=== 打开性格选择弹窗 ===');
console.log('当前性格数据:', petBaseInfo.value.personality, '类型:', typeof petBaseInfo.value.personality);
if (typeof petBaseInfo.value.personality === 'string' && petBaseInfo.value.personality.trim() !== '') {
//
let personalityArray = petBaseInfo.value.personality.split(/[,,]/).map(item => item.trim()).filter(item => item !== '');
tempPersonality.value = personalityArray;
console.log('从字符串解析的性格数组:', personalityArray);
} else if (Array.isArray(petBaseInfo.value.personality)) {
tempPersonality.value = petBaseInfo.value.personality || [];
console.log('直接使用数组:', petBaseInfo.value.personality);
} else {
tempPersonality.value = [];
console.log('设置为空数组');
}
console.log('最终tempPersonality.value:', tempPersonality.value);
console.log('=== 性格弹窗准备完成 ===');
showDisposition.value = true;
};
const dispositionSelectClose = () => {
//
if (typeof petBaseInfo.value.personality === 'string' && petBaseInfo.value.personality.trim() !== '') {
let personalityArray = petBaseInfo.value.personality.split(/[,,]/).map(item => item.trim()).filter(item => item !== '');
tempPersonality.value = personalityArray;
} else {
tempPersonality.value = [];
}
showDisposition.value = false;
};
const dispositionSelectConfirm = () => {
//
console.log('=== 性格选择确认开始 ===');
console.log('tempPersonality.value:', tempPersonality.value);
console.log('tempPersonality类型:', typeof tempPersonality.value);
console.log('tempPersonality长度:', tempPersonality.value?.length);
//
if (!tempPersonality.value || tempPersonality.value.length === 0) {
uni.showToast({
title: '请至少选择一个性格特征!',
duration: 2000,
icon: "none"
});
return;
}
const personalityString = tempPersonality.value.join(',');
console.log('转换后的personalityString:', personalityString);
console.log('personalityString长度:', personalityString.length);
petBaseInfo.value.personality = personalityString;
// tempPersonalitypetBaseInfo.personality使
console.log('保存后petBaseInfo.value.personality:', petBaseInfo.value.personality);
console.log('保存后personalityText.value:', personalityText.value);
console.log('=== 性格选择确认完成 ===');
showDisposition.value = false;
};
const personalityCheckboxChange = (n) => {
console.log('性格checkbox变化:', n, tempPersonality.value);
};
const getMaxDate = () => {
return Date.now();
};
const getMinDate = () => {
let dt = new Date();
dt.setFullYear(dt.getFullYear() - 30);
return Date.parse(dt);
};
//
const vaccineSelect = (e) => {
petHealthInfo.value.vaccine = e.indexs[0];
showVaccine.value = false;
console.log('疫苗选择:', petHealthInfo.value.vaccine);
};
const expellingSelect = (e) => {
petHealthInfo.value.deworm = e.indexs[0];
showExpelling.value = false;
console.log('驱虫选择:', petHealthInfo.value.deworm);
};
const sterilizationSelect = (e) => {
petHealthInfo.value.neutered = e.indexs[0];
showSterilization.value = false;
console.log('绝育选择:', petHealthInfo.value.neutered);
};
const dogSelect = (e) => {
petHealthInfo.value.petCard = e.indexs[0];
showDog.value = false;
console.log('狗证选择:', petHealthInfo.value.petCard);
};
const healthsSelect = () => {
//
console.log('=== 打开健康选择弹窗 ===');
console.log('当前健康数据:', petHealthInfo.value.health, '类型:', typeof petHealthInfo.value.health);
console.log('当前备注数据:', petHealthInfo.value.remark);
if (typeof petHealthInfo.value.health === 'string' && petHealthInfo.value.health.trim() !== '') {
//
let healthArray = petHealthInfo.value.health.split(/[,,]/).map(item => item.trim()).filter(item => item !== '');
// "xxx"
let processedHealthArray = [];
let otherRemark = '';
healthArray.forEach(item => {
if (item.startsWith('其他:')) {
processedHealthArray.push('其他');
otherRemark = item.substring(3); // ""
} else {
processedHealthArray.push(item);
}
});
tempHealthStatus.value = processedHealthArray;
// ""使
tempRemark.value = otherRemark || petHealthInfo.value.remark || '';
console.log('从字符串解析的健康数组:', processedHealthArray);
console.log('解析出的其他备注:', otherRemark);
} else if (Array.isArray(petHealthInfo.value.health)) {
tempHealthStatus.value = petHealthInfo.value.health || [];
tempRemark.value = petHealthInfo.value.remark || '';
console.log('直接使用数组:', petHealthInfo.value.health);
} else {
tempHealthStatus.value = [];
tempRemark.value = petHealthInfo.value.remark || '';
console.log('设置为空数组');
}
console.log('最终tempHealthStatus.value:', tempHealthStatus.value);
console.log('最终tempRemark.value:', tempRemark.value);
console.log('=== 健康弹窗准备完成 ===');
showHealths.value = true;
uni.pageScrollTo({
scrollTop: 2000,
duration: 300
});
};
const healthSelectClose = () => {
tempHealthStatus.value = [];
tempRemark.value = '';
showHealths.value = false;
};
const healthSelectConfirm = () => {
//
console.log('=== 健康选择确认开始 ===');
console.log('tempHealthStatus.value:', tempHealthStatus.value);
console.log('tempHealthStatus类型:', typeof tempHealthStatus.value);
console.log('tempHealthStatus长度:', tempHealthStatus.value?.length);
console.log('tempRemark.value:', tempRemark.value);
console.log('tempRemark是否有值:', tempRemark.value && tempRemark.value.trim() !== '');
//
if (!tempHealthStatus.value || tempHealthStatus.value.length === 0) {
uni.showToast({
title: '请至少选择一个健康状态!',
duration: 2000,
icon: "none"
});
return;
}
let healthArray = [...tempHealthStatus.value];
console.log('原始healthArray:', healthArray);
console.log('是否包含其他:', healthArray.includes('其他'));
// "" - """"
if (healthArray.includes('其他') && tempRemark.value && tempRemark.value.trim() !== '') {
const otherIndex = healthArray.indexOf('其他');
const newOtherValue = `其他:${tempRemark.value.trim()}`;
healthArray[otherIndex] = newOtherValue;
console.log('替换其他选项:', newOtherValue);
}
const healthString = healthArray.join(',');
console.log('转换后的healthString:', healthString);
console.log('healthString长度:', healthString.length);
petHealthInfo.value.health = healthString;
petHealthInfo.value.remark = tempRemark.value; // remark
console.log('保存后petHealthInfo.value.health:', petHealthInfo.value.health);
console.log('保存后petHealthInfo.value.remark:', petHealthInfo.value.remark);
console.log('保存后healthText.value:', healthText.value);
console.log('=== 健康选择确认完成 ===');
showHealths.value = false;
};
const healthCheckboxChange = (n) => {
console.log('=== 健康checkbox变化 ===');
console.log('新值:', n);
console.log('tempHealthStatus.value:', tempHealthStatus.value);
console.log('是否包含其他:', tempHealthStatus.value.includes('其他'));
// ""
if (!tempHealthStatus.value.includes('其他')) {
tempRemark.value = '';
console.log('取消选择其他,清空备注');
}
console.log('当前备注:', tempRemark.value);
console.log('=== 健康checkbox变化完成 ===');
};
const save = () => {
console.log('=== 开始保存宠物信息 ===');
console.log('petBaseInfo:', petBaseInfo.value);
console.log('petHealthInfo:', petHealthInfo.value);
console.log('当前personality值:', petBaseInfo.value.personality, '长度:', petBaseInfo.value.personality?.length);
console.log('personalityText显示值:', personalityText.value);
if (!(fileList.value.length > 0 && fileList.value[0].url)) {
uni.showToast({
title: '请上传宠物照片!',
@ -220,6 +796,68 @@
return;
}
headImage.value = fileList.value[0].url;
//
let finalPersonality = '';
// 使tempPersonality
if (tempPersonality.value && tempPersonality.value.length > 0) {
finalPersonality = tempPersonality.value.join(',');
console.log('使用tempPersonality数据:', finalPersonality);
} else if (petBaseInfo.value.personality && petBaseInfo.value.personality.trim() !== '') {
// temp使
finalPersonality = petBaseInfo.value.personality.replace(/,/g, ',');
console.log('使用petBaseInfo原有数据:', finalPersonality);
}
//
let finalHealth = '';
let finalRemark = tempRemark.value || petHealthInfo.value.remark || '';
console.log('=== 处理健康数据开始 ===');
console.log('petHealthInfo.value.health:', petHealthInfo.value.health);
console.log('tempHealthStatus.value:', tempHealthStatus.value);
console.log('tempRemark.value:', tempRemark.value);
console.log('petHealthInfo.value.remark:', petHealthInfo.value.remark);
// 使tempHealthStatus
if (tempHealthStatus.value && tempHealthStatus.value.length > 0) {
let healthArray = [...tempHealthStatus.value];
console.log('使用tempHealthStatus数据:', healthArray);
// ""
if (healthArray.includes('其他') && tempRemark.value && tempRemark.value.trim() !== '') {
const otherIndex = healthArray.indexOf('其他');
const newOtherValue = `其他:${tempRemark.value.trim()}`;
healthArray[otherIndex] = newOtherValue;
console.log('合并其他选项和备注:', newOtherValue);
}
finalHealth = healthArray.join(',');
console.log('使用tempHealthStatus生成的finalHealth:', finalHealth);
} else if (petHealthInfo.value.health && petHealthInfo.value.health.trim() !== '') {
// temp使
finalHealth = petHealthInfo.value.health.replace(/,/g, ',');
console.log('使用petHealthInfo原有数据:', finalHealth);
}
console.log('最终finalHealth:', finalHealth);
console.log('最终finalRemark:', finalRemark);
console.log('=== 处理健康数据完成 ===');
//
let finalBirthday = '';
if (petBaseInfo.value.birthday) {
// "2025-07" "2025-07-01 00:00:00"
if (petBaseInfo.value.birthday.length === 7) { // "2025-07"
finalBirthday = `${petBaseInfo.value.birthday}-01 00:00:00`;
} else {
finalBirthday = petBaseInfo.value.birthday;
}
}
console.log('原生日格式:', petBaseInfo.value.birthday);
console.log('转换后生日格式:', finalBirthday);
let params = {
...{
petType: petType.value,
@ -227,9 +865,21 @@
},
userId:userId.value,
...petBaseInfo.value,
...petHealthInfo.value
...petHealthInfo.value,
personality: finalPersonality, // personality
health: finalHealth, // health
remark: finalRemark, // remark
birthday: finalBirthday // birthday
};
console.log(params);
console.log('合并后的提交参数:', params);
console.log('其中personality值:', params.personality, '类型:', typeof params.personality, '长度:', params.personality?.length);
console.log('其中health值:', params.health, '类型:', typeof params.health, '长度:', params.health?.length);
console.log('其中remark值:', params.remark, '类型:', typeof params.remark, '长度:', params.remark?.length);
console.log('其中birthday值:', params.birthday, '类型:', typeof params.birthday);
console.log('finalPersonality:', finalPersonality);
console.log('finalHealth:', finalHealth);
console.log('finalRemark:', finalRemark);
console.log('finalBirthday:', finalBirthday);
if (!params.nickName) {
uni.showToast({
title: '请填写宠物昵称!',
@ -254,9 +904,14 @@
});
return;
}
if (!params.personality) {
if (!params.personality || params.personality.trim() === '' || params.personality === undefined || params.personality === null) {
console.log('=== 性格验证失败详情 ===');
console.log('params.personality:', params.personality);
console.log('petBaseInfo.value.personality:', petBaseInfo.value.personality);
console.log('personalityText.value:', personalityText.value);
console.log('tempPersonality.value:', tempPersonality.value);
uni.showToast({
title: '请输入宠物性格!',
title: '请选择宠物性格!',
duration: 3000,
icon: "none"
});
@ -278,9 +933,15 @@
});
return;
}
if (!params.health) {
if (!params.health || params.health.trim() === '' || params.health === undefined || params.health === null) {
console.log('=== 健康验证失败详情 ===');
console.log('params.health:', params.health);
console.log('petHealthInfo.value.health:', petHealthInfo.value.health);
console.log('healthText.value:', healthText.value);
console.log('tempHealthStatus.value:', tempHealthStatus.value);
console.log('tempRemark.value:', tempRemark.value);
uni.showToast({
title: '请填写宠物健康情况',
title: '请选择宠物健康情况',
duration: 3000,
icon: "none"
});
@ -409,7 +1070,12 @@
background-color: #fff;
margin-top: 10px;
padding: 10px 20px;
}
.personal-pet-health-info {
background-color: #fff;
padding: 10px 20px 0 20px;
margin-top: 10px;
}
}
@ -446,4 +1112,53 @@
}
}
}
.personal-pet-disposition-btns {
display: flex;
justify-content: space-between;
box-sizing: border-box;
background-color: #FFFFFF;
padding: 20rpx 20rpx 40rpx 20rpx;
left: 0;
width: 100%;
height: 160rpx;
position: fixed;
bottom: 0;
z-index: 100;
}
.personal-pet-disposition-btn {
width: 40%;
}
.health-select {
background-color: #fffcf2;
padding: 10px 20px;
}
.dog-tips {
color: #A94F20;
font-size: 12px;
display: flex;
align-items: baseline;
margin-top: 5px;
}
.health-select-btns {
display: flex;
justify-content: space-between;
box-sizing: border-box;
background-color: #FFFFFF;
padding: 20rpx 20rpx 40rpx 20rpx;
left: 0;
width: 100%;
height: 160rpx;
position: fixed;
bottom: 0;
z-index: 100;
}
.health-select-btn {
width: 40%;
}
</style>

+ 0
- 36
otherPages/userManage/record/index.scss View File

@ -1,36 +0,0 @@
.box{
width: 100vw;
height: 100vh;
.top{
width: 100vw;
height: 100rpx;
padding: 3% 4%;
background-color: #FFF;
color: #FFBF60;
font-size: 30rpx;
.line{
width: 106rpx;
height: 6rpx;
background-color: #FFBF60;
margin: 10rpx 0 0 8rpx;
}
}
.center{
width: 100vw;
height: auto;
background-color: #fff;
}
}
.box-size{
box-sizing: border-box;
}
.level{
display: flex;
}

+ 0
- 52
otherPages/userManage/record/index.vue View File

@ -1,52 +0,0 @@
<template>
<!-- <div>服务记录</div> -->
<view class="box">
<view class="top box-size">
全部记录
<view class="line" :style="{borderRadius:'3rpx'}"></view>
</view>
<view class="center mt10 pl10 pr20 pb10 box-size" v-for="item in items">
<view class="flex-b">
<view class="level">
<view>
<image src="" mode=""
:style="{width:'126rpx',height:'126rpx',borderRadius:'100%',backgroundColor:'red'}"
class="mt22 ml10"></image>
</view>
<view class="mt22 ml10">
<view class="size-30 fw700">猫小姐</view>
<view class="size-28 color-777">服务时间2023-12-10 </view>
<view class="size-28 color-777">宠物猫咪</view>
</view>
</view>
<view class="size-28 mr10 mt20" :style="{color:'#FFBF60'}">重庆市</view>
</view>
<view class="ml30 size-30 mt10">
猫咪正常饮食无异常排尿较少可能天气较热服务全程与主人视频愉快的一天
</view>
<view class="level">
<view class="ml20 mt20">
<image src="" mode=""
:style="{width:'154rpx',height:'164rpx',backgroundColor:'red',borderRadius:'16rpx'}"></image>
</view>
</view>
<view class="flex-rowr">
<view >
<image src="" mode="" :style="{width:'17rpx',height:'19rpx'}"></image>
</view>
<view class="ml10 mr20" :style="{color:'#707070',fontSize:'22rpx'}">编辑</view>
<view >
<up-icon name="edit-pen" color="#2979ff" size="58"></up-icon>
</view>
<view class="ml10" :style="{color:'#707070',fontSize:'22rpx'}">删除</view>
</view>
</view>
</view>
</template>
<script>
</script>
<style scoped lang="scss">
@import"index.scss";
</style>

+ 2
- 0
otherPages/userManage/userInfo.vue View File

@ -52,6 +52,8 @@
</view>
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</template>
<script>


+ 0
- 193
otherPages/workbenchManage/bindUser/sharing - 副本.vue View File

@ -1,193 +0,0 @@
<template>
<view class="page">
<navbar title="分享好友" leftClick @leftClick="$utils.navigateBack" />
<view class="flex content">
<view style="width: 598rpx; height: 1063rpx;">
<canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas>
</view>
<view class="flex btns">
<button class="flex btn btn-back" @click="$utils.navigateBack">返回</button>
<button plain class="flex btn btn-save" @click="saveImg" >保存到相册</button>
</view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
wxCodeImage: null,
canvas: {},
}
},
computed : {
...mapState(['configList'])
},
onLoad() {
},
onReady() {
this.fetchQrCode()
},
methods: {
async fetchQrCode() {
try {
const url = (await this.$fetch('getQrCode'))?.url
this.wxCodeImage = this.$config.aliOss.url + url
console.log('--wxCodeImage', this.wxCodeImage)
this.draw()
} catch (err) {
}
},
draw() {
uni.showLoading({
title: "拼命绘画中..."
})
wx.createSelectorQuery()
.select('#myCanvas') // canvasid
.fields({
node: true,
size: true
})
.exec(async (res) => {
const canvas = res[0].node
//
const ctx = canvas.getContext('2d')
// Canvas
const width = res[0].width
const height = res[0].height
//
const dpr = wx.getWindowInfo().pixelRatio
//dpr
// dpr 2 4
// 3 6
console.log("--dpr", dpr)
canvas.width = width * dpr
canvas.height = height * dpr
let Ratio = canvas.width / 598
this.canvas = canvas
ctx.scale(dpr, dpr)
ctx.clearRect(0, 0, width, height)
//
const bgImage = canvas.createImage()
bgImage.src = this.configList.index_lvxing || 'https://image.hhlm1688.com//upload/组3833x_1742803627396.png'
bgImage.onload = () => {
ctx.drawImage(bgImage, 0, 0, width, height)
//
const coderImage = canvas.createImage()
coderImage.src = this.wxCodeImage
coderImage.onload = () => {
const x = 197 * Ratio / dpr
const y = 562 * Ratio / dpr
const size = 206 * Ratio / dpr
ctx.drawImage(coderImage, x, y, size, size)
uni.hideLoading()
}
}
})
},
saveImg(){
this.$authorize('scope.writePhotosAlbum').then((res) => {
this.imgApi()
})
},
imgApi() {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this.canvas.width,
height: this.canvas.height,
canvas: this.canvas,
success: (res) => {
let tempFilePath = res.tempFilePath;
this.saveImgToPhone(tempFilePath)
},
fail: (err) => {
console.log('--canvasToTempFilePath--fail', err)
}
}, this);
},
saveImgToPhone(image) {
/* 获取图片的信息 */
uni.getImageInfo({
src: image,
success: function(image) {
/* 保存图片到手机相册 */
uni.saveImageToPhotosAlbum({
filePath: image.path,
success: function() {
uni.showModal({
title: '保存成功',
content: '图片已成功保存到相册',
showCancel: false
});
},
complete(res) {
console.log(res);
}
});
}
});
}
},
}
</script>
<style scoped lang="scss">
.page {
background-color: #111317;
height: 100vh;
}
.content {
margin-top: 79rpx;
flex-direction: column;
}
.btns {
justify-content: space-between;
margin-top: 53rpx;
width: 598rpx;
}
.btn {
display: inline-flex;
width: 280rpx;
height: 90rpx;
font-size: 36rpx;
line-height: 1;
color: #FFFFFF;
border-radius: 45rpx;
margin: 0;
&-back {
background-color: #4E5053;
}
&-save {
background-image: linear-gradient(to right, #02DED6, #05D9A2);
}
}
</style>

+ 3
- 0
otherPages/workbenchManage/feedingStaff/index.vue View File

@ -40,6 +40,9 @@
</view>
</view>
<submitBut text="下一步" @click="handleClick"></submitBut>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 0
- 66
otherPages/workbenchManage/list/index.vue View File

@ -1,66 +0,0 @@
<template>
<view class="container">
<view v-for="(item,index) in questions" :key="index">
<view>{{ item.name }}</view>
<up-radio-group
v-model="radioValue[index]"
placement="column"
iconPlacement="right"
>
<up-radio
:customStyle="{marginBottom: '8px'}"
v-for="(item, index) in item.option"
:key="index"
:label="item.name"
:name="item.value"
@change="radioChange"
>
</up-radio>
</up-radio-group>
</view>
<up-button text="提交1" open-type="getPhoneNumber"
@getphonenumber="handleSubmit"></up-button>
</view>
</template>
<script setup>
import {useMixin} from "@/utils/useMixin";
import {ref} from "vue";
const {questions} = useMixin()
const radioValue = ref([])
const handleSubmit = (e) => {
wx.login({
success: res => {
if (res.code) {
// code
wx.request({
url: 'https://yourserver.com/getPhoneNumber', //
method: 'POST',
data: {
code: res.code,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv
},
success: function(response) {
//
console.log(response.data.phoneNumber);
}
});
} else {
console.log('登录失败!' + res.errMsg);
}
}
});
}
</script>
<style>
.container {
padding: 20px;
font-size: 14px;
line-height: 24px;
background: #FFFFFF;
}
</style>

+ 10
- 12
pages.json View File

@ -8,7 +8,8 @@
"^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [{
"pages": [
{
"path": "pages/workbenchManage/index",
"style": {
"navigationBarTitleText": "工作台"
@ -33,9 +34,15 @@
}
},
{
"path": "pages/test/index",
"path": "pages/userManage/mine/platformManual",
"style": {
"navigationBarTitleText": "平台手册"
}
},
{
"path": "pages/userManage/mine/home_agreement",
"style": {
"navigationBarTitleText": "测试"
"navigationBarTitleText": "平台协议"
}
},
{
@ -171,9 +178,6 @@
"root": "otherPages/orderTakingManage",
//
"pages": [{
"path": "list/index"
},
{
"path": "detail/index",
"style": {
"navigationBarTitleText": "订单详情"
@ -205,9 +209,6 @@
"root": "otherPages/myOrdersManage",
//
"pages": [{
"path": "list/index"
},
{
"path": "bond/index",
"style": {
"navigationBarTitleText": "保证金"
@ -356,9 +357,6 @@
"root": "otherPages/userManage",
//
"pages": [{
"path": "list/index"
},
{
"path": "record/index",
"style": {
"navigationBarTitleText": "服务记录"


+ 2
- 0
pages/login/index.vue View File

@ -37,6 +37,8 @@
<view style="padding: 10rpx 20rpx;height: 50vh;overflow-y: scroll;" v-html="content"></view>
</up-popup>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>


+ 2
- 0
pages/login/wxUserInfo.vue View File

@ -51,6 +51,8 @@
<view class="btn" @click="submit">
确认
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>


+ 0
- 277
pages/myOrdersManage/components/petServicePopup - 副本.vue View File

@ -1,277 +0,0 @@
<template>
<up-popup :show="show" mode="bottom" @close="close" :round="10" :closeable="true">
<view class="popup-container">
<view class="popup-title">宠物服务时间列表</view>
<view class="pet-list">
<!-- 宠物列表循环 -->
<view class="pet-item" v-for="(pet, petIndex) in petList" :key="pet.id">
<view class="pet-info">
<up-image class="pet-avatar" width="50px" height="50px" :src="pet.photo" shape="circle"></up-image>
<view class="pet-details">
<view class="pet-name">{{ pet.name }}</view>
<view class="pet-breed">{{ pet.breed }} {{ pet.bodyType }}</view>
</view>
</view>
<!-- 该宠物的服务时间列表 -->
<view class="service-list">
<view class="service-item" v-for="(service, index) in pet.services" :key="index">
<view class="service-header">
<view class="service-date">{{ service.serviceDate }}</view>
<view class="service-time">{{ getServiceTimeText(service.expectServiceTime) }}</view>
</view>
<view class="service-products">
<view class="product-item" v-for="(product, pIndex) in service.products" :key="pIndex">
<view class="product-name">{{ product.productName }}</view>
<!-- <view class="product-price">{{ product.salePrice }}</view> -->
</view>
</view>
<view class="service-footer">
<up-button
type="primary"
text="查看记录"
@click="handleClock(service)"
shape="circle"
size="mini"
style="height: 60rpx;"
color="#FFAA48"></up-button>
</view>
</view>
</view>
</view>
</view>
</view>
</up-popup>
</template>
<script setup>
import { ref, defineProps, defineEmits } from 'vue';
const props = defineProps({
show: {
type: Boolean,
default: false
},
orderData: {
type: Object,
default: () => ({})
}
});
const emit = defineEmits(['close']);
const petList = ref([]);
//
const close = () => {
emit('close');
};
//
const getServiceTimeText = (timeCode) => {
const timeMap = {
'MORNING': '早上',
'AFTERNOON': '下午',
'EVENING': '晚上'
};
return timeMap[timeCode] || '未指定';
};
//
const handleClock = (service) => {
if (!props.orderData || !service) return;
let url = `/otherPages/myOrdersManage/clock/index?id=${props.orderData.orderId}&itemID=${props.orderData.id}&serviceId=${service.id}&isRead=true`
if(service){
}
uni.navigateTo({
url,
});
};
//
const processOrderData = (orderData) => {
if (!orderData || !orderData.h5OrderVO) return [];
const { orderServiceList, orderItemList, petVOList } = orderData.h5OrderVO;
if (!orderServiceList || !orderItemList || !petVOList) return [];
// ID
const petMap = {};
//
petVOList.forEach(pet => {
petMap[pet.id] = {
...pet,
services: []
};
});
// ID
const serviceMap = {};
// ID
orderServiceList.forEach(service => {
const serviceId = service.id;
const petId = service.petId;
const serviceDate = service.serviceDate;
const expectServiceTime = service.expectServiceTime;
if (!serviceMap[serviceId]) {
serviceMap[serviceId] = {
id: serviceId,
petId,
serviceDate,
expectServiceTime,
products: []
};
}
});
//
orderItemList.forEach(item => {
const serviceId = item.orderServiceId;
if (serviceMap[serviceId]) {
serviceMap[serviceId].products.push({
productName: item.productName,
salePrice: item.salePrice,
pic: item.pic,
quantity: item.quantity,
spData: item.spData ? JSON.parse(item.spData) : {}
});
}
});
//
Object.values(serviceMap).forEach(service => {
if (petMap[service.petId]) {
petMap[service.petId].services.push(service);
}
});
//
const result = Object.values(petMap);
result.forEach(pet => {
pet.services.sort((a, b) => new Date(a.serviceDate) - new Date(b.serviceDate));
});
return result;
};
//
const updateServiceList = () => {
if (props.orderData && props.orderData.h5OrderVO) {
petList.value = processOrderData(props.orderData);
}
};
//
defineExpose({
updateServiceList
});
</script>
<style scoped lang="scss">
.popup-container {
padding: 30rpx;
max-height: 70vh;
overflow-y: auto;
}
.popup-title {
font-size: 32rpx;
font-weight: bold;
text-align: center;
margin-bottom: 30rpx;
}
.pet-list {
.pet-item {
margin-bottom: 40rpx;
.pet-info {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.pet-avatar {
flex-shrink: 0;
margin-right: 20rpx;
}
.pet-details {
.pet-name {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.pet-breed {
font-size: 24rpx;
color: #666;
margin-top: 6rpx;
}
}
}
}
}
.service-list {
.service-item {
margin-bottom: 20rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
padding: 20rpx;
.service-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 10rpx;
border-bottom: 1px solid #eee;
.service-date {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.service-time {
font-size: 24rpx;
color: #666;
background-color: #f0f0f0;
padding: 4rpx 12rpx;
border-radius: 20rpx;
}
}
.service-products {
margin-bottom: 20rpx;
.product-item {
display: flex;
justify-content: space-between;
padding: 10rpx 0;
.product-name {
font-size: 26rpx;
color: #666;
}
.product-price {
font-size: 26rpx;
color: #FF530A;
}
}
}
.service-footer {
display: flex;
justify-content: flex-end;
}
}
}
</style>

+ 0
- 636
pages/myOrdersManage/components/timelineService - 副本.vue View File

@ -1,636 +0,0 @@
<template>
<view class="timeline-container">
<!-- 日期和状态标签 -->
<view class="date-header">
<view class="date-box">
<view class="date-box-color" :style="{'background-color': getTopBgColor()}"></view>
<view class="date-month-day">{{ formatDate(date).month }}-{{ formatDate(date).day }}</view>
</view>
<view class="status-tag" :class="{'status-tag-pending': status}">
<image src="/static/images/ydd/icon1.png"
mode="aspectFit"
v-if="status"
class="status-icon"></image>
<image src="/static/images/order/success.png"
mode="aspectFit"
v-else
class="status-icon"></image>
{{ status ? '待上门' : '已完成' }}
</view>
</view>
<!-- 空状态显示 -->
<view v-if="!processedList || processedList.length === 0" class="empty-state">
<text class="empty-text">暂无订单数据</text>
</view>
<!-- 时间线主体 -->
<view v-else class="timeline-body" v-for="(item, index) in processedList" :key="index">
<view class="timeline-line"></view>
<view class="time-point">
<view class="time-icon">
<image src="/static/images/order/address.png" mode="aspectFit" class="time-image"></image>
</view>
<view class="time-text">{{ item.address }}</view>
<view class="collapse-icon" @click="toggleServiceCard(index)">
{{ serviceCardCollapsed[index] ? '展开' : '收起' }} <text class="arrow" :class="{'arrow-up': !serviceCardCollapsed[index]}"></text>
</view>
</view>
<!-- 服务内容卡片 -->
<view v-if="!serviceCardCollapsed[index]" class="service-card">
<!-- 服务日期 -->
<view class="service-section">
<view class="section-title">
<view class="title-indicator"></view>
<text>服务日期</text>
<text style="margin-left: auto;font-weight: 500;font-size: 24rpx;">订单编号{{ item.orderId }}</text>
</view>
<view class="section-content date-content" :class="{bgSuccessQ : item.status}">
{{ item.fullDate }}
</view>
</view>
<!-- 陪伴对象 -->
<view class="service-section">
<view class="section-title">
<view class="title-indicator"></view>
<text>陪伴对象</text>
<view class="collapse-icon" @click="togglePetList(index)">
{{ petListCollapsed[index] ? '展开' : '收起' }} <text class="arrow" :class="{'arrow-up': !petListCollapsed[index]}"></text>
</view>
</view>
<view class="section-content pet-list" :class="{bgSuccessQ : item.status}" v-if="!petListCollapsed[index]">
<view v-for="(pet, i) in item.petList" :key="i" class="pet-item">
<view class="pet-avatar">
<image :src="pet.avatar" mode="aspectFill" class="avatar-image"></image>
</view>
<view class="pet-info">
<view class="pet-name">
{{ pet.name }}
<text class="pet-gender" :class="{'pet-gender-male': pet.gender === 'male', 'pet-gender-female': pet.gender === 'female'}">
{{ pet.gender === 'male' ? '♂' : '♀' }}
</text>
</view>
<view class="pet-description">
{{ pet.breed }}{{ pet.bodyType }} | {{ pet.services.join('+') }}
</view>
</view>
</view>
</view>
</view>
<!-- 上门地址 -->
<view class="service-section">
<view class="section-title">
<view class="title-indicator"></view>
<text>上门地址</text>
</view>
<view class="section-content address-content" :class="{bgSuccessQ : item.status}">
{{ item.addressDetail }}
</view>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<view class="btn btn-clock" :class="{bgSuccess : item.status}" @click="handleClock(item)">{{ item.status ? '打卡记录' : '打卡' }}</view>
<view class="btn btn-clock" :class="{bgSuccess : item.status}" @click="handlePetFile(item)">宠物档案</view>
<view class="btn btn-clock" :class="{bgSuccess : item.status}" @click="handleServiceFile(item)">服务档案</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import { getOrderServiceText, getProductNameText } from '@/utils/serviceTime.js';
//
const props = defineProps({
date: {
type: String,
default: '2024-12-08'
},
orderCount: {
type: Number,
default: 2
},
status : {
type: Boolean,
default: true
},
current: {
type: Number,
default: 0
},
list: {
type: Array,
default: () => []
}
});
// - 使
const petListCollapsed = ref([]);
// - 使
const serviceCardCollapsed = ref([]);
//
const togglePetList = (index) => {
if (petListCollapsed.value[index] === undefined) {
petListCollapsed.value[index] = true;
} else {
petListCollapsed.value[index] = !petListCollapsed.value[index];
}
};
//
const toggleServiceCard = (index) => {
if (serviceCardCollapsed.value[index] === undefined) {
serviceCardCollapsed.value[index] = true;
} else {
serviceCardCollapsed.value[index] = !serviceCardCollapsed.value[index];
}
};
//
const formatDate = (dateString) => {
const date = new Date(dateString);
return {
day: date.getDate().toString().padStart(2, '0'),
month: (date.getMonth() + 1).toString().padStart(2, '0')
};
};
//
const processedList = computed(() => {
return props.list.map(order => {
//
const petList = [];
let orderId = 0
let serviceId = 0
let status = 1
if (order.appletOrderItemDate && order.appletOrderItemDate.length > 0) {
order.appletOrderItemDate.forEach(item => {
if (item.orderServiceList && item.orderServiceList.petVo) {
const pet = item.orderServiceList.petVo;
const services = [];
//
if (item.orderItemList && item.orderItemList.length > 0) {
item.orderItemList.forEach(orderItem => {
services.push(orderItem.productName);
orderId = orderItem.orderId
});
}
serviceId = item.id
if(item.status == 0){
status = 0
}
petList.push({
name: pet.name,
serviceId : item.id,
gender: pet.gender === '男生' ? 'male' : 'female',
breed: pet.breed,
bodyType: `(${pet.bodyType})`,
services: services,
avatar: pet.photo || (pet.petType === 'dog' ? '/static/images/ydd/dog.png' : '/static/images/ydd/cat.png')
});
}
});
}
return {
id: order.orderId,
orderId,
serviceId,
status,
address: order.cityAddress,
addressDetail: order.address,
fullDate: props.date.replace(/-/g, '/'),
petList: petList
};
});
});
//
const handleClock = (item) => {
console.log(item);
//
const paths = [
`/otherPages/myOrdersManage/clock/index?id=${item.orderId}&itemID=${item.id}&serviceId=${item.serviceId}`,
`/otherPages/myOrdersManage/clock/index?isRead=true&id=${item.orderId}&itemID=${item.id}&serviceId=${item.serviceId}`,
];
uni.navigateTo({
url: props.status ? paths[0] : paths[1]
});
};
const handlePetFile = (item) => {
uni.navigateTo({
url: "/otherPages/orderTakingManage/pet/index?id=" + item.orderId
});
};
const handleServiceFile = (item) => {
uni.navigateTo({
url: "/otherPages/myOrdersManage/service/index?id=" + item.orderId
});
};
function getTopBgColor(){
return props.status ? '#FFAA48' : '#4CD964';
}
</script>
<style lang="scss" scoped>
.bgSuccess{
background-color: #4CD964 !important;
}
.bgSuccessQ{
background-color: #4CD96422 !important;
}
.timeline-container {
position: relative;
padding: 20rpx;
margin-bottom: 30rpx;
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
.empty-image {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
.empty-text {
color: #999;
font-size: 28rpx;
}
}
.date-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.date-box {
width: 80rpx;
background-color: #ffffff;
border: 2px solid #333;
border-radius: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-right: 20rpx;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
border-radius: 14rpx;
.date-box-color{
height: 20rpx;
width: 100%;
border-top-left-radius: 14rpx;
border-top-right-radius: 14rpx;
position: relative;
&::before{
content: '';
display: block;
background-color: #ddd;
width: 100%;
height: 26rpx;
top: 100%;
left: 0;
position: absolute;
}
}
.date-month-day {
position: relative;
font-size: 26rpx;
font-weight: bold;
color: #333;
height: 50rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
}
.status-tag {
background-color: #4CD96422;
color: #4CD964;
border: 4rpx solid #4CD964;
padding: 16rpx 26rpx;
border-radius: 14rpx;
font-size: 26rpx;
display: flex;
align-items: center;
position: relative;
margin-left: 20rpx;
.status-icon {
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
&::after{
content: '';
display: block;
position: absolute;
width: 0;
height: 0;
top: 50%;
transform: translateY(-50%);
left: -16rpx;
border-top: 16rpx solid transparent;
border-bottom: 16rpx solid transparent;
border-right: 16rpx solid #4CD964;
}
&::before{
content: '';
display: block;
position: absolute;
width: 0;
height: 0;
top: 50%;
transform: translateY(-50%);
left: -12rpx;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
border-right: 12rpx solid #4CD96422;
z-index: 1;
}
}
.status-tag-pending {
background-color: #FFAA4822;
color: #FFAA48;
border-color: #FFAA48;
&::after{
border-right-color: #FFAA48;
}
&::before{
border-right-color: #FFAA4822;
}
}
}
.timeline-body {
position: relative;
padding-left: 40rpx;
padding-bottom: 40rpx;
.timeline-line {
position: absolute;
left: 40rpx;
top: 0;
height: 100%;
width: 0;
border-left: 2rpx dashed #707070;
border-left-style: dashed;
border-image: repeating-linear-gradient(to bottom, #707070 0, #707070 8rpx, transparent 8rpx, transparent 20rpx) 1;
z-index: 0;
&::after{
content: '';
display: block;
position: absolute;
width: 8rpx;
height: 8rpx;
background-color: #000;
border: 2rpx solid #707070;
border-radius: 50%;
left: -7rpx;
top: 30rpx;
}
}
.time-point {
display: flex;
align-items: center;
margin-bottom: 20rpx;
position: relative;
z-index: 1;
.time-icon {
width: 60rpx;
height: 60rpx;
background-color: #fff;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
position: relative;
left: 20rpx;
.time-image {
width: 40rpx;
height: 40rpx;
}
}
.time-text {
font-size: 28rpx;
color: #333;
margin-left: 20rpx;
flex: 1;
}
.collapse-icon {
font-size: 24rpx;
color: #999;
padding: 0 20rpx;
.arrow {
transition: transform 0.3s;
display: inline-block;
}
.arrow-up {
transform: rotate(180deg);
}
}
}
.service-card {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
margin-left: 20rpx;
.service-section {
margin-bottom: 30rpx;
.section-title {
display: flex;
align-items: center;
margin-bottom: 15rpx;
.title-indicator {
width: 6rpx;
height: 30rpx;
background-color: #FFAA48;
margin-right: 15rpx;
}
text {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.collapse-icon {
margin-left: auto;
font-size: 24rpx;
color: #999;
.arrow {
transition: transform 0.3s;
display: inline-block;
}
.arrow-up {
transform: rotate(180deg);
}
}
}
.section-content {
padding: 0 15rpx;
background-color: #FFF9F0;
}
.date-content {
background-color: #FFF9F0;
padding: 20rpx;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
}
.pet-list {
padding: 15rpx;
.pet-item {
display: flex;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.pet-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
overflow: hidden;
margin-right: 20rpx;
.avatar-image {
width: 100%;
height: 100%;
}
}
.pet-info {
flex: 1;
.pet-name {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
.pet-gender {
display: inline-block;
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
border-radius: 50%;
color: #fff;
font-size: 20rpx;
margin-left: 10rpx;
}
.pet-gender-male {
background-color: #4A90E2;
}
.pet-gender-female {
background-color: #FF6B9A;
}
}
.pet-description {
font-size: 24rpx;
color: #7D8196;
}
}
}
}
.address-content {
padding: 20rpx;
border-radius: 8rpx;
font-size: 28rpx;
color: #7D8196;
}
}
.action-buttons {
display: flex;
justify-content: space-between;
.btn {
width: 30%;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 40rpx;
font-size: 28rpx;
}
.btn-clock {
background-color: #FFAA48;
color: #fff;
}
.btn-pet-file, .btn-service-file {
background-color: #F6F7FB;
color: #333;
border: 1px solid #E5E6EB;
}
}
}
}
}
</style>

+ 3
- 0
pages/myOrdersManage/index.vue View File

@ -76,6 +76,9 @@
</view>
</view>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>


+ 3
- 1
pages/myOrdersManage/index1.vue View File

@ -20,7 +20,9 @@
</view>
<up-button text="提交1" open-type="getPhoneNumber"
@getphonenumber="handleSubmit"></up-button>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>
<script setup>


+ 3
- 0
pages/orderTakingManage/index.vue View File

@ -28,6 +28,9 @@
<!-- 已登录状态 -->
<List v-else :orderList="orderlist" :current="current" @update="updateList"></List>
</view>
<!-- 客服组件 -->
<CustomerService />
</view>
</template>


+ 0
- 79
pages/test/index.vue View File

@ -1,79 +0,0 @@
<template>
<view class="base-info">
<view class="form-title">基本信息</view>
<view class="box">
<DForm :list="state.list" @submit="handleSubmit" />
</view>
</view>
</template>
<script setup>
import {
reactive
} from "vue";
import DForm from "@/components/dForm/index.vue"
const state = reactive({
list: [{
type: "input",
label: "姓名",
key: "name",
placeholder: "请输入您的真实姓名",
},
{
type: "input",
label: "身份证号",
key: "idCard",
placeholder: "请输入您的真实身份证号",
},
{
type: "radio",
label: "性别",
key: "sex",
options: [{
name: "男"
},
{
name: "女"
}
]
},
{
type: "input",
label: "年龄",
key: "gender",
placeholder: "请输入您的年龄",
},
{
type: "input",
label: "养宠经验",
key: "shij",
placeholder: "请输入您的养宠年限",
unit: "年"
},
]
})
const handleSubmit = (val) => {
console.log("获取参数", val)
}
</script>
<style scoped>
.base-info {
width: 718rpx;
margin: 0 auto;
background-color: #fff;
border-radius: 28rpx;
}
.box {
width: 680rpx;
margin: 0 auto;
background-color: #fff;
padding: 0 36rpx;
box-sizing: border-box;
}
</style>

+ 4
- 0
pages/userManage/index.scss View File

@ -54,6 +54,10 @@
display: flex;
flex-direction: column;
align-items: center;
background-color: #fff;
.image{
background-color: #fff;
}
}
.cw-box {


+ 25
- 10
pages/userManage/index.vue View File

@ -107,8 +107,10 @@
<!-- <up-image class="mb20" src="https://cdn.catmdogd.com/Work/image/work/icon1.png" width="68rpx"
height="68rpx"></up-image> -->
<up-image class="mb20" src="/static/images/user/1.png" width="58rpx"
height="58rpx"></up-image>
<image class="mb20"
src="/static/images/user/1.png"
style="width: 58rpx;height: 58rpx;"
></image>
<view>我的评价</view>
</view>
<view @click="platformProtocol" class="icon-list">
@ -116,8 +118,10 @@
src="https://cdn.catmdogd.com/Work/image/work/icon2.png" width="68rpx"
height="68rpx"></up-image> -->
<up-image class="mb20" src="/static/images/user/2.png" width="58rpx"
height="58rpx"></up-image>
<image class="mb20"
src="/static/images/user/2.png"
style="width: 58rpx;height: 58rpx;"
></image>
<view>平台协议</view>
</view>
<button plain class="btn-share" open-type="contact">
@ -126,8 +130,10 @@
src="https://cdn.catmdogd.com/Work/image/work/icon3.png" width="68rpx"
height="68rpx"></up-image> -->
<up-image class="mb20" src="/static/images/user/4.png" width="58rpx"
height="58rpx"></up-image>
<image class="mb20"
src="/static/images/user/4.png"
style="width: 58rpx;height: 58rpx;"
></image>
<view>联系客服</view>
</view>
</button>
@ -137,8 +143,10 @@
src="https://t9.baidu.com/it/u=2834693008,3232051400&fm=193" width="68rpx"
height="68rpx"></up-image> -->
<up-image class="mb20" src="/static/images/user/5.png" width="58rpx"
height="58rpx"></up-image>
<image class="mb20"
src="/static/images/user/5.png"
style="width: 58rpx;height: 58rpx;"
></image>
<view>分享好友</view>
</view>
</button>
@ -166,6 +174,9 @@
</view>
</template>
</Modal>
<!-- 客服组件 -->
<CustomerService />
</template>
<script setup>
@ -300,7 +311,7 @@
const handleWithdraw = () => {
if (!checkLoginAndRedirect()) return
uni.navigateTo({
url: "/otherPages/binding/withdrawal/index"
url: "/otherPages/binding/withdrawal/index?type=1"
})
}
@ -363,7 +374,11 @@
}
const platformProtocol = () => {
show.value = true;
uni.navigateTo({
url: '/pages/userManage/mine/home_agreement'
})
return
// show.value = true;
}
const cancelLogout = () => {


+ 29
- 0
pages/userManage/mine/home_agreement.vue View File

@ -0,0 +1,29 @@
<template>
<view class="page">
<up-parse :content="configList.home_agreement.paramValueArea"></up-parse>
</view>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
data() {
return {
}
},
computed : {
...mapGetters(['configList'])
},
methods: {
}
}
</script>
<style scoped lang="scss">
.page{
padding: 30rpx;
background-color: #fff;
}
</style>

+ 29
- 0
pages/userManage/mine/platformManual.vue View File

@ -0,0 +1,29 @@
<template>
<view class="page">
<up-parse :content="configList.pet_platform_introduction.paramValueArea"></up-parse>
</view>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
data() {
return {
}
},
computed : {
...mapGetters(['configList'])
},
methods: {
}
}
</script>
<style scoped lang="scss">
.page{
padding: 30rpx;
background-color: #fff;
}
</style>

+ 9
- 1
pages/workbenchManage/index.vue View File

@ -67,6 +67,9 @@
</view>
<ModalCom :open="isoOpen" @close="modalClose" />
<configPopup ref="configPopupRef" />
<!-- 客服组件 -->
<CustomerService />
</view>
</template>
@ -201,7 +204,12 @@
if (item.name == '平台手册') {
// pet_platform_introduction
return configPopupRef.value.open('pet_platform_introduction')
// return configPopupRef.value.open('pet_platform_introduction')
uni.navigateTo({
url: '/pages/userManage/mine/platformManual'
})
return
}
if (item.url) {


+ 1
- 0
store/modules/system.js View File

@ -36,6 +36,7 @@ const system = {
const data = await getConfigList()
commit('setConfigList', data)
} catch (err) {
}


+ 0
- 256
uni_modules/uview-plus/components/u-swipe-action-item/index - backup.wxs View File

@ -1,256 +0,0 @@
/**
* 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
* wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
*/
// 开始触摸
function touchstart(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
var state = instance.getState()
if (state.disable) return
var touches = event.touches
// 如果进行的是多指触控,不允许进行操作
if (touches && touches.length > 1) return
// 标识当前为滑动中状态
state.moving = true
// 记录触摸开始点的坐标值
state.startX = touches[0].pageX
state.startY = touches[0].pageY
}
// 触摸滑动
function touchmove(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照
var state = instance.getState()
if (state.disabled || !state.moving) return
var touches = event.touches
var pageX = touches[0].pageX
var pageY = touches[0].pageY
var moveX = pageX - state.startX
var moveY = pageY - state.startY
var buttonsWidth = state.buttonsWidth
// 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
event.preventDefault()
event.stopPropagation()
}
// 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
if (Math.abs(moveX) < Math.abs(moveY)) return
// 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
// 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
// 在超出后,设置为0
if (state.status === 'open') {
// 在开启状态下,向左滑动,需忽略
if (moveX < 0) moveX = 0
// 想要收起菜单,最大能移动的距离为按钮的总宽度
if (moveX > buttonsWidth) moveX = buttonsWidth
// 如果是已经打开了的状态,向左滑动时,移动收起菜单
moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
} else {
// 关闭状态下,右滑动需忽略
if (moveX > 0) moveX = 0
// 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
// 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
moveSwipeAction(moveX, instance, ownerInstance)
}
}
// 触摸结束
function touchend(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照
var state = instance.getState()
if (!state.moving || state.disabled) return
var touches = event.changedTouches ? event.changedTouches[0] : {}
var pageX = touches.pageX
var pageY = touches.pageY
var moveX = pageX - state.startX
if (state.status === 'open') {
// 在展开的状态下,继续左滑,无需操作
if (moveX < 0) return
// 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
if (moveX === 0) {
return closeSwipeAction(instance, ownerInstance)
}
// 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
if (Math.abs(moveX) < state.threshold) {
openSwipeAction(instance, ownerInstance)
} else {
// 如果滑动距离大于阈值,则执行收起逻辑
closeSwipeAction(instance, ownerInstance)
}
} else {
// 在关闭的状态下,右滑,无需操作
if (moveX > 0) return
// 理由同上
if (Math.abs(moveX) < state.threshold) {
closeSwipeAction(instance, ownerInstance)
} else {
openSwipeAction(instance, ownerInstance)
}
}
}
// 获取过渡时间
function getDuration(value) {
if (value.toString().indexOf('s') >= 0) return value
return value > 30 ? value + 'ms' : value + 's'
}
// 滑动结束时判断滑动的方向
function getMoveDirection(instance, ownerInstance) {
var state = instance.getState()
}
// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
function moveSwipeAction(moveX, instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
var len = buttons.length
var previewButtonsMoveX = 0
// 设置菜单内容部分的偏移
instance.requestAnimationFrame(function() {
instance.setStyle({
// 设置translateX的值
'transition': 'none',
transform: 'translateX(' + moveX + 'px)',
'-webkit-transform': 'translateX(' + moveX + 'px)'
})
// 折叠按钮动画
for (var i = len - 1; i >= 0; i--) {
// 通过比例,得出元素自身该移动的距离
var translateX = state.buttons[i].width / state.buttonsWidth * moveX
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
var realTranslateX = translateX + previewButtonsMoveX
buttons[i].setStyle({
// 在移动期间,不能使用过渡效果,否则会造成卡顿,本质原因是每次移动一点,就要花一定时间去过渡这个过程
'transition': 'none',
'transform': 'translateX(' + realTranslateX + 'px)',
'-webkit-transform': 'translateX(' + realTranslateX + 'px)'
})
// 记录本按钮之前的所有按钮的移动距离之和
previewButtonsMoveX += translateX
}
})
}
// 一次性展开滑动菜单
function openSwipeAction(instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
var len = buttons.length
// 处理duration单位问题
const duration = getDuration(state.duration)
// 展开过程中,是向左移动,所以X的偏移应该为负值
var buttonsWidth = -state.buttonsWidth
var previewButtonsMoveX = 0
instance.requestAnimationFrame(function() {
// 设置菜单主体内容
instance.setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(' + buttonsWidth + 'px)',
'-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
})
// 设置各个隐藏的按钮为展开的状态
for (var i = len - 1; i >= 0; i--) {
// 通过比例,得出元素自身该移动的距离
var translateX = state.buttons[i].width / state.buttonsWidth * buttonsWidth
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
var realTranslateX = translateX + previewButtonsMoveX
buttons[i].setStyle({
// 在移动期间,需要加上动画效果
'transition': 'transform ' + duration,
'transform': 'translateX(' + realTranslateX + 'px)',
'-webkit-transform': 'translateX(' + realTranslateX + 'px)'
})
// 记录本按钮之前的所有按钮的移动距离之和
previewButtonsMoveX += translateX
}
})
setStatus('open', instance)
}
// 标记菜单的当前状态,open-已经打开,close-已经关闭
function setStatus(status, instance) {
var state = instance.getState()
state.status = status
}
// 一次性收起滑动菜单
function closeSwipeAction(instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
var len = buttons.length
// 处理duration单位问题
const duration = getDuration(state.duration)
instance.requestAnimationFrame(function() {
// 设置菜单主体内容
instance.setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(0px)',
'-webkit-transform': 'translateX(0px)'
})
// 设置各个隐藏的按钮为收起的状态
for (var i = len - 1; i >= 0; i--) {
buttons[i].setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(0px)',
'-webkit-transform': 'translateX(0px)'
})
}
})
setStatus('close', instance)
}
// show的状态发生变化
function showChange(newValue, oldValue, ownerInstance, instance) {
var state = instance.getState()
if (state.disabled) return
// 打开或关闭单元格
if (newValue) {
openSwipeAction(instance, ownerInstance)
} else {
closeSwipeAction(instance, ownerInstance)
}
}
// 菜单尺寸发生变化
function sizeChange(newValue, oldValue, ownerInstance, instance) {
// wxs内的局部变量快照
var state = instance.getState()
state.disabled = newValue.disabled
state.duration = newValue.duration
state.show = newValue.show
state.threshold = newValue.threshold
state.buttons = newValue.buttons
var len = state.buttons.length
if (len) {
var buttonsWidth = 0
var buttons = newValue.buttons
for (var i = 0; i < len; i++) {
buttonsWidth += buttons[i].width
}
}
state.buttonsWidth = buttonsWidth
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
sizeChange: sizeChange
}

+ 0
- 270
uni_modules/uview-plus/components/u-swipe-action-item/nvue - backup.js View File

@ -1,270 +0,0 @@
// nvue操作dom的库,用于获取dom的尺寸信息
const dom = uni.requireNativePlugin('dom')
// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue
const animation = uni.requireNativePlugin('animation')
import { sleep } from '../../libs/function/index';
export default {
data() {
return {
// 是否滑动中
moving: false,
// 状态,open-打开状态,close-关闭状态
status: 'close',
// 开始触摸点的X和Y轴坐标
startX: 0,
startY: 0,
// 所有隐藏按钮的尺寸信息数组
buttons: [],
// 所有按钮的总宽度
buttonsWidth: 0,
// 记录上一次移动的位置值
moveX: 0,
// 记录上一次滑动的位置,用于前后两次做对比,如果移动的距离小于某一阈值,则认为前后之间没有移动,为了解决可能存在的通信阻塞问题
lastX: 0
}
},
computed: {
// 获取过渡时间
getDuratin() {
let duration = String(this.duration)
// 如果ms为单位,返回ms的数值部分
if (duration.indexOf('ms') >= 0) return parseInt(duration)
// 如果s为单位,为了得到ms的数值,需要乘以1000
if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
// 如果值传了数值,且小于30,认为是s单位
duration = Number(duration)
return duration < 30 ? duration * 1000 : duration
}
},
watch: {
show: {
immediate: true,
handler(n) {
// if(n === true) {
// sleep(50).then(() => {
// this.openSwipeAction()
// })
// } else {
// this.closeSwipeAction()
// }
}
}
},
mounted() {
sleep(20).then(() => {
this.queryRect()
})
},
methods: {
close() {
this.closeSwipeAction()
},
// 触摸单元格
touchstart(event) {
if (this.disabled) return
this.closeOther()
const { touches } = event
// 记录触摸开始点的坐标值
this.startX = touches[0].pageX
this.startY = touches[0].pageY
},
// // 触摸滑动
touchmove(event) {
if (this.disabled) return
const { touches } = event
const { pageX } = touches[0]
const { pageY } = touches[0]
let moveX = pageX - this.startX
const moveY = pageY - this.startY
const { buttonsWidth } = this
const len = this.buttons.length
// 判断前后两次的移动距离,如果小于一定值,则不进行移动处理
if (Math.abs(pageX - this.lastX) < 0.3) return
this.lastX = pageX
// 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) {
event.stopPropagation()
}
// 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
if (Math.abs(moveX) < Math.abs(moveY)) return
// 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
// 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
// 在超出后,设置为0
if (this.status === 'open') {
// 在开启状态下,向左滑动,需忽略
if (moveX < 0) moveX = 0
// 想要收起菜单,最大能移动的距离为按钮的总宽度
if (moveX > buttonsWidth) moveX = buttonsWidth
// 如果是已经打开了的状态,向左滑动时,移动收起菜单
this.moveSwipeAction(-buttonsWidth + moveX)
} else {
// 关闭状态下,右滑动需忽略
if (moveX > 0) moveX = 0
// 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
// 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
this.moveSwipeAction(moveX)
}
},
// 单元格结束触摸
touchend(event) {
if (this.disabled) return
const touches = event.changedTouches ? event.changedTouches[0] : {}
const { pageX } = touches
const { pageY } = touches
const { buttonsWidth } = this
this.moveX = pageX - this.startX
if (this.status === 'open') {
// 在展开的状态下,继续左滑,无需操作
if (this.moveX < 0) this.moveX = 0
if (this.moveX > buttonsWidth) this.moveX = buttonsWidth
// 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
if (this.moveX === 0) {
return this.closeSwipeAction()
}
// 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
if (Math.abs(this.moveX) < this.threshold) {
this.openSwipeAction()
} else {
// 如果滑动距离大于阈值,则执行收起逻辑
this.closeSwipeAction()
}
} else {
// 在关闭的状态下,右滑,无需操作
if (this.moveX >= 0) this.moveX = 0
if (this.moveX <= -buttonsWidth) this.moveX = -buttonsWidth
// 理由同上
if (Math.abs(this.moveX) < this.threshold) {
this.closeSwipeAction()
} else {
this.openSwipeAction()
}
}
},
// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
moveSwipeAction(moveX) {
if (this.moving) return
this.moving = true
let previewButtonsMoveX = 0
const len = this.buttons.length
animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
styles: {
transform: `translateX(${moveX}px)`
},
timingFunction: 'linear'
}, () => {
this.moving = false
})
// 按钮的组的长度
for (let i = len - 1; i >= 0; i--) {
const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
// 通过比例,得出元素自身该移动的距离
const translateX = this.buttons[i].width / this.buttonsWidth * moveX
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
const realTranslateX = translateX + previewButtonsMoveX
animation.transition(buttonRef, {
styles: {
transform: `translateX(${realTranslateX}px)`
},
duration: 0,
delay: 0,
timingFunction: 'linear'
}, () => {})
// 记录本按钮之前的所有按钮的移动距离之和
previewButtonsMoveX += translateX
}
},
// 关闭菜单
closeSwipeAction() {
if (this.status === 'close') return
this.moving = true
const { buttonsWidth } = this
animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
styles: {
transform: 'translateX(0px)'
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {
this.status = 'close'
this.moving = false
this.closeHandler()
})
// 按钮的组的长度
const len = this.buttons.length
for (let i = len - 1; i >= 0; i--) {
const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
// 如果不满足边界条件,返回
if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
animation.transition(buttonRef, {
styles: {
transform: 'translateX(0px)'
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {})
}
},
// 打开菜单
openSwipeAction() {
if (this.status === 'open') return
this.moving = true
const buttonsWidth = -this.buttonsWidth
let previewButtonsMoveX = 0
animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
styles: {
transform: `translateX(${buttonsWidth}px)`
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {
this.status = 'open'
this.moving = false
this.openHandler()
})
// 按钮的组的长度
const len = this.buttons.length
for (let i = len - 1; i >= 0; i--) {
const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
// 如果不满足边界条件,返回
if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
// 通过比例,得出元素自身该移动的距离
const translateX = this.buttons[i].width / this.buttonsWidth * buttonsWidth
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
const realTranslateX = translateX + previewButtonsMoveX
animation.transition(buttonRef, {
styles: {
transform: `translateX(${realTranslateX}px)`
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {})
previewButtonsMoveX += translateX
}
},
// 查询按钮节点信息
queryRect() {
// 历遍所有按钮数组,通过getRectByDom返回一个promise
const promiseAll = this.rightOptions.map((item, index) => this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0]))
// 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
Promise.all(promiseAll).then((sizes) => {
this.buttons = sizes
// 计算所有按钮总宽度
this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
})
},
// 通过nvue的dom模块,查询节点信息
getRectByDom(ref) {
return new Promise((resolve) => {
dom.getComponentRect(ref, (res) => {
resolve(res.size)
})
})
}
}
}

+ 103
- 21
utils/exam.js View File

@ -1,60 +1,137 @@
import dayjs from "dayjs";
// 判断题目是否为多选题
export function isMultipleChoice(question) {
// 只有选择题才有多选/单选的概念
if (question.typeAnswer !== 0) return false;
if (!question.answerList || !question.answerList.length) return false;
const correctAnswers = question.answerList.filter(option => option.isTrue === 1 || option.isTrue === true);
return correctAnswers.length > 1;
}
// 判断选项是否被选中
export function isOptionSelected(question, optionId) {
// 只有选择题才有选项的概念
if (question.typeAnswer !== 0) return false;
if (!question.userAnswerBaseList || !question.userAnswerBaseList.length) return false;
return question.userAnswerBaseList.some(answer => answer.answerId === optionId);
}
// 判断答案是否正确
export function isAnswerCorrect(question) {
if (!question.userAnswerBaseList || !question.userAnswerBaseList.length) return false;
if (question.typeAnswer === 0) {
// 选择题逻辑
if (!question.userAnswerBaseList || !question.userAnswerBaseList.length) return false;
// 获取所有正确答案的ID
const correctOptionIds = question.answerList
.filter(option => option.isTrue)
.map(option => option.id);
// 获取所有正确答案的ID
const correctOptionIds = question.answerList
.filter(option => option.isTrue === 1 || option.isTrue === true)
.map(option => option.id);
// 获取用户选择的答案ID
const userAnswerIds = [...new Set(question.userAnswerBaseList.map(answer => answer.answerId))];
// 获取用户选择的答案ID
const userAnswerIds = [...new Set(question.userAnswerBaseList.map(answer => answer.answerId))];
// 判断用户选择的答案数量是否与正确答案数量相同
if (userAnswerIds.length !== correctOptionIds.length) {
return false;
}
// 判断用户选择的答案数量是否与正确答案数量相同
if (userAnswerIds.length !== correctOptionIds.length) {
return false;
}
// 判断用户选择的答案是否都是正确的
return userAnswerIds.every(answerId => correctOptionIds.includes(answerId));
// 判断用户选择的答案是否都是正确的
return userAnswerIds.every(answerId => correctOptionIds.includes(answerId));
} else if (question.typeAnswer === 1) {
// 填空题逻辑
if (!question.userAppletAnswerTrain || !question.userAppletAnswerTrain.answer) return false;
// 如果有标准答案,进行比较
if (question.answer) {
// 简单的字符串比较,可以根据实际需求调整比较逻辑
return question.userAppletAnswerTrain.answer.trim().toLowerCase() === question.answer.trim().toLowerCase();
}
// 如果没有标准答案,只要有内容就认为是正确的
return question.userAppletAnswerTrain.answer.trim() !== '';
}
return false;
}
export function isQuestionAnswered(question) {
if (question.type == '培训') {
if (question.typeAnswer === 1) {
// 填空题:检查是否有答案且答案不为空
return question.userAppletAnswerTrain &&
question.userAppletAnswerTrain.answer &&
question.userAppletAnswerTrain.answer.trim() !== '';
} else {
} else if (question.typeAnswer === 0) {
// 选择题:检查是否有选择的答案
return question.userAnswerBaseList && question.userAnswerBaseList.length > 0;
if (!question.userAnswerBaseList || !question.userAnswerBaseList.length) return false;
// 对于多选题和单选题,都要求至少有一个答案
return question.userAnswerBaseList.length > 0;
}
return false;
}
// 获取题目的用户答案数组(支持单选和多选)
export function getUserAnswerIds(question) {
// 只有选择题才有answerId的概念
if (question.typeAnswer !== 0) return [];
if (!question.userAnswerBaseList || !question.userAnswerBaseList.length) return [];
return [...new Set(question.userAnswerBaseList.map(answer => answer.answerId))];
}
// 判断全部题目答案是否达到指定的准确率
// 获取题目的正确答案数组
export function getCorrectAnswerIds(question) {
// 只有选择题才有answerId的概念
if (question.typeAnswer !== 0) return [];
if (!question.answerList || !question.answerList.length) return [];
return question.answerList
.filter(option => option.isTrue === 1 || option.isTrue === true)
.map(option => option.id);
}
// 获取填空题的用户答案
export function getUserTextAnswer(question) {
if (question.typeAnswer !== 1) return '';
if (!question.userAppletAnswerTrain || !question.userAppletAnswerTrain.answer) return '';
return question.userAppletAnswerTrain.answer.trim();
}
// 获取填空题的正确答案
export function getCorrectTextAnswer(question) {
if (question.typeAnswer !== 1) return '';
return question.answer || '';
}
// 判断全部题目答案是否达到指定的准确率(只判断选择题)
export function isSuccessPrecision(list, number = 100){
let errNumber = 0
let totalChoiceQuestions = 0
for (var index = 0; index < list.length; index++) {
var question = list[index];
if (question.type == '基本' && !isAnswerCorrect(question)) {
errNumber++
// 只判断选择题(typeAnswer === 0),无论是基本题还是培训题
if (question.typeAnswer === 0) {
totalChoiceQuestions++
if (!isAnswerCorrect(question)) {
errNumber++
}
}
}
console.log(errNumber, (list.length - errNumber) / list.length * 100);
// 如果没有选择题,返回true
if (totalChoiceQuestions === 0) {
console.log('没有选择题需要判断');
return true
}
const accuracy = (totalChoiceQuestions - errNumber) / totalChoiceQuestions * 100
console.log('选择题总数:', totalChoiceQuestions, '答错题目数:', errNumber, '正确率:', accuracy);
if(((list.length - errNumber) / list.length * 100) < number){
if(accuracy < number){
return false
}
@ -62,8 +139,13 @@ export function isSuccessPrecision(list, number = 100){
}
export default {
isMultipleChoice,
isOptionSelected,
isAnswerCorrect,
isQuestionAnswered,
getUserAnswerIds,
getCorrectAnswerIds,
getUserTextAnswer,
getCorrectTextAnswer,
isSuccessPrecision,
}

+ 1
- 1
utils/getUrl.js View File

@ -3,7 +3,7 @@ const accountInfo = wx.getAccountInfoSync();
// current = accountInfo.miniProgram.envVersion;
const api = {
develop:"http://127.0.0.1:8002",
develop:"http://127.0.0.1:8080",
// develop:"http://h5.xzaiyp.top",
// develop:"https://api.catmdogd.com/prod-api",
trial:"https://pet-admin.hhlm1688.com/api",


+ 101
- 0
utils/shareMixin.js View File

@ -0,0 +1,101 @@
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
data() {
return {
shareConfig: {
// 默认分享配置
title: '',
desc: '',
imageUrl: '',
path: '',
query: ''
}
}
},
async onLoad(options) {
// 构建当前页面路径,包含参数
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
let path = currentPage.route
// 构建查询参数
if (options && Object.keys(options).length > 0) {
const queryString = Object.keys(options)
.map(key => `${key}=${options[key]}`)
.join('&')
path = `${path}?${queryString}`
}
this.shareConfig.path = path
this.shareConfig.query = options || {}
// 设置默认分享内容
this.setDefaultShareConfig()
},
methods: {
// 设置默认分享配置
setDefaultShareConfig() {
try {
const store = useStore()
const configList = computed(() => store.getters.configList)
// 从全局配置中获取默认分享信息
this.shareConfig.title = configList.value?.applet_info?.paramValueText || '猫妈狗爸伴宠师'
this.shareConfig.imageUrl = configList.value?.applet_info?.paramValueImage || ''
} catch (error) {
console.log('获取分享配置失败:', error)
// 设置默认值
this.shareConfig.title = '猫妈狗爸伴宠师'
this.shareConfig.imageUrl = ''
}
},
// 自定义分享配置
setShareConfig(config = {}) {
this.shareConfig = {
...this.shareConfig,
...config
}
},
// 获取分享配置
getShareConfig() {
return {
title: this.shareConfig.title,
desc: this.shareConfig.desc,
imageUrl: this.shareConfig.imageUrl,
path: this.shareConfig.path,
query: this.shareConfig.query
}
},
// 显示分享菜单
showShareMenu() {
// #ifdef MP-WEIXIN
uni.showShareMenu({
withShareTicket: true,
success: () => {
console.log('显示分享菜单成功')
},
fail: (error) => {
console.error('显示分享菜单失败:', error)
}
})
// #endif
},
},
// 微信小程序分享给朋友
onShareAppMessage(res) {
return this.getShareConfig()
},
// 微信小程序分享到朋友圈
onShareTimeline(res) {
return this.getShareConfig()
},
}

Loading…
Cancel
Save