| | |
| | | <template> |
| | | <PageLayoutWithBg class="mineHire-page-wrapper" :title="'服务名'" :need-auth="false"> |
| | | <PageLayout class="serciceDetail-page-wrapper" :title="detail?.name ?? ''" :need-auth="false"> |
| | | <LoadingLayout :loading="isLoading" :error="isError" :loadError="refetch"> |
| | | <ContentScrollView style="background-color: transparent"> serciceDetail </ContentScrollView> |
| | | <InfiniteLoading |
| | | commonMode |
| | | :refetch="refetch" |
| | | :isLoading="isLoading" |
| | | :isError="isError" |
| | | :showMoreText="false" |
| | | scrollViewClassName="common-infinite-scroll-list-no-padding" |
| | | > |
| | | <div class="serciceDetail-top-view"> |
| | | <SquareView> |
| | | <nut-swiper |
| | | :auto-play="3000" |
| | | v-if="detail?.files?.length > 0" |
| | | class="serciceDetail-swiper" |
| | | > |
| | | <nut-swiper-item |
| | | v-for="(item, index) in detail.files" |
| | | :key="item" |
| | | class="serciceDetail-swiper-item" |
| | | > |
| | | <img |
| | | :src="setOSSLink(item)" |
| | | class="serciceDetail-swiper-item-img" |
| | | draggable="false" |
| | | /> |
| | | </nut-swiper-item> |
| | | </nut-swiper> |
| | | </SquareView> |
| | | <div class="serciceDetail-top-view-title-wrapper"> |
| | | <div class="serciceDetail-price-wrapper"> |
| | | <div class="serciceDetail-price">{{ toThousand(minPrice) }}</div> |
| | | <div class="serciceDetail-price-unit">元起</div> |
| | | </div> |
| | | <div class="serciceDetail-top-view-title">{{ detail?.name ?? '' }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <List class="serciceDetail-content-list"> |
| | | <ListItem title="规格" @click="openSkuDialog()"> |
| | | <template #extra> |
| | | <div>共{{ detail.specs?.length }}类</div> |
| | | </template> |
| | | </ListItem> |
| | | </List> |
| | | <ProTabs |
| | | v-model="tab" |
| | | name="serciceDetail-content-tab" |
| | | class="serciceDetail-content-tabs" |
| | | flexTitle |
| | | > |
| | | <ProTabPane :title="`服务详情`" pane-key="1"> |
| | | <RichEditorContent :content="detail?.description ?? ''"></RichEditorContent> |
| | | </ProTabPane> |
| | | <ProTabPane :title="`客户评价`" pane-key="2"> |
| | | <NoData /> |
| | | </ProTabPane> |
| | | <ProTabPane :title="`相关推荐`" pane-key="3"> |
| | | <NoData /> |
| | | </ProTabPane> |
| | | </ProTabs> |
| | | </InfiniteLoading> |
| | | <Sku |
| | | v-model:visible="skuState.visible" |
| | | :sku="skuState.sku" |
| | | v-model:goods="skuState.goods" |
| | | @clickBtnOperate="clickBtnOperate" |
| | | > |
| | | </Sku> |
| | | <PageFooter> |
| | | <PageFooterAction |
| | | :icon="detail.isCollection ? IconAttentionActive : IconAttention" |
| | | text="收藏" |
| | | :isFlex="false" |
| | | @click="handleAttention" |
| | | ></PageFooterAction> |
| | | <PageFooterAction |
| | | :icon="IconShare" |
| | | text="客服" |
| | | :isFlex="false" |
| | | :open-type="'contact'" |
| | | ></PageFooterAction> |
| | | <PageFooterBtn type="primary" @click="openSkuDialog()">预约下单</PageFooterBtn> |
| | | </PageFooter> |
| | | </LoadingLayout> |
| | | </PageLayoutWithBg> |
| | | </PageLayout> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { useTaskInfo } from '@12333/hooks'; |
| | | import InnerPage from './InnerPage.vue'; |
| | | import { useStandardServiceDetail } from '@12333/hooks'; |
| | | import Taro from '@tarojs/taro'; |
| | | import * as standardOrderServices from '@12333/services/apiV2/standardOrder'; |
| | | import * as standardServiceServices from '@12333/services/apiV2/standardService'; |
| | | import { toThousand, setOSSLink, Message } from '@12333/utils'; |
| | | import { |
| | | Sku, |
| | | Goods, |
| | | SkuItem, |
| | | SkuUtils, |
| | | List, |
| | | ListItem, |
| | | ProTabs, |
| | | ProTabPane, |
| | | SquareView, |
| | | } from '@12333/components'; |
| | | import { useAccessLogin } from '@/hooks'; |
| | | import IconShare from '@/assets/flexJob/icon-share.png'; |
| | | import IconAttention from '@/assets/flexJob/icon-attention-lg.png'; |
| | | import IconAttentionActive from '@/assets/flexJob/icon-attention-lg-active.png'; |
| | | |
| | | defineOptions({ |
| | | name: 'serciceDetail', |
| | |
| | | const router = Taro.useRouter(); |
| | | const id = router.params?.id ?? ''; |
| | | |
| | | const { isLoading, isError, detail, refetch } = useTaskInfo({ |
| | | const tab = ref('1'); |
| | | |
| | | const { isLoading, isError, detail, refetch, minPrice } = useStandardServiceDetail({ |
| | | id, |
| | | onSuccess(res) { |
| | | skuState.sku = [ |
| | | { |
| | | id: SkuUtils.DefaultSkuSpecId, |
| | | name: '规格', |
| | | list: res.specs.map((item, index) => ({ |
| | | id: item.id, |
| | | name: item.name, |
| | | price: item.price, |
| | | active: index === 0, |
| | | disable: false, |
| | | })), |
| | | }, |
| | | ]; |
| | | skuState.goods = { |
| | | skuId: SkuUtils.DefaultSkuSpecId, |
| | | price: toThousand(res.specs[0].price), |
| | | imagePath: setOSSLink(res.files[0]), |
| | | name: res.name, |
| | | }; |
| | | }, |
| | | }); |
| | | |
| | | const skuState = reactive({ |
| | | visible: false, |
| | | sku: [] as SkuItem[], |
| | | goods: {} as Goods, |
| | | }); |
| | | |
| | | // 底部操作按钮触发 |
| | | const clickBtnOperate = (op: { type: string; value: number }) => { |
| | | // addStandardOrder(op.value); |
| | | Message.warning('支付系统维护中,请稍后预约'); |
| | | }; |
| | | |
| | | const openSkuDialog = () => { |
| | | skuState.visible = true; |
| | | }; |
| | | |
| | | const goAddStandardOrder = useAccessLogin((specNumber: number) => { |
| | | Taro.navigateTo({ |
| | | url: `${RouterPath.addStandardOrder}?specNumber=${specNumber}`, |
| | | }); |
| | | }); |
| | | |
| | | 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 = { |
| | | ids: [id], |
| | | isCollect: !detail.value.isCollection, |
| | | }; |
| | | let res = await standardServiceServices.collectionStandardService(params); |
| | | if (res) { |
| | | refetch({ type: 'inactive' }); |
| | | } |
| | | } catch (error) {} |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | @import '@/styles/common.scss'; |
| | | |
| | | .serciceDetail-page-wrapper { |
| | | .serciceDetail-swiper { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .serciceDetail-swiper-item-img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | /* 可选:调整图片裁剪的对齐方式(默认居中) */ |
| | | object-position: center center; |
| | | } |
| | | } |
| | | |
| | | .serciceDetail-top-view { |
| | | margin-bottom: 20px; |
| | | background-color: #fff; |
| | | |
| | | .serciceDetail-top-view-title-wrapper { |
| | | padding: 24px boleGetCssVar('size', 'body-padding-h') 32px; |
| | | |
| | | .serciceDetail-price-wrapper { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | margin-bottom: 24px; |
| | | |
| | | .serciceDetail-price { |
| | | font-weight: 600; |
| | | font-size: 48px; |
| | | color: #ff6414; |
| | | line-height: 52px; |
| | | } |
| | | |
| | | .serciceDetail-price-unit { |
| | | font-weight: 400; |
| | | font-size: 28px; |
| | | color: #ff6414; |
| | | line-height: 40px; |
| | | } |
| | | } |
| | | |
| | | .serciceDetail-top-view-title { |
| | | font-weight: 500; |
| | | font-size: 32px; |
| | | color: boleGetCssVar('text-color', 'primary'); |
| | | line-height: 44px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .serciceDetail-content-list { |
| | | margin-bottom: 20px; |
| | | } |
| | | } |
| | | </style> |