From b2555a328ee990e8e25f32040633c2ec2fde8221 Mon Sep 17 00:00:00 2001
From: 祖安之光 <11848914+light-of-zuan@user.noreply.gitee.com>
Date: 星期一, 18 八月 2025 16:47:57 +0800
Subject: [PATCH] 修改新增

---
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/index.vue                       |  160 ++
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/index.vue                 |  215 +++
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/index.vue                     |  299 +++++
 src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/components/editDialog.vue        |  257 ++++
 src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/index.vue                        |  215 +++
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/components/editDialog.vue     |  603 +++++++++++
 src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/components/editDialog.vue           |  265 ++++
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/components/editDialog.vue       |  257 ++++
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/components/editDialog.vue |  257 ++++
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/components/editDialog.vue       |  168 +++
 public/outsourceProcessReview.docx                                                                    |    0 
 src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/index.vue                       |  215 +++
 src/api/outsourcingCooperate/outsourcingCooperate.js                                                  |  105 +
 src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/index.vue                           |  214 +++
 14 files changed, 3,230 insertions(+), 0 deletions(-)

diff --git a/public/outsourceProcessReview.docx b/public/outsourceProcessReview.docx
new file mode 100644
index 0000000..1c0e585
--- /dev/null
+++ b/public/outsourceProcessReview.docx
Binary files differ
diff --git a/src/api/outsourcingCooperate/outsourcingCooperate.js b/src/api/outsourcingCooperate/outsourcingCooperate.js
new file mode 100644
index 0000000..05bceac
--- /dev/null
+++ b/src/api/outsourcingCooperate/outsourcingCooperate.js
@@ -0,0 +1,105 @@
+import request from '@/utils/request'
+
+export function getOutsourcedProductNameList(query) {
+  return request({
+    url: '/system/outsourcedProduct/selectOutsourcedProductList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delOutsourcedProductNameList(query) {
+  return request({
+    url: '/system/outsourcedProduct/deletedOutsourcedProduct',
+    method: 'get',
+    params: query
+  })
+}
+
+export function updateOutsourcedProductNameList(data) {
+  return request({
+    url: '/system/outsourcedProduct/saveOutsourcedProduct',
+    method: 'post',
+    data: data
+  })
+}
+
+export function getOutsourcedCommonList(query) {
+  return request({
+    url: '/system/outsourcedCommon/selectOutsourcedCommonList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delOutsourcedCommon(query) {
+  return request({
+    url: '/system/outsourcedCommon/deletedOutsourcedCommon',
+    method: 'get',
+    params: query
+  })
+}
+
+export function updateOutsourcedCommon(data) {
+  return request({
+    url: '/system/outsourcedCommon/saveOutsourcedCommon',
+    method: 'post',
+    data: data
+  })
+}
+
+export function getOutsourcedUnqualifiedList(query) {
+  return request({
+    url: '/system/outsourcedUnqualified/selectOutsourcedUnqualifiedList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delOutsourcedUnqualified(query) {
+  return request({
+    url: '/system/outsourcedUnqualified/deletedOutsourcedUnqualified',
+    method: 'get',
+    params: query
+  })
+}
+
+export function updateOutsourcedUnqualified(data) {
+  return request({
+    url: '/system/outsourcedUnqualified/saveOutsourcedUnqualified',
+    method: 'post',
+    data: data
+  })
+}
+
+export function getOutsourcedReviewList(query) {
+  return request({
+    url: '/system/outsourcedReview/selectOutsourcedReviewList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delOutsourcedReview(query) {
+  return request({
+    url: '/system/outsourcedReview/deletedOutsourcedReview',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getOutsourcedReviewDetail(query) {
+  return request({
+    url: '/system/outsourcedReview/getOutsourcedReview',
+    method: 'get',
+    params: query
+  })
+}
+
+export function updateOutsourcedReview(data) {
+  return request({
+    url: '/system/outsourcedReview/saveOutsourcedReview',
+    method: 'post',
+    data: data
+  })
+}
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/components/editDialog.vue b/src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/components/editDialog.vue
new file mode 100644
index 0000000..1cf9ef4
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/components/editDialog.vue
@@ -0,0 +1,257 @@
+<template>
+  <div class="notice">
+    <el-dialog
+        v-model="dialogVisible"
+        :title="state.title"
+        width="700px"
+        :before-close="handleClose"
+        :close-on-press-escape="false"
+        :close-on-click-modal="false"
+    >
+      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="150px" >
+        <el-form-item v-if="state.isAdmin" label="企业:" prop="companyId">
+          <el-select v-model="state.form.companyId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="供应商:" prop="supplierId">
+          <el-select v-model="state.form.supplierId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.supplierList"
+                :key="item.id"
+                :label="item.supplierName"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="文件名称:" prop="fileName">
+          <el-input v-model.trim="state.form.fileName" :readonly="state.title =='查看'" placeholder="文件名称"></el-input>
+        </el-form-item>
+        <el-form-item label="文件:" prop="filePath">
+          <el-upload :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" >
+            <el-button type="primary">点击上传</el-button>
+            <template #tip>
+              <div class="el-upload__tip">文件尺寸小于5M,最多可上传1份</div>
+            </template>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <template #footer v-if="state.title !='查看'">
+        <span class="dialog-footer">
+            <el-button @click="handleClose" size="default">取 消</el-button>
+            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
+import {ElMessage} from "element-plus";
+import {addUser, editUser, getUserById, resetPwd} from "@/api/onlineEducation/user"
+import {Base64} from "js-base64"
+import {getCompany} from "@/api/onlineEducation/company";
+import {updateInfoPlatforms} from "@/api/staffManage/staff";
+import {getSupplierPage} from "@/api/supplier/supplierList";
+import {updateOutsourcedCommon, updateOutsourcedProductNameList} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {delPic} from "@/api/onlineEducation/banner";
+import {getToken} from "@/utils/auth";
+
+const emit = defineEmits(["getList"]);
+const dialogVisible = ref(false)
+const checkFiles = (rule, value, callback) => {
+  if (state.fileList.length == 0) {
+    callback(new Error('请上传文件'))
+  } else {
+    callback()
+  }
+}
+const superRef = ref()
+const state = reactive({
+  title: '',
+  form: {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    fileName: '',
+    filePath: '',
+    format: '',
+    osType: 3
+  },
+  formRules:{
+    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
+    supplierId: [{ required: true, message: '请选择供应商', trigger: 'blur' }],
+    filePath: [{ required: true, validator: checkFiles, trigger: 'blur' }]
+  },
+  isAdmin: false,
+  companyList: [],
+  supplierList: [],
+  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
+  header: {
+    Authorization: getToken()
+  },
+  fileLimit: 1,
+  fileList: []
+})
+onMounted(() => {
+
+});
+
+const openDialog = async (type, value,companyId, isAdmin, companyList) => {
+  state.isAdmin = isAdmin
+  if(isAdmin){
+    state.companyList = companyList
+  }
+  await getSupplierList()
+  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看'
+  state.form.companyId = companyId
+  if(state.title == '编辑'||state.title == '查看'){
+    Object.keys(state.form).forEach(key => {
+      if (key in value) {
+        state.form[key] = value[key]
+      }
+    })
+    if(value.filePath) {
+      const obj = {
+        url: value.filePath,
+        name: '工艺流程图'
+      }
+      state.fileList = [obj]
+    }
+  }
+  dialogVisible.value = true
+}
+
+const getSupplierList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getSupplierPage(queryParams)
+  if (res.code == 200) {
+    state.supplierList = res.data.list?res.data.list:[]
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const onSubmit = async () => {
+  const valid = await superRef.value.validate();
+  if(valid){
+    let data = {}
+    if(state.title == '新增'){
+      data = {
+        companyId: state.form.companyId,
+        fileName: state.form.fileName,
+        supplierId: state.form.supplierId,
+        supplierName: state.supplierList.find(i=>i.id == state.form.supplierId)?.supplierName,
+        filePath: state.form.filePath,
+        format: state.form.format,
+        osType: 3
+      }
+    }else{
+      data = state.form
+    }
+      const res = await updateOutsourcedCommon(data)
+      if(res.code == 200){
+        ElMessage.success(res.message)
+        emit('getList')
+        handleClose()
+        dialogVisible.value = false;
+      }else{
+        ElMessage.warning(res.message)
+      }
+
+  }
+}
+
+const handleClose = () => {
+  state.form = {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    fileName: '',
+    filePath: '',
+    format: '',
+    osType: 3
+  }
+  state.fileList = []
+  superRef.value.clearValidate();
+  superRef.value.resetFields()
+  dialogVisible.value = false;
+}
+
+const handleAvatarSuccess = (res, uploadFile) => {
+  if(res.code == 200){
+    state.form.filePath = res.data.path
+    state.form.format = '.' + res.data.filename.split('.')[1]
+  }else{
+    state.fileList = []
+    ElMessage({
+      type: 'warning',
+      message: '文件上传失败'
+    })
+  }
+}
+
+const showTip =()=>{
+  ElMessage({
+    type: 'warning',
+    message: '超出文件上传数量'
+  });
+}
+const picSize = async (rawFile) => {
+  if(rawFile.size / 1024 / 1024 > 5){
+    ElMessage({
+      type: 'warning',
+      message: '文件大小不能超过5M'
+    });
+    return false
+  }
+};
+const handleRemove = async (file, uploadFiles) => {
+  let path = state.form.filePath;
+  await delPic({path: path}).then(res => {
+    if(res.code == 200){
+      // ElMessage({
+      //   type: 'success',
+      //   message: '文件已删除'
+      // })
+      state.form.filePath = ''
+      state.form.format = ''
+    }else{
+      ElMessage({
+        type: 'warning',
+        message: res.message
+      })
+    }
+  }).catch(() => {
+    state.form.imgUrl = ''
+  });
+}
+
+defineExpose({
+  openDialog
+});
+
+</script>
+
+<style scoped lang="scss">
+.notice{
+  :deep(.el-form .el-form-item__label) {
+    font-size: 15px;
+  }
+  .file {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+</style>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/index.vue b/src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/index.vue
new file mode 100644
index 0000000..006cfed
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/inspectQualifyRecord/index.vue
@@ -0,0 +1,215 @@
+<template>
+  <div class="app-container">
+    <div style="display: flex;justify-content: space-between">
+      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
+        <el-form-item>
+          <el-button
+              type="primary"
+              plain
+              icon="Plus"
+              @click="openDialog('add',{})"
+          >新增</el-button>
+        </el-form-item>
+        <el-form-item v-if="isAdmin" label="企业:" >
+          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
+            <el-option
+                v-for="item in companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item >
+          <el-button v-if="isAdmin" type="primary" @click="getList">查询</el-button>
+          <el-button v-if="isAdmin" type="primary" plain @click="reset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 表格数据 -->
+    <el-table v-loading="loading" :data="dataList" :border="true">
+      <el-table-column label="序号" type="index" align="center" width="80"/>
+      <el-table-column label="供应商" prop="supplierName" align="center"/>
+      <el-table-column label="检验记录" align="center">
+        <template #default="scope">
+          <el-link type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName !== '' ?scope.row.fileName + scope.row.format : '检验记录'}}</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" >
+        <template #default="scope">
+          <el-button link type="primary" @click="downloadFile(scope.row.filePath)">下载</el-button>
+          <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+    />
+
+    <edit-dialog ref="dialogRef" @getList=getList></edit-dialog>
+  </div>
+</template>
+
+<script setup>
+import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {delCompany, getCompany} from "@/api/onlineEducation/company";
+import Cookies from "js-cookie";
+import editDialog from './components/editDialog.vue'
+import useUserStore from "@/store/modules/user";
+import {
+  delOutsourcedCommon,
+  delOutsourcedProductNameList, getOutsourcedCommonList,
+  getOutsourcedProductNameList
+} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {renderAsync} from "docx-preview";
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const dialogRef = ref();
+const data = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null,
+    osType: 3
+  },
+  total: 0,
+  dataList: [],
+  companyList: [],
+  isAdmin: false
+});
+
+const { queryParams, total, dataList,companyList, isAdmin } = toRefs(data);
+const userInfo = ref()
+onMounted(async ()=>{
+  userInfo.value = JSON.parse(Cookies.get('userInfo'))
+  if(userStore.roles.includes('admin')){
+    data.isAdmin = true
+    await getCompanyList()
+  }else{
+    data.isAdmin = false
+    data.queryParams.companyId = userStore.companyId
+  }
+  await getList()
+})
+
+onUnmounted(()=>{
+
+})
+
+const getList = async () => {
+  loading.value = true
+  const res = await getOutsourcedCommonList(data.queryParams)
+  if(res.code == 200){
+    data.dataList = res.data.list || []
+    data.total = res.data.total
+  }else{
+    ElMessage.warning(res.message)
+  }
+  loading.value = false
+}
+
+const getCompanyList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getCompany(queryParams)
+  if (res.code == 200) {
+    data.companyList = res.data.list?res.data.list:[]
+    // data.queryParams.companyId = data.companyList[0].id
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const downloadFile = (path)=>{
+  window.open(import.meta.env.VITE_APP_BASE_API + '/' + path)
+}
+
+const openFile = async(path)=>{
+  const ext = path.split('.').pop().toLowerCase();
+  if (ext === 'doc' || ext === 'pptx' || ext === 'ppt') {
+    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
+      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+    }).catch(() => {
+      console.log('取消预览')
+    });
+    return
+  }else if(ext === 'pdf' || ext === 'jpg' || ext === 'jpeg' || ext === 'png' || ext === 'cad'){
+    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+  }else{
+    try {
+      // 1. 获取文件
+      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
+      const arrayBuffer = await response.arrayBuffer();
+      // 2. 创建新窗口
+      const win = window.open('', '_blank');
+      win.document.write(`
+      <!DOCTYPE html>
+      <html>
+        <head>
+          <title>预览</title>
+          <style>
+            body { margin: 20px; font-family: Arial; }
+            .docx-container { width: 100%; height: 100%; }
+          </style>
+        </head>
+        <body>
+          <div id="container" class="docx-container"></div>
+        </body>
+      </html>
+    `);
+      // 3. 渲染 DOCX
+      await renderAsync(arrayBuffer, win.document.getElementById('container'));
+
+    } catch (error) {
+      console.error('预览失败:', error);
+      alert(`预览失败: ${error.message}`);
+    }
+  }
+}
+
+const openDialog = (type, value) => {
+  dialogRef.value.openDialog(type, value, data.queryParams.companyId, data.isAdmin, data.companyList );
+}
+
+/** 重置新增的表单以及其他数据  */
+const reset= async()=> {
+  data.queryParams = {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null,
+    osType: 3
+  }
+  await getCompanyList()
+  await getList()
+}
+const handleDelete = (val) => {
+  ElMessageBox.confirm(
+      '确定删除此条数据?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+      .then( async() => {
+        const res = await delOutsourcedCommon({id: val.id})
+        if(res.code == 200){
+          ElMessage.success('数据删除成功')
+          await getList()
+        }else{
+          ElMessage.warning(res.message)
+        }
+      })
+}
+
+</script>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/components/editDialog.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/components/editDialog.vue
new file mode 100644
index 0000000..9bcadbe
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/components/editDialog.vue
@@ -0,0 +1,257 @@
+<template>
+  <div class="notice">
+    <el-dialog
+        v-model="dialogVisible"
+        :title="state.title"
+        width="700px"
+        :before-close="handleClose"
+        :close-on-press-escape="false"
+        :close-on-click-modal="false"
+    >
+      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="150px" >
+        <el-form-item v-if="state.isAdmin" label="企业:" prop="companyId">
+          <el-select v-model="state.form.companyId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="供应商:" prop="supplierId">
+          <el-select v-model="state.form.supplierId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.supplierList"
+                :key="item.id"
+                :label="item.supplierName"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="文件名称:" prop="fileName">
+          <el-input v-model.trim="state.form.fileName" :readonly="state.title =='查看'" placeholder="文件名称"></el-input>
+        </el-form-item>
+        <el-form-item label="文件:" prop="filePath">
+          <el-upload :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" >
+            <el-button type="primary">点击上传</el-button>
+            <template #tip>
+              <div class="el-upload__tip">文件尺寸小于5M,最多可上传1份</div>
+            </template>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <template #footer v-if="state.title !='查看'">
+        <span class="dialog-footer">
+            <el-button @click="handleClose" size="default">取 消</el-button>
+            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
+import {ElMessage} from "element-plus";
+import {addUser, editUser, getUserById, resetPwd} from "@/api/onlineEducation/user"
+import {Base64} from "js-base64"
+import {getCompany} from "@/api/onlineEducation/company";
+import {updateInfoPlatforms} from "@/api/staffManage/staff";
+import {getSupplierPage} from "@/api/supplier/supplierList";
+import {updateOutsourcedCommon, updateOutsourcedProductNameList} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {delPic} from "@/api/onlineEducation/banner";
+import {getToken} from "@/utils/auth";
+
+const emit = defineEmits(["getList"]);
+const dialogVisible = ref(false)
+const checkFiles = (rule, value, callback) => {
+  if (state.fileList.length == 0) {
+    callback(new Error('请上传文件'))
+  } else {
+    callback()
+  }
+}
+const superRef = ref()
+const state = reactive({
+  title: '',
+  form: {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    fileName: '',
+    filePath: '',
+    format: '',
+    osType: 2
+  },
+  formRules:{
+    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
+    supplierId: [{ required: true, message: '请选择供应商', trigger: 'blur' }],
+    filePath: [{ required: true, validator: checkFiles, trigger: 'blur' }]
+  },
+  isAdmin: false,
+  companyList: [],
+  supplierList: [],
+  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
+  header: {
+    Authorization: getToken()
+  },
+  fileLimit: 1,
+  fileList: []
+})
+onMounted(() => {
+
+});
+
+const openDialog = async (type, value,companyId, isAdmin, companyList) => {
+  state.isAdmin = isAdmin
+  if(isAdmin){
+    state.companyList = companyList
+  }
+  await getSupplierList()
+  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看'
+  state.form.companyId = companyId
+  if(state.title == '编辑'||state.title == '查看'){
+    Object.keys(state.form).forEach(key => {
+      if (key in value) {
+        state.form[key] = value[key]
+      }
+    })
+    if(value.filePath) {
+      const obj = {
+        url: value.filePath,
+        name: '工艺流程图'
+      }
+      state.fileList = [obj]
+    }
+  }
+  dialogVisible.value = true
+}
+
+const getSupplierList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getSupplierPage(queryParams)
+  if (res.code == 200) {
+    state.supplierList = res.data.list?res.data.list:[]
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const onSubmit = async () => {
+  const valid = await superRef.value.validate();
+  if(valid){
+    let data = {}
+    if(state.title == '新增'){
+      data = {
+        companyId: state.form.companyId,
+        fileName: state.form.fileName,
+        supplierId: state.form.supplierId,
+        supplierName: state.supplierList.find(i=>i.id == state.form.supplierId)?.supplierName,
+        filePath: state.form.filePath,
+        format: state.form.format,
+        osType: 2
+      }
+    }else{
+      data = state.form
+    }
+      const res = await updateOutsourcedCommon(data)
+      if(res.code == 200){
+        ElMessage.success(res.message)
+        emit('getList')
+        handleClose()
+        dialogVisible.value = false;
+      }else{
+        ElMessage.warning(res.message)
+      }
+
+  }
+}
+
+const handleClose = () => {
+  state.form = {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    fileName: '',
+    filePath: '',
+    format: '',
+    osType: 2
+  }
+  state.fileList = []
+  superRef.value.clearValidate();
+  superRef.value.resetFields()
+  dialogVisible.value = false;
+}
+
+const handleAvatarSuccess = (res, uploadFile) => {
+  if(res.code == 200){
+    state.form.filePath = res.data.path
+    state.form.format = '.' + res.data.filename.split('.')[1]
+  }else{
+    state.fileList = []
+    ElMessage({
+      type: 'warning',
+      message: '文件上传失败'
+    })
+  }
+}
+
+const showTip =()=>{
+  ElMessage({
+    type: 'warning',
+    message: '超出文件上传数量'
+  });
+}
+const picSize = async (rawFile) => {
+  if(rawFile.size / 1024 / 1024 > 5){
+    ElMessage({
+      type: 'warning',
+      message: '文件大小不能超过5M'
+    });
+    return false
+  }
+};
+const handleRemove = async (file, uploadFiles) => {
+  let path = state.form.filePath;
+  await delPic({path: path}).then(res => {
+    if(res.code == 200){
+      // ElMessage({
+      //   type: 'success',
+      //   message: '文件已删除'
+      // })
+      state.form.filePath = ''
+      state.form.format = ''
+    }else{
+      ElMessage({
+        type: 'warning',
+        message: res.message
+      })
+    }
+  }).catch(() => {
+    state.form.imgUrl = ''
+  });
+}
+
+defineExpose({
+  openDialog
+});
+
+</script>
+
+<style scoped lang="scss">
+.notice{
+  :deep(.el-form .el-form-item__label) {
+    font-size: 15px;
+  }
+  .file {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+</style>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/index.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/index.vue
new file mode 100644
index 0000000..9a12289
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedContractAgreement/index.vue
@@ -0,0 +1,215 @@
+<template>
+  <div class="app-container">
+    <div style="display: flex;justify-content: space-between">
+      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
+        <el-form-item>
+          <el-button
+              type="primary"
+              plain
+              icon="Plus"
+              @click="openDialog('add',{})"
+          >新增</el-button>
+        </el-form-item>
+        <el-form-item v-if="isAdmin" label="企业:" >
+          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
+            <el-option
+                v-for="item in companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item >
+          <el-button v-if="isAdmin" type="primary" @click="getList">查询</el-button>
+          <el-button v-if="isAdmin" type="primary" plain @click="reset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 表格数据 -->
+    <el-table v-loading="loading" :data="dataList" :border="true">
+      <el-table-column label="序号" type="index" align="center" width="80"/>
+      <el-table-column label="供应商" prop="supplierName" align="center"/>
+      <el-table-column label="合同质量协议" align="center">
+        <template #default="scope">
+          <el-link type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName !== '' ?scope.row.fileName + scope.row.format : '合同质量协议'}}</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" >
+        <template #default="scope">
+          <el-button link type="primary" @click="downloadFile(scope.row.filePath)">下载</el-button>
+          <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+    />
+
+    <edit-dialog ref="dialogRef" @getList=getList></edit-dialog>
+  </div>
+</template>
+
+<script setup>
+import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {delCompany, getCompany} from "@/api/onlineEducation/company";
+import Cookies from "js-cookie";
+import editDialog from './components/editDialog.vue'
+import useUserStore from "@/store/modules/user";
+import {
+  delOutsourcedCommon,
+  delOutsourcedProductNameList, getOutsourcedCommonList,
+  getOutsourcedProductNameList
+} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {renderAsync} from "docx-preview";
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const dialogRef = ref();
+const data = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null,
+    osType: 2
+  },
+  total: 0,
+  dataList: [],
+  companyList: [],
+  isAdmin: false
+});
+
+const { queryParams, total, dataList,companyList, isAdmin } = toRefs(data);
+const userInfo = ref()
+onMounted(async ()=>{
+  userInfo.value = JSON.parse(Cookies.get('userInfo'))
+  if(userStore.roles.includes('admin')){
+    data.isAdmin = true
+    await getCompanyList()
+  }else{
+    data.isAdmin = false
+    data.queryParams.companyId = userStore.companyId
+  }
+  await getList()
+})
+
+onUnmounted(()=>{
+
+})
+
+const getList = async () => {
+  loading.value = true
+  const res = await getOutsourcedCommonList(data.queryParams)
+  if(res.code == 200){
+    data.dataList = res.data.list || []
+    data.total = res.data.total
+  }else{
+    ElMessage.warning(res.message)
+  }
+  loading.value = false
+}
+
+const getCompanyList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getCompany(queryParams)
+  if (res.code == 200) {
+    data.companyList = res.data.list?res.data.list:[]
+    // data.queryParams.companyId = data.companyList[0].id
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const downloadFile = (path)=>{
+  window.open(import.meta.env.VITE_APP_BASE_API + '/' + path)
+}
+
+const openFile = async(path)=>{
+  const ext = path.split('.').pop().toLowerCase();
+  if (ext === 'doc' || ext === 'pptx' || ext === 'ppt') {
+    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
+      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+    }).catch(() => {
+      console.log('取消预览')
+    });
+    return
+  }else if(ext === 'pdf' || ext === 'jpg' || ext === 'jpeg' || ext === 'png' || ext === 'cad'){
+    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+  }else{
+    try {
+      // 1. 获取文件
+      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
+      const arrayBuffer = await response.arrayBuffer();
+      // 2. 创建新窗口
+      const win = window.open('', '_blank');
+      win.document.write(`
+      <!DOCTYPE html>
+      <html>
+        <head>
+          <title>预览</title>
+          <style>
+            body { margin: 20px; font-family: Arial; }
+            .docx-container { width: 100%; height: 100%; }
+          </style>
+        </head>
+        <body>
+          <div id="container" class="docx-container"></div>
+        </body>
+      </html>
+    `);
+      // 3. 渲染 DOCX
+      await renderAsync(arrayBuffer, win.document.getElementById('container'));
+
+    } catch (error) {
+      console.error('预览失败:', error);
+      alert(`预览失败: ${error.message}`);
+    }
+  }
+}
+
+const openDialog = (type, value) => {
+  dialogRef.value.openDialog(type, value, data.queryParams.companyId, data.isAdmin, data.companyList );
+}
+
+/** 重置新增的表单以及其他数据  */
+const reset= async()=> {
+  data.queryParams = {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null,
+    osType: 2
+  }
+  await getCompanyList()
+  await getList()
+}
+const handleDelete = (val) => {
+  ElMessageBox.confirm(
+      '确定删除此条数据?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+      .then( async() => {
+        const res = await delOutsourcedCommon({id: val.id})
+        if(res.code == 200){
+          ElMessage.success('数据删除成功')
+          await getList()
+        }else{
+          ElMessage.warning(res.message)
+        }
+      })
+}
+
+</script>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/components/editDialog.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/components/editDialog.vue
new file mode 100644
index 0000000..6521278
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/components/editDialog.vue
@@ -0,0 +1,257 @@
+<template>
+  <div class="notice">
+    <el-dialog
+        v-model="dialogVisible"
+        :title="state.title"
+        width="700px"
+        :before-close="handleClose"
+        :close-on-press-escape="false"
+        :close-on-click-modal="false"
+    >
+      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="150px" >
+        <el-form-item v-if="state.isAdmin" label="企业:" prop="companyId">
+          <el-select v-model="state.form.companyId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="供应商:" prop="supplierId">
+          <el-select v-model="state.form.supplierId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.supplierList"
+                :key="item.id"
+                :label="item.supplierName"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="文件名称:" prop="fileName">
+          <el-input v-model.trim="state.form.fileName" :readonly="state.title =='查看'" placeholder="文件名称"></el-input>
+        </el-form-item>
+        <el-form-item label="文件:" prop="filePath">
+          <el-upload :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" >
+            <el-button type="primary">点击上传</el-button>
+            <template #tip>
+              <div class="el-upload__tip">文件尺寸小于5M,最多可上传1份</div>
+            </template>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <template #footer v-if="state.title !='查看'">
+        <span class="dialog-footer">
+            <el-button @click="handleClose" size="default">取 消</el-button>
+            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
+import {ElMessage} from "element-plus";
+import {addUser, editUser, getUserById, resetPwd} from "@/api/onlineEducation/user"
+import {Base64} from "js-base64"
+import {getCompany} from "@/api/onlineEducation/company";
+import {updateInfoPlatforms} from "@/api/staffManage/staff";
+import {getSupplierPage} from "@/api/supplier/supplierList";
+import {updateOutsourcedCommon, updateOutsourcedProductNameList} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {delPic} from "@/api/onlineEducation/banner";
+import {getToken} from "@/utils/auth";
+
+const emit = defineEmits(["getList"]);
+const dialogVisible = ref(false)
+const checkFiles = (rule, value, callback) => {
+  if (state.fileList.length == 0) {
+    callback(new Error('请上传文件'))
+  } else {
+    callback()
+  }
+}
+const superRef = ref()
+const state = reactive({
+  title: '',
+  form: {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    fileName: '',
+    filePath: '',
+    format: '',
+    osType: 1
+  },
+  formRules:{
+    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
+    supplierId: [{ required: true, message: '请选择供应商', trigger: 'blur' }],
+    filePath: [{ required: true, validator: checkFiles, trigger: 'blur' }]
+  },
+  isAdmin: false,
+  companyList: [],
+  supplierList: [],
+  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
+  header: {
+    Authorization: getToken()
+  },
+  fileLimit: 1,
+  fileList: []
+})
+onMounted(() => {
+
+});
+
+const openDialog = async (type, value,companyId, isAdmin, companyList) => {
+  state.isAdmin = isAdmin
+  if(isAdmin){
+    state.companyList = companyList
+  }
+  await getSupplierList()
+  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看'
+  state.form.companyId = companyId
+  if(state.title == '编辑'||state.title == '查看'){
+    Object.keys(state.form).forEach(key => {
+      if (key in value) {
+        state.form[key] = value[key]
+      }
+    })
+    if(value.filePath) {
+      const obj = {
+        url: value.filePath,
+        name: '工艺流程图'
+      }
+      state.fileList = [obj]
+    }
+  }
+  dialogVisible.value = true
+}
+
+const getSupplierList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getSupplierPage(queryParams)
+  if (res.code == 200) {
+    state.supplierList = res.data.list?res.data.list:[]
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const onSubmit = async () => {
+  const valid = await superRef.value.validate();
+  if(valid){
+    let data = {}
+    if(state.title == '新增'){
+      data = {
+        companyId: state.form.companyId,
+        fileName: state.form.fileName,
+        supplierId: state.form.supplierId,
+        supplierName: state.supplierList.find(i=>i.id == state.form.supplierId)?.supplierName,
+        filePath: state.form.filePath,
+        format: state.form.format,
+        osType: 1
+      }
+    }else{
+      data = state.form
+    }
+      const res = await updateOutsourcedCommon(data)
+      if(res.code == 200){
+        ElMessage.success(res.message)
+        emit('getList')
+        handleClose()
+        dialogVisible.value = false;
+      }else{
+        ElMessage.warning(res.message)
+      }
+
+  }
+}
+
+const handleClose = () => {
+  state.form = {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    fileName: '',
+    filePath: '',
+    format: '',
+    osType: 1
+  }
+  state.fileList = []
+  superRef.value.clearValidate();
+  superRef.value.resetFields()
+  dialogVisible.value = false;
+}
+
+const handleAvatarSuccess = (res, uploadFile) => {
+  if(res.code == 200){
+    state.form.filePath = res.data.path
+    state.form.format = '.' + res.data.filename.split('.')[1]
+  }else{
+    state.fileList = []
+    ElMessage({
+      type: 'warning',
+      message: '文件上传失败'
+    })
+  }
+}
+
+const showTip =()=>{
+  ElMessage({
+    type: 'warning',
+    message: '超出文件上传数量'
+  });
+}
+const picSize = async (rawFile) => {
+  if(rawFile.size / 1024 / 1024 > 5){
+    ElMessage({
+      type: 'warning',
+      message: '文件大小不能超过5M'
+    });
+    return false
+  }
+};
+const handleRemove = async (file, uploadFiles) => {
+  let path = state.form.filePath;
+  await delPic({path: path}).then(res => {
+    if(res.code == 200){
+      // ElMessage({
+      //   type: 'success',
+      //   message: '文件已删除'
+      // })
+      state.form.filePath = ''
+      state.form.format = ''
+    }else{
+      ElMessage({
+        type: 'warning',
+        message: res.message
+      })
+    }
+  }).catch(() => {
+    state.form.imgUrl = ''
+  });
+}
+
+defineExpose({
+  openDialog
+});
+
+</script>
+
+<style scoped lang="scss">
+.notice{
+  :deep(.el-form .el-form-item__label) {
+    font-size: 15px;
+  }
+  .file {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+</style>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/index.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/index.vue
new file mode 100644
index 0000000..1579d17
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessFlow/index.vue
@@ -0,0 +1,215 @@
+<template>
+  <div class="app-container">
+    <div style="display: flex;justify-content: space-between">
+      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
+        <el-form-item>
+          <el-button
+              type="primary"
+              plain
+              icon="Plus"
+              @click="openDialog('add',{})"
+          >新增</el-button>
+        </el-form-item>
+        <el-form-item v-if="isAdmin" label="企业:" >
+          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
+            <el-option
+                v-for="item in companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item >
+          <el-button v-if="isAdmin" type="primary" @click="getList">查询</el-button>
+          <el-button v-if="isAdmin" type="primary" plain @click="reset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 表格数据 -->
+    <el-table v-loading="loading" :data="dataList" :border="true">
+      <el-table-column label="序号" type="index" align="center" width="80"/>
+      <el-table-column label="供应商" prop="supplierName" align="center"/>
+      <el-table-column label="工艺流程图" align="center">
+        <template #default="scope">
+          <el-link type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName !== '' ?scope.row.fileName + scope.row.format : '工艺流程图'}}</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" >
+        <template #default="scope">
+          <el-button link type="primary" @click="downloadFile(scope.row.filePath)">下载</el-button>
+          <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+    />
+
+    <edit-dialog ref="dialogRef" @getList=getList></edit-dialog>
+  </div>
+</template>
+
+<script setup>
+import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {delCompany, getCompany} from "@/api/onlineEducation/company";
+import Cookies from "js-cookie";
+import editDialog from './components/editDialog.vue'
+import useUserStore from "@/store/modules/user";
+import {
+  delOutsourcedCommon,
+  delOutsourcedProductNameList, getOutsourcedCommonList,
+  getOutsourcedProductNameList
+} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {renderAsync} from "docx-preview";
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const dialogRef = ref();
+const data = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null,
+    osType: 1
+  },
+  total: 0,
+  dataList: [],
+  companyList: [],
+  isAdmin: false
+});
+
+const { queryParams, total, dataList,companyList, isAdmin } = toRefs(data);
+const userInfo = ref()
+onMounted(async ()=>{
+  userInfo.value = JSON.parse(Cookies.get('userInfo'))
+  if(userStore.roles.includes('admin')){
+    data.isAdmin = true
+    await getCompanyList()
+  }else{
+    data.isAdmin = false
+    data.queryParams.companyId = userStore.companyId
+  }
+  await getList()
+})
+
+onUnmounted(()=>{
+
+})
+
+const getList = async () => {
+  loading.value = true
+  const res = await getOutsourcedCommonList(data.queryParams)
+  if(res.code == 200){
+    data.dataList = res.data.list || []
+    data.total = res.data.total
+  }else{
+    ElMessage.warning(res.message)
+  }
+  loading.value = false
+}
+
+const getCompanyList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getCompany(queryParams)
+  if (res.code == 200) {
+    data.companyList = res.data.list?res.data.list:[]
+    // data.queryParams.companyId = data.companyList[0].id
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const downloadFile = (path)=>{
+  window.open(import.meta.env.VITE_APP_BASE_API + '/' + path)
+}
+
+const openFile = async(path)=>{
+  const ext = path.split('.').pop().toLowerCase();
+  if (ext === 'doc' || ext === 'pptx' || ext === 'ppt') {
+    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
+      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+    }).catch(() => {
+      console.log('取消预览')
+    });
+    return
+  }else if(ext === 'pdf' || ext === 'jpg' || ext === 'jpeg' || ext === 'png' || ext === 'cad'){
+    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+  }else{
+    try {
+      // 1. 获取文件
+      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
+      const arrayBuffer = await response.arrayBuffer();
+      // 2. 创建新窗口
+      const win = window.open('', '_blank');
+      win.document.write(`
+      <!DOCTYPE html>
+      <html>
+        <head>
+          <title>预览</title>
+          <style>
+            body { margin: 20px; font-family: Arial; }
+            .docx-container { width: 100%; height: 100%; }
+          </style>
+        </head>
+        <body>
+          <div id="container" class="docx-container"></div>
+        </body>
+      </html>
+    `);
+      // 3. 渲染 DOCX
+      await renderAsync(arrayBuffer, win.document.getElementById('container'));
+
+    } catch (error) {
+      console.error('预览失败:', error);
+      alert(`预览失败: ${error.message}`);
+    }
+  }
+}
+
+const openDialog = (type, value) => {
+  dialogRef.value.openDialog(type, value, data.queryParams.companyId, data.isAdmin, data.companyList );
+}
+
+/** 重置新增的表单以及其他数据  */
+const reset= async()=> {
+  data.queryParams = {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null,
+    osType: 1
+  }
+  await getCompanyList()
+  await getList()
+}
+const handleDelete = (val) => {
+  ElMessageBox.confirm(
+      '确定删除此条数据?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+      .then( async() => {
+        const res = await delOutsourcedCommon({id: val.id})
+        if(res.code == 200){
+          ElMessage.success('数据删除成功')
+          await getList()
+        }else{
+          ElMessage.warning(res.message)
+        }
+      })
+}
+
+</script>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/components/editDialog.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/components/editDialog.vue
new file mode 100644
index 0000000..26f5958
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/components/editDialog.vue
@@ -0,0 +1,603 @@
+<template>
+  <div class="notice">
+    <el-dialog
+        v-model="dialogVisible"
+        :title="state.title"
+        width="50%"
+        :before-close="handleClose"
+        :close-on-press-escape="false"
+        :close-on-click-modal="false"
+    >
+      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="180px" >
+        <el-form-item v-if="state.isAdmin" label="企业:" prop="companyId">
+          <el-select v-model="state.form.companyId" placeholder="请选择" :disabled="state.title =='查看'" clearable @change="getDeptList">
+            <el-option
+                v-for="item in state.companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="评审表名称:" prop="reviewName">
+          <el-input v-model.trim="state.form.reviewName" :readonly="state.title =='查看'"></el-input>
+        </el-form-item>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="产品名称:" prop="productName">
+              <el-input v-model.trim="state.form.productName" :readonly="state.title =='查看'"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="申请部门:" prop="deptId">
+              <el-select
+                  clearable
+                  v-model="state.form.deptId"
+                  :disabled="state.title =='查看'"
+                  filterable
+                  placeholder="选择部门"
+                  style="width: 100%"
+              >
+                <el-option
+                    v-for="item in state.deptList"
+                    :key="item.deptId"
+                    :label="item.deptName"
+                    :value="item.deptId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="外包过程名称:" prop="outsourcedProcess">
+              <el-input v-model.trim="state.form.outsourcedProcess" :readonly="state.title =='查看'"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="部门负责人:" prop="deptLeaderId">
+              <el-select clearable v-model="state.form.deptLeaderId" :disabled="state.title =='查看'" filterable placeholder="部门负责人" style="width: 100%">
+                <el-option
+                    v-for="item in state.userList"
+                    :key="item.userId"
+                    :label="item.name"
+                    :value="item.userId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="是否为关键件、重要件:" prop="pieceType" >
+          <el-radio-group v-model="state.form.pieceType" :disabled="state.title === '查看'">
+            <el-radio :label="1">关键件</el-radio>
+            <el-radio :label="2">重要件</el-radio>
+            <el-radio :label="3">普通件</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="过程重要性:" prop="processType" >
+          <el-radio-group v-model="state.form.processType" :disabled="state.title === '查看'">
+            <el-radio :label="1">关键</el-radio>
+            <el-radio :label="2">一般</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="过程外包的必要性:" prop="necessity" >
+          <el-checkbox-group v-model="state.form.necessity">
+            <el-checkbox label="1">本单位没有能力满足产品实现需求</el-checkbox>
+            <el-checkbox label="2">生产急需,利用外部资源实现产品</el-checkbox>
+            <el-checkbox label="3">其他</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="过程外包的可行性:" prop="feasibility" >
+          <el-checkbox-group v-model="state.form.feasibility">
+            <el-checkbox label="1" value="1">本单位可提供现成的图纸、技术资料</el-checkbox>
+            <el-checkbox label="2" value="2">本单位可提供合格的原材料</el-checkbox>
+            <el-checkbox label="3" value="3">本单位有能力对供方产品实现过程实施控制</el-checkbox>
+            <el-checkbox label="4" value="4">其他</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="风险识别:" prop="riskRecognition" >
+          <el-checkbox-group v-model="state.form.riskRecognition">
+            <el-checkbox label="1" value="1">外包过程对我所产品质量有很大影响</el-checkbox>
+            <el-checkbox label="2" value="2">外包产品供货影响我所生产进度</el-checkbox>
+            <el-checkbox label="3" value="3">实施外包对我所经济效益有影响</el-checkbox>
+            <el-checkbox label="4" value="4">其他</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="控制措施:"></el-form-item>
+        <el-form-item label="控制质量影响的措施:" prop="qualityMeasures" >
+          <el-checkbox-group v-model="state.form.qualityMeasures">
+            <el-checkbox label="1" value="1">进货检验</el-checkbox>
+            <el-checkbox label="2" value="2">我司到供方现场验证</el-checkbox>
+            <el-checkbox label="3" value="3">顾客到供方现场验证</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="控制影响进度的措施:" prop="progressMeasures" >
+          <el-checkbox-group v-model="state.form.progressMeasures">
+            <el-checkbox label="1" value="1">与供方及时沟通</el-checkbox>
+            <el-checkbox label="2" value="2">要求供方列出进度计划</el-checkbox>
+            <el-checkbox label="3" value="3">纳入协同合作采取经济制约</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="控制经济影响的措施:" prop="economicMeasure" >
+          <el-checkbox-group v-model="state.form.economicMeasure">
+            <el-checkbox label="1" value="1">我司财务预算</el-checkbox>
+            <el-checkbox label="2" value="2">在预算范围内外包</el-checkbox>
+            <el-checkbox label="3" value="3">与供方协商解决</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="拟协作单位:" prop="outsourcedReviewSubsidiaryList">
+          <el-button
+              type="primary"
+              plain
+              icon="Plus"
+              @click="addLine"
+              style="margin-bottom: 10px"
+              v-if="state.title !=='查看'"
+          >新增</el-button>
+        </el-form-item>
+
+        <el-table :data="state.form.outsourcedReviewSubsidiaryList" class="customedTable" :border="true">
+          <el-table-column label="序号" type="index" width="80" align="center"></el-table-column>
+          <el-table-column label="拟协作单位名称" prop="assistName" align="center">
+            <template #default="scope">
+              <el-input
+                  v-model.trim="scope.row.assistName"
+                  size="large"
+                  type="textarea"
+                  style="width: 100%;"
+                  clearable
+                  :readonly="state.title =='查看'"
+              >
+              </el-input>
+            </template>
+          </el-table-column>
+          <el-table-column label="拟协作单位地址" prop="assistAddress" align="center">
+            <template #default="scope">
+              <el-input
+                  v-model.trim="scope.row.assistAddress"
+                  size="large"
+                  type="textarea"
+                  style="width: 100%;"
+                  clearable
+                  :readonly="state.title =='查看'"
+              >
+              </el-input>
+            </template>
+          </el-table-column>
+          <el-table-column label="电话" prop="phone" align="center">
+            <template #default="scope">
+              <el-input
+                  v-model.trim="scope.row.phone"
+                  size="large"
+                  type="textarea"
+                  style="width: 100%;"
+                  clearable
+                  :readonly="state.title =='查看'"
+              >
+              </el-input>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="70" v-if="state.title !=='查看'">
+            <template #default="scope">
+              <el-button link type="danger" @click="handleDelete(scope.$index)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-form-item label="评审意见及签署:" prop="outsourceType" >
+          <el-radio-group v-model="state.form.outsourceType" :disabled="state.title === '查看'">
+            <el-radio :label="1">可以外包</el-radio>
+            <el-radio :label="2">不可以外包,由部门采取措施解决。措施内容:</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item prop="measure" v-if="state.form.outsourceType == 2">
+          <el-input
+              v-model.trim="state.form.measure"
+              size="large"
+              type="textarea"
+              style="width: 100%;"
+              clearable
+              :readonly="state.title =='查看'"
+          >
+          </el-input>
+        </el-form-item>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="申请部门:" prop="applyDept">
+              <el-input v-model.trim="state.form.applyDept" :readonly="state.title =='查看'"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="applyDeptDate">
+              <el-date-picker
+                  v-model="state.form.applyDeptDate"
+                  type="date"
+                  value-format="YYYY-MM-DD"
+                  placeholder="请选择日期"
+                  :disabled="state.title =='查看'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="质检部:" prop="qualityInspection">
+              <el-input v-model.trim="state.form.qualityInspection" :readonly="state.title =='查看'"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="qualityInspectionDate">
+              <el-date-picker
+                  v-model="state.form.qualityInspectionDate"
+                  type="date"
+                  value-format="YYYY-MM-DD"
+                  placeholder="请选择日期"
+                  :disabled="state.title =='查看'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="管理者代表:" prop="managementRepresentative">
+              <el-input v-model.trim="state.form.managementRepresentative" :readonly="state.title =='查看'"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="managementRepresentativeDate">
+              <el-date-picker
+                  v-model="state.form.managementRepresentativeDate"
+                  type="date"
+                  value-format="YYYY-MM-DD"
+                  placeholder="请选择日期"
+                  :disabled="state.title =='查看'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="批准意见:" prop="approvalOpinions">
+          <el-input
+              v-model.trim="state.form.approvalOpinions"
+              size="large"
+              type="textarea"
+              style="width: 100%"
+              :autosize="{minRows: 3}"
+              :readonly="state.title =='查看'"
+          >
+          </el-input>
+        </el-form-item>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="主管领导:" prop="executive">
+              <el-input v-model.trim="state.form.executive" :readonly="state.title =='查看'"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="executiveDate">
+              <el-date-picker
+                  v-model="state.form.executiveDate"
+                  type="date"
+                  value-format="YYYY-MM-DD"
+                  placeholder="请选择日期"
+                  :disabled="state.title =='查看'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer v-if="state.title !='查看'">
+        <span class="dialog-footer">
+            <el-button @click="handleClose" size="default">取 消</el-button>
+            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
+import {ElMessage} from "element-plus";
+import {getToken} from "@/utils/auth";
+import {
+  updateMaintenanceEvaluate,
+  getMaintenanceEvaluateDetail
+} from "@/api/infrastructureMng/ledger";
+
+import {listUser} from "@/api/system/user";
+import {getOutsourcedReviewDetail, updateOutsourcedReview} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {getDepart} from "@/api/orgStructure/depart";
+
+const emit = defineEmits(["getList"]);
+const dialogVisible = ref(false)
+const superRef = ref()
+const checkList = (rule, value, callback) => {
+  if (state.form.outsourcedReviewSubsidiaryList.length == 0) {
+    callback(new Error('协作单位数据不可为空'))
+  } else if(state.form.outsourcedReviewSubsidiaryList.length > 0 && state.form.outsourcedReviewSubsidiaryList.find(item=>item.assistName == '' || item.assistAddress == '' ||item.phone == '')){
+    callback(new Error('协作单位数据未完善'))
+  } else {
+    callback()
+  }
+}
+const state = reactive({
+  title: '',
+  form: {
+    id: null,
+    companyId: null,
+    reviewName: '',
+    productName: '',
+    deptId: null,
+    outsourcedProcess: '',
+    deptLeaderId: null,
+    deptName: '',
+    deptLeaderName: '',
+    pieceType: null,
+    processType: null,
+    necessity: [],
+    feasibility: [],
+    riskRecognition: [],
+    qualityMeasures: [],
+    progressMeasures: [],
+    economicMeasure: [],
+    outsourcedReviewSubsidiaryList: [],
+    delOutsourcedReviewSubsidiaryIds: [],
+    outsourceType: null,
+    measure: '',
+    applyDept: '',
+    applyDeptDate: '',
+    qualityInspection: '',
+    qualityInspectionDate: '',
+    managementRepresentative: '',
+    managementRepresentativeDate: '',
+    approvalOpinions: '',
+    executive: '',
+    executiveDate: '',
+  },
+  oldDeviceList: [],
+  formRules:{
+    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
+    reviewName: [{ required: true, message: '请填写评审表名称', trigger: 'blur' }],
+    deptId: [{ required: true, message: '请选择申请部门', trigger: 'blur' }],
+    productName: [{ required: true, message: '请填写产品名称', trigger: 'blur' }],
+    outsourcedProcess: [{ required: true, message: '请填写外包过程名称', trigger: 'blur' }],
+    deptLeaderId: [{ required: true, message: '请选择部门负责人', trigger: 'blur' }],
+    pieceType: [{ required: true, message: '请选择是否为关键件、重要件', trigger: 'blur' }],
+    processType: [{ required: true, message: '请选择过程重要性', trigger: 'blur' }],
+    necessity: [{ required: true, message: '请选择过程外包必要性', trigger: 'blur' }],
+    feasibility: [{ required: true, message: '请选择过程外包可行性', trigger: 'blur' }],
+    riskRecognition: [{ required: true, message: '请选择风险识别', trigger: 'blur' }],
+    qualityMeasures: [{ required: true, message: '请选择控制措施', trigger: 'blur' }],
+    progressMeasures: [{ required: true, message: '请选择控制措施', trigger: 'blur' }],
+    economicMeasure: [{ required: true, message: '请选择控制措施', trigger: 'blur' }],
+    outsourcedReviewSubsidiaryList: [{ required: true, validator: checkList, trigger: 'blur' }],
+    outsourceType: [{ required: true, message: '请选择评审意见', trigger: 'blur' }],
+    measure: [{ required: true, message: '请填写措施内容', trigger: 'blur' }],
+    applyDept: [{ required: true, message: '请填写申请部门', trigger: 'blur' }],
+    applyDeptDate: [{ required: true, message: '请选择日期', trigger: 'blur' }],
+    qualityInspection: [{ required: true, message: '请填写质检部', trigger: 'blur' }],
+    qualityInspectionDate: [{ required: true, message: '请选择日期', trigger: 'blur' }],
+    managementRepresentative: [{ required: true, message: '请填写管理者代表', trigger: 'blur' }],
+    managementRepresentativeDate: [{ required: true, message: '请选择日期', trigger: 'blur' }],
+    executive: [{ required: true, message: '请填写主管领导', trigger: 'blur' }],
+    executiveDate: [{ required: true, message: '请选择日期', trigger: 'blur' }]
+  },
+  isAdmin: false,
+  companyList: [],
+  deptList: [],
+  userList: []
+})
+onMounted(() => {
+
+});
+
+const openDialog = async (type, value,companyId, isAdmin, companyList) => {
+  state.isAdmin = isAdmin
+  if(isAdmin){
+    state.companyList = companyList
+  }
+  await getDepartList(companyId)
+  await getUserList(companyId)
+  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看'
+  state.form.companyId = companyId
+  if(state.title == '编辑'||state.title == '查看'){
+    await getInfo(value.id)
+  }
+  dialogVisible.value = true
+}
+
+const addLine = () => {
+  const obj = {
+    id: null,
+    reviewId: null,
+    assistName: '',
+    assistAddress: '',
+    phone: ''
+  }
+  state.form.outsourcedReviewSubsidiaryList.push(obj);
+}
+
+const handleDelete = (i) =>{
+  state.form.outsourcedReviewSubsidiaryList = state.form.outsourcedReviewSubsidiaryList.filter((item,index) => index != i)
+}
+
+const onSubmit = async () => {
+  const valid = await superRef.value.validate();
+  if(valid){
+    const data = JSON.parse(JSON.stringify(state.form))
+    console.log(data,'data')
+    data.delOutsourcedReviewSubsidiaryIds = state.oldDeviceList.filter(i =>!data.outsourcedReviewSubsidiaryList.some(item=>item.id == i.id)).map(i=>i.id)
+    data.necessity = data.necessity.join(',')
+    data.feasibility = data.feasibility.join(',')
+    data.riskRecognition = data.riskRecognition.join(',')
+    data.qualityMeasures = data.qualityMeasures.join(',')
+    data.progressMeasures = data.progressMeasures.join(',')
+    data.economicMeasure = data.economicMeasure.join(',')
+    data.deptName = state.deptList.find(i=>i.deptId == data.deptId)?.deptName
+    data.deptLeaderName = state.userList.find(i=>i.userId == data.deptLeaderId)?.name
+    if(data.outsourceType == 1){
+      data.measure = ''
+    }
+    if(state.title == '新增'){
+      delete data.id
+      const res = await updateOutsourcedReview(data)
+      if(res.code == 200){
+        ElMessage.success(res.message)
+        emit('getList')
+        handleClose()
+        dialogVisible.value = false;
+      }else{
+        ElMessage.warning(res.message)
+      }
+    }else{
+      const res = await updateOutsourcedReview(data)
+      if(res.code == 200){
+        ElMessage.success(res.message)
+        emit('getList')
+        handleClose()
+        dialogVisible.value = false;
+      }else{
+        ElMessage.warning(res.message)
+      }
+    }
+  }
+}
+
+const getInfo = async (id)=> {
+  const res = await getOutsourcedReviewDetail({id: id})
+  if(res.code == 200){
+    Object.keys(state.form).forEach(key => {
+      if (key in res.data) {
+        state.form[key] = res.data[key]
+      }
+    })
+    state.form.outsourcedReviewSubsidiaryList = res.data.outsourcedReviewSubsidiaryList?.map(item=>{
+      return {
+        id: item.id,
+        reviewId: item.reviewId,
+        assistName: item.assistName,
+        assistAddress: item.assistAddress,
+        phone: item.phone
+      }
+    })
+    state.oldDeviceList = state.form.outsourcedReviewSubsidiaryList
+    state.form.necessity = state.form.necessity.split(',')
+    state.form.feasibility = state.form.feasibility.split(',')
+    state.form.riskRecognition = state.form.riskRecognition.split(',')
+    state.form.qualityMeasures = state.form.qualityMeasures.split(',')
+    state.form.progressMeasures =state.form.progressMeasures.split(',')
+    state.form.economicMeasure = state.form.economicMeasure.split(',')
+    // state.form.applyDeptDate = state.form.applyDeptDate.substring(0,10)
+    // state.form.qualityInspectionDate = state.form.qualityInspectionDate.substring(0,10)
+    // state.form.managementRepresentativeDate = state.form.managementRepresentativeDate.substring(0,10)
+    // state.form.executiveDate = state.form.executiveDate.substring(0,10)
+  }else{
+    ElMessage.warning(res.message)
+  }
+}
+
+const getDeptList = async ()=>{
+  state.form.deptId = null
+  state.form.deptLeaderId = null
+  await getDepartList(state.form.companyId)
+  await getUserList(state.form.companyId)
+}
+
+const getDepartList = async (companyId)=> {
+  const params = {
+    companyId: companyId
+  }
+  const res = await getDepart(params)
+  if(res.code == 200){
+    state.deptList = res.data
+  }else{
+    ElMessage.warning(res.message)
+  }
+}
+
+const getUserList = async (companyId)=> {
+  const res = await listUser({pageIndex: 1,pageSize: 999,companyId: companyId})
+  if(res.code == 200){
+    state.userList = res.data.list?res.data.list.map(item=>{
+      const user = item.id
+      const {id, ...data} = item
+      return {
+        ...data,
+        userId: user
+      }
+    }):[]
+  }else{
+    ElMessage.warning(res.message)
+  }
+}
+
+const handleClose = () => {
+  state.form = {
+    id: null,
+    companyId: null,
+    reviewName: '',
+    productName: '',
+    deptId: '',
+    outsourcedProcess: '',
+    deptLeaderId: '',
+    deptName: '',
+    deptLeaderName: '',
+    pieceType: null,
+    processType: null,
+    necessity: [],
+    feasibility: [],
+    riskRecognition: [],
+    qualityMeasures: [],
+    progressMeasures: [],
+    economicMeasure: [],
+    outsourcedReviewSubsidiaryList: [],
+    delOutsourcedReviewSubsidiaryIds: [],
+    outsourceType: null,
+    measure: '',
+    applyDept: '',
+    applyDeptDate: '',
+    qualityInspection: '',
+    qualityInspectionDate: '',
+    managementRepresentative: '',
+    managementRepresentativeDate: '',
+    approvalOpinions: '',
+    executive: '',
+    executiveDate: '',
+  }
+  superRef.value.clearValidate();
+  superRef.value.resetFields()
+  dialogVisible.value = false;
+}
+
+defineExpose({
+  openDialog
+});
+
+</script>
+
+<style scoped lang="scss">
+.notice{
+  :deep(.el-form .el-form-item__label) {
+    font-size: 15px;
+  }
+  .file {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+</style>
+<style lang="scss">
+.customedTable{
+  width: calc(100% - 150px);
+  margin-left: 150px;
+  margin-bottom: 30px;
+  .el-table__cell{
+    padding: 2px 0 !important;
+    font-size: 14px !important;
+  }
+  .cell{
+    padding: 0 2px !important;
+    font-size: 14px !important;
+  }
+}
+</style>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/index.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/index.vue
new file mode 100644
index 0000000..e59ab28
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/index.vue
@@ -0,0 +1,299 @@
+<template>
+  <div class="app-container">
+    <div style="display: flex;justify-content: space-between">
+      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
+        <el-form-item>
+          <el-button
+              type="primary"
+              plain
+              icon="Plus"
+              @click="openDialog('add',{})"
+          >新增</el-button>
+        </el-form-item>
+        <el-form-item v-if="isAdmin" label="企业:" >
+          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
+            <el-option
+                v-for="item in companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item >
+          <el-button v-if="isAdmin" type="primary" @click="getList">查询</el-button>
+          <el-button v-if="isAdmin" type="primary" plain @click="reset">重置</el-button>
+<!--          <el-button type="primary">导出</el-button>-->
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 表格数据 -->
+    <el-table v-loading="loading" :data="dataList" :border="true">
+      <el-table-column type="index" label="序号"></el-table-column>
+      <el-table-column prop="reviewName" align="center" label="评审表名称">
+        <template #default="scope">
+          {{scope.row.reviewName}}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button link type="primary" @click="openDialog('view',scope.row)">查看</el-button>
+          <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button>
+          <el-button link type="primary" @click="downloadFile(scope.row)">导出</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+    />
+
+    <edit-dialog ref="dialogRef" @getList=getList></edit-dialog>
+  </div>
+</template>
+
+<script setup>
+import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {delCompany, getCompany} from "@/api/onlineEducation/company";
+import Cookies from "js-cookie";
+import editDialog from './components/editDialog.vue'
+import useUserStore from "@/store/modules/user";
+import {generateWordDocument} from "@/utils/exportWord";
+import {
+  delMaintenanceEvaluate,
+  getMaintenanceEvaluateDetail,
+  getMaintenanceEvaluateList
+} from "@/api/infrastructureMng/ledger";
+import {
+  delOutsourcedReview,
+  getOutsourcedReviewDetail,
+  getOutsourcedReviewList
+} from "@/api/outsourcingCooperate/outsourcingCooperate";
+
+
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const dialogRef = ref();
+const data = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null
+  },
+  total: 0,
+  dataList: [],
+  companyList: [],
+  isAdmin: false,
+  keyList: [
+    {id: 1, label: '关键件'},
+    {id: 2, label: '重要件'},
+    {id: 3, label: '普通件'}
+  ],
+  vitalList: [
+    {id: 1, label: '关键'},
+    {id: 2, label: '一般'}
+  ],
+  necessaryList: [
+    {id: '1', label: '本单位没有能力满足产品实现需求'},
+    {id: '2', label: '生产急需,利用外部资源实现产品'},
+    {id: '3', label: '其他'}
+  ],
+  workList: [
+    {id: '1', label: '本单位可提供现成的图纸、技术资料'},
+    {id: '2', label: '本单位可提供合格的原材料'},
+    {id: '3', label: '本单位有能力对供方产品实现过程实施控制'},
+    {id: '4', label: '其他'}
+  ],
+  riskList: [
+    {id: '1', label: '外包过程对我所产品质量有很大影响'},
+    {id: '2', label: '外包产品供货影响我所生产进度'},
+    {id: '3', label: '实施外包对我所经济效益有影响'},
+    {id: '4', label: '其他'}
+  ],
+  qualityList: [
+    {id: '1', label: '进货检验'},
+    {id: '2', label: '我司到供方现场验证'},
+    {id: '3', label: '顾客到供方现场验证'}
+  ],
+  processList: [
+    {id: '1', label: '与供方及时沟通'},
+    {id: '2', label: '要求供方列出进度计划'},
+    {id: '3', label: '纳入协同合作采取经济制约'}
+  ],
+  economyList: [
+    {id: '1', label: '我司财务预算'},
+    {id: '2', label: '在预算范围内外包'},
+    {id: '3', label: '与供方协商解决'}
+  ],
+  signList: [
+    {id: 1, label: '可以外包'},
+    {id: 2, label: '不可以外包,由部门采取措施解决。措施内容:'},
+  ]
+});
+
+const { queryParams, total, dataList,companyList, isAdmin } = toRefs(data);
+const userInfo = ref()
+onMounted(async ()=>{
+  if(userStore.roles.includes('admin')){
+    data.isAdmin = true
+    await getCompanyList()
+  }else{
+    data.isAdmin = false
+    data.queryParams.companyId = userStore.companyId
+  }
+  await getList()
+})
+
+onUnmounted(()=>{
+
+})
+
+const getList = async () => {
+  loading.value = true
+  const res = await getOutsourcedReviewList(data.queryParams)
+  if(res.code == 200){
+    data.dataList = res.data.list || []
+    data.total = res.data.total
+  }else{
+    ElMessage.warning(res.message)
+  }
+  loading.value = false
+}
+
+const getCompanyList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getCompany(queryParams)
+  if (res.code == 200) {
+    data.companyList = res.data.list?res.data.list:[]
+    // data.queryParams.companyId = data.companyList[0].id
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const downloadFile = async (val)=>{
+  const res = await getOutsourcedReviewDetail({id: val.id})
+  if(res.code == 200){
+    if(res.data){
+      let tableData = res.data
+      tableData.reviewList = res.data.outsourcedReviewSubsidiaryList
+      tableData.keyList = data.keyList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.pieceType == item.id ? false: true
+        }
+      })
+      tableData.vitalList = data.vitalList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.processType == item.id ? false: true
+        }
+      })
+      tableData.necessaryList = data.necessaryList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.necessity.split(',').includes(item.id) ? false: true
+        }
+      })
+      tableData.workList = data.workList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.feasibility.split(',').includes(item.id) ? false: true
+        }
+      })
+      tableData.riskList = data.riskList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.riskRecognition.split(',').includes(item.id) ? false: true
+        }
+      })
+      tableData.qualityList = data.qualityList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.qualityMeasures.split(',').includes(item.id) ? false: true
+        }
+      })
+      tableData.processList = data.processList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.progressMeasures.split(',').includes(item.id) ? false: true
+        }
+      })
+      tableData.economyList = data.economyList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.economicMeasure.split(',').includes(item.id) ? false: true
+        }
+      })
+      tableData.signList = data.signList.map(item=>{
+        return {
+          ...item,
+          checked: tableData.outsourceType == item.id ? false: true
+        }
+      })
+      tableData.applyDeptDate = tableData.applyDeptDate.replace(/^(\d{4})-(\d{1,2})-(\d{1,2}).*$/, (match, year, month, day) => `${year}年${parseInt(month)}月${parseInt(day)}日`)
+      tableData.qualityInspectionDate = tableData.qualityInspectionDate.replace(/^(\d{4})-(\d{1,2})-(\d{1,2}).*$/, (match, year, month, day) => `${year}年${parseInt(month)}月${parseInt(day)}日`)
+      tableData.managementRepresentativeDate = tableData.managementRepresentativeDate.replace(/^(\d{4})-(\d{1,2})-(\d{1,2}).*$/, (match, year, month, day) => `${year}年${parseInt(month)}月${parseInt(day)}日`)
+      tableData.executiveDate = tableData.executiveDate.replace(/^(\d{4})-(\d{1,2})-(\d{1,2}).*$/, (match, year, month, day) => `${year}年${parseInt(month)}月${parseInt(day)}日`)
+      tableData.reviewList = res.data.outsourcedReviewSubsidiaryList
+      try {
+        generateWordDocument('/outsourceProcessReview.docx', tableData, tableData.reviewName +'.docx');
+      } catch (error){
+        ElMessage({
+          type: 'warning',
+          message: '导出失败'
+        });
+      }
+    }else{
+      ElMessage.warning('暂无数据')
+    }
+  }else{
+    ElMessage.warning(res.message)
+  }
+}
+
+const openDialog = (type, value) => {
+  dialogRef.value.openDialog(type, value, data.queryParams.companyId, data.isAdmin, data.companyList);
+}
+
+/** 重置新增的表单以及其他数据  */
+const reset= async()=> {
+  data.queryParams = {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null
+  }
+  await getCompanyList()
+  await getList()
+}
+const handleDelete = (val) => {
+  ElMessageBox.confirm(
+      '确定删除此条数据?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+      .then( async() => {
+        const res = await delOutsourcedReview({id: val.id})
+        if(res.code == 200){
+          ElMessage.success('数据删除成功')
+          await getList()
+        }else{
+          ElMessage.warning(res.message)
+        }
+      })
+}
+
+</script>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/components/editDialog.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/components/editDialog.vue
new file mode 100644
index 0000000..4edab53
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/components/editDialog.vue
@@ -0,0 +1,168 @@
+<template>
+  <div class="notice">
+    <el-dialog
+        v-model="dialogVisible"
+        :title="state.title"
+        width="700px"
+        :before-close="handleClose"
+        :close-on-press-escape="false"
+        :close-on-click-modal="false"
+    >
+      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="150px" >
+        <el-form-item v-if="state.isAdmin" label="企业:" prop="companyId">
+          <el-select v-model="state.form.companyId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="供应商:" prop="supplierId">
+          <el-select v-model="state.form.supplierId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.supplierList"
+                :key="item.id"
+                :label="item.supplierName"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="产品名称:" prop="productName">
+          <el-input v-model.trim="state.form.productName" :readonly="state.title =='查看'" placeholder="产品名称"></el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer v-if="state.title !='查看'">
+        <span class="dialog-footer">
+            <el-button @click="handleClose" size="default">取 消</el-button>
+            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
+import {ElMessage} from "element-plus";
+import {addUser, editUser, getUserById, resetPwd} from "@/api/onlineEducation/user"
+import {Base64} from "js-base64"
+import {getCompany} from "@/api/onlineEducation/company";
+import {updateInfoPlatforms} from "@/api/staffManage/staff";
+import {getSupplierPage} from "@/api/supplier/supplierList";
+import {updateOutsourcedProductNameList} from "@/api/outsourcingCooperate/outsourcingCooperate";
+
+const emit = defineEmits(["getList"]);
+const dialogVisible = ref(false)
+const superRef = ref()
+const state = reactive({
+  title: '',
+  form: {
+    id: null,
+    companyId: null,
+    productName: '',
+    supplierId: null,
+    supplierName: ''
+  },
+  formRules:{
+    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
+    productName: [{ required: true, message: '请输入外包产品名称', trigger: 'blur' }],
+    supplierId: [{ required: true, message: '请选择供应商', trigger: 'blur' }]
+  },
+  isAdmin: false,
+  companyList: [],
+  supplierList: []
+})
+onMounted(() => {
+
+});
+
+const openDialog = async (type, value,companyId, isAdmin, companyList) => {
+  state.isAdmin = isAdmin
+  if(isAdmin){
+    state.companyList = companyList
+  }
+  await getSupplierList()
+  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看'
+  state.form.companyId = companyId
+  if(state.title == '编辑'||state.title == '查看'){
+    Object.keys(state.form).forEach(key => {
+      if (key in value) {
+        state.form[key] = value[key]
+      }
+    })
+  }
+  dialogVisible.value = true
+}
+
+const getSupplierList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getSupplierPage(queryParams)
+  if (res.code == 200) {
+    state.supplierList = res.data.list?res.data.list:[]
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const onSubmit = async () => {
+  const valid = await superRef.value.validate();
+  if(valid){
+    let data = {}
+    if(state.title == '新增'){
+      data = {
+        companyId: state.form.companyId,
+        productName: state.form.productName,
+        supplierId: state.form.supplierId,
+        supplierName: state.supplierList.find(i=>i.id == state.form.supplierId)?.supplierName
+      }
+    }else{
+      data = state.form
+    }
+      const res = await updateOutsourcedProductNameList(data)
+      if(res.code == 200){
+        ElMessage.success(res.message)
+        emit('getList')
+        handleClose()
+        dialogVisible.value = false;
+      }else{
+        ElMessage.warning(res.message)
+      }
+
+  }
+}
+
+const handleClose = () => {
+  state.form = {
+    id: null,
+    companyId: null,
+    productName: '',
+    supplierId: null,
+    supplierName: ''
+  }
+  superRef.value.clearValidate();
+  superRef.value.resetFields()
+  dialogVisible.value = false;
+}
+
+defineExpose({
+  openDialog
+});
+
+</script>
+
+<style scoped lang="scss">
+.notice{
+  :deep(.el-form .el-form-item__label) {
+    font-size: 15px;
+  }
+  .file {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+</style>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/index.vue b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/index.vue
new file mode 100644
index 0000000..f67a308
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/outsourcedProductName/index.vue
@@ -0,0 +1,160 @@
+<template>
+  <div class="app-container">
+    <div style="display: flex;justify-content: space-between">
+      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
+        <el-form-item>
+          <el-button
+              type="primary"
+              plain
+              icon="Plus"
+              @click="openDialog('add',{})"
+          >新增</el-button>
+        </el-form-item>
+        <el-form-item v-if="isAdmin" label="企业:" >
+          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
+            <el-option
+                v-for="item in companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item >
+          <el-button v-if="isAdmin" type="primary" @click="getList">查询</el-button>
+          <el-button v-if="isAdmin" type="primary" plain @click="reset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 表格数据 -->
+    <el-table v-loading="loading" :data="dataList" :border="true">
+      <el-table-column label="序号" type="index" align="center" width="80"/>
+      <el-table-column label="供应商" prop="supplierName" align="center"/>
+      <el-table-column label="产品名称" prop="productName" align="center"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" >
+        <template #default="scope">
+          <el-button link type="primary" @click="openDialog('view',scope.row)">查看</el-button>
+          <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+    />
+
+    <edit-dialog ref="dialogRef" @getList=getList></edit-dialog>
+  </div>
+</template>
+
+<script setup>
+import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {delCompany, getCompany} from "@/api/onlineEducation/company";
+import Cookies from "js-cookie";
+import editDialog from './components/editDialog.vue'
+import useUserStore from "@/store/modules/user";
+import {
+  delOutsourcedProductNameList,
+  getOutsourcedProductNameList
+} from "@/api/outsourcingCooperate/outsourcingCooperate";
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const dialogRef = ref();
+const data = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null
+  },
+  total: 0,
+  dataList: [],
+  companyList: [],
+  isAdmin: false
+});
+
+const { queryParams, total, dataList,companyList, isAdmin } = toRefs(data);
+const userInfo = ref()
+onMounted(async ()=>{
+  userInfo.value = JSON.parse(Cookies.get('userInfo'))
+  if(userStore.roles.includes('admin')){
+    data.isAdmin = true
+    await getCompanyList()
+  }else{
+    data.isAdmin = false
+    data.queryParams.companyId = userStore.companyId
+  }
+  await getList()
+})
+
+onUnmounted(()=>{
+
+})
+
+const getList = async () => {
+  loading.value = true
+  const res = await getOutsourcedProductNameList(data.queryParams)
+  if(res.code == 200){
+    data.dataList = res.data.list || []
+    data.total = res.data.total
+  }else{
+    ElMessage.warning(res.message)
+  }
+  loading.value = false
+}
+
+const getCompanyList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getCompany(queryParams)
+  if (res.code == 200) {
+    data.companyList = res.data.list?res.data.list:[]
+    // data.queryParams.companyId = data.companyList[0].id
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const openDialog = (type, value) => {
+  dialogRef.value.openDialog(type, value, data.queryParams.companyId, data.isAdmin, data.companyList );
+}
+
+/** 重置新增的表单以及其他数据  */
+const reset= async()=> {
+  data.queryParams = {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null
+  }
+  await getCompanyList()
+  await getList()
+}
+const handleDelete = (val) => {
+  ElMessageBox.confirm(
+      '确定删除此条数据?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+      .then( async() => {
+        const res = await delOutsourcedProductNameList({id: val.id})
+        if(res.code == 200){
+          ElMessage.success('数据删除成功')
+          await getList()
+        }else{
+          ElMessage.warning(res.message)
+        }
+      })
+}
+
+</script>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/components/editDialog.vue b/src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/components/editDialog.vue
new file mode 100644
index 0000000..1428041
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/components/editDialog.vue
@@ -0,0 +1,265 @@
+<template>
+  <div class="notice">
+    <el-dialog
+        v-model="dialogVisible"
+        :title="state.title"
+        width="700px"
+        :before-close="handleClose"
+        :close-on-press-escape="false"
+        :close-on-click-modal="false"
+    >
+      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="150px" >
+        <el-form-item v-if="state.isAdmin" label="企业:" prop="companyId">
+          <el-select v-model="state.form.companyId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="供应商:" prop="supplierId">
+          <el-select v-model="state.form.supplierId" placeholder="请选择" :disabled="state.title =='查看'" clearable style="width: 100%">
+            <el-option
+                v-for="item in state.supplierList"
+                :key="item.id"
+                :label="item.supplierName"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="不合格品名称:" prop="unqualifiedProductName">
+          <el-input v-model.trim="state.form.unqualifiedProductName" :readonly="state.title =='查看'" placeholder="不合格品名称"></el-input>
+        </el-form-item>
+        <el-form-item label="文件名称:" prop="fileName">
+          <el-input v-model.trim="state.form.fileName" :readonly="state.title =='查看'" placeholder="文件名称"></el-input>
+        </el-form-item>
+        <el-form-item label="文件:" prop="filePath">
+          <el-upload :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" >
+            <el-button type="primary">点击上传</el-button>
+            <template #tip>
+              <div class="el-upload__tip">文件尺寸小于5M,最多可上传1份</div>
+            </template>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <template #footer v-if="state.title !='查看'">
+        <span class="dialog-footer">
+            <el-button @click="handleClose" size="default">取 消</el-button>
+            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
+import {ElMessage} from "element-plus";
+import {addUser, editUser, getUserById, resetPwd} from "@/api/onlineEducation/user"
+import {Base64} from "js-base64"
+import {getCompany} from "@/api/onlineEducation/company";
+import {updateInfoPlatforms} from "@/api/staffManage/staff";
+import {getSupplierPage} from "@/api/supplier/supplierList";
+import {
+  updateOutsourcedCommon,
+  updateOutsourcedProductNameList,
+  updateOutsourcedUnqualified
+} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {delPic} from "@/api/onlineEducation/banner";
+import {getToken} from "@/utils/auth";
+
+const emit = defineEmits(["getList"]);
+const dialogVisible = ref(false)
+const checkFiles = (rule, value, callback) => {
+  if (state.fileList.length == 0) {
+    callback(new Error('请上传文件'))
+  } else {
+    callback()
+  }
+}
+const superRef = ref()
+const state = reactive({
+  title: '',
+  form: {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    unqualifiedProductName: '',
+    fileName: '',
+    filePath: '',
+    format: ''
+  },
+  formRules:{
+    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
+    supplierId: [{ required: true, message: '请选择供应商', trigger: 'blur' }],
+    unqualifiedProductName: [{ required: true, message: '请填写不合格品名称', trigger: 'blur' }],
+    filePath: [{ required: true, validator: checkFiles, trigger: 'blur' }]
+  },
+  isAdmin: false,
+  companyList: [],
+  supplierList: [],
+  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
+  header: {
+    Authorization: getToken()
+  },
+  fileLimit: 1,
+  fileList: []
+})
+onMounted(() => {
+
+});
+
+const openDialog = async (type, value,companyId, isAdmin, companyList) => {
+  state.isAdmin = isAdmin
+  if(isAdmin){
+    state.companyList = companyList
+  }
+  await getSupplierList()
+  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看'
+  state.form.companyId = companyId
+  if(state.title == '编辑'||state.title == '查看'){
+    Object.keys(state.form).forEach(key => {
+      if (key in value) {
+        state.form[key] = value[key]
+      }
+    })
+    if(value.filePath) {
+      const obj = {
+        url: value.filePath,
+        name: '工艺流程图'
+      }
+      state.fileList = [obj]
+    }
+  }
+  dialogVisible.value = true
+}
+
+const getSupplierList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getSupplierPage(queryParams)
+  if (res.code == 200) {
+    state.supplierList = res.data.list?res.data.list:[]
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const onSubmit = async () => {
+  const valid = await superRef.value.validate();
+  if(valid){
+    let data = {}
+    if(state.title == '新增'){
+      data = {
+        companyId: state.form.companyId,
+        fileName: state.form.fileName,
+        unqualifiedProductName: state.form.unqualifiedProductName,
+        supplierId: state.form.supplierId,
+        supplierName: state.supplierList.find(i=>i.id == state.form.supplierId)?.supplierName,
+        filePath: state.form.filePath,
+        format: state.form.format
+      }
+    }else{
+      data = state.form
+    }
+      const res = await updateOutsourcedUnqualified(data)
+      if(res.code == 200){
+        ElMessage.success(res.message)
+        emit('getList')
+        handleClose()
+        dialogVisible.value = false;
+      }else{
+        ElMessage.warning(res.message)
+      }
+
+  }
+}
+
+const handleClose = () => {
+  state.form = {
+    id: null,
+    companyId: null,
+    supplierId: null,
+    supplierName: '',
+    unqualifiedProductName: '',
+    fileName: '',
+    filePath: '',
+    format: ''
+  }
+  state.fileList = []
+  superRef.value.clearValidate();
+  superRef.value.resetFields()
+  dialogVisible.value = false;
+}
+
+const handleAvatarSuccess = (res, uploadFile) => {
+  if(res.code == 200){
+    state.form.filePath = res.data.path
+    state.form.format = '.' + res.data.filename.split('.')[1]
+  }else{
+    state.fileList = []
+    ElMessage({
+      type: 'warning',
+      message: '文件上传失败'
+    })
+  }
+}
+
+const showTip =()=>{
+  ElMessage({
+    type: 'warning',
+    message: '超出文件上传数量'
+  });
+}
+const picSize = async (rawFile) => {
+  if(rawFile.size / 1024 / 1024 > 5){
+    ElMessage({
+      type: 'warning',
+      message: '文件大小不能超过5M'
+    });
+    return false
+  }
+};
+const handleRemove = async (file, uploadFiles) => {
+  let path = state.form.filePath;
+  await delPic({path: path}).then(res => {
+    if(res.code == 200){
+      // ElMessage({
+      //   type: 'success',
+      //   message: '文件已删除'
+      // })
+      state.form.filePath = ''
+      state.form.format = ''
+    }else{
+      ElMessage({
+        type: 'warning',
+        message: res.message
+      })
+    }
+  }).catch(() => {
+    state.form.imgUrl = ''
+  });
+}
+
+defineExpose({
+  openDialog
+});
+
+</script>
+
+<style scoped lang="scss">
+.notice{
+  :deep(.el-form .el-form-item__label) {
+    font-size: 15px;
+  }
+  .file {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+</style>
diff --git a/src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/index.vue b/src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/index.vue
new file mode 100644
index 0000000..7455709
--- /dev/null
+++ b/src/views/work/qualityInfo/outsourcingCooperate/unqualifiedHandle/index.vue
@@ -0,0 +1,214 @@
+<template>
+  <div class="app-container">
+    <div style="display: flex;justify-content: space-between">
+      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
+        <el-form-item>
+          <el-button
+              type="primary"
+              plain
+              icon="Plus"
+              @click="openDialog('add',{})"
+          >新增</el-button>
+        </el-form-item>
+        <el-form-item v-if="isAdmin" label="企业:" >
+          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
+            <el-option
+                v-for="item in companyList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item >
+          <el-button v-if="isAdmin" type="primary" @click="getList">查询</el-button>
+          <el-button v-if="isAdmin" type="primary" plain @click="reset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 表格数据 -->
+    <el-table v-loading="loading" :data="dataList" :border="true">
+      <el-table-column label="序号" type="index" align="center" width="80"/>
+      <el-table-column label="供应商" prop="supplierName" align="center"/>
+      <el-table-column label="不合格品名称" prop="supplierName" align="center"/>
+      <el-table-column label="纠正措施记录" align="center">
+        <template #default="scope">
+          <el-link type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName !== '' ?scope.row.fileName + scope.row.format : '纠正措施记录'}}</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" >
+        <template #default="scope">
+          <el-button link type="primary" @click="downloadFile(scope.row.filePath)">下载</el-button>
+          <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+    />
+
+    <edit-dialog ref="dialogRef" @getList=getList></edit-dialog>
+  </div>
+</template>
+
+<script setup>
+import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {delCompany, getCompany} from "@/api/onlineEducation/company";
+import Cookies from "js-cookie";
+import editDialog from './components/editDialog.vue'
+import useUserStore from "@/store/modules/user";
+import {
+  delOutsourcedCommon,
+  delOutsourcedProductNameList, delOutsourcedUnqualified, getOutsourcedCommonList,
+  getOutsourcedProductNameList, getOutsourcedUnqualifiedList
+} from "@/api/outsourcingCooperate/outsourcingCooperate";
+import {renderAsync} from "docx-preview";
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const dialogRef = ref();
+const data = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null
+  },
+  total: 0,
+  dataList: [],
+  companyList: [],
+  isAdmin: false
+});
+
+const { queryParams, total, dataList,companyList, isAdmin } = toRefs(data);
+const userInfo = ref()
+onMounted(async ()=>{
+  userInfo.value = JSON.parse(Cookies.get('userInfo'))
+  if(userStore.roles.includes('admin')){
+    data.isAdmin = true
+    await getCompanyList()
+  }else{
+    data.isAdmin = false
+    data.queryParams.companyId = userStore.companyId
+  }
+  await getList()
+})
+
+onUnmounted(()=>{
+
+})
+
+const getList = async () => {
+  loading.value = true
+  const res = await getOutsourcedUnqualifiedList(data.queryParams)
+  if(res.code == 200){
+    data.dataList = res.data.list || []
+    data.total = res.data.total
+  }else{
+    ElMessage.warning(res.message)
+  }
+  loading.value = false
+}
+
+const getCompanyList = async ()=>{
+  const queryParams = {
+    pageNum: 1,
+    pageSize: 999
+  }
+  const res = await getCompany(queryParams)
+  if (res.code == 200) {
+    data.companyList = res.data.list?res.data.list:[]
+    // data.queryParams.companyId = data.companyList[0].id
+  } else {
+    ElMessage.warning(res.message)
+  }
+}
+
+const downloadFile = (path)=>{
+  window.open(import.meta.env.VITE_APP_BASE_API + '/' + path)
+}
+
+const openFile = async(path)=>{
+  const ext = path.split('.').pop().toLowerCase();
+  if (ext === 'doc' || ext === 'pptx' || ext === 'ppt') {
+    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
+      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+    }).catch(() => {
+      console.log('取消预览')
+    });
+    return
+  }else if(ext === 'pdf' || ext === 'jpg' || ext === 'jpeg' || ext === 'png' || ext === 'cad'){
+    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
+  }else{
+    try {
+      // 1. 获取文件
+      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
+      const arrayBuffer = await response.arrayBuffer();
+      // 2. 创建新窗口
+      const win = window.open('', '_blank');
+      win.document.write(`
+      <!DOCTYPE html>
+      <html>
+        <head>
+          <title>预览</title>
+          <style>
+            body { margin: 20px; font-family: Arial; }
+            .docx-container { width: 100%; height: 100%; }
+          </style>
+        </head>
+        <body>
+          <div id="container" class="docx-container"></div>
+        </body>
+      </html>
+    `);
+      // 3. 渲染 DOCX
+      await renderAsync(arrayBuffer, win.document.getElementById('container'));
+
+    } catch (error) {
+      console.error('预览失败:', error);
+      alert(`预览失败: ${error.message}`);
+    }
+  }
+}
+
+const openDialog = (type, value) => {
+  dialogRef.value.openDialog(type, value, data.queryParams.companyId, data.isAdmin, data.companyList );
+}
+
+/** 重置新增的表单以及其他数据  */
+const reset= async()=> {
+  data.queryParams = {
+    pageNum: 1,
+    pageSize: 10,
+    companyId: null
+  }
+  await getCompanyList()
+  await getList()
+}
+const handleDelete = (val) => {
+  ElMessageBox.confirm(
+      '确定删除此条数据?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+      .then( async() => {
+        const res = await delOutsourcedUnqualified({id: val.id})
+        if(res.code == 200){
+          ElMessage.success('数据删除成功')
+          await getList()
+        }else{
+          ElMessage.warning(res.message)
+        }
+      })
+}
+
+</script>

--
Gitblit v1.9.2