| apps/cStandardMiniApp/src/styles/nut.scss | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| apps/housekeepingMiniApp/project.private.config.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| apps/housekeepingMiniApp/src/hooks/enterprise.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| apps/housekeepingMiniApp/src/styles/nut.scss | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| apps/housekeepingMiniApp/src/subpackages/mine/addressManange/InnerPage.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| apps/housekeepingMiniApp/src/subpackages/sercice/addStandardOrder/InnerPage.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| apps/housekeepingMiniApp/src/subpackages/sercice/serciceDetail/serciceDetail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| packages/components/src/Card/ServiceDetailAddressCard.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| packages/components/src/Input/ChooseInputWithDatePicker.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
apps/cStandardMiniApp/src/styles/nut.scss
@@ -127,6 +127,11 @@ .bole-input-textarea:not(.nut-input--disabled) { color: boleGetCssVar('text-color', 'primary') !important; height: 100rpx; .textarea-placeholder { color: boleGetCssVar('text-color', 'placeholder') !important; font-size: 26rpx; } } .bole-input-text-placeholder { apps/housekeepingMiniApp/project.private.config.json
@@ -23,11 +23,18 @@ "miniprogram": { "list": [ { "name": "选择地址", "pathName": "subpackages/mine/addressManange/addressManange", "query": "type=select", "scene": null, "launchMode": "default" }, { "name": "预约下单", "pathName": "subpackages/sercice/addStandardOrder/addStandardOrder", "query": "specNumber=1&specId=72ec3beb-90dc-473b-d214-08de429af188&id=53208c5d-a823-40d3-e79e-08de429af17b", "scene": null, "launchMode": "default" "launchMode": "default", "scene": null }, { "name": "新增地址", apps/housekeepingMiniApp/src/hooks/enterprise.ts
@@ -1,13 +1,15 @@ import { useInfiniteLoading } from '@12333/hooks'; import { BaseData, useInfiniteLoading } from '@12333/hooks'; import Taro from '@tarojs/taro'; import * as enterpriseServices from '@12333/services/apiV2/enterprise'; import { InfiniteData } from '@tanstack/vue-query'; type UseEnterpriseAddressesOptions = { rows?: number; onSuccess?: (data: InfiniteData<BaseData<API.GetEnterpriseAddressesQueryResultItem>>) => any; }; export function useEnterpriseAddresses(options: UseEnterpriseAddressesOptions = {}) { const { rows = 20 } = options; const { rows = 20, onSuccess } = options; const { infiniteLoadingProps, invalidateQueries } = useInfiniteLoading( ({ pageParam }) => { @@ -24,6 +26,9 @@ }, { queryKey: ['enterpriseServices/getEnterpriseAddresses', rows], onSuccess(data) { onSuccess?.(data); }, } ); apps/housekeepingMiniApp/src/styles/nut.scss
@@ -127,6 +127,11 @@ .bole-input-textarea:not(.nut-input--disabled) { color: boleGetCssVar('text-color', 'primary') !important; height: 100rpx; .textarea-placeholder { color: boleGetCssVar('text-color', 'placeholder') !important; font-size: 26rpx; } } .bole-input-text-placeholder { apps/housekeepingMiniApp/src/subpackages/mine/addressManange/InnerPage.vue
@@ -1,6 +1,6 @@ <template> <InfiniteLoading scrollViewClassName="common-infinite-scroll-list home-list" scrollViewClassName="common-page-infinite-scroll-list" v-bind="infiniteLoadingProps" > <nut-address-list @@ -28,6 +28,11 @@ name: 'InnerPage', }); const route = Taro.useRouter(); const id = route.params?.id ?? ''; const mode = route.params?.mode ?? ''; const isSelectMode = mode === 'select'; const dataOptions = reactive({ id: 'id', addressDetail: 'addressDetail', apps/housekeepingMiniApp/src/subpackages/sercice/addStandardOrder/InnerPage.vue
@@ -2,46 +2,108 @@ <LoadingLayout :loading="isLoading" :error="isError" :loadError="refetch"> <ContentScrollView hasPaddingTop> <ServiceDetailAddressCard :name="defaultAddress?.name ?? ''" :contactPhoneNumber="defaultAddress?.contactPhoneNumber ?? ''" :addressDetail="defaultAddress?.addressDetail ?? ''" :name="selectedAddress?.name ?? ''" :contactPhoneNumber="selectedAddress?.contactPhoneNumber ?? ''" :addressDetail="selectedAddress?.addressDetail ?? ''" showArrow @click="goSelectAddress" /> <div class="addStandardOrder-detail-card"> <nut-card :img-url="'//img10.360buyimg.com/n2/s240x240_jfs/t1/210890/22/4728/163829/6163a590Eb7c6f4b5/6390526d49791cb9.jpg!q70.jpg'" :title="detail?.name ?? ''" :price="toThousand(spec?.price ?? 0)" class="service-good-card" <ServiceDetailGoodCard :name="detail?.name" :price="spec?.price" :specName="spec?.name" :specNumber="specNumber" :imgUrl="detail?.files?.[0]" /> <!-- <List> <ListItem title="服务时间"> <template #extra> <div class="mine-service-detail-view-list-item">{{ '请选择' }}</div> </template> </ListItem> <ListItem title="服务机构"> <template #extra> <div class="mine-service-detail-view-list-item">{{ '请选择' }}</div> </template> </ListItem> </List> --> <nut-form :model-value="form" ref="formRef" :rules="rules" class="addStandardOrder-form"> <nut-form-item label="服务开始时间:" class="bole-form-item" prop="beginTime" label-width="90px" > <template #prolist> <div class="card-tag-list"> <span class="tag">{{ spec?.name }}</span> </div> </template> <template #origin> <div></div> </template> <template #footer> <div class="card-footer">x{{ specNumber }}</div> </template> </nut-card> </div> <ChooseInputWithDatePicker v-model="form.beginTime" :minDate="nowDate" placeholder="请选择" type="datetime" format="YYYY-MM-DD HH:mm:ss" ></ChooseInputWithDatePicker> </nut-form-item> <nut-form-item label="服务结束时间:" class="bole-form-item" prop="endTime" label-width="90px" > <ChooseInputWithDatePicker v-model="form.endTime" :minDate="endMinTime" :maxDate="endMaxTime" placeholder="请选择" type="datetime" format="YYYY-MM-DD HH:mm:ss" ></ChooseInputWithDatePicker> </nut-form-item> <nut-form-item label="备注:" class="bole-form-item alignTop" prop="remark" label-width="90px" > <nut-textarea v-model="form.remark" class="bole-input-textarea" rows="4" placeholder="请填写备注信息" > </nut-textarea> </nut-form-item> </nut-form> </ContentScrollView> <PageFooter> <PageFooterBtn type="primary" class="business-card-btn" @click="goConfirm" >立即下单</PageFooterBtn > </PageFooter> <nut-address v-model:visible="form.selectAddressVisible" v-model:value="form.addressId" type="exist" :exist-address="existAddress" :is-show-custom-address="false" @selected="selected" exist-address-title="选择地址" ></nut-address> </LoadingLayout> </template> <script setup lang="ts"> import { ServiceDetailAddressCard } from '@12333/components'; import { ServiceDetailAddressCard, ServiceDetailGoodCard, List, ListItem, ChooseInputWithDatePicker, } from '@12333/components'; import Taro from '@tarojs/taro'; import * as standardServiceServices from '@12333/services/apiV2/standardService'; import { RouterPath } from '@/constants'; import { useStandardServiceDetail } from '@12333/hooks'; import { toThousand } from '@12333/utils'; import * as standardOrderServices from '@12333/services/apiV2/standardOrder'; import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types'; import dayjs from 'dayjs'; defineOptions({ name: 'InnerPage', @@ -52,6 +114,37 @@ const specId = route.params?.specId ?? ''; const specNumber = Number(route.params?.specNumber); const form = reactive({ addressId: '', selectAddressVisible: false, beginTime: '', endTime: '', supplierEnterpriseId: '', enterpriseEmployeeIds: [] as string[], remark: '', }); const nowDate = dayjs().toDate(); const endMinTime = computed(() => { if (form.beginTime) { return dayjs(form.beginTime).add(1, 'minute').toDate(); } else { return dayjs().toDate(); } }); const endMaxTime = computed(() => { if (form.beginTime) { return dayjs(dayjs(form.beginTime).format('YYYY-MM-DD 23:59:59')).toDate(); } else { return undefined; } }); const rules = reactive<FormRules>({ beginTime: [{ required: true, message: '请选择服务开始时间' }], endTime: [{ required: true, message: '请选择服务结束时间' }], }); const { isLoading, isError, detail, refetch } = useStandardServiceDetail({ id, }); @@ -65,26 +158,120 @@ const { infiniteLoadingProps } = useEnterpriseAddresses({ rows: 100, onSuccess(res) { const data = res.pages[0].data; const address = data.find((item) => item.isDefault); if (address) { form.addressId = address.id; } else { form.addressId = data[0].id; } }, }); const defaultAddress = computed(() => { const address = infiniteLoadingProps.value.flattenListData.find((item) => item.isDefault); const existAddress = computed(() => { return infiniteLoadingProps.value.flattenListData.map((x) => ({ id: x.id, addressDetail: x.addressDetail, cityName: '', countyName: '', provinceName: '', selectedAddress: x.id === form.addressId, townName: '', name: x.name, phone: x.contactPhoneNumber, })); }); const selectedAddress = computed(() => { const address = infiniteLoadingProps.value.flattenListData.find( (item) => item.id === form.addressId ); return address || infiniteLoadingProps.value.flattenListData[0]; }); function goCancel() { Taro.navigateTo({ url: `${RouterPath.mineReserveServiceCancel}?id=${id}`, function goSelectAddress() { // Taro.navigateTo({ // url: `${RouterPath.addressManange}?mode=select`, // }); form.selectAddressVisible = true; } const selected = (prevExistAdd, nowExistAdd, arr) => { form.addressId = nowExistAdd.id; }; const formRef = ref<any>(null); function goConfirm() { if (!formRef.value) return; formRef.value.validate().then(({ valid, errors }: any) => { if (valid) { // addStandardOrder(); } }); } function goConfirm() { Taro.navigateTo({ url: `${RouterPath.mineReserveServiceConfirm}?id=${id}`, }); async function addStandardOrder() { try { let params: API.AddStandardOrderCommand = { serviceId: detail.value.id, serviceName: detail.value.name, serviceCode: detail.value.code, specId: spec.value.id, specName: spec.value.name, specPrice: spec.value.price ?? 0, specNumber: specNumber, addressId: '3fa85f64-5717-4562-b3fc-2c963f66afa6', name: detail.value.name, contactPhoneNumber: 'string', provinceCode: 'string', provinceContent: 'string', cityCode: 'string', cityContent: 'string', areaCode: 'string', areaContent: 'string', addressName: 'string', addressDetail: 'string', longitude: 0, latitude: 0, beginTime: '2025-12-24T08:25:08.372Z', endTime: '2025-12-24T08:25:08.372Z', supplierEnterpriseId: '3fa85f64-5717-4562-b3fc-2c963f66afa6', enterpriseEmployeeIds: ['3fa85f64-5717-4562-b3fc-2c963f66afa6'], remark: 'string', payAccess: 1, }; let res = await standardOrderServices.addStandardOrder(params); } catch (error) {} } async function pay() { try { let params: API.PayStandardOrderCommand = { id: '9e919af2-3d33-4eac-f6dc-08de429676b3', }; let res = await standardOrderServices.payStandardOrder(params); if (res) { Taro.requestPayment({ timeStamp: res.timestamp, nonceStr: res.nonceStr, package: res.package, signType: res.signType as any, paySign: res.paySign, }); } } catch (error) {} } </script> <style lang="scss"> @import '@/styles/common.scss'; .addStandardOrder-page-wrapper { .addStandardOrder-form { .nut-cell-group__wrap { box-shadow: none; } } } </style> apps/housekeepingMiniApp/src/subpackages/sercice/serciceDetail/serciceDetail.vue
@@ -166,59 +166,6 @@ }); }); async function addStandardOrder(specNumber: number) { try { const spec = SkuUtils.getCurrentActiveSpec(skuState.sku); let params: API.AddStandardOrderCommand = { serviceId: detail.value.id, serviceName: detail.value.name, serviceCode: detail.value.code, specId: spec.id, specName: spec.name, specPrice: detail.value.specs.find((x) => x.id === spec.id)?.price ?? 0, specNumber: specNumber, addressId: '3fa85f64-5717-4562-b3fc-2c963f66afa6', name: detail.value.name, contactPhoneNumber: 'string', provinceCode: 'string', provinceContent: 'string', cityCode: 'string', cityContent: 'string', areaCode: 'string', areaContent: 'string', addressName: 'string', addressDetail: 'string', longitude: 0, latitude: 0, beginTime: '2025-12-24T08:25:08.372Z', endTime: '2025-12-24T08:25:08.372Z', supplierEnterpriseId: '3fa85f64-5717-4562-b3fc-2c963f66afa6', enterpriseEmployeeIds: ['3fa85f64-5717-4562-b3fc-2c963f66afa6'], remark: 'string', payAccess: 1, }; let res = await standardOrderServices.addStandardOrder(params); } catch (error) {} } async function pay() { try { let params: API.PayStandardOrderCommand = { id: '9e919af2-3d33-4eac-f6dc-08de429676b3', }; let res = await standardOrderServices.payStandardOrder(params); if (res) { Taro.requestPayment({ timeStamp: res.timestamp, nonceStr: res.nonceStr, package: res.package, signType: res.signType as any, paySign: res.paySign, }); } } catch (error) {} } async function handleAttention() { try { let params: API.CollectionStandardServiceCommand = { packages/components/src/Card/ServiceDetailAddressCard.vue
@@ -1,14 +1,19 @@ <template> <div class="mine-service-detail-view-address"> <div class="mine-service-detail-view-title-wrapper"> <div class="mine-service-detail-view-title">服务地址</div> <div class="mine-service-detail-view-address-inner"> <div class="mine-service-detail-view-title-wrapper"> <div class="mine-service-detail-view-title">服务地址</div> </div> <div class="mine-service-detail-view-item">{{ addressDetail }}</div> <div class="mine-service-detail-view-item">{{ name }} {{ contactPhoneNumber }}</div> </div> <div class="mine-service-detail-view-item">{{ addressDetail }}</div> <div class="mine-service-detail-view-item">{{ name }} {{ contactPhoneNumber }}</div> <img v-if="showArrow" :src="IconArrow" class="mine-service-detail-view-arrow" /> </div> </template> <script setup lang="ts"> import IconArrow from '@/assets/setting/icon-arrow.png'; defineOptions({ name: 'ServiceDetailAddressCard', }); @@ -19,9 +24,12 @@ contactPhoneNumber?: string; /** 省市区+详细地址+门牌号 */ addressDetail?: string; showArrow?: boolean; }; const props = withDefaults(defineProps<Props>(), {}); const props = withDefaults(defineProps<Props>(), { showArrow: false, }); </script> <style lang="scss"> @@ -32,6 +40,13 @@ margin-bottom: 24px; background-color: #fff; border-radius: 12px; display: flex; align-items: center; .mine-service-detail-view-address-inner { flex: 1; min-width: 0; } .mine-service-detail-view-title-wrapper { display: flex; @@ -59,5 +74,11 @@ margin-top: 14px; } } .mine-service-detail-view-arrow { width: 32px; height: 32px; margin-left: 12px; } } </style> packages/components/src/Input/ChooseInputWithDatePicker.vue
@@ -53,7 +53,15 @@ type: props.type, maxDate: props.maxDate, onConfirm: ({ selectedValue }) => { emit('update:modelValue', dayjs(selectedValue.join('-')).format(props.format)); let _selectedVale = ''; if (selectedValue.length > 3) { _selectedVale = `${selectedValue.slice(0, 3).join('-')} ${selectedValue .slice(3) .join(':')}`; } else { _selectedVale = selectedValue.join('-'); } emit('update:modelValue', dayjs(_selectedVale).format(props.format)); onClose(); }, onChange: () => {