zhengyiming
2025-03-10 866879aaf4b209e4820d21d11f9569e72dd6e0a5
fix: 二期需求
14个文件已修改
11个文件已添加
779 ■■■■ 已修改文件
apps/taro/src/app.config.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/taro/src/constants/router.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/taro/src/pages/home/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/taro/src/subpackages/recharge/gasBillRecharge/InnerPage.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/taro/src/subpackages/recharge/gasBillRecharge/gasBillRecharge.config.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/taro/src/subpackages/recharge/gasBillRecharge/gasBillRecharge.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/taro/src/subpackages/recharge/rechargeResult/rechargeResult.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/components/Card/AccountAddCard.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/components/Card/AccountCard.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/components/Card/AccountList.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/components/Dialog/ConfirmDialogInfoItem.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/constants/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/styles/card.scss 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/styles/components.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/styles/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/views/PhoneBillRecharge/PhoneBillRecharge.vue 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/views/PhoneBillRecharge/PhoneBillRechargeBaseForm.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/views/PhoneBillRecharge/PhoneBillRechargeStep1.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/views/PhoneBillRecharge/PhoneBillRechargeStep2.vue 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/views/PhoneBillRecharge/context.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/views/RechargeResultView/RechargeResultView.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/views/electricBillRecharge/electricBillRecharge.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/core-vue/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/core/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/core/src/lifeRecharge.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/taro/src/app.config.ts
@@ -56,6 +56,7 @@
      pages: [
        'phoneBillRecharge/phoneBillRecharge',
        'electricBillRecharge/electricBillRecharge',
        'gasBillRecharge/gasBillRecharge',
        'selectPayType/selectPayType',
        'rechargeResult/rechargeResult',
        'rechargeElectricResult/rechargeElectricResult',
apps/taro/src/constants/router.ts
@@ -10,6 +10,7 @@
  phoneBillRecharge = '/subpackages/recharge/phoneBillRecharge/phoneBillRecharge',
  electricBillRecharge = '/subpackages/recharge/electricBillRecharge/electricBillRecharge',
  gasBillRecharge = '/subpackages/recharge/gasBillRecharge/gasBillRecharge',
  order = '/subpackages/order/order/order',
  selectPayType = '/subpackages/recharge/selectPayType/selectPayType',
  rechargeResult = '/subpackages/recharge/rechargeResult/rechargeResult',
apps/taro/src/pages/home/index.vue
@@ -33,7 +33,7 @@
});
const goGasBillRecharge = useAccessLogin(() => {
  Taro.navigateTo({
    url: `${RouterPath.electricBillRecharge}`,
    url: `${RouterPath.gasBillRecharge}`,
  });
});
</script>
apps/taro/src/subpackages/recharge/gasBillRecharge/InnerPage.vue
New file
@@ -0,0 +1,13 @@
<template>
  <div>燃气充值</div>
</template>
<script setup lang="ts">
defineOptions({
  name: 'InnerPage',
});
</script>
<style lang="scss">
@import '@/styles/common.scss';
</style>
apps/taro/src/subpackages/recharge/gasBillRecharge/gasBillRecharge.config.ts
New file
@@ -0,0 +1,3 @@
export default definePageConfig({
  disableScroll: true,
});
apps/taro/src/subpackages/recharge/gasBillRecharge/gasBillRecharge.vue
New file
@@ -0,0 +1,14 @@
<template>
  <PageLayout title="燃气充值" class="gasBillRecharge-page-wrapper" hasBorder>
    <InnerPage />
  </PageLayout>
</template>
<script setup lang="ts">
import { PageLayout } from '@/components';
import InnerPage from './InnerPage.vue';
defineOptions({
  name: 'gasBillRecharge',
});
</script>
apps/taro/src/subpackages/recharge/rechargeResult/rechargeResult.vue
@@ -1,7 +1,7 @@
<template>
  <PageLayout title="充值成功" class="rechargeResult-page-wrapper" hasBorder>
    <ContentScrollView>
      <RechargeResultView style="margin-top: 40px" @go-back-home="goHome()" />
      <RechargeResultView style="margin-top: 40px" @go-back-home="goHome()" :orderNo="orderNo" />
    </ContentScrollView>
  </PageLayout>
