xiaodai2017 2 years ago
parent
commit
5a9233a7bf
51 changed files with 3808 additions and 0 deletions
  1. 0 0
      src/views/InvoiceSales/invoiceQuery/columns.tsx
  2. 0 0
      src/views/InvoiceSales/invoiceQuery/components/components/invoice-modal.vue
  3. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase-config/_details.ts
  4. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase-config/_opitons.ts
  5. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase-config/_rules.ts
  6. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase-config/content.config.ts
  7. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase-config/modal.config.ts
  8. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase-config/search.config.ts
  9. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase-config/xls-template.ts
  10. 0 0
      src/views/InvoiceSales/invoiceQuery/components/purchase.vue
  11. 0 0
      src/views/InvoiceSales/invoiceQuery/components/sale-config/content.config.ts
  12. 0 0
      src/views/InvoiceSales/invoiceQuery/components/sale-config/modal.config.ts
  13. 0 0
      src/views/InvoiceSales/invoiceQuery/components/sale-config/search.config.ts
  14. 0 0
      src/views/InvoiceSales/invoiceQuery/components/sale.vue
  15. 0 0
      src/views/InvoiceSales/invoiceQuery/index.vue
  16. 0 0
      src/views/InvoiceSales/invoiceQuery/options.ts
  17. 0 0
      src/views/InvoiceSales/invoiceQuery/发票查询管理.md
  18. 179 0
      src/views/purchase/buyInvoiceQuery/components/choose-modal.vue
  19. 40 0
      src/views/purchase/buyInvoiceQuery/components/execl-files-upload/columns-config.ts
  20. 233 0
      src/views/purchase/buyInvoiceQuery/components/execl-files-upload/index.vue
  21. 21 0
      src/views/purchase/buyInvoiceQuery/components/invoice-dialog.vue
  22. 279 0
      src/views/purchase/buyInvoiceQuery/components/invoice-form.vue
  23. 21 0
      src/views/purchase/buyInvoiceQuery/components/invoice-modal.vue
  24. 67 0
      src/views/purchase/buyInvoiceQuery/components/invoice-table.vue
  25. 65 0
      src/views/purchase/buyInvoiceQuery/components/return-form.vue
  26. 351 0
      src/views/purchase/buyInvoiceQuery/config/_details.ts
  27. 76 0
      src/views/purchase/buyInvoiceQuery/config/_opitons.ts
  28. 82 0
      src/views/purchase/buyInvoiceQuery/config/_rules.ts
  29. 198 0
      src/views/purchase/buyInvoiceQuery/config/content.config.ts
  30. 95 0
      src/views/purchase/buyInvoiceQuery/config/modal.config.ts
  31. 65 0
      src/views/purchase/buyInvoiceQuery/config/search.config.ts
  32. 7 0
      src/views/purchase/buyInvoiceQuery/config/xls-template.ts
  33. 125 0
      src/views/purchase/buyInvoiceQuery/index.vue
  34. 0 0
      src/views/purchase/buyInvoiceQuery/买入回票查询.md
  35. 179 0
      src/views/supply/sellInvoiceQuery/components/choose-modal.vue
  36. 40 0
      src/views/supply/sellInvoiceQuery/components/execl-files-upload/columns-config.ts
  37. 233 0
      src/views/supply/sellInvoiceQuery/components/execl-files-upload/index.vue
  38. 21 0
      src/views/supply/sellInvoiceQuery/components/invoice-dialog.vue
  39. 279 0
      src/views/supply/sellInvoiceQuery/components/invoice-form.vue
  40. 21 0
      src/views/supply/sellInvoiceQuery/components/invoice-modal.vue
  41. 67 0
      src/views/supply/sellInvoiceQuery/components/invoice-table.vue
  42. 65 0
      src/views/supply/sellInvoiceQuery/components/return-form.vue
  43. 351 0
      src/views/supply/sellInvoiceQuery/config/_details.ts
  44. 76 0
      src/views/supply/sellInvoiceQuery/config/_opitons.ts
  45. 82 0
      src/views/supply/sellInvoiceQuery/config/_rules.ts
  46. 198 0
      src/views/supply/sellInvoiceQuery/config/content.config.ts
  47. 95 0
      src/views/supply/sellInvoiceQuery/config/modal.config.ts
  48. 65 0
      src/views/supply/sellInvoiceQuery/config/search.config.ts
  49. 7 0
      src/views/supply/sellInvoiceQuery/config/xls-template.ts
  50. 125 0
      src/views/supply/sellInvoiceQuery/index.vue
  51. 0 0
      src/views/supply/sellInvoiceQuery/卖出回票查询.md

+ 0 - 0
src/views/invoice/query/columns.tsx → src/views/InvoiceSales/invoiceQuery/columns.tsx


+ 0 - 0
src/views/invoice/query/components/components/invoice-modal.vue → src/views/InvoiceSales/invoiceQuery/components/components/invoice-modal.vue


+ 0 - 0
src/views/invoice/query/components/purchase-config/_details.ts → src/views/InvoiceSales/invoiceQuery/components/purchase-config/_details.ts


+ 0 - 0
src/views/invoice/query/components/purchase-config/_opitons.ts → src/views/InvoiceSales/invoiceQuery/components/purchase-config/_opitons.ts


+ 0 - 0
src/views/invoice/query/components/purchase-config/_rules.ts → src/views/InvoiceSales/invoiceQuery/components/purchase-config/_rules.ts


+ 0 - 0
src/views/invoice/query/components/purchase-config/content.config.ts → src/views/InvoiceSales/invoiceQuery/components/purchase-config/content.config.ts


+ 0 - 0
src/views/invoice/query/components/purchase-config/modal.config.ts → src/views/InvoiceSales/invoiceQuery/components/purchase-config/modal.config.ts


+ 0 - 0
src/views/invoice/query/components/purchase-config/search.config.ts → src/views/InvoiceSales/invoiceQuery/components/purchase-config/search.config.ts


+ 0 - 0
src/views/invoice/query/components/purchase-config/xls-template.ts → src/views/InvoiceSales/invoiceQuery/components/purchase-config/xls-template.ts


+ 0 - 0
src/views/invoice/query/components/purchase.vue → src/views/InvoiceSales/invoiceQuery/components/purchase.vue


+ 0 - 0
src/views/invoice/query/components/sale-config/content.config.ts → src/views/InvoiceSales/invoiceQuery/components/sale-config/content.config.ts


+ 0 - 0
src/views/invoice/query/components/sale-config/modal.config.ts → src/views/InvoiceSales/invoiceQuery/components/sale-config/modal.config.ts


+ 0 - 0
src/views/invoice/query/components/sale-config/search.config.ts → src/views/InvoiceSales/invoiceQuery/components/sale-config/search.config.ts


+ 0 - 0
src/views/invoice/query/components/sale.vue → src/views/InvoiceSales/invoiceQuery/components/sale.vue


+ 0 - 0
src/views/invoice/query/index.vue → src/views/InvoiceSales/invoiceQuery/index.vue


+ 0 - 0
src/views/invoice/query/options.ts → src/views/InvoiceSales/invoiceQuery/options.ts


+ 0 - 0
src/views/invoice/query/发票查询管理.md → src/views/InvoiceSales/invoiceQuery/发票查询管理.md


+ 179 - 0
src/views/purchase/buyInvoiceQuery/components/choose-modal.vue

@@ -0,0 +1,179 @@
+<script setup lang="ts">
+import { ref, unref, reactive } from "vue";
+import { pay_status } from "/@/utils/details/reconciliation";
+import { httpList } from "/@/api/purchase/orderRecord";
+import { useCompany } from "/@/hooks/core/useCompany";
+import { ElMessage, ElTable } from "element-plus";
+import { columns } from "../config/_details";
+import { useResponseHandle } from "/@/hooks";
+
+const emit = defineEmits(["choose"]);
+
+const visible = ref(false);
+const paymentList = ref<Array<Record<string, string>>>([]);
+const selects = ref<Array<Record<string, string>>>([]);
+
+const pagination = reactive({
+  total: 0,
+  size: 10,
+  page: 1,
+  background: true
+});
+
+const formData = ref<Record<string, string>>({
+  pay_status: "",
+  currentValue: ""
+});
+
+const tableRef = ref<InstanceType<typeof ElTable>>(null);
+const currentKey = ref("payNo");
+const loading = ref(false);
+const responseHandle = useResponseHandle();
+
+const { currentCompany } = useCompany();
+
+//初始化订单对账列表
+async function requestPaymentList() {
+  const { size, page } = pagination;
+  loading.value = true;
+
+  const { currentValue, ...otherParams } = formData.value;
+
+  const { code, message, data } = await httpList({
+    pay_status: "0",
+    status: "3",
+    [currentKey.value]: currentValue,
+    companyNo: currentCompany.value.companyNo,
+    ...otherParams,
+    page,
+    size
+  });
+
+  responseHandle({
+    code,
+    message,
+    handler: () => {
+      pagination.total = data.count;
+      paymentList.value = data.list;
+    }
+  });
+  loading.value = false;
+}
+
+function handleConfirm() {
+  const [order] = selects.value;
+  if (!order) return ElMessage.warning("请选择一个对账单");
+  emit("choose", unref(order.payNo));
+  visible.value = false;
+}
+
+function handleSelectionChange(values) {
+  if (values.length > 1) {
+    const value = values.pop();
+    tableRef.value.clearSelection();
+    tableRef.value.toggleRowSelection(value, true);
+    selects.value = [value];
+  } else {
+    selects.value = [values[0]];
+  }
+}
+
+function handleSearch(_isReset?: boolean) {
+  if (_isReset) formData.value = {};
+  pagination.page = 1;
+  requestPaymentList();
+}
+
+defineExpose({
+  onDisplay: () => (visible.value = true)
+});
+</script>
+
+<template>
+  <el-dialog
+    v-model="visible"
+    title="订单对账"
+    center
+    width="1040px"
+    @open="() => requestPaymentList()"
+  >
+    <div flex justify-between mb-2>
+      <div flex gap-2>
+        <el-input
+          clearable
+          v-model="formData.currentValue"
+          placeholder="对账编号"
+        >
+          <template #prepend>
+            <el-select v-model="currentKey" style="width: 130px">
+              <el-option label="对账编号" value="payNo" />
+              <el-option label="卖方公司编号" value="supplierNo" />
+              <el-option label="卖方公司名称" value="supplierName" />
+            </el-select>
+          </template>
+        </el-input>
+        <el-select
+          clearable
+          w-80
+          v-model="formData.pay_status"
+          placeholder="付款状态"
+        >
+          <el-option value="1" label="未付款" />
+          <el-option value="2" label="部分付款" />
+        </el-select>
+      </div>
+
+      <div>
+        <el-button type="primary" @click="() => handleSearch()">搜索</el-button>
+        <el-button @click="() => handleSearch(true)">重置</el-button>
+      </div>
+    </div>
+    <el-table
+      border
+      ref="tableRef"
+      :data="paymentList"
+      size="small"
+      row-key="id"
+      @selection-change="handleSelectionChange"
+      mb-2
+      v-loading="loading"
+    >
+      <el-table-column type="selection" width="55" />
+      <el-table-column
+        v-for="(col, index) in columns"
+        :key="index"
+        v-bind="col"
+        show-overflow-tooltip
+      >
+        <template #="{ row }">
+          <el-tag v-if="col.prop === 'pay_status'">{{
+            pay_status.find(p => p.value === String(row.pay_status))?.label
+          }}</el-tag>
+
+          <span v-else>{{ row[col.prop] }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <div flex>
+      <el-pagination
+        v-model:current-page="pagination.page"
+        :page-size="pagination.size"
+        :total="pagination.total"
+        @current-change="requestPaymentList"
+      />
+
+      <div w-full flex justify-end mt-2>
+        <el-button type="primary" @click="handleConfirm">保存</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-table__header) {
+  .el-checkbox {
+    display: none;
+  }
+}
+</style>

+ 40 - 0
src/views/purchase/buyInvoiceQuery/components/execl-files-upload/columns-config.ts

@@ -0,0 +1,40 @@
+const initheaders = [
+  "买方公司编码",
+  "回票申请编码",
+  "对账编号",
+  "审核状态",
+  "审核备注"
+];
+
+export const mapProp = {
+  value0: "companyNo",
+  value1: "hpNo",
+  value2: "payNo",
+  value3: "status",
+  value4: "remark"
+};
+
+export const requireHeaders = ["payNo", "companyNo", "hpNo", "status"];
+
+const columns = () => {
+  const list: any[] = [
+    {
+      type: "index",
+      width: "50",
+      fixed: "left",
+      label: "序号"
+    }
+  ];
+  initheaders.forEach((si, sii) => {
+    list.push({
+      prop: "value" + sii,
+      label: si,
+      minWidth: sii === 0 || sii === 1 ? "120px" : "90px"
+    });
+  });
+
+  console.log(list);
+  return list;
+};
+
+export { initheaders, columns };

+ 233 - 0
src/views/purchase/buyInvoiceQuery/components/execl-files-upload/index.vue

