Authored by zhangwenxue

refactor(finance/invoice): using standalone pdf-upload component

<template>
<Modal v-model="visible" :title="title" @on-ok="ok" @on-cancel="cancel">
<Modal v-model="visible" :title="title" :loading="inLoading">
<Form ref="modal" :model="formItem" :label-width="80" :rules="rules">
<FormItem label="订单编号">
<i-input v-model="formItem.orderCode" readonly></i-input>
<span>{{ formItem.orderCode }}</span>
</FormItem>
<FormItem label="发票类型">
<RadioGroup v-model="formItem.type">
<Radio :label="ELECTRONIC">电子发票</Radio>
<Radio :label="PAPER">纸质发票</Radio>
<Radio v-if="formItem.status !== UNOPEN" :label="CANCELLED">作废</Radio>
<Radio :label="CANCELLED">作废</Radio>
</RadioGroup>
</FormItem>
<template v-if="formItem.status !== CANCELLED">
<template v-if="formItem.type == ELECTRONIC">
<FormItem label="上传PDF" prop="pdfUrl">
<!-- TODO: 设置文件类型,文件大小,及提示信息 -->
<drag-file-upload
upload-icon-title="上传发票"
bucket="invoice"
:format="['pdf']"
:format-err-msg="formatPdfFileTypeMsg"
:max-size="5 * 1024"
:format-max-size-msg="formatMaxSizeMsg"
@success="uploadPDFSuccess"
@remove="uploadPDFRemove"
>
</drag-file-upload>
</FormItem>
</template>
<template v-else>
<template v-if="formItem.type !== CANCELLED">
<FormItem v-show="formItem.type === ELECTRONIC" label="上传PDF" prop="pdfUrl">
<PDFUpload v-model="formItem.pdfUrl" @input="onPDFUrlChang" />
</FormItem>
<FormItem v-show="formItem.type === PAPER" label="物流信息">
<RadioGroup v-model="formItem.logisticsSwitch">
<Radio :label="LOGISTICS_YES">需要物流</Radio>
<Radio :label="LOGISTICS_NO">不需要物流</Radio>
</RadioGroup>
<template v-if="formItem.logisticsSwitch === LOGISTICS_YES">
<FormItem v-model="formItem.logisticsId" prop="logisticsId">
<Select>
<Option v-for="(item, idx) in expressList" :key="idx" :value="item.id">
{item.companyName}
</Option>
</Select>
</FormItem>
<FormItem prop="expressNumber">
<i-input v-model="formItem.expressNumber" type="text" placeholder="物流单号"></i-input>
</FormItem>
</template>
</FormItem>
<template v-if="formItem.logisticsSwitch === LOGISTICS_YES && formItem.type === PAPER">
<FormItem prop="logisticsId" class="logistics-company">
<Select v-model="formItem.logisticsId">
<Option v-for="(item, idx) in expressList" :key="idx" :value="item.id">
{{ item.companyName }}
</Option>
</Select>
</FormItem>
<FormItem prop="expressNumber" class="express-number">
<i-input v-model="formItem.expressNumber" type="text" placeholder="物流单号"></i-input>
</FormItem>
</template>
</template>
</Form>
<div slot="footer" class="modal-footer">
<span v-if="formItem.status !== UNOPEN"><i>*</i>在第三方开票系统冲红或作废后更新保存发票信息</span>
<Button type="primary" long :loading="inLoading" @click="update">保存</Button>
<Button long @click="close">关闭</Button>
<Button type="primary" :loading="inLoading" @click="update">保存</Button>
<Button @click="close">关闭</Button>
</div>
</Modal>
</template>
... ... @@ -61,14 +48,17 @@ import _ from 'lodash';
import { InvoiceStatusName2Id, InvoiceTypeName2Id, LogisticsTypeName2Id } from '../store/constant';
import InvoiceService from 'services/finance/invoice-service';
import TradeService from 'services/trade/trade-service';
// TODO: move to vuex
let ExpressCompanyList = [];
import PDFUpload from './pdf-upload';
export default {
name: 'UpdateInvoice',
components: {
PDFUpload,
},
data() {
return {
visible: false,
inLoading: false,
title: '',
// constants
UNOPEN: InvoiceStatusName2Id.UNOPEN,
... ... @@ -77,44 +67,49 @@ export default {
LOGISTICS_NO: LogisticsTypeName2Id.NO,
LOGISTICS_YES: LogisticsTypeName2Id.YES,
// api result data
expressList: ExpressCompanyList,
expressList: [],
formItem: {},
// rules
rules: {
pdfUrl: {
trigger: 'blur',
validator(rule, value, data) {
if (data.type === InvoiceTypeName2Id.ELECTRONIC) {
trigger: 'change',
validator: (rule, value, callback) => {
if (this.formItem.type === InvoiceTypeName2Id.ELECTRONIC) {
if (!value) {
return new Error('请上传发票PDF文件');
return callback(new Error('请上传发票PDF文件'));
}
}
callback();
},
},
logisticsId: {
trigger: 'blur',
validator(rule, value, data) {
if (data.type === InvoiceTypeName2Id.PAPER) {
trigger: 'change',
validator: (rule, value, callback) => {
if (this.formItem.type === InvoiceTypeName2Id.PAPER) {
if (!value) {
return new Error('请选择物流公司');
return callback(new Error('请选择物流公司'));
}
}
callback();
},
},
expressNumber: {
trigger: 'blur',
validator(rule, value, data) {
if (data.type === InvoiceTypeName2Id.PAPER) {
validator: (rule, value, callback) => {
if (this.formItem.type === InvoiceTypeName2Id.PAPER) {
if (!value) {
return new Error('请输入物流单号');
return callback(new Error('请输入物流单号'));
}
if (!/^[\w\d_-]{5,+}$/.test(value)) {
if (!/^[\w\d_-]{5,}$/.test(value)) {
// TODO: refs https://www.cnblogs.com/Jerseyblog/p/10077136.html
return new Error('请输入有效的物流单号'); // TODO: 描述下什么是有效的?
return callback(new Error('请输入有效的物流单号')); // TODO: 描述下什么是有效的?
}
}
callback();
},
},
},
... ... @@ -124,8 +119,8 @@ export default {
this.invoiceAPI = new InvoiceService();
this.tradeAPI = new TradeService();
if (this.expressList.length === 0) {
this.tradeAPI.allotExpressCompList().then(({ data }) => {
this.expressList = ExpressCompanyList = data;
this.tradeAPI.allotExpressCompList().then(data => {
this.expressList = data;
});
}
},
... ... @@ -138,11 +133,17 @@ export default {
}
},
show(invoice) {
this.$refs.modal.resetFields();
// this.$refs.modal.resetFields();
this.setTitle(invoice);
// initial data
this.invoiceData = invoice;
this.formItem = { ...invoice };
this.formItem = {
pdfUrl: null,
logisticsId: null,
expressNumber: null,
logisticsSwitch: this.LOGISTICS_YES,
...invoice,
};
this.visible = true;
},
/**
... ... @@ -176,7 +177,7 @@ export default {
*/
updateState(invoice) {
const { status: currentStatus } = this.invoiceData;
const isCancled = (invoice.status = InvoiceStatusName2Id.CANCELLED);
const isCancled = invoice.status === InvoiceStatusName2Id.CANCELLED;
if (isCancled) {
if (currentStatus === isCancled) {
... ... @@ -225,44 +226,60 @@ export default {
this.close(true);
return;
}
this.inLoading = true;
this.invoiceAPI
.update(params)
.then(() => {
this.inLoading = false;
this.$Message.success('更新成功');
this.$emit('updated', params);
this.close();
})
.catch(() => {
this.$methods.warn('更新失败');
this.inLoading = false;
this.$Message.warning('更新失败');
});
}
});
// TODO:
// update status
},
ok() {
debugger;
},
cancel() {
debugger;
},
close(force) {
if (!force) {
// dirty check
}
this.visible = false;
},
uploadPDFSuccess(attach, res) {
this.formItem.pdfUrl = res.url;
},
uploadPDFRemove() {
this.formItem.pdfUrl = '';
},
formatPdfFileTypeMsg(file) {
return `文件${file.name}格式不正确, 请上传pdf文件`;
},
formatMaxSizeMsg(file) {
return `文件${file.name}太大,不能超过5M`;
onPDFUrlChang() {
this.$refs.modal.validateField('pdfUrl');
},
},
};
</script>
<style lang="scss" scoped>
.modal-footer {
& > span {
float: left;
line-height: 32px;
}
i {
color: red;
}
}
.file-upload {
display: inline-block;
}
.logistics-company {
display: inline-block;
min-width: 20em;
}
.express-number {
display: inline-block;
/deep/ .ivu-form-item-content {
margin-left: 20px !important;
}
}
</style>
... ...
<template>
<div>
<template v-show="uploadList.length !== 0">
<div v-for="(item, idx) in uploadList" :key="idx" class="upload-list upload-wrap">
<template v-if="item.status === 'finished'">
<Icon type="document" size="32" style="line-height: 64px"></Icon>
<div class="upload-list-cover">
<Icon type="ios-eye-outline" @click.native="handleView(item.name)"></Icon>
<Icon type="ios-trash-outline" @click.native="handleRemove(item)"></Icon>
</div>
</template>
<template v-else>
<Progress v-if="item.showProgress" :percent="item.percentage" hide-info></Progress>
</template>
</div>
</template>
<Upload
v-show="uploadList.length === 0"
ref="upload"
:data="{ bucket: 'invoices' }"
:show-upload-list="false"
:on-success="handleSuccess"
:format="['pdf']"
:max-size="1024 * 1024 * 5"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
:before-upload="handleBeforeUpload"
type="drag"
action="/Api/upload/image"
style="display: inline-block;width:64px;"
>
<div class="upload-wrap">
<Icon type="ios-cloud-upload-outline" title="上标发票文件" size="32"></Icon>
</div>
</Upload>
<Modal v-model="visible" title="查看发票" :width="800">
<iframe v-if="visible" :src="uploadList[0].url" frameborder="0" scrolling="auto" />
</Modal>
</div>
</template>
<script>
import _ from 'lodash';
export default {
props: {
value: {
type: [String, null],
default: null,
},
},
data() {
return {
visible: false,
uploadList: [],
};
},
// watch: {
// value: {
// handler(newVal) {
// },
// },
// computed: {
// uploadList() {
// return this.$refs.upload.fileList;
// },
// },
mounted() {
this.clearFileList();
this.unwatch = this.$watch(
'value',
newVal => {
let addNew = true;
if (this.uploadList[0]) {
if (this.uploadList[0].url !== newVal) {
this.clearFileList();
} else {
addNew = false;
}
}
if (addNew && newVal) {
this.uploadList = this.$refs.upload.fileList = [
{
url: newVal,
status: 'finished',
showProcent: false,
},
];
}
},
{
immediate: true,
}
);
},
destroyed() {
this.unwatch();
},
methods: {
clearFileList() {
// const fileList = ;
// fileList.splice(0, fileList.length); // delete all
// return fileList;
this.uploadList = this.$refs.upload.fileList = [];
},
handleView() {
this.visible = true;
},
handleRemove() {
// const fileList = this.$refs.upload.fileList;
// this.$refs.upload.fileList.splice(fileList.indexOf(file), 1);
this.clearFileList();
this.$emit('input', null);
},
handleSuccess(res, file) {
if (_.get(res, 'data.imagesList.length', 0)) {
file.url = res.data.imagesList[0];
} else {
return;
}
this.$emit('input', file.url);
},
handleFormatError(file) {
this.$Notice.warning({
title: '格式错误',
desc: `${file.name}不是pdf文件,请上传pdf`,
});
},
handleMaxSize(file) {
this.$Notice.warning({
title: '文件大小错误',
desc: `${file.name}的大小三于5MB`,
});
},
handleBeforeUpload() {
return this.uploadList.length < 2;
},
},
};
</script>
<style lang="scss" scoped>
.upload-wrap {
width: 64px;
height: 64px;
line-height: 64px;
text-align: center;
i {
line-height: 64px;
}
}
.upload-list {
display: inline-block;
border: 1px solid #999;
border-radius: 4px;
overflow: hidden;
background: #fff;
position: relative;
}
.upload-list img {
width: 100%;
height: 100%;
}
.upload-list-cover {
display: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
}
.upload-list:hover .upload-list-cover {
display: block;
}
.upload-list-cover i {
color: #fff;
font-size: 20px;
cursor: pointer;
margin: 0 2px;
}
iframe {
border: none;
width: 100%;
height: 600px;
}
</style>
... ...
... ... @@ -38,7 +38,7 @@
<Table border :columns="tableCols" :data="tableData" />
<Page :total="pageData.total" :current="pageData.pageNo" :page-size="20" show-total @on-change="pageChange" />
</layout-list>
<invoice-update ref="modal" @updated="invoiceUpdated" />
<invoice-update v-if="modalVisible" ref="modal" @updated="invoiceUpdated" />
</layout-body>
</template>
... ... @@ -73,6 +73,8 @@ export default {
noOpenInvoiceSum: 0, // 未开票
openInvoiceSum: 0, // 已开票
returnNoOptionSum: 0, // 退货待处理
modalVisible: false,
};
},
computed: {
... ... @@ -171,7 +173,10 @@ export default {
this.search();
},
onClickInfo(params) {
this.$refs.modal.show(params.row);
this.modalVisible = true;
this.$nextTick(() => {
this.$refs.modal.show(params.row);
});
},
exportList() {
// TODO: check timerange
... ...