invoice-update.vue 8.4 KB
<template>
  <Modal v-model="visible" :title="title" @on-ok="ok" @on-cancel="cancel">
    <Form ref="modal" :model="formItem" :label-width="80" :rules="rules">
      <FormItem label="订单编号">
        <i-input v-model="formItem.orderCode" readonly></i-input>
      </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>
        </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>
          <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>
        </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>
    </div>
  </Modal>
</template>

<script>
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 = [];

export default {
  name: 'UpdateInvoice',
  data() {
    return {
      visible: false,
      title: '',
      // constants
      UNOPEN: InvoiceStatusName2Id.UNOPEN,
      CANCELLED: InvoiceStatusName2Id.CANCELLED,
      ...InvoiceTypeName2Id,
      LOGISTICS_NO: LogisticsTypeName2Id.NO,
      LOGISTICS_YES: LogisticsTypeName2Id.YES,
      // api result data
      expressList: ExpressCompanyList,
      formItem: {},

      // rules
      rules: {
        pdfUrl: {
          trigger: 'blur',
          validator(rule, value, data) {
            if (data.type === InvoiceTypeName2Id.ELECTRONIC) {
              if (!value) {
                return new Error('请上传发票PDF文件');
              }
            }
          },
        },
        logisticsId: {
          trigger: 'blur',
          validator(rule, value, data) {
            if (data.type === InvoiceTypeName2Id.PAPER) {
              if (!value) {
                return new Error('请选择物流公司');
              }
            }
          },
        },
        expressNumber: {
          trigger: 'blur',
          validator(rule, value, data) {
            if (data.type === InvoiceTypeName2Id.PAPER) {
              if (!value) {
                return new Error('请输入物流单号');
              }

              if (!/^[\w\d_-]{5,+}$/.test(value)) {
                // TODO: refs https://www.cnblogs.com/Jerseyblog/p/10077136.html
                return new Error('请输入有效的物流单号'); // TODO: 描述下什么是有效的?
              }
            }
          },
        },
      },
    };
  },
  created() {
    this.invoiceAPI = new InvoiceService();
    this.tradeAPI = new TradeService();
    if (this.expressList.length === 0) {
      this.tradeAPI.allotExpressCompList().then(({ data }) => {
        this.expressList = ExpressCompanyList = data;
      });
    }
  },
  methods: {
    setTitle(invoice) {
      if (invoice.status === InvoiceStatusName2Id.UNOPEN) {
        this.title = '录入发票';
      } else {
        this.title = '发票详情';
      }
    },
    show(invoice) {
      this.$refs.modal.resetFields();
      this.setTitle(invoice);
      // initial data
      this.invoiceData = invoice;
      this.formItem = { ...invoice };
      this.visible = true;
    },
    /**
     * 前端可控制的状态转移
     * Q:
     * 这个作废是什么意思?
     * 选择作废后,下面的 pdf 或物流信息怎么处理
     * 如果发票状态是未开发票,可以先作废吗
     * 选作废后,是不是就不可以编辑当前发票的状态了
     *
     * A:
     * 1.作废就是不提供发票了,之前提供的发票也不展示了
     * 2.删除
     * 3.可以,表示不提供发票
     * 4.可以编辑,作废只是状态之一,不是终点
     *
     * so: rules
     *
     * [1]未开发票->已经开发票
     * [2]退货未处理->退货已经处理
     * [3]作废->已开发票(针对退款额为0)
     * [4]作废->退货已经处理((针对退款额不为0)
     *
     * [5]非作废状态都可作废(*->作废)
     * 退货未处理->作废
     * 退货已经处理->作废
     * 未开发票->作废
     * 已开发票->作废
     * [6] 已开票->已开票, 退货已经处理->退货已经处理(更新发票信息)
     * return: true: 可以提交, false: 不必提交(当前状态为作废, 选择状态还是作废)
     */
    updateState(invoice) {
      const { status: currentStatus } = this.invoiceData;
      const isCancled = (invoice.status = InvoiceStatusName2Id.CANCELLED);

      if (isCancled) {
        if (currentStatus === isCancled) {
          return false;
        }

        // 作废, 删除关联数据 [5]
        _.assign(invoice, {
          type: InvoiceTypeName2Id.ELECTRONIC,
          pdfUrl: null,
          logisticsSwitch: null,
          logisticsId: null,
          expressNumber: null,
        });
      } else {
        let nextStatus;
        switch (currentStatus) {
          case InvoiceStatusName2Id.UNOPEN:
            nextStatus = InvoiceStatusName2Id.OPNED; // [1]
            break;
          case InvoiceStatusName2Id.REJECT_UNHANDLED:
            nextStatus = InvoiceStatusName2Id.REJECT_HANDLED; // [2]
            break;
          case InvoiceStatusName2Id.CANCELLED:
            if (this.invoiceData.returnAmount === 0) {
              nextStatus = InvoiceStatusName2Id.OPNED; // [3]
            } else {
              nextStatus = InvoiceStatusName2Id.REJECT_HANDLED; // [4]
            }
            break;
          default:
            nextStatus = currentStatus; // [6]
        }
        invoice.status = nextStatus;
      }

      return true;
    },
    update() {
      const params = { ...this.formItem };

      // validate
      this.$refs.modal.validate(valid => {
        if (valid) {
          if (!this.updateState(params)) {
            this.close(true);
            return;
          }
          this.invoiceAPI
            .update(params)
            .then(() => {
              this.$emit('updated', params);
              this.close();
            })
            .catch(() => {
              this.$methods.warn('更新失败');
            });
        }
      });
      // 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`;
    },
  },
};
</script>