@@ -0,0 +1,233 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import { ElMessage } from "element-plus";
+import { execlUpload } from "/@/components/execlUpload";
+import { httpBatchimport } from "/@/api/purchase/ticketReturn";
+import { useResponseHandle } from "/@/hooks";
+import { useCompany } from "/@/hooks/core/useCompany";
+
+import {
+  initheaders,
+  columns,
+  mapProp,
+  requireHeaders
+} from "./columns-config";
+
+const visible = ref(false);
+const loading = ref(false);
+const tableData = ref([]);
+const columnsConfig = columns();
+const emit = defineEmits(["onSuccess"]);
+
+const { currentCompany } = useCompany();
+
+const responseHandle = useResponseHandle();
+
+const Uploadsuccess = ({ results, header }) => {
+  loading.value = true;
+
+  if (results.length === 0) {
+    ElMessage.error("表格无有效数据!");
+    loading.value = false;
+    return;
+  }
+
+  let headok = true;
+  if (header.length !== initheaders.length) {
+    headok = false;
+  } else {
+    initheaders.forEach((si, sii) => {
+      if (si !== header[sii]) {
+        headok = false;
+      }
+    });
+  }
+
+  if (!headok) {
+    ElMessage.error("表头与导入模板不匹配!");
+    loading.value = false;
+    return;
+  }
+  tableData.value = [];
+
+  try {
+    for (const v1 of results) {
+      const b = Object.values(v1);
+      let model = {};
+      b.forEach((si, sii) => {
+        model["value" + sii] = si + "";
+      });
+      tableData.value.push(model);
+    }
+    loading.value = false;
+  } catch (err) {
+    return err;
+  }
+};
+//提交
+const handleSubmit = async () => {
+  if (loading.value) return;
+  loading.value = true;
+
+  const data = [];
+  const errorStatus = [];
+  const errorHp = [];
+  const errorDz = [];
+
+  tableData.value.forEach((key, index) => {
+    const obj: Record<string, string> = {};
+
+    for (let i in key) {
+      const prop = mapProp[i];
+      const value = key[i];
+      if (prop === "status") {
+        const trimValue = value.trim();
+        if (trimValue !== "通过" && trimValue !== "驳回") {
+          errorStatus.push(index + 1);
+        } else {
+          obj[prop] = trimValue === "通过" ? "4" : "7";
+        }
+      } else if (prop === "hpNo") {
+        if (!value) errorHp.push(index + 1);
+        obj[prop] = value;
+      } else if (prop === "payNo") {
+        if (!value) errorDz.push(index + 1);
+        obj[prop] = value;
+      } else {
+        obj[prop] = value;
+      }
+    }
+    data.push(obj);
+  });
+
+  const buyers = data.map(({ companyNo }) => companyNo);
+  const setBuyers = [...new Set(buyers)];
+
+  if (errorStatus.length > 0) {
+    ElMessage.error(
+      `第 ${errorStatus.join(",")} 行审核状态格式不正确,应该为'通过',或'驳回'`
+    );
+    loading.value = false;
+    return;
+  }
+
+  if (errorHp.length > 0) {
+    ElMessage.error(
+      `第 ${errorHp.join(",")} 行格式不正确,回票申请编号为必填项`
+    );
+    loading.value = false;
+    return;
+  }
+
+  if (errorDz.length > 0) {
+    ElMessage.error(`第 ${errorDz.join(",")} 行格式不正确,对账编号为必填项`);
+    loading.value = false;
+    return;
+  }
+
+  if (setBuyers.length > 1) {
+    ElMessage.error("买方公司编码不一致");
+    loading.value = false;
+    return;
+  }
+
+  if (setBuyers[0] !== currentCompany.value.companyNo) {
+    ElMessage.error("买方公司编码与当前选择的公司不一致");
+    loading.value = false;
+    return;
+  }
+
+  data.forEach(item => {
+    delete item["payNo"];
+    delete item["companyNo"];
+  });
+
+  const { code, message } = await httpBatchimport({
+    list: data
+  });
+
+  loading.value = false;
+
+  responseHandle({
+    code,
+    message,
+    handler: () => {
+      ElMessage.success("数据导入成功!");
+      emit("onSuccess");
+      visible.value = false;
+    }
+  });
+};
+const cancel = () => {
+  tableData.value = [];
+};
+defineExpose({
+  onDisplay: () => ((visible.value = true), (tableData.value = []))
+});
+</script>
+
+<template>
+  <el-dialog
+    v-model="visible"
+    title="批量导入认证结果"
+    width="1040px"
+    top="8vh"
+    center
+  >
+    <execlUpload @on-success="Uploadsuccess" v-if="tableData.length === 0" />
+
+    <el-table
+      :data="tableData"
+      stripe
+      border
+      max-height="500px"
+      size="small"
+      style="width: 100%"
+    >
+      <el-table-column
+        v-for="(si, sii) in columnsConfig"
+        :type="si.type"
+        :minWidth="si.minWidth"
+        :fixed="si.fixed"
+        :key="sii"
+        :prop="si.prop"
+        show-overflow-tooltip
+      >
+        <template #header>
+          <span
+            v-if="
+              !requireHeaders.includes(mapProp[si.prop]) || si.label === '序号'
+            "
+            >{{ si.label }}</span
+          >
+          <p v-else>
+            <span style="color: #f56c6c; font-size: 14px">* </span>
+            {{ si.label }}
+          </p>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div
+      flex
+      justify-end
+      gap-2
+      v-if="tableData.length !== 0"
+      style="padding: 10px 0 0 0"
+    >
+      <el-button size="small" @click="cancel">取消</el-button>
+      <el-button
+        size="small"
+        type="primary"
+        :loading="loading"
+        @click="handleSubmit"
+        >保存</el-button
+      >
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-upload-list__item) {
+  display: none !important;
+}
+</style>

+ 21 - 0
src/views/purchase/buyInvoiceQuery/components/invoice-dialog.vue

@@ -0,0 +1,21 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import Invoice from "/@/components/Invoice";
+
+const visible = ref(false);
+
+const invoiceNumber = ref("");
+
+defineExpose({
+  onDisplay: ({ invoiceNumber: _invoiceNumber }) => {
+    visible.value = true;
+    invoiceNumber.value = _invoiceNumber;
+  }
+});
+</script>
+
+<template>
+  <el-dialog v-model="visible" width="1040px">
+    <Invoice :inv-number="invoiceNumber" type="purchase" />
+  </el-dialog>
+</template>

+ 279 - 0
src/views/purchase/buyInvoiceQuery/components/invoice-form.vue

@@ -0,0 +1,279 @@
+<script setup lang="ts">
+import { computed, ref, watch } from "vue";
+import { dayjs, ElForm, ElInput, ElMessage, UploadProps } from "element-plus";
+import { ticketFormItems } from "../config/_details";
+import { invoceRules } from "../config/_rules";
+import { useResponseHandle } from "/@/hooks/core/useAsync";
+import { httpImageUpload, baseUrl } from "/@/api/other";
+import { useUserStore } from "/@/store/modules/user";
+import { inv_type_list } from "/@/utils/status";
+import { INPUT_MAX_LENGTH } from "/@/utils/global";
+
+const emit = defineEmits(["change"]);
+
+const visible = ref(false);
+const fileList = ref([]);
+
+//支持的图片格式
+const imgs = ref<any[]>([]);
+const invType = ref("1");
+const loading = ref(false);
+const types = ["png", "jpg", "bmp", "jpeg"].map(format => "image/" + format);
+const formItems = computed(() => ticketFormItems[invType.value] || []);
+const rules = ref({ ...invoceRules });
+const formRef = ref<InstanceType<typeof ElForm>>(null);
+const showScanInput = computed(() => invType.value === "1");
+const scanInputValue = ref("");
+
+const formData = ref<Record<string, string>>({
+  currentValue: ""
+});
+
+const userStore = useUserStore();
+const responseHandle = useResponseHandle();
+
+//图片上传前判断类型大小尺寸
+const onBeforeReturnImageUpload: UploadProps["beforeUpload"] = ({
+  type,
+  size
+}) => {
+  if (!types.includes(type)) {
+    ElMessage.error("请上传jpg.png.bmp.jpeg类型图片");
+    return false;
+  }
+
+  if (size / 1024 / 1024 > 1) {
+    ElMessage.error("图片大小超过1M");
+    return false;
+  }
+};
+
+function handleClose() {
+  imgs.value = [];
+  fileList.value = [];
+  formData.value = {};
+}
+
+//保存采购回票
+function handleSave() {
+  formRef.value.validate(async isValid => {
+    if (!isValid) return;
+    if (!invType.value) return ElMessage.error("请选择申请类型");
+
+    if (invType.value === "2") {
+      if (!imgs.value.length) return ElMessage.error("请选择上传的图片");
+      if (imgs.value.length > 100)
+        return ElMessage.error("上传图片不能超过100张");
+    }
+
+    emit(
+      "change",
+      invType.value === "2"
+        ? imgs.value
+        : {
+            ...formData.value,
+            invType: invType.value
+          }
+    );
+
+    visible.value = false;
+  });
+}
+
+function handleScanKeydown(evt: KeyboardEvent) {
+  const { keyCode } = evt;
+
+  console.log(keyCode);
+  if (keyCode !== 13) return;
+
+  //校验扫描枪值是否含有中文逗号
+  if (scanInputValue.value.indexOf(",") >= 0) {
+    ElMessage.error("不能包含中文逗号");
+    return (scanInputValue.value = "");
+  }
+
+  const [_, invoiceType, ...chunks] = scanInputValue.value.split(",");
+  const [invCode, invNumber, subtotal_amount, open_time, checkNumber] = chunks;
+
+  const year = open_time.slice(0, 4);
+  const month = open_time.slice(4, 6);
+  const day = open_time.slice(6);
+
+  formData.value = {
+    invoiceType,
+    invCode,
+    subtotal_amount,
+    open_time: dayjs(`${year}-${month}-${day}`).format("YYYY-MM-DD HH:mm:ss"),
+    invNumber,
+    checkNumber
+  };
+
+  scanInputValue.value = "";
+}
+
+const handleRequeset: UploadProps["httpRequest"] = async ({ file }) => {
+  const _formData = new FormData();
+  _formData.append("img", file);
+  _formData.append("token", userStore.token);
+  const { message, code, data } = await httpImageUpload(_formData);
+
+  responseHandle({
+    message,
+    code,
+    handler: () => {
+      data.forEach(({ url, name }) => {
+        imgs.value.push({
+          inv_img: baseUrl + "/" + url,
+          invName: name,
+          invType: "2"
+        });
+      });
+    }
+  });
+};
+
+const handleRemove = uploadFile => {
+  const delIndex = fileList.value.findIndex(
+    ({ uid }) => uid === uploadFile.uid
+  );
+  imgs.value.splice(delIndex, 1);
+  return true;
+};
+
+watch(
+  () => formItems.value,
+  newVal => {
+    const newFormData: Record<string, string> = {};
+    newFormData.payNo = formData.value.payNo;
+
+    newVal.forEach(item => {
+      if (item.prop !== "payNo") newFormData[item.prop] = "";
+    });
+
+    formData.value = newFormData;
+  }
+);
+
+const disabledDate = time => {
+  return time.getTime() > Date.now();
+};
+
+defineExpose({
+  onDisplay: () => (visible.value = true)
+});
+</script>
+
+<template>
+  <el-dialog v-model="visible" @close="handleClose">
+    <el-form
+      label-width="100px"
+      :model="formData"
+      :rules="rules"
+      ref="formRef"
+      size="small"
+    >
+      <el-form-item label="发票上传方式">
+        <el-select v-model="invType">
+          <el-option label="手工添加" value="1" />
+          <el-option label="ocr识别" value="2" />
+        </el-select>
+      </el-form-item>
+
+      <!-- 扫码枪识别 -->
+      <el-form-item label="扫码枪" v-if="showScanInput">
+        <el-input
+          type="textarea"
+          placeholder="请在扫码完成后手动回车"
+          @keydown="handleScanKeydown"
+          v-model="scanInputValue"
+          v-bind="INPUT_MAX_LENGTH"
+        />
+      </el-form-item>
+
+      <el-form-item
+        v-for="(item, index) in formItems"
+        :label="item.label"
+        :prop="item.prop"
+        :key="index"
+      >
+        <template v-if="item.prop === 'invoiceType'">
+          <el-select
+            v-model="formData[item.prop]"
+            placeholder="请选择发票类型"
+            w-750px
+          >
+            <el-option
+              v-for="(type, index) in inv_type_list"
+              :label="type.label"
+              :value="type.hwy_value"
+              :key="index"
+            />
+          </el-select>
+        </template>
+
+        <template v-else-if="item.prop === 'inv_img'">
+          <!-- list-type="picture" -->
+          <div w-full>
+            <el-upload
+              class="upload-demo"
+              v-model:file-list="fileList"
+              :before-upload="onBeforeReturnImageUpload"
+              :before-remove="handleRemove"
+              :http-request="handleRequeset"
+              multiple
+            >
+              <el-button type="primary">上传图片</el-button>
+            </el-upload>
+          </div>
+        </template>
+
+        <template v-else-if="item.prop === 'open_time'">
+          <el-date-picker
+            v-model="formData[item.prop]"
+            :disabled-date="disabledDate"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="请输入开票日期"
+          />
+        </template>
+
+        <template v-else>
+          <el-input v-model="formData[item.prop]" v-bind="INPUT_MAX_LENGTH" />
+        </template>
+      </el-form-item>
+
+      <el-form-item class="justify-end flex w-full">
+        <el-button type="primary" :loading="loading" @click="handleSave"
+          >保存</el-button
+        >
+      </el-form-item>
+    </el-form>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.upload {
+  width: 178px;
+  height: 178px;
+  display: block;
+  border: 1px dashed var(--el-border-color);
+
+  .icon {
+    font-size: 24px;
+    color: #8c939d;
+    width: 178px;
+    height: 178px;
+    text-align: center;
+  }
+
+  .text {
+    font-size: 20px;
+    line-height: 178px;
+    width: 178px;
+    text-align: center;
+  }
+
+  &:hover {
+    border-color: #409eff;
+  }
+}
+</style>

