| | |
| | | 'rechargeResult/rechargeResult', |
| | | ], |
| | | }, |
| | | { |
| | | root: 'subpackages/order', |
| | | pages: ['order/order'], |
| | | }, |
| | | ], |
| | | // preloadRule: { |
| | | // 'pages/mine/index': { |
| | |
| | | |
| | | phoneBillRecharge = '/subpackages/recharge/phoneBillRecharge/phoneBillRecharge', |
| | | electricBillRecharge = '/subpackages/recharge/electricBillRecharge/electricBillRecharge', |
| | | order = '/subpackages/order/order/order', |
| | | selectPayType = '/subpackages/recharge/selectPayType/selectPayType', |
| | | rechargeResult = '/subpackages/recharge/rechargeResult/rechargeResult', |
| | | } |
| | |
| | | </div> |
| | | </div> |
| | | <ContentScrollView> |
| | | <List class="mine-list-wrapper" v-if="isLogin"> |
| | | <List class="mine-list-wrapper"> |
| | | <ListItem title="订单管理" @click="goOrderManage"></ListItem> |
| | | <ListItem title="退出登录" @click="goLogout"></ListItem> |
| | | <ListItem v-if="isLogin" title="退出登录" @click="goLogout"></ListItem> |
| | | </List> |
| | | </ContentScrollView> |
| | | </PageLayoutWithBg> |
| | |
| | | |
| | | <script setup lang="ts"> |
| | | import { TransparentNavigationBar, List, ListItem } from '@/components'; |
| | | import { useUser, useIsLogin, useGoLogin } from '@/hooks'; |
| | | import { useUser, useIsLogin, useGoLogin, useAccessLogin } from '@/hooks'; |
| | | import Taro from '@tarojs/taro'; |
| | | import { RouterPath, OssAssets } from '@/constants'; |
| | | import DefaultAvatar from '@/assets/components/icon-default-avatar.png'; |
| | |
| | | }; |
| | | }); |
| | | |
| | | function goOrderManage() {} |
| | | const goOrderManage = useAccessLogin(() => goPage(RouterPath.order)); |
| | | |
| | | function goLogout() { |
| | | userStore.logout(); |
New file |
| | |
| | | <template> |
| | | <ContentScrollView :paddingH="false"> |
| | | <Order /> |
| | | </ContentScrollView> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { Order } from '@life-payment/components'; |
| | | import Taro from '@tarojs/taro'; |
| | | |
| | | defineOptions({ |
| | | name: 'InnerPage', |
| | | }); |
| | | |
| | | function goPay() { |
| | | Taro.navigateTo({ |
| | | url: RouterPath.selectPayType, |
| | | }); |
| | | } |
| | | </script> |
New file |
| | |
| | | export default definePageConfig({ |
| | | disableScroll: true, |
| | | }); |
New file |
| | |
| | | <template> |
| | | <PageLayout title="订单管理" class="order-page-wrapper" hasBorder> |
| | | <InnerPage /> |
| | | </PageLayout> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { PageLayout } from '@/components'; |
| | | import InnerPage from './InnerPage.vue'; |
| | | |
| | | defineOptions({ |
| | | name: 'order', |
| | | }); |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div class="order-card"> |
| | | <div class="order-card-title"> |
| | | <div class="order-card-title-top"> |
| | | <div class="order-card-title-text">{{ title }}</div> |
| | | <div class="order-card-title-status">{{ status }}</div> |
| | | </div> |
| | | <div class="order-card-title-ordernum"> |
| | | {{ `订单编号:${'JF202502191515350002'}` }} |
| | | </div> |
| | | </div> |
| | | <div class="order-card-content"> |
| | | <slot></slot> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | defineOptions({ |
| | | name: 'OrderCard', |
| | | }); |
| | | |
| | | type Props = { |
| | | title: string; |
| | | status: string; |
| | | }; |
| | | |
| | | const props = withDefaults(defineProps<Props>(), {}); |
| | | </script> |
| | | <style lang="scss"> |
| | | .order-card { |
| | | border: 1px solid #e8e8e8; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | |
| | | .order-card-title { |
| | | display: flex; |
| | | flex-direction: column; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | padding-bottom: 20px; |
| | | margin-bottom: 20px; |
| | | |
| | | .order-card-title-top { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | color: #333333; |
| | | font-size: 28px; |
| | | line-height: 40px; |
| | | font-weight: 600; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .order-card-title-ordernum { |
| | | font-size: 24px; |
| | | color: #999999; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="order-card-item"> |
| | | <div class="order-card-item-label" :style="{ width: labelWidth, textAlign: textAlign }"> |
| | | <slot name="label">{{ label }}</slot> |
| | | </div> |
| | | <div class="order-card-item-value"> |
| | | <slot name="value">{{ value }}</slot> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | defineOptions({ |
| | | name: 'OrderCardItem', |
| | | }); |
| | | |
| | | type Props = { |
| | | label: string; |
| | | value: string; |
| | | labelWidth?: any; |
| | | textAlign?: any; |
| | | }; |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | labelWidth: '80px', |
| | | textAlign: 'left', |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .order-card-item { |
| | | display: flex; |
| | | font-size: 28px; |
| | | line-height: 40px; |
| | | margin-bottom: 15px; |
| | | |
| | | .order-card-item-label { |
| | | color: #333333; |
| | | } |
| | | |
| | | .order-card-item-value { |
| | | color: #666666; |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <scroll-view |
| | | :scroll-y="true" |
| | | :refresher-background="'transparent'" |
| | | :lowerThreshold="100" |
| | | @scrolltolower="lower" |
| | | :refresherEnabled="refresherEnabled" |
| | | :refresherTriggered="state.triggered" |
| | | @refresherrefresh="onRefresherRefresh" |
| | | :show-scrollbar="false" |
| | | :style="scrollViewStyle" |
| | | :enable-back-to-top="true" |
| | | :class="[scrollViewClassName]" |
| | | @scroll="onScroll" |
| | | :scroll-top="scrollDistance" |
| | | enhanced |
| | | :scroll-with-animation="true" |
| | | v-bind="{ ...$attrs }" |
| | | > |
| | | <slot name="header"></slot> |
| | | <LoadingLayout :loading="isLoading" :error="isError" :loadError="() => refetch?.()"> |
| | | <NoData v-if="hasNoData" /> |
| | | <div |
| | | :class="['infinite-list-inner', { noShowMoreText: !showMoreText, hasPaddingTop }]" |
| | | v-else |
| | | > |
| | | <slot name="extra" /> |
| | | <slot v-if="$slots.default" /> |
| | | <template v-else> |
| | | <template v-for="(group, index) in listData.pages" :key="index"> |
| | | <template v-for="(item, i) in group.data" :key="i"> |
| | | <slot |
| | | name="renderItem" |
| | | :item="item" |
| | | :groupIndex="index" |
| | | :itemIndex="i" |
| | | :index="findDataIndex(item)" |
| | | /> |
| | | </template> |
| | | </template> |
| | | </template> |
| | | </div> |
| | | <div |
| | | v-if="!hasNoData && showMoreText && listData?.pages?.length > 0 && !hasMore" |
| | | class="loading-more-tips" |
| | | > |
| | | {{ noMoreText }} |
| | | </div> |
| | | <div v-if="isFetching && hasMore && enabledLoadingMore" class="infiniting-tips"> |
| | | <Loading class="infiniting-tips-icon"></Loading>数据加载中... |
| | | </div> |
| | | </LoadingLayout> |
| | | </scroll-view> |
| | | <div class="back-top-wrapper" @click.stop="backToTop" v-show="oldScrollDistance > 100"> |
| | | <img class="back-top-img" :src="IconBackTop" /> |
| | | <div class="back-top-text">返回顶部</div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup generic="T"> |
| | | import { VNode, CSSProperties, computed, reactive } from 'vue'; |
| | | import NoData from '../NoData/NoData.vue'; |
| | | import LoadingLayout from '../Layout/LoadingLayout.vue'; |
| | | import { FetchNextPageOptions } from '@tanstack/vue-query'; |
| | | import { Loading } from '@nutui/icons-vue-taro'; |
| | | import { useScrollDistance } from 'senin-mini/hooks'; |
| | | import IconBackTop from '@/assets/components/icon-back-top.png'; |
| | | |
| | | defineOptions({ |
| | | name: 'InfiniteLoading', |
| | | }); |
| | | |
| | | type Page = { |
| | | data?: T[]; |
| | | pageModel?: { |
| | | rows?: number; |
| | | page?: number; |
| | | totalCount?: number; |
| | | totalPage?: number; |
| | | }; |
| | | [key: string]: any; |
| | | }; |
| | | |
| | | type TData = { |
| | | pages: Page[]; |
| | | }; |
| | | |
| | | type Props = { |
| | | // list?: TData; |
| | | listData?: TData; |
| | | flattenListData?: T[]; |
| | | renderItem?: (item: T, index: number) => VNode; |
| | | refresherEnabled?: boolean; |
| | | hasMore?: boolean; |
| | | isLoading?: boolean; |
| | | isError?: boolean; |
| | | isFetching?: boolean; |
| | | isFetchingNextPage?: boolean; |
| | | refetch?: (options?: any) => Promise<any>; |
| | | scrollViewStyle?: string | CSSProperties; |
| | | scrollViewClassName?: string; |
| | | fetchNextPage?: (options?: FetchNextPageOptions) => Promise<any>; |
| | | showMoreText?: boolean; |
| | | hasPaddingTop?: boolean; |
| | | noMoreText?: string; |
| | | enabledLoadingMore?: boolean; |
| | | customNoData?: boolean; |
| | | }; |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | renderItem: () => () => null, |
| | | refresherEnabled: true, |
| | | showMoreText: true, |
| | | hasPaddingTop: false, |
| | | noMoreText: '没有更多内容了~', |
| | | enabledLoadingMore: true, |
| | | customNoData: undefined, |
| | | }); |
| | | |
| | | const emit = defineEmits<{ |
| | | (e: 'refresh', done: () => any): void; |
| | | (e: 'loadMore'): void; |
| | | }>(); |
| | | |
| | | const hasNoData = computed(() => { |
| | | if (props.customNoData !== undefined) return props.customNoData; |
| | | if (props.listData?.pages?.length) { |
| | | return props.listData?.pages[0].data.length === 0; |
| | | } |
| | | return true; |
| | | }); |
| | | |
| | | const state = reactive({ |
| | | triggered: false, |
| | | }); |
| | | |
| | | function lower() { |
| | | if (props.hasMore && props.enabledLoadingMore) { |
| | | props.fetchNextPage?.(); |
| | | emit('loadMore'); |
| | | } |
| | | } |
| | | |
| | | // eslint-disable-next-line no-unused-vars |
| | | async function onRefresherRefresh() { |
| | | try { |
| | | // 正处于刷新状态 |
| | | if (state.triggered) return; |
| | | state.triggered = true; |
| | | |
| | | await props.refetch?.(); |
| | | |
| | | state.triggered = false; |
| | | } catch (error) {} |
| | | } |
| | | |
| | | const { onScroll, scrollDistance, oldScrollDistance, setScrollDistance } = useScrollDistance(); |
| | | |
| | | function backToTop() { |
| | | setScrollDistance(0); |
| | | } |
| | | |
| | | const scrollToBottom = (dis = 300) => { |
| | | setScrollDistance(scrollDistance.value + dis); |
| | | }; |
| | | |
| | | function findDataIndex(item: T) { |
| | | return props.flattenListData?.findIndex((data) => data === item); |
| | | } |
| | | |
| | | defineExpose({ backToTop, scrollToBottom }); |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | @import '@/styles/common.scss'; |
| | | |
| | | .loading-more-tips { |
| | | color: boleGetCssVar('text-color', 'primary'); |
| | | padding: 18px 10px; |
| | | width: auto; |
| | | font-size: 24px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .infinite-list-inner { |
| | | // padding: 30px 30px 0; |
| | | |
| | | &.hasPaddingTop { |
| | | padding-top: 20px; |
| | | } |
| | | |
| | | &.noShowMoreText { |
| | | padding-bottom: 30px; |
| | | } |
| | | } |
| | | |
| | | .infiniting-tips { |
| | | color: boleGetCssVar('text-color', 'primary'); |
| | | padding: 18px 10px; |
| | | width: auto; |
| | | font-size: 24px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | .infiniting-tips-icon { |
| | | margin-right: 10px; |
| | | } |
| | | } |
| | | |
| | | .back-top-wrapper { |
| | | width: 92px; |
| | | height: 92px; |
| | | background: #ffffff; |
| | | box-shadow: 0px 0px 28px 0px rgba(0, 0, 0, 0.18); |
| | | position: fixed; |
| | | border-radius: 50%; |
| | | right: boleGetCssVar('size', 'body-padding-h'); |
| | | bottom: 390px; |
| | | |
| | | .back-top-img { |
| | | width: 44px; |
| | | height: 44px; |
| | | margin: 12px auto 2px; |
| | | } |
| | | |
| | | .back-top-text { |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: boleGetCssVar('text-color', 'regular'); |
| | | line-height: 22px; |
| | | text-align: center; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | export { default as RechargeGrid } from './views/RechargeGrid/RechargeGrid.vue'; |
| | | export { default as Order } from './views/Order/Order.vue'; |
| | | export { default as PhoneBillRecharge } from './views/PhoneBillRecharge/PhoneBillRecharge.vue'; |
| | | export { default as electricBillRecharge } from './views/electricBillRecharge/electricBillRecharge.vue'; |
| | | export { default as SelectPayTypeView } from './views/SelectPayTypeView/SelectPayTypeView.vue'; |
| | |
| | | [IspCode.dianxin]: '中国电信', |
| | | [IspCode.liantong]: '中国联通', |
| | | }; |
| | | |
| | | export enum ElectricType { |
| | | /**国家电网 */ |
| | | guowang = 'guowang', |
| | | /**南方电网 */ |
| | | nanwang = 'nanwang', |
| | | } |
| | | |
| | | export const ElectricTypeText = { |
| | | [ElectricType.guowang]: '国家电网', |
| | | [ElectricType.nanwang]: '南方电网', |
| | | }; |
| | | |
| | | export enum ElectricAccountType { |
| | | /**住宅 */ |
| | | house = 'house', |
| | | /**企事业 */ |
| | | enterprise = 'enterprise', |
| | | /**店铺 */ |
| | | shop = 'shop', |
| | | /**店铺-不建议使用 */ |
| | | nanwashopng = 'nanwashopng', |
| | | } |
| | | |
| | | export const ElectricAccountTypeText = { |
| | | [ElectricAccountType.house]: '住宅', |
| | | [ElectricAccountType.enterprise]: '企事业', |
| | | [ElectricAccountType.shop]: '店铺', |
| | | [ElectricAccountType.nanwashopng]: '店铺-不建议使用', |
| | | }; |
| | | } |
New file |
| | |
| | | <template> |
| | | <div class="order-wrapper"> |
| | | <nut-tabs v-model="orderType"> |
| | | <nut-tab-pane title="话费订单" pane-key="1"> |
| | | <PhoneOrder></PhoneOrder> |
| | | </nut-tab-pane> |
| | | <nut-tab-pane title="电费订单" pane-key="2"> |
| | | <ElectricOrder></ElectricOrder> |
| | | </nut-tab-pane> |
| | | </nut-tabs> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref } from 'vue'; |
| | | import PhoneOrder from './components/PhoneOrder.vue'; |
| | | import ElectricOrder from './components/ElectricOrder.vue'; |
| | | |
| | | defineOptions({ |
| | | name: 'Order', |
| | | }); |
| | | |
| | | const orderType = ref(1); |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <!-- <InfiniteLoading scrollViewClassName="common-infinite-scroll-list" v-bind="infiniteLoadingProps"> |
| | | <template #renderItem="{ item }"> --> |
| | | <OrderCard title="电费充值" status="待支付"> |
| | | <OrderCardItem label="充值地区:" :value="'18888888888'" /> |
| | | <OrderCardItem label="充值户号:" :value="'18888888888'" /> |
| | | <OrderCardItem label="下单时间:" :value="'2025-02-19 17:15:54'" /> |
| | | <OrderCardItem label="充值金额:" :value="'18888888888'" /> |
| | | <OrderCardItem label="优惠金额:" :value="'18888888888'" /> |
| | | <OrderCardItem label="实付金额:" :value="'18888888888'" /> |
| | | <OrderCardItem label="支付时间:" :value="'2025-02-19 17:15:54'" /> |
| | | <OrderCardItem label="完成时间:" :value="'2025-02-19 17:15:54'" /> |
| | | </OrderCard> |
| | | <!-- </template> |
| | | </InfiniteLoading> --> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import InfiniteLoading from '../../../components/InfiniteLoading/InfiniteLoading.vue'; |
| | | import OrderCard from '../../../components/Card/OrderCard.vue'; |
| | | import OrderCardItem from '../../../components/Card/OrderCardItem.vue'; |
| | | |
| | | defineOptions({ |
| | | name: 'ElectricOrder', |
| | | }); |
| | | |
| | | // type Props = {}; |
| | | |
| | | // const props = withDefaults(defineProps<Props>(), {}); |
| | | |
| | | const infiniteLoadingProps = {}; |
| | | </script> |
| | | |
| | | <style lang="scss"></style> |
New file |
| | |
| | | <template> |
| | | <!-- <InfiniteLoading scrollViewClassName="common-infinite-scroll-list" v-bind="infiniteLoadingProps"> |
| | | <template #renderItem="{ item }"> --> |
| | | <OrderCard title="话费充值" status="待支付"> |
| | | <OrderCardItem label="充值账号:" :value="'18888888888'" /> |
| | | <OrderCardItem label="下单时间:" :value="'18888888888'" /> |
| | | <OrderCardItem label="充值金额:" :value="'18888888888'" /> |
| | | <OrderCardItem label="优惠金额:" :value="'18888888888'" /> |
| | | <OrderCardItem label="实付金额:" :value="'18888888888'" /> |
| | | <OrderCardItem label="支付时间:" :value="'2025-02-19 17:15:54'" /> |
| | | <OrderCardItem label="完成时间:" :value="'2025-02-19 17:15:54'" /> |
| | | </OrderCard> |
| | | <!-- </template> |
| | | </InfiniteLoading> --> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import InfiniteLoading from '../../../components/InfiniteLoading/InfiniteLoading.vue'; |
| | | import OrderCard from '../../../components/Card/OrderCard.vue'; |
| | | import OrderCardItem from '../../../components/Card/OrderCardItem.vue'; |
| | | |
| | | defineOptions({ |
| | | name: 'PhoneOrder', |
| | | }); |
| | | |
| | | // type Props = {}; |
| | | |
| | | // const props = withDefaults(defineProps<Props>(), {}); |
| | | |
| | | const infiniteLoadingProps = {}; |
| | | </script> |
| | | |
| | | <style lang="scss"></style> |
| | |
| | | label-position="top" |
| | | class="order-bill-recharge electric" |
| | | > |
| | | <FormItem label="所在城市" class="bole-form-item" prop="type" required> |
| | | <FormItem label="所在城市" class="bole-form-item" prop="province" required> |
| | | <ChooseInputWithPicker |
| | | v-model="form.type" |
| | | v-model="form.province" |
| | | placeholder="请选择城市" |
| | | :value-enum="IspCodeText" |
| | | /> |
| | | </FormItem> |
| | | <FormItem label="电网类型" class="bole-form-item" prop="type" required> |
| | | <FormItem label="电网类型" class="bole-form-item" prop="electricType" required> |
| | | <ChooseInputWithPicker |
| | | v-model="form.type" |
| | | v-model="form.electricType" |
| | | placeholder="请选择电网类型" |
| | | :value-enum="IspCodeText" |
| | | :value-enum="blLifeRecharge.constants.ElectricTypeText" |
| | | /> |
| | | </FormItem> |
| | | <FormItem label="电费类型" class="bole-form-item" prop="type" required> |
| | | <FormItem label="电费类型" class="bole-form-item" prop="electricAccountType" required> |
| | | <ChooseInputWithPicker |
| | | v-model="form.type" |
| | | v-model="form.electricAccountType" |
| | | placeholder="请选择电费类型" |
| | | :value-enum="IspCodeText" |
| | | :value-enum="blLifeRecharge.constants.ElectricAccountTypeText" |
| | | /> |
| | | </FormItem> |
| | | <FormItem label="电网户号" class="bole-form-item" prop="phone" required> |
| | | <FormItem label="电网户号" class="bole-form-item" prop="electricAccount" required> |
| | | <Input |
| | | v-model.trim="form.phone" |
| | | v-model.trim="form.electricAccount" |
| | | class="bole-input-text" |
| | | placeholder="请输入13位数字编号" |
| | | type="text" |
| | | /> |
| | | </FormItem> |
| | | <FormItem |
| | | v-if="form.electricType === blLifeRecharge.constants.ElectricType.nanwang" |
| | | label="身份证后六位" |
| | | class="bole-form-item" |
| | | prop="sixID" |
| | | required |
| | | > |
| | | <Input |
| | | v-model.trim="form.sixID" |
| | | class="bole-input-text" |
| | | placeholder="请输入身份证后六位" |
| | | type="text" |
| | | /> |
| | | </FormItem> |
| | |
| | | |
| | | const form = reactive({ |
| | | ispCode: IspCode.yidong, |
| | | phone: '', |
| | | parValue: 100, |
| | | type: IspCodeText.yidong, |
| | | electricAccount: '', |
| | | electricType: '', |
| | | electricAccountType: '', |
| | | province: '', |
| | | sixID: '', |
| | | }); |
| | | |
| | | const rate = 0.96; |
| | |
| | | body: API.RefundLifePayOrderInput, |
| | | options?: API.RequestConfig |
| | | ) { |
| | | return request<any>('/api/LifePay/RefundLifePayOrder', { |
| | | return request<number>('/api/LifePay/RefundLifePayOrder', { |
| | | method: 'POST', |
| | | headers: { |
| | | 'Content-Type': 'application/json', |