</template>
@@ -10,8 +10,12 @@
import { PageLayout } from '@/components';
import { goHome } from '@/utils';
import { RechargeResultView } from '@life-payment/components';
import Taro from '@tarojs/taro';
defineOptions({
  name: 'rechargeResult',
});
const router = Taro.useRouter();
const orderNo = router.params?.orderNo ?? '';
</script>
packages/components/src/components/Card/AccountAddCard.vue
New file
@@ -0,0 +1,11 @@
<template>
  <div class="account-add-card"><Uploader /> 新增手机号</div>
</template>
<script setup lang="ts">
import { Uploader } from '@nutui/icons-vue-taro';
defineOptions({
  name: 'AccountAddCard',
});
</script>
packages/components/src/components/Card/AccountCard.vue
New file
@@ -0,0 +1,26 @@
<template>
  <div class="account-card">
    <div class="account-card-top">
      <div class="account-card-title">{{ title }}</div>
      <div class="account-card-actions">
        <slot name="action" />
      </div>
    </div>
    <div class="account-card-content">{{ content }}</div>
    <div class="account-card-remark">{{ remark }}</div>
  </div>
</template>
<script setup lang="ts">
defineOptions({
  name: 'AccountCard',
});
type Props = {
  title?: string;
  content?: string;
  remark?: string;
};
const props = withDefaults(defineProps<Props>(), {});
</script>
packages/components/src/components/Card/AccountList.vue
New file
@@ -0,0 +1,9 @@
<template>
  <div class="par-account-list"></div>
</template>
<script setup lang="ts">
defineOptions({
  name: 'AccountList',
});
</script>
packages/components/src/components/Dialog/ConfirmDialogInfoItem.vue
@@ -1,11 +1,16 @@
<template>
  <div class="confirm-dialog-content-info-item" :class="{ danger }">
    <div class="confirm-dialog-content-info-item-label">{{ label }}</div>
    <div class="confirm-dialog-content-info-item-label" :style="{ width: _labelWidth }">
      {{ label }}
    </div>
    <div class="confirm-dialog-content-info-item-content">{{ content }}</div>
  </div>