+ 21 - 0
src/views/purchase/buyInvoiceQuery/components/invoice-modal.vue

@@ -0,0 +1,21 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import Invoice from "/@/components/Invoice";
+
+const visible = ref(false);
+
+const invoiceNumber = ref("");
+
+defineExpose({
+  onDisplay: ({ invoiceNumber: _invoiceNumber }) => {
+    visible.value = true;
+    invoiceNumber.value = _invoiceNumber;
+  }
+});
+</script>
+
+<template>
+  <el-dialog v-model="visible" width="1040px">
+    <Invoice :inv-number="invoiceNumber" type="purchase" />
+  </el-dialog>
+</template>

+ 67 - 0
src/views/purchase/buyInvoiceQuery/components/invoice-table.vue

@@ -0,0 +1,67 @@
+<script setup lang="ts">
+import { ref, unref } from "vue";
+import { invTypeOptions } from "../config/_opitons";
+import InvoceForm from "./invoice-form.vue";
+
+const emit = defineEmits(["change"]);
+
+const invArr = ref<string[]>([]);
+const invoceFormRef = ref<InstanceType<typeof InvoceForm>>(null);
+
+function handleChange(data) {
+  invArr.value = [...invArr.value, ...(Array.isArray(data) ? data : [data])];
+  emit("change", unref(invArr));
+}
+
+function handleDelete({ $index }) {
+  invArr.value.splice($index, 1);
+  emit("change", unref(invArr));
+}
+</script>
+
+<template>
+  <div flex w-full justify-end mb-5>
+    <el-button type="primary" link @click="() => invoceFormRef.onDisplay()"
+      >添加</el-button
+    >
+  </div>
+  <el-table size="small" :data="invArr" border>
+    <el-table-column type="index" width="50" />
+
+    <el-table-column label="发票上传方式" prop="invType" show-overflow-tooltip>
+      <template #="{ row }">
+        <el-tag>{{
+          invTypeOptions.find(t => t.value === row.invType)?.label
+        }}</el-tag>
+      </template>
+    </el-table-column>
+
+    <el-table-column label="发票号码" prop="invNumber" show-overflow-tooltip />
+    <el-table-column label="发票代码" prop="invCode" show-overflow-tooltip />
+    <el-table-column label="校验码" prop="checkNumber" show-overflow-tooltip />
+    <el-table-column label="发票名称" prop="invName" show-overflow-tooltip />
+    <el-table-column label="发票图片" prop="inv_img" show-overflow-tooltip>
+      <template #="{ row }">
+        <el-image
+          :src="row.inv_img"
+          :preview-src-list="[row.inv_img]"
+          style="height: 30px"
+          preview-teleported
+        />
+      </template>
+    </el-table-column>
+    <el-table-column label="税前金额" prop="subtotal_amount" />
+
+    <el-table-column label="开票日期" prop="open_time" show-overflow-tooltip />
+
+    <el-table-column label="操作" width="60px">
+      <template #="scope">
+        <el-button link type="primary" @click="() => handleDelete(scope)"
+          >删除</el-button
+        >
+      </template>
+    </el-table-column>
+  </el-table>
+
+  <InvoceForm ref="invoceFormRef" @change="handleChange" />
+</template>

+ 65 - 0
src/views/purchase/buyInvoiceQuery/components/return-form.vue

@@ -0,0 +1,65 @@
+<script setup lang="ts">
+import { ref, unref } from "vue";
+import ReconciliationForm from "/@/components/ReconciliationForm";
+import { ElForm } from "element-plus";
+import ChooseModal from "./choose-modal.vue";
+import InvoiceTable from "./invoice-table.vue";
+import { createRules } from "../config/_rules";
+
+const emit = defineEmits(["create"]);
+
+const formRef = ref<InstanceType<typeof ElForm> | null>(null);
+const chooseModalRef = ref<InstanceType<typeof ChooseModal> | null>(null);
+const reconciliationRef = ref<InstanceType<typeof ReconciliationForm> | null>(
+  null
+);
+
+const formData = ref<Record<string, any>>({
+  payNo: "",
+  invArr: []
+});
+
+const handleChoose = () => chooseModalRef.value.onDisplay();
+
+function handleCreate() {
+  formRef.value.validate(isValid => isValid && emit("create", unref(formData)));
+}
+
+const handleChange = invArr => (formData.value.invArr = invArr);
+</script>
+
+<template>
+  <div w-full>
+    <ElForm
+      ref="formRef"
+      label-width="100px"
+      :model="formData"
+      :rules="createRules"
+    >
+      <ElFormItem label="订单对账" prop="payNo">
+        <ReconciliationForm
+          ref="reconciliationRef"
+          isPurchPay
+          :id="formData.payNo"
+          :readonly="!!formData.payNo"
+          @choose="handleChoose"
+        />
+      </ElFormItem>
+
+      <ElFormItem label="发票" prop="invArr">
+        <InvoiceTable @change="handleChange" />
+      </ElFormItem>
+
+      <ElFormItem>
+        <div class="flex w-full justify-end">
+          <ElButton type="primary" @click="handleCreate">保存</ElButton>
+        </div>
+      </ElFormItem>
+    </ElForm>
+
+    <ChooseModal
+      ref="chooseModalRef"
+      @choose="payNo => (formData.payNo = payNo)"
+    />
+  </div>
+</template>

+ 351 - 0
src/views/purchase/buyInvoiceQuery/config/_details.ts

@@ -0,0 +1,351 @@
+/**
+ * 采购回票详情
+ */
+
+import { ElImage, ElTag } from "element-plus";
+import { h } from "vue";
+import { createTooltip } from "/@/utils/tootip";
+import { DescriptionColumns } from "/@/components/BasicDescriptions";
+import { invTypeOptions, statusOptions } from "./_opitons";
+import {
+  invoiceTypeList,
+  seller_check_type,
+  buyer_check_type
+} from "/@/utils/status";
+
+export const cgInvTypeOptions = [
+  {
+    value: "special",
+    label: "增值税专用发票"
+  },
+  {
+    value: "normal",
+    label: "增值税普通发票"
+  },
+  {
+    value: "roll",
+    label: "增值税普通发票(卷式)"
+  },
+  {
+    value: "special_electronic",
+    label: "增值税电子专用发票"
+  },
+  {
+    value: "electronic",
+    label: "增值税电子普通发票"
+  },
+  {
+    value: "toll",
+    label: "增值税电子普通发票(通行费)"
+  }
+];
+
+export const ticketReturnColumns: DescriptionColumns = [
+  {
+    field: "hpNo",
+    label: "对账回票编号"
+  },
+  {
+    field: "status",
+    label: "状态",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => statusOptions.find(s => s.value === String(data))?.label
+      });
+    }
+  },
+  {
+    field: "apply_name",
+    label: "申请人"
+  },
+  {
+    field: "addtime",
+    label: "创建时间"
+  },
+
+  {
+    field: "inv_fee",
+    label: "回票金额"
+  },
+
+  {
+    field: "invType",
+    label: "发票上传方式",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => invTypeOptions.find(s => s.value === String(data))?.label
+      });
+    }
+  },
+  {
+    field: "invoiceType",
+    label: "发票类型",
+    render(data) {
+      return h(ElTag, null, {
+        default: () =>
+          invoiceTypeList.find(s => s.value === String(data))?.label || "--"
+      });
+    }
+  },
+
+  {
+    field: "open_time",
+    label: "发票日期"
+  },
+  {
+    field: "invoiceNumber",
+    label: "发票号码"
+  },
+  {
+    field: "invoiceCode",
+    label: "发票代码"
+  },
+  {
+    field: "inv_subtotal_amount",
+    label: "税前总金额"
+  },
+  {
+    field: "inv_amount",
+    label: "税后总金额"
+  },
+  {
+    field: "checkNumber",
+    span: 8,
+    label: "校验码"
+  },
+  {
+    field: "inv_img",
+    label: "发票图片",
+    span: 4,
+    render(src) {
+      return h(ElImage, {
+        src,
+        previewSrcList: [src],
+        style: {
+          height: "20px"
+        }
+      });
+    }
+  },
+  {
+    field: "remark",
+    span: 12,
+    label: "审核备注"
+  },
+  {
+    label: "购买方抬头状态",
+    field: "seller_check",
+    render(data) {
+      return h(
+        ElTag,
+        {
+          type: seller_check_type.find(s => s.value === String(data))
+            ?.type as any
+        },
+        {
+          default: () =>
+            seller_check_type.find(s => s.value === String(data))?.label || "--"
+        }
+      );
+    }
+  },
+  {
+    label: "销售方抬头状态",
+    field: "buyer_check",
+    render(data) {
+      return h(
+        ElTag,
+        {
+          type: buyer_check_type.find(s => s.value === String(data))
+            ?.type as any
+        },
+        {
+          default: () =>
+            buyer_check_type.find(s => s.value === String(data))?.label || "--"
+        }
+      );
+    }
+  }
+];
+
+export const TICKET_INV_COLUMNS = [
+  {
+    field: "invoiceType",
+    span: 1,
+    label: "发票类型"
+  },
+  {
+    field: "inv_img",
+    span: 1,
+    label: "发票图片",
+    render(src) {
+      return h(ElImage, {
+        src,
+        previewSrcList: [src],
+        style: {
+          height: "20px"
+        }
+      });
+    }
+  },
+  {
+    field: "inv_fee",
+    span: 2,
+    label: "发票税前金额"
+  },
+  {
+    field: "open_time",
+    label: "开票时间"
+  },
+  {
+    field: "invoiceNumber",
+    label: "发票号码"
+  },
+  {
+    field: "invoiceCode",
+    label: "发票代码"
+  },
+  {
+    field: "checkNumber",
+    label: "校验码"
+  }
+];
+
+export const TICKET_COLUMNS = [
+  {
+    field: "payNo",
+    span: 2,
+    label: "对账编号"
+  },
+  {
+    field: "hpNo",
+    span: 2,
+    label: "回票申请编号"
+  },
+  {
+    field: "apply_name",
+    label: "申请人",
+    span: 2,
+    render: (apply_name, { apply_id }) =>
+      createTooltip(apply_name, "申请人ID : " + apply_id, 160)
+  },
+  {
+    field: "invType",
+    label: "发票申请类型",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => invTypeOptions.find(s => s.value === data)?.label
+      });
+    }
+  },
+  {
+    field: "status",
+    label: "状态",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => statusOptions.find(s => s.value === data)?.label
+      });
+    }
+  },
+  {
+    field: "total_fee",
+    label: "回票总额"
+  }
+];
+
+export const columns = [
+  {
+    label: "对账编号",
+    prop: "payNo",
+    minWidth: 160,
+    align: "left"
+  },
+  {
+    label: "供应商编号",
+    prop: "supplierNo",
+    minWidth: 150,
+    align: "left"
+  },
+  {
+    label: "供应商名称",
+    prop: "supplierName",
+    minWidth: 180,
+    align: "left"
+  },
+  {
+    label: "业务公司编号",
+    prop: "companyNo",
+    minWidth: 150
+  },
+  {
+    label: "业务公司名称",
+    prop: "companyName",
+    minWidth: 180
+  },
+  {
+    label: "付款状态",
+    prop: "pay_status",
+    minWidth: 100
+  },
+  {
+    label: "总额款",
+    prop: "total_fee",
+    minWidth: 110,
+    align: "total_fee"
+  },
+  {
+    label: "付款标签金额",
+    prop: "pay_tag_fee",
+    minWidth: 110,
+    align: "left"
+  },
+  {
+    label: "已付款金额",
+    prop: "apay_fee",
+    minWidth: 110,
+    align: "left"
+  },
+  {
+    label: "申请人",
+    prop: "apply_name",
+    minWidth: 90,
+    align: "total_fee"
+  }
+];
+
+export const ticketFormItems = {
+  //手工开票
+  "1": [
+    {
+      label: "发票类型",
+      prop: "invoiceType"
+    },
+    {
+      label: "发票代码",
+      prop: "invCode"
+    },
+    {
+      label: "发票号码",
+      prop: "invNumber"
+    },
+    {
+      label: "税前金额",
+      prop: "subtotal_amount"
+    },
+    {
+      label: "校验码",
+      prop: "checkNumber"
+    },
+    {
+      label: "开票日期",
+      prop: "open_time"
+    }
+  ],
+  //ocr识别
+  "2": [
+    {
+      label: "发票图片",
+      prop: "inv_img"
+    }
+  ]
+};

