Browse Source

表格和按钮样式调整

apply^2
Min5203 4 years ago
parent
commit
d1a77ea0dc
  1. 59
      apis/finance.js
  2. 8
      apis/ocr.js
  3. 168
      components/Expenditure.vue
  4. 29
      components/FinanceExamine.vue
  5. 4
      components/FinanceManage.vue
  6. 19
      components/HistoricalApplication.vue
  7. 285
      components/RingEcharts.vue
  8. 179
      pages/Initiate-application.vue
  9. 8
      pages/applicant.vue
  10. 13
      pages/application-details.vue
  11. 276
      pages/financial-approval-details.vue
  12. 25
      pages/financial-approval.vue
  13. 128
      pages/index.vue
  14. 4
      plugins/vant.js

59
apis/finance.js

@ -1,23 +1,36 @@
import http from 'apis/axios';
const apiUrl = import.meta.env.VITE_API_URL;
const ptccsens = `${apiUrl}/ptccsens/v1.0`;
const finance = `${ptccsens}/finance`;
// 发起申请
export const apply = params => http.post(`${finance}/apply`, params);
// 审批
export const audit = params => http.post(`${finance}/audit`, params);
// 查询申请详情
export const getApplyDetail = params => http.post(`${finance}/getApplyDetail`, params);
// 通过任务id查看任务关联的财务信息
export const getByTask = params => http.post(`${finance}/getByTask`, params);
// 查看当前用户的费用申请历史信息(奖金)
export const personalHistory = params => http.post(`${finance}/personalHistory`, params);
// 查询费用申请类型
export const queryType = params => http.post(`${finance}/queryType`, params);
import http from 'apis/axios';
const apiUrl = import.meta.env.VITE_API_URL;
const ptccsens = `${apiUrl}/ptccsens/v1.0`;
const finance = `${ptccsens}/finance`;
// 发起申请
export const apply = params => http.post(`${finance}/apply`, params);
// 审批
export const audit = params => http.post(`${finance}/audit`, params);
// 查询申请详情
export const getApplyDetail = params =>
http.post(`${finance}/getApplyDetail`, params);
// 通过任务id查看任务关联的财务信息
export const getByTask = params => http.post(`${finance}/getByTask`, params);
// 查看当前用户的费用申请历史信息(奖金)
export const personalHistory = params =>
http.post(`${finance}/personalHistory`, params);
// 查询费用申请类型
export const queryType = params => http.post(`${finance}/queryType`, params);
// 任务支出统计
export const taskExpense = params =>
http.post(`${finance}/taskExpense`, params);
// 名目支出统计
export const rowExpense = params => http.post(`${finance}/rowExpense`, params);
// 成员财务统计
export const memberFinance = params =>
http.post(`${finance}/memberFinance`, params);

8
apis/ocr.js

@ -0,0 +1,8 @@
import http from 'apis/axios';
const apiUrl = import.meta.env.VITE_API_URL;
const ptccsens = `${apiUrl}/ptccsens/v1.0`;
const ocr = `${ptccsens}/ocr`;
// 发起申请
export const bill = `${ocr}/bill`;

168
components/Expenditure.vue

@ -0,0 +1,168 @@
<template>
<div v-if="data.info.length">
<div class="w-full overflow-x-scroll">
<table class="text-gray-500 mt-4 text-xs" v-if="props.id === 'taskTable'">
<tr class="bg-gray-100">
<td width="20%">任务名称</td>
<td width="16%">支出</td>
<td width="16%">占比</td>
<td width="16%">追加</td>
<td width="16%">操作</td>
</tr>
<tr v-for="item in data.info">
<td>{{ item.taskName }}</td>
<td>{{ item.money }}</td>
<td>{{ item.percentage }}%</td>
<td>
<div v-if="!item.showField" @click="item.showField = true">
{{ item.budget - 0 }}
</div>
<van-field
v-else-if="financeId - 0 !== 0 && item.showField"
v-model="item.budget"
type="number"
class="input-box"
@change="handleUpdateBudget(item)"
@blur="item.showField = false"
/>
</td>
<td>
<van-icon name="plus" @click="toApplication" />
</td>
</tr>
</table>
<table class="text-gray-500 mt-4 text-xs" v-if="props.id === 'nameTable'">
<tr class="bg-gray-100">
<td width="15%">任务名称</td>
<td width="16%">预算()</td>
<td width="16%">占比()</td>
</tr>
<tr v-for="item in data.info">
<td>{{ item.rowName }}</td>
<td>{{ item.percentage }}</td>
<td>{{ item.money }}</td>
</tr>
</table>
</div>
<div class="w-1/2 mt-4 ml-48">
<van-pagination
v-model="data.pageNum"
:items-per-page="data.pageSize"
:page-count="data.pages"
mode="simple"
/>
</div>
</div>
<van-empty v-else description="暂无数据" />
<van-dialog
v-model:show="data.show"
title="追加预算"
show-cancel-button
@confirm="handleAdd"
>
<van-field
:border="data.border"
v-model="data.appendBudget"
type="textarea"
class="appendBudget"
placeholder="追加预算"
/>
</van-dialog>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { taskExpense, rowExpense } from 'apis/finance';
import { addBudget } from 'apis/projectFinance';
const projectId = useProjectId();
const router = useRouter();
const props = defineProps({
id: { type: Object, default: () => {} },
});
console.log('props.id: ', props.id);
const data = reactive({
info: [],
pageNum: 1,
pageSize: 10,
pages: 0,
show: false,
appendBudget: '',
auditInfo: {},
border: true,
});
//
async function handleUpdateBudget(item) {
try {
const params = {
param: {
appendBudget: item.budget - 0,
financeId: item.financeId,
projectId: projectId.value,
},
};
await addBudget(params);
} catch (error) {
console.error('error: ', error);
}
}
// ,
function toApplication() {
const routeValue = router.currentRoute.value;
const query = routeValue.query;
router.push({ path: '/Initiate-application', query });
}
// echarts()
async function getTaskExpense() {
const params = { param: { projectId: projectId.value } };
const res = await taskExpense(params);
for (let i = 0; i < res.length; i++) {
res.showField = false;
}
data.info = res;
}
// echarts()
async function getRowExpense() {
const params = { param: { projectId: projectId.value } };
const res = await rowExpense(params);
data.info = res;
}
function setData() {
if (props.id === 'taskTable') {
getTaskExpense();
} else if (props.id === 'nameTable') {
getRowExpense();
}
}
onMounted(() => {
nextTick(() => {
// handleFinanceOfProject();
setData();
});
});
</script>
<style scoped lang="less">
table {
width: 120%;
td {
border: 0.5px solid #ccc;
padding: 0.5rem;
}
}
.input-box {
padding: 0 !important;
border-bottom: 1px solid #ccc;
}
.appendBudget {
border: 1px solid #ccc;
border-radius: 4px;
margin: 5%;
width: 90%;
}
</style>

