Selaa lähdekoodia

.质量分析系统的销售反馈 从原因分析到整改及纠正措施验证三个步骤增加附件功能。

jxq 3 viikkoa sitten
vanhempi
commit
a384f3b864

+ 2 - 2
.env.development

@@ -8,9 +8,9 @@ VITE_APP_PORT = 3005
 VITE_APP_BASE_API = '/dev-api'
 
 # 上传文件接口地址
-VITE_APP_UPLOAD_URL = 'http://127.0.0.1:7105'
+VITE_APP_UPLOAD_URL = 'http://113.44.0.55:8010'
 # 开发接口地址
- VITE_APP_API_URL = 'http://127.0.0.1:7105'
+ VITE_APP_API_URL = 'http://192.168.110.9:7105'
 
 
 # 是否启用 Mock 服务

+ 158 - 0
src/components/Upload/FilesUpload.vue

@@ -0,0 +1,158 @@
+<template>
+  <el-upload
+    v-model:file-list="fileList"
+    class="upload-demo"
+    :on-change="handleChange"
+    :limit="limit"
+    :auto-upload="false"
+    :show-file-list="false"
+  >
+    <el-button
+      type="primary"
+      :loading="loading"
+      :disabled="fileList.length >= limit"
+      >点击上传文件</el-button
+    >
+    <template #tip>
+      <div class="tip" v-if="showTip">
+        文件类型限制为.jpg,.jpeg,.png,word文档,pdf,大小不超过{{ size }}M
+      </div>
+    </template>
+    <div>
+      <el-tag
+        class="file-item"
+        v-for="(file, index) in fileList"
+        :key="file.name"
+        closable
+        type="success"
+        @close="deleteFile(index)"
+        @click.stop.prevent="handlePreview(index)"
+      >
+        {{ file.name }}
+      </el-tag>
+    </div>
+  </el-upload>
+  <el-drawer
+    v-model="PDFVisible"
+    :footer="false"
+    :header="false"
+    :show-close="false"
+    destroy-on-close
+    direction="rtl"
+    :append-to-body="true"
+    size="972px"
+  >
+    <VuePdfEmbed :source="pdfSource" annotation-layer text-layer />
+  </el-drawer>
+</template>
+<script lang="ts" setup>
+import { ref } from "vue";
+import { UploadFile, UploadFiles, UploadUserFile } from "element-plus";
+import { uploadFileApi } from "@/api/file";
+import PDFView from "@/components/PDFView/index.vue";
+import VuePdfEmbed from "vue-pdf-embed";
+
+const props = defineProps({
+  size: {
+    type: Number,
+    default: 4,
+  },
+  limit: {
+    type: Number,
+    default: 1,
+  },
+  src: {
+    type: String,
+    default: "",
+  },
+  srcList: {
+    type: Array<string>,
+    default: () => [],
+  },
+  pdfList: {
+    type: Array<string>,
+    default: () => [],
+  },
+  fileNameList: {
+    type: Array<string>,
+    default: () => [],
+  },
+  generatePdf: {
+    type: Boolean,
+    default: false,
+  },
+  showTip: {
+    type: Boolean,
+    default: true,
+  },
+});
+
+const loading = ref(false);
+
+const emit = defineEmits([
+  "update:src",
+  "update:srcList",
+  "update:pdfList",
+  "update:fileNameList",
+  "finished",
+]);
+
+// 上传文件成功返回的值
+const src = useVModel(props, "src", emit); //单文件用这个
+const srcList = useVModel(props, "srcList", emit); //多文件用这个
+const pdfList = useVModel(props, "pdfList", emit); //转换成pdf后的多文件用这个
+const fileNameList = useVModel(props, "fileNameList", emit); //多文件上传要显示文件名
+
+// el-upload 绑定的值
+const fileList = ref<UploadUserFile[]>([]);
+
+const handleChange = async (uploadFile: UploadFile) => {
+  if (uploadFile.size! > props.size * 1048 * 1048) {
+    ElMessage.warning(`上传文件不能大于${props.size}M`);
+    return;
+  }
+  loading.value = true;
+  uploadFileApi(uploadFile.raw as File, props.generatePdf)
+    .then((res: any) => {
+      loading.value = false;
+      src.value = res.data.fileUrl;
+      pdfList.value.push(res.data.pdfUrl);
+      srcList.value.push(res.data.fileUrl);
+      fileNameList.value.push(res.data.fileName);
+      emit("finished");
+    })
+    .catch((err) => {
+      ElMessage.error(err.message);
+      loading.value = false;
+    });
+};
+
+const deleteFile = (index: number) => {
+  src.value = ""; //删除直接清空src即可,不用考虑是哪个,因为单文件不会有多个
+  fileList.value.splice(index, 1);
+  srcList.value.splice(index, 1);
+  pdfList.value.splice(index, 1);
+  fileNameList.value.splice(index, 1);
+};
+
+const PDFVisible = ref(false);
+const pdfSource = ref("");
+const handlePreview = (index: number) => {
+  if (srcList.value.length > index && props.generatePdf) {
+    pdfSource.value =
+      import.meta.env.VITE_APP_UPLOAD_URL + pdfList.value[index];
+    PDFVisible.value = true;
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.tip {
+  font-size: 12px;
+  color: #e6a23c;
+  margin-top: 5px;
+}
+.file-item {
+  margin-left: 10px;
+}
+</style>

+ 84 - 42
src/views/sales/DetailCom.vue

@@ -2,54 +2,76 @@
   <div>
     <div class="sale-header">销售信息反馈表</div>
     <table border-collapse="collapse">
-        <tr>
-          <th>顾客名称</th>
-          <th>详细地址</th>
-          <th>联系人</th>
-          <th>联系电话</th>
-        </tr>
-        <tr>
-          <td>{{ saleModel.customer }}</td>
-          <td>{{ saleModel.address }}</td>
-          <td>{{ saleModel.contacts }}</td>
-          <td>{{ saleModel.phoneNo }}</td>
-        </tr>
-        <tr>
-          <th>产品型号</th>
-          <th>产品分类</th>
-          <th>检验批号</th>
-          <th>数量</th>
-        </tr>
-        <tr>
-          <td>{{ saleModel.materialModel }}</td>
-          <td>{{ saleModel.type }}</td>
-          <td>{{ saleModel.checkCode }}</td>
-          <td>{{ saleModel.num }}</td>
-        </tr>
+      <tr>
+        <th>顾客名称</th>
+        <th>详细地址</th>
+        <th>联系人</th>
+        <th>联系电话</th>
+      </tr>
+      <tr>
+        <td>{{ saleModel.customer }}</td>
+        <td>{{ saleModel.address }}</td>
+        <td>{{ saleModel.contacts }}</td>
+        <td>{{ saleModel.phoneNo }}</td>
+      </tr>
+      <tr>
+        <th>产品型号</th>
+        <th>产品分类</th>
+        <th>检验批号</th>
+        <th>数量</th>
+      </tr>
+      <tr>
+        <td>{{ saleModel.materialModel }}</td>
+        <td>{{ saleModel.type }}</td>
+        <td>{{ saleModel.checkCode }}</td>
+        <td>{{ saleModel.num }}</td>
+      </tr>
       <tr>
         <th>产品编号</th>
-        <th colspan="3" style="font-weight: normal;">{{ saleModel.seqs }}</th>
+        <th colspan="3" style="font-weight: normal">{{ saleModel.seqs }}</th>
       </tr>
-        <tr v-for="(item, index) in contentArray" :key="index">
-          <td colspan="4">
-            <div class="sale-box">
-              <div class="sale-title">{{ item.title }}</div>
-              <div class="sale-content">
-                {{ item.content }}
+      <tr v-for="(item, index) in contentArray" :key="index">
+        <td colspan="4">
+          <div class="sale-box">
+            <div class="sale-title">{{ item.title }}</div>
+            <div class="sale-content">
+              {{ item.content }}
+            </div>
+            <div class="sale-bottom">
+              <div class="desc" v-if="index === 0">
+                市场营销部:{{ item[`user`] }}
+              </div>
+              <div class="desc" v-if="index === 1">
+                质量管理部:{{ item[`user`] }}
+              </div>
+              <div class="desc" v-if="index === 2">
+                责任部门:{{ item[`user`] }}
               </div>
-              <div class="sale-bottom">
-                <div class="desc" v-if="index === 0">市场营销部:{{ item[`user`] }}</div>
-                <div class="desc" v-if="index === 1">质量管理部:{{ item[`user`] }}</div>
-                <div class="desc" v-if="index === 2">责任部门:{{ item[`user`] }}</div>
-                <div class="desc" v-if="index === 3">责任部门:{{ item[`user`] }}</div>
-                <div class="desc" v-if="index === 4">质量管理部:{{ item[`user`] }}</div>
-                <div class="desc" v-if="index === 5">质量管理部:{{ item[`user`] }}</div>
-                <div class="desc" v-if="index === 6">市场营销部:{{ item[`user`] }}</div>
-                <div class="desc">时间: {{ item.time }}</div>
+              <div class="desc" v-if="index === 3">
+                责任部门:{{ item[`user`] }}
               </div>
+              <div class="desc" v-if="index === 4">
+                质量管理部:{{ item[`user`] }}
+              </div>
+              <div class="desc" v-if="index === 5">
+                质量管理部:{{ item[`user`] }}
+              </div>
+              <div class="desc" v-if="index === 6">
+                市场营销部:{{ item[`user`] }}
+              </div>
+              <div class="desc">时间: {{ item.time }}</div>
+              <el-button
+                v-if="item.fileUrl"
+                link
+                type="primary"
+                size="small"
+                @click="handleDownload(item)"
+                >下载附件</el-button
+              >
             </div>
-          </td>
-        </tr>
+          </div>
+        </td>
+      </tr>
     </table>
   </div>
 </template>
@@ -93,6 +115,26 @@ const refreshView = (row) => {
   }
 };
 
+const handleDownload = (item) => {
+  let url = import.meta.env.VITE_APP_UPLOAD_URL + item.fileUrl;
+
+  // 创建一个 <a> 元素
+  var link = document.createElement("a");
+  // 设置文件的 URL
+  link.href = url;
+  // 设置下载的文件名
+  link.download = item.fileName;
+
+  // 将 <a> 元素添加到文档中(虽然不需要在页面上可见)
+  document.body.appendChild(link);
+
+  // 触发点击事件
+  link.click();
+
+  // 移除 <a> 元素
+  document.body.removeChild(link);
+};
+
 defineExpose({ refreshView });
 </script>
 

+ 28 - 11
src/views/sales/handle3.vue

@@ -18,7 +18,7 @@
           :autosize="{ minRows: 3, maxRows: 6 }"
         />
       </el-form-item>
-<!--      <el-form-item label="填表人" prop="user">
+      <!--      <el-form-item label="填表人" prop="user">
         <el-select
           v-model="remark.user"
           placeholder="请选择"
@@ -32,7 +32,7 @@
           />
         </el-select>
       </el-form-item>-->
-<!--      <el-form-item label="日期" prop="time">
+      <!--      <el-form-item label="日期" prop="time">
         <el-date-picker
           v-model="remark.time"
           type="date"
@@ -44,11 +44,22 @@
           style="width: 100%"
         />
       </el-form-item>-->
-      <el-form-item label="下一步处理人" prop="nextRemarkUser" v-if="remark.state == 0">
+      <el-form-item
+        label="下一步处理人"
+        prop="nextRemarkUser"
+        v-if="remark.state == 0"
+      >
         <el-tree-select
-            v-model="remark.nextRemarkUser"
-            :data="userList"
-            filterable
+          v-model="remark.nextRemarkUser"
+          :data="userList"
+          filterable
+        />
+      </el-form-item>
+      <el-form-item label="附件" prop="fileUrl">
+        <FilesUpload
+          v-model:src="remark.fileUrl"
+          :generate-pdf="false"
+          v-model:file-name-list="fileNameList"
         />
       </el-form-item>
       <el-form-item label="是否通过" prop="state">
@@ -72,17 +83,18 @@
 import DetailCom from "@/views/sales/DetailCom.vue";
 import { dealFeedback, getUserList } from "@/api/sales/index";
 import { getUserTree } from "@/api/user/index";
-import {useUserStore} from "@/store";
+import { useUserStore } from "@/store";
+import FilesUpload from "@/components/Upload/FilesUpload.vue";
 const drawerVisible = ref(false);
 const detailComRef = ref(null);
 const saleModel = ref({});
 const userStore = useUserStore();
-const disabledDate = (time)=> {
+const disabledDate = (time) => {
   //选择今天以及今天之后的日期
-  return time.getTime() < Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择
+  return time.getTime() < Date.now() - 8.64e7; //如果没有后面的-8.64e7就是不可以选择
   //选择今天以及今天之前的日期
   //return time.getTime() > Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择今天的
-}
+};
 const showDrawer = (row) => {
   drawerVisible.value = true;
   saleModel.value = row;
@@ -120,7 +132,9 @@ const remark = reactive({
   time: "",
   nextRemarkUser: "",
   state: 0,
+  fileUrl: "",
 });
+const fileNameList = ref([]);
 
 const cancelClick = () => {
   drawerVisible.value = false;
@@ -142,7 +156,8 @@ const getUserInfo = async () => {
 const confirmClick = () => {
   remark.user = userStore.user.username;
   const date = new Date();
-  const now = date.getFullYear() + "年" + date.getMonth() + "月" + date.getDate() + "日"
+  const now =
+    date.getFullYear() + "年" + date.getMonth() + "月" + date.getDate() + "日";
   remarkRef.value.validate((valid) => {
     if (valid) {
       let remark2Copy = {
@@ -150,6 +165,8 @@ const confirmClick = () => {
         user: remark.user,
         time: now,
         state: remark.state,
+        fileUrl: remark.fileUrl,
+        fileName: fileNameList.value?.length > 0 ? fileNameList.value[0] : "",
       };
       let remark3 = {
         content: "",

+ 35 - 18
src/views/sales/handle4.vue

@@ -5,17 +5,17 @@
     </div>
 
     <el-form
-        ref="remarkRef"
-        :model="remark"
-        label-width="150"
-        :rules="rules1"
-        style="max-width: 600px; margin-left: 80px; margin-top: 20px"
+      ref="remarkRef"
+      :model="remark"
+      label-width="150"
+      :rules="rules1"
+      style="max-width: 600px; margin-left: 80px; margin-top: 20px"
     >
       <el-form-item label="整改及纠正措施" prop="content">
         <el-input
-            v-model="remark.content"
-            type="textarea"
-            :autosize="{ minRows: 3, maxRows: 6 }"
+          v-model="remark.content"
+          type="textarea"
+          :autosize="{ minRows: 3, maxRows: 6 }"
         />
       </el-form-item>
       <!--      <el-form-item label="填表人" prop="user">
@@ -32,7 +32,7 @@
                 />
               </el-select>
             </el-form-item>-->
-<!--      <el-form-item label="日期" prop="time">
+      <!--      <el-form-item label="日期" prop="time">
         <el-date-picker
             v-model="remark.time"
             type="date"
@@ -44,7 +44,11 @@
             style="width: 100%"
         />
       </el-form-item>-->
-      <el-form-item label="下一步处理人" prop="nextRemarkUser" v-if="remark.state == 0">
+      <el-form-item
+        label="下一步处理人"
+        prop="nextRemarkUser"
+        v-if="remark.state == 0"
+      >
         <!--        <el-select
                   v-model="remark.nextRemarkUser"
                   placeholder="请选择处理人"
@@ -58,9 +62,16 @@
                   />
                 </el-select>-->
         <el-tree-select
-            v-model="remark.nextRemarkUser"
-            :data="userList"
-            filterable
+          v-model="remark.nextRemarkUser"
+          :data="userList"
+          filterable
+        />
+      </el-form-item>
+      <el-form-item label="附件" prop="fileUrl">
+        <FilesUpload
+          v-model:src="remark.fileUrl"
+          :generate-pdf="false"
+          v-model:file-name-list="fileNameList"
         />
       </el-form-item>
       <el-form-item label="是否通过" prop="state">
@@ -84,17 +95,18 @@
 import DetailCom from "@/views/sales/DetailCom.vue";
 import { dealFeedback, getUserList } from "@/api/sales/index";
 import { getUserTree } from "@/api/user/index";
-import {useUserStore} from "@/store";
+import { useUserStore } from "@/store";
+import FilesUpload from "@/components/Upload/FilesUpload.vue";
 const drawerVisible = ref(false);
 const detailComRef = ref(null);
 const saleModel = ref({});
 const userStore = useUserStore();
-const disabledDate = (time)=> {
+const disabledDate = (time) => {
   //选择今天以及今天之后的日期
-  return time.getTime() < Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择
+  return time.getTime() < Date.now() - 8.64e7; //如果没有后面的-8.64e7就是不可以选择
   //选择今天以及今天之前的日期
   //return time.getTime() > Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择今天的
-}
+};
 const showDrawer = (row) => {
   drawerVisible.value = true;
   saleModel.value = row;
@@ -132,7 +144,9 @@ const remark = reactive({
   time: "",
   nextRemarkUser: "",
   state: 0,
+  fileUrl: "",
 });
+const fileNameList = ref([]);
 
 const cancelClick = () => {
   drawerVisible.value = false;
@@ -154,7 +168,8 @@ const getUserInfo = async () => {
 const confirmClick = () => {
   remark.user = userStore.user.username;
   const date = new Date();
-  const now = date.getFullYear() + "年" + date.getMonth() + "月" + date.getDate() + "日"
+  const now =
+    date.getFullYear() + "年" + date.getMonth() + "月" + date.getDate() + "日";
   remarkRef.value.validate((valid) => {
     if (valid) {
       let remark2Copy = {
@@ -162,6 +177,8 @@ const confirmClick = () => {
         user: remark.user,
         time: now,
         state: remark.state,
+        fileUrl: remark.fileUrl,
+        fileName: fileNameList.value?.length > 0 ? fileNameList.value[0] : "",
       };
       let remark3 = {
         content: "",

+ 35 - 18
src/views/sales/handle5.vue

@@ -5,17 +5,17 @@
     </div>
 
     <el-form
-        ref="remarkRef"
-        :model="remark"
-        label-width="150"
-        :rules="rules1"
-        style="max-width: 600px; margin-left: 80px; margin-top: 20px"
+      ref="remarkRef"
+      :model="remark"
+      label-width="150"
+      :rules="rules1"
+      style="max-width: 600px; margin-left: 80px; margin-top: 20px"
     >
       <el-form-item label="整改及纠正措施验证" prop="content">
         <el-input
-            v-model="remark.content"
-            type="textarea"
-            :autosize="{ minRows: 3, maxRows: 6 }"
+          v-model="remark.content"
+          type="textarea"
+          :autosize="{ minRows: 3, maxRows: 6 }"
         />
       </el-form-item>
       <!--      <el-form-item label="填表人" prop="user">
@@ -32,7 +32,7 @@
                 />
               </el-select>
             </el-form-item>-->
-<!--      <el-form-item label="日期" prop="time">
+      <!--      <el-form-item label="日期" prop="time">
         <el-date-picker
             v-model="remark.time"
             type="date"
@@ -44,7 +44,11 @@
             style="width: 100%"
         />
       </el-form-item>-->
-      <el-form-item label="下一步处理人" prop="nextRemarkUser" v-if="remark.state == 0">
+      <el-form-item
+        label="下一步处理人"
+        prop="nextRemarkUser"
+        v-if="remark.state == 0"
+      >
         <!--        <el-select
                   v-model="remark.nextRemarkUser"
                   placeholder="请选择处理人"
@@ -58,9 +62,16 @@
                   />
                 </el-select>-->
         <el-tree-select
-            v-model="remark.nextRemarkUser"
-            :data="userList"
-            filterable
+          v-model="remark.nextRemarkUser"
+          :data="userList"
+          filterable
+        />
+      </el-form-item>
+      <el-form-item label="附件" prop="fileUrl">
+        <FilesUpload
+          v-model:src="remark.fileUrl"
+          :generate-pdf="false"
+          v-model:file-name-list="fileNameList"
         />
       </el-form-item>
       <el-form-item label="是否通过" prop="state">
@@ -84,17 +95,18 @@
 import DetailCom from "@/views/sales/DetailCom.vue";
 import { dealFeedback, getUserList } from "@/api/sales/index";
 import { getUserTree } from "@/api/user/index";
-import {useUserStore} from "@/store";
+import { useUserStore } from "@/store";
+import FilesUpload from "@/components/Upload/FilesUpload.vue";
 const drawerVisible = ref(false);
 const detailComRef = ref(null);
 const saleModel = ref({});
 const userStore = useUserStore();
-const disabledDate = (time)=> {
+const disabledDate = (time) => {
   //选择今天以及今天之后的日期
-  return time.getTime() < Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择
+  return time.getTime() < Date.now() - 8.64e7; //如果没有后面的-8.64e7就是不可以选择
   //选择今天以及今天之前的日期
   //return time.getTime() > Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择今天的
-}
+};
 const showDrawer = (row) => {
   drawerVisible.value = true;
   saleModel.value = row;
@@ -132,7 +144,9 @@ const remark = reactive({
   time: "",
   nextRemarkUser: "",
   state: 0,
+  fileUrl: "",
 });
+const fileNameList = ref([]);
 
 const cancelClick = () => {
   drawerVisible.value = false;
@@ -154,7 +168,8 @@ const getUserInfo = async () => {
 const confirmClick = () => {
   remark.user = userStore.user.username;
   const date = new Date();
-  const now = date.getFullYear() + "年" + date.getMonth() + "月" + date.getDate() + "日"
+  const now =
+    date.getFullYear() + "年" + date.getMonth() + "月" + date.getDate() + "日";
   remarkRef.value.validate((valid) => {
     if (valid) {
       let remark2Copy = {
@@ -162,6 +177,8 @@ const confirmClick = () => {
         user: remark.user,
         time: now,
         state: remark.state,
+        fileUrl: remark.fileUrl,
+        fileName: fileNameList.value?.length > 0 ? fileNameList.value[0] : "",
       };
       let remark3 = {
         content: "",