+ 76 - 0
src/views/purchase/buyInvoiceQuery/config/_opitons.ts

@@ -0,0 +1,76 @@
+export const statusOptions = [
+  {
+    label: "图片识别中",
+    value: "0"
+  },
+  {
+    label: "待系统验证",
+    value: "1"
+  },
+  {
+    label: "买方公司财务审核",
+    value: "2"
+  },
+  {
+    label: "买方公司认证",
+    value: "3"
+  },
+  {
+    label: "认证成功",
+    value: "4"
+  },
+  {
+    label: "验证失败",
+    value: "5"
+  },
+  {
+    label: "买方审核驳回",
+    value: "6"
+  },
+  {
+    label: "认证失败",
+    value: "7"
+  },
+  {
+    label: "回票流程终止",
+    value: "8"
+  },
+  {
+    label: "验证超过次数",
+    value: "9"
+  },
+  {
+    label: "回票已退",
+    value: "10"
+  }
+];
+
+export const invTypeOptions = [
+  {
+    value: "1",
+    label: "手工添加"
+  },
+  {
+    value: "2",
+    label: "ocr识别"
+  },
+  {
+    value: "3",
+    label: "金税识别"
+  }
+];
+
+export const checkTypeOptions = [
+  {
+    value: "0",
+    label: "未分配查询"
+  },
+  {
+    value: "1",
+    label: "金税接口"
+  },
+  {
+    value: "2",
+    label: "华为云"
+  }
+];

+ 82 - 0
src/views/purchase/buyInvoiceQuery/config/_rules.ts

@@ -0,0 +1,82 @@
+import { FormRules } from "element-plus";
+
+export const createRules: FormRules = {
+  payNo: {
+    trigger: "change",
+    required: true,
+    message: "请选择订单"
+  },
+  invArr: {
+    required: true,
+    message: "请上传发票",
+    validator(_, value: Array<any>) {
+      return value.length !== 0;
+    }
+  }
+};
+
+export const invoceRules: FormRules = {
+  payNo: [
+    {
+      trigger: "blur",
+      message: "请选择订单",
+      required: true
+    }
+  ],
+  invoiceType: [
+    {
+      trigger: "blur",
+      message: "请选择发票类型",
+      required: true
+    }
+  ],
+  invType: [
+    {
+      trigger: "blur",
+      message: "请选择申请方式",
+      required: true
+    }
+  ],
+  invNumber: [
+    {
+      trigger: "blur",
+      message: "请输入发票代码",
+      required: true
+    }
+  ],
+  invCode: [
+    {
+      trigger: "blur",
+      message: "请选择发票编码",
+      required: true
+    }
+  ],
+  open_time: [
+    {
+      trigger: "blur",
+      message: "请选择开票时间",
+      required: true
+    }
+  ],
+  subtotal_amount: [
+    {
+      trigger: "blur",
+      message: "请输入金额",
+      required: true
+    }
+  ],
+  invName: [
+    {
+      trigger: "blur",
+      message: "请输入发票名称",
+      required: true
+    }
+  ],
+  gold: [
+    {
+      trigger: "blur",
+      message: "请选择金额",
+      required: true
+    }
+  ]
+};

+ 198 - 0
src/views/purchase/buyInvoiceQuery/config/content.config.ts

@@ -0,0 +1,198 @@
+import { ContentConfig } from "/@/components/PageContent";
+import { httpList, httpDelete } from "/@/api/purchase/ticketReturn";
+
+import dayjs from "dayjs";
+import { h } from "vue";
+import { ElImage, ElTag } from "element-plus";
+import { checkTypeOptions, invTypeOptions, statusOptions } from "./_opitons";
+import {
+  invoiceTypeList,
+  seller_check_type,
+  buyer_check_type
+} from "/@/utils/status";
+
+const columns = [
+  {
+    type: "selection",
+    width: 55,
+    hide: ({ checkList }) => !checkList.includes("勾选列")
+  },
+  {
+    label: "序号",
+    type: "index",
+    width: 70,
+    hide: ({ checkList }) => !checkList.includes("序号列")
+  },
+  {
+    label: "对账回票申请",
+    prop: "hpNo",
+    width: 180
+  },
+  {
+    label: "对账编号",
+    prop: "payNo",
+    width: 180
+  },
+  {
+    label: "状态",
+    prop: "status",
+    width: 160,
+    transform: status =>
+      statusOptions.find(s => String(status) === String(s.value))?.label,
+    cellRenderer: ({ row, props }) =>
+      h(
+        ElTag,
+        {
+          size: props.size
+        },
+        {
+          default: () =>
+            statusOptions.find(s => String(row.status) === String(s.value))
+              ?.label
+        }
+      )
+  },
+  {
+    label: "发票上传方式",
+    prop: "invType",
+    width: 120,
+    cellRenderer: ({ row, props }) =>
+      h(
+        ElTag,
+        {
+          size: props.size
+        },
+        {
+          default: () =>
+            invTypeOptions.find(s => String(row.invType) === String(s.value))
+              ?.label
+        }
+      )
+  },
+  {
+    label: "发票查验方式",
+    width: 120,
+    cellRenderer: ({ row, props }) =>
+      h(
+        ElTag,
+        {
+          size: props.size
+        },
+        {
+          default: () =>
+            checkTypeOptions.find(s => String(row.checkApi) === s.value)?.label
+        }
+      )
+  },
+  {
+    label: "发票图片",
+    prop: "inv_img",
+    width: 85,
+    cellRenderer: ({ row }) =>
+      h(ElImage, {
+        src: row.inv_img,
+        style: { height: "20px" },
+        previewSrcList: [row.inv_img],
+        previewTeleported: true
+      })
+  },
+  {
+    label: "发票税前金额",
+    width: "110px",
+    prop: "inv_subtotal_amount"
+  },
+  {
+    label: "发票税后金额",
+    width: "110px",
+    prop: "total"
+  },
+  {
+    label: "购买方抬头状态",
+    prop: "seller_check",
+    width: 130,
+    cellRenderer: ({ row }) => {
+      return h(ElTag, null, {
+        default: () =>
+          seller_check_type.find(item => item.value == row.seller_check + "")
+            ?.label || "--"
+      });
+    }
+  },
+  {
+    label: "销售方抬头状态",
+    prop: "buyer_check",
+    width: 130,
+    cellRenderer: ({ row }) => {
+      return h(ElTag, null, {
+        default: () =>
+          buyer_check_type.find(item => item.value == row.buyer_check + "")
+            ?.label || "--"
+      });
+    }
+  },
+  {
+    label: "发票号码",
+    width: 110,
+    prop: "invoiceNumber"
+  },
+  {
+    label: "发票代码",
+    width: 110,
+    prop: "invoiceCode"
+  },
+  {
+    label: "开票日期",
+    prop: "open_time",
+    width: 120,
+    formatter: ({ open_time }) =>
+      open_time ? dayjs(open_time).format("YYYY-MM-DD") : ""
+  },
+  {
+    label: "发票状态",
+    width: 110,
+    prop: "invStatus_cn"
+  },
+  {
+    label: "发票类型",
+    width: 110,
+    cellRenderer({ row }) {
+      return h(ElTag, null, {
+        default: () =>
+          invoiceTypeList.find(({ value }) => value === row.invoiceType)
+            ?.label || "--"
+      });
+    }
+  },
+  {
+    label: "申请人",
+    prop: "apply_name"
+  },
+  {
+    label: "创建时间",
+    prop: "addtime",
+    width: 180,
+    formatter: ({ addtime }) => dayjs(addtime).format("YYYY-MM-DD HH:mm:ss")
+  },
+  {
+    label: "操作",
+    fixed: "right",
+    slot: "operation",
+    width: 110
+  }
+];
+
+const contentConfig: ContentConfig = {
+  title: "采购回票",
+  statusProp: "companyNo",
+  columns,
+  apis: {
+    httpDelete,
+    httpList
+  },
+  deleteProp: "hpNo",
+  delTooltip: "是否撤回采购回票申请",
+  superUserNoAction: true,
+  showDelete: ({ status }) => Number(status) <= 3
+};
+
+export default contentConfig;

+ 95 - 0
src/views/purchase/buyInvoiceQuery/config/modal.config.ts

@@ -0,0 +1,95 @@
+import { ModalConfig } from "/@/components/PageModal/src/types";
+
+const modalConfig: ModalConfig = {
+  title: "采票回票",
+  labelWidth: "100px",
+  colLayout: { span: 24 },
+  formItems: [
+    {
+      field: "payNo",
+      type: "input",
+      label: "对账编号",
+      placeholder: "对账编号"
+    },
+    {
+      field: "hpNo",
+      type: "input",
+      label: "对账回票申请",
+      placeholder: "审核状态"
+    },
+    {
+      field: "apply_name",
+      type: "input",
+      label: "申请人名称",
+      placeholder: "申请人名称"
+    },
+    {
+      field: "invType",
+      type: "select",
+      label: "发票类型",
+      placeholder: "发票类型"
+    },
+    {
+      field: "status",
+      type: "select",
+      label: "审核状态",
+      placeholder: "审核状态"
+    },
+    {
+      field: "status",
+      type: "select",
+      label: "进项设置",
+      placeholder: "进项设置",
+      options: [
+        {
+          label: "人工",
+          value: "1"
+        },
+        {
+          label: "金税",
+          value: "2"
+        }
+      ]
+    },
+    {
+      field: "input_ticket",
+      type: "select",
+      label: "进项设置",
+      placeholder: "进项设置",
+      options: [
+        {
+          label: "人工",
+          value: "1"
+        },
+        {
+          label: "金税",
+          value: "2"
+        }
+      ]
+    },
+    {
+      field: "out_ticket",
+      type: "select",
+      label: "销项设置",
+      placeholder: "销项设置",
+      options: [
+        {
+          label: "人工",
+          value: "1"
+        },
+        {
+          label: "金税",
+          value: "2"
+        }
+      ]
+    },
+    {
+      field: "open_time",
+      type: "input",
+      label: "开票时间",
+      placeholder: "开票时间"
+    }
+  ]
+};
+
+export default modalConfig;

+ 65 - 0
src/views/purchase/buyInvoiceQuery/config/search.config.ts

@@ -0,0 +1,65 @@
+import { FormConfig } from "/@/components/PageSearch";
+import { checkTypeOptions, invTypeOptions, statusOptions } from "./_opitons";
+
+const searchFormConfig: FormConfig = {
+  formItems: [
+    {
+      field: "status",
+      type: "select",
+      placeholder: "审核状态",
+      options: statusOptions
+    },
+    {
+      field: "invType",
+      type: "select",
+      placeholder: "发票上传方式",
+      options: invTypeOptions
+    },
+    {
+      field: "checkApi",
+      type: "select",
+      placeholder: "发票查验方式",
+      options: checkTypeOptions
+    },
+    {
+      field: "payNo",
+      type: "input",
+      placeholder: "对账编号"
+    },
+    {
+      field: "hpNo",
+      type: "input",
+      placeholder: "对账申请编号"
+    },
+    {
+      field: "apply_name",
+      type: "input",
+      placeholder: "申请人"
+    },
+    {
+      field: "create_timer",
+      type: "date_picker",
+      otherOptions: {
+        type: "daterange",
+        startProp: "start",
+        endProp: "end",
+        startPlaceholder: "新建起始时间",
+        endPlaceholder: "新建结束时间"
+      }
+    }
+    // ,
+    // {
+    //   field: "open_timer",
+    //   type: "date_picker",
+    //   otherOptions: {
+    //     type: "daterange",
+    //     startProp: "open_start",
+    //     endProp: "open_end",
+    //     startPlaceholder: "开票起始日期",
+    //     endPlaceholder: "开票结束日期"
+    //   }
+    // }
+  ]
+};
+
+export default searchFormConfig;

+ 7 - 0
src/views/purchase/buyInvoiceQuery/config/xls-template.ts

@@ -0,0 +1,7 @@
+export const template = {
+  买方公司编码: "",
+  回票申请编码: "",
+  对账编号: "",
+  审核状态: "",
+  审核备注: ""
+};

+ 125 - 0
src/views/purchase/buyInvoiceQuery/index.vue

