260 lines
6.3 KiB
Vue
260 lines
6.3 KiB
Vue
<template>
|
||
<view class="px-base">
|
||
<CuNavbar title="报销申请"> </CuNavbar>
|
||
<view class="space-y-base mt-base">
|
||
<view class="card-shadow bg-white rounded-19rpx px-base">
|
||
<uv-form
|
||
labelWidth="160rpx"
|
||
:model="form"
|
||
:rules="rules"
|
||
errorType="toast"
|
||
ref="formRef"
|
||
labelPosition="left"
|
||
>
|
||
<uv-form-item label="报销分类" required prop="reimbursement_type_id">
|
||
<view
|
||
@click="openType"
|
||
keyName="name"
|
||
class="h-full w-full flex justify-end"
|
||
>
|
||
<view :class="{ 'text-gray': !type.name }">{{
|
||
type.name ?? '请选择'
|
||
}}</view>
|
||
<uv-icon name="arrow-right"></uv-icon>
|
||
</view>
|
||
</uv-form-item>
|
||
<uv-line color="#f5f5f5"></uv-line>
|
||
<uv-form-item label="报销金额" required prop="expense">
|
||
<uv-input
|
||
:border="`none`"
|
||
placeholder="请输入报销金额"
|
||
type="digit"
|
||
input-align="right"
|
||
v-model="form.expense"
|
||
@blur="handleBlurExpense"
|
||
></uv-input>
|
||
</uv-form-item>
|
||
<uv-line color="#f5f5f5"></uv-line>
|
||
<uv-form-item
|
||
label="报销原因"
|
||
required
|
||
prop="reason"
|
||
labelPosition="top"
|
||
>
|
||
<uv-textarea
|
||
:border="`none`"
|
||
v-model="form.reason"
|
||
placeholder="请输入报销原因"
|
||
:customStyle="{ padding: '0' }"
|
||
></uv-textarea>
|
||
</uv-form-item>
|
||
<uv-line color="#f5f5f5"></uv-line>
|
||
<uv-form-item label="报销凭证" prop="photos" required>
|
||
<view class="text-right w-full text-hex-999"
|
||
>{{ form.photos.length }}/9</view
|
||
>
|
||
</uv-form-item>
|
||
<view class="w-full">
|
||
<uv-upload
|
||
:maxCount="9"
|
||
multiple
|
||
:fileList="form.photos"
|
||
@afterRead="afterRead"
|
||
@delete="deletePic"
|
||
@error="uploadError"
|
||
name="photos"
|
||
></uv-upload>
|
||
</view>
|
||
</uv-form>
|
||
</view>
|
||
</view>
|
||
<view class="mt-100rpx">
|
||
<uv-button type="primary" @click="submit">提交</uv-button>
|
||
</view>
|
||
|
||
<uv-picker
|
||
ref="typeRef"
|
||
keyName="name"
|
||
:columns="[typeList]"
|
||
@confirm="typeConfirm"
|
||
></uv-picker>
|
||
|
||
<uv-modal
|
||
ref="modalRef"
|
||
title="提示"
|
||
content="确定提交报销申请?"
|
||
@confirm="onSubmit"
|
||
:showCancelButton="true"
|
||
></uv-modal>
|
||
</view>
|
||
</template>
|
||
<script setup>
|
||
import CuNavbar from '@/components/cu-navbar/index'
|
||
import Cell from '@/components/cell/index'
|
||
import { ref, reactive } from 'vue'
|
||
import { http } from '@/utils/request'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
const typeRef = ref(null)
|
||
const typeList = ref([])
|
||
const type = ref({})
|
||
const modalRef = ref(null)
|
||
const formRef = ref(null)
|
||
const loading = ref(false)
|
||
const id = ref(null)
|
||
const form = reactive({
|
||
reimbursement_type_id: '',
|
||
expense: '',
|
||
reason: '',
|
||
photos: [],
|
||
})
|
||
const rules = reactive({
|
||
reimbursement_type_id: [
|
||
{
|
||
required: true,
|
||
message: '请选择报销分类',
|
||
},
|
||
],
|
||
expense: [
|
||
{ required: true, message: '请输入报销金额' },
|
||
{
|
||
required: true,
|
||
type: 'number',
|
||
min: 0,
|
||
message: '报销金额不能小于0',
|
||
},
|
||
],
|
||
reason: [{ required: true, message: '请输入报销原因' }],
|
||
photos: {
|
||
type: 'array',
|
||
required: true,
|
||
message: '请上传报销凭证',
|
||
},
|
||
})
|
||
|
||
onLoad((e) => {
|
||
if (e.id) {
|
||
id.value = e.id
|
||
http.get(`reimbursements/${e.id}`).then((res) => {
|
||
type.value = res.type
|
||
form.reimbursement_type_id = res.reimbursement_type_id
|
||
form.expense = res.expense
|
||
form.reason = res.reason
|
||
if (res.photos && res.photos.length > 0) {
|
||
form.photos = res.photos.map((item) => {
|
||
return { url: item }
|
||
})
|
||
}
|
||
})
|
||
}
|
||
getTypes()
|
||
})
|
||
|
||
const handleBlurExpense = (e) => {
|
||
form.expense = Math.floor(e * 100) / 100
|
||
}
|
||
|
||
const submit = () => {
|
||
formRef.value
|
||
.validate()
|
||
.then((res) => {
|
||
modalRef.value.open()
|
||
})
|
||
.catch((error) => {})
|
||
}
|
||
const onSubmit = async () => {
|
||
if (loading.value) return
|
||
loading.value = true
|
||
try {
|
||
const params = {
|
||
reimbursement_type_id: form.reimbursement_type_id,
|
||
expense: form.expense,
|
||
reason: form.reason,
|
||
photos: form.photos.map((item) => item.url),
|
||
}
|
||
if (id.value) {
|
||
await http.put(`/reimbursements/${id.value}`, params)
|
||
} else {
|
||
await http.post('/reimbursements', params)
|
||
}
|
||
uni.showToast({
|
||
title: '提交成功',
|
||
icon: 'none',
|
||
})
|
||
formRef.value.resetFields()
|
||
uni.$emit('refresh', { index: 0 })
|
||
uni.navigateBack()
|
||
} catch (error) {
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
const afterRead = async (event) => {
|
||
let lists = [].concat(event.file)
|
||
let fileListLen = form[event.name].length
|
||
|
||
lists.map((item) => {
|
||
form[event.name].push({
|
||
...item,
|
||
status: 'uploading',
|
||
message: '上传中',
|
||
})
|
||
})
|
||
for (let i = 0; i < lists.length; i++) {
|
||
const result = await uploadFilePromise(lists[i].url)
|
||
let item = form[event.name][fileListLen]
|
||
form[event.name].splice(
|
||
fileListLen,
|
||
1,
|
||
Object.assign(item, {
|
||
status: 'success',
|
||
message: '',
|
||
url: result,
|
||
})
|
||
)
|
||
fileListLen++
|
||
}
|
||
}
|
||
|
||
const uploadFilePromise = (url) => {
|
||
return new Promise((resolve, reject) => {
|
||
http
|
||
.upload('/fileupload', {
|
||
filePath: url,
|
||
name: 'file',
|
||
})
|
||
.then((res) => {
|
||
resolve(res.url)
|
||
})
|
||
.catch((err) => {
|
||
reject(err)
|
||
})
|
||
})
|
||
}
|
||
|
||
const deletePic = (event) => {
|
||
form[event.name].splice(event.index, 1)
|
||
}
|
||
const getTypes = () => {
|
||
http.get('/keyword?parent_key=reimbursement_type').then((res) => {
|
||
typeList.value = res
|
||
})
|
||
}
|
||
|
||
const typeConfirm = ({ value }) => {
|
||
type.value = value[0]
|
||
form.reimbursement_type_id = type.value.id
|
||
}
|
||
|
||
const openType = () => {
|
||
typeRef.value.open()
|
||
}
|
||
|
||
const uploadError = (err) => {
|
||
uni.showToast({
|
||
icon: 'none',
|
||
title: '权限未开启,请前往设置给予APP相应权限',
|
||
})
|
||
}
|
||
</script>
|