Browse Source

Merge branch 'master' of http://36.137.93.232:3000/hf/byte-sailing-new

cz 2 years ago
parent
commit
99ed3a2800

BIN
public/img/jc.png


+ 422 - 0
src/views/purchaseManage/purchaseManage/purchaseDocumentary/index.vue

@@ -0,0 +1,422 @@
+<template>
+  <div class="content">
+    <el-form :inline="true" :model="sourceList.pagination" class="demo-form-inline">
+      <el-form-item label="采购合同号:" prop="code">
+        <el-input v-model="sourceList.pagination.code" placeholder="请输入采购合同号" />
+      </el-form-item>
+      <el-form-item label="采购员:" prop="userId">
+        <el-select v-model="sourceList.pagination.userId" placeholder="请选择业务员">
+          <el-option v-for="item in userList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getList">搜索</el-button>
+      </el-form-item>
+    </el-form>
+    <div style="padding: 10px 20px; width: 100%; box-sizing: border-box">
+      <div style="width: 100%; display: flex; padding-bottom: 5px; overflow-x: auto" class="topScrollDom" @scroll="handleScroll($event, true)">
+        <div v-for="(item, index) in purchaseTrackType" :key="index" style="display: flex; align-items: center">
+          <div style="width: 200px; height: 80px; position: relative" @click="selectRegion(item)">
+            <div :class="selectRecordDocumentaryId === item.id ? 'regionSelect' : 'regionNoSelect'" style="cursor: pointer">
+              <div style="color: #333333; font-size: 13px; font-weight: 700">{{ item.count }}</div>
+              <div style="color: #333333; font-size: 12px; font-weight: 700">{{ item.name }}</div>
+              <div class="right-bottom" style="position: absolute; right: 0; bottom: 0">
+                {{ index + 1 }}
+              </div>
+            </div>
+            <el-icon class="beacon" v-if="index < purchaseTrackType.length - 1"><CaretRight /></el-icon>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="main-content" v-loading="loading">
+      <el-card class="box-card" v-for="(item, index) in sourceList.data" :key="index" style="margin-bottom: 20px">
+        <template #header>
+          <el-row>
+            <el-col :span="4">
+              <span>采购合同号: </span>
+              <span style="cursor: pointer; color: #66b1ff">
+                {{ item.code }}
+              </span>
+            </el-col>
+            <el-col :span="4">
+              <span>采购员: {{ item.userName }}</span>
+            </el-col>
+            <el-col :span="4">
+              <span>供应商: {{ item.customerName }}</span>
+            </el-col>
+            <el-col :span="12">
+              <span>合同金额: {{ item.currency }}{{ item.amount }}</span>
+            </el-col>
+          </el-row>
+        </template>
+        <div style="overflow-x: auto; width: 100%" class="scrollDom" @scroll="handleScroll($event, false, index)">
+          <el-steps :space="200" align-center>
+            <el-step v-for="(itemType, key) in purchaseTrackType" :key="key" :title="itemType.name" :status="getStatus(itemType, item)" />
+          </el-steps>
+          <div style="display: flex">
+            <div v-for="(itemType, key) in purchaseTrackType" :key="key" style="width: 200px !important; min-width: 200px !important">
+              <div style="padding: 8px 0; text-align: center">
+                <el-icon style="color: #409eff; cursor: pointer" @click="clickAdd(item, itemType)"><EditPen /></el-icon>
+              </div>
+              <div style="text-align: center" v-if="item.documentaryRecord && item.documentaryRecord[itemType.id]">
+                <el-tooltip class="box-item" effect="light" placement="bottom">
+                  <template #content>
+                    <div style="max-width: 400px; max-height: 50vh; overflow-y: auto">
+                      <el-timeline>
+                        <el-timeline-item
+                          v-for="(activity, index) in item.documentaryRecord[itemType.id]"
+                          :key="index"
+                          :timestamp="activity.createTime"
+                          :hide-timestamp="true">
+                          <div style="background-color: #e2fbe8; padding: 8px">
+                            <div style="font-size: 12px">{{ activity.createTime }}</div>
+                            <div style="font-size: 12px" v-html="getStyle(activity.remark)"></div>
+                            <div v-if="activity.fileList && activity.fileList.length > 0">
+                              <div v-for="(file, index) in activity.fileList" :key="index">
+                                <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                              </div>
+                            </div>
+                          </div>
+                        </el-timeline-item>
+                      </el-timeline>
+                    </div>
+                  </template>
+                  <span style="color: #409eff; cursor: pointer; padding: 8px 0; font-size: 12px">跟单详情</span>
+                </el-tooltip>
+              </div>
+            </div>
+          </div>
+        </div>
+      </el-card>
+    </div>
+
+    <el-dialog :title="title" v-if="dialogVisible" v-model="dialogVisible" width="600" v-loading="loadingDialog">
+      <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
+        <template #completionTime>
+          <div>
+            <el-date-picker v-model="formData.data.completionTime" type="datetime" placeholder="请选择完成时间" value-format="YYYY-MM-DD HH:mm:ss" />
+          </div>
+        </template>
+        <template #file>
+          <div style="width: 100%">
+            <el-upload
+              v-model:fileList="fileList"
+              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+              :data="uploadData"
+              multiple
+              :before-upload="uploadFile"
+              :on-preview="onPreviewFile">
+              <el-button>选择</el-button>
+            </el-upload>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitForm()" size="large">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import useUserStore from "@/store/modules/user";
+import byForm from "@/components/byForm/index";
+import { ElMessage } from "element-plus";
+
+const { proxy } = getCurrentInstance();
+const userList = ref([]);
+const purchaseTrackType = ref([]);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    code: "",
+    userId: "",
+  },
+});
+const timer = ref(null);
+const loading = ref(false);
+const handleScroll = (event, flag, index) => {
+  const listDom = document.querySelectorAll(".scrollDom");
+  let scroll_left;
+  if (index !== " " && index !== undefined) {
+    scroll_left = listDom[index].scrollLeft;
+  }
+  if (timer.value) {
+    clearTimeout(timer.value);
+  }
+  if (flag) {
+    timer.value = setTimeout(() => {
+      const list = document.querySelectorAll(".scrollDom");
+      for (let i = 0; i < list.length; i++) {
+        const ele = list[i];
+        ele.scrollLeft = event.target.scrollLeft;
+      }
+    }, 200);
+  } else {
+    timer.value = setTimeout(() => {
+      const topScroll = document.querySelector(".topScrollDom");
+      topScroll.scrollLeft = scroll_left;
+      const list = document.querySelectorAll(".scrollDom");
+      for (let i = 0; i < list.length; i++) {
+        const ele = list[i];
+        ele.scrollLeft = scroll_left;
+      }
+    }, 200);
+  }
+};
+const getDict = () => {
+  proxy.get("/tenantUser/list", { pageNum: 1, pageSize: 10000, tenantId: useUserStore().user.tenantId }).then((res) => {
+    if (res.rows && res.rows.length > 0) {
+      userList.value = res.rows.map((item) => {
+        return {
+          label: item.userName,
+          value: item.userId,
+        };
+      });
+    }
+  });
+};
+const selectRecordDocumentaryId = ref("");
+const getList = () => {
+  loading.value = true;
+  proxy
+    .post("/documentaryRecord/info", {
+      type: "2",
+      pageNum: sourceList.value.pagination.pageNum,
+      pageSize: sourceList.value.pagination.pageSize,
+      condition: {
+        code: sourceList.value.pagination.code,
+        userId: sourceList.value.pagination.userId,
+      },
+      noRecordDocumentaryId: selectRecordDocumentaryId.value,
+    })
+    .then(
+      (res) => {
+        purchaseTrackType.value = res.documentaryList;
+        sourceList.value.data = res.page.rows;
+        sourceList.value.pagination.total = res.page.total;
+        setTimeout(() => {
+          loading.value = false;
+        }, 200);
+      },
+      (err) => {
+        console.log(err);
+        loading.value = false;
+      }
+    );
+};
+getDict();
+getList();
+const selectRegion = (item) => {
+  sourceList.value.pagination.pageNum = 1;
+  if (selectRecordDocumentaryId.value && selectRecordDocumentaryId.value === item.id) {
+    selectRecordDocumentaryId.value = "";
+  } else {
+    selectRecordDocumentaryId.value = item.id;
+  }
+  getList();
+};
+const getStatus = (typeItem, row) => {
+  if (row.documentaryRecord && row.documentaryRecord[typeItem.id] && row.documentaryRecord[typeItem.id].length > 0) {
+    return "success";
+  }
+  return "process";
+};
+const title = ref("");
+const dialogVisible = ref(false);
+const loadingDialog = ref(false);
+const submit = ref(null);
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const formData = reactive({
+  data: {
+    fileList: [],
+  },
+});
+const fileList = ref([]);
+const uploadData = ref({});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "slot",
+      prop: "completionTime",
+      slotName: "completionTime",
+      label: "完成时间",
+    },
+    {
+      type: "slot",
+      prop: "file",
+      slotName: "file",
+      label: "上传附件",
+    },
+    {
+      type: "input",
+      prop: "remark",
+      label: "摘要",
+      itemType: "textarea",
+    },
+  ];
+});
+const rules = ref({
+  completionTime: [{ required: true, message: "请选择完成时间", trigger: "change" }],
+});
+const clickAdd = (item, row) => {
+  title.value = "编辑: " + row.name;
+  formData.data = {
+    documentaryId: row.id,
+    businessId: item.id,
+    completionTime: "",
+    remark: "",
+    fileList: [],
+  };
+  loadingDialog.value = false;
+  dialogVisible.value = true;
+};
+const uploadFile = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  file.id = res.id;
+  file.fileName = res.fileName;
+  file.fileUrl = res.fileUrl;
+  return true;
+};
+const onPreviewFile = (file) => {
+  window.open(file.raw.fileUrl, "_blank");
+};
+const submitForm = () => {
+  submit.value.handleSubmit(() => {
+    if (fileList.value && fileList.value.length > 0) {
+      formData.data.fileList = fileList.value.map((item) => {
+        return {
+          id: item.raw.id,
+          fileName: item.raw.fileName,
+          fileUrl: item.raw.fileUrl,
+        };
+      });
+    }
+    loadingDialog.value = true;
+    proxy.post("/documentaryRecord/add", formData.data).then(
+      () => {
+        ElMessage({
+          message: "编辑成功",
+          type: "success",
+        });
+        dialogVisible.value = false;
+        getList();
+      },
+      (err) => {
+        console.log(err);
+        loadingDialog.value = false;
+      }
+    );
+  });
+};
+const getStyle = (text) => {
+  if (text) {
+    return text.replace(/\n|\r\n/g, "<br>");
+  } else {
+    return "";
+  }
+};
+const openFile = (path) => {
+  window.open(path, "_blank");
+};
+</script>
+
+<style lang="scss" scoped>
+.content {
+  margin: 20px;
+  padding: 20px;
+  background-color: white;
+  height: calc(100vh - 140px);
+}
+.right-bottom {
+  color: #fff;
+  width: 20px;
+  height: 20px;
+  line-height: 20px;
+  text-align: center;
+  background: url("/img/jc.png") no-repeat;
+  background-size: 20px 20px;
+}
+.topScrollDom {
+  .regionSelect {
+    background: linear-gradient(#ffd9d9, #eff6ff);
+    border-radius: 10px;
+    flex: 1;
+    width: 160px;
+    height: 80px;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+    position: relative;
+    margin-left: 20px;
+  }
+  .regionNoSelect {
+    background: linear-gradient(#d9edff, #eff6ff);
+    border-radius: 10px;
+    flex: 1;
+    width: 160px;
+    height: 80px;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+    position: relative;
+    margin-left: 20px;
+  }
+  .beacon {
+    margin: 0px 10px;
+    color: #46a6ff;
+    width: 20px;
+    height: 20px;
+    line-height: 20px;
+    text-align: center;
+    background-color: #eff6ff;
+    border-radius: 10px;
+    position: absolute;
+    right: -20px;
+    top: 30px;
+  }
+}
+.main-content {
+  margin-top: 20px;
+  height: calc(100vh - 140px - 232px);
+  overflow: auto;
+  &::-webkit-scrollbar {
+    width: 0px;
+    height: 0px;
+  }
+}
+.scrollDom {
+  &::-webkit-scrollbar {
+    width: 0px;
+    height: 0px;
+  }
+}
+::v-deep(.el-card__header) {
+  background-color: #f4f4f5;
+  padding: 14px 15px !important;
+  font-size: 14px !important;
+}
+.el-steps {
+  display: block !important;
+}
+::v-deep(.el-step__title) {
+  width: 200px;
+}
+::v-deep(.el-timeline) {
+  padding: 8px !important;
+}
+</style>

+ 3 - 6
src/views/purchaseSales/outAndInWarehouse/manualDelivery/index.vue

@@ -93,12 +93,9 @@ const config = computed(() => {
     {
       attrs: {
         label: "仓库名称",
-        prop: "warehouseId",
+        prop: "warehouseName",
         width: 220,
       },
-      render(type) {
-        return proxy.dictValueLabel(type, warehouseList.value);
-      },
     },
     {
       attrs: {
@@ -138,7 +135,7 @@ const config = computed(() => {
     {
       attrs: {
         label: "操作人",
-        prop: "userName",
+        prop: "opUserName",
         width: 140,
       },
     },
@@ -166,7 +163,7 @@ const getDict = () => {
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
-  proxy.post("/stockJournal/page", sourceList.value.pagination).then((res) => {
+  proxy.post("/stockJournalDetails/page", sourceList.value.pagination).then((res) => {
     sourceList.value.data = res.rows;
     sourceList.value.pagination.total = res.total;
     setTimeout(() => {

+ 3 - 6
src/views/purchaseSales/outAndInWarehouse/manualWarehousing/index.vue

@@ -93,12 +93,9 @@ const config = computed(() => {
     {
       attrs: {
         label: "仓库名称",
-        prop: "warehouseId",
+        prop: "warehouseName",
         width: 220,
       },
-      render(type) {
-        return proxy.dictValueLabel(type, warehouseList.value);
-      },
     },
     {
       attrs: {
@@ -138,7 +135,7 @@ const config = computed(() => {
     {
       attrs: {
         label: "操作人",
-        prop: "userName",
+        prop: "opUserName",
         width: 140,
       },
     },
@@ -166,7 +163,7 @@ const getDict = () => {
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
-  proxy.post("/stockJournal/page", sourceList.value.pagination).then((res) => {
+  proxy.post("/stockJournalDetails/page", sourceList.value.pagination).then((res) => {
     sourceList.value.data = res.rows;
     sourceList.value.pagination.total = res.total;
     setTimeout(() => {

+ 5 - 20
src/views/purchaseSales/outAndInWarehouse/record/index.vue

@@ -35,18 +35,6 @@ const opTypeList = ref([
     label: "出库",
     value: "2",
   },
-  {
-    label: "调仓",
-    value: "3",
-  },
-  {
-    label: "入库",
-    value: "4",
-  },
-  {
-    label: "出库",
-    value: "5",
-  },
 ]);
 const typeList = ref([
   {
@@ -118,7 +106,7 @@ const sourceList = ref({
     pageNum: 1,
     pageSize: 10,
     keyword: "",
-    type: "",
+    opType: "",
     productId: "",
   },
 });
@@ -127,8 +115,8 @@ const selectConfig = computed(() => {
   return [
     {
       label: "操作类型",
-      prop: "type",
-      data: typeList.value,
+      prop: "opType",
+      data: opTypeList.value,
     },
   ];
 });
@@ -157,12 +145,9 @@ const config = computed(() => {
     {
       attrs: {
         label: "仓库名称",
-        prop: "warehouseId",
+        prop: "warehouseName",
         width: 220,
       },
-      render(type) {
-        return proxy.dictValueLabel(type, warehouseList.value);
-      },
     },
     {
       attrs: {
@@ -202,7 +187,7 @@ const config = computed(() => {
     {
       attrs: {
         label: "操作人",
-        prop: "userName",
+        prop: "opUserName",
         width: 120,
       },
     },

+ 11 - 11
src/views/purchaseSales/outAndInWarehouse/warehouseAdjustment/index.vue

@@ -76,7 +76,7 @@ const sourceList = ref({
     keyword: "",
     warehouseId: "",
     toWarehouseId: "",
-    type: "3",
+    type: "3,11",
   },
 });
 const loading = ref(false);
@@ -106,22 +106,22 @@ const config = computed(() => {
     {
       attrs: {
         label: "调出仓库",
-        prop: "warehouseId",
+        prop: "warehouseName",
         width: 220,
       },
-      render(type) {
-        return proxy.dictValueLabel(type, warehouseList.value);
-      },
+      // render(type) {
+      //   return proxy.dictValueLabel(type, warehouseList.value);
+      // },
     },
     {
       attrs: {
         label: "调出仓库",
-        prop: "toWarehouseId",
+        prop: "toWarehouseName",
         width: 220,
       },
-      render(type) {
-        return proxy.dictValueLabel(type, warehouseList.value);
-      },
+      // render(type) {
+      //   return proxy.dictValueLabel(type, warehouseList.value);
+      // },
     },
     {
       attrs: {
@@ -161,7 +161,7 @@ const config = computed(() => {
     {
       attrs: {
         label: "操作人",
-        prop: "userName",
+        prop: "opUserName",
         width: 140,
       },
     },
@@ -189,7 +189,7 @@ const getDict = () => {
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
-  proxy.post("/stockJournal/page", sourceList.value.pagination).then((res) => {
+  proxy.post("/stockJournalDetails/page", sourceList.value.pagination).then((res) => {
     sourceList.value.data = res.rows;
     sourceList.value.pagination.total = res.total;
     setTimeout(() => {

+ 422 - 0
src/views/salesMange/saleContract/salesDocumentary/index.vue

@@ -0,0 +1,422 @@
+<template>
+  <div class="content">
+    <el-form :inline="true" :model="sourceList.pagination" class="demo-form-inline">
+      <el-form-item label="销售合同号:" prop="code">
+        <el-input v-model="sourceList.pagination.code" placeholder="请输入销售合同号" />
+      </el-form-item>
+      <el-form-item label="业务员:" prop="userId">
+        <el-select v-model="sourceList.pagination.userId" placeholder="请选择业务员">
+          <el-option v-for="item in userList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getList">搜索</el-button>
+      </el-form-item>
+    </el-form>
+    <div style="padding: 10px 20px; width: 100%; box-sizing: border-box">
+      <div style="width: 100%; display: flex; padding-bottom: 5px; overflow-x: auto" class="topScrollDom" @scroll="handleScroll($event, true)">
+        <div v-for="(item, index) in purchaseTrackType" :key="index" style="display: flex; align-items: center">
+          <div style="width: 200px; height: 80px; position: relative" @click="selectRegion(item)">
+            <div :class="selectRecordDocumentaryId === item.id ? 'regionSelect' : 'regionNoSelect'" style="cursor: pointer">
+              <div style="color: #333333; font-size: 13px; font-weight: 700">{{ item.count }}</div>
+              <div style="color: #333333; font-size: 12px; font-weight: 700">{{ item.name }}</div>
+              <div class="right-bottom" style="position: absolute; right: 0; bottom: 0">
+                {{ index + 1 }}
+              </div>
+            </div>
+            <el-icon class="beacon" v-if="index < purchaseTrackType.length - 1"><CaretRight /></el-icon>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="main-content" v-loading="loading">
+      <el-card class="box-card" v-for="(item, index) in sourceList.data" :key="index" style="margin-bottom: 20px">
+        <template #header>
+          <el-row>
+            <el-col :span="4">
+              <span>销售合同号: </span>
+              <span style="cursor: pointer; color: #66b1ff">
+                {{ item.code }}
+              </span>
+            </el-col>
+            <el-col :span="4">
+              <span>业务员: {{ item.userName }}</span>
+            </el-col>
+            <el-col :span="4">
+              <span>客户名称: {{ item.customerName }}</span>
+            </el-col>
+            <el-col :span="12">
+              <span>合同金额: {{ item.currency }}{{ item.amount }}</span>
+            </el-col>
+          </el-row>
+        </template>
+        <div style="overflow-x: auto; width: 100%" class="scrollDom" @scroll="handleScroll($event, false, index)">
+          <el-steps :space="200" align-center>
+            <el-step v-for="(itemType, key) in purchaseTrackType" :key="key" :title="itemType.name" :status="getStatus(itemType, item)" />
+          </el-steps>
+          <div style="display: flex">
+            <div v-for="(itemType, key) in purchaseTrackType" :key="key" style="width: 200px !important; min-width: 200px !important">
+              <div style="padding: 8px 0; text-align: center">
+                <el-icon style="color: #409eff; cursor: pointer" @click="clickAdd(item, itemType)"><EditPen /></el-icon>
+              </div>
+              <div style="text-align: center" v-if="item.documentaryRecord && item.documentaryRecord[itemType.id]">
+                <el-tooltip class="box-item" effect="light" placement="bottom">
+                  <template #content>
+                    <div style="max-width: 400px; max-height: 50vh; overflow-y: auto">
+                      <el-timeline>
+                        <el-timeline-item
+                          v-for="(activity, index) in item.documentaryRecord[itemType.id]"
+                          :key="index"
+                          :timestamp="activity.createTime"
+                          :hide-timestamp="true">
+                          <div style="background-color: #e2fbe8; padding: 8px">
+                            <div style="font-size: 12px">{{ activity.createTime }}</div>
+                            <div style="font-size: 12px" v-html="getStyle(activity.remark)"></div>
+                            <div v-if="activity.fileList && activity.fileList.length > 0">
+                              <div v-for="(file, index) in activity.fileList" :key="index">
+                                <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                              </div>
+                            </div>
+                          </div>
+                        </el-timeline-item>
+                      </el-timeline>
+                    </div>
+                  </template>
+                  <span style="color: #409eff; cursor: pointer; padding: 8px 0; font-size: 12px">跟单详情</span>
+                </el-tooltip>
+              </div>
+            </div>
+          </div>
+        </div>
+      </el-card>
+    </div>
+
+    <el-dialog :title="title" v-if="dialogVisible" v-model="dialogVisible" width="600" v-loading="loadingDialog">
+      <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
+        <template #completionTime>
+          <div>
+            <el-date-picker v-model="formData.data.completionTime" type="datetime" placeholder="请选择完成时间" value-format="YYYY-MM-DD HH:mm:ss" />
+          </div>
+        </template>
+        <template #file>
+          <div style="width: 100%">
+            <el-upload
+              v-model:fileList="fileList"
+              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+              :data="uploadData"
+              multiple
+              :before-upload="uploadFile"
+              :on-preview="onPreviewFile">
+              <el-button>选择</el-button>
+            </el-upload>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitForm()" size="large">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import useUserStore from "@/store/modules/user";
+import byForm from "@/components/byForm/index";
+import { ElMessage } from "element-plus";
+
+const { proxy } = getCurrentInstance();
+const userList = ref([]);
+const purchaseTrackType = ref([]);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    code: "",
+    userId: "",
+  },
+});
+const timer = ref(null);
+const loading = ref(false);
+const handleScroll = (event, flag, index) => {
+  const listDom = document.querySelectorAll(".scrollDom");
+  let scroll_left;
+  if (index !== " " && index !== undefined) {
+    scroll_left = listDom[index].scrollLeft;
+  }
+  if (timer.value) {
+    clearTimeout(timer.value);
+  }
+  if (flag) {
+    timer.value = setTimeout(() => {
+      const list = document.querySelectorAll(".scrollDom");
+      for (let i = 0; i < list.length; i++) {
+        const ele = list[i];
+        ele.scrollLeft = event.target.scrollLeft;
+      }
+    }, 200);
+  } else {
+    timer.value = setTimeout(() => {
+      const topScroll = document.querySelector(".topScrollDom");
+      topScroll.scrollLeft = scroll_left;
+      const list = document.querySelectorAll(".scrollDom");
+      for (let i = 0; i < list.length; i++) {
+        const ele = list[i];
+        ele.scrollLeft = scroll_left;
+      }
+    }, 200);
+  }
+};
+const getDict = () => {
+  proxy.get("/tenantUser/list", { pageNum: 1, pageSize: 10000, tenantId: useUserStore().user.tenantId }).then((res) => {
+    if (res.rows && res.rows.length > 0) {
+      userList.value = res.rows.map((item) => {
+        return {
+          label: item.userName,
+          value: item.userId,
+        };
+      });
+    }
+  });
+};
+const selectRecordDocumentaryId = ref("");
+const getList = () => {
+  loading.value = true;
+  proxy
+    .post("/documentaryRecord/info", {
+      type: "1",
+      pageNum: sourceList.value.pagination.pageNum,
+      pageSize: sourceList.value.pagination.pageSize,
+      condition: {
+        code: sourceList.value.pagination.code,
+        userId: sourceList.value.pagination.userId,
+      },
+      noRecordDocumentaryId: selectRecordDocumentaryId.value,
+    })
+    .then(
+      (res) => {
+        purchaseTrackType.value = res.documentaryList;
+        sourceList.value.data = res.page.rows;
+        sourceList.value.pagination.total = res.page.total;
+        setTimeout(() => {
+          loading.value = false;
+        }, 200);
+      },
+      (err) => {
+        console.log(err);
+        loading.value = false;
+      }
+    );
+};
+getDict();
+getList();
+const selectRegion = (item) => {
+  sourceList.value.pagination.pageNum = 1;
+  if (selectRecordDocumentaryId.value && selectRecordDocumentaryId.value === item.id) {
+    selectRecordDocumentaryId.value = "";
+  } else {
+    selectRecordDocumentaryId.value = item.id;
+  }
+  getList();
+};
+const getStatus = (typeItem, row) => {
+  if (row.documentaryRecord && row.documentaryRecord[typeItem.id] && row.documentaryRecord[typeItem.id].length > 0) {
+    return "success";
+  }
+  return "process";
+};
+const title = ref("");
+const dialogVisible = ref(false);
+const loadingDialog = ref(false);
+const submit = ref(null);
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const formData = reactive({
+  data: {
+    fileList: [],
+  },
+});
+const fileList = ref([]);
+const uploadData = ref({});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "slot",
+      prop: "completionTime",
+      slotName: "completionTime",
+      label: "完成时间",
+    },
+    {
+      type: "slot",
+      prop: "file",
+      slotName: "file",
+      label: "上传附件",
+    },
+    {
+      type: "input",
+      prop: "remark",
+      label: "摘要",
+      itemType: "textarea",
+    },
+  ];
+});
+const rules = ref({
+  completionTime: [{ required: true, message: "请选择完成时间", trigger: "change" }],
+});
+const clickAdd = (item, row) => {
+  title.value = "编辑: " + row.name;
+  formData.data = {
+    documentaryId: row.id,
+    businessId: item.id,
+    completionTime: "",
+    remark: "",
+    fileList: [],
+  };
+  loadingDialog.value = false;
+  dialogVisible.value = true;
+};
+const uploadFile = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  file.id = res.id;
+  file.fileName = res.fileName;
+  file.fileUrl = res.fileUrl;
+  return true;
+};
+const onPreviewFile = (file) => {
+  window.open(file.raw.fileUrl, "_blank");
+};
+const submitForm = () => {
+  submit.value.handleSubmit(() => {
+    if (fileList.value && fileList.value.length > 0) {
+      formData.data.fileList = fileList.value.map((item) => {
+        return {
+          id: item.raw.id,
+          fileName: item.raw.fileName,
+          fileUrl: item.raw.fileUrl,
+        };
+      });
+    }
+    loadingDialog.value = true;
+    proxy.post("/documentaryRecord/add", formData.data).then(
+      () => {
+        ElMessage({
+          message: "编辑成功",
+          type: "success",
+        });
+        dialogVisible.value = false;
+        getList();
+      },
+      (err) => {
+        console.log(err);
+        loadingDialog.value = false;
+      }
+    );
+  });
+};
+const getStyle = (text) => {
+  if (text) {
+    return text.replace(/\n|\r\n/g, "<br>");
+  } else {
+    return "";
+  }
+};
+const openFile = (path) => {
+  window.open(path, "_blank");
+};
+</script>
+
+<style lang="scss" scoped>
+.content {
+  margin: 20px;
+  padding: 20px;
+  background-color: white;
+  height: calc(100vh - 140px);
+}
+.right-bottom {
+  color: #fff;
+  width: 20px;
+  height: 20px;
+  line-height: 20px;
+  text-align: center;
+  background: url("/img/jc.png") no-repeat;
+  background-size: 20px 20px;
+}
+.topScrollDom {
+  .regionSelect {
+    background: linear-gradient(#ffd9d9, #eff6ff);
+    border-radius: 10px;
+    flex: 1;
+    width: 160px;
+    height: 80px;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+    position: relative;
+    margin-left: 20px;
+  }
+  .regionNoSelect {
+    background: linear-gradient(#d9edff, #eff6ff);
+    border-radius: 10px;
+    flex: 1;
+    width: 160px;
+    height: 80px;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+    position: relative;
+    margin-left: 20px;
+  }
+  .beacon {
+    margin: 0px 10px;
+    color: #46a6ff;
+    width: 20px;
+    height: 20px;
+    line-height: 20px;
+    text-align: center;
+    background-color: #eff6ff;
+    border-radius: 10px;
+    position: absolute;
+    right: -20px;
+    top: 30px;
+  }
+}
+.main-content {
+  margin-top: 20px;
+  height: calc(100vh - 140px - 232px);
+  overflow: auto;
+  &::-webkit-scrollbar {
+    width: 0px;
+    height: 0px;
+  }
+}
+.scrollDom {
+  &::-webkit-scrollbar {
+    width: 0px;
+    height: 0px;
+  }
+}
+::v-deep(.el-card__header) {
+  background-color: #f4f4f5;
+  padding: 14px 15px !important;
+  font-size: 14px !important;
+}
+.el-steps {
+  display: block !important;
+}
+::v-deep(.el-step__title) {
+  width: 200px;
+}
+::v-deep(.el-timeline) {
+  padding: 8px !important;
+}
+</style>