@@ -0,0 +1,125 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import contentConfig from "./config/content.config";
+import searchConfig from "./config/search.config";
+import { usePageSearch, type PageHooks, type PageEvents } from "/@/hooks/page";
+import InvoiceModal from "./components/invoice-modal.vue";
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
+import { useRouter } from "vue-router";
+// import { useUserInfo } from "/@/hooks/core/useUser";
+import { httpRequsetExport } from "/@/utils/export";
+import { useCompany } from "/@/hooks/core/useCompany";
+import { PageContent } from "/@/components/PageContent";
+import { utils, writeFile } from "xlsx";
+import { template } from "./config/xls-template";
+import ExeclUpoad from "./components/execl-files-upload/index.vue";
+import { usePermission } from "/@/hooks/core/usePermission";
+import { useUserInfo } from "/@/hooks/core/useUser";
+
+const PageName = "buyInvoiceQuery";
+const baseUrl = "/purchase/ticketReturnDetail";
+const invStatus = ["4", "10"];
+
+// { code: "017", name: "导出回票数据" },
+// { code: "018", name: "批量导入认证结果" },
+// { code: "019", name: "下载回票批量申请模板" },
+const { hasPermissionWithCode } = usePermission(PageName);
+const { isSuperUser } = useUserInfo();
+const pageContentRef = ref<InstanceType<typeof PageContent> | null>(null);
+
+const { push } = useRouter();
+const { currentCompany } = useCompany();
+const loading = ref(false);
+const invoiceModalRef = ref<InstanceType<typeof InvoiceModal>>(null);
+const execelUploadRef = ref<InstanceType<typeof ExeclUpoad>>(null);
+
+async function handleDownload() {
+  await httpRequsetExport({
+    url: "ile",
+    name: "回票数据表",
+    onStart: () => (loading.value = true),
+    onSuccess: () => (loading.value = false),
+    onFail: () => (loading.value = false),
+    params: {
+      companyNo: currentCompany.value.companyNo,
+      ...(pageContentRef.value as any).getBasicParams()
+    }
+  });
+}
+
+const hooks: PageHooks = {
+  pageSearchHook: () => usePageSearch(undefined, undefined, searchConfig)
+};
+
+const events: PageEvents = {
+  content: {
+    preview: ({ hpNo }) => push(`${baseUrl}?id=${hpNo}`),
+    create: () => push(baseUrl)
+  }
+};
+
+//导出模板
+function onDownloadTemplate() {
+  //创建数据表
+  const workBook = utils.book_new();
+  const workSheet = utils.json_to_sheet([template]);
+  utils.book_append_sheet(workBook, workSheet, "sheet");
+
+  //导出模板
+  writeFile(workBook, "回票批量导入模板.xlsx", {
+    bookType: "xlsx"
+  });
+}
+</script>
+
+<template>
+  <PageAuth :pageName="PageName">
+    <PageContainer
+      :hooks="hooks"
+      :events="events"
+      :contentConfig="contentConfig"
+      :search-config="searchConfig"
+      :get-content-ref="(ref:any) => (pageContentRef = ref)"
+    >
+      <template #content_header>
+        <ElButton
+          size="small"
+          v-if="hasPermissionWithCode('017')"
+          :icon="useRenderIcon('arrow-down-line')"
+          :loading="loading"
+          @click="() => handleDownload()"
+          >导出回票数据</ElButton
+        >
+
+        <ElButton
+          size="small"
+          v-if="hasPermissionWithCode('018') && !isSuperUser"
+          :icon="useRenderIcon('arrow-up-line')"
+          @click="() => execelUploadRef.onDisplay()"
+          >批量导入认证结果</ElButton
+        >
+
+        <ElButton
+          size="small"
+          v-if="hasPermissionWithCode('019')"
+          :icon="useRenderIcon('arrow-down-line')"
+          @click="() => onDownloadTemplate()"
+          >下载回票批量申请模板</ElButton
+        >
+      </template>
+
+      <template #content_action="row">
+        <ElButton
+          v-if="invStatus.includes(String(row.status))"
+          link
+          type="primary"
+          :icon="useRenderIcon('scaletooriginal')"
+          @click="() => invoiceModalRef.onDisplay(row)"
+        />
+      </template>
+    </PageContainer>
+
+    <InvoiceModal ref="invoiceModalRef" />
+    <ExeclUpoad ref="execelUploadRef" />
+  </PageAuth>
+</template>

+ 0 - 0
src/views/purchase/buyInvoiceQuery/买入回票查询.md


+ 179 - 0
src/views/supply/sellInvoiceQuery/components/choose-modal.vue

@@ -0,0 +1,179 @@
+<script setup lang="ts">
+import { ref, unref, reactive } from "vue";
+import { pay_status } from "/@/utils/details/reconciliation";
+import { httpList } from "/@/api/purchase/orderRecord";
+import { useCompany } from "/@/hooks/core/useCompany";
+import { ElMessage, ElTable } from "element-plus";
+import { columns } from "../config/_details";
+import { useResponseHandle } from "/@/hooks";
+
+const emit = defineEmits(["choose"]);
+
+const visible = ref(false);
+const paymentList = ref<Array<Record<string, string>>>([]);
+const selects = ref<Array<Record<string, string>>>([]);
+
+const pagination = reactive({
+  total: 0,
+  size: 10,
+  page: 1,
+  background: true
+});
+
+const formData = ref<Record<string, string>>({
+  pay_status: "",
+  currentValue: ""
+});
+
+const tableRef = ref<InstanceType<typeof ElTable>>(null);
+const currentKey = ref("payNo");
+const loading = ref(false);
+const responseHandle = useResponseHandle();
+
+const { currentCompany } = useCompany();
+
+//初始化订单对账列表
+async function requestPaymentList() {
+  const { size, page } = pagination;
+  loading.value = true;
+
+  const { currentValue, ...otherParams } = formData.value;
+
+  const { code, message, data } = await httpList({
+    pay_status: "0",
+    status: "3",
+    [currentKey.value]: currentValue,
+    companyNo: currentCompany.value.companyNo,
+    ...otherParams,
+    page,
+    size
+  });
+
+  responseHandle({
+    code,
+    message,
+    handler: () => {
+      pagination.total = data.count;
+      paymentList.value = data.list;
+    }
+  });
+  loading.value = false;
+}
+
+function handleConfirm() {
+  const [order] = selects.value;
+  if (!order) return ElMessage.warning("请选择一个对账单");
+  emit("choose", unref(order.payNo));
+  visible.value = false;
+}
+
+function handleSelectionChange(values) {
+  if (values.length > 1) {
+    const value = values.pop();
+    tableRef.value.clearSelection();
+    tableRef.value.toggleRowSelection(value, true);
+    selects.value = [value];
+  } else {
+    selects.value = [values[0]];
+  }
+}
+
+function handleSearch(_isReset?: boolean) {
+  if (_isReset) formData.value = {};
+  pagination.page = 1;
+  requestPaymentList();
+}
+
+defineExpose({
+  onDisplay: () => (visible.value = true)
+});
+</script>
+
+<template>
+  <el-dialog
+    v-model="visible"
+    title="订单对账"
+    center
+    width="1040px"
+    @open="() => requestPaymentList()"
+  >
+    <div flex justify-between mb-2>
+      <div flex gap-2>
+        <el-input
+          clearable
+          v-model="formData.currentValue"
+          placeholder="对账编号"
+        >
+          <template #prepend>
+            <el-select v-model="currentKey" style="width: 130px">
+              <el-option label="对账编号" value="payNo" />
+              <el-option label="卖方公司编号" value="supplierNo" />
+              <el-option label="卖方公司名称" value="supplierName" />
+            </el-select>
+          </template>
+        </el-input>
+        <el-select
+          clearable
+          w-80
+          v-model="formData.pay_status"
+          placeholder="付款状态"
+        >
+          <el-option value="1" label="未付款" />
+          <el-option value="2" label="部分付款" />
+        </el-select>
+      </div>
+
+      <div>
+        <el-button type="primary" @click="() => handleSearch()">搜索</el-button>
+        <el-button @click="() => handleSearch(true)">重置</el-button>
+      </div>
+    </div>
+    <el-table
+      border
+      ref="tableRef"
+      :data="paymentList"
+      size="small"
+      row-key="id"
+      @selection-change="handleSelectionChange"
+      mb-2
+      v-loading="loading"
+    >
+      <el-table-column type="selection" width="55" />
+      <el-table-column
+        v-for="(col, index) in columns"
+        :key="index"
+        v-bind="col"
+        show-overflow-tooltip
+      >
+        <template #="{ row }">
+          <el-tag v-if="col.prop === 'pay_status'">{{
+            pay_status.find(p => p.value === String(row.pay_status))?.label
+          }}</el-tag>
+
+          <span v-else>{{ row[col.prop] }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <div flex>
+      <el-pagination
+        v-model:current-page="pagination.page"
+        :page-size="pagination.size"
+        :total="pagination.total"
+        @current-change="requestPaymentList"
+      />
+
+      <div w-full flex justify-end mt-2>
+        <el-button type="primary" @click="handleConfirm">保存</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-table__header) {
+  .el-checkbox {
+    display: none;
+  }
+}
+</style>

+ 40 - 0
src/views/supply/sellInvoiceQuery/components/execl-files-upload/columns-config.ts

@@ -0,0 +1,40 @@
+const initheaders = [
+  "买方公司编码",
+  "回票申请编码",
+  "对账编号",
+  "审核状态",
+  "审核备注"
+];
+
+export const mapProp = {
+  value0: "companyNo",
+  value1: "hpNo",
+  value2: "payNo",
+  value3: "status",
+  value4: "remark"
+};
+
+export const requireHeaders = ["payNo", "companyNo", "hpNo", "status"];
+
+const columns = () => {
+  const list: any[] = [
+    {
+      type: "index",
+      width: "50",
+      fixed: "left",
+      label: "序号"
+    }
+  ];
+  initheaders.forEach((si, sii) => {
+    list.push({
+      prop: "value" + sii,
+      label: si,
+      minWidth: sii === 0 || sii === 1 ? "120px" : "90px"
+    });
+  });
+
+  console.log(list);
+  return list;
+};
+
+export { initheaders, columns };

+ 233 - 0
src/views/supply/sellInvoiceQuery/components/execl-files-upload/index.vue

@@ -0,0 +1,233 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import { ElMessage } from "element-plus";
+import { execlUpload } from "/@/components/execlUpload";
+import { httpBatchimport } from "/@/api/purchase/ticketReturn";
+import { useResponseHandle } from "/@/hooks";
+import { useCompany } from "/@/hooks/core/useCompany";
+
+import {
+  initheaders,
+  columns,
+  mapProp,
+  requireHeaders
+} from "./columns-config";
+
+const visible = ref(false);
+const loading = ref(false);
+const tableData = ref([]);
+const columnsConfig = columns();
+const emit = defineEmits(["onSuccess"]);
+
+const { currentCompany } = useCompany();
+
+const responseHandle = useResponseHandle();
+
+const Uploadsuccess = ({ results, header }) => {
+  loading.value = true;
+
+  if (results.length === 0) {
+    ElMessage.error("表格无有效数据!");
+    loading.value = false;
+    return;
+  }
+
+  let headok = true;
+  if (header.length !== initheaders.length) {
+    headok = false;
+  } else {
+    initheaders.forEach((si, sii) => {
+      if (si !== header[sii]) {
+        headok = false;
+      }
+    });
+  }
+
+  if (!headok) {
+    ElMessage.error("表头与导入模板不匹配!");
+    loading.value = false;
+    return;
+  }
+  tableData.value = [];
+
+  try {
+    for (const v1 of results) {
+      const b = Object.values(v1);
+      let model = {};
+      b.forEach((si, sii) => {
+        model["value" + sii] = si + "";
+      });
+      tableData.value.push(model);
+    }
+    loading.value = false;
+  } catch (err) {
+    return err;
+  }
+};
+//提交
+const handleSubmit = async () => {
+  if (loading.value) return;
+  loading.value = true;
+
+  const data = [];
+  const errorStatus = [];
+  const errorHp = [];
+  const errorDz = [];
+
+  tableData.value.forEach((key, index) => {
+    const obj: Record<string, string> = {};
+
+    for (let i in key) {
+      const prop = mapProp[i];
+      const value = key[i];
+      if (prop === "status") {
+        const trimValue = value.trim();
+        if (trimValue !== "通过" && trimValue !== "驳回") {
+          errorStatus.push(index + 1);
+        } else {
+          obj[prop] = trimValue === "通过" ? "4" : "7";
+        }
+      } else if (prop === "hpNo") {
+        if (!value) errorHp.push(index + 1);
+        obj[prop] = value;
+      } else if (prop === "payNo") {
+        if (!value) errorDz.push(index + 1);
+        obj[prop] = value;
+      } else {
+        obj[prop] = value;
+      }
+    }
+    data.push(obj);
+  });
+
+  const buyers = data.map(({ companyNo }) => companyNo);
+  const setBuyers = [...new Set(buyers)];
+
+  if (errorStatus.length > 0) {
+    ElMessage.error(
+      `第 ${errorStatus.join(",")} 行审核状态格式不正确,应该为'通过',或'驳回'`
+    );
+    loading.value = false;
+    return;
+  }
+
+  if (errorHp.length > 0) {
+    ElMessage.error(
+      `第 ${errorHp.join(",")} 行格式不正确,回票申请编号为必填项`
+    );
+    loading.value = false;
+    return;
+  }
+
+  if (errorDz.length > 0) {
+    ElMessage.error(`第 ${errorDz.join(",")} 行格式不正确,对账编号为必填项`);
+    loading.value = false;
+    return;
+  }
+
+  if (setBuyers.length > 1) {
+    ElMessage.error("买方公司编码不一致");
+    loading.value = false;
+    return;
+  }
+
+  if (setBuyers[0] !== currentCompany.value.companyNo) {
+    ElMessage.error("买方公司编码与当前选择的公司不一致");
+    loading.value = false;
+    return;
+  }
+
+  data.forEach(item => {
+    delete item["payNo"];
+    delete item["companyNo"];
+  });
+
+  const { code, message } = await httpBatchimport({
+    list: data
+  });
+
+  loading.value = false;
+
+  responseHandle({
+    code,
+    message,
+    handler: () => {
+      ElMessage.success("数据导入成功!");
+      emit("onSuccess");
+      visible.value = false;
+    }
+  });
+};
+const cancel = () => {
+  tableData.value = [];
+};
+defineExpose({
+  onDisplay: () => ((visible.value = true), (tableData.value = []))
+});
+</script>
+
+<template>
+  <el-dialog
+    v-model="visible"
+    title="批量导入认证结果"
+    width="1040px"
+    top="8vh"
+    center
+  >
+    <execlUpload @on-success="Uploadsuccess" v-if="tableData.length === 0" />
+
+    <el-table
+      :data="tableData"
+      stripe
+      border
+      max-height="500px"
+      size="small"
+      style="width: 100%"
+    >
+      <el-table-column
+        v-for="(si, sii) in columnsConfig"
+        :type="si.type"
+        :minWidth="si.minWidth"
+        :fixed="si.fixed"
+        :key="sii"
+        :prop="si.prop"
+        show-overflow-tooltip
+      >
+        <template #header>
+          <span
+            v-if="
+              !requireHeaders.includes(mapProp[si.prop]) || si.label === '序号'
+            "
+            >{{ si.label }}</span
+          >
+          <p v-else>
+            <span style="color: #f56c6c; font-size: 14px">* </span>
+            {{ si.label }}
+          </p>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div
+      flex
+      justify-end
+      gap-2
+      v-if="tableData.length !== 0"
+      style="padding: 10px 0 0 0"
+    >
+      <el-button size="small" @click="cancel">取消</el-button>
+      <el-button
+        size="small"
+        type="primary"
+        :loading="loading"
+        @click="handleSubmit"
+        >保存</el-button
+      >
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-upload-list__item) {
+  display: none !important;
+}
+</style>