29
components/FinanceExamine.vue

@ -1,19 +1,19 @@
<template>
<div v-if="data.info.list && data.info.list.length">
<div class="w-full overflow-x-scroll">
<table class="text-gray-500 mt-4 text-xs">
<table class="text-gray-500 mt-4">
<tr class="bg-gray-100">
<td width="15%">申请人</td>
<td width="16%">金额()</td>
<td width="20%">时间</td>
<td width="10%">操作</td>
<td width="20%">申请人</td>
<td width="25%">金额()</td>
<td width="30%">时间</td>
<td width="25%">操作</td>
</tr>
<tr v-for="item in data.info.list">
<td>{{item.submitName}}</td>
<td>{{item.money}}</td>
<td>{{dayjs(item.submitTime - 0).format('YYYY-MM-DD')}}</td>
<td @click="openDetails(item.applyId)">{{item.submitName}}</td>
<td @click="openDetails(item.applyId)">{{item.money}}</td>
<td @click="openDetails(item.applyId)">{{dayjs(item.submitTime - 0).format('YYYY-MM-DD')}}</td>
<td>
<div v-if="!item.applyType" class="flex flex-row justify-between">
<div v-if="!item.applyType" class="flex flex-row justify-around">
<van-button type="success" size="mini" class="rounded" @click="showRemark(item.financeCheckId, 1)">通过</van-button>
<van-button type="danger" size="mini" class="rounded" @click="showRemark(item.financeCheckId, 2)">驳回</van-button>
@ -40,6 +40,9 @@ import {ref, reactive, onMounted, nextTick} from'vue';
import { audit } from 'apis/finance';
import { queryNeedCheckByMe } from 'apis/projectFinance';
import { Toast } from 'vant';
import { useRouter } from 'vue-router';
const router = useRouter()
const data = reactive({
info: {},
@ -116,6 +119,14 @@ async function handleAudit(financeCheckId, checkStatus){
console.error('error: ', error);
}
}
//
function openDetails(applyId){
const routeValue = router.currentRoute.value;
const query = routeValue.query;
query.applyId = applyId;
router.push({ path: '/financial-approval-details', query });
}
</script>
<style scoped lang="less">

4
components/FinanceManage.vue

@ -4,8 +4,8 @@
<table class="w-full text-gray-500 mt-4 text-ms">
<tr class="bg-gray-100">
<td class="name">任务名称</td>
<td class="">预算()</td>
<td class="">奖金()</td>
<td>预算()</td>
<td>奖金()</td>
</tr>
<tr v-for="item in data.info.taskFinanceList.list">
<td>{{item.name}}</td>

19
components/HistoricalApplication.vue

@ -12,8 +12,8 @@
<td class="status">状态</td>
</tr>
<tr v-for="item in data.info.list" class="text-gray-500">
<td>{{ item.submitName }}</td>
<td>
<td @click="openDetails(item.applyId)">{{ item.submitName }}</td>
<td @click="openDetails(item.applyId)">
<!-- v-if="!item.showBudgetEdit" -->
{{ (+item.money / 100).toFixed(2) }}
<!-- <van-field
@ -25,7 +25,7 @@
@blur="item.showBudgetEdit = false"
/> -->
</td>
<td>
<td @click="openDetails(item.applyId)">
{{ dayjs(item.submitTime - 0).format('YYYY/MM/DD') }}
</td>
<td>
@ -74,6 +74,9 @@
import dayjs from 'dayjs';
import { personalHistory } from 'apis/finance';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const data = reactive({
info: {},
@ -124,6 +127,14 @@ onMounted(() => {
handlePersonalHistory();
});
});
//
function openDetails(applyId){
const routeValue = router.currentRoute.value;
const query = routeValue.query;
query.applyId = applyId;
router.push({ path: '/application-details', query });
}
</script>
<style scoped lang="less">
@ -141,7 +152,7 @@ table {
width: 100px;
}
.time {
width: 170px;
width: 120px;
}
.status {
width: 120px;

285
components/RingEcharts.vue

@ -1,141 +1,198 @@
<template>
<!-- <div id="yield-chart"></div> -->
<div :id="props.id" style="width:400px;height:260px"></div>
<div :id="props.id" style="width: 400px; height: 260px"></div>
</template>
<script setup>
import { onMounted} from 'vue';
import { onMounted } from 'vue';
import { taskExpense, rowExpense, memberFinance } from 'apis/finance';
const projectId = useProjectId();
const props = defineProps({ id: { type: String, default: () => {} } });
onMounted(()=>{
if(props.id){
var myChart = echarts.init(document.getElementById(props.id));
let oData = [
{
name: '办公费',
value: 36,
rate: 12,
},
{
name: '车辆费用',
value: 20,
rate: 20,
},
{
name: '差旅费33',
value: 15,
rate: -40,
},
{
name: '租赁费',
value: 10,
rate: -15,
},
{
name: '其他',
value: 9,
rate: 12,
},
];
// echarts()
async function getTaskExpense() {
const params = { param: { projectId: projectId.value } };
const res = await taskExpense(params);
return changeData(res, 'taskName');
}
// echarts()
async function getRowExpense() {
const params = { param: { projectId: projectId.value } };
const res = await rowExpense(params);
return changeData(res, 'rowName');
}
// echarts()
async function getMemberFinance() {
const params = { param: { projectId: projectId.value } };
const res = await memberFinance(params);
return changeData(res, 'memberName');
}
// optionData
function changeData(list, name) {
let optionsData = [];
for (let i = 0; i < list.length; i++) {
const data = {
name: list[i][name],
value: list[i].money - 0,
percentage: list[i].percentage,
};
optionsData.push(data);
}
const data = [
{
name: '办公费',
value: 36,
},
{
name: '车辆费用',
value: 20,
},
{
name: '差旅费',
value: 15,
},
{
name: '租赁费',
value: 10,
},
{
name: '其他',
value: 9,
},
];
return optionsData;
}
function setData() {
if (props.id === 'taskEcharts') {
return getTaskExpense();
} else if (props.id === 'nameEcharts') {
return getRowExpense();
} else if (props.id === 'memberEcharts') {
return getMemberFinance();
} else {
return oData;
}
}
function getTitleNum(list) {
let num = 0;
for (let i = 0; i < list.length; i++) {
num += list[i].value;
}
return num;
}
onMounted(async () => {
if (props.id) {
var myChart = echarts.init(document.getElementById(props.id));
}
const data = await setData();
const title = getTitleNum(data);
const option = {
title: {
text: 100,
textStyle:{
fontSize:17,
color:"black"
},
textAlign:"center",
x: '24%',
y: '35%',
text: title,
textStyle: {
fontSize: 17,
color: 'black',
},
textAlign: 'center',
x: '24%',
y: '35%',
},
legend: {
type: 'plain',
icon: 'circle',
orient: 'vertical',
left: '55%',
top: '15%',
align: 'left',
itemGap: 15,
itemWidth: 10, //
itemHeight: 10, //
symbolKeepAspect: false,
textStyle: {
color: '#000',
rich: {
name: {
verticalAlign: 'right',
align: 'left',
width: 50,
fontSize: 12
},
value: {
align: 'left',
width: 40,
fontSize: 12
},
count: {
align: 'left',
width: 80,
fontSize: 12
},
}
type: 'plain',
icon: 'circle',
orient: 'vertical',
left: '55%',
top: '15%',
align: 'left',
itemGap: 15,
itemWidth: 10, //
itemHeight: 10, //
symbolKeepAspect: false,
textStyle: {
color: '#000',
rich: {
name: {
verticalAlign: 'right',
align: 'left',
width: 50,
fontSize: 12,
},
value: {
align: 'left',
width: 40,
fontSize: 12,
},
count: {
align: 'left',
width: 80,
fontSize: 12,
},
},
data: data.map(item => item.name),
formatter: function(name) {
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
if (name === data[i].name) {
return (
'{name| ' +
name +
'} | ' +
'{value| ' +
data[i].value +
'%}' +
'{count| ' +
data[i].value +
'} '
)
}
}
},
data: data.map(item => item.name),
formatter: function (name) {
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
if (name === data[i].name) {
return (
'{name| ' +
name +
'} | ' +
'{value| ' +
data[i].percentage +
'%}' +
'{count| ' +
data[i].value +
'} '
);
}
}
}
},
},
series: [{
series: [
{
name: '数量',
type: 'pie',
radius: ['40%', '55%'],
center: ['25%', '40%'],
data: data,
label: {
normal: {
show: false,
position: 'center',
formatter: '{text|{c}}\n{b}',
rich: {
text: {
align: 'center',
verticalAlign: 'middle',
padding: 8,
fontSize: 30
},
value: {
align: 'center',
verticalAlign: 'middle',
fontSize: 20
}
}
normal: {
show: false,
position: 'center',
formatter: '{text|{c}}\n{b}',
rich: {
text: {
align: 'center',
verticalAlign: 'middle',
padding: 8,
fontSize: 30,
},
value: {
align: 'center',
verticalAlign: 'middle',
fontSize: 20,
},
},
},
},
labelLine: {
normal: {
show: true
}
}
}]
};
myChart.setOption(option);
})
normal: {
show: true,
},
},
},
],
};
myChart.setOption(option);
});
</script>
<style scoped>
</style>
<style scoped></style>

179
pages/Initiate-application.vue

@ -20,20 +20,26 @@
>手动输入</van-button
>
</div>
<div
class="text-center border-b w-52 h-24 bg-gray-100 border-dashed border-2"
@click="uploadBill"
v-show="!data.isSuccess"
>
<p class="text-gray-400 text-xl pt-3">+</p>
<p class="text-gray-400 text-xs">上传并识别凭证</p>
</div>
<!-- <van-overlay :show="data.showUploading"> -->
<div
class="text-center border-b w-52 h-24 border-dashed border-2 upload-box"
v-show="!data.isSuccess"
>
<van-uploader v-model="data.fileList" multiple :max-count="1" :after-read="afterRead" class="z-50 opacity-0" />
<div class="upload-txt">
<p class="text-gray-400 text-xl pt-3">+</p>
<p class="text-gray-400 text-xs">上传并识别凭证</p>
</div>
<!-- <van-loading type="spinner" v-if="data.showUploading" class="upload-txt z-50" /> -->
</div>
<!-- </van-overlay> -->
<!-- 上传票据成功后显示发票信息 -->
<div v-show="data.isSuccess">
<van-field
v-for="item in data.invoiceInfo"
required
v-model="item.values"
v-model="item.value"
:label="item.name"
@change="cahngeInvoiceList($event, item)"
input-align="right"
@ -45,7 +51,7 @@
<div v-show="!data.isInvoice">
<van-field
required
v-model="data.applyMoney"
v-model="data.money"
label="申请金额"
placeholder="输入申请金额"
input-align="right"
@ -78,13 +84,13 @@
class="button"
size="mini"
v-for="item in data.reviewerList"
:key="item.userId"
:key="item.memberId"
:type="
data.checkerList.find(checker => checker === item.userId)
data.checkerList.find(checker => checker === item.memberId)
? 'primary'
: 'default'
"
@click="handleSelectChecker(item.userId)"
@click="handleSelectChecker(item.memberId)"
>
{{ item.name }}
</van-button>
@ -96,7 +102,7 @@
<div class="text-gray-500 p-4 font-semibold">其他信息</div>
<!-- 申请类型 -->
<!-- 普通票据申请 -->
<div v-show="data.isInvoice">
<div>
<van-field
v-model="data.applyType"
is-link
@ -110,9 +116,11 @@
<van-popup v-model:show="data.showType" round position="bottom">
<van-picker
title="申请类型"
v-if="data.applyTypeOptions && data.applyTypeOptions.length"
:columns="data.applyTypeOptions"
@confirm="finishApplyType"
/>
<van-loading v-else class="my-20 text-center" />
</van-popup>
<!-- 所属项目 -->
<van-field
@ -131,6 +139,7 @@
<!-- 所属任务的 -->
<van-field
v-model="data.taskName"
v-show="data.isInvoice"
is-link
readonly
label="所属任务"
@ -155,13 +164,16 @@
<van-popup v-model:show="data.showCategory" round position="bottom">
<van-picker
title="请选择类目"
v-if="data.applyCategoryOptions && data.applyCategoryOptions.length"
:columns="data.applyCategoryOptions"
@confirm="finishApplyCategory"
/>
<van-loading v-else class="my-20 text-center" />
</van-popup>
<!-- 名目选择 -->
<van-field
v-model="data.applyName"
v-show="data.isInvoice"
is-link
readonly
label="名目"
@ -172,28 +184,13 @@
<van-popup v-model:show="data.showName" round position="bottom">
<van-picker
title="请选择名目"
v-if="data.applyNameOptions && data.applyNameOptions.length"
:columns="data.applyNameOptions"
@confirm="finishApplyName"
/>
<van-loading v-else class="my-20 text-center" />
</van-popup>
</div>
<!-- 个人手动申请 -->
<div v-show="!data.isInvoice">
<div class="pl-2">
<van-cell
title="单元格"
is-link
:value="data.personalType"
required
/>
<van-cell
title="单元格"
is-link
:value="data.personalCategory"
required
/>
</div>
</div>
</div>
<!-- 提交人信息 -->
<div class="bg-white mt-3">
@ -219,58 +216,62 @@
<div class="text-gray-500 font-semibold">历史申请</div>
<div>
<Search />
<FinanceExamine />
<HistoricalApplication />
</div>
</div>
<!-- 底部立即提交按钮 -->
<div class="mx-6 mt-10">
<van-button type="primary" size="small" block>立即提交</van-button>
<van-button @click="submit" type="primary" size="small" block>立即提交</van-button>
</div>
</div>
</template>
<script setup>
import axios from 'axios';
import { ref, reactive } from 'vue';
// import useApplication from 'hooks/useApplication';
import { queryChecker } from 'apis/member';
import { queryType } from 'apis/finance';
// const getApplication = useApplication();
import { queryType, apply } from 'apis/finance';
import { bill } from 'apis/ocr';
import { Toast } from 'vant';
const token = useToken()
const projectName = useProjectName();
const taskName = useTaskName();
const projectId = useProjectId();
const taskDetailId = useTaskId();
const data = reactive({
isInvoice: true,
fileList: [],
showUploading: false,
remark: '',
invoiceList: [],
invoiceInfo: [
{
name: '发票代码',
values: 0,
value: 0,
label: 'invoiceCode',
},
{
name: '发票号码',
values: 1,
value: 0,
label: 'invoiceNumber',
},
{
name: '合计金额(元)',
values: 2,
value: 0,
label: 'money',
},
{
name: '税额(元)',
values: 3,
value: 0,
label: 'taxMoney',
},
{
name: '开票日期',
values: 4,
value: 0,
label: 'invoiceTime',
},
{
name: '发票备注信息',
values: 5,
name: '备注信息',
value: '无',
label: 'remark',
},
],
@ -284,6 +285,7 @@ const data = reactive({
currentPage: 0, //
personalType: '个人申请', //
personalCategory: '用款',
money: '',
//
showType: false, //
applyTypes: [],
@ -302,9 +304,64 @@ const data = reactive({
rowId: '',
});
//
function upLoaderImg(file, url) { //file
//new FormData
let params = new FormData()
params.append('part', file)
let config = {
headers: { //
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + token.value
}
}
return new Promise((resolve, reject) => {
// uploadUrl
axios.post(`${bill}`, params, config).then(res => {
resolve(res)
}).catch(err => {
reject(err)
});
})
}
//
async function afterRead(file){
data.showUploading = true
//
upLoaderImg(file.file, 'upload').then(res => {
data.showUploading = false
if (res.data.code == 200) {
for(let key in res.data.data){
data.invoiceInfo.forEach(item => {
if(item.label === key){
if(item.label === 'money' || item.label === 'taxMoney'){
item.value = (+res.data.data[key] / 100).toFixed(2)
}else{
item.value = res.data.data[key]
}
}
})
}
console.log('data.invoiceInfo: ', data.invoiceInfo);
data.invoiceList.push(res.data.data)
data.titleHidden = false;
data.isSuccess = true;
} else {
Toast.fail(res.data.msg || '上传失败')
}
})
}
//
function cahngeInvoiceList(e, item) {
console.log('e: ', e.target.value, item);
data.invoiceList.forEach(invoice => {
for(let key in data.invoiceList){
if(item.label === key){
invoice[key] = item.value
}
}
})
}
//
@ -345,15 +402,6 @@ function handleSelectChecker(id) {
}
}
//
function uploadBill() {
//TODO:
setTimeout(() => {
data.titleHidden = false;
data.isSuccess = true;
}, 1000);
}
/**
* 查询所有成员
* @param { String } projectId
@ -378,7 +426,6 @@ async function handleQueryChecker() {
* @param { Number } type 类型0申请类型 1类目 2名目
*/
async function handleQueryType(parentId, type) {
console.log('parentId: ', parentId);
try {
const params = {
param: {
@ -421,7 +468,7 @@ onMounted(() => {
* 发起申请
* @param { Object } params
*/
async function submit(params) {
async function submit() {
try {
if(!verification()) return
const params = {}
@ -435,7 +482,8 @@ async function submit(params) {
}
//
function verification(){
const { isSuccess, categoryId, checkerList, department, money, rowId, submitName, typeId, isInvoice } = data
const { isSuccess, invoiceInfo, categoryId, checkerList, department, money, rowId, submitName, typeId, isInvoice } = data
//
if(!isSuccess && isInvoice){
Toast.fail('请上传票据凭证');
return
@ -473,18 +521,14 @@ function verification(){
//
function setParams(){
const { remark, money, categoryId, checkerList, department, rowId, submitName, typeId, invoiceInfo, isInvoice } = data
const { remark, money, categoryId, checkerList, department, rowId, submitName, typeId, invoiceInfo, isInvoice, invoiceList } = data
let param = {}
let invoiceList = []
let list = {url: 'https://cdn.nlark.com/yuque/0/2022/png/413990/1642482454748-931b2e93-8964-492b-b06b-b8db42733c02.png'}
let totleMoney = 0
invoiceInfo.forEach(item => {
list[item.label] = item.value
if(item.label === 'money'){
totleMoney += item.value - 0
totleMoney += item.value
}
})
invoiceList.push(list)
if(isInvoice){
param = {
money: totleMoney * 100,invoiceList, remark, checkerList, typeId, projectId: projectId.value,
@ -520,4 +564,15 @@ function setParams(){
padding: 0 0.75rem;
margin: 0.5rem;
}
.upload-box{
background: #f7f8fa;
position: relative;
}
.upload-txt{
position: absolute;
width: 100%;
bottom: 22px;
}
</style>

8
pages/applicant.vue

@ -9,11 +9,11 @@
title-active-color="#59B4FF"
>
<van-tab title="我的申请">
<div class="h-full bg-white px-4">
<div class="mt-4 flex flex-col h-96 overflow-hidden">
<div class=" bg-white px-4">
<div class="mt-4 flex flex-col overflow-hidden h-full">
<div class="py-4 pb-0 text-gray-500 font-semibold">历史申请</div>
<Search class="px-4 pt-0" />
<HistoricalApplication class="px-4 mt-0" />
<Search />
<HistoricalApplication />
</div>
<!-- 底部提交按钮部分 -->
<div class="fixed w-11/12 box-border bottom-10 " @click="toApplication">

13
pages/application-details.vue

@ -41,11 +41,11 @@
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>合计金额()</div>
<div>{{item.money}}</div>
<div>{{ (+item.money / 100).toFixed(2) }}</div>
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>税额()</div>
<div>{{item.taxMoney}}</div>
<div>{{ (+item.taxMoney / 100).toFixed(2) }}</div>
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>开票日期</div>
@ -87,7 +87,10 @@
<script setup>
import dayjs from "dayjs";
import { ref, computed } from 'vue';
import { getApplyDetail } from 'apis/finance'
import { getApplyDetail } from 'apis/finance';
import { useRouter } from 'vue-router';
const router = useRouter();
//
const currentRate = ref(0);
const text = computed(() => currentRate.value.toFixed(0));
@ -162,7 +165,9 @@ const submitter = ref([
*/
async function handleApplyDetail(){
try {
const params = {param : { applyId: '1485797754654695424' }}
const routeValue = router.currentRoute.value;
const applyId = routeValue.query.applyId;
const params = {param : { applyId }}
const res = await getApplyDetail(params)
//
checkerList.value = res.checkerList

276
pages/financial-approval-details.vue

@ -1,9 +1,39 @@
<template>
<div>
<!-- 发票信息 -->
<div class="bg-white px-3">
<div class="text-gray-500 font-semibold px-1 py-3 mt-5">发票信息</div>
<div v-for="item in billList" class="flex p-2 text-gray-400 text-base justify-between">
<!-- 发票信息 -->
<div class="bg-white px-3" v-if="isInvoice">
<div class="text-gray-500 font-semibold px-1 py-3">发票信息</div>
<div v-for="(item,invoiceIndex) in invoiceList" :key="invoiceIndex">
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>发票代码</div>
<div>{{item.invoiceCode}}</div>
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>发票号码</div>
<div>{{item.invoiceNumber}}</div>
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>合计金额()</div>
<div>{{ (+item.money / 100).toFixed(2) }}</div>
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>税额()</div>
<div>{{ (+item.taxMoney / 100).toFixed(2) }}</div>
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>开票日期</div>
<div>{{dayjs(item.invoiceTime - 0).format('YYYY年MM月DD日')}}</div>
</div>
<div class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>备注</div>
<div>{{item.remark}}</div>
</div>
</div>
</div>
<!-- 金额信息 -->
<div class="bg-white px-3" v-else>
<div class="text-gray-500 font-semibold px-1 py-3">发票信息</div>
<div v-for="(item,moneyIndex) in moneyInfo" :key="moneyIndex" class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>{{item.name}}</div>
<div>{{item.value}}</div>
</div>
@ -11,164 +41,218 @@
<!-- 其他信息 -->
<div class="bg-white px-3">
<div class="text-gray-500 font-semibold px-1 py-3 mt-5">其他信息</div>
<div v-for="item in otherData" class="flex p-2 text-gray-400 text-base justify-between">
<div v-for="(item,otherIndex) in otherData" :key="otherIndex" class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>{{item.name}}</div>
<div>{{item.value}}</div>
</div>
</div>
<!-- 提交人信息 -->
<div class="bg-white px-3">
<div class="bg-white px-3 mb-5">
<div class="text-gray-500 font-semibold px-1 py-3 mt-5">提交人信息</div>
<div v-for="item in submitter" class="flex p-2 text-gray-400 text-base justify-between">
<div v-for="(item, index) in submitter" :key="index" class="flex py-2 px-4 text-gray-400 text-base justify-between">
<div>{{item.name}}</div>
<div>{{item.value}}</div>
</div>
</div>
<!-- 审核人信息 -->
<div class="bg-white px-3">
<div class="text-gray-500 font-semibold px-1 py-3 mt-5">审核结果</div>
<div v-for="item in checkerList" class="flex p-3 text-gray-400 text-base justify-between">
<!-- 遍历的审核人的姓名建议和时间 -->
<div class="bg-white px-3 mb-5">
<div class="text-gray-500 font-semibold px-1 py-3">审核结果</div>
<div v-for="(item, checkerIndex) in checkerList" :key="checkerIndex" class="flex py-3 px-4 text-gray-400 text-base justify-between">
<div>
<div>{{item.name}}</div>
<div class="text-sm pt-1">{{item.advice}}</div>
<div class="text-sm pt-1">{{item.checkedTime}}</div>
<div>{{item.checkerName}}</div>
<div class="text-sm pt-1">{{item.remark}}</div>
<div class="text-sm pt-1">{{item.time}}</div>
</div>
<!--其他审核人 -->
<div class="text-center" v-show="!item.isMine">
<div :class="item.status === '1' ? 'text-green-500' : item.status === '2' ? 'text-red-500' : ''">
{{item.status === '1' ? '已通过' : item.status === '2' ? '已驳回' : '待审批'}}
<div class="text-center">
<div v-if="item.checkStatus == '1' || item.checkStatus == '2'" :class="item.checkStatus == '1' ? 'text-green-500' : item.checkStatus == '2' ? 'text-red-500' : ''">
{{item.checkStatus == '1' ? '已通过' : item.checkStatus == '2' ? '已驳回' : '待审批'}}
</div>
<div v-if="item.score>0" class="mt-1 text-yellow-500">
<van-circle
v-model:current-rate="item.score"
:rate="item.score"
:speed="100"
:text="item.score"
color="#ff6700"
:stroke-width="100"
/>
</div>
</div>
<!-- 当前审核人已审核完毕 -->
<div class="text-center" v-show="item.isMine && item.status !== '0'">
<div :class="item.status === '1' ? 'text-green-500' : item.status === '2' ? 'text-red-500' : ''">
{{item.status === '1' ? '已通过' : item.status === '2' ? '已驳回' : '待审批'}}
<div class="text-center" v-else>
<van-button type="success" round size="mini" class="button ml-5" @click="showRemark(item.financeCheckId, 1)">通过</van-button>
<van-button type="danger" round size="mini" class="button" @click="showRemark(item.financeCheckId, 2)">驳回</van-button>
</div>
<div v-if="item.score>0" class="mt-1 text-yellow-500">
<div v-if="item.checkStatus == '1' || item.checkStatus == '2'" class="mt-1">
<van-circle
v-model:current-rate="item.score"
:rate="item.score"
v-model:current-rate="currentRate"
:rate="100"
:speed="100"
:text="item.score"
:text="100"
class="w-12"
color="#ff6700"
:stroke-width="100"
/>
</div>
</div>
<!-- 当前审核人未审批状态 -->
<div class="text-center" v-show="item.isMine && item.status === '0'">
<van-button type="success" round size="mini" class="button ml-5">通过</van-button>
<van-button type="danger" round size="mini" class="button">驳回</van-button>
</div>
</div>
</div>
<van-dialog v-model:show="data.show" title="备注" show-cancel-button @confirm="handleAudit">
<van-field :border="data.border" v-model="data.remark" type="textarea" class="remark" placeholder="请输入备注" />
</van-dialog>
</div>
</template>
<script setup>
import {ref} from 'vue'
//
const billList = ref([
{
name: "发票代码",
value: "4154153125646",
},
{
name: "发票号码",
value: "545648789",
},
{
name: "合计金额(元)",
value: "40",
},
{
name: "税额(元)",
value: "2",
},
import dayjs from "dayjs";
import { ref, computed } from 'vue';
import { getApplyDetail, audit } from 'apis/finance';
import { useRouter } from 'vue-router';
import { Toast } from 'vant';
const router = useRouter();
const data = reactive({
show: false,
remark: '',
auditInfo: {},
border: true
})
//
const currentRate = ref(0);
const text = computed(() => currentRate.value.toFixed(0));
const isInvoice = ref(false)
const checkerList = ref([])
//
const invoiceList = ref([])
//
const moneyInfo = ref([
{
name: "开票日期",
value: "2022年1月2日",
name: "申请金额",
value: "",
label: "money",
},
{
name: "备注",
value: "业务支出",
value: "",
label: "remark",
},
])
//
const otherData = ref([
{
name: "申请类型",
value: "项目申请"
value: "",
label: "typeName",
},
{
name: "所属项目",
value: "PT项目"
value: "",
label: "projectName",
},
{
name: "所属任务",
value: "财务条界面设计"
value: "",
label: "taskDetailName",
},
{
name: "类目",
value: "报销"
value: "",
label: "categoryName",
},
{
name: "名目",
value: "业务招待费"
value: "",
label: "rowName",
},
])
//
const submitter = ref([
{
name:'姓名',
value:'黛西'
value:'',
label: "submitName",
},
{
name:'部门',
value:'软件部'
value:'软件部',
label: "department",
},
{
name:'提交时间',
value:'2021/12/29 13:22'
},
]);
//
const checkerList = ref([
{
name: "冯教授",
advice: "",
checkedTime: "",
status: "0",
isMine: "1",
},
{
name: "薇薇安",
advice: "很棒!",
checkedTime: "12/18 14:55",
status: "0",
score:''
value:'',
label: "applyTime",
},
{
name: "小明",
advice: "很棒!",
checkedTime: "12/18 14:55",
status: "1",
score:'80'
])
/**
* 查询申请详情
* @param { Number } applyId 申请记录id
*/
async function handleApplyDetail(){
try {
const routeValue = router.currentRoute.value;
const applyId = routeValue.query.applyId;
const params = {param : { applyId }}
const res = await getApplyDetail(params)
//
checkerList.value = res.checkerList
//
isInvoice.value = res.invoiceList && res.invoiceList.length ? true : false;
invoiceList.value = res.invoiceList
for(let key in res){
moneyInfo.value.forEach(item => {
if(item.label === key){
item.value = res[key]
}
})
}
//
for(let key in res){
otherData.value.forEach(item => {
if(item.label === key){
item.value = res[key]
}
})
}
//
for(let key in res){
submitter.value.forEach(item => {
if(item.label === key){
item.value = res[key]
}
})
}
} catch (error) {
console.error('error: ', error);
}
]);
}
handleApplyDetail()
//
function showRemark(financeCheckId, checkStatus){
data.show = true
data.auditInfo = { financeCheckId, checkStatus }
}
/**
* 审批
* @param { Number } checkStatus 审核状态 1已通过 2驳回
* @param { String } financeCheckId 审核id
* @param { String } remark 备注
*/
async function handleAudit(financeCheckId, checkStatus){
try {
const { auditInfo, remark } = data
const { financeCheckId, checkStatus } = data.auditInfo
const params = {
param:{
financeCheckId,
checkStatus,
remark: data.remark
}
}
await audit(params)
Toast('审批成功');
handleFinanceOfProject()
data.remark = ''
} catch (error) {
console.error('error: ', error);
}
}
</script>
<style lang="less" scoped>
.van-circle{
width: 2.5rem !important;

25
pages/financial-approval.vue

@ -1,9 +1,9 @@
<template>
<div class="mb-60">
<!-- 财务审批页面 -->
<div class="bg-white p-4 flex text-gray-500 flex-col d_jump">
<div class="bg-white p-4 flex text-gray-500 flex-col">
<div>
<span class="font-semibold" id="finance-audit">财务审批</span> <span class="ml-2 text-xs">对员工提交的申请进行审批</span>
<span class="font-semibold" id="finance-audit">财务审批</span> <span class="ml-2">对员工提交的申请进行审批</span>
</div>
<Search />
<FinanceExamine />
@ -60,7 +60,7 @@
</div>
<!-- 成员财务图 -->
<div class="h-64 overflow-hidden">
<div>
<div class="mt-5">
<span class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"></span>
<span>成员财务图</span>
</div>
@ -73,19 +73,16 @@
<span>时间财务图</span>
</div>
<BarEcharts class="w-full h-full mt-5" />
<!-- 没有数据展示空组件 -->
<!-- <van-empty description="暂无图表数据信息" /> -->
</div>
</div>
<!-- 悬浮跳转 -->
<ul class="menu">
<a href="#finance-audit">财务审批</a>
<a href="#finance-statistical">财务统计</a>
</ul>
</div>
</template>
<script>
@ -99,21 +96,7 @@ import {ref} from 'vue'
const taskState = ref(true);
const nameState = ref(true);
function onClickLeft(){
console.log('返回上一页')
}
function jump(index) {
let jump = document.querySelectorAll('.d_jump')
//
let total = jump[index].offsetTop
// Chrome
document.body.scrollTop = total
// Firefox
document.documentElement.scrollTop = total
// Safari
window.pageYOffset = total
}
</script>
<style lang="less" scoped>

128
pages/index.vue

@ -8,15 +8,15 @@
title-active-color="#59B4FF"
>
<van-tab title="财务管理">
<div class="h-screen overflow-y-scroll w-screen mt-4">
<div class="h-screen w-full pb-60 overflow-y-scroll">
<!-- 财务管理页面 -->
<div class="bg-white p-4 text-gray-500 flex-col">
<div class="bg-white p-4 mt-5 text-gray-500 flex-col">
<div>
<span class="font-semibold" id="finance-manage">财务管理</span>
<span class="ml-2">对项目预算奖金进行配置</span>
</div>
<Search />
<FinanceManage />
<Search class="mr-5"/>
<FinanceManage class="mr-5"/>
</div>
<!-- 财务审批页面 -->
<div class="bg-white p-4 mt-5 text-gray-500 flex-col">
@ -24,80 +24,109 @@
<span class="font-semibold" id="finance-audit">财务审批</span>
<span class="ml-2">对员工提交的申请进行审批</span>
</div>
<Search />
<FinanceExamine />
<Search class="mr-5"/>
<div class="overflow-x-hidden mr-5 table-box">
<FinanceExamine />
</div>
</div>
<!-- 财务统计页面 -->
<div class="bg-white p-4 mt-5 mb-60 text-gray-500 flex-col">
<div class="bg-white p-4 mt-5 text-gray-500 flex-col">
<div>
<span class="font-semibold" id="finance-statistical">财务统计</span>
<span class="ml-2">财务明细统计查看</span>
</div>
<!-- 任务支出统计 -->
<!-- 任务支出统计 -->
<div class="h-64 overflow-hidden">
<div class="mt-5 flex justify-between">
<div>
<span class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"></span>
<span
class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"
></span>
<span>任务支出统计</span>
</div>
<div>
<van-button :type="taskState ? 'primary' : 'default'" size="mini" @click="taskState = true">图表</van-button>
<van-button :type="!taskState ? 'primary' : 'default'" size="mini" @click="taskState = false">表格</van-button>
<div class="mr-5">
<van-button
:type="taskState ? 'primary' : 'default'"
size="mini"
@click="taskState = true"
>图表</van-button
>
<van-button
:type="!taskState ? 'primary' : 'default'"
size="mini"
@click="taskState = false"
>表格</van-button
>
</div>
</div>
<!-- 任务支出统计的图表展示 -->
<div v-show="taskState">
<!-- <van-empty description="暂无图表数据信息" /> -->
<RingEcharts class="w-full h-full" id="taskEcharts"/>
<RingEcharts class="w-full h-full" id="taskEcharts" />
</div>
<!-- 任务支出统计的表格展示 -->
<div v-show="!taskState">
<FinanceExamine />
<Expenditure class="w-full h-full" id="taskTable" />
</div>
</div>
<!-- 名目支出统计 -->
<div class="h-64 overflow-hidden">
<div class="mt-5 flex justify-between">
<div>
<span class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"></span>
<span>名目支出统计</span>
</div>
<div>
<van-button :type="nameState ? 'primary' : 'default'" size="mini" @click="nameState = true">图表</van-button>
<van-button :type="!nameState ? 'primary' : 'default'" size="mini" @click="nameState = false">表格</van-button>
</div>
</div>
<!-- 名目支出统计的图表展示 -->
<div v-show="nameState">
<!-- <van-empty description="暂无图表数据信息" /> -->
<RingEcharts class="w-full h-full" id="nameEcharts"/>
<div class="mt-5 flex justify-between">
<div>
<span
class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"
></span>
<span>名目支出统计</span>
</div>
<!-- 名目支出统计的表格展示 -->
<div v-show="!nameState">
<FinanceExamine />
<div class="mr-5">
<van-button
:type="nameState ? 'primary' : 'default'"
size="mini"
@click="nameState = true"
>图表</van-button
>
<van-button
:type="!nameState ? 'primary' : 'default'"
size="mini"
@click="nameState = false"
>表格</van-button
>
</div>
</div>
<!-- 名目支出统计的图表展示 -->
<div v-show="nameState">
<!-- <van-empty description="暂无图表数据信息" /> -->
<RingEcharts class="w-full h-full" id="nameEcharts" />
</div>
<!-- 名目支出统计的表格展示 -->
<div v-show="!nameState">
<Expenditure class="w-full h-full" id="nameTable" />
</div>
</div>
<!-- 成员财务图 -->
<div class="h-64 overflow-hidden">
<div>
<span class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"></span>
<div class="mt-5">
<span
class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"
></span>
<span>成员财务图</span>
</div>
<RingEcharts class="w-full h-full" id="memberEcharts"/>
<RingEcharts class="w-full h-full" id="memberEcharts" />
</div>
<!-- 时间财务图 -->
<div class="overflow-hidden">
<div>
<span class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"></span>
<span>时间财务图</span>
</div>
<BarEcharts class="w-full h-full mt-5" />
<!-- 没有数据展示空组件 -->
<!-- <van-empty description="暂无图表数据信息" /> -->
<div class="h-96 overflow-hidden">
<div>
<span
class="inline-block w-2 h-2 border-2 border-blue-400 rounded-full mr-3"
></span>
<span>时间财务图</span>
</div>
<BarEcharts class="w-full h-full" />
</div>
</div>
</div>
</van-tab>
<van-tab title="角色管理"> <div class="h-screen overflow-y-scroll w-screen mt-4">角色管理</div></van-tab>
<van-tab title="任务管理"><div class="h-screen overflow-y-scroll w-screen mt-4">任务管理</div></van-tab>
@ -120,6 +149,7 @@ export default {
<script setup>
import { ref, onMounted, nextTick } from 'vue';
const active = ref(0);
const taskState = ref(true);
const nameState = ref(true);
@ -138,7 +168,6 @@ async function scrollToElementByHash() {
document.documentElement.scrollTop = scrollDom.offsetTop;
window.pageYOffset = scrollDom.offsetTop;
}
</script>
<style lang="less">
@ -165,10 +194,17 @@ async function scrollToElementByHash() {
border-radius: 30px 0 0 30px;
margin-bottom: 15px;
}
.van-button--mini{
.button {
padding: 0px;
margin: 0px;
}
.van-button--mini {
padding: 0.5rem 1rem;
}
.van-button--mini+.van-button--mini{
.van-button--mini + .van-button--mini {
margin-left: 0px;
}
.table-box{
width: 95%;
}
</style>

4
plugins/vant.js

@ -25,6 +25,7 @@ import {
Dialog,
Picker,
Loading,
Overlay
} from 'vant';
import { defineNuxtPlugin } from '#app';
@ -53,5 +54,6 @@ export default defineNuxtPlugin(nuxtApp => {
.use(Popover)
.use(Dialog)
.use(Picker)
.use(Loading);
.use(Loading)
.use(Overlay)
});

Loading…
Cancel
Save