| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- <!--
- * @Author: wanglj wanglijie@dashoo.cn
- * @Date: 2025-03-24 09:17:15
- * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-04-14 09:30:11
- * @FilePath: \labsop_h5\src\view\instr\detail.vue
- * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
- -->
- <template>
- <div class="entry-container">
- <van-form ref="formRef" required="auto" label-width="100">
- <h4>项目信息</h4>
- <van-cell-group>
- <van-field v-model="state.form.projectName" label="项目名称" readonly />
- <van-field v-model="state.form.projectSource" label="项目来源" readonly />
- <van-field v-model="state.form.projectIncharge" label="项目负责人" readonly />
- <van-field v-model="state.form.deptName" label="所属科室" readonly />
- <van-field v-model="state.form.subjName" label="费用科目" readonly />
- <van-field v-model="state.form.subSubjName" label="费用类型" readonly />
- <van-field v-model="showAmount" label="入账金额" readonly />
- <van-field v-model="showBalanceAmount" label="可用金额" readonly />
- <van-field v-model="state.form.purpose" label="事项" :rules="[{ required: true, message: '请输入事项' }]" />
- </van-cell-group>
- <h4>报销经费</h4>
- <van-cell-group>
- <van-field
- v-model="state.form.expendTime"
- label="支出日期"
- readonly
- is-link
- @click="openExpandTimePicker"
- :rules="[{ required: true, message: '请选择支出日期' }]"
- />
- <van-field v-model="state.form.handle" label="申请人" />
- <van-field v-model="state.form.expend" label="出纳" />
- <van-field v-model.number="state.form.amount" type="number" label="支出金额">
- <template #extra>元</template>
- </van-field>
- </van-cell-group>
- <h4>发票信息</h4>
- <van-cell-group v-for="(item, index) in state.form.invoice" :key="index" :class="{ mt10: index != 0 }">
- <van-field v-model="item.invoiceNo" label="发票号码" :rules="[{ required: true, message: '请输入发票号码' }]" />
- <van-field v-model.number="item.amount" type="number" label="金额" :rules="[{ required: true, message: '请输入金额' }]" />
- <van-field
- v-model="item.invoiceBy"
- label="开票日期"
- readonly
- is-link
- @click="openInvoiceByPicker(item, index)"
- :rules="[{ required: true, message: '请选择开票日期' }]"
- />
- <div class="file-card">
- <van-uploader v-model="item.fileList" :after-read="afterRead" preview-size="60" :preview-full-image="false" />
- </div>
- <van-cell v-if="index > 0" center>
- <template #default>
- <van-button class="w100" size="small" plain round type="danger" @click="deleteInvoice(index)">删除</van-button>
- </template>
- </van-cell>
- </van-cell-group>
- <van-button class="w100 mt10 mb10" size="small" plain round icon="plus" type="primary" @click="addInvoice"> 添加发票 </van-button>
- <h4>支付信息</h4>
- <van-cell-group v-for="(item, index) in state.form.payment" :key="index" :class="{ mt10: index != 0 }">
- <van-field v-model="item.payType" label="支付方式" readonly is-link @click="openPicker(item, index)">
- <template #input>
- <template v-if="item.payType == '10'">现金</template>
- <template v-else-if="item.payType == '20'">银行转账</template>
- <template v-else-if="item.payType == '30'">国库集中支付</template>
- </template>
- </van-field>
- <van-field v-model="item.receiver" label="收款人/单位" />
- <van-field v-model="item.receiverType" label="个人/单位类型" :rules="[{ required: true, message: '请输入个人/单位类型' }]">
- <template #input>
- <van-radio-group v-model="item.receiverType" direction="horizontal">
- <van-radio v-for="(item, index) in receiverTypeOptions" :key="index" :name="item.dictValue">{{ item.dictLabel }}</van-radio>
- </van-radio-group>
- </template>
- </van-field>
- <van-field v-model.number="item.amount" type="number" label="金额" :rules="[{ required: true, message: '请输入金额' }]" />
- <!-- <van-field v-model="item.purpose" label="用途" :rules="[{ required: true, message: '请输入用途' }]" />
- <van-field v-model="item.bankName" label="具体开户行" /> -->
- <van-cell v-if="index > 0" center>
- <template #default>
- <van-button class="w100" size="small" plain round type="danger" @click="deletePayment(index)">删除</van-button>
- </template>
- </van-cell>
- </van-cell-group>
- <van-button class="w100 mt10 mb10" size="small" plain round icon="plus" type="primary" @click="addPayment">添加支付信息</van-button>
- <h4>附件信息</h4>
- <div class="file-card">
- <van-uploader v-model="state.form.fileList" accept="image/*,.pdf" :after-read="afterRead" multiple preview-size="60" :preview-full-image="false" />
- </div>
- </van-form>
- <!-- 支出日期选择 -->
- <van-popup v-model:show="showExpandTimePicker" destroy-on-close position="bottom">
- <van-date-picker v-model="state.expendTime" title="选择日期" @confirm="onExpandTimeConfirm" @cancel="showExpandTimePicker = false" />
- </van-popup>
- <!-- 支付方式选择 -->
- <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
- <van-picker :columns="columns" :model-value="state.pickerPayment" @confirm="onConfirm" @cancel="showPicker = false" />
- </van-popup>
- <!-- 开票日期选择 -->
- <van-popup v-model:show="showPickerInvoice" destroy-on-close position="bottom">
- <van-date-picker v-model="state.invoiceBy" title="选择日期" @confirm="onPaymentConfirm" @cancel="showPickerInvoice = false" />
- </van-popup>
- </div>
- <van-action-bar placeholder>
- <van-action-bar-icon icon="wap-home-o" text="首页" @click="onRouterPush('/home')" />
- <van-action-bar-icon icon="info-o" text="报销提醒" @click="onRouterPush('/fund/reimbursement-remind')" />
- <van-action-bar-button class="w100" type="primary" text="提交" @click="onClickButton" />
- </van-action-bar>
- </template>
- <script lang="ts" setup>
- import to from 'await-to-js'
- import { useRoute, useRouter } from 'vue-router'
- import { computed, onMounted, reactive, ref } from 'vue'
- import { formatDate } from '/@/utils/formatTime'
- import { showConfirmDialog, showNotify } from 'vant'
- import { useUserInfo } from '/@/stores/userInfo'
- import { storeToRefs } from 'pinia'
- import instAppoint from '/@/api/instr/instAppoint'
- import { useConfigApi } from '/@/api/system/config'
- import { useFundApi } from '/@/api/fund'
- import { useDictApi } from '/@/api/system/dict'
- import { getDictLabel, formatAmount, formatAmountYuan, formatInteger } from '/@/utils/other'
- import { useClaimApi } from '/@/api/fund/claim'
- import { useExpenseRemindApi } from '/@/api/fund/reimbursement-remind'
- import { handleUpload } from '/@/utils/upload'
- import { useExpenseApi } from '/@/api/base/expense'
- import { useFundCardApi } from '/@/api/fund/card'
- import { useBaseReimburseApi } from '/@/api/fund/reimburse'
- import { useLoginApi } from '/@/api/login'
- import { Local, Session } from '/@/utils/storage'
- const storesUseUserInfo = useUserInfo()
- const { userInfos, openId, unionId } = storeToRefs(storesUseUserInfo)
- const route = useRoute()
- const router = useRouter()
- const expenseRemindApi = useExpenseRemindApi()
- const fundApi = useFundApi()
- const loginApi = useLoginApi()
- const dictApi = useDictApi()
- const expenseApi = useExpenseApi()
- const configApi = useConfigApi()
- const fundCardApi = useFundCardApi()
- const baseReimburseApi = useBaseReimburseApi()
- const formRef = ref()
- const paymentReceivedTypeOptions = ref<RowDicDataType[]>([])
- const receiverTypeOptions = ref<RowDicDataType[]>([])
- const showPicker = ref(false)
- const columns = [
- {
- text: '现金',
- value: '10'
- },
- {
- text: '银行转账',
- value: '20'
- },
- {
- text: '国库集中支付',
- value: '30'
- }
- ]
- const showPickerInvoice = ref(false)
- const showExpandTimePicker = ref(false)
- const state = reactive({
- loading: false,
- isActiveService: false,
- showProject: false,
- shwoService: false,
- showExpenseCard: false,
- showAppointUser: false,
- isInstrHead: false,
- fundDetail: {} as any,
- detail: [] as any,
- form: {
- id: 0,
- orderNo: '',
- price: 0,
- buyerName: '',
- createdTime: '',
- status: '',
- fileList: [],
- amount: 0,
- noticeId: null,
- projectType: '',
- allotId: null,
- handle: '', //经办人
- projectId: 0, //项目Id
- projectIncharge: '', //项目负责人
- projectCode: '',
- projectName: '', //项目名称
- projectSource: '',
- deptName: '',
- startDate: '',
- endDate: '',
- purpose: '', //事项
- detail: [],
- invoice: [
- {
- fileList: [],
- fileName: '',
- fileUrl: '',
- invoiceNo: '',
- amount: null,
- invoiceBy: '',
- invoiceType: ''
- }
- ],
- payment: [
- {
- payType: '10',
- receiver: '',
- receiverType: '',
- amount: '',
- purpose: '',
- bankName: ''
- }
- ],
- deptId: null,
- subjCode: '',
- subjName: '',
- subSubjCode: '',
- subSubjName: '',
- expendTime: '',
- expend: ''
- },
- pickerPayment: [],
- pickerIndex: -1,
- invoiceBy: [],
- pickerPaymentIndex: -1,
- expendTime: []
- })
- const costSourceList = ref<RowDicDataType[]>([])
- const expenseSubjectList = ref<RowDicDataType[]>([])
- const subList = ref([])
- const subChildrenList = ref<any>([])
- const sysParamsConfig = ref()
- const showAmount = ref(0) //根据项目和费用类型查询入账和剩余入账
- const showBalanceAmount = ref(0) //根据项目和费用类型查询入账和剩余入账
- const getDict = async () => {
- await Promise.all([dictApi.getDictDataByType('PaymentReceivedType'), fundApi.getAllFirstSubj()]).then(([type, parList]) => {
- paymentReceivedTypeOptions.value = type?.data?.values || []
- state.detail =
- parList?.data.map((item: any) => ({
- ...item,
- amount: 0
- })) || []
- })
- await Promise.all([
- dictApi.getDictDataByType('sci_cost_source'),
- expenseApi.GetListNoPage(),
- fundApi.getAllFirstSubj(),
- configApi.getEntityMapByKeys({ configKeys: ['sci_fund_expense_notice_business_code', 'sci_fund_expense_notice_material_code'] }), // 获取系统参数
- dictApi.getDictDataByType('sci_receiver_type')
- ]).then(([source, subj, sub, sysParams, recevied]) => {
- costSourceList.value = source.data?.values
- expenseSubjectList.value = subj?.data?.list
- subList.value = sub?.data
- sysParamsConfig.value = sysParams?.data
- // 费用科目
- const find = subList.value.find((item: any) => item.subjCode == sysParamsConfig.value.sci_fund_expense_notice_business_code)
- subChildrenList.value = find?.children
- state.form.subjCode = find?.subjCode
- state.form.subjName = find?.subjName
- // 二级费用科目
- const subFind = subChildrenList.value.find((item: any) => item.subjCode == sysParamsConfig.value.sci_fund_expense_notice_material_code)
- state.form.subSubjCode = subFind?.subjCode
- state.form.subSubjName = subFind?.subjName
- receiverTypeOptions.value = recevied?.data?.values || []
- })
- }
- // 获取入账信息
- const getAmountInfo = async () => {
- if (!state.form.projectId || !state.form.subjCode) return
- const params = {
- projectId: state.form.projectId,
- projectType: state.form.projectType,
- subjCode: state.form.subjCode
- }
- showAmount.value = 0
- showBalanceAmount.value = 0
- const [err, res]: ToResponse = await to(fundCardApi.getSubjAmount(params))
- if (err) return
- showAmount.value = res?.data?.amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
- showBalanceAmount.value = res?.data?.balanceAmount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
- }
- const getFundDetail = async (id: number) => {
- const [err, res]: ToResponse = await to(expenseRemindApi.getDetails({ id }))
- if (err) return
- const row = res.data
- state.form = {
- ...state.form,
- ...row
- }
- state.form.noticeId = row.id
- state.form.amount = row.price
- state.form.deptName = row.projectDeptName
- state.form.handle = userInfos.value.nickName
- }
- // 获取上传附件信息
- const getUploadFileList = async () => {
- if (!state.form.subjCode) {
- state.form.fileList = []
- return
- }
- const find = subList.value.find((item: any) => item.subjCode == state.form.subSubjCode)
- const subjId = find?.id
- const [err, res]: ToResponse = await to(baseReimburseApi.getList({ subjId: subjId }))
- if (err) return
- const arr = res?.data?.list || []
- state.form.fileList = arr.map((item: any) => {
- return {
- fileName: '',
- fileUrl: '',
- fileList: [],
- required: item.isRequired === '10',
- fileType: item.fileName,
- accept: item.fileType,
- fileTempUrl: item.fileTempUrl
- }
- })
- }
- const onRouterPush = (val: string, params?: any) => {
- router.push({
- path: val,
- query: { ...params }
- })
- }
- const afterRead = async (files: any) => {
- if (files.length) {
- for (const file of files) {
- file.status = 'uploading'
- const [err, res]: ToResponse = await to(handleUpload(file.file))
- if (err) {
- file.status = 'failed'
- return
- }
- file.status = 'success'
- file.url = res
- file.fileName = file.file.name
- file.fileUrl = res
- file.fileSize = file.file.size
- file.invoiceType = file.file.type
- }
- } else {
- const file = files
- file.status = 'uploading'
- const [err, res]: ToResponse = await to(handleUpload(file.file))
- if (err) {
- file.status = 'failed'
- return
- }
- file.status = 'success'
- file.url = res
- file.fileName = file.file.name
- file.fileUrl = res
- file.fileSize = file.file.size
- file.invoiceType = file.file.type
- }
- }
- const addInvoice = () => {
- state.form.invoice.push({
- fileList: [],
- fileName: '',
- fileUrl: '',
- invoiceNo: '',
- amount: null,
- invoiceBy: '',
- invoiceType: ''
- })
- }
- const deleteInvoice = (idx: number) => {
- showConfirmDialog({
- title: '提示',
- message: '确认删除?'
- })
- .then(() => {
- state.form.invoice.splice(idx, 1)
- })
- .catch(() => {
- // on cancel
- })
- }
- const addPayment = () => {
- state.form.payment.push({
- payType: '10',
- receiver: '',
- receiverType: '',
- amount: '',
- purpose: '',
- bankName: ''
- })
- }
- const deletePayment = (idx: number) => {
- showConfirmDialog({
- title: '提示',
- message: '确认删除?'
- })
- .then(() => {
- state.form.payment.splice(idx, 1)
- })
- .catch(() => {
- // on cancel
- })
- }
- const openExpandTimePicker = () => {
- const now = new Date()
- state.expendTime = [formatDate(now, 'YYYY'), formatDate(now, 'mm'), formatDate(now, 'dd')]
- showExpandTimePicker.value = true
- }
- const onExpandTimeConfirm = ({ selectedValues }) => {
- state.form.expendTime = selectedValues.join('-')
- showExpandTimePicker.value = false
- }
- const openInvoiceByPicker = (item: any, idx: number) => {
- const now = new Date()
- state.invoiceBy = item.invoiceBy ? item.invoiceBy.split('-') : [formatDate(now, 'YYYY'), formatDate(now, 'mm'), formatDate(now, 'dd')]
- showPickerInvoice.value = true
- state.pickerPaymentIndex = idx
- }
- const onPaymentConfirm = ({ selectedValues }) => {
- state.form.invoice[state.pickerPaymentIndex].invoiceBy = selectedValues.join('-')
- showPickerInvoice.value = false
- }
- const openPicker = (item: any, idx: number) => {
- state.pickerPayment = [item.payType]
- state.pickerIndex = idx
- showPicker.value = true
- }
- const onConfirm = ({ selectedValues }) => {
- state.form.payment[state.pickerIndex].payType = selectedValues[0]
- showPicker.value = false
- }
- const onClickButton = async () => {
- state.loading = true
- if (state.form.invoice.length == 0) {
- showNotify({
- type: 'warning',
- message: '发票信息不能为空'
- })
- return
- }
- if (state.form.payment.length == 0) {
- showNotify({
- type: 'warning',
- message: '支付信息不能为空'
- })
- return
- }
- const [errValid] = await to(formRef.value.validate())
- if (errValid) {
- state.loading = false
- return
- }
- const params = JSON.parse(JSON.stringify(state.form))
- params.fileList = params.fileList.map((item: any) => {
- return {
- fileName: item.fileName,
- fileSize: item.fileSize,
- invoiceType: item.invoiceType,
- fileUrl: item.fileUrl
- }
- })
- params.fileUrl = JSON.stringify(params.fileList)
- params.invoice = params.invoice.map((item: any) => {
- return {
- amount: item.amount,
- invoiceBy: item.invoiceBy,
- invoiceNo: item.invoiceNo,
- fileName: item.fileList[0].fileName,
- fileUrl: item.fileList[0].url,
- invoiceType: item.fileList[0].invoiceType
- }
- })
- const [err]: ToResponse = await to(expenseRemindApi.create(params))
- state.loading = false
- if (err) return
- showNotify({
- type: 'success',
- message: '操作成功'
- })
- router.push({
- path: '/fund/reimbursement-remind'
- })
- }
- onMounted(async () => {
- // 进行openId登录
- const code: string = route.query.code ? route.query.code.toString() : ''
- console.log('11111111111111111111111')
- let param = {
- code: code,
- unionId: unionId.value,
- user_name: userInfos.value?.userName,
- tenant: Local.get('Tenant'),
- }
- if (code) {
- console.log('2222222222222222222222')
- const [err, res]: ToResponse = await to(loginApi.weChatLogin(param))
- if (err) {
- // 跳转到登录页面
- Local.remove('token')
- router.push('/login')
- return
- }
- // 存储 token 到浏览器缓存
- Local.set('token', res?.data?.token)
- }
- const id = route.query.id ? +route.query.id : 0
- await getDict()
- await getFundDetail(id)
- getAmountInfo()
- })
- </script>
- <style lang="scss" scoped>
- .entry-container {
- flex: 1;
- padding: 0 10px 10px;
- background-color: #f9f9f9;
- overflow-y: auto;
- h4 {
- height: 38px;
- line-height: 38px;
- display: flex;
- align-items: center;
- span {
- font-weight: normal;
- margin-left: auto;
- }
- &::before {
- display: inline-block;
- content: '';
- width: 3px;
- height: 18px;
- background-color: #1c9bfd;
- margin-right: 4px;
- vertical-align: middle;
- }
- .van-button {
- margin-left: auto;
- }
- }
- .file-card {
- background: #fff;
- padding: 10px 10px 0;
- }
- }
- </style>
|