+ 21 - 0
src/views/supply/sellInvoiceQuery/components/invoice-dialog.vue

@@ -0,0 +1,21 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import Invoice from "/@/components/Invoice";
+
+const visible = ref(false);
+
+const invoiceNumber = ref("");
+
+defineExpose({
+  onDisplay: ({ invoiceNumber: _invoiceNumber }) => {
+    visible.value = true;
+    invoiceNumber.value = _invoiceNumber;
+  }
+});
+</script>
+
+<template>
+  <el-dialog v-model="visible" width="1040px">
+    <Invoice :inv-number="invoiceNumber" type="purchase" />
+  </el-dialog>
+</template>

+ 279 - 0
src/views/supply/sellInvoiceQuery/components/invoice-form.vue

@@ -0,0 +1,279 @@
+<script setup lang="ts">
+import { computed, ref, watch } from "vue";
+import { dayjs, ElForm, ElInput, ElMessage, UploadProps } from "element-plus";
+import { ticketFormItems } from "../config/_details";
+import { invoceRules } from "../config/_rules";
+import { useResponseHandle } from "/@/hooks/core/useAsync";
+import { httpImageUpload, baseUrl } from "/@/api/other";
+import { useUserStore } from "/@/store/modules/user";
+import { inv_type_list } from "/@/utils/status";
+import { INPUT_MAX_LENGTH } from "/@/utils/global";
+
+const emit = defineEmits(["change"]);
+
+const visible = ref(false);
+const fileList = ref([]);
+
+//支持的图片格式
+const imgs = ref<any[]>([]);
+const invType = ref("1");
+const loading = ref(false);
+const types = ["png", "jpg", "bmp", "jpeg"].map(format => "image/" + format);
+const formItems = computed(() => ticketFormItems[invType.value] || []);
+const rules = ref({ ...invoceRules });
+const formRef = ref<InstanceType<typeof ElForm>>(null);
+const showScanInput = computed(() => invType.value === "1");
+const scanInputValue = ref("");
+
+const formData = ref<Record<string, string>>({
+  currentValue: ""
+});
+
+const userStore = useUserStore();
+const responseHandle = useResponseHandle();
+
+//图片上传前判断类型大小尺寸
+const onBeforeReturnImageUpload: UploadProps["beforeUpload"] = ({
+  type,
+  size
+}) => {
+  if (!types.includes(type)) {
+    ElMessage.error("请上传jpg.png.bmp.jpeg类型图片");
+    return false;
+  }
+
+  if (size / 1024 / 1024 > 1) {
+    ElMessage.error("图片大小超过1M");
+    return false;
+  }
+};
+
+function handleClose() {
+  imgs.value = [];
+  fileList.value = [];
+  formData.value = {};
+}
+
+//保存采购回票
+function handleSave() {
+  formRef.value.validate(async isValid => {
+    if (!isValid) return;
+    if (!invType.value) return ElMessage.error("请选择申请类型");
+
+    if (invType.value === "2") {
+      if (!imgs.value.length) return ElMessage.error("请选择上传的图片");
+      if (imgs.value.length > 100)
+        return ElMessage.error("上传图片不能超过100张");
+    }
+
+    emit(
+      "change",
+      invType.value === "2"
+        ? imgs.value
+        : {
+            ...formData.value,
+            invType: invType.value
+          }
+    );
+
+    visible.value = false;
+  });
+}
+
+function handleScanKeydown(evt: KeyboardEvent) {
+  const { keyCode } = evt;
+
+  console.log(keyCode);
+  if (keyCode !== 13) return;
+
+  //校验扫描枪值是否含有中文逗号
+  if (scanInputValue.value.indexOf(",") >= 0) {
+    ElMessage.error("不能包含中文逗号");
+    return (scanInputValue.value = "");
+  }
+
+  const [_, invoiceType, ...chunks] = scanInputValue.value.split(",");
+  const [invCode, invNumber, subtotal_amount, open_time, checkNumber] = chunks;
+
+  const year = open_time.slice(0, 4);
+  const month = open_time.slice(4, 6);
+  const day = open_time.slice(6);
+
+  formData.value = {
+    invoiceType,
+    invCode,
+    subtotal_amount,
+    open_time: dayjs(`${year}-${month}-${day}`).format("YYYY-MM-DD HH:mm:ss"),
+    invNumber,
+    checkNumber
+  };
+
+  scanInputValue.value = "";
+}
+
+const handleRequeset: UploadProps["httpRequest"] = async ({ file }) => {
+  const _formData = new FormData();
+  _formData.append("img", file);
+  _formData.append("token", userStore.token);
+  const { message, code, data } = await httpImageUpload(_formData);
+
+  responseHandle({
+    message,
+    code,
+    handler: () => {
+      data.forEach(({ url, name }) => {
+        imgs.value.push({
+          inv_img: baseUrl + "/" + url,
+          invName: name,
+          invType: "2"
+        });
+      });
+    }
+  });
+};
+
+const handleRemove = uploadFile => {
+  const delIndex = fileList.value.findIndex(
+    ({ uid }) => uid === uploadFile.uid
+  );
+  imgs.value.splice(delIndex, 1);
+  return true;
+};
+
+watch(
+  () => formItems.value,
+  newVal => {
+    const newFormData: Record<string, string> = {};
+    newFormData.payNo = formData.value.payNo;
+
+    newVal.forEach(item => {
+      if (item.prop !== "payNo") newFormData[item.prop] = "";
+    });
+
+    formData.value = newFormData;
+  }
+);
+
+const disabledDate = time => {
+  return time.getTime() > Date.now();
+};
+
+defineExpose({
+  onDisplay: () => (visible.value = true)
+});
+</script>
+
+<template>
+  <el-dialog v-model="visible" @close="handleClose">
+    <el-form
+      label-width="100px"
+      :model="formData"
+      :rules="rules"
+      ref="formRef"
+      size="small"
+    >
+      <el-form-item label="发票上传方式">
+        <el-select v-model="invType">
+          <el-option label="手工添加" value="1" />
+          <el-option label="ocr识别" value="2" />
+        </el-select>
+      </el-form-item>
+
+      <!-- 扫码枪识别 -->
+      <el-form-item label="扫码枪" v-if="showScanInput">
+        <el-input
+          type="textarea"
+          placeholder="请在扫码完成后手动回车"
+          @keydown="handleScanKeydown"
+          v-model="scanInputValue"
+          v-bind="INPUT_MAX_LENGTH"
+        />
+      </el-form-item>
+
+      <el-form-item
+        v-for="(item, index) in formItems"
+        :label="item.label"
+        :prop="item.prop"
+        :key="index"
+      >
+        <template v-if="item.prop === 'invoiceType'">
+          <el-select
+            v-model="formData[item.prop]"
+            placeholder="请选择发票类型"
+            w-750px
+          >
+            <el-option
+              v-for="(type, index) in inv_type_list"
+              :label="type.label"
+              :value="type.hwy_value"
+              :key="index"
+            />
+          </el-select>
+        </template>
+
+        <template v-else-if="item.prop === 'inv_img'">
+          <!-- list-type="picture" -->
+          <div w-full>
+            <el-upload
+              class="upload-demo"
+              v-model:file-list="fileList"
+              :before-upload="onBeforeReturnImageUpload"
+              :before-remove="handleRemove"
+              :http-request="handleRequeset"
+              multiple
+            >
+              <el-button type="primary">上传图片</el-button>
+            </el-upload>
+          </div>
+        </template>
+
+        <template v-else-if="item.prop === 'open_time'">
+          <el-date-picker
+            v-model="formData[item.prop]"
+            :disabled-date="disabledDate"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="请输入开票日期"
+          />
+        </template>
+
+        <template v-else>
+          <el-input v-model="formData[item.prop]" v-bind="INPUT_MAX_LENGTH" />
+        </template>
+      </el-form-item>
+
+      <el-form-item class="justify-end flex w-full">
+        <el-button type="primary" :loading="loading" @click="handleSave"
+          >保存</el-button
+        >
+      </el-form-item>
+    </el-form>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.upload {
+  width: 178px;
+  height: 178px;
+  display: block;
+  border: 1px dashed var(--el-border-color);
+
+  .icon {
+    font-size: 24px;
+    color: #8c939d;
+    width: 178px;
+    height: 178px;
+    text-align: center;
+  }
+
+  .text {
+    font-size: 20px;
+    line-height: 178px;
+    width: 178px;
+    text-align: center;
+  }
+
+  &:hover {
+    border-color: #409eff;
+  }
+}
+</style>

+ 21 - 0
src/views/supply/sellInvoiceQuery/components/invoice-modal.vue

@@ -0,0 +1,21 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import Invoice from "/@/components/Invoice";
+
+const visible = ref(false);
+
+const invoiceNumber = ref("");
+
+defineExpose({
+  onDisplay: ({ invoiceNumber: _invoiceNumber }) => {
+    visible.value = true;
+    invoiceNumber.value = _invoiceNumber;
+  }
+});
+</script>
+
+<template>
+  <el-dialog v-model="visible" width="1040px">
+    <Invoice :inv-number="invoiceNumber" type="purchase" />
+  </el-dialog>
+</template>

+ 67 - 0
src/views/supply/sellInvoiceQuery/components/invoice-table.vue

@@ -0,0 +1,67 @@
+<script setup lang="ts">
+import { ref, unref } from "vue";
+import { invTypeOptions } from "../config/_opitons";
+import InvoceForm from "./invoice-form.vue";
+
+const emit = defineEmits(["change"]);
+
+const invArr = ref<string[]>([]);
+const invoceFormRef = ref<InstanceType<typeof InvoceForm>>(null);
+
+function handleChange(data) {
+  invArr.value = [...invArr.value, ...(Array.isArray(data) ? data : [data])];
+  emit("change", unref(invArr));
+}
+
+function handleDelete({ $index }) {
+  invArr.value.splice($index, 1);
+  emit("change", unref(invArr));
+}
+</script>
+
+<template>
+  <div flex w-full justify-end mb-5>
+    <el-button type="primary" link @click="() => invoceFormRef.onDisplay()"
+      >添加</el-button
+    >
+  </div>
+  <el-table size="small" :data="invArr" border>
+    <el-table-column type="index" width="50" />
+
+    <el-table-column label="发票上传方式" prop="invType" show-overflow-tooltip>
+      <template #="{ row }">
+        <el-tag>{{
+          invTypeOptions.find(t => t.value === row.invType)?.label
+        }}</el-tag>
+      </template>
+    </el-table-column>
+
+    <el-table-column label="发票号码" prop="invNumber" show-overflow-tooltip />
+    <el-table-column label="发票代码" prop="invCode" show-overflow-tooltip />
+    <el-table-column label="校验码" prop="checkNumber" show-overflow-tooltip />
+    <el-table-column label="发票名称" prop="invName" show-overflow-tooltip />
+    <el-table-column label="发票图片" prop="inv_img" show-overflow-tooltip>
+      <template #="{ row }">
+        <el-image
+          :src="row.inv_img"
+          :preview-src-list="[row.inv_img]"
+          style="height: 30px"
+          preview-teleported
+        />
+      </template>
+    </el-table-column>
+    <el-table-column label="税前金额" prop="subtotal_amount" />
+
+    <el-table-column label="开票日期" prop="open_time" show-overflow-tooltip />
+
+    <el-table-column label="操作" width="60px">
+      <template #="scope">
+        <el-button link type="primary" @click="() => handleDelete(scope)"
+          >删除</el-button
+        >
+      </template>
+    </el-table-column>
+  </el-table>
+
+  <InvoceForm ref="invoceFormRef" @change="handleChange" />
+</template>