</template>
<script setup lang="ts">
import Taro from '@tarojs/taro';
import { computed } from 'vue';
defineOptions({
  name: 'ConfirmDialogInfoItem',
});
@@ -14,7 +19,18 @@
  label?: string;
  content?: string | number;
  danger?: boolean;
  labelWidth?: string | number;
};
const props = withDefaults(defineProps<Props>(), {});
const props = withDefaults(defineProps<Props>(), {
  labelWidth: 'auto',
});
const _labelWidth = computed(() => {
  if (typeof props.labelWidth === 'string') {
    return props.labelWidth;
  }
  return Taro.pxTransform(props.labelWidth);
});
</script>
packages/components/src/constants/index.ts
@@ -23,3 +23,6 @@
   */
  Desc,
}
export const CustomerServicePhone = '16505012333';
export const CustomerServiceTips = `如充值过程中遇到问题,请联系客服 ${CustomerServicePhone}(周一到周五 9:00-17:30)`;
packages/components/src/styles/card.scss
New file
@@ -0,0 +1,69 @@
@use './common.scss' as *;
.account-add-card {
  border-radius: 16px;
  min-height: 120px;
  border: 1px solid #e8e8e8;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  color: boleGetCssVar('color', 'primary');
}
.account-card {
  border-radius: 16px;
  border: 1px solid #e8e8e8;
  padding: 28px;
  .account-card-top {
    display: flex;
    font-size: 28px;
    line-height: 32px;
    align-items: center;
    margin-bottom: 32px;
    .account-card-title {
      flex: 1;
      min-width: 0;
      @include ellipsis;
      color: boleGetCssVar('text-color', 'primary');
    }
    .account-card-actions {
      display: flex;
      align-items: center;
      .account-card-action {
        color: boleGetCssVar('color', 'primary');
      }
      .account-card-action + .account-card-action {
        margin-left: 10px;
      }
    }
  }
  .account-card-content {
    font-size: 36px;
    font-weight: bold;
    line-height: 48px;
    align-items: center;
    margin-bottom: 32px;
    color: boleGetCssVar('text-color', 'primary');
    @include ellipsis;
  }
  .account-card-remark {
    font-size: 28px;
    line-height: 32px;
    color: boleGetCssVar('text-color', 'secondary');
    @include ellipsis;
  }
}
.par-account-list {
  .nut-radio--button {
    margin-bottom: 20px !important;
  }
}
packages/components/src/styles/components.scss
@@ -50,6 +50,7 @@
      .confirm-dialog-content-info-item-label {
        color: boleGetCssVar('text-color', 'regular');
        margin-right: 12px;
        text-align: left;
      }
      .confirm-dialog-content-info-item-content {
@@ -110,6 +111,14 @@
    line-height: 48px;
  }
  .recharge-result-view-subtitle {
    font-size: 28px;
    color: boleGetCssVar('text-color', 'primary');
    margin-bottom: 24px;
    text-align: center;
    line-height: 32px;
  }
  .recharge-result-view-tips {
    margin-bottom: 24px;
    font-size: 32px;
@@ -118,12 +127,19 @@
  }
  .recharge-result-view-warning {
    margin-bottom: 40px;
    margin-bottom: 24px;
    font-size: 24px;
    color: boleGetCssVar('color', 'warning');
    line-height: 32px;
  }
  .recharge-result-view-customerService {
    margin-bottom: 40px;
    font-size: 24px;
    color: boleGetCssVar('text-color', 'primary');
    line-height: 32px;
  }
  .recharge-result-view-btn-wrapper {
    display: flex;
    justify-content: center;
packages/components/src/styles/index.scss
@@ -2,6 +2,7 @@
@use './var.scss' as *;
@use './function.scss' as *;
@use './orderCard.scss' as *;
@use './card.scss' as *;
@use './nut.scss' as *;
@use './layout.scss' as *;
@use './rechargeGrid.scss' as *;
packages/components/src/views/PhoneBillRecharge/PhoneBillRecharge.vue
@@ -1,111 +1,18 @@
<template>
  <NutForm
    :model-value="form"
    ref="formRef"
    :rules="rules"
    label-position="top"
    class="order-bill-recharge phone"
  >
    <NutFormItem label="选择运营商:" class="bole-form-item" prop="ispCode" required>
      <NutRadioGroup v-model="form.ispCode" direction="horizontal" @change="changeIspCode">
        <BlRadio
          :label="key"
          v-for="(val, key) in BlLifeRecharge.constants.IspCodeText"
          :key="key"
          >{{ val }}</BlRadio
        >
      </NutRadioGroup>
    </NutFormItem>
    <NutFormItem label="充值手机号" class="bole-form-item" prop="phone" required>
      <NutInput
        v-model.trim="form.phone"
        class="bole-input-text"
        placeholder="请填写您需要充值的手机号码"
        type="text"
      />
    </NutFormItem>
    <NutFormItem
      label="姓名"
      class="bole-form-item"
      prop="name"
      required
      v-if="form.ispCode === BlLifeRecharge.constants.IspCode.dianxin"
    >
      <NutInput
        v-model.trim="form.name"
        class="bole-input-text"
        placeholder="请填写您的姓名"
        type="text"
      />
    </NutFormItem>
    <NutFormItem label="选择充值金额" class="bole-form-item" prop="parValue" required>
      <NutRadioGroup v-model="form.parValue" direction="horizontal" class="parValue-radio-group">
        <NutRadio
          :label="Number(item)"
          :key="item"
          shape="button"
          v-for="item in parValueList"
          class="parValue-item"
        >
          <div class="parValue-item-inner">
            <div class="amount-wrapper">
              <div class="amount">{{ item }}</div>
              <div class="unit">元</div>
            </div>
            <div class="price-wrapper">
              <div class="price-text">折后</div>
              <div class="price">
                {{ blLifeRecharge.getRechargeParValue(item, lifePayPhoneRate) }}元
              </div>
            </div>
            <div class="discountTag">{{ lifePayPhoneRate * 100 }}折</div>
          </div>
        </NutRadio>
      </NutRadioGroup>
    </NutFormItem>
    <div class="common-content">
      <nut-button class="recharge-button" type="primary" @click="handleSubmit">
        <div class="recharge-button-inner">
          <div>¥{{ realParValue }}</div>
          <div class="recharge-button-text">立即充值</div>
        </div>
      </nut-button>
      <RechargeTipsView :tips="tips" />
    </div>
    <ConfirmDialog v-model:visible="confirmDialogVisible" @ok="goPay">
      <template #info>
        <ConfirmDialogInfoItem label="充值账号" :content="form.phone" />
        <ConfirmDialogInfoItem label="充值金额" :content="`¥${form.parValue.toFixed(2)}`" danger />
        <ConfirmDialogInfoItem label="优惠金额" :content="`¥${discountParValue.toFixed(2)}`" />
        <ConfirmDialogInfoItem label="实付金额" :content="`¥${realParValue}`" danger />
      </template>
    </ConfirmDialog>
  </NutForm>
  <PhoneBillRechargeStep1 v-if="current === 'step1'" />
  <PhoneBillRechargeStep2
    v-else-if="current === 'step2'"
    v-bind="props"
    @go-pay="emit('goPay', $event)"
  />
</template>
<script setup lang="ts">
import {
  Form as NutForm,
  FormItem as NutFormItem,
  RadioGroup as NutRadioGroup,
  Radio as NutRadio,
  Input as NutInput,
  Button as NutButton,
} from '@nutui/nutui-taro';
import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types';
import { reactive, ref, computed } from 'vue';
import BlRadio from '../../components/Radio/Radio.vue';
import { FormValidator } from '../../utils';
import {
  useLifeRechargeContext,
  BlLifeRecharge,
  LifePhoneDataCreateLifePayOrderInput,
  LifeRechargeConstants,
} from '@life-payment/core-vue';
import RechargeTipsView from '../../components/RechargeTipsView/RechargeTipsView.vue';
import ConfirmDialog from '../../components/Dialog/ConfirmDialog.vue';
import ConfirmDialogInfoItem from '../../components/Dialog/ConfirmDialogInfoItem.vue';
import { useGetRate, useGetPhoneParValue } from '../../hooks';
import { computed, provide } from 'vue';
import { useStepper } from 'senin-mini/hooks';
import { PhoneBillRechargeContextKey } from './context';
import PhoneBillRechargeStep1 from './PhoneBillRechargeStep1.vue';
import PhoneBillRechargeStep2 from './PhoneBillRechargeStep2.vue';
defineOptions({
  name: 'PhoneBillRecharge',
@@ -119,88 +26,14 @@
  isDev: false,
});
const form = reactive({
  ispCode: '',
  phone: '',
  parValue: 0,
  name: '',
});
const stepperInfo = useStepper(['step1', 'step2'], 'step2');
const current = computed(() => stepperInfo.current.value);
const emit = defineEmits<{
  (e: 'goPay', orderNo: string): void;
}>();
const { lifePayPhoneRate } = useGetRate();
const { phoneParValueList } = useGetPhoneParValue();
const parValueList = computed(
  () => phoneParValueList.value.find((x) => x.ispCode === form.ispCode)?.parValue ?? []
);
function changeIspCode(val: LifeRechargeConstants.IspCode) {
  const phoneParValueItem = phoneParValueList.value.find((x) => x.ispCode === val);
  if (phoneParValueItem.parValue.every((x) => Number(x) !== form.parValue)) {
    form.parValue = 0;
  }
}
const realParValue = computed(() =>
  blLifeRecharge.getRechargeParValue(form.parValue, lifePayPhoneRate.value)
);
const discountParValue = computed(() => form.parValue - Number(realParValue.value));
const { blLifeRecharge } = useLifeRechargeContext();
const rules = reactive<FormRules>({
  ispCode: [{ required: true, message: '请选择运营商' }],
  phone: [
    { required: true, message: '请输入充值手机号' },
    { validator: FormValidator.validatorPhoneNumber, message: '请输入正确的手机号' },
  ],
  name: [{ required: true, message: '请输入姓名' }],
  parValue: [
    { required: true, message: '请选择充值金额', validator: FormValidator.validatorNumberNotNull },
  ],
provide(PhoneBillRechargeContextKey, {
  ...stepperInfo,
});
const formRef = ref<any>(null);
function handleSubmit() {
  if (!formRef.value) return;
  formRef.value.validate().then(({ valid, errors }: any) => {
    if (valid) {
      recharge();
    }
  });
}
const tips = [
  '平台提供慢充服务,订单提交后,话费将于0 - 24小时内到账。若未能按时到账,系统将自动发起退款。',
  '充值期间,若同一号码款项未到账,请勿在其他平台重复充值;主副卡不可同时充值。因上述操作导致的资金损失,由用户自行承担。',
  '本平台话费充值服务不适用于已停机号码。电信号码若有欠费,也无法完成充值。电信已完成维护的区域包括:广东、江苏、湖北、四川、江西、河北、河南、福建、辽宁。其它区域正在分批次进行维护中,在此期间可能会出现充值不成功并自动退款的情况,请您谅解。',
  '如接到陌生来电,对方以缴费或误操作等理由要求处理款项,务必立即拉黑,谨防诈骗。',
  '充值发票由运营商提供,您可登录网上营业厅下载电子发票。',
];
const confirmDialogVisible = ref(false);
function recharge() {
  confirmDialogVisible.value = true;
}
async function goPay() {
  try {
    let params: LifePhoneDataCreateLifePayOrderInput = {
      userId: blLifeRecharge.accountModel.userId,
      productData: {
        ispCode: form.ispCode,
        parValue: props.isDev ? 0.1 : form.parValue,
        phone: form.phone,
        name: form.ispCode === BlLifeRecharge.constants.IspCode.dianxin ? form.name : '',
      },
    };
    let res = await blLifeRecharge.services.createLifePayPhoneOrder(params);
    emit('goPay', res.orderNo);
  } catch (error) {}
}
</script>
packages/components/src/views/PhoneBillRecharge/PhoneBillRechargeBaseForm.vue
New file
@@ -0,0 +1,82 @@
<template>
  <NutForm
    :model-value="form"
    ref="formRef"
    :rules="rules"
    label-position="top"
    class="order-bill-recharge phone"
  >
    <NutFormItem label="选择运营商:" class="bole-form-item" prop="ispCode" required>
      <NutRadioGroup v-model="form.ispCode" direction="horizontal">
        <BlRadio
          :label="key"
          v-for="(val, key) in BlLifeRecharge.constants.IspCodeText"
          :key="key"
          >{{ val }}</BlRadio
        >
      </NutRadioGroup>
    </NutFormItem>
    <NutFormItem label="充值手机号" class="bole-form-item" prop="phone" required>
      <NutInput
        v-model.trim="form.phone"
        class="bole-input-text"
        placeholder="请填写您需要充值的手机号码"
        type="text"
      />
    </NutFormItem>
    <NutFormItem
      label="姓名"
      class="bole-form-item"
      prop="name"
      required
      v-if="form.ispCode === BlLifeRecharge.constants.IspCode.dianxin"
    >
      <NutInput
        v-model.trim="form.name"
        class="bole-input-text"
        placeholder="请填写您的姓名"
        type="text"
      />
    </NutFormItem>
    <slot></slot>
  </NutForm>
</template>
<script setup lang="ts">
import {
  Form as NutForm,
  FormItem as NutFormItem,
  RadioGroup as NutRadioGroup,
  Input as NutInput,
} from '@nutui/nutui-taro';
import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types';
import { reactive, ref, computed } from 'vue';
import BlRadio from '../../components/Radio/Radio.vue';
import { FormValidator } from '../../utils';
import { BlLifeRecharge } from '@life-payment/core-vue';
defineOptions({
  name: 'PhoneBillRechargeBaseForm',
});
const form = defineModel<{
  ispCode: string;
  phone: string;
  name: string;
}>('form');
const rules = reactive<FormRules>({
  ispCode: [{ required: true, message: '请选择运营商' }],
  phone: [
    { required: true, message: '请输入充值手机号' },
    { validator: FormValidator.validatorPhoneNumber, message: '请输入正确的手机号' },
  ],
  name: [{ required: true, message: '请输入姓名' }],
});
const formRef = ref<any>(null);
defineExpose({
  validate: computed(() => formRef.value.validate),
});
</script>
packages/components/src/views/PhoneBillRecharge/PhoneBillRechargeStep1.vue
New file
@@ -0,0 +1,61 @@
<template>
  <PhoneBillRechargeBaseForm ref="formRef" v-model:form="form">
    <div class="common-content">
      <nut-button class="recharge-button" type="primary" @click="handleNext">
        <div class="recharge-button-inner">
          <div class="recharge-button-text">立即充值</div>
        </div>
      </nut-button>
    </div>
  </PhoneBillRechargeBaseForm>
</template>
<script setup lang="ts">
import {
  Form as NutForm,
  FormItem as NutFormItem,
  RadioGroup as NutRadioGroup,
  Radio as NutRadio,
  Input as NutInput,
  Button as NutButton,
} from '@nutui/nutui-taro';
import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types';
import { reactive, ref, computed, provide } from 'vue';
import BlRadio from '../../components/Radio/Radio.vue';
import { FormValidator } from '../../utils';
import {
  useLifeRechargeContext,
  BlLifeRecharge,
  LifePhoneDataCreateLifePayOrderInput,
  LifeRechargeConstants,
} from '@life-payment/core-vue';
import RechargeTipsView from '../../components/RechargeTipsView/RechargeTipsView.vue';
import ConfirmDialog from '../../components/Dialog/ConfirmDialog.vue';
import ConfirmDialogInfoItem from '../../components/Dialog/ConfirmDialogInfoItem.vue';
import { useGetRate, useGetPhoneParValue } from '../../hooks';
import PhoneBillRechargeBaseForm from './PhoneBillRechargeBaseForm.vue';
import { usePhoneBillRechargeContext } from './context';
defineOptions({
  name: 'PhoneBillRechargeStep1',
});
const form = reactive({
  ispCode: '',
  phone: '',
  name: '',
});
const { goToNext } = usePhoneBillRechargeContext();
const formRef = ref<any>(null);
function handleNext() {
  if (!formRef.value) return;
  formRef.value.validate().then(({ valid, errors }: any) => {
    if (valid) {
      goToNext();
    }
  });
}
</script>
packages/components/src/views/PhoneBillRecharge/PhoneBillRechargeStep2.vue
New file
@@ -0,0 +1,192 @@
<template>
  <NutForm
    :model-value="form"
    ref="formRef"
    :rules="rules"
    label-position="top"
    class="order-bill-recharge phone"
  >
    <NutFormItem class="bole-form-item">
      <!-- <AccountAddCard /> -->
      <NutRadioGroup v-model="val1" direction="horizontal" class="par-account-list">
        <NutRadio label="1" shape="button">Option 1</NutRadio>
        <NutRadio label="2" shape="button">Option 2</NutRadio>
        <NutRadio label="3" shape="button">Option 3</NutRadio>
        <NutRadio label="4" shape="button">Option 4</NutRadio>
        <NutRadio label="5" shape="button">Option 5</NutRadio>
        <NutRadio label="6" shape="button">Option 6</NutRadio>
      </NutRadioGroup>
      <AccountCard title="充值手机号" content="18858418480" remark="的地方骄傲的开始和">
        <template #action>
          <div class="account-card-action">新增</div>
        </template>
      </AccountCard>
    </NutFormItem>
    <NutFormItem label="选择充值金额" class="bole-form-item" prop="parValue" required>
      <NutRadioGroup v-model="form.parValue" direction="horizontal" class="parValue-radio-group">
        <NutRadio
          :label="Number(item)"
          :key="item"
          shape="button"
          v-for="item in parValueList"
          class="parValue-item"
        >
          <div class="parValue-item-inner">
            <div class="amount-wrapper">
              <div class="amount">{{ item }}</div>
              <div class="unit">元</div>
            </div>
            <div class="price-wrapper">
              <div class="price-text">折后</div>
              <div class="price">
                {{ blLifeRecharge.getRechargeParValue(item, lifePayPhoneRate) }}元
              </div>
            </div>
            <div class="discountTag">{{ lifePayPhoneRate * 100 }}折</div>
          </div>
        </NutRadio>
      </NutRadioGroup>
    </NutFormItem>
    <div class="common-content">
      <nut-button class="recharge-button" type="primary" @click="handleSubmit">
        <div class="recharge-button-inner">
          <div>¥{{ realParValue }}</div>
          <div class="recharge-button-text">立即充值</div>
        </div>
      </nut-button>
      <RechargeTipsView :tips="tips" />
    </div>
    <ConfirmDialog v-model:visible="confirmDialogVisible" @ok="goPay">
      <template #info>
        <ConfirmDialogInfoItem label="充值账号" :content="form.phone" />
        <ConfirmDialogInfoItem label="充值金额" :content="`¥${form.parValue.toFixed(2)}`" danger />
        <ConfirmDialogInfoItem label="优惠金额" :content="`¥${discountParValue.toFixed(2)}`" />
        <ConfirmDialogInfoItem label="实付金额" :content="`¥${realParValue}`" danger />
      </template>
    </ConfirmDialog>
  </NutForm>
</template>
<script setup lang="ts">
import {
  Form as NutForm,
  FormItem as NutFormItem,
  RadioGroup as NutRadioGroup,
  Radio as NutRadio,
  Input as NutInput,
  Button as NutButton,
} from '@nutui/nutui-taro';
import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types';
import { reactive, ref, computed, provide } from 'vue';
import { FormValidator } from '../../utils';
import {
  useLifeRechargeContext,
  BlLifeRecharge,
  LifePhoneDataCreateLifePayOrderInput,
  LifeRechargeConstants,
} from '@life-payment/core-vue';
import RechargeTipsView from '../../components/RechargeTipsView/RechargeTipsView.vue';
import ConfirmDialog from '../../components/Dialog/ConfirmDialog.vue';
import ConfirmDialogInfoItem from '../../components/Dialog/ConfirmDialogInfoItem.vue';
import { useGetRate, useGetPhoneParValue } from '../../hooks';
import { CustomerServiceTips } from '../../constants';
import AccountAddCard from '../../components/Card/AccountAddCard.vue';
import AccountCard from '../../components/Card/AccountCard.vue';
defineOptions({
  name: 'PhoneBillRechargeStep2',
});
type Props = {
  isDev?: boolean;
};
const props = withDefaults(defineProps<Props>(), {
  isDev: false,
});
const form = reactive({
  ispCode: '',
  phone: '',
  parValue: 0,
  name: '',
});
const val1 = ref('');
const emit = defineEmits<{
  (e: 'goPay', orderNo: string): void;
}>();
const { lifePayPhoneRate } = useGetRate();
const { phoneParValueList } = useGetPhoneParValue();
const parValueList = computed(() => {
  const _parValueList =
    phoneParValueList.value.find((x) => x.ispCode === form.ispCode)?.parValue ?? [];
  return blLifeRecharge.filterParValueList(_parValueList);
});
function changeIspCode(val: LifeRechargeConstants.IspCode) {
  const phoneParValueItem = phoneParValueList.value.find((x) => x.ispCode === val);
  if (phoneParValueItem.parValue.every((x) => Number(x) !== form.parValue)) {
    form.parValue = 0;
  }
}
const realParValue = computed(() =>
  blLifeRecharge.getRechargeParValue(form.parValue, lifePayPhoneRate.value)
);
const discountParValue = computed(() => form.parValue - Number(realParValue.value));
const { blLifeRecharge } = useLifeRechargeContext();
const rules = reactive<FormRules>({
  parValue: [
    { required: true, message: '请选择充值金额', validator: FormValidator.validatorNumberNotNull },
  ],
});
const formRef = ref<any>(null);
function handleSubmit() {
  if (!formRef.value) return;
  formRef.value.validate().then(({ valid, errors }: any) => {
    if (valid) {
      recharge();
    }
  });
}
const tips = [
  '平台提供慢充服务,订单提交后,话费将于0 - 24小时内到账。若未能按时到账,系统将自动发起退款。',
  '充值期间,若同一号码款项未到账,请勿在其他平台重复充值;主副卡不可同时充值。因上述操作导致的资金损失,由用户自行承担。',
  '本平台话费充值服务不适用于已停机号码。电信号码若有欠费,也无法完成充值。电信已完成维护的区域包括:广东、江苏、湖北、四川、江西、河北、河南、福建、辽宁。其它区域正在分批次进行维护中,在此期间可能会出现充值不成功并自动退款的情况,请您谅解。',
  '如接到陌生来电,对方以缴费或误操作等理由要求处理款项,务必立即拉黑,谨防诈骗。',
  '充值发票由运营商提供,您可登录网上营业厅下载电子发票。',
  CustomerServiceTips,
];
const confirmDialogVisible = ref(false);
function recharge() {
  confirmDialogVisible.value = true;
}
async function goPay() {
  try {
    let params: LifePhoneDataCreateLifePayOrderInput = {
      userId: blLifeRecharge.accountModel.userId,
      productData: {
        ispCode: form.ispCode,
        parValue: props.isDev ? 0.1 : form.parValue,
        phone: form.phone,
        name: form.ispCode === BlLifeRecharge.constants.IspCode.dianxin ? form.name : '',
      },
    };
    let res = await blLifeRecharge.services.createLifePayPhoneOrder(params);
    emit('goPay', res.orderNo);
  } catch (error) {}
}
</script>
packages/components/src/views/PhoneBillRecharge/context.ts
New file
@@ -0,0 +1,27 @@
import type { InjectionKey, UnwrapNestedRefs, Ref } from 'vue';
import { inject } from 'vue';
import { UseStepperReturn } from 'senin-mini/hooks';
type PhoneBillRechargeSteps = 'step1' | 'step2';
export interface PhoneBillRechargeContext
  extends UseStepperReturn<
    PhoneBillRechargeSteps,
    PhoneBillRechargeSteps[],
    PhoneBillRechargeSteps
  > {
  //   form: UnwrapNestedRefs<{
  //     ispCode: string;
  //     phone: string;
  //     parValue: number;
  //     name: string;
  //   }>;
}
export const PhoneBillRechargeContextKey: InjectionKey<PhoneBillRechargeContext> = Symbol(
  'PhoneBillRechargeContextKey'
);
export function usePhoneBillRechargeContext() {
  return inject(PhoneBillRechargeContextKey);
}
packages/components/src/views/RechargeResultView/RechargeResultView.vue
@@ -1,12 +1,14 @@
<template>
  <div class="recharge-result-view">
    <div class="recharge-result-view-title">{{ title }}</div>
    <div class="recharge-result-view-subtitle">订单号:{{ orderNo }}</div>
    <div class="recharge-result-view-tips">
      同一号码充值期间,未到账前切勿在其他任何平台再次充值。因此造成的资金损失须用户自行承担!!!
    </div>
    <div class="recharge-result-view-warning">
      如接到陌生来电,对方以缴费或误操作等理由要求处理款项,务必立即拉黑,谨防诈骗!!!
    </div>
    <div class="recharge-result-view-customerService">{{ CustomerServiceTips }}</div>
    <div class="recharge-result-view-btn-wrapper">
      <div class="recharge-result-view-btn" @click="emit('goBackHome')">回首页</div>
    </div>
@@ -15,6 +17,7 @@
<script setup lang="ts">
import { LifeRechargeConstants } from '@life-payment/core-vue';
import { CustomerServiceTips } from '../../constants';
defineOptions({
  name: 'RechargeResultView',
packages/components/src/views/electricBillRecharge/electricBillRecharge.vue
@@ -110,7 +110,7 @@
          label="电网类型"
          :content="blLifeRecharge.constants.ElectricTypeText[form.electricType]"
        />
        <ConfirmDialogInfoItem label="电费类型" content="住宅" />
        <ConfirmDialogInfoItem :label-width="96" label="户号" :content="form.electricAccount" />
        <ConfirmDialogInfoItem label="充值金额" :content="`¥${form.parValue.toFixed(2)}`" danger />
        <ConfirmDialogInfoItem label="优惠金额" :content="`¥${discountParValue.toFixed(2)}`" />
        <ConfirmDialogInfoItem label="实付金额" :content="`¥${realParValue}`" danger />
@@ -145,6 +145,7 @@
import NumberInput from '../../components/Input/NumberInput.vue';
import { useGetRate, useGetElectricParValue } from '../../hooks';
import { FormValidator } from '../../utils';
import { CustomerServiceTips } from '../../constants';
defineOptions({
  name: 'electricBillRecharge',
@@ -175,9 +176,11 @@
const { lifePayElectricRate } = useGetRate();
const { electricParValueList } = useGetElectricParValue();
const parValueList = computed(
  () => electricParValueList.value.find((x) => x.cityName === form.province)?.parValue ?? []
);
const parValueList = computed(() => {
  const parValueList =
    electricParValueList.value.find((x) => x.cityName === form.province)?.parValue ?? [];
  return blLifeRecharge.filterParValueList(parValueList);
});
const electricCityList = computed(
  () => electricParValueList.value.find((x) => x.cityName === form.province)?.childCityList ?? []
@@ -234,6 +237,7 @@
  '为确保充值顺利进行,目前平台不支持对欠款金额超过1000元的账户进行充值,且每次充值金额必须高于欠费总额。',
  '如接到陌生来电,对方以缴费或误操作等理由要求处理款项,务必立即拉黑,谨防诈骗。',
  '下单时,请您务必准确填写完整的省市及户号信息。充值完成后,发票由运营商提供,您可登录网上营业厅下载对应的电子发票。',
  CustomerServiceTips,
];
const confirmDialogVisible = ref(false);
packages/core-vue/package.json
@@ -2,7 +2,6 @@
  "name": "@life-payment/core-vue",
  "main": "src/index.ts",
  "module": "./src/index.ts",
  "types": "./dist/types/index.d.ts",
  "source": "./src/index.ts",
  "version": "0.0.3",
  "scripts": {
packages/core/package.json
@@ -2,7 +2,6 @@
  "name": "@life-payment/core",
  "main": "src/index.ts",
  "module": "./src/index.ts",
  "types": "./dist/types/index.d.ts",
  "source": "./src/index.ts",
  "version": "0.0.2",
  "scripts": {
packages/core/src/lifeRecharge.ts
@@ -45,4 +45,10 @@
  getRechargeParValue(amount: number | string, rate: number) {
    return (Number(amount) * rate).toFixed(2);
  }
  MaxParValue = 300;
  filterParValueList(parValueList: string[]) {
    return parValueList.filter((x) => Number(x) <= this.MaxParValue);
  }
}