li.shaoyi 3 rokov pred
rodič
commit
392929f00a

+ 12 - 11
public/proto/thj.proto

@@ -264,17 +264,18 @@ message UserReceiveIsDefaultRsp {
 // 新增修改用户发票信息请求
 message UserReceiptInfoReq {
 	optional MessageHead Header = 1;
-	optional uint64 ClientSerialID = 2; // 客户端唯一ID
-	optional uint32 UserID = 3; // 用户ID
-	optional string UserName = 4; // 发票抬头姓名
-	optional uint32 ReceiptType = 5; // 发票类型
-	optional string TaxpayerID = 6; // 纳税人识别号
-	optional string ContactInfo = 7; // 联系方式
-	optional uint64 ReceiptInfoId = 8; // 修改时填用户发票信息id
-	optional string ReceiptBank = 9; // 发票开户行[发票类型:企业]
-	optional string ReceiptAccount = 10; // 发票帐号[发票类型:企业]
-	optional string Address = 11; // 地址[发票类型:企业]
-	optional string IDNum = 12; // 身份证号码[发票类型:个人]
+		optional uint64 ClientSerialID = 2; // 客户端唯一ID
+		optional uint32 UserID = 3; // 用户ID
+		optional string UserName = 4; // 发票抬头姓名
+		optional uint32 ReceiptType = 5; // 发票类型
+		optional string TaxpayerID = 6; // 纳税人识别号
+		optional string ContactInfo = 7; // 联系方式
+		optional uint64 ReceiptInfoId = 8; // 修改时填用户发票信息id
+		optional string ReceiptBank = 9; // 发票开户行[发票类型:企业]
+		optional string ReceiptAccount = 10; // 发票帐号[发票类型:企业]
+		optional string Address = 11; // 地址[发票类型:企业]
+		optional string IDNum = 12; // 身份证号码[发票类型:个人]
+		optional string Email = 13; // 收件邮箱
 }
 
 // 新增修改用户发票信息请求响应

+ 2 - 1
src/business/user/index.ts

@@ -1,2 +1,3 @@
 export { useAccountCancellation, useAccountPassword, addAuthReq } from './account'
-export { useAddress, useAddressForm } from './address'
+export { useAddress, useAddressForm } from './address'
+export { useInvoice, useInvoiceForm } from './invoice'

+ 102 - 0
src/business/user/invoice.ts

@@ -0,0 +1,102 @@
+import { shallowRef, reactive } from 'vue'
+import { useDataTable } from '@/hooks/datatable'
+import { queryWrUserReceiptInfo, userReceiptInfo, delUserReceiptInfo } from '@/services/api/user'
+import { useLoginStore } from '@/stores'
+import { getCertificateTypeList } from '@/constants/certificate'
+
+export function useInvoice() {
+    const { getUserId } = useLoginStore()
+    const { dataList, total, pageIndex, pageSize } = useDataTable<Model.WrUserReceiptInfoRsp>()
+    const loading = shallowRef(false)
+    const columns = shallowRef([])
+
+    const getUserInvoiceList = () => {
+        loading.value = true
+        return queryWrUserReceiptInfo({
+            data: {
+                userid: getUserId(),
+            },
+            success: (res) => {
+                total.value = res.total
+                dataList.value = res.data
+            },
+            complete: () => {
+                loading.value = false
+            }
+        })
+    }
+
+    return {
+        loading,
+        dataList,
+        columns,
+        total,
+        pageIndex,
+        pageSize,
+        getUserInvoiceList,
+    }
+}
+
+export function useInvoiceForm(selectedRow?: Model.WrUserReceiptInfoRsp) {
+    const { getUserId } = useLoginStore()
+    const loading = shallowRef(false)
+    const certificateTypeList = getCertificateTypeList()
+
+    const formData = reactive<Proto.UserReceiptInfoReq>({
+        UserID: getUserId(), // 用户ID
+        UserName: '', // 发票抬头姓名
+        ReceiptType: 1, // 发票类型
+        TaxpayerID: '', // 纳税人识别号
+        ContactInfo: '', // 联系方式
+        ReceiptInfoId: 0, // 修改时填用户发票信息id
+        ReceiptBank: '', // 发票开户行[发票类型:企业]
+        ReceiptAccount: '', // 发票帐号[发票类型:企业]
+        Address: '', // 地址[发票类型:企业]
+    })
+
+    if (selectedRow?.autoid) {
+        ({
+            autoid: formData.ReceiptInfoId,
+            userid: formData.UserID,
+            username: formData.UserName,
+            receipttype: formData.ReceiptType,
+            taxpayerid: formData.TaxpayerID,
+            contactinfo: formData.ContactInfo,
+            receiptbank: formData.ReceiptBank,
+            receiptaccount: formData.ReceiptAccount,
+            address: formData.Address,
+            idnum: formData.IDNum,
+            email: formData.Email,
+        } = selectedRow)
+    }
+
+    const addOrUpdate = () => {
+        loading.value = true
+        return userReceiptInfo({
+            data: formData,
+            complete: () => {
+                loading.value = false
+            }
+        })
+    }
+
+    const deleteInvoice = () => {
+        loading.value = true
+        return delUserReceiptInfo({
+            data: {
+                ReceiptInfoId: formData.ReceiptInfoId
+            },
+            complete: () => {
+                loading.value = false
+            }
+        })
+    }
+
+    return {
+        loading,
+        certificateTypeList,
+        formData,
+        addOrUpdate,
+        deleteInvoice,
+    }
+}

+ 20 - 0
src/constants/certificate.ts

@@ -0,0 +1,20 @@
+import { useEnumStore } from '@/stores'
+
+const { getEnumTypeList, getEnumTypeName } = useEnumStore()
+
+/**
+ * 获取证件类型列表
+ * @returns 
+ */
+export function getCertificateTypeList() {
+    return getEnumTypeList('certificatetype')
+}
+
+/**
+ * 获取证件类型名称
+ * @returns 
+ */
+export function getCertificateTypeName(value?: number) {
+    const enums = getCertificateTypeList()
+    return getEnumTypeName(enums, value)
+}

+ 11 - 1
src/packages/mobile/components/base/region/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="app-region" @click="show = true">
+    <div class="app-region" @click="onClick">
         <slot :label="inputValue">
             <input class="app-region__input" v-model="inputValue" :placeholder="placeholder" readonly />
         </slot>
@@ -27,6 +27,10 @@ const props = defineProps({
         type: String,
         default: '请选择地区'
     },
+    readonly: {
+        type: Boolean,
+        default: false
+    },
     placeholder: {
         type: String,
         default: '请选择'
@@ -44,6 +48,12 @@ const selectedValue = computed({
     set: (val) => emit('update:modelValue', val)
 })
 
+const onClick = () => {
+    if (!props.readonly) {
+        show.value = true
+    }
+}
+
 // 选中项变化时触发
 const onChange = ({ value }: { value: string | number }) => {
     emit('change', value)

+ 4 - 1
src/packages/mobile/components/base/select/index.less

@@ -4,7 +4,10 @@
     align-items: center;
 
     &__input {
-        flex : 1;
+        flex: 1;
+    }
+
+    input {
         width: 100%;
     }
 }

+ 14 - 4
src/packages/mobile/components/base/select/index.vue

@@ -1,9 +1,9 @@
 <template>
-    <div class="app-select" @click="show = true">
-        <slot :value="inputValue">
-            <input class="app-select__input" v-model="inputValue" :placeholder="placeholder" readonly />
+    <div class="app-select" @click="onClick">
+        <slot :label="inputValue">
+            <input class="'app-select__input'" v-model="inputValue" :placeholder="placeholder" readonly />
         </slot>
-        <Popup v-model:show="show" position="bottom" teleport="body">
+        <Popup v-model:show="show" position="bottom" teleport="body" round>
             <Picker :columns="columns" @cancel="onCancel" @confirm="onConfirm" />
         </Popup>
     </div>
@@ -28,6 +28,10 @@ const props = defineProps({
             value: 'value'
         })
     },
+    readonly: {
+        type: Boolean,
+        default: false
+    },
     placeholder: {
         type: String,
         default: '请选择'
@@ -55,6 +59,12 @@ const inputValue = computed(() => {
     return ''
 })
 
+const onClick = () => {
+    if (!props.readonly) {
+        show.value = true
+    }
+}
+
 const onCancel = (currentValue: PickerOption, currentIndex: number) => {
     show.value = false
     emit('cancel', props.modelValue, currentIndex)

+ 5 - 0
src/packages/mobile/router/index.ts

@@ -348,6 +348,11 @@ const routes: Array<RouteRecordRaw> = [
         name: 'mine-address',
         component: () => import('../views/mine/address/index.vue'),
       },
+      {
+        path: 'invoice',
+        name: 'mine-invoice',
+        component: () => import('../views/mine/invoice/index.vue'),
+      },
     ]
   },
   {

+ 1 - 1
src/packages/mobile/views/home/components/main/index.vue

@@ -17,7 +17,7 @@
             <app-iconfont icon="icon-chanpinjieshao" label-direction="bottom">产品介绍</app-iconfont>
           </li>
           <li>
-            <app-iconfont icon="icon-chanpinjiage" label-direction="bottom">产品价格</app-iconfont>
+            <app-iconfont icon="icon-chanpinjiage" label-direction="bottom">合同转让</app-iconfont>
           </li>
           <li @click="routerTo('rules-ptgz')">
             <app-iconfont icon="icon-pingtaiguize" label-direction="bottom">平台规则</app-iconfont>

+ 1 - 1
src/packages/mobile/views/home/components/mine/index.vue

@@ -59,7 +59,7 @@
         <li @click="routerTo('my-wareorder')">
           <app-iconfont icon="icon-wodecangdan" label-direction="bottom">我的仓单</app-iconfont>
         </li>
-        <li>
+        <li @click="routerTo('mine-invoice')">
           <app-iconfont icon="icon-fapiaoxinxi" label-direction="bottom">发票信息</app-iconfont>
         </li>
         <li @click="routerTo('mine-address')">

+ 3 - 3
src/packages/mobile/views/mine/address/components/edit/index.vue

@@ -1,6 +1,6 @@
 <template>
-    <app-modal class="main-address-edit" direction="right" height="100%" v-model:show="showModal" :refresh="refresh">
-        <app-view class="g-form" style="">
+    <app-modal direction="right" height="100%" v-model:show="showModal" :refresh="refresh">
+        <app-view class="g-form">
             <template #header>
                 <app-navbar :title="selectedRow.autoid ? '修改收货地址' : '新增收货地址'" @back="closed" />
             </template>
@@ -10,7 +10,7 @@
                         label="收货人" placeholder="必填" />
                     <Field v-model="formData.PhoneNum" :rules="formRules.PhoneNum" type="tel" name="PhoneNum"
                         label="联系电话" placeholder="必填" />
-                    <Field :rules="formRules.Region" name="Region" label="收货地区" placeholder="请选择" is-link>
+                    <Field :rules="formRules.Region" name="Region" label="收货地区" is-link>
                         <template #input>
                             <app-region v-model="formData.DistrictID" :label="regionName" @finish="onRegionFinish" />
                         </template>

+ 1 - 1
src/packages/mobile/views/mine/address/index.vue

@@ -13,7 +13,7 @@
                         <div class="list-item__info-name">
                             <span>{{ item.receivername }}</span>
                             <span>{{ item.phonenum }}</span>
-                            <Tag type="danger" round v-if="item.isdefault" />
+                            <Tag type="danger" round v-if="item.isdefault">默认</Tag>
                         </div>
                         <div class="list-item__info-address">
                             <span>{{ [item.provincename, item.cityname, item.districtname, item.address].join(' ')

+ 127 - 0
src/packages/mobile/views/mine/invoice/components/edit/index.vue

@@ -0,0 +1,127 @@
+<template>
+    <app-modal direction="right" height="100%" v-model:show="showModal" :refresh="refresh">
+        <app-view class="g-form">
+            <template #header>
+                <app-navbar :title="selectedRow.autoid ? '修改发票信息' : '新增发票信息'" @back="closed" />
+            </template>
+            <Form ref="formRef" class="g-form__container" @submit="formSubmit">
+                <CellGroup inset>
+                    <Field name="ReceiptType" label="发票类型" :rules="formRules.ReceiptType"
+                        :is-link="!selectedRow.autoid">
+                        <template #input>
+                            <app-select v-model="formData.ReceiptType" :options="receiptTypeList"
+                                :readonly="!!selectedRow.autoid" />
+                        </template>
+                    </Field>
+                    <Field v-model="formData.UserName" :rules="formRules.UserName" name="UserName" label="发票抬头"
+                        placeholder="必填" />
+                    <template v-if="formData.ReceiptType === 2">
+                        <Field v-model="formData.TaxpayerID" :rules="formRules.TaxpayerID" name="TaxpayerID" label="税号"
+                            placeholder="必填" />
+                        <Field v-model="formData.ReceiptBank" :rules="formRules.ReceiptBank" name="ReceiptBank"
+                            label="开户银行" placeholder="选填" />
+                        <Field v-model="formData.ReceiptAccount" :rules="formRules.ReceiptAccount" name="ReceiptAccount"
+                            label="银行账号" placeholder="选填" />
+                        <Field v-model="formData.Address" :rules="formRules.Address" name="Address" label="企业地址"
+                            placeholder="选填" />
+                        <Field v-model="formData.ContactInfo" :rules="formRules.ContactInfo" name="ContactInfo"
+                            label="企业电话" placeholder="选填" />
+                    </template>
+                    <Field v-model="formData.Email" :rules="formRules.Email" name="Email" label="邮箱" placeholder="选填" />
+                </CellGroup>
+            </Form>
+            <template #footer>
+                <div class="g-form__footer">
+                    <Button type="warning" @click="formDelete" round block v-if="selectedRow.autoid">删除</Button>
+                    <Button type="primary" @click="formRef?.submit" round block>保存</Button>
+                </div>
+            </template>
+        </app-view>
+    </app-modal>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, PropType } from 'vue'
+import { CellGroup, Button, Field, Form, FormInstance, Toast, FieldRule } from 'vant'
+import { fullloading, dialog } from '@/utils/vant'
+import { validateRules } from '@/constants/regex'
+import { useInvoiceForm } from '@/business/user'
+import AppModal from '@/components/base/modal/index.vue'
+import AppSelect from '@mobile/components/base/select/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.WrUserReceiptInfoRsp>,
+        default: () => ({})
+    }
+})
+
+const { formData, addOrUpdate, deleteInvoice } = useInvoiceForm(props.selectedRow)
+const formRef = shallowRef<FormInstance>()
+const showModal = shallowRef(true)
+const refresh = shallowRef(false) // 是否刷新父组件数据
+
+const receiptTypeList = [
+    { label: '个人', value: 1 },
+    { label: '企业', value: 2 }
+]
+
+// 表单验证规则
+const formRules: { [key in keyof Proto.UserReceiptInfoReq]?: FieldRule[] } = {
+    UserName: [{
+        required: true,
+        message: '请输入发票抬头',
+    }],
+    TaxpayerID: [{
+        required: true,
+        message: '请输入纳税人识别号',
+    }],
+    Email: [{
+        validator: (val) => {
+            if (validateRules.email.validate(val)) {
+                return true
+            }
+            return validateRules.email.message
+        }
+    }],
+}
+
+// 表单提交
+const formSubmit = () => {
+    fullloading((hideLoading) => {
+        addOrUpdate().then(() => {
+            hideLoading()
+            closed(true)
+        }).catch((err) => {
+            Toast.fail(err)
+        })
+    })
+}
+
+// 删除发票
+const formDelete = () => {
+    dialog('是否删除该发票?', {
+        showCancelButton: true
+    }).then(() => {
+        fullloading((hideLoading) => {
+            deleteInvoice().then(() => {
+                hideLoading()
+                closed(true)
+            }).catch((err) => {
+                Toast.fail(err)
+            })
+        })
+    })
+}
+
+// 关闭弹窗
+const closed = (isRefresh = false) => {
+    refresh.value = isRefresh
+    showModal.value = false
+}
+
+// 暴露组件属性给父组件调用
+defineExpose({
+    closed,
+})
+</script>

+ 0 - 0
src/packages/mobile/views/mine/invoice/index.less


+ 84 - 0
src/packages/mobile/views/mine/invoice/index.vue

@@ -0,0 +1,84 @@
+<template>
+    <app-view class="mine-invoice">
+        <template #header>
+            <app-navbar title="发票信息" />
+        </template>
+        <RadioGroup class="mine-invoice__container" v-model="selectedRow" v-if="dataList.length">
+            <ul class="list">
+                <li class="list-item" v-for="(item, index) in dataList" :key="index">
+                    <div class="list-item__checkbox" @click="onChange(item)" v-if="showRadio">
+                        <Radio :name="item" checked-color="#ee0a24" />
+                    </div>
+                    <div class="list-item__info" @click="onChange(item)">
+                        <div class="list-item__info-name">
+                            <span>{{ item.receipttype }}</span>
+                            <span>{{ item.username }}</span>
+                            <Tag type="danger" round v-if="item.isdefault">默认</Tag>
+                        </div>
+                        <div class="list-item__info-address">
+                            <span>{{ item.taxpayerid }}</span>
+                        </div>
+                    </div>
+                    <div class="list-item__button">
+                        <Button icon="edit" @click="onEdit(item)" />
+                    </div>
+                </li>
+            </ul>
+        </RadioGroup>
+        <Empty v-else />
+        <template #footer>
+            <div class="g-form__footer">
+                <Button type="primary" @click="onEdit" round block>新增发票</Button>
+            </div>
+            <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)"
+                @closed="closeComponent" v-if="componentId" />
+        </template>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, defineAsyncComponent, PropType } from 'vue'
+import { RadioGroup, Radio, Button, Tag, Empty } from 'vant'
+import { useNavigation } from '@/hooks/navigation'
+import { useComponent } from '@/hooks/component'
+import { useInvoice } from '@/business/user'
+
+const componentMap = new Map<string, unknown>([
+    ['edit', defineAsyncComponent(() => import('./components/edit/index.vue'))],
+])
+
+const props = defineProps({
+    // 是否显示单选按钮
+    showRadio: {
+        type: Boolean,
+        default: false
+    },
+    checked: {
+        type: Object as PropType<Model.WrUserReceiptInfoRsp>,
+    }
+})
+
+const emit = defineEmits(['change'])
+const { beforeRouteLeave } = useNavigation()
+const { dataList, getUserInvoiceList } = useInvoice()
+const { componentRef, componentId, openComponent, closeComponent, closeComponentEach } = useComponent(getUserInvoiceList)
+const selectedRow = shallowRef(props.checked)
+
+// 选择发票
+const onChange = (item: Model.WrUserReceiptInfoRsp) => {
+    selectedRow.value = item
+    emit('change', item)
+}
+
+const onEdit = (item?: Model.WrUserReceiptInfoRsp) => {
+    selectedRow.value = item
+    openComponent('edit')
+}
+
+getUserInvoiceList()
+beforeRouteLeave(() => closeComponentEach())
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 1 - 0
src/types/model/user.d.ts

@@ -45,5 +45,6 @@ declare namespace Model {
         taxpayerid: string; // 纳税人识别号
         userid: number; // 用户ID
         username: string; // 户名(个人姓名或企业名称)
+        email: string;
     }
 }

+ 1 - 0
src/types/proto/user.d.ts

@@ -70,6 +70,7 @@ declare global {
             ReceiptAccount: string; // 发票帐号[发票类型:企业]
             Address: string; // 地址[发票类型:企业]
             IDNum?: string; // 身份证号码[发票类型:个人]
+            Email?: string; // 收件邮箱
         }
 
         /** 新增修改用户发票信息响应 */