+ 65 - 0
src/views/supply/sellInvoiceQuery/components/return-form.vue

@@ -0,0 +1,65 @@
+<script setup lang="ts">
+import { ref, unref } from "vue";
+import ReconciliationForm from "/@/components/ReconciliationForm";
+import { ElForm } from "element-plus";
+import ChooseModal from "./choose-modal.vue";
+import InvoiceTable from "./invoice-table.vue";
+import { createRules } from "../config/_rules";
+
+const emit = defineEmits(["create"]);
+
+const formRef = ref<InstanceType<typeof ElForm> | null>(null);
+const chooseModalRef = ref<InstanceType<typeof ChooseModal> | null>(null);
+const reconciliationRef = ref<InstanceType<typeof ReconciliationForm> | null>(
+  null
+);
+
+const formData = ref<Record<string, any>>({
+  payNo: "",
+  invArr: []
+});
+
+const handleChoose = () => chooseModalRef.value.onDisplay();
+
+function handleCreate() {
+  formRef.value.validate(isValid => isValid && emit("create", unref(formData)));
+}
+
+const handleChange = invArr => (formData.value.invArr = invArr);
+</script>
+
+<template>
+  <div w-full>
+    <ElForm
+      ref="formRef"
+      label-width="100px"
+      :model="formData"
+      :rules="createRules"
+    >
+      <ElFormItem label="订单对账" prop="payNo">
+        <ReconciliationForm
+          ref="reconciliationRef"
+          isPurchPay
+          :id="formData.payNo"
+          :readonly="!!formData.payNo"
+          @choose="handleChoose"
+        />
+      </ElFormItem>
+
+      <ElFormItem label="发票" prop="invArr">
+        <InvoiceTable @change="handleChange" />
+      </ElFormItem>
+
+      <ElFormItem>
+        <div class="flex w-full justify-end">
+          <ElButton type="primary" @click="handleCreate">保存</ElButton>
+        </div>
+      </ElFormItem>
+    </ElForm>
+
+    <ChooseModal
+      ref="chooseModalRef"
+      @choose="payNo => (formData.payNo = payNo)"
+    />
+  </div>
+</template>

+ 351 - 0
src/views/supply/sellInvoiceQuery/config/_details.ts

@@ -0,0 +1,351 @@
+/**
+ * 采购回票详情
+ */
+
+import { ElImage, ElTag } from "element-plus";
+import { h } from "vue";
+import { createTooltip } from "/@/utils/tootip";
+import { DescriptionColumns } from "/@/components/BasicDescriptions";
+import { invTypeOptions, statusOptions } from "./_opitons";
+import {
+  invoiceTypeList,
+  seller_check_type,
+  buyer_check_type
+} from "/@/utils/status";
+
+export const cgInvTypeOptions = [
+  {
+    value: "special",
+    label: "增值税专用发票"
+  },
+  {
+    value: "normal",
+    label: "增值税普通发票"
+  },
+  {
+    value: "roll",
+    label: "增值税普通发票(卷式)"
+  },
+  {
+    value: "special_electronic",
+    label: "增值税电子专用发票"
+  },
+  {
+    value: "electronic",
+    label: "增值税电子普通发票"
+  },
+  {
+    value: "toll",
+    label: "增值税电子普通发票(通行费)"
+  }
+];
+
+export const ticketReturnColumns: DescriptionColumns = [
+  {
+    field: "hpNo",
+    label: "对账回票编号"
+  },
+  {
+    field: "status",
+    label: "状态",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => statusOptions.find(s => s.value === String(data))?.label
+      });
+    }
+  },
+  {
+    field: "apply_name",
+    label: "申请人"
+  },
+  {
+    field: "addtime",
+    label: "创建时间"
+  },
+
+  {
+    field: "inv_fee",
+    label: "回票金额"
+  },
+
+  {
+    field: "invType",
+    label: "发票上传方式",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => invTypeOptions.find(s => s.value === String(data))?.label
+      });
+    }
+  },
+  {
+    field: "invoiceType",
+    label: "发票类型",
+    render(data) {
+      return h(ElTag, null, {
+        default: () =>
+          invoiceTypeList.find(s => s.value === String(data))?.label || "--"
+      });
+    }
+  },
+
+  {
+    field: "open_time",
+    label: "发票日期"
+  },
+  {
+    field: "invoiceNumber",
+    label: "发票号码"
+  },
+  {
+    field: "invoiceCode",
+    label: "发票代码"
+  },
+  {
+    field: "inv_subtotal_amount",
+    label: "税前总金额"
+  },
+  {
+    field: "inv_amount",
+    label: "税后总金额"
+  },
+  {
+    field: "checkNumber",
+    span: 8,
+    label: "校验码"
+  },
+  {
+    field: "inv_img",
+    label: "发票图片",
+    span: 4,
+    render(src) {
+      return h(ElImage, {
+        src,
+        previewSrcList: [src],
+        style: {
+          height: "20px"
+        }
+      });
+    }
+  },
+  {
+    field: "remark",
+    span: 12,
+    label: "审核备注"
+  },
+  {
+    label: "购买方抬头状态",
+    field: "seller_check",
+    render(data) {
+      return h(
+        ElTag,
+        {
+          type: seller_check_type.find(s => s.value === String(data))
+            ?.type as any
+        },
+        {
+          default: () =>
+            seller_check_type.find(s => s.value === String(data))?.label || "--"
+        }
+      );
+    }
+  },
+  {
+    label: "销售方抬头状态",
+    field: "buyer_check",
+    render(data) {
+      return h(
+        ElTag,
+        {
+          type: buyer_check_type.find(s => s.value === String(data))
+            ?.type as any
+        },
+        {
+          default: () =>
+            buyer_check_type.find(s => s.value === String(data))?.label || "--"
+        }
+      );
+    }
+  }
+];
+
+export const TICKET_INV_COLUMNS = [
+  {
+    field: "invoiceType",
+    span: 1,
+    label: "发票类型"
+  },
+  {
+    field: "inv_img",
+    span: 1,
+    label: "发票图片",
+    render(src) {
+      return h(ElImage, {
+        src,
+        previewSrcList: [src],
+        style: {
+          height: "20px"
+        }
+      });
+    }
+  },
+  {
+    field: "inv_fee",
+    span: 2,
+    label: "发票税前金额"
+  },
+  {
+    field: "open_time",
+    label: "开票时间"
+  },
+  {
+    field: "invoiceNumber",
+    label: "发票号码"
+  },
+  {
+    field: "invoiceCode",
+    label: "发票代码"
+  },
+  {
+    field: "checkNumber",
+    label: "校验码"
+  }
+];
+
+export const TICKET_COLUMNS = [
+  {
+    field: "payNo",
+    span: 2,
+    label: "对账编号"
+  },
+  {
+    field: "hpNo",
+    span: 2,
+    label: "回票申请编号"
+  },
+  {
+    field: "apply_name",
+    label: "申请人",
+    span: 2,
+    render: (apply_name, { apply_id }) =>
+      createTooltip(apply_name, "申请人ID : " + apply_id, 160)
+  },
+  {
+    field: "invType",
+    label: "发票申请类型",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => invTypeOptions.find(s => s.value === data)?.label
+      });
+    }
+  },
+  {
+    field: "status",
+    label: "状态",
+    render(data) {
+      return h(ElTag, null, {
+        default: () => statusOptions.find(s => s.value === data)?.label
+      });
+    }
+  },
+  {
+    field: "total_fee",
+    label: "回票总额"
+  }
+];
+
+export const columns = [
+  {
+    label: "对账编号",
+    prop: "payNo",
+    minWidth: 160,
+    align: "left"
+  },
+  {
+    label: "供应商编号",
+    prop: "supplierNo",
+    minWidth: 150,
+    align: "left"
+  },
+  {
+    label: "供应商名称",
+    prop: "supplierName",
+    minWidth: 180,
+    align: "left"
+  },
+  {
+    label: "业务公司编号",
+    prop: "companyNo",
+    minWidth: 150
+  },
+  {
+    label: "业务公司名称",
+    prop: "companyName",
+    minWidth: 180
+  },
+  {
+    label: "付款状态",
+    prop: "pay_status",
+    minWidth: 100
+  },
+  {
+    label: "总额款",
+    prop: "total_fee",
+    minWidth: 110,
+    align: "total_fee"
+  },
+  {
+    label: "付款标签金额",
+    prop: "pay_tag_fee",
+    minWidth: 110,
+    align: "left"
+  },
+  {
+    label: "已付款金额",
+    prop: "apay_fee",
+    minWidth: 110,
+    align: "left"
+  },
+  {
+    label: "申请人",
+    prop: "apply_name",
+    minWidth: 90,
+    align: "total_fee"
+  }
+];
+
+export const ticketFormItems = {
+  //手工开票
+  "1": [
+    {
+      label: "发票类型",
+      prop: "invoiceType"
+    },
+    {
+      label: "发票代码",
+      prop: "invCode"
+    },
+    {
+      label: "发票号码",
+      prop: "invNumber"
+    },
+    {
+      label: "税前金额",
+      prop: "subtotal_amount"
+    },
+    {
+      label: "校验码",
+      prop: "checkNumber"
+    },
+    {
+      label: "开票日期",
+      prop: "open_time"
+    }
+  ],
+  //ocr识别
+  "2": [
+    {
+      label: "发票图片",
+      prop: "inv_img"
+    }
+  ]
+};

+ 76 - 0
src/views/supply/sellInvoiceQuery/config/_opitons.ts

@@ -0,0 +1,76 @@
+export const statusOptions = [
+  {
+    label: "图片识别中",
+    value: "0"
+  },
+  {
+    label: "待系统验证",
+    value: "1"
+  },
+  {
+    label: "买方公司财务审核",
+    value: "2"
+  },
+  {
+    label: "买方公司认证",
+    value: "3"
+  },
+  {
+    label: "认证成功",
+    value: "4"
+  },
+  {
+    label: "验证失败",
+    value: "5"
+  },
+  {
+    label: "买方审核驳回",
+    value: "6"
+  },
+  {
+    label: "认证失败",
+    value: "7"
+  },
+  {
+    label: "回票流程终止",
+    value: "8"
+  },
+  {
+    label: "验证超过次数",
+    value: "9"
+  },
+  {
+    label: "回票已退",
+    value: "10"
+  }
+];
+
+export const invTypeOptions = [
+  {
+    value: "1",
+    label: "手工添加"
+  },
+  {
+    value: "2",
+    label: "ocr识别"
+  },
+  {
+    value: "3",
+    label: "金税识别"
+  }
+];
+
+export const checkTypeOptions = [
+  {
+    value: "0",
+    label: "未分配查询"
+  },
+  {
+    value: "1",
+    label: "金税接口"
+  },
+  {
+    value: "2",
+    label: "华为云"
+  }
+];

+ 82 - 0
src/views/supply/sellInvoiceQuery/config/_rules.ts

@@ -0,0 +1,82 @@
+import { FormRules } from "element-plus";
+
+export const createRules: FormRules = {
+  payNo: {
+    trigger: "change",
+    required: true,
+    message: "请选择订单"
+  },
+  invArr: {
+    required: true,
+    message: "请上传发票",
+    validator(_, value: Array<any>) {
+      return value.length !== 0;
+    }
+  }
+};
+
+export const invoceRules: FormRules = {
+  payNo: [
+    {
+      trigger: "blur",
+      message: "请选择订单",
+      required: true
+    }
+  ],
+  invoiceType: [
+    {
+      trigger: "blur",
+      message: "请选择发票类型",
+      required: true
+    }
+  ],
+  invType: [
+    {
+      trigger: "blur",
+      message: "请选择申请方式",
+      required: true
+    }
+  ],
+  invNumber: [
+    {
+      trigger: "blur",
+      message: "请输入发票代码",
+      required: true
+    }
+  ],
+  invCode: [
+    {
+      trigger: "blur",
+      message: "请选择发票编码",
+      required: true
+    }
+  ],
+  open_time: [
+    {
+      trigger: "blur",
+      message: "请选择开票时间",
+      required: true
+    }
+  ],
+  subtotal_amount: [
+    {
+      trigger: "blur",
+      message: "请输入金额",
+      required: true
+    }
+  ],
+  invName: [
+    {
+      trigger: "blur",
+      message: "请输入发票名称",
+      required: true
+    }
+  ],
+  gold: [
+    {
+      trigger: "blur",
+      message: "请选择金额",
+      required: true
+    }
+  ]
+};

+ 198 - 0
src/views/supply/sellInvoiceQuery/config/content.config.ts

@@ -0,0 +1,198 @@
+import { ContentConfig } from "/@/components/PageContent";
+import { httpList, httpDelete } from "/@/api/purchase/ticketReturn";
+
+import dayjs from "dayjs";
+import { h } from "vue";
+import { ElImage, ElTag } from "element-plus";
+import { checkTypeOptions, invTypeOptions, statusOptions } from "./_opitons";
+import {
+  invoiceTypeList,
+  seller_check_type,
+  buyer_check_type
+} from "/@/utils/status";
+
+const columns = [
+  {
+    type: "selection",
+    width: 55,
+    hide: ({ checkList }) => !checkList.includes("勾选列")
+  },
+  {
+    label: "序号",
+    type: "index",
+    width: 70,
+    hide: ({ checkList }) => !checkList.includes("序号列")
+  },
+  {
+    label: "对账回票申请",
+    prop: "hpNo",
+    width: 180
+  },
+  {
+    label: "对账编号",
+    prop: "payNo",
+    width: 180
+  },
+  {
+    label: "状态",
+    prop: "status",
+    width: 160,
+    transform: status =>
+      statusOptions.find(s => String(status) === String(s.value))?.label,
+    cellRenderer: ({ row, props }) =>
+      h(
+        ElTag,
+        {
+          size: props.size
+        },
+        {
+          default: () =>
+            statusOptions.find(s => String(row.status) === String(s.value))
+              ?.label
+        }
+      )
+  },
+  {
+    label: "发票上传方式",
+    prop: "invType",
+    width: 120,
+    cellRenderer: ({ row, props }) =>
+      h(
+        ElTag,
+        {
+          size: props.size
+        },
+        {
+          default: () =>
+            invTypeOptions.find(s => String(row.invType) === String(s.value))
+              ?.label
+        }
+      )
+  },
+  {
+    label: "发票查验方式",
+    width: 120,
+    cellRenderer: ({ row, props }) =>
+      h(
+        ElTag,
+        {
+          size: props.size
+        },
+        {
+          default: () =>
+            checkTypeOptions.find(s => String(row.checkApi) === s.value)?.label
+        }
+      )
+  },
+  {
+    label: "发票图片",
+    prop: "inv_img",
+    width: 85,
+    cellRenderer: ({ row }) =>
+      h(ElImage, {
+        src: row.inv_img,
+        style: { height: "20px" },
+        previewSrcList: [row.inv_img],
+        previewTeleported: true
+      })
+  },
+  {
+    label: "发票税前金额",
+    width: "110px",
+    prop: "inv_subtotal_amount"
+  },
+  {
+    label: "发票税后金额",
+    width: "110px",
+    prop: "total"
+  },
+  {
+    label: "购买方抬头状态",
+    prop: "seller_check",
+    width: 130,
+    cellRenderer: ({ row }) => {
+      return h(ElTag, null, {
+        default: () =>
+          seller_check_type.find(item => item.value == row.seller_check + "")
+            ?.label || "--"
+      });
+    }
+  },
+  {
+    label: "销售方抬头状态",
+    prop: "buyer_check",
+    width: 130,
+    cellRenderer: ({ row }) => {
+      return h(ElTag, null, {
+        default: () =>
+          buyer_check_type.find(item => item.value == row.buyer_check + "")
+            ?.label || "--"
+      });
+    }
+  },
+  {
+    label: "发票号码",
+    width: 110,
+    prop: "invoiceNumber"
+  },
+  {
+    label: "发票代码",
+    width: 110,
+    prop: "invoiceCode"
+  },
+  {
+    label: "开票日期",
+    prop: "open_time",
+    width: 120,
+    formatter: ({ open_time }) =>
+      open_time ? dayjs(open_time).format("YYYY-MM-DD") : ""
+  },
+  {
+    label: "发票状态",
+    width: 110,
+    prop: "invStatus_cn"
+  },
+  {
+    label: "发票类型",
+    width: 110,
+    cellRenderer({ row }) {
+      return h(ElTag, null, {
+        default: () =>
+          invoiceTypeList.find(({ value }) => value === row.invoiceType)
+            ?.label || "--"
+      });
+    }
+  },
+  {
+    label: "申请人",
+    prop: "apply_name"
+  },
+  {
+    label: "创建时间",
+    prop: "addtime",
+    width: 180,
+    formatter: ({ addtime }) => dayjs(addtime).format("YYYY-MM-DD HH:mm:ss")
+  },
+  {
+    label: "操作",
+    fixed: "right",
+    slot: "operation",
+    width: 110
+  }
+];
+
+const contentConfig: ContentConfig = {
+  title: "采购回票",
+  statusProp: "companyNo",
+  columns,
+  apis: {
+    httpDelete,
+    httpList
+  },
+  deleteProp: "hpNo",
+  delTooltip: "是否撤回采购回票申请",
+  superUserNoAction: true,
+  showDelete: ({ status }) => Number(status) <= 3
+};
+
+export default contentConfig;

+ 95 - 0
src/views/supply/sellInvoiceQuery/config/modal.config.ts

@@ -0,0 +1,95 @@
+import { ModalConfig } from "/@/components/PageModal/src/types";
+
+const modalConfig: ModalConfig = {
+  title: "采票回票",
+  labelWidth: "100px",
+  colLayout: { span: 24 },
+  formItems: [
+    {
+      field: "payNo",
+      type: "input",
+      label: "对账编号",
+      placeholder: "对账编号"
+    },
+    {
+      field: "hpNo",
+      type: "input",
+      label: "对账回票申请",
+      placeholder: "审核状态"
+    },
+    {
+      field: "apply_name",
+      type: "input",
+      label: "申请人名称",
+      placeholder: "申请人名称"
+    },
+    {
+      field: "invType",
+      type: "select",
+      label: "发票类型",
+      placeholder: "发票类型"
+    },
+    {
+      field: "status",
+      type: "select",
+      label: "审核状态",
+      placeholder: "审核状态"
+    },
+    {
+      field: "status",
+      type: "select",
+      label: "进项设置",
+      placeholder: "进项设置",
+      options: [
+        {
+          label: "人工",
+          value: "1"
+        },
+        {
+          label: "金税",
+          value: "2"
+        }
+      ]
+    },
+    {
+      field: "input_ticket",
+      type: "select",
+      label: "进项设置",
+      placeholder: "进项设置",
+      options: [
+        {
+          label: "人工",
+          value: "1"
+        },
+        {
+          label: "金税",
+          value: "2"
+        }
+      ]
+    },
+    {
+      field: "out_ticket",
+      type: "select",
+      label: "销项设置",
+      placeholder: "销项设置",
+      options: [
+        {
+          label: "人工",
+          value: "1"
+        },
+        {
+          label: "金税",
+          value: "2"
+        }
+      ]
+    },
+    {
+      field: "open_time",
+      type: "input",
+      label: "开票时间",
+      placeholder: "开票时间"
+    }
+  ]
+};
+
+export default modalConfig;

+ 65 - 0
src/views/supply/sellInvoiceQuery/config/search.config.ts

@@ -0,0 +1,65 @@
+import { FormConfig } from "/@/components/PageSearch";
+import { checkTypeOptions, invTypeOptions, statusOptions } from "./_opitons";
+
+const searchFormConfig: FormConfig = {
+  formItems: [
+    {
+      field: "status",
+      type: "select",
+      placeholder: "审核状态",
+      options: statusOptions
+    },
+    {
+      field: "invType",
+      type: "select",
+      placeholder: "发票上传方式",
+      options: invTypeOptions
+    },
+    {
+      field: "checkApi",
+      type: "select",
+      placeholder: "发票查验方式",
+      options: checkTypeOptions
+    },
+    {
+      field: "payNo",
+      type: "input",
+      placeholder: "对账编号"
+    },
+    {
+      field: "hpNo",
+      type: "input",
+      placeholder: "对账申请编号"
+    },
+    {
+      field: "apply_name",
+      type: "input",
+      placeholder: "申请人"
+    },
+    {
+      field: "create_timer",
+      type: "date_picker",
+      otherOptions: {
+        type: "daterange",
+        startProp: "start",
+        endProp: "end",
+        startPlaceholder: "新建起始时间",
+        endPlaceholder: "新建结束时间"
+      }
+    }
+    // ,
+    // {
+    //   field: "open_timer",
+    //   type: "date_picker",
+    //   otherOptions: {
+    //     type: "daterange",
+    //     startProp: "open_start",
+    //     endProp: "open_end",
+    //     startPlaceholder: "开票起始日期",
+    //     endPlaceholder: "开票结束日期"
+    //   }
+    // }
+  ]
+};
+
+export default searchFormConfig;

+ 7 - 0
src/views/supply/sellInvoiceQuery/config/xls-template.ts

@@ -0,0 +1,7 @@
+export const template = {
+  买方公司编码: "",
+  回票申请编码: "",
+  对账编号: "",
+  审核状态: "",
+  审核备注: ""
+};

+ 125 - 0
src/views/supply/sellInvoiceQuery/index.vue

@@ -0,0 +1,125 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import contentConfig from "./config/content.config";
+import searchConfig from "./config/search.config";
+import { usePageSearch, type PageHooks, type PageEvents } from "/@/hooks/page";
+import InvoiceModal from "./components/invoice-modal.vue";
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
+import { useRouter } from "vue-router";
+// import { useUserInfo } from "/@/hooks/core/useUser";
+import { httpRequsetExport } from "/@/utils/export";
+import { useCompany } from "/@/hooks/core/useCompany";
+import { PageContent } from "/@/components/PageContent";
+import { utils, writeFile } from "xlsx";
+import { template } from "./config/xls-template";
+import ExeclUpoad from "./components/execl-files-upload/index.vue";
+import { usePermission } from "/@/hooks/core/usePermission";
+import { useUserInfo } from "/@/hooks/core/useUser";
+
+const PageName = "sellInvoiceQuery";
+const baseUrl = "/purchase/ticketReturnDetail";
+const invStatus = ["4", "10"];
+
+// { code: "017", name: "导出回票数据" },
+// { code: "018", name: "批量导入认证结果" },
+// { code: "019", name: "下载回票批量申请模板" },
+const { hasPermissionWithCode } = usePermission(PageName);
+const { isSuperUser } = useUserInfo();
+const pageContentRef = ref<InstanceType<typeof PageContent> | null>(null);
+
+const { push } = useRouter();
+const { currentCompany } = useCompany();
+const loading = ref(false);
+const invoiceModalRef = ref<InstanceType<typeof InvoiceModal>>(null);
+const execelUploadRef = ref<InstanceType<typeof ExeclUpoad>>(null);
+
+async function handleDownload() {
+  await httpRequsetExport({
+    url: "ile",
+    name: "回票数据表",
+    onStart: () => (loading.value = true),
+    onSuccess: () => (loading.value = false),
+    onFail: () => (loading.value = false),
+    params: {
+      companyNo: currentCompany.value.companyNo,
+      ...(pageContentRef.value as any).getBasicParams()
+    }
+  });
+}
+
+const hooks: PageHooks = {
+  pageSearchHook: () => usePageSearch(undefined, undefined, searchConfig)
+};
+
+const events: PageEvents = {
+  content: {
+    preview: ({ hpNo }) => push(`${baseUrl}?id=${hpNo}`),
+    create: () => push(baseUrl)
+  }
+};
+
+//导出模板
+function onDownloadTemplate() {
+  //创建数据表
+  const workBook = utils.book_new();
+  const workSheet = utils.json_to_sheet([template]);
+  utils.book_append_sheet(workBook, workSheet, "sheet");
+
+  //导出模板
+  writeFile(workBook, "回票批量导入模板.xlsx", {
+    bookType: "xlsx"
+  });
+}
+</script>
+
+<template>
+  <PageAuth :pageName="PageName">
+    <PageContainer
+      :hooks="hooks"
+      :events="events"
+      :contentConfig="contentConfig"
+      :search-config="searchConfig"
+      :get-content-ref="(ref:any) => (pageContentRef = ref)"
+    >
+      <template #content_header>
+        <ElButton
+          size="small"
+          v-if="hasPermissionWithCode('017')"
+          :icon="useRenderIcon('arrow-down-line')"
+          :loading="loading"
+          @click="() => handleDownload()"
+          >导出回票数据</ElButton
+        >
+
+        <ElButton
+          size="small"
+          v-if="hasPermissionWithCode('018') && !isSuperUser"
+          :icon="useRenderIcon('arrow-up-line')"
+          @click="() => execelUploadRef.onDisplay()"
+          >批量导入认证结果</ElButton
+        >
+
+        <ElButton
+          size="small"
+          v-if="hasPermissionWithCode('019')"
+          :icon="useRenderIcon('arrow-down-line')"
+          @click="() => onDownloadTemplate()"
+          >下载回票批量申请模板</ElButton
+        >
+      </template>
+
+      <template #content_action="row">
+        <ElButton
+          v-if="invStatus.includes(String(row.status))"
+          link
+          type="primary"
+          :icon="useRenderIcon('scaletooriginal')"
+          @click="() => invoiceModalRef.onDisplay(row)"
+        />
+      </template>
+    </PageContainer>
+
+    <InvoiceModal ref="invoiceModalRef" />
+    <ExeclUpoad ref="execelUploadRef" />
+  </PageAuth>
+</template>

+ 0 - 0
src/views/supply/sellInvoiceQuery/卖出回票查询.md