| | |
| | | <template> |
| | | <div class="qr-code-generator"> |
| | | <!-- 二维码生成表单 --> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="qr-form"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px" |
| | | class="qr-form"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="标识类型" prop="type"> |
| | | <el-select v-model="form.type" placeholder="请选择标识类型" style="width: 100%"> |
| | | <el-option label="二维码" value="qrcode"></el-option> |
| | | <el-option label="防伪码" value="security"></el-option> |
| | | <el-form-item label="标识类型" |
| | | prop="type"> |
| | | <el-select v-model="form.type" |
| | | placeholder="请选择标识类型" |
| | | style="width: 100%"> |
| | | <el-option label="二维码" |
| | | value="qrcode"></el-option> |
| | | <el-option label="防伪码" |
| | | value="security"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="内容" prop="content"> |
| | | <el-input |
| | | v-model="form.content" |
| | | <el-form-item label="内容" |
| | | prop="content"> |
| | | <el-input v-model="form.content" |
| | | placeholder="请输入要编码的内容" |
| | | :type="form.type === 'security' ? 'textarea' : 'text'" |
| | | :rows="form.type === 'security' ? 3 : 1" |
| | | ></el-input> |
| | | :rows="form.type === 'security' ? 3 : 1"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="尺寸" prop="size"> |
| | | <el-input-number |
| | | v-model="form.size" |
| | | <el-form-item label="尺寸" |
| | | prop="size"> |
| | | <el-input-number v-model="form.size" |
| | | :min="100" |
| | | :max="500" |
| | | :step="50" |
| | | style="width: 100%" |
| | | ></el-input-number> |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="边距" prop="margin"> |
| | | <el-input-number |
| | | v-model="form.margin" |
| | | <el-form-item label="边距" |
| | | prop="margin"> |
| | | <el-input-number v-model="form.margin" |
| | | :min="0" |
| | | :max="10" |
| | | :step="1" |
| | | style="width: 100%" |
| | | ></el-input-number> |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="前景色" prop="foregroundColor"> |
| | | <el-color-picker v-model="form.foregroundColor" style="width: 100%"></el-color-picker> |
| | | <el-form-item label="前景色" |
| | | prop="foregroundColor"> |
| | | <el-color-picker v-model="form.foregroundColor" |
| | | style="width: 100%"></el-color-picker> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="背景色" prop="backgroundColor"> |
| | | <el-color-picker v-model="form.backgroundColor" style="width: 100%"></el-color-picker> |
| | | <el-form-item label="背景色" |
| | | prop="backgroundColor"> |
| | | <el-color-picker v-model="form.backgroundColor" |
| | | style="width: 100%"></el-color-picker> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="generateCode" :loading="generating"> |
| | | <el-button type="primary" |
| | | @click="generateCode" |
| | | :loading="generating"> |
| | | 生成{{ form.type === 'qrcode' ? '二维码' : '防伪码' }} |
| | | </el-button> |
| | | <el-button @click="resetForm">重置</el-button> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | |
| | | <!-- 生成的码显示区域 --> |
| | | <div v-if="generatedCodeUrl" class="code-display"> |
| | | <div v-if="generatedCodeUrl" |
| | | class="code-display"> |
| | | <el-divider content-position="center"> |
| | | {{ form.type === 'qrcode' ? '生成的二维码' : '生成的防伪码' }} |
| | | </el-divider> |
| | | |
| | | <div class="code-container"> |
| | | <div class="code-image"> |
| | | <img :src="generatedCodeUrl" :alt="form.type === 'qrcode' ? '二维码' : '防伪码'" /> |
| | | <img :src="generatedCodeUrl" |
| | | :alt="form.type === 'qrcode' ? '二维码' : '防伪码'" /> |
| | | </div> |
| | | |
| | | <div class="code-info"> |
| | | <p><strong>内容:</strong>{{ form.content }}</p> |
| | | <p><strong>类型:</strong>{{ form.type === 'qrcode' ? '二维码' : '防伪码' }}</p> |
| | |
| | | <p><strong>生成时间:</strong>{{ generateTime }}</p> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="code-actions"> |
| | | <el-button type="success" @click="downloadCode" icon="Download"> |
| | | <el-button type="success" |
| | | @click="downloadCode" |
| | | icon="Download"> |
| | | 下载图片 |
| | | </el-button> |
| | | <el-button type="primary" @click="copyToClipboard" icon="CopyDocument"> |
| | | <el-button type="primary" |
| | | @click="copyToClipboard" |
| | | icon="CopyDocument"> |
| | | 复制内容 |
| | | </el-button> |
| | | <el-button @click="printCode" icon="Printer"> |
| | | <el-button @click="printCode" |
| | | icon="Printer"> |
| | | 打印 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 批量生成对话框 --> |
| | | <el-dialog v-model="batchDialogVisible" title="批量生成" width="600px"> |
| | | <el-form :model="batchForm" label-width="120px"> |
| | | <el-dialog v-model="batchDialogVisible" |
| | | title="批量生成" |
| | | width="600px"> |
| | | <el-form :model="batchForm" |
| | | label-width="120px"> |
| | | <el-form-item label="生成数量"> |
| | | <el-input-number v-model="batchForm.quantity" :min="1" :max="100" style="width: 100%"></el-input-number> |
| | | <el-input-number v-model="batchForm.quantity" |
| | | :min="1" |
| | | :max="100" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | <el-form-item label="前缀"> |
| | | <el-input v-model="batchForm.prefix" placeholder="请输入前缀,如:PROD_"></el-input> |
| | | <el-input v-model="batchForm.prefix" |
| | | placeholder="请输入前缀,如:PROD_"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="起始编号"> |
| | | <el-input-number v-model="batchForm.startNumber" :min="1" style="width: 100%"></el-input-number> |
| | | <el-input-number v-model="batchForm.startNumber" |
| | | :min="1" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="generateBatchCodes">开始生成</el-button> |
| | | <el-button @click="batchDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="generateBatchCodes">开始生成</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 批量生成结果 --> |
| | | <div v-if="batchCodes.length > 0" class="batch-results"> |
| | | <div v-if="batchCodes.length > 0" |
| | | class="batch-results"> |
| | | <el-divider content-position="center">批量生成结果</el-divider> |
| | | |
| | | <div class="batch-grid"> |
| | | <div |
| | | v-for="(code, index) in batchCodes" |
| | | <div v-for="(code, index) in batchCodes" |
| | | :key="index" |
| | | class="batch-item" |
| | | > |
| | | <img :src="code.url" :alt="code.content" /> |
| | | class="batch-item"> |
| | | <img :src="code.url" |
| | | :alt="code.content" /> |
| | | <p class="batch-content">{{ code.content }}</p> |
| | | <el-button size="small" @click="downloadSingleCode(code)">下载</el-button> |
| | | <el-button size="small" |
| | | @click="downloadSingleCode(code)">下载</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="batch-actions"> |
| | | <el-button type="success" @click="downloadAllCodes">下载全部</el-button> |
| | | <el-button type="success" |
| | | @click="downloadAllCodes">下载全部</el-button> |
| | | <el-button @click="clearBatchCodes">清空结果</el-button> |
| | | </div> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from 'vue' |
| | | import QRCode from 'qrcode' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Download, CopyDocument, Printer } from '@element-plus/icons-vue' |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import QRCode from "qrcode"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Download, CopyDocument, Printer } from "@element-plus/icons-vue"; |
| | | |
| | | // 定义组件名称 |
| | | defineOptions({ |
| | | name: 'QRCodeGenerator' |
| | | }) |
| | | name: "QRCodeGenerator", |
| | | }); |
| | | |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | type: 'qrcode', |
| | | content: '', |
| | | type: "qrcode", |
| | | content: "", |
| | | size: 200, |
| | | margin: 2, |
| | | foregroundColor: '#000000', |
| | | backgroundColor: '#FFFFFF' |
| | | }) |
| | | foregroundColor: "#000000", |
| | | backgroundColor: "#FFFFFF", |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | type: [{ required: true, message: '请选择标识类型', trigger: 'change' }], |
| | | content: [{ required: true, message: '请输入内容', trigger: 'blur' }] |
| | | } |
| | | type: [{ required: true, message: "请选择标识类型", trigger: "change" }], |
| | | content: [{ required: true, message: "请输入内容", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 响应式数据 |
| | | const formRef = ref() |
| | | const generating = ref(false) |
| | | const generatedCodeUrl = ref('') |
| | | const generateTime = ref('') |
| | | const batchDialogVisible = ref(false) |
| | | const formRef = ref(); |
| | | const generating = ref(false); |
| | | const generatedCodeUrl = ref(""); |
| | | const generateTime = ref(""); |
| | | const batchDialogVisible = ref(false); |
| | | const batchForm = reactive({ |
| | | quantity: 10, |
| | | prefix: '', |
| | | startNumber: 1 |
| | | }) |
| | | const batchCodes = ref([]) |
| | | prefix: "", |
| | | startNumber: 1, |
| | | }); |
| | | const batchCodes = ref([]); |
| | | |
| | | // 生成二维码或防伪码 |
| | | const generateCode = async () => { |
| | | try { |
| | | await formRef.value.validate() |
| | | await formRef.value.validate(); |
| | | |
| | | if (!form.content.trim()) { |
| | | ElMessage.warning('请输入要编码的内容') |
| | | return |
| | | ElMessage.warning("请输入要编码的内容"); |
| | | return; |
| | | } |
| | | |
| | | generating.value = true |
| | | generating.value = true; |
| | | |
| | | if (form.type === 'qrcode') { |
| | | if (form.type === "qrcode") { |
| | | // 生成二维码 |
| | | generatedCodeUrl.value = await QRCode.toDataURL(form.content, { |
| | | width: form.size, |
| | | margin: form.margin, |
| | | color: { |
| | | dark: form.foregroundColor, |
| | | light: form.backgroundColor |
| | | light: form.backgroundColor, |
| | | }, |
| | | errorCorrectionLevel: 'M' |
| | | }) |
| | | errorCorrectionLevel: "M", |
| | | }); |
| | | } else { |
| | | // 生成防伪码(使用二维码技术,但内容格式不同) |
| | | const securityContent = generateSecurityCode(form.content) |
| | | const securityContent = generateSecurityCode(form.content); |
| | | generatedCodeUrl.value = await QRCode.toDataURL(securityContent, { |
| | | width: form.size, |
| | | margin: form.margin, |
| | | color: { |
| | | dark: form.foregroundColor, |
| | | light: form.backgroundColor |
| | | light: form.backgroundColor, |
| | | }, |
| | | errorCorrectionLevel: 'H' // 防伪码使用最高纠错级别 |
| | | }) |
| | | errorCorrectionLevel: "H", // 防伪码使用最高纠错级别 |
| | | }); |
| | | } |
| | | |
| | | generateTime.value = new Date().toLocaleString() |
| | | ElMessage.success('生成成功!') |
| | | |
| | | generateTime.value = new Date().toLocaleString(); |
| | | ElMessage.success("生成成功!"); |
| | | } catch (error) { |
| | | console.error('生成失败:', error) |
| | | ElMessage.error('生成失败:' + error.message) |
| | | console.error("生成失败:", error); |
| | | ElMessage.error("生成失败:" + error.message); |
| | | } finally { |
| | | generating.value = false |
| | | generating.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 生成防伪码内容 |
| | | const generateSecurityCode = (content) => { |
| | | const timestamp = Date.now() |
| | | const random = Math.random().toString(36).substr(2, 8) |
| | | return `SEC_${content}_${timestamp}_${random}` |
| | | } |
| | | const generateSecurityCode = content => { |
| | | const timestamp = Date.now(); |
| | | const random = Math.random().toString(36).substr(2, 8); |
| | | return `SEC_${content}_${timestamp}_${random}`; |
| | | }; |
| | | |
| | | // 下载生成的码 |
| | | const downloadCode = () => { |
| | | if (!generatedCodeUrl.value) { |
| | | ElMessage.warning('请先生成码') |
| | | return |
| | | ElMessage.warning("请先生成码"); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement('a') |
| | | a.href = generatedCodeUrl.value |
| | | a.download = `${form.type === 'qrcode' ? '二维码' : '防伪码'}_${new Date().getTime()}.png` |
| | | document.body.appendChild(a) |
| | | a.click() |
| | | document.body.removeChild(a) |
| | | ElMessage.success('下载成功!') |
| | | } |
| | | const a = document.createElement("a"); |
| | | a.href = generatedCodeUrl.value; |
| | | a.download = `${ |
| | | form.type === "qrcode" ? "二维码" : "防伪码" |
| | | }_${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | ElMessage.success("下载成功!"); |
| | | }; |
| | | |
| | | // 复制内容到剪贴板 |
| | | const copyToClipboard = async () => { |
| | | try { |
| | | await navigator.clipboard.writeText(form.content) |
| | | ElMessage.success('内容已复制到剪贴板') |
| | | await navigator.clipboard.writeText(form.content); |
| | | ElMessage.success("内容已复制到剪贴板"); |
| | | } catch (error) { |
| | | // 降级方案 |
| | | const textArea = document.createElement('textarea') |
| | | textArea.value = form.content |
| | | document.body.appendChild(textArea) |
| | | textArea.select() |
| | | document.execCommand('copy') |
| | | document.body.removeChild(textArea) |
| | | ElMessage.success('内容已复制到剪贴板') |
| | | const textArea = document.createElement("textarea"); |
| | | textArea.value = form.content; |
| | | document.body.appendChild(textArea); |
| | | textArea.select(); |
| | | document.execCommand("copy"); |
| | | document.body.removeChild(textArea); |
| | | ElMessage.success("内容已复制到剪贴板"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 打印码 |
| | | const printCode = () => { |
| | | if (!generatedCodeUrl.value) { |
| | | ElMessage.warning('请先生成码') |
| | | return |
| | | ElMessage.warning("请先生成码"); |
| | | return; |
| | | } |
| | | |
| | | const printWindow = window.open('', '_blank') |
| | | const printWindow = window.open("", "_blank"); |
| | | printWindow.document.write(` |
| | | <html> |
| | | <head> |
| | | <title>打印${form.type === 'qrcode' ? '二维码' : '防伪码'}</title> |
| | | <title>打印${form.type === "qrcode" ? "二维码" : "防伪码"}</title> |
| | | <style> |
| | | body { text-align: center; padding: 20px; } |
| | | img { max-width: 100%; height: auto; } |
| | |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <h2>${form.type === 'qrcode' ? '二维码' : '防伪码'}</h2> |
| | | <img src="${generatedCodeUrl.value}" alt="${form.type === 'qrcode' ? '二维码' : '防伪码'}" /> |
| | | <h2>${form.type === "qrcode" ? "二维码" : "防伪码"}</h2> |
| | | <img src="${generatedCodeUrl.value}" alt="${ |
| | | form.type === "qrcode" ? "二维码" : "防伪码" |
| | | }" /> |
| | | <div class="info"> |
| | | <p><strong>内容:</strong>${form.content}</p> |
| | | <p><strong>生成时间:</strong>${generateTime.value}</p> |
| | | </div> |
| | | </body> |
| | | </html> |
| | | `) |
| | | printWindow.document.close() |
| | | printWindow.print() |
| | | } |
| | | `); |
| | | printWindow.document.close(); |
| | | printWindow.print(); |
| | | }; |
| | | |
| | | // 重置表单 |
| | | const resetForm = () => { |
| | | formRef.value.resetFields() |
| | | generatedCodeUrl.value = '' |
| | | generateTime.value = '' |
| | | batchCodes.value = [] |
| | | } |
| | | formRef.value.resetFields(); |
| | | generatedCodeUrl.value = ""; |
| | | generateTime.value = ""; |
| | | batchCodes.value = []; |
| | | }; |
| | | |
| | | // 批量生成 |
| | | const generateBatchCodes = async () => { |
| | | if (!batchForm.prefix.trim()) { |
| | | ElMessage.warning('请输入前缀') |
| | | return |
| | | ElMessage.warning("请输入前缀"); |
| | | return; |
| | | } |
| | | |
| | | batchCodes.value = [] |
| | | generating.value = true |
| | | batchCodes.value = []; |
| | | generating.value = true; |
| | | |
| | | try { |
| | | for (let i = 0; i < batchForm.quantity; i++) { |
| | | const number = batchForm.startNumber + i |
| | | const content = `${batchForm.prefix}${number.toString().padStart(6, '0')}` |
| | | const number = batchForm.startNumber + i; |
| | | const content = `${batchForm.prefix}${number |
| | | .toString() |
| | | .padStart(6, "0")}`; |
| | | |
| | | let codeUrl |
| | | if (form.type === 'qrcode') { |
| | | let codeUrl; |
| | | if (form.type === "qrcode") { |
| | | codeUrl = await QRCode.toDataURL(content, { |
| | | width: form.size, |
| | | margin: form.margin, |
| | | color: { |
| | | dark: form.foregroundColor, |
| | | light: form.backgroundColor |
| | | } |
| | | }) |
| | | light: form.backgroundColor, |
| | | }, |
| | | }); |
| | | } else { |
| | | const securityContent = generateSecurityCode(content) |
| | | const securityContent = generateSecurityCode(content); |
| | | codeUrl = await QRCode.toDataURL(securityContent, { |
| | | width: form.size, |
| | | margin: form.margin, |
| | | color: { |
| | | dark: form.foregroundColor, |
| | | light: form.backgroundColor |
| | | } |
| | | }) |
| | | light: form.backgroundColor, |
| | | }, |
| | | }); |
| | | } |
| | | |
| | | batchCodes.value.push({ |
| | | content, |
| | | url: codeUrl |
| | | }) |
| | | url: codeUrl, |
| | | }); |
| | | } |
| | | |
| | | ElMessage.success(`批量生成完成,共生成 ${batchForm.quantity} 个码`) |
| | | batchDialogVisible.value = false |
| | | |
| | | ElMessage.success(`批量生成完成,共生成 ${batchForm.quantity} 个码`); |
| | | batchDialogVisible.value = false; |
| | | } catch (error) { |
| | | console.error('批量生成失败:', error) |
| | | ElMessage.error('批量生成失败:' + error.message) |
| | | console.error("批量生成失败:", error); |
| | | ElMessage.error("批量生成失败:" + error.message); |
| | | } finally { |
| | | generating.value = false |
| | | generating.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 下载单个批量生成的码 |
| | | const downloadSingleCode = (code) => { |
| | | const a = document.createElement('a') |
| | | a.href = code.url |
| | | a.download = `${code.content}.png` |
| | | document.body.appendChild(a) |
| | | a.click() |
| | | document.body.removeChild(a) |
| | | } |
| | | const downloadSingleCode = code => { |
| | | const a = document.createElement("a"); |
| | | a.href = code.url; |
| | | a.download = `${code.content}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | }; |
| | | |
| | | // 下载所有批量生成的码 |
| | | const downloadAllCodes = async () => { |
| | | if (batchCodes.value.length === 0) { |
| | | ElMessage.warning('没有可下载的码') |
| | | return |
| | | ElMessage.warning("没有可下载的码"); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // 使用JSZip打包下载 |
| | | const JSZip = await import('jszip') |
| | | const zip = new JSZip.default() |
| | | const JSZip = await import("jszip"); |
| | | const zip = new JSZip.default(); |
| | | |
| | | batchCodes.value.forEach((code, index) => { |
| | | // 将base64转换为blob |
| | | const base64Data = code.url.split(',')[1] |
| | | const byteCharacters = atob(base64Data) |
| | | const byteNumbers = new Array(byteCharacters.length) |
| | | const base64Data = code.url.split(",")[1]; |
| | | const byteCharacters = atob(base64Data); |
| | | const byteNumbers = new Array(byteCharacters.length); |
| | | for (let i = 0; i < byteCharacters.length; i++) { |
| | | byteNumbers[i] = byteCharacters.charCodeAt(i) |
| | | byteNumbers[i] = byteCharacters.charCodeAt(i); |
| | | } |
| | | const byteArray = new Uint8Array(byteNumbers) |
| | | const byteArray = new Uint8Array(byteNumbers); |
| | | |
| | | zip.file(`${code.content}.png`, byteArray) |
| | | }) |
| | | zip.file(`${code.content}.png`, byteArray); |
| | | }); |
| | | |
| | | const content = await zip.generateAsync({ type: 'blob' }) |
| | | const a = document.createElement('a') |
| | | a.href = URL.createObjectURL(content) |
| | | a.download = `批量${form.type === 'qrcode' ? '二维码' : '防伪码'}_${new Date().getTime()}.zip` |
| | | document.body.appendChild(a) |
| | | a.click() |
| | | document.body.removeChild(a) |
| | | URL.revokeObjectURL(a.href) |
| | | const content = await zip.generateAsync({ type: "blob" }); |
| | | const a = document.createElement("a"); |
| | | a.href = URL.createObjectURL(content); |
| | | a.download = `批量${ |
| | | form.type === "qrcode" ? "二维码" : "防伪码" |
| | | }_${new Date().getTime()}.zip`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | URL.revokeObjectURL(a.href); |
| | | |
| | | ElMessage.success('批量下载完成!') |
| | | ElMessage.success("批量下载完成!"); |
| | | } catch (error) { |
| | | console.error('批量下载失败:', error) |
| | | ElMessage.error('批量下载失败,请逐个下载') |
| | | console.error("批量下载失败:", error); |
| | | ElMessage.error("批量下载失败,请逐个下载"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 清空批量生成结果 |
| | | const clearBatchCodes = () => { |
| | | batchCodes.value = [] |
| | | } |
| | | batchCodes.value = []; |
| | | }; |
| | | |
| | | // 暴露方法给父组件 |
| | | defineExpose({ |
| | | generateCode, |
| | | downloadCode, |
| | | resetForm, |
| | | form |
| | | }) |
| | | form, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-tabs v-model="activeTab" type="border-card"> |
| | | <el-tabs v-model="activeTab" |
| | | type="border-card"> |
| | | <!-- 假期设置 --> |
| | | <el-tab-pane label="假期设置" name="holiday"> |
| | | <el-tab-pane label="假期设置" |
| | | name="holiday"> |
| | | <div class="tab-content"> |
| | | <el-button type="primary" @click="openDialog('holiday', 'add')">新增假期</el-button> |
| | | |
| | | <el-table :data="holidayData" border style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="name" label="假期名称" /> |
| | | <el-table-column prop="type" label="假期类型"> |
| | | <el-button type="primary" |
| | | @click="openDialog('holiday', 'add')">新增假期</el-button> |
| | | <el-table :data="holidayData" |
| | | border |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="name" |
| | | label="假期名称" /> |
| | | <el-table-column prop="type" |
| | | label="假期类型"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getTagType(scope.row.type)">{{ getTypeLabel(scope.row.type) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="startDate" label="开始日期" /> |
| | | <el-table-column prop="endDate" label="结束日期" /> |
| | | <el-table-column prop="days" label="天数" align="center" /> |
| | | <el-table-column prop="status" label="状态" > |
| | | <el-table-column prop="startDate" |
| | | label="开始日期" /> |
| | | <el-table-column prop="endDate" |
| | | label="结束日期" /> |
| | | <el-table-column prop="days" |
| | | label="天数" |
| | | align="center" /> |
| | | <el-table-column prop="status" |
| | | label="状态"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '启用' : '停用' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" fixed="right"> |
| | | <el-table-column label="操作" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" size="small" @click="openDialog('holiday', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" size="small" @click="deleteItem('holiday', scope.row)">删除</el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openDialog('holiday', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="deleteItem('holiday', scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-tab-pane> |
| | | |
| | | <!-- 年假设置 --> |
| | | <el-tab-pane label="年假设置" name="annual"> |
| | | <el-tab-pane label="年假设置" |
| | | name="annual"> |
| | | <div class="tab-content"> |
| | | <el-button type="primary" @click="openDialog('annual', 'add')">新增年假规则</el-button> |
| | | |
| | | <el-table :data="annualData" border style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="employeeType" label="员工类型"> |
| | | <el-button type="primary" |
| | | @click="openDialog('annual', 'add')">新增年假规则</el-button> |
| | | <el-table :data="annualData" |
| | | border |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="employeeType" |
| | | label="员工类型"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getTagType(scope.row.employeeType)">{{ getTypeLabel(scope.row.employeeType) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="workYears" label="工作年限" /> |
| | | <el-table-column prop="annualDays" label="年假天数" align="center" /> |
| | | <el-table-column prop="maxCarryOver" label="最大结转天数" align="center" /> |
| | | <el-table-column prop="status" label="状态"> |
| | | <el-table-column prop="workYears" |
| | | label="工作年限" /> |
| | | <el-table-column prop="annualDays" |
| | | label="年假天数" |
| | | align="center" /> |
| | | <el-table-column prop="maxCarryOver" |
| | | label="最大结转天数" |
| | | align="center" /> |
| | | <el-table-column prop="status" |
| | | label="状态"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '启用' : '停用' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" fixed="right"> |
| | | <el-table-column label="操作" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" size="small" @click="openDialog('annual', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" size="small" @click="deleteItem('annual', scope.row)">删除</el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openDialog('annual', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="deleteItem('annual', scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-tab-pane> |
| | | |
| | | <!-- 加班设置 --> |
| | | <el-tab-pane label="加班设置" name="overtime"> |
| | | <el-tab-pane label="加班设置" |
| | | name="overtime"> |
| | | <div class="tab-content"> |
| | | <el-button type="primary" @click="openDialog('overtime', 'add')">新增加班规则</el-button> |
| | | |
| | | <el-table :data="overtimeData" border style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="name" label="规则名称" /> |
| | | <el-table-column prop="type" label="加班类型" > |
| | | <el-button type="primary" |
| | | @click="openDialog('overtime', 'add')">新增加班规则</el-button> |
| | | <el-table :data="overtimeData" |
| | | border |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="name" |
| | | label="规则名称" /> |
| | | <el-table-column prop="type" |
| | | label="加班类型"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getTagType(scope.row.type)">{{ getTypeLabel(scope.row.type) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="startTime" label="开始时间" /> |
| | | <el-table-column prop="endTime" label="结束时间" /> |
| | | <el-table-column prop="rate" label="倍率" align="center" /> |
| | | <el-table-column prop="status" label="状态" > |
| | | <el-table-column prop="startTime" |
| | | label="开始时间" /> |
| | | <el-table-column prop="endTime" |
| | | label="结束时间" /> |
| | | <el-table-column prop="rate" |
| | | label="倍率" |
| | | align="center" /> |
| | | <el-table-column prop="status" |
| | | label="状态"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '启用' : '停用' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" fixed="right"> |
| | | <el-table-column label="操作" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" size="small" @click="openDialog('overtime', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" size="small" @click="deleteItem('overtime', scope.row)">删除</el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openDialog('overtime', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="deleteItem('overtime', scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-tab-pane> |
| | | |
| | | <!-- 上班时间设置 --> |
| | | <el-tab-pane label="上班时间设置" name="worktime"> |
| | | <el-tab-pane label="上班时间设置" |
| | | name="worktime"> |
| | | <div class="tab-content"> |
| | | <el-button type="primary" @click="openDialog('worktime', 'add')">新增时间段</el-button> |
| | | |
| | | <el-table :data="worktimeData" border style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="name" label="时间段名称" /> |
| | | <el-table-column prop="startTime" label="上班时间"/> |
| | | <el-table-column prop="endTime" label="下班时间" /> |
| | | <el-table-column prop="flexibleStart" label="弹性上班"> |
| | | <el-button type="primary" |
| | | @click="openDialog('worktime', 'add')">新增时间段</el-button> |
| | | <el-table :data="worktimeData" |
| | | border |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="name" |
| | | label="时间段名称" /> |
| | | <el-table-column prop="startTime" |
| | | label="上班时间" /> |
| | | <el-table-column prop="endTime" |
| | | label="下班时间" /> |
| | | <el-table-column prop="flexibleStart" |
| | | label="弹性上班"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.flexibleStart === 'true' ? 'success' : 'info'"> |
| | | {{ scope.row.flexibleStart === 'true' ? '是' : '否' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="flexibleMinutes" label="弹性时间(分钟)" width="120" align="center" /> |
| | | <el-table-column prop="status" label="状态" > |
| | | <el-table-column prop="flexibleMinutes" |
| | | label="弹性时间(分钟)" |
| | | width="120" |
| | | align="center" /> |
| | | <el-table-column prop="status" |
| | | label="状态"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '启用' : '停用' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" fixed="right"> |
| | | <el-table-column label="操作" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" size="small" @click="openDialog('worktime', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" size="small" @click="deleteItem('worktime', scope.row)">删除</el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openDialog('worktime', 'edit', scope.row)">编辑</el-button> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="deleteItem('worktime', scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-tab-pane> |
| | | |
| | | <!-- 打卡记录 --> |
| | | <el-tab-pane label="打卡记录" name="attendance"> |
| | | <el-tab-pane label="打卡记录" |
| | | name="attendance"> |
| | | <div class="tab-content"> |
| | | <div style="margin-bottom: 20px;"> |
| | | <el-date-picker |
| | | v-model="attendanceDate" |
| | | <el-date-picker v-model="attendanceDate" |
| | | type="date" |
| | | placeholder="选择日期" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | style="margin-right: 10px;" |
| | | @change="filterAttendanceData" |
| | | /> |
| | | <el-select |
| | | v-model="attendanceStatus" |
| | | @change="filterAttendanceData" /> |
| | | <el-select v-model="attendanceStatus" |
| | | placeholder="选择状态" |
| | | style="width: 120px; margin-right: 10px;" |
| | | @change="filterAttendanceData" |
| | | > |
| | | <el-option label="全部" value="" /> |
| | | <el-option label="正常" value="normal" /> |
| | | <el-option label="迟到" value="late" /> |
| | | <el-option label="早退" value="early" /> |
| | | <el-option label="缺勤" value="absent" /> |
| | | @change="filterAttendanceData"> |
| | | <el-option label="全部" |
| | | value="" /> |
| | | <el-option label="正常" |
| | | value="normal" /> |
| | | <el-option label="迟到" |
| | | value="late" /> |
| | | <el-option label="早退" |
| | | value="early" /> |
| | | <el-option label="缺勤" |
| | | value="absent" /> |
| | | </el-select> |
| | | <el-button type="primary" @click="exportAttendance">导出记录</el-button> |
| | | <el-button type="primary" |
| | | @click="exportAttendance">导出记录</el-button> |
| | | </div> |
| | | |
| | | <el-table :data="filteredAttendanceData" border style="width: 100%;"> |
| | | <el-table-column prop="employeeName" label="员工姓名" width="120" /> |
| | | <el-table-column prop="department" label="部门" width="120" /> |
| | | <el-table-column prop="date" label="日期" width="120" /> |
| | | <el-table-column prop="clockInTime" label="上班打卡" width="120" /> |
| | | <el-table-column prop="clockOutTime" label="下班打卡" width="120" /> |
| | | <el-table-column prop="workHours" label="工作时长" width="100" align="center" /> |
| | | <el-table-column prop="status" label="状态" width="100" align="center"> |
| | | <el-table :data="filteredAttendanceData" |
| | | border |
| | | style="width: 100%;"> |
| | | <el-table-column prop="employeeName" |
| | | label="员工姓名" |
| | | width="120" /> |
| | | <el-table-column prop="department" |
| | | label="部门" |
| | | width="120" /> |
| | | <el-table-column prop="date" |
| | | label="日期" |
| | | width="120" /> |
| | | <el-table-column prop="clockInTime" |
| | | label="上班打卡" |
| | | width="120" /> |
| | | <el-table-column prop="clockOutTime" |
| | | label="下班打卡" |
| | | width="120" /> |
| | | <el-table-column prop="workHours" |
| | | label="工作时长" |
| | | width="100" |
| | | align="center" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getAttendanceTagType(scope.row.status)">{{ getAttendanceStatusLabel(scope.row.status) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="打卡地点" width="150" /> |
| | | <el-table-column prop="remark" label="备注" min-width="150" /> |
| | | <el-table-column prop="location" |
| | | label="打卡地点" |
| | | width="150" /> |
| | | <el-table-column prop="remark" |
| | | label="备注" |
| | | min-width="150" /> |
| | | </el-table> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | |
| | | <!-- 通用弹窗 --> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px"> |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="100px"> |
| | | <el-form-item label="名称" prop="name" v-if="currentType !== 'annual'"> |
| | | <el-input v-model="form.name" placeholder="请输入名称" /> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="600px"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="100px"> |
| | | <el-form-item label="名称" |
| | | prop="name" |
| | | v-if="currentType !== 'annual'"> |
| | | <el-input v-model="form.name" |
| | | placeholder="请输入名称" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="类型" prop="type" v-if="currentType === 'holiday' || currentType === 'overtime'"> |
| | | <el-select v-model="form.type" placeholder="请选择类型" style="width: 100%"> |
| | | <el-option |
| | | v-for="option in getTypeOptions()" |
| | | <el-form-item label="类型" |
| | | prop="type" |
| | | v-if="currentType === 'holiday' || currentType === 'overtime'"> |
| | | <el-select v-model="form.type" |
| | | placeholder="请选择类型" |
| | | style="width: 100%"> |
| | | <el-option v-for="option in getTypeOptions()" |
| | | :key="option.value" |
| | | :label="option.label" |
| | | :value="option.value" |
| | | /> |
| | | :value="option.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="员工类型" prop="employeeType" v-if="currentType === 'annual'"> |
| | | <el-select v-model="form.employeeType" placeholder="请选择员工类型" style="width: 100%"> |
| | | <el-form-item label="员工类型" |
| | | prop="employeeType" |
| | | v-if="currentType === 'annual'"> |
| | | <el-select v-model="form.employeeType" |
| | | placeholder="请选择员工类型" |
| | | style="width: 100%"> |
| | | <!-- <el-option label="正式员工" value="regular" /> |
| | | <el-option label="试用期员工" value="probation" /> |
| | | <el-option label="实习生" value="intern" /> --> |
| | | <el-option |
| | | v-for="option in getTypeOptions()" |
| | | <el-option v-for="option in getTypeOptions()" |
| | | :key="option.value" |
| | | :label="option.label" |
| | | :value="option.value" |
| | | /> |
| | | :value="option.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="工作年限" prop="workYears" v-if="currentType === 'annual'"> |
| | | <el-input v-model="form.workYears" placeholder="如:1-3年、3-5年等" /> |
| | | <el-form-item label="工作年限" |
| | | prop="workYears" |
| | | v-if="currentType === 'annual'"> |
| | | <el-input v-model="form.workYears" |
| | | placeholder="如:1-3年、3-5年等" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="年假天数" prop="annualDays" v-if="currentType === 'annual'"> |
| | | <el-input-number v-model="form.annualDays" :min="0" :max="365" style="width: 100%" /> |
| | | <el-form-item label="年假天数" |
| | | prop="annualDays" |
| | | v-if="currentType === 'annual'"> |
| | | <el-input-number v-model="form.annualDays" |
| | | :min="0" |
| | | :max="365" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="最大结转天数" prop="maxCarryOver" v-if="currentType === 'annual'"> |
| | | <el-input-number v-model="form.maxCarryOver" :min="0" :max="30" style="width: 100%" /> |
| | | <el-form-item label="最大结转天数" |
| | | prop="maxCarryOver" |
| | | v-if="currentType === 'annual'"> |
| | | <el-input-number v-model="form.maxCarryOver" |
| | | :min="0" |
| | | :max="30" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="日期范围" prop="dateRange" v-if="currentType === 'holiday'"> |
| | | <el-date-picker |
| | | v-model="form.dateRange" |
| | | <el-form-item label="日期范围" |
| | | prop="dateRange" |
| | | v-if="currentType === 'holiday'"> |
| | | <el-date-picker v-model="form.dateRange" |
| | | type="daterange" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | style="width: 100%" |
| | | @change="calculateDays" |
| | | /> |
| | | @change="calculateDays" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="天数" prop="days" v-if="currentType === 'holiday'"> |
| | | <el-input-number v-model="form.days" :min="0" style="width: 100%" /> |
| | | <el-form-item label="天数" |
| | | prop="days" |
| | | v-if="currentType === 'holiday'"> |
| | | <el-input-number v-model="form.days" |
| | | :min="0" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="开始时间" prop="startTime" v-if="currentType === 'overtime'"> |
| | | <el-time-picker |
| | | v-model="form.startTime" |
| | | <el-form-item label="开始时间" |
| | | prop="startTime" |
| | | v-if="currentType === 'overtime'"> |
| | | <el-time-picker v-model="form.startTime" |
| | | placeholder="开始时间" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | style="width: 100%" |
| | | @change="validateTimeField('startTime')" |
| | | /> |
| | | @change="validateTimeField('startTime')" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="结束时间" prop="endTime" v-if="currentType === 'overtime'"> |
| | | <el-time-picker |
| | | v-model="form.endTime" |
| | | <el-form-item label="结束时间" |
| | | prop="endTime" |
| | | v-if="currentType === 'overtime'"> |
| | | <el-time-picker v-model="form.endTime" |
| | | placeholder="结束时间" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | style="width: 100%" |
| | | @change="validateTimeField('endTime')" |
| | | /> |
| | | @change="validateTimeField('endTime')" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="倍率" prop="rate" v-if="currentType === 'overtime'"> |
| | | <el-input-number v-model="form.rate" :min="1" :max="3" :step="0.5" style="width: 100%" /> |
| | | <el-form-item label="倍率" |
| | | prop="rate" |
| | | v-if="currentType === 'overtime'"> |
| | | <el-input-number v-model="form.rate" |
| | | :min="1" |
| | | :max="3" |
| | | :step="0.5" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="上班时间" prop="workStartTime" v-if="currentType === 'worktime'"> |
| | | <el-time-picker |
| | | v-model="form.workStartTime" |
| | | <el-form-item label="上班时间" |
| | | prop="workStartTime" |
| | | v-if="currentType === 'worktime'"> |
| | | <el-time-picker v-model="form.workStartTime" |
| | | placeholder="上班时间" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | style="width: 100%" |
| | | @change="validateTimeField('workStartTime')" |
| | | /> |
| | | @change="validateTimeField('workStartTime')" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="下班时间" prop="workEndTime" v-if="currentType === 'worktime'"> |
| | | <el-time-picker |
| | | v-model="form.workEndTime" |
| | | <el-form-item label="下班时间" |
| | | prop="workEndTime" |
| | | v-if="currentType === 'worktime'"> |
| | | <el-time-picker v-model="form.workEndTime" |
| | | placeholder="下班时间" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | style="width: 100%" |
| | | @change="validateTimeField('workEndTime')" |
| | | /> |
| | | @change="validateTimeField('workEndTime')" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="弹性上班" prop="flexibleStart" v-if="currentType === 'worktime'"> |
| | | <el-form-item label="弹性上班" |
| | | prop="flexibleStart" |
| | | v-if="currentType === 'worktime'"> |
| | | <el-switch v-model="form.flexibleStart" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="弹性时间(分钟)" prop="flexibleMinutes" v-if="currentType === 'worktime' && form.flexibleStart"> |
| | | <el-input-number v-model="form.flexibleMinutes" :min="0" :max="120" style="width: 100%" /> |
| | | <el-form-item label="弹性时间(分钟)" |
| | | prop="flexibleMinutes" |
| | | v-if="currentType === 'worktime' && form.flexibleStart"> |
| | | <el-input-number v-model="form.flexibleMinutes" |
| | | :min="0" |
| | | :max="120" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio value="active">启用</el-radio> |
| | | <el-radio value="inactive">停用</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { listHolidaySettings, addHolidaySettings, updateHolidaySettings, delHolidaySettings, listAnnualLeaveSettingList, addAnnualLeaveSetting, updateAnnualLeaveSetting, delAnnualLeaveSetting, listOvertimeSettingList, addOvertimeSetting, updateOvertimeSetting, delOvertimeSetting, listWorkingHoursSettingList, addWorkingHoursSetting, updateWorkingHoursSetting, delWorkingHoursSetting } from '@/api/collaborativeApproval/attendanceManagement.js' |
| | | import { ref, reactive, onMounted, onUnmounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | listHolidaySettings, |
| | | addHolidaySettings, |
| | | updateHolidaySettings, |
| | | delHolidaySettings, |
| | | listAnnualLeaveSettingList, |
| | | addAnnualLeaveSetting, |
| | | updateAnnualLeaveSetting, |
| | | delAnnualLeaveSetting, |
| | | listOvertimeSettingList, |
| | | addOvertimeSetting, |
| | | updateOvertimeSetting, |
| | | delOvertimeSetting, |
| | | listWorkingHoursSettingList, |
| | | addWorkingHoursSetting, |
| | | updateWorkingHoursSetting, |
| | | delWorkingHoursSetting, |
| | | } from "@/api/collaborativeApproval/attendanceManagement.js"; |
| | | |
| | | // 当前激活的标签页 |
| | | const activeTab = ref('holiday') |
| | | const activeTab = ref("holiday"); |
| | | |
| | | // 弹窗相关 |
| | | const dialogVisible = ref(false) |
| | | const dialogTitle = ref('') |
| | | const currentType = ref('') |
| | | const currentAction = ref('') |
| | | const currentEditId = ref('') |
| | | const formRef = ref() |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const currentType = ref(""); |
| | | const currentAction = ref(""); |
| | | const currentEditId = ref(""); |
| | | const formRef = ref(); |
| | | const page = { |
| | | current: 1, |
| | | size: 20, |
| | | total: 0, |
| | | } |
| | | const holidayData = ref([]) |
| | | const annualData = ref([]) |
| | | const overtimeData = ref([]) |
| | | const worktimeData = ref([]) |
| | | }; |
| | | const holidayData = ref([]); |
| | | const annualData = ref([]); |
| | | const overtimeData = ref([]); |
| | | const worktimeData = ref([]); |
| | | |
| | | // 打卡记录相关数据 |
| | | const attendanceData = ref([]) |
| | | const filteredAttendanceData = ref([]) |
| | | const attendanceDate = ref('') |
| | | const attendanceStatus = ref('') |
| | | const attendanceData = ref([]); |
| | | const filteredAttendanceData = ref([]); |
| | | const attendanceDate = ref(""); |
| | | const attendanceStatus = ref(""); |
| | | |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | name: '', |
| | | type: '', |
| | | name: "", |
| | | type: "", |
| | | dateRange: [], |
| | | startDate: '', |
| | | endDate: '', |
| | | startDate: "", |
| | | endDate: "", |
| | | days: 0, |
| | | employeeType: '', |
| | | workYears: '', |
| | | employeeType: "", |
| | | workYears: "", |
| | | annualDays: 0, |
| | | maxCarryOver: 0, |
| | | startTime: '', // 加班开始时间 |
| | | endTime: '', // 加班结束时间 |
| | | workStartTime: '', // 上班时间 |
| | | workEndTime: '', // 下班时间 |
| | | startTime: "", // 加班开始时间 |
| | | endTime: "", // 加班结束时间 |
| | | workStartTime: "", // 上班时间 |
| | | workEndTime: "", // 下班时间 |
| | | rate: 1.5, |
| | | flexibleStart: false, |
| | | flexibleMinutes: 30, |
| | | status: 'active' |
| | | }) |
| | | status: "active", |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | name: [{ required: true, message: '请输入名称', trigger: 'blur' }], |
| | | type: [{ required: true, message: '请选择类型', trigger: 'change' }], |
| | | dateRange: [{ required: true, message: '请选择日期范围', trigger: 'change' }], |
| | | days: [{ required: true, message: '请输入天数', trigger: 'blur' }], |
| | | employeeType: [{ required: true, message: '请选择员工类型', trigger: 'change' }], |
| | | workYears: [{ required: true, message: '请输入工作年限', trigger: 'blur' }], |
| | | annualDays: [{ required: true, message: '请输入年假天数', trigger: 'blur' }], |
| | | maxCarryOver: [{ required: true, message: '请输入最大结转天数', trigger: 'blur' }], |
| | | startTime: [{ |
| | | name: [{ required: true, message: "请输入名称", trigger: "blur" }], |
| | | type: [{ required: true, message: "请选择类型", trigger: "change" }], |
| | | dateRange: [{ required: true, message: "请选择日期范围", trigger: "change" }], |
| | | days: [{ required: true, message: "请输入天数", trigger: "blur" }], |
| | | employeeType: [ |
| | | { required: true, message: "请选择员工类型", trigger: "change" }, |
| | | ], |
| | | workYears: [{ required: true, message: "请输入工作年限", trigger: "blur" }], |
| | | annualDays: [{ required: true, message: "请输入年假天数", trigger: "blur" }], |
| | | maxCarryOver: [ |
| | | { required: true, message: "请输入最大结转天数", trigger: "blur" }, |
| | | ], |
| | | startTime: [ |
| | | { |
| | | required: true, |
| | | message: '请选择开始时间', |
| | | trigger: 'change', |
| | | message: "请选择开始时间", |
| | | trigger: "change", |
| | | validator: (rule, value, callback) => { |
| | | if (!value) { |
| | | callback(new Error('请选择开始时间')) |
| | | callback(new Error("请选择开始时间")); |
| | | } else { |
| | | callback() |
| | | callback(); |
| | | } |
| | | } |
| | | }], |
| | | endTime: [{ |
| | | }, |
| | | }, |
| | | ], |
| | | endTime: [ |
| | | { |
| | | required: true, |
| | | message: '请选择结束时间', |
| | | trigger: 'change', |
| | | message: "请选择结束时间", |
| | | trigger: "change", |
| | | validator: (rule, value, callback) => { |
| | | if (!value) { |
| | | callback(new Error('请选择结束时间')) |
| | | callback(new Error("请选择结束时间")); |
| | | } else { |
| | | callback() |
| | | callback(); |
| | | } |
| | | } |
| | | }], |
| | | workStartTime: [{ |
| | | }, |
| | | }, |
| | | ], |
| | | workStartTime: [ |
| | | { |
| | | required: true, |
| | | message: '请选择上班时间', |
| | | trigger: 'change', |
| | | message: "请选择上班时间", |
| | | trigger: "change", |
| | | validator: (rule, value, callback) => { |
| | | if (!value) { |
| | | callback(new Error('请选择上班时间')) |
| | | callback(new Error("请选择上班时间")); |
| | | } else { |
| | | callback() |
| | | callback(); |
| | | } |
| | | } |
| | | }], |
| | | workEndTime: [{ |
| | | }, |
| | | }, |
| | | ], |
| | | workEndTime: [ |
| | | { |
| | | required: true, |
| | | message: '请选择下班时间', |
| | | trigger: 'change', |
| | | message: "请选择下班时间", |
| | | trigger: "change", |
| | | validator: (rule, value, callback) => { |
| | | if (!value) { |
| | | callback(new Error('请选择下班时间')) |
| | | callback(new Error("请选择下班时间")); |
| | | } else { |
| | | callback() |
| | | callback(); |
| | | } |
| | | } |
| | | }], |
| | | rate: [{ required: true, message: '请输入倍率', trigger: 'blur' }] |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | rate: [{ required: true, message: "请输入倍率", trigger: "blur" }], |
| | | }; |
| | | // 工具函数 |
| | | const getTagType = (type) => { |
| | | const getTagType = type => { |
| | | const tagMap = { |
| | | legal: 'success', adjustment: 'warning', special: 'info', company: 'primary', |
| | | weekday: 'primary', weekend: 'warning', holiday: 'danger', night: 'info', |
| | | regular: 'success', probation: 'info', intern: 'danger' |
| | | } |
| | | return tagMap[type] || 'info' |
| | | } |
| | | legal: "success", |
| | | adjustment: "warning", |
| | | special: "info", |
| | | company: "primary", |
| | | weekday: "primary", |
| | | weekend: "warning", |
| | | holiday: "danger", |
| | | night: "info", |
| | | regular: "success", |
| | | probation: "info", |
| | | intern: "danger", |
| | | }; |
| | | return tagMap[type] || "info"; |
| | | }; |
| | | |
| | | const getTypeLabel = (type) => { |
| | | const getTypeLabel = type => { |
| | | const labelMap = { |
| | | legal: '法定节假日', adjustment: '调休日', special: '特殊假期', company: '公司假期', |
| | | weekday: '工作日加班', weekend: '周末加班', holiday: '节假日加班', night: '深夜加班', |
| | | regular: '正式员工', probation: '试用期员工', intern: '实习生' |
| | | } |
| | | return labelMap[type] || type |
| | | } |
| | | legal: "法定节假日", |
| | | adjustment: "调休日", |
| | | special: "特殊假期", |
| | | company: "公司假期", |
| | | weekday: "工作日加班", |
| | | weekend: "周末加班", |
| | | holiday: "节假日加班", |
| | | night: "深夜加班", |
| | | regular: "正式员工", |
| | | probation: "试用期员工", |
| | | intern: "实习生", |
| | | }; |
| | | return labelMap[type] || type; |
| | | }; |
| | | |
| | | // 打卡记录相关工具函数 |
| | | const getAttendanceTagType = (status) => { |
| | | const getAttendanceTagType = status => { |
| | | const tagMap = { |
| | | normal: 'success', |
| | | late: 'warning', |
| | | early: 'warning', |
| | | absent: 'danger' |
| | | } |
| | | return tagMap[status] || 'info' |
| | | } |
| | | normal: "success", |
| | | late: "warning", |
| | | early: "warning", |
| | | absent: "danger", |
| | | }; |
| | | return tagMap[status] || "info"; |
| | | }; |
| | | |
| | | const getAttendanceStatusLabel = (status) => { |
| | | const getAttendanceStatusLabel = status => { |
| | | const labelMap = { |
| | | normal: '正常', |
| | | late: '迟到', |
| | | early: '早退', |
| | | absent: '缺勤' |
| | | } |
| | | return labelMap[status] || status |
| | | } |
| | | normal: "正常", |
| | | late: "迟到", |
| | | early: "早退", |
| | | absent: "缺勤", |
| | | }; |
| | | return labelMap[status] || status; |
| | | }; |
| | | |
| | | const getTypeOptions = () => { |
| | | if (currentType.value === 'holiday') { |
| | | if (currentType.value === "holiday") { |
| | | return [ |
| | | { label: '法定节假日', value: 'legal' }, |
| | | { label: '调休日', value: 'adjustment' }, |
| | | { label: '特殊假期', value: 'special' }, |
| | | { label: '公司假期', value: 'company' } |
| | | ] |
| | | } else if (currentType.value === 'overtime') { |
| | | { label: "法定节假日", value: "legal" }, |
| | | { label: "调休日", value: "adjustment" }, |
| | | { label: "特殊假期", value: "special" }, |
| | | { label: "公司假期", value: "company" }, |
| | | ]; |
| | | } else if (currentType.value === "overtime") { |
| | | return [ |
| | | { label: '工作日加班', value: 'weekday' }, |
| | | { label: '周末加班', value: 'weekend' }, |
| | | { label: '节假日加班', value: 'holiday' }, |
| | | { label: '深夜加班', value: 'night' } |
| | | ] |
| | | } else if (currentType.value === 'annual') { |
| | | { label: "工作日加班", value: "weekday" }, |
| | | { label: "周末加班", value: "weekend" }, |
| | | { label: "节假日加班", value: "holiday" }, |
| | | { label: "深夜加班", value: "night" }, |
| | | ]; |
| | | } else if (currentType.value === "annual") { |
| | | return [ |
| | | { label: '正式员工', value: 'regular' }, |
| | | { label: '试用期员工', value: 'probation' }, |
| | | { label: '实习生', value: 'intern' } |
| | | ] |
| | | { label: "正式员工", value: "regular" }, |
| | | { label: "试用期员工", value: "probation" }, |
| | | { label: "实习生", value: "intern" }, |
| | | ]; |
| | | } |
| | | return [] |
| | | } |
| | | return []; |
| | | }; |
| | | |
| | | // 计算假期天数 |
| | | const calculateDays = () => { |
| | | try { |
| | | if (form.dateRange && form.dateRange.length === 2 && form.dateRange[0] && form.dateRange[1]) { |
| | | const start = new Date(form.dateRange[0]) |
| | | const end = new Date(form.dateRange[1]) |
| | | form.startDate = start.toISOString().split('T')[0] |
| | | form.endDate = end.toISOString().split('T')[0] |
| | | if ( |
| | | form.dateRange && |
| | | form.dateRange.length === 2 && |
| | | form.dateRange[0] && |
| | | form.dateRange[1] |
| | | ) { |
| | | const start = new Date(form.dateRange[0]); |
| | | const end = new Date(form.dateRange[1]); |
| | | form.startDate = start.toISOString().split("T")[0]; |
| | | form.endDate = end.toISOString().split("T")[0]; |
| | | |
| | | if (isNaN(start.getTime()) || isNaN(end.getTime())) { |
| | | console.warn('无效的日期格式') |
| | | return |
| | | console.warn("无效的日期格式"); |
| | | return; |
| | | } |
| | | |
| | | const diffTime = Math.abs(end - start) |
| | | const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1 |
| | | form.days = diffDays |
| | | const diffTime = Math.abs(end - start); |
| | | const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1; |
| | | form.days = diffDays; |
| | | } |
| | | } catch (error) { |
| | | console.error('计算天数失败:', error) |
| | | console.error("计算天数失败:", error); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 验证时间格式 |
| | | // const validateTime = (time) => { |
| | |
| | | // } |
| | | |
| | | // 验证时间字段 |
| | | const validateTimeField = (fieldName) => { |
| | | const validateTimeField = fieldName => { |
| | | try { |
| | | const value = form[fieldName] |
| | | if (value && typeof value === 'object' && value.hour !== undefined) { |
| | | const value = form[fieldName]; |
| | | if (value && typeof value === "object" && value.hour !== undefined) { |
| | | // 如果是时间对象,转换为字符串格式 |
| | | const hours = value.hour.toString().padStart(2, '0') |
| | | const minutes = value.minute.toString().padStart(2, '0') |
| | | form[fieldName] = `${hours}:${minutes}` |
| | | const hours = value.hour.toString().padStart(2, "0"); |
| | | const minutes = value.minute.toString().padStart(2, "0"); |
| | | form[fieldName] = `${hours}:${minutes}`; |
| | | } |
| | | } catch (error) { |
| | | console.error(`验证时间字段 ${fieldName} 失败:`, error) |
| | | form[fieldName] = '' |
| | | console.error(`验证时间字段 ${fieldName} 失败:`, error); |
| | | form[fieldName] = ""; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 打开弹窗 |
| | | const openDialog = (type, action, row = null) => { |
| | | try { |
| | | currentType.value = type |
| | | currentAction.value = action |
| | | currentType.value = type; |
| | | currentAction.value = action; |
| | | |
| | | if (action === 'add') { |
| | | dialogTitle.value = `新增${getTypeName(type)}` |
| | | currentEditId.value = '' |
| | | resetForm() |
| | | } else if (action === 'edit' && row) { |
| | | dialogTitle.value = `编辑${getTypeName(type)}` |
| | | currentEditId.value = row.id |
| | | fillForm(row) |
| | | if (action === "add") { |
| | | dialogTitle.value = `新增${getTypeName(type)}`; |
| | | currentEditId.value = ""; |
| | | resetForm(); |
| | | } else if (action === "edit" && row) { |
| | | dialogTitle.value = `编辑${getTypeName(type)}`; |
| | | currentEditId.value = row.id; |
| | | fillForm(row); |
| | | } |
| | | |
| | | dialogVisible.value = true |
| | | dialogVisible.value = true; |
| | | } catch (error) { |
| | | console.error('打开弹窗失败:', error) |
| | | ElMessage.error('打开弹窗失败,请重试') |
| | | console.error("打开弹窗失败:", error); |
| | | ElMessage.error("打开弹窗失败,请重试"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const getTypeName = (type) => { |
| | | const getTypeName = type => { |
| | | const nameMap = { |
| | | holiday: '假期', |
| | | annual: '年假规则', |
| | | overtime: '加班规则', |
| | | worktime: '时间段' |
| | | } |
| | | return nameMap[type] || '' |
| | | } |
| | | holiday: "假期", |
| | | annual: "年假规则", |
| | | overtime: "加班规则", |
| | | worktime: "时间段", |
| | | }; |
| | | return nameMap[type] || ""; |
| | | }; |
| | | |
| | | const resetForm = () => { |
| | | Object.assign(form, { |
| | | name: '', |
| | | type: '', |
| | | name: "", |
| | | type: "", |
| | | dateRange: [], |
| | | startDate: '', |
| | | endDate: '', |
| | | startDate: "", |
| | | endDate: "", |
| | | days: 0, |
| | | employeeType: '', |
| | | workYears: '', |
| | | employeeType: "", |
| | | workYears: "", |
| | | annualDays: 0, |
| | | maxCarryOver: 0, |
| | | startTime: '', |
| | | endTime: '', |
| | | workStartTime: '', |
| | | workEndTime: '', |
| | | startTime: "", |
| | | endTime: "", |
| | | workStartTime: "", |
| | | workEndTime: "", |
| | | rate: 1.5, |
| | | flexibleStart: false, |
| | | flexibleMinutes: 30, |
| | | status: 'active' |
| | | }) |
| | | } |
| | | status: "active", |
| | | }); |
| | | }; |
| | | |
| | | const fillForm = (row) => { |
| | | if (currentType.value === 'holiday') { |
| | | const fillForm = row => { |
| | | if (currentType.value === "holiday") { |
| | | Object.assign(form, { |
| | | name: row.name, |
| | | type: row.type, |
| | |
| | | startDate: row.startDate, |
| | | endDate: row.endDate, |
| | | days: row.days, |
| | | status: row.status |
| | | }) |
| | | } else if (currentType.value === 'annual') { |
| | | status: row.status, |
| | | }); |
| | | } else if (currentType.value === "annual") { |
| | | Object.assign(form, { |
| | | employeeType: row.employeeType, |
| | | workYears: row.workYears, |
| | | annualDays: row.annualDays, |
| | | maxCarryOver: row.maxCarryOver, |
| | | status: row.status |
| | | }) |
| | | } else if (currentType.value === 'overtime') { |
| | | status: row.status, |
| | | }); |
| | | } else if (currentType.value === "overtime") { |
| | | Object.assign(form, { |
| | | name: row.name, |
| | | type: row.type, |
| | | startTime: row.startTime || '', |
| | | endTime: row.endTime || '', |
| | | startTime: row.startTime || "", |
| | | endTime: row.endTime || "", |
| | | rate: row.rate, |
| | | status: row.status |
| | | }) |
| | | } else if (currentType.value === 'worktime') { |
| | | status: row.status, |
| | | }); |
| | | } else if (currentType.value === "worktime") { |
| | | Object.assign(form, { |
| | | name: row.name, |
| | | workStartTime: row.startTime || '', |
| | | workEndTime: row.endTime || '', |
| | | workStartTime: row.startTime || "", |
| | | workEndTime: row.endTime || "", |
| | | flexibleStart: row.flexibleStart, |
| | | flexibleMinutes: row.flexibleMinutes, |
| | | status: row.status |
| | | }) |
| | | status: row.status, |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 提交表单 |
| | | const submitForm = async () => { |
| | | try { |
| | | if (!formRef.value) { |
| | | ElMessage.error('表单引用不存在') |
| | | return |
| | | ElMessage.error("表单引用不存在"); |
| | | return; |
| | | } |
| | | |
| | | await formRef.value.validate() |
| | | await formRef.value.validate(); |
| | | |
| | | if (currentAction.value === 'add') { |
| | | addItem() |
| | | } else if (currentAction.value === 'edit') { |
| | | editItem() |
| | | if (currentAction.value === "add") { |
| | | addItem(); |
| | | } else if (currentAction.value === "edit") { |
| | | editItem(); |
| | | } |
| | | |
| | | dialogVisible.value = false |
| | | ElMessage.success('操作成功') |
| | | dialogVisible.value = false; |
| | | ElMessage.success("操作成功"); |
| | | } catch (error) { |
| | | console.error('表单验证失败:', error) |
| | | ElMessage.error('表单验证失败,请检查输入') |
| | | console.error("表单验证失败:", error); |
| | | ElMessage.error("表单验证失败,请检查输入"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const addItem = () => { |
| | | |
| | | if (currentType.value === 'holiday') { |
| | | if (currentType.value === "holiday") { |
| | | const params = { |
| | | name: form.name, |
| | | type: form.type, |
| | | startDate: form.startDate, |
| | | endDate: form.endDate, |
| | | days: form.days, |
| | | status: form.status |
| | | } |
| | | addHolidaySettings(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | addHolidaySettings(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | // dialogVisible.value = false; |
| | | getHolidaySettingsList() |
| | | getHolidaySettingsList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } else if (currentType.value === 'annual') { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else if (currentType.value === "annual") { |
| | | // annualData.value.push(newItem) |
| | | const params = { |
| | | employeeType: form.employeeType, |
| | | workYears: form.workYears, |
| | | annualDays: form.annualDays, |
| | | maxCarryOver: form.maxCarryOver, |
| | | status: form.status |
| | | } |
| | | addAnnualLeaveSetting(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | addAnnualLeaveSetting(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | // dialogVisible.value = false; |
| | | getAnnualLeaveSettingList() |
| | | getAnnualLeaveSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } else if (currentType.value === 'overtime') { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else if (currentType.value === "overtime") { |
| | | const params = { |
| | | name: form.name, |
| | | type: form.type, |
| | | startTime: form.startTime || '', |
| | | endTime: form.endTime || '', |
| | | startTime: form.startTime || "", |
| | | endTime: form.endTime || "", |
| | | rate: form.rate, |
| | | status: form.status |
| | | } |
| | | addOvertimeSetting(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | addOvertimeSetting(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | // dialogVisible.value = false; |
| | | getOvertimeSettingList() |
| | | getOvertimeSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | // newItem.startTime = form.startTime || '' |
| | | // newItem.endTime = form.endTime || '' |
| | | // overtimeData.value.push(newItem) |
| | | } else if (currentType.value === 'worktime') { |
| | | } else if (currentType.value === "worktime") { |
| | | const params = { |
| | | name: form.name, |
| | | startTime: form.workStartTime || '', |
| | | endTime: form.workEndTime || '', |
| | | startTime: form.workStartTime || "", |
| | | endTime: form.workEndTime || "", |
| | | flexibleStart: form.flexibleStart, |
| | | flexibleMinutes: form.flexibleMinutes, |
| | | status: form.status |
| | | } |
| | | addWorkingHoursSetting(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | addWorkingHoursSetting(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | getWorkingHoursSettingList() |
| | | getWorkingHoursSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | // newItem.startTime = form.workStartTime || '' |
| | | // newItem.endTime = form.workEndTime || '' |
| | | // worktimeData.value.push(newItem) |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const editItem = () => { |
| | | let dataArray |
| | | let index |
| | | let dataArray; |
| | | let index; |
| | | |
| | | if (currentType.value === 'holiday') { |
| | | if (currentType.value === "holiday") { |
| | | const params = { |
| | | id: currentEditId.value, |
| | | name: form.name, |
| | | type: form.type, |
| | | startDate: form.dateRange[0].toISOString().split('T')[0], |
| | | endDate: form.dateRange[1].toISOString().split('T')[0], |
| | | startDate: form.dateRange[0].toISOString().split("T")[0], |
| | | endDate: form.dateRange[1].toISOString().split("T")[0], |
| | | days: form.days, |
| | | status: form.status |
| | | } |
| | | updateHolidaySettings(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | updateHolidaySettings(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("更新成功"); |
| | | // dialogVisible.value = false; |
| | | getHolidaySettingsList() |
| | | getHolidaySettingsList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } else if (currentType.value === 'annual') { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else if (currentType.value === "annual") { |
| | | const params = { |
| | | id: currentEditId.value, |
| | | employeeType: form.employeeType, |
| | | workYears: form.workYears, |
| | | annualDays: form.annualDays, |
| | | maxCarryOver: form.maxCarryOver, |
| | | status: form.status |
| | | } |
| | | updateAnnualLeaveSetting(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | updateAnnualLeaveSetting(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("更新成功"); |
| | | getAnnualLeaveSettingList() |
| | | getAnnualLeaveSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } else if (currentType.value === 'overtime') { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else if (currentType.value === "overtime") { |
| | | const params = { |
| | | id: currentEditId.value, |
| | | name: form.name, |
| | | type: form.type, |
| | | startTime: form.startTime || '', |
| | | endTime: form.endTime || '', |
| | | startTime: form.startTime || "", |
| | | endTime: form.endTime || "", |
| | | rate: form.rate, |
| | | status: form.status |
| | | } |
| | | updateOvertimeSetting(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | updateOvertimeSetting(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("更新成功"); |
| | | getOvertimeSettingList() |
| | | getOvertimeSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | |
| | | // dataArray = overtimeData.value |
| | | // index = dataArray.findIndex(item => item.id === currentEditId.value) |
| | |
| | | // status: form.status |
| | | // } |
| | | // } |
| | | } else if (currentType.value === 'worktime') { |
| | | } else if (currentType.value === "worktime") { |
| | | const params = { |
| | | id: currentEditId.value, |
| | | name: form.name, |
| | | startTime: form.workStartTime || '', |
| | | endTime: form.workEndTime || '', |
| | | startTime: form.workStartTime || "", |
| | | endTime: form.workEndTime || "", |
| | | flexibleStart: form.flexibleStart, |
| | | flexibleMinutes: form.flexibleMinutes, |
| | | status: form.status |
| | | } |
| | | updateWorkingHoursSetting(params).then(res => { |
| | | status: form.status, |
| | | }; |
| | | updateWorkingHoursSetting(params) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("更新成功"); |
| | | getWorkingHoursSettingList() |
| | | getWorkingHoursSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | // dataArray = worktimeData.value |
| | | // index = dataArray.findIndex(item => item.id === currentEditId.value) |
| | | // if (index > -1) { |
| | |
| | | // } |
| | | // } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 打卡记录过滤功能 |
| | | const filterAttendanceData = () => { |
| | | let filtered = attendanceData.value |
| | | let filtered = attendanceData.value; |
| | | |
| | | // 按日期过滤 |
| | | if (attendanceDate.value) { |
| | | filtered = filtered.filter(item => item.date === attendanceDate.value) |
| | | filtered = filtered.filter(item => item.date === attendanceDate.value); |
| | | } |
| | | |
| | | // 按状态过滤 |
| | | if (attendanceStatus.value) { |
| | | filtered = filtered.filter(item => item.status === attendanceStatus.value) |
| | | filtered = filtered.filter(item => item.status === attendanceStatus.value); |
| | | } |
| | | |
| | | filteredAttendanceData.value = filtered |
| | | } |
| | | filteredAttendanceData.value = filtered; |
| | | }; |
| | | |
| | | // 导出打卡记录 |
| | | const exportAttendance = () => { |
| | | ElMessage.success('导出功能开发中...') |
| | | } |
| | | ElMessage.success("导出功能开发中..."); |
| | | }; |
| | | |
| | | // 初始化打卡记录假数据 |
| | | const initAttendanceData = () => { |
| | | const mockData = [ |
| | | { |
| | | id: 1, |
| | | employeeName: '陈志强', |
| | | department: '技术部', |
| | | date: '2025-08-15', |
| | | clockInTime: '09:00:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '8.0h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "陈志强", |
| | | department: "技术部", |
| | | date: "2025-08-15", |
| | | clockInTime: "09:00:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "8.0h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 2, |
| | | employeeName: '李雪梅', |
| | | department: '市场部', |
| | | date: '2025-08-16', |
| | | clockInTime: '08:58:00', |
| | | clockOutTime: '18:05:00', |
| | | workHours: '8.12h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "李雪梅", |
| | | department: "市场部", |
| | | date: "2025-08-16", |
| | | clockInTime: "08:58:00", |
| | | clockOutTime: "18:05:00", |
| | | workHours: "8.12h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 3, |
| | | employeeName: '王建华', |
| | | department: '人事部', |
| | | date: '2025-08-16', |
| | | clockInTime: '09:02:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '7.97h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "王建华", |
| | | department: "人事部", |
| | | date: "2025-08-16", |
| | | clockInTime: "09:02:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "7.97h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 4, |
| | | employeeName: '赵晓丽', |
| | | department: '财务部', |
| | | date: '2025-09-02', |
| | | clockInTime: '08:55:00', |
| | | clockOutTime: '18:10:00', |
| | | workHours: '8.25h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "赵晓丽", |
| | | department: "财务部", |
| | | date: "2025-09-02", |
| | | clockInTime: "08:55:00", |
| | | clockOutTime: "18:10:00", |
| | | workHours: "8.25h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 5, |
| | | employeeName: '张国庆', |
| | | department: '技术部', |
| | | date: '2025-09-02', |
| | | clockInTime: '09:00:00', |
| | | clockOutTime: '18:30:00', |
| | | workHours: '8.5h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '加班' |
| | | employeeName: "张国庆", |
| | | department: "技术部", |
| | | date: "2025-09-02", |
| | | clockInTime: "09:00:00", |
| | | clockOutTime: "18:30:00", |
| | | workHours: "8.5h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "加班", |
| | | }, |
| | | { |
| | | id: 6, |
| | | employeeName: '刘明辉', |
| | | department: '运营部', |
| | | date: '2025-09-03', |
| | | clockInTime: '09:05:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '7.92h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "刘明辉", |
| | | department: "运营部", |
| | | date: "2025-09-03", |
| | | clockInTime: "09:05:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "7.92h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 7, |
| | | employeeName: '孙丽华', |
| | | department: '设计部', |
| | | date: '2025-09-03', |
| | | clockInTime: '08:59:00', |
| | | clockOutTime: '18:02:00', |
| | | workHours: '8.05h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "孙丽华", |
| | | department: "设计部", |
| | | date: "2025-09-03", |
| | | clockInTime: "08:59:00", |
| | | clockOutTime: "18:02:00", |
| | | workHours: "8.05h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 8, |
| | | employeeName: '周建军', |
| | | department: '销售部', |
| | | date: '2025-09-04', |
| | | clockInTime: '09:15:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '7.75h', |
| | | status: 'late', |
| | | location: '公司总部', |
| | | remark: '交通堵塞' |
| | | employeeName: "周建军", |
| | | department: "销售部", |
| | | date: "2025-09-04", |
| | | clockInTime: "09:15:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "7.75h", |
| | | status: "late", |
| | | location: "公司总部", |
| | | remark: "交通堵塞", |
| | | }, |
| | | { |
| | | id: 9, |
| | | employeeName: '吴小芳', |
| | | department: '客服部', |
| | | date: '2025-09-04', |
| | | clockInTime: '09:01:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '7.98h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "吴小芳", |
| | | department: "客服部", |
| | | date: "2025-09-04", |
| | | clockInTime: "09:01:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "7.98h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 10, |
| | | employeeName: '马文杰', |
| | | department: '技术部', |
| | | date: '2025-09-05', |
| | | clockInTime: '08:57:00', |
| | | clockOutTime: '17:30:00', |
| | | workHours: '7.55h', |
| | | status: 'early', |
| | | location: '公司总部', |
| | | remark: '有急事提前离开' |
| | | employeeName: "马文杰", |
| | | department: "技术部", |
| | | date: "2025-09-05", |
| | | clockInTime: "08:57:00", |
| | | clockOutTime: "17:30:00", |
| | | workHours: "7.55h", |
| | | status: "early", |
| | | location: "公司总部", |
| | | remark: "有急事提前离开", |
| | | }, |
| | | { |
| | | id: 11, |
| | | employeeName: '林晓东', |
| | | department: '行政部', |
| | | date: '2025-09-05', |
| | | clockInTime: '09:03:00', |
| | | clockOutTime: '18:08:00', |
| | | workHours: '8.08h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "林晓东", |
| | | department: "行政部", |
| | | date: "2025-09-05", |
| | | clockInTime: "09:03:00", |
| | | clockOutTime: "18:08:00", |
| | | workHours: "8.08h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 12, |
| | | employeeName: '黄美玲', |
| | | department: '财务部', |
| | | date: '2025-09-06', |
| | | clockInTime: '', |
| | | clockOutTime: '', |
| | | workHours: '0h', |
| | | status: 'absent', |
| | | location: '', |
| | | remark: '请病假' |
| | | employeeName: "黄美玲", |
| | | department: "财务部", |
| | | date: "2025-09-06", |
| | | clockInTime: "", |
| | | clockOutTime: "", |
| | | workHours: "0h", |
| | | status: "absent", |
| | | location: "", |
| | | remark: "请病假", |
| | | }, |
| | | { |
| | | id: 13, |
| | | employeeName: '郑海涛', |
| | | department: '市场部', |
| | | date: '2025-08-14', |
| | | clockInTime: '09:00:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '8.0h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "郑海涛", |
| | | department: "市场部", |
| | | date: "2025-08-14", |
| | | clockInTime: "09:00:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "8.0h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 14, |
| | | employeeName: '谢丽娟', |
| | | department: '人事部', |
| | | date: '2025-08-20', |
| | | clockInTime: '08:58:00', |
| | | clockOutTime: '18:03:00', |
| | | workHours: '8.08h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "谢丽娟", |
| | | department: "人事部", |
| | | date: "2025-08-20", |
| | | clockInTime: "08:58:00", |
| | | clockOutTime: "18:03:00", |
| | | workHours: "8.08h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 15, |
| | | employeeName: '何志伟', |
| | | department: '技术部', |
| | | date: '2025-08-21', |
| | | clockInTime: '09:10:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '7.83h', |
| | | status: 'late', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "何志伟", |
| | | department: "技术部", |
| | | date: "2025-08-21", |
| | | clockInTime: "09:10:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "7.83h", |
| | | status: "late", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 16, |
| | | employeeName: '许雅芳', |
| | | department: '设计部', |
| | | date: '2025-08-22', |
| | | clockInTime: '09:01:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '7.98h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "许雅芳", |
| | | department: "设计部", |
| | | date: "2025-08-22", |
| | | clockInTime: "09:01:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "7.98h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 17, |
| | | employeeName: '邓建平', |
| | | department: '运营部', |
| | | date: '2025-09-10', |
| | | clockInTime: '08:59:00', |
| | | clockOutTime: '18:05:00', |
| | | workHours: '8.1h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | employeeName: "邓建平", |
| | | department: "运营部", |
| | | date: "2025-09-10", |
| | | clockInTime: "08:59:00", |
| | | clockOutTime: "18:05:00", |
| | | workHours: "8.1h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | { |
| | | id: 18, |
| | | employeeName: '曾小红', |
| | | department: '客服部', |
| | | date: '2025-09-11', |
| | | clockInTime: '09:02:00', |
| | | clockOutTime: '18:00:00', |
| | | workHours: '7.97h', |
| | | status: 'normal', |
| | | location: '公司总部', |
| | | remark: '' |
| | | } |
| | | ] |
| | | employeeName: "曾小红", |
| | | department: "客服部", |
| | | date: "2025-09-11", |
| | | clockInTime: "09:02:00", |
| | | clockOutTime: "18:00:00", |
| | | workHours: "7.97h", |
| | | status: "normal", |
| | | location: "公司总部", |
| | | remark: "", |
| | | }, |
| | | ]; |
| | | |
| | | attendanceData.value = mockData |
| | | filteredAttendanceData.value = mockData |
| | | } |
| | | attendanceData.value = mockData; |
| | | filteredAttendanceData.value = mockData; |
| | | }; |
| | | |
| | | // 删除项目 |
| | | const deleteItem = (type, row) => { |
| | | ElMessageBox.confirm('确定要删除这个项目吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | ElMessageBox.confirm("确定要删除这个项目吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | let ids = []; |
| | | let dataArray |
| | | if (type === 'holiday') { |
| | | ids.push(row.id) |
| | | delHolidaySettings(ids).then(res => { |
| | | let dataArray; |
| | | if (type === "holiday") { |
| | | ids.push(row.id); |
| | | delHolidaySettings(ids) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("删除成功"); |
| | | ids = [] |
| | | getHolidaySettingsList() |
| | | ids = []; |
| | | getHolidaySettingsList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } |
| | | else if (type === 'annual') { |
| | | ids.push(row.id) |
| | | delAnnualLeaveSetting(ids).then(res => { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else if (type === "annual") { |
| | | ids.push(row.id); |
| | | delAnnualLeaveSetting(ids) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("删除成功"); |
| | | ids = [] |
| | | getAnnualLeaveSettingList() |
| | | ids = []; |
| | | getAnnualLeaveSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } |
| | | else if (type === 'overtime') { |
| | | ids.push(row.id) |
| | | delOvertimeSetting(ids).then(res => { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else if (type === "overtime") { |
| | | ids.push(row.id); |
| | | delOvertimeSetting(ids) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("删除成功"); |
| | | ids = [] |
| | | getOvertimeSettingList() |
| | | ids = []; |
| | | getOvertimeSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } |
| | | else if (type === 'worktime') { |
| | | ids.push(row.id) |
| | | delWorkingHoursSetting(ids).then(res => { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else if (type === "worktime") { |
| | | ids.push(row.id); |
| | | delWorkingHoursSetting(ids) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("删除成功"); |
| | | ids = [] |
| | | getWorkingHoursSettingList() |
| | | ids = []; |
| | | getWorkingHoursSettingList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } |
| | | |
| | | // const index = dataArray.findIndex(item => item.id === row.id) |
| | |
| | | // dataArray.splice(index, 1) |
| | | // ElMessage.success('删除成功') |
| | | // } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | // 获取假期设置列表 |
| | | const getHolidaySettingsList = () => { |
| | | // tableLoading.value = true; |
| | | listHolidaySettings({...page.value}) |
| | | .then(res => { |
| | | // tableLoading.value = false; |
| | | holidayData.value = res.data.records |
| | | holidayData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | // tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | // tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 获取年假规则列表 |
| | | const getAnnualLeaveSettingList = () => { |
| | | |
| | | listAnnualLeaveSettingList({...page.value}) |
| | | .then(res => { |
| | | // console.log(res.data) |
| | | annualData.value = res.data.records |
| | | annualData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | }) |
| | | .catch(err => {}); |
| | | }; |
| | | // 获取加班规则列表 |
| | | const getOvertimeSettingList = () => { |
| | | |
| | | listOvertimeSettingList({...page.value}) |
| | | .then(res => { |
| | | // console.log(res.data) |
| | | overtimeData.value = res.data.records |
| | | overtimeData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | }) |
| | | .catch(err => {}); |
| | | }; |
| | | // 获取工作时间规则列表 |
| | | const getWorkingHoursSettingList = () => { |
| | | |
| | | listWorkingHoursSettingList({...page.value}) |
| | | .then(res => { |
| | | // console.log(res.data) |
| | | worktimeData.value = res.data.records |
| | | worktimeData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | }) |
| | | .catch(err => {}); |
| | | }; |
| | | onMounted(() => { |
| | | getHolidaySettingsList() |
| | | getAnnualLeaveSettingList() |
| | | getOvertimeSettingList() |
| | | getWorkingHoursSettingList() |
| | | initAttendanceData() |
| | | console.log('考勤管理页面加载完成') |
| | | }) |
| | | getHolidaySettingsList(); |
| | | getAnnualLeaveSettingList(); |
| | | getOvertimeSettingList(); |
| | | getWorkingHoursSettingList(); |
| | | initAttendanceData(); |
| | | console.log("考勤管理页面加载完成"); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | // 清理工作 |
| | | dialogVisible.value = false |
| | | currentType.value = '' |
| | | currentAction.value = '' |
| | | currentEditId.value = '' |
| | | }) |
| | | dialogVisible.value = false; |
| | | currentType.value = ""; |
| | | currentAction.value = ""; |
| | | currentEditId.value = ""; |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">知识标题:</span> |
| | | <el-input |
| | | v-model="searchForm.title" |
| | | <el-input v-model="searchForm.title" |
| | | style="width: 240px" |
| | | placeholder="请输入知识标题搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title ml10">知识类型:</span> |
| | | <el-select v-model="searchForm.type" clearable @change="handleQuery" style="width: 240px"> |
| | | <el-option label="合同特批" :value="'contract'" /> |
| | | <el-option label="审批案例" :value="'approval'" /> |
| | | <el-option label="解决方案" :value="'solution'" /> |
| | | <el-option label="经验总结" :value="'experience'" /> |
| | | <el-option label="操作指南" :value="'guide'" /> |
| | | <el-select v-model="searchForm.type" |
| | | clearable |
| | | @change="handleQuery" |
| | | style="width: 240px"> |
| | | <el-option label="合同特批" |
| | | :value="'contract'" /> |
| | | <el-option label="审批案例" |
| | | :value="'approval'" /> |
| | | <el-option label="解决方案" |
| | | :value="'solution'" /> |
| | | <el-option label="经验总结" |
| | | :value="'experience'" /> |
| | | <el-option label="操作指南" |
| | | :value="'guide'" /> |
| | | </el-select> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px"> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px"> |
| | | 搜索 |
| | | </el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleExport" style="margin-right: 10px">导出</el-button> |
| | | <el-button type="primary" @click="openForm('add')">新增知识</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">删除</el-button> |
| | | <el-button @click="handleExport" |
| | | style="margin-right: 10px">导出</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增知识</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">删除</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | |
| | | <!-- 新增/编辑知识弹窗 --> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="800px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="知识标题" prop="title"> |
| | | <el-input v-model="form.title" placeholder="请输入知识标题" /> |
| | | <el-form-item label="知识标题" |
| | | prop="title"> |
| | | <el-input v-model="form.title" |
| | | placeholder="请输入知识标题" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="知识类型" prop="type"> |
| | | <el-select v-model="form.type" placeholder="请选择知识类型" style="width: 100%"> |
| | | <el-option label="合同特批" value="contract" /> |
| | | <el-option label="审批案例" value="approval" /> |
| | | <el-option label="解决方案" value="solution" /> |
| | | <el-option label="经验总结" value="experience" /> |
| | | <el-option label="操作指南" value="guide" /> |
| | | <el-form-item label="知识类型" |
| | | prop="type"> |
| | | <el-select v-model="form.type" |
| | | placeholder="请选择知识类型" |
| | | style="width: 100%"> |
| | | <el-option label="合同特批" |
| | | value="contract" /> |
| | | <el-option label="审批案例" |
| | | value="approval" /> |
| | | <el-option label="解决方案" |
| | | value="solution" /> |
| | | <el-option label="经验总结" |
| | | value="experience" /> |
| | | <el-option label="操作指南" |
| | | value="guide" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="适用场景" prop="scenario"> |
| | | <el-input v-model="form.scenario" placeholder="请输入适用场景" /> |
| | | <el-form-item label="适用场景" |
| | | prop="scenario"> |
| | | <el-input v-model="form.scenario" |
| | | placeholder="请输入适用场景" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="解决效率" prop="efficiency"> |
| | | <el-select v-model="form.efficiency" placeholder="请选择解决效率" style="width: 100%"> |
| | | <el-option label="显著提升" value="high" /> |
| | | <el-option label="一般提升" value="medium" /> |
| | | <el-option label="轻微提升" value="low" /> |
| | | <el-form-item label="解决效率" |
| | | prop="efficiency"> |
| | | <el-select v-model="form.efficiency" |
| | | placeholder="请选择解决效率" |
| | | style="width: 100%"> |
| | | <el-option label="显著提升" |
| | | value="high" /> |
| | | <el-option label="一般提升" |
| | | value="medium" /> |
| | | <el-option label="轻微提升" |
| | | value="low" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="问题描述" prop="problem"> |
| | | <el-input |
| | | v-model="form.problem" |
| | | <el-form-item label="问题描述" |
| | | prop="problem"> |
| | | <el-input v-model="form.problem" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请描述遇到的问题" |
| | | /> |
| | | placeholder="请描述遇到的问题" /> |
| | | </el-form-item> |
| | | <el-form-item label="解决方案" prop="solution"> |
| | | <el-input |
| | | v-model="form.solution" |
| | | <el-form-item label="解决方案" |
| | | prop="solution"> |
| | | <el-input v-model="form.solution" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请详细描述解决方案" |
| | | /> |
| | | placeholder="请详细描述解决方案" /> |
| | | </el-form-item> |
| | | <el-form-item label="关键要点" prop="keyPoints"> |
| | | <el-input |
| | | v-model="form.keyPoints" |
| | | <el-form-item label="关键要点" |
| | | prop="keyPoints"> |
| | | <el-input v-model="form.keyPoints" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入关键要点,用逗号分隔" |
| | | /> |
| | | placeholder="请输入关键要点,用逗号分隔" /> |
| | | </el-form-item> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="创建人" prop="creator"> |
| | | <el-input v-model="form.creator" placeholder="请输入创建人" /> |
| | | <el-form-item label="创建人" |
| | | prop="creator"> |
| | | <el-input v-model="form.creator" |
| | | placeholder="请输入创建人" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="使用次数" prop="usageCount"> |
| | | <el-input-number v-model="form.usageCount" :min="0" style="width: 100%" /> |
| | | <el-form-item label="使用次数" |
| | | prop="usageCount"> |
| | | <el-input-number v-model="form.usageCount" |
| | | :min="0" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 查看知识详情弹窗 --> |
| | | <el-dialog |
| | | v-model="viewDialogVisible" |
| | | <el-dialog v-model="viewDialogVisible" |
| | | title="知识详情" |
| | | width="900px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | :close-on-click-modal="false"> |
| | | <div class="knowledge-detail"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="知识标题" :span="2"> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="知识标题" |
| | | :span="2"> |
| | | <span class="detail-title">{{ currentKnowledge.title }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="知识类型"> |
| | |
| | | {{ currentKnowledge.createTime }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="detail-section"> |
| | | <h4>问题描述</h4> |
| | | <div class="detail-content">{{ currentKnowledge.problem }}</div> |
| | | </div> |
| | | |
| | | <div class="detail-section"> |
| | | <h4>解决方案</h4> |
| | | <div class="detail-content">{{ currentKnowledge.solution }}</div> |
| | | </div> |
| | | |
| | | <div class="detail-section"> |
| | | <h4>关键要点</h4> |
| | | <div class="key-points"> |
| | | <el-tag |
| | | v-for="(point, index) in currentKnowledge.keyPoints.split(',')" |
| | | <el-tag v-for="(point, index) in currentKnowledge.keyPoints.split(',')" |
| | | :key="index" |
| | | type="success" |
| | | style="margin-right: 8px; margin-bottom: 8px;" |
| | | > |
| | | style="margin-right: 8px; margin-bottom: 8px;"> |
| | | {{ point.trim() }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="detail-section"> |
| | | <h4>使用统计</h4> |
| | | <div class="usage-stats"> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="copyKnowledge">复制知识</el-button> |
| | | <el-button @click="viewDialogVisible = false">关闭</el-button> |
| | | <el-button type="primary" @click="copyKnowledge">复制知识</el-button> |
| | | <!-- <el-button type="success" @click="markAsFavorite">收藏@</el-button> --> |
| | | </span> |
| | | </template> |
| | |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { listKnowledgeBase, delKnowledgeBase,addKnowledgeBase,updateKnowledgeBase } from "@/api/collaborativeApproval/knowledgeBase.js"; |
| | | import { |
| | | listKnowledgeBase, |
| | | delKnowledgeBase, |
| | | addKnowledgeBase, |
| | | updateKnowledgeBase, |
| | | } from "@/api/collaborativeApproval/knowledgeBase.js"; |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | title: [ |
| | | { required: true, message: "请输入知识标题", trigger: "blur" } |
| | | ], |
| | | type: [ |
| | | { required: true, message: "请选择知识类型", trigger: "change" } |
| | | ], |
| | | problem: [ |
| | | { required: true, message: "请描述遇到的问题", trigger: "blur" } |
| | | ], |
| | | title: [{ required: true, message: "请输入知识标题", trigger: "blur" }], |
| | | type: [{ required: true, message: "请选择知识类型", trigger: "change" }], |
| | | problem: [{ required: true, message: "请描述遇到的问题", trigger: "blur" }], |
| | | solution: [ |
| | | { required: true, message: "请详细描述解决方案", trigger: "blur" } |
| | | ] |
| | | { required: true, message: "请详细描述解决方案", trigger: "blur" }, |
| | | ], |
| | | }; |
| | | |
| | | // 响应式数据 |
| | |
| | | solution: "", |
| | | keyPoints: "", |
| | | creator: "", |
| | | usageCount: 0 |
| | | usageCount: 0, |
| | | }, |
| | | dialogVisible: false, |
| | | dialogTitle: "", |
| | | dialogType: "add", |
| | | viewDialogVisible: false, |
| | | currentKnowledge: {} |
| | | currentKnowledge: {}, |
| | | }); |
| | | |
| | | const { |
| | |
| | | dialogTitle, |
| | | dialogType, |
| | | viewDialogVisible, |
| | | currentKnowledge |
| | | currentKnowledge, |
| | | } = toRefs(data); |
| | | |
| | | // 表单引用 |
| | |
| | | label: "知识类型", |
| | | prop: "type", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | const typeMap = { |
| | | contract: "合同特批", |
| | | approval: "审批案例", |
| | | solution: "解决方案", |
| | | experience: "经验总结", |
| | | guide: "操作指南" |
| | | guide: "操作指南", |
| | | }; |
| | | return typeMap[params] || params; |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | const typeMap = { |
| | | contract: "success", |
| | | approval: "warning", |
| | | solution: "primary", |
| | | experience: "info", |
| | | guide: "danger" |
| | | guide: "danger", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "适用场景", |
| | |
| | | label: "解决效率", |
| | | prop: "efficiency", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | const efficiencyMap = { |
| | | high: "显著提升", |
| | | medium: "一般提升", |
| | | low: "轻微提升" |
| | | low: "轻微提升", |
| | | }; |
| | | return efficiencyMap[params] || params; |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | const typeMap = { |
| | | high: "success", |
| | | medium: "warning", |
| | | low: "info" |
| | | low: "info", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "使用次数", |
| | | prop: "usageCount", |
| | | width: 100, |
| | | align: "center" |
| | | align: "center", |
| | | }, |
| | | { |
| | | label: "创建人", |
| | |
| | | { |
| | | name: "编辑", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | name: "查看", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | viewKnowledge(row); |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | // 模拟数据 |
| | |
| | | "{type}标准化操作流程", |
| | | "{scenario}问题解决方案", |
| | | "{type}最佳实践总结", |
| | | "{scenario}效率提升方案" |
| | | "{scenario}效率提升方案", |
| | | ]; |
| | | |
| | | // 知识类型配置 |
| | |
| | | { type: "approval", label: "审批案例", efficiency: "medium" }, |
| | | { type: "solution", label: "解决方案", efficiency: "high" }, |
| | | { type: "experience", label: "经验总结", efficiency: "medium" }, |
| | | { type: "guide", label: "操作指南", efficiency: "low" } |
| | | { type: "guide", label: "操作指南", efficiency: "low" }, |
| | | ]; |
| | | |
| | | // 场景列表 |
| | | const scenarios = ["大额合同审批", "跨部门协作", "紧急采购", "特殊申请", "流程优化", "问题处理", "标准化建设", "效率提升"]; |
| | | const scenarios = [ |
| | | "大额合同审批", |
| | | "跨部门协作", |
| | | "紧急采购", |
| | | "特殊申请", |
| | | "流程优化", |
| | | "问题处理", |
| | | "标准化建设", |
| | | "效率提升", |
| | | ]; |
| | | |
| | | // 自动生成新数据 |
| | | const generateNewData = () => { |
| | | const newId = (mockData.length + 1).toString(); |
| | | const now = new Date(); |
| | | const randomType = knowledgeTypes[Math.floor(Math.random() * knowledgeTypes.length)]; |
| | | const randomScenario = scenarios[Math.floor(Math.random() * scenarios.length)]; |
| | | const randomType = |
| | | knowledgeTypes[Math.floor(Math.random() * knowledgeTypes.length)]; |
| | | const randomScenario = |
| | | scenarios[Math.floor(Math.random() * scenarios.length)]; |
| | | |
| | | // 生成随机标题 |
| | | let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)]; |
| | | title = title |
| | | .replace('{type}', randomType.label) |
| | | .replace('{scenario}', randomScenario); |
| | | .replace("{type}", randomType.label) |
| | | .replace("{scenario}", randomScenario); |
| | | |
| | | const newKnowledge = { |
| | | id: newId, |
| | |
| | | problem: `在${randomScenario}过程中遇到的问题描述...`, |
| | | solution: `针对${randomScenario}的解决方案和操作步骤...`, |
| | | keyPoints: "关键要点1,关键要点2,关键要点3,关键要点4", |
| | | creator: ["张经理", "李主管", "王专员", "刘总监"][Math.floor(Math.random() * 4)], |
| | | creator: ["张经理", "李主管", "王专员", "刘总监"][ |
| | | Math.floor(Math.random() * 4) |
| | | ], |
| | | usageCount: Math.floor(Math.random() * 20) + 1, |
| | | createTime: now.toLocaleString() |
| | | createTime: now.toLocaleString(), |
| | | }; |
| | | |
| | | // 添加到数据开头 |
| | |
| | | listKnowledgeBase({...page.value, ...searchForm.value}) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 分页处理 |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.value.current = obj.page; |
| | | page.value.size = obj.limit; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 选择变化处理 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedIds.value = selection.map(item => item.id); |
| | | }; |
| | | |
| | |
| | | solution: "", |
| | | keyPoints: "", |
| | | creator: "", |
| | | usageCount: 0 |
| | | usageCount: 0, |
| | | }); |
| | | } else if (type === "edit" && row) { |
| | | dialogTitle.value = "编辑知识"; |
| | |
| | | solution: row.solution, |
| | | keyPoints: row.keyPoints, |
| | | creator: row.creator, |
| | | usageCount: row.usageCount |
| | | usageCount: row.usageCount, |
| | | }); |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // 查看知识详情 |
| | | const viewKnowledge = (row) => { |
| | | const viewKnowledge = row => { |
| | | currentKnowledge.value = { ...row }; |
| | | viewDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 获取类型标签类型 |
| | | const getTypeTagType = (type) => { |
| | | const getTypeTagType = type => { |
| | | const typeMap = { |
| | | contract: "success", |
| | | approval: "warning", |
| | | solution: "primary", |
| | | experience: "info", |
| | | guide: "danger" |
| | | guide: "danger", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | // 获取类型标签文本 |
| | | const getTypeLabel = (type) => { |
| | | const getTypeLabel = type => { |
| | | const typeMap = { |
| | | contract: "合同特批", |
| | | approval: "审批案例", |
| | | solution: "解决方案", |
| | | experience: "经验总结", |
| | | guide: "操作指南" |
| | | guide: "操作指南", |
| | | }; |
| | | return typeMap[type] || type; |
| | | }; |
| | | |
| | | // 获取效率标签类型 |
| | | const getEfficiencyTagType = (efficiency) => { |
| | | const getEfficiencyTagType = efficiency => { |
| | | const typeMap = { |
| | | high: "success", |
| | | medium: "warning", |
| | | low: "info" |
| | | low: "info", |
| | | }; |
| | | return typeMap[efficiency] || "info"; |
| | | }; |
| | | |
| | | // 获取效率标签文本 |
| | | const getEfficiencyLabel = (efficiency) => { |
| | | const getEfficiencyLabel = efficiency => { |
| | | const efficiencyMap = { |
| | | high: "显著提升", |
| | | medium: "一般提升", |
| | | low: "轻微提升" |
| | | low: "轻微提升", |
| | | }; |
| | | return efficiencyMap[efficiency] || efficiency; |
| | | }; |
| | | |
| | | // 获取效率提升百分比 |
| | | const getEfficiencyScore = (efficiency) => { |
| | | const getEfficiencyScore = efficiency => { |
| | | const scoreMap = { |
| | | high: 40, |
| | | medium: 25, |
| | | low: 15 |
| | | low: 15, |
| | | }; |
| | | return scoreMap[efficiency] || 0; |
| | | }; |
| | | |
| | | // 获取平均节省时间 |
| | | const getTimeSaved = (efficiency) => { |
| | | const getTimeSaved = efficiency => { |
| | | const timeMap = { |
| | | high: "2-3天", |
| | | medium: "1-2天", |
| | | low: "0.5-1天" |
| | | low: "0.5-1天", |
| | | }; |
| | | return timeMap[efficiency] || "未知"; |
| | | }; |
| | |
| | | `.trim(); |
| | | |
| | | // 复制到剪贴板 |
| | | navigator.clipboard.writeText(knowledgeText).then(() => { |
| | | navigator.clipboard |
| | | .writeText(knowledgeText) |
| | | .then(() => { |
| | | ElMessage.success("知识内容已复制到剪贴板"); |
| | | }).catch(() => { |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("复制失败,请手动复制"); |
| | | }); |
| | | }; |
| | |
| | | // 收藏知识 |
| | | const markAsFavorite = () => { |
| | | // 增加使用次数 |
| | | const index = mockData.findIndex(item => item.id === currentKnowledge.value.id); |
| | | const index = mockData.findIndex( |
| | | item => item.id === currentKnowledge.value.id |
| | | ); |
| | | if (index !== -1) { |
| | | mockData[index].usageCount += 1; |
| | | currentKnowledge.value.usageCount += 1; |
| | |
| | | await formRef.value.validate(); |
| | | if (dialogType.value === "add") { |
| | | // 新增知识 |
| | | addKnowledgeBase({...form.value}).then(res => { |
| | | addKnowledgeBase({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else { |
| | | updateKnowledgeBase({...form.value}).then(res => { |
| | | updateKnowledgeBase({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("更新成功"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("表单验证失败:", error); |
| | |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | }) |
| | | .then(() => { |
| | | // console.log(selectedIds.value); |
| | | delKnowledgeBase(selectedIds.value).then(res => { |
| | | if(res.code == 200){ |
| | |
| | | selectedIds.value = []; |
| | | getList(); |
| | | } |
| | | }); |
| | | }) |
| | | }).catch(() => { |
| | | .catch(() => { |
| | | // 用户取消 |
| | | }); |
| | | }; |
| | | |
| | | // 导出 |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | const handleExport = () => { |
| | | proxy.download('/knowledgeBase/export', { ...searchForm.value }, '知识库.xlsx') |
| | | } |
| | | proxy.download( |
| | | "/knowledgeBase/export", |
| | | { ...searchForm.value }, |
| | | "知识库.xlsx" |
| | | ); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">通知标题:</span> |
| | | <el-input |
| | | v-model="searchForm.title" |
| | | <el-input v-model="searchForm.title" |
| | | style="width: 240px" |
| | | placeholder="请输入通知标题搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title ml10">通知类型:</span> |
| | | <el-select v-model="searchForm.type" clearable @change="handleQuery" style="width: 240px"> |
| | | <el-option label="放假通知" :value="'holiday'" /> |
| | | <el-option label="处罚通知" :value="'penalty'" /> |
| | | <el-option label="开会通知" :value="'meeting'" /> |
| | | <el-option label="临时通知" :value="'temporary'" /> |
| | | <el-option label="正式通知" :value="'formal'" /> |
| | | <el-select v-model="searchForm.type" |
| | | clearable |
| | | @change="handleQuery" |
| | | style="width: 240px"> |
| | | <el-option label="放假通知" |
| | | :value="'holiday'" /> |
| | | <el-option label="处罚通知" |
| | | :value="'penalty'" /> |
| | | <el-option label="开会通知" |
| | | :value="'meeting'" /> |
| | | <el-option label="临时通知" |
| | | :value="'temporary'" /> |
| | | <el-option label="正式通知" |
| | | :value="'formal'" /> |
| | | </el-select> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px"> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px"> |
| | | 搜索 |
| | | </el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">新增通知</el-button> |
| | | <el-button type="success" @click="openMeetingDialog">在线会议</el-button> |
| | | <el-button type="warning" @click="openFileShareDialog">文件共享</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增通知</el-button> |
| | | <el-button type="success" |
| | | @click="openMeetingDialog">在线会议</el-button> |
| | | <el-button type="warning" |
| | | @click="openFileShareDialog">文件共享</el-button> |
| | | <!-- <el-button type="info" @click="refreshEmployees">刷新员工</el-button> --> |
| | | <el-button type="danger" plain @click="handleDelete">删除</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">删除</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | |
| | | <!-- 新增/编辑通知弹窗 --> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="800px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="通知标题" prop="title"> |
| | | <el-input v-model="form.title" placeholder="请输入通知标题" /> |
| | | <el-form-item label="通知标题" |
| | | prop="title"> |
| | | <el-input v-model="form.title" |
| | | placeholder="请输入通知标题" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="通知类型" prop="type"> |
| | | <el-select v-model="form.type" placeholder="请选择通知类型" style="width: 100%"> |
| | | <el-option label="放假通知" value="holiday" /> |
| | | <el-option label="处罚通知" value="penalty" /> |
| | | <el-option label="开会通知" value="meeting" /> |
| | | <el-option label="临时通知" value="temporary" /> |
| | | <el-option label="正式通知" value="formal" /> |
| | | <el-form-item label="通知类型" |
| | | prop="type"> |
| | | <el-select v-model="form.type" |
| | | placeholder="请选择通知类型" |
| | | style="width: 100%"> |
| | | <el-option label="放假通知" |
| | | value="holiday" /> |
| | | <el-option label="处罚通知" |
| | | value="penalty" /> |
| | | <el-option label="开会通知" |
| | | value="meeting" /> |
| | | <el-option label="临时通知" |
| | | value="temporary" /> |
| | | <el-option label="正式通知" |
| | | value="formal" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="优先级" prop="priority"> |
| | | <el-select v-model="form.priority" placeholder="请选择优先级" style="width: 100%"> |
| | | <el-option label="普通" value="low" /> |
| | | <el-option label="重要" value="medium" /> |
| | | <el-option label="紧急" value="high" /> |
| | | <el-form-item label="优先级" |
| | | prop="priority"> |
| | | <el-select v-model="form.priority" |
| | | placeholder="请选择优先级" |
| | | style="width: 100%"> |
| | | <el-option label="普通" |
| | | value="low" /> |
| | | <el-option label="重要" |
| | | value="medium" /> |
| | | <el-option label="紧急" |
| | | value="high" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="有效期至" prop="expireDate"> |
| | | <el-date-picker |
| | | v-model="form.expireDate" |
| | | <el-form-item label="有效期至" |
| | | prop="expireDate"> |
| | | <el-date-picker v-model="form.expireDate" |
| | | type="date" |
| | | placeholder="请选择有效期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="接收部门" prop="departments"> |
| | | <el-select |
| | | v-model="form.departments" |
| | | <el-form-item label="接收部门" |
| | | prop="departments"> |
| | | <el-select v-model="form.departments" |
| | | multiple |
| | | placeholder="请选择接收部门" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="dept in departments" |
| | | style="width: 100%"> |
| | | <el-option v-for="dept in departments" |
| | | :key="dept" |
| | | :label="dept" |
| | | :value="dept" |
| | | /> |
| | | :value="dept" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="同步方式" prop="syncMethods"> |
| | | <el-form-item label="同步方式" |
| | | prop="syncMethods"> |
| | | <el-checkbox-group v-model="form.syncMethods"> |
| | | <el-checkbox |
| | | v-for="method in syncMethods" |
| | | <el-checkbox v-for="method in syncMethods" |
| | | :key="method.value" |
| | | :label="method.value" |
| | | > |
| | | :label="method.value"> |
| | | {{ method.label }} |
| | | </el-checkbox> |
| | | </el-checkbox-group> |
| | | </el-form-item> |
| | | <el-form-item label="通知内容" prop="content"> |
| | | <el-input |
| | | v-model="form.content" |
| | | <el-form-item label="通知内容" |
| | | prop="content"> |
| | | <el-input v-model="form.content" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入通知内容" |
| | | /> |
| | | placeholder="请输入通知内容" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 在线会议弹窗 --> |
| | | <el-dialog |
| | | v-model="meetingDialogVisible" |
| | | <el-dialog v-model="meetingDialogVisible" |
| | | title="创建在线会议" |
| | | width="700px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-form ref="meetingFormRef" :model="meetingForm" :rules="meetingRules" label-width="120px"> |
| | | <el-form-item label="会议标题" prop="title"> |
| | | <el-input v-model="meetingForm.title" placeholder="请输入会议标题" /> |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="meetingFormRef" |
| | | :model="meetingForm" |
| | | :rules="meetingRules" |
| | | label-width="120px"> |
| | | <el-form-item label="会议标题" |
| | | prop="title"> |
| | | <el-input v-model="meetingForm.title" |
| | | placeholder="请输入会议标题" /> |
| | | </el-form-item> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始时间" prop="startTime"> |
| | | <el-date-picker |
| | | v-model="meetingForm.startTime" |
| | | <el-form-item label="开始时间" |
| | | prop="startTime"> |
| | | <el-date-picker v-model="meetingForm.startTime" |
| | | type="datetime" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | placeholder="请选择开始时间" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议时长" prop="duration"> |
| | | <el-input-number |
| | | v-model="meetingForm.duration" |
| | | <el-form-item label="会议时长" |
| | | prop="duration"> |
| | | <el-input-number v-model="meetingForm.duration" |
| | | :min="15" |
| | | :max="480" |
| | | :step="15" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | <span style="margin-left: 10px">分钟</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="会议平台" prop="platform"> |
| | | <el-select v-model="meetingForm.platform" placeholder="请选择会议平台" style="width: 100%"> |
| | | <el-option |
| | | v-for="platform in meetingPlatforms" |
| | | <el-form-item label="会议平台" |
| | | prop="platform"> |
| | | <el-select v-model="meetingForm.platform" |
| | | placeholder="请选择会议平台" |
| | | style="width: 100%"> |
| | | <el-option v-for="platform in meetingPlatforms" |
| | | :key="platform.value" |
| | | :label="platform.label" |
| | | :value="platform.value" |
| | | /> |
| | | :value="platform.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="参会人员" prop="participants"> |
| | | <el-select |
| | | v-model="meetingForm.participants" |
| | | <el-form-item label="参会人员" |
| | | prop="participants"> |
| | | <el-select v-model="meetingForm.participants" |
| | | multiple |
| | | filterable |
| | | remote |
| | | :remote-method="filterEmployees" |
| | | :loading="employeesLoading" |
| | | placeholder="请选择参会人员" |
| | | style="width: 100%" |
| | | > |
| | | <el-option-group |
| | | v-for="group in employeeGroups" |
| | | style="width: 100%"> |
| | | <el-option-group v-for="group in employeeGroups" |
| | | :key="group.label" |
| | | :label="group.label" |
| | | > |
| | | <el-option |
| | | v-for="employee in group.options" |
| | | :label="group.label"> |
| | | <el-option v-for="employee in group.options" |
| | | :key="employee.value" |
| | | :label="`${employee.label} (${employee.dept})`" |
| | | :value="employee.value" |
| | | > |
| | | :value="employee.value"> |
| | | <div style="display: flex; justify-content: space-between; align-items: center;"> |
| | | <div> |
| | | <div style="font-weight: 500;">{{ employee.label }}</div> |
| | |
| | | 已选择 {{ meetingForm.participants.length }} 人 |
| | | </div> |
| | | <!-- 已选择人员详情 --> |
| | | <div v-if="meetingForm.participants.length > 0" style="margin-top: 10px;"> |
| | | <el-tag |
| | | v-for="participantId in meetingForm.participants" |
| | | <div v-if="meetingForm.participants.length > 0" |
| | | style="margin-top: 10px;"> |
| | | <el-tag v-for="participantId in meetingForm.participants" |
| | | :key="participantId" |
| | | closable |
| | | @close="removeParticipant(participantId)" |
| | | style="margin-right: 8px; margin-bottom: 8px;" |
| | | > |
| | | style="margin-right: 8px; margin-bottom: 8px;"> |
| | | {{ getEmployeeName(participantId) }} |
| | | </el-tag> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="会议描述" prop="description"> |
| | | <el-input |
| | | v-model="meetingForm.description" |
| | | <el-form-item label="会议描述" |
| | | prop="description"> |
| | | <el-input v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入会议描述" |
| | | /> |
| | | placeholder="请输入会议描述" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="createMeeting">创建会议</el-button> |
| | | <el-button @click="meetingDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="createMeeting">创建会议</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 文件共享弹窗 --> |
| | | <el-dialog |
| | | v-model="fileShareDialogVisible" |
| | | <el-dialog v-model="fileShareDialogVisible" |
| | | title="文件共享" |
| | | width="700px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-form ref="fileShareFormRef" :model="fileShareForm" :rules="fileShareRules" label-width="120px"> |
| | | <el-form-item label="共享标题" prop="title"> |
| | | <el-input v-model="fileShareForm.title" placeholder="请输入共享标题" /> |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="fileShareFormRef" |
| | | :model="fileShareForm" |
| | | :rules="fileShareRules" |
| | | label-width="120px"> |
| | | <el-form-item label="共享标题" |
| | | prop="title"> |
| | | <el-input v-model="fileShareForm.title" |
| | | placeholder="请输入共享标题" /> |
| | | </el-form-item> |
| | | <el-form-item label="共享描述" prop="description"> |
| | | <el-input |
| | | v-model="fileShareForm.description" |
| | | <el-form-item label="共享描述" |
| | | prop="description"> |
| | | <el-input v-model="fileShareForm.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入共享描述" |
| | | /> |
| | | placeholder="请输入共享描述" /> |
| | | </el-form-item> |
| | | <el-form-item label="接收部门" prop="departments"> |
| | | <el-select |
| | | v-model="fileShareForm.departments" |
| | | <el-form-item label="接收部门" |
| | | prop="departments"> |
| | | <el-select v-model="fileShareForm.departments" |
| | | multiple |
| | | placeholder="请选择接收部门" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="dept in departments" |
| | | style="width: 100%"> |
| | | <el-option v-for="dept in departments" |
| | | :key="dept" |
| | | :label="dept" |
| | | :value="dept" |
| | | /> |
| | | :value="dept" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="上传文件" prop="files"> |
| | | <el-upload |
| | | ref="uploadRef" |
| | | <el-form-item label="上传文件" |
| | | prop="files"> |
| | | <el-upload ref="uploadRef" |
| | | :auto-upload="false" |
| | | :on-change="handleFileChange" |
| | | :on-remove="removeFile" |
| | | :file-list="fileList" |
| | | multiple |
| | | :limit="10" |
| | | accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.txt,.jpg,.jpeg,.png,.gif" |
| | | > |
| | | accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.txt,.jpg,.jpeg,.png,.gif"> |
| | | <el-button type="primary">选择文件</el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="shareFiles">共享文件</el-button> |
| | | <el-button @click="fileShareDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="shareFiles">共享文件</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js"; |
| | | import { listNotification, addNotification, updateNotification, delNotification,addOnlineMeeting,addFileSharing } from "@/api/collaborativeApproval/notificationManagement.js"; |
| | | import { |
| | | listNotification, |
| | | addNotification, |
| | | updateNotification, |
| | | delNotification, |
| | | addOnlineMeeting, |
| | | addFileSharing, |
| | | } from "@/api/collaborativeApproval/notificationManagement.js"; |
| | | import { id } from "element-plus/es/locales.mjs"; |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | title: [ |
| | | { required: true, message: "请输入通知标题", trigger: "blur" } |
| | | ], |
| | | type: [ |
| | | { required: true, message: "请选择通知类型", trigger: "change" } |
| | | ], |
| | | content: [ |
| | | { required: true, message: "请输入通知内容", trigger: "blur" } |
| | | ] |
| | | title: [{ required: true, message: "请输入通知标题", trigger: "blur" }], |
| | | type: [{ required: true, message: "请选择通知类型", trigger: "change" }], |
| | | content: [{ required: true, message: "请输入通知内容", trigger: "blur" }], |
| | | }; |
| | | |
| | | const meetingRules = { |
| | | title: [ |
| | | { required: true, message: "请输入会议标题", trigger: "blur" } |
| | | ], |
| | | title: [{ required: true, message: "请输入会议标题", trigger: "blur" }], |
| | | startTime: [ |
| | | { required: true, message: "请选择会议开始时间", trigger: "change" } |
| | | { required: true, message: "请选择会议开始时间", trigger: "change" }, |
| | | ], |
| | | participants: [ |
| | | { required: true, message: "请选择参会人员", trigger: "change" } |
| | | ] |
| | | { required: true, message: "请选择参会人员", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | const fileShareRules = { |
| | | title: [ |
| | | { required: true, message: "请输入共享标题", trigger: "blur" } |
| | | ], |
| | | description: [ |
| | | { required: true, message: "请输入共享描述", trigger: "blur" } |
| | | ] |
| | | title: [{ required: true, message: "请输入共享标题", trigger: "blur" }], |
| | | description: [{ required: true, message: "请输入共享描述", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 响应式数据 |
| | |
| | | content: "", |
| | | departments: [], |
| | | expireDate: "", |
| | | syncMethods: [] |
| | | syncMethods: [], |
| | | }, |
| | | dialogVisible: false, |
| | | dialogTitle: "", |
| | |
| | | duration: 60, |
| | | participants: [], |
| | | description: "", |
| | | platform: "wechat" |
| | | platform: "wechat", |
| | | }, |
| | | // 文件共享相关 |
| | | fileShareDialogVisible: false, |
| | |
| | | title: "", |
| | | description: "", |
| | | departments: [], |
| | | files: [] |
| | | files: [], |
| | | }, |
| | | fileList: [] |
| | | fileList: [], |
| | | }); |
| | | |
| | | const { |
| | |
| | | meetingForm, |
| | | fileShareDialogVisible, |
| | | fileShareForm, |
| | | fileList |
| | | fileList, |
| | | } = toRefs(data); |
| | | |
| | | // 表单引用 |
| | |
| | | label: "通知类型", |
| | | prop: "type", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | const typeMap = { |
| | | holiday: "放假通知", |
| | | penalty: "处罚通知", |
| | | meeting: "开会通知", |
| | | temporary: "临时通知", |
| | | formal: "正式通知" |
| | | formal: "正式通知", |
| | | }; |
| | | return typeMap[params] || params; |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | const typeMap = { |
| | | holiday: "success", |
| | | penalty: "danger", |
| | | meeting: "warning", |
| | | temporary: "info", |
| | | formal: "primary" |
| | | formal: "primary", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "优先级", |
| | | prop: "priority", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | const priorityMap = { |
| | | low: "普通", |
| | | medium: "重要", |
| | | high: "紧急" |
| | | high: "紧急", |
| | | }; |
| | | return priorityMap[params] || params; |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | const typeMap = { |
| | | low: "info", |
| | | medium: "warning", |
| | | high: "danger" |
| | | high: "danger", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "状态", |
| | | prop: "status", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | const statusMap = { |
| | | draft: "草稿", |
| | | published: "已发布", |
| | | expired: "已过期" |
| | | expired: "已过期", |
| | | }; |
| | | return statusMap[params] || params; |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | const typeMap = { |
| | | draft: "info", |
| | | published: "success", |
| | | expired: "danger" |
| | | expired: "danger", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "接收部门", |
| | | prop: "departments", |
| | | width: 150, |
| | | showOverflowTooltip: true, |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | if (!params || params.length === 0) return "全部部门"; |
| | | return params.join(", "); |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "有效期至", |
| | | prop: "expireDate", |
| | | width: 150, |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | if (!params) return "永久有效"; |
| | | return params; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "创建时间", |
| | |
| | | { |
| | | name: "编辑", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | name: "发布", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | publishNotification(row); |
| | | }, |
| | | // disabled: (row) => row.status === "published" |
| | |
| | | { |
| | | name: "撤回", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | revokeNotification(row); |
| | | }, |
| | | // disabled: (row) => row.status !== "published" |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | // 通知标题模板 |
| | | const titleTemplates = [ |
| | |
| | | "{dept}部门工作安排通知", |
| | | "关于{project}项目进度的通知", |
| | | "{dept}部门人员调整通知", |
| | | "公司{policy}政策更新通知" |
| | | "公司{policy}政策更新通知", |
| | | ]; |
| | | |
| | | // 通知类型配置 |
| | |
| | | { type: "meeting", label: "开会通知", priority: "medium" }, |
| | | { type: "penalty", label: "处罚通知", priority: "high" }, |
| | | { type: "temporary", label: "临时通知", priority: "low" }, |
| | | { type: "formal", label: "正式通知", priority: "medium" } |
| | | { type: "formal", label: "正式通知", priority: "medium" }, |
| | | ]; |
| | | |
| | | // 部门列表 |
| | | const departments = ["技术部", "销售部", "人事部", "财务部", "运营部", "市场部", "客服部"]; |
| | | const departments = [ |
| | | "技术部", |
| | | "销售部", |
| | | "人事部", |
| | | "财务部", |
| | | "运营部", |
| | | "市场部", |
| | | "客服部", |
| | | ]; |
| | | |
| | | // 人员列表 |
| | | const employees = ref([]); |
| | |
| | | const userResponse = await userListNoPageByTenantId(); |
| | | |
| | | if (userResponse.data) { |
| | | employees.value = userResponse.data.map(user => ({ |
| | | label: user.nickName || user.userName || '未知姓名', |
| | | employees.value = userResponse.data |
| | | .map(user => ({ |
| | | label: user.nickName || user.userName || "未知姓名", |
| | | value: user.userId || user.id, |
| | | dept: user.dept?.deptName || '未知部门', |
| | | phone: user.phonenumber || '', |
| | | email: user.email || '', |
| | | status: user.status || '0' |
| | | })).filter(user => user.status === '0'); // 只显示正常状态的用户 |
| | | dept: user.dept?.deptName || "未知部门", |
| | | phone: user.phonenumber || "", |
| | | email: user.email || "", |
| | | status: user.status || "0", |
| | | })) |
| | | .filter(user => user.status === "0"); // 只显示正常状态的用户 |
| | | } else { |
| | | // 如果系统用户接口失败,使用员工台账接口 |
| | | const response = await staffOnJobListPage({ |
| | | pageNum: 1, |
| | | pageSize: 1000, |
| | | staffState: 1 // 在职状态 |
| | | staffState: 1, // 在职状态 |
| | | }); |
| | | |
| | | if (response.data && response.data.records) { |
| | | employees.value = response.data.records.map(employee => ({ |
| | | label: employee.staffName || employee.name || '未知姓名', |
| | | label: employee.staffName || employee.name || "未知姓名", |
| | | value: employee.staffNo || employee.id || employee.staffId, |
| | | dept: employee.deptName || employee.department || '未知部门', |
| | | phone: employee.phone || employee.mobile || '', |
| | | email: employee.email || '', |
| | | status: '0' |
| | | dept: employee.deptName || employee.department || "未知部门", |
| | | phone: employee.phone || employee.mobile || "", |
| | | email: employee.email || "", |
| | | status: "0", |
| | | })); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error('获取员工列表失败:', error); |
| | | console.error("获取员工列表失败:", error); |
| | | // 如果接口都失败,使用默认数据 |
| | | employees.value = [ |
| | | { label: "张三", value: "001", dept: "技术部", phone: "13800138001", email: "zhangsan@company.com", status: "0" }, |
| | | { label: "李四", value: "002", dept: "销售部", phone: "13800138002", email: "lisi@company.com", status: "0" }, |
| | | { label: "王五", value: "003", dept: "人事部", phone: "13800138003", email: "wangwu@company.com", status: "0" } |
| | | { |
| | | label: "张三", |
| | | value: "001", |
| | | dept: "技术部", |
| | | phone: "13800138001", |
| | | email: "zhangsan@company.com", |
| | | status: "0", |
| | | }, |
| | | { |
| | | label: "李四", |
| | | value: "002", |
| | | dept: "销售部", |
| | | phone: "13800138002", |
| | | email: "lisi@company.com", |
| | | status: "0", |
| | | }, |
| | | { |
| | | label: "王五", |
| | | value: "003", |
| | | dept: "人事部", |
| | | phone: "13800138003", |
| | | email: "wangwu@company.com", |
| | | status: "0", |
| | | }, |
| | | ]; |
| | | } finally { |
| | | employeesLoading.value = false; |
| | |
| | | const employeeGroups = computed(() => { |
| | | const groups = {}; |
| | | employees.value.forEach(employee => { |
| | | const dept = employee.dept || '其他部门'; |
| | | const dept = employee.dept || "其他部门"; |
| | | if (!groups[dept]) { |
| | | groups[dept] = []; |
| | | } |
| | |
| | | .sort() |
| | | .map(dept => ({ |
| | | label: dept, |
| | | options: groups[dept].sort((a, b) => a.label.localeCompare(b.label, 'zh-CN')) |
| | | options: groups[dept].sort((a, b) => |
| | | a.label.localeCompare(b.label, "zh-CN") |
| | | ), |
| | | })); |
| | | }); |
| | | |
| | | // 过滤员工(远程搜索) |
| | | const filterEmployees = (query) => { |
| | | if (query !== '') { |
| | | const filterEmployees = query => { |
| | | if (query !== "") { |
| | | const lowerQuery = query.toLowerCase(); |
| | | return employees.value.filter(employee => |
| | | return employees.value.filter( |
| | | employee => |
| | | employee.label.toLowerCase().includes(lowerQuery) || |
| | | employee.dept.toLowerCase().includes(lowerQuery) || |
| | | (employee.phone && employee.phone.includes(query)) || |
| | |
| | | // 统计各部门人数 |
| | | const deptStats = {}; |
| | | employees.value.forEach(emp => { |
| | | const dept = emp.dept || '其他部门'; |
| | | const dept = emp.dept || "其他部门"; |
| | | deptStats[dept] = (deptStats[dept] || 0) + 1; |
| | | }); |
| | | |
| | | const deptInfo = Object.entries(deptStats) |
| | | .map(([dept, count]) => `${dept}: ${count}人`) |
| | | .join(', '); |
| | | .join(", "); |
| | | |
| | | ElMessage.success(`员工列表刷新完成,共 ${employees.value.length} 人 (${deptInfo})`); |
| | | ElMessage.success( |
| | | `员工列表刷新完成,共 ${employees.value.length} 人 (${deptInfo})` |
| | | ); |
| | | }; |
| | | |
| | | // 获取员工姓名 |
| | | const getEmployeeName = (employeeId) => { |
| | | const getEmployeeName = employeeId => { |
| | | const employee = employees.value.find(emp => emp.value === employeeId); |
| | | return employee ? employee.label : '未知人员'; |
| | | return employee ? employee.label : "未知人员"; |
| | | }; |
| | | |
| | | // 获取员工详细信息 |
| | | const getEmployeeInfo = (employeeId) => { |
| | | const getEmployeeInfo = employeeId => { |
| | | const employee = employees.value.find(emp => emp.value === employeeId); |
| | | if (!employee) return null; |
| | | |
| | |
| | | name: employee.label, |
| | | dept: employee.dept, |
| | | phone: employee.phone, |
| | | email: employee.email |
| | | email: employee.email, |
| | | }; |
| | | }; |
| | | |
| | | // 移除参会人员 |
| | | const removeParticipant = (participantId) => { |
| | | const removeParticipant = participantId => { |
| | | const index = meetingForm.value.participants.indexOf(participantId); |
| | | if (index > -1) { |
| | | meetingForm.value.participants.splice(index, 1); |
| | |
| | | { label: "企业微信", value: "wechat" }, |
| | | { label: "钉钉", value: "dingtalk" }, |
| | | { label: "邮件", value: "email" }, |
| | | { label: "短信", value: "sms" } |
| | | { label: "短信", value: "sms" }, |
| | | ]; |
| | | |
| | | // 会议平台选项 |
| | |
| | | { label: "企业微信会议", value: "wechat" }, |
| | | { label: "钉钉会议", value: "dingtalk" }, |
| | | { label: "腾讯会议", value: "tencent" }, |
| | | { label: "Zoom", value: "zoom" } |
| | | { label: "Zoom", value: "zoom" }, |
| | | ]; |
| | | |
| | | // 自动生成新数据 |
| | | const generateNewData = () => { |
| | | const newId = (mockData.length + 1).toString(); |
| | | const now = new Date(); |
| | | const randomType = notificationTypes[Math.floor(Math.random() * notificationTypes.length)]; |
| | | const randomDept = departments[Math.floor(Math.random() * departments.length)]; |
| | | const randomType = |
| | | notificationTypes[Math.floor(Math.random() * notificationTypes.length)]; |
| | | const randomDept = |
| | | departments[Math.floor(Math.random() * departments.length)]; |
| | | |
| | | // 生成随机标题 |
| | | let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)]; |
| | | title = title |
| | | .replace('{year}', now.getFullYear()) |
| | | .replace('{holiday}', ['春节', '国庆', '中秋', '元旦'][Math.floor(Math.random() * 4)]) |
| | | .replace('{dept}', randomDept) |
| | | .replace('{meeting}', ['周例会', '月度总结', '项目评审', '培训会议'][Math.floor(Math.random() * 4)]) |
| | | .replace('{behavior}', ['考勤', '着装', '工作态度', '团队协作'][Math.floor(Math.random() * 4)]) |
| | | .replace('{company}', ['公司', '集团', '总部'][Math.floor(Math.random() * 4)]) |
| | | .replace('{project}', ['数字化转型', '产品升级', '市场拓展', '人才培养'][Math.floor(Math.random() * 4)]) |
| | | .replace('{policy}', ['考勤', '薪酬', '福利', '晋升'][Math.floor(Math.random() * 4)]); |
| | | .replace("{year}", now.getFullYear()) |
| | | .replace( |
| | | "{holiday}", |
| | | ["春节", "国庆", "中秋", "元旦"][Math.floor(Math.random() * 4)] |
| | | ) |
| | | .replace("{dept}", randomDept) |
| | | .replace( |
| | | "{meeting}", |
| | | ["周例会", "月度总结", "项目评审", "培训会议"][ |
| | | Math.floor(Math.random() * 4) |
| | | ] |
| | | ) |
| | | .replace( |
| | | "{behavior}", |
| | | ["考勤", "着装", "工作态度", "团队协作"][Math.floor(Math.random() * 4)] |
| | | ) |
| | | .replace( |
| | | "{company}", |
| | | ["公司", "集团", "总部"][Math.floor(Math.random() * 4)] |
| | | ) |
| | | .replace( |
| | | "{project}", |
| | | ["数字化转型", "产品升级", "市场拓展", "人才培养"][ |
| | | Math.floor(Math.random() * 4) |
| | | ] |
| | | ) |
| | | .replace( |
| | | "{policy}", |
| | | ["考勤", "薪酬", "福利", "晋升"][Math.floor(Math.random() * 4)] |
| | | ); |
| | | |
| | | // 随机状态 |
| | | const statuses = ['draft', 'published']; |
| | | const statuses = ["draft", "published"]; |
| | | const randomStatus = statuses[Math.floor(Math.random() * statuses.length)]; |
| | | |
| | | // 随机优先级 |
| | | const priorities = ['low', 'medium', 'high']; |
| | | const randomPriority = priorities[Math.floor(Math.random() * priorities.length)]; |
| | | const priorities = ["low", "medium", "high"]; |
| | | const randomPriority = |
| | | priorities[Math.floor(Math.random() * priorities.length)]; |
| | | |
| | | const newNotification = { |
| | | id: newId, |
| | |
| | | status: randomStatus, |
| | | content: `这是${title}的详细内容,请相关人员注意查看...`, |
| | | departments: [randomDept], |
| | | expireDate: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], // 30天后过期 |
| | | expireDate: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000) |
| | | .toISOString() |
| | | .split("T")[0], // 30天后过期 |
| | | syncMethods: ["wechat", "dingtalk"], |
| | | createTime: now.toLocaleString() |
| | | createTime: now.toLocaleString(), |
| | | }; |
| | | |
| | | // 添加到数据开头 |
| | |
| | | listNotification({...page.value, ...searchForm.value}) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | tableData.value = res.data.records; |
| | | page.value.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 分页处理 |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.value.current = obj.page; |
| | | page.value.size = obj.limit; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 选择变化处理 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedIds.value = selection.map(item => item.id); |
| | | }; |
| | | |
| | |
| | | departments: [], |
| | | expireDate: "", |
| | | status: "draft", |
| | | syncMethods: [] |
| | | syncMethods: [], |
| | | }); |
| | | } else if (type === "edit" && row) { |
| | | dialogTitle.value = "编辑通知"; |
| | |
| | | departments: row.departments || [], |
| | | expireDate: row.expireDate || "", |
| | | status: row.status, |
| | | syncMethods: row.syncMethods || [] |
| | | syncMethods: row.syncMethods || [], |
| | | }); |
| | | } |
| | | dialogVisible.value = true; |
| | |
| | | duration: 60, |
| | | participants: [], |
| | | description: "", |
| | | platform: "wechat" |
| | | platform: "wechat", |
| | | }); |
| | | meetingDialogVisible.value = true; |
| | | }; |
| | |
| | | title: "", |
| | | description: "", |
| | | departments: [], |
| | | files: [] |
| | | files: [], |
| | | }); |
| | | fileList.value = []; |
| | | fileShareDialogVisible.value = true; |
| | |
| | | |
| | | if (dialogType.value === "add") { |
| | | // 新增通知 |
| | | addNotification({...form.value}).then(res => { |
| | | addNotification({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else { |
| | | // 编辑通知 |
| | | updateNotification({...form.value}).then(res => { |
| | | updateNotification({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("更新成功"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("表单验证失败:", error); |
| | |
| | | duration: meetingForm.value.duration, |
| | | participants: meetingForm.value.participants, |
| | | description: meetingForm.value.description, |
| | | platform: meetingForm.value.platform |
| | | platform: meetingForm.value.platform, |
| | | }; |
| | | // 新增会议 |
| | | addOnlineMeeting({...meetingInfo}).then(res => { |
| | | addOnlineMeeting({ ...meetingInfo }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("会议添加成功"); |
| | | meetingDialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | // 模拟发送到企业微信/钉钉 |
| | | // const platformName = meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "未知平台"; |
| | | // ElMessage.success(`会议创建成功!会议ID: ${meetingInfo.meetingId},将通过${platformName}发送通知`); |
| | | |
| | | // 获取参会人员信息 |
| | | const participantNames = meetingForm.value.participants.map(participantId => { |
| | | const employee = employees.value.find(emp => emp.value === participantId); |
| | | return employee ? employee.label : '未知人员'; |
| | | }).join('、'); |
| | | const participantNames = meetingForm.value.participants |
| | | .map(participantId => { |
| | | const employee = employees.value.find( |
| | | emp => emp.value === participantId |
| | | ); |
| | | return employee ? employee.label : "未知人员"; |
| | | }) |
| | | .join("、"); |
| | | |
| | | // 获取参会人员详细信息 |
| | | const participantDetails = meetingForm.value.participants.map(participantId => { |
| | | const employee = employees.value.find(emp => emp.value === participantId); |
| | | return employee ? { |
| | | const participantDetails = meetingForm.value.participants |
| | | .map(participantId => { |
| | | const employee = employees.value.find( |
| | | emp => emp.value === participantId |
| | | ); |
| | | return employee |
| | | ? { |
| | | name: employee.label, |
| | | dept: employee.dept, |
| | | phone: employee.phone, |
| | | email: employee.email |
| | | } : null; |
| | | }).filter(Boolean); |
| | | email: employee.email, |
| | | } |
| | | : null; |
| | | }) |
| | | .filter(Boolean); |
| | | |
| | | // 将会议信息添加到通知列表 |
| | | const meetingNotification = { |
| | |
| | | type: "meeting", |
| | | priority: "high", |
| | | status: "published", |
| | | content: `会议时间: ${meetingInfo.startTime},时长: ${meetingInfo.duration}分钟,平台: ${meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "未知平台"},参会人员: ${participantNames},共${participantDetails.length}人`, |
| | | content: `会议时间: ${meetingInfo.startTime},时长: ${ |
| | | meetingInfo.duration |
| | | }分钟,平台: ${ |
| | | meetingPlatforms.find(p => p.value === meetingForm.value.platform) |
| | | ?.label || "未知平台" |
| | | },参会人员: ${participantNames},共${participantDetails.length}人`, |
| | | departments: [], |
| | | expireDate: "", |
| | | syncMethods: [meetingForm.value.platform] |
| | | syncMethods: [meetingForm.value.platform], |
| | | }; |
| | | addNotification({...meetingNotification}).then(res => { |
| | | addNotification({ ...meetingNotification }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | // dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | // mockData.unshift(meetingNotification); |
| | | // getList(); |
| | | } catch (error) { |
| | |
| | | }; |
| | | |
| | | // 文件上传处理 |
| | | const handleFileChange = (file) => { |
| | | const handleFileChange = file => { |
| | | const isLt10M = file.size / 1024 / 1024 < 10; |
| | | if (!isLt10M) { |
| | | ElMessage.error("上传文件大小不能超过 10MB!"); |
| | |
| | | name: file.name, |
| | | size: file.size, |
| | | type: file.type, |
| | | uid: file.uid |
| | | uid: file.uid, |
| | | }; |
| | | |
| | | fileList.value.push(fileInfo); |
| | |
| | | }; |
| | | |
| | | // 移除文件 |
| | | const removeFile = (file) => { |
| | | const removeFile = file => { |
| | | const index = fileList.value.findIndex(item => item.uid === file.uid); |
| | | if (index !== -1) { |
| | | const index2 = fileShareForm.value.files.findIndex(item => item.uid === file.uid); |
| | | const index2 = fileShareForm.value.files.findIndex( |
| | | item => item.uid === file.uid |
| | | ); |
| | | if (index2 !== -1) { |
| | | fileShareForm.value.files.splice(index2, 1); |
| | | } |
| | |
| | | departments: fileShareForm.value.departments, |
| | | files: fileShareForm.value.files, |
| | | }; |
| | | addFileSharing({...shareInfo}).then(res => { |
| | | addFileSharing({ ...shareInfo }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("文件共享成功"); |
| | | fileShareDialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | |
| | | // ElMessage.success(`文件共享成功!共享ID: ${shareInfo.shareId},已通知相关部门`); |
| | | |
| | | |
| | | // 将文件共享信息添加到通知列表 |
| | | const fileShareNotification = { |
| | |
| | | expireDate: "", |
| | | syncMethods: ["wechat", "dingtalk"], |
| | | }; |
| | | addNotification({...fileShareNotification}).then(res => { |
| | | addNotification({ ...fileShareNotification }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | // dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | |
| | | // mockData.unshift(fileShareNotification); |
| | | // getList(); |
| | |
| | | }; |
| | | |
| | | // 发布通知 |
| | | const publishNotification = (row) => { |
| | | const publishNotification = row => { |
| | | Object.assign(form.value, { |
| | | id: row.id, |
| | | title: row.title, |
| | |
| | | departments: row.departments || [], |
| | | expireDate: row.expireDate || "", |
| | | status: row.status, |
| | | syncMethods: row.syncMethods || [] |
| | | syncMethods: row.syncMethods || [], |
| | | }); |
| | | form.value.status = "published"; |
| | | updateNotification({...form.value}).then(res => { |
| | | updateNotification({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("通知发布成功"); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | }; |
| | | |
| | | // 撤回通知 |
| | | const revokeNotification = (row) => { |
| | | const revokeNotification = row => { |
| | | Object.assign(form.value, { |
| | | id: row.id, |
| | | title: row.title, |
| | |
| | | departments: row.departments || [], |
| | | expireDate: row.expireDate || "", |
| | | status: row.status, |
| | | syncMethods: row.syncMethods || [] |
| | | syncMethods: row.syncMethods || [], |
| | | }); |
| | | form.value.status = "draft"; |
| | | updateNotification({...form.value}).then(res => { |
| | | updateNotification({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("通知已撤回"); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | }; |
| | | |
| | | // 删除通知 |
| | |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | delNotification(ids).then(res => { |
| | | }) |
| | | .then(() => { |
| | | delNotification(ids) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("删除成功"); |
| | | selectedIds.value = []; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | }).catch(() => { |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | // 用户取消 |
| | | }); |
| | | }; |
| | |
| | | <!-- 页面标题 --> |
| | | <div class="page-header"> |
| | | <h2>会议草稿</h2> |
| | | <el-button type="primary" @click="handleAdd"> |
| | | <el-icon><Plus /></el-icon> |
| | | <el-button type="primary" |
| | | @click="handleAdd"> |
| | | <el-icon> |
| | | <Plus /> |
| | | </el-icon> |
| | | 新建草稿 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- 搜索区域 --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" label-width="100px" inline> |
| | | <el-form :model="searchForm" |
| | | label-width="100px" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable /> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="会议日期"> |
| | | <el-date-picker |
| | | v-model="searchForm.meetingDate" |
| | | <el-date-picker v-model="searchForm.meetingDate" |
| | | type="date" |
| | | placeholder="请选择会议日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- 草稿列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="draftList" border> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="room" label="会议室" align="center" width="120" /> |
| | | <el-table-column prop="host" label="主持人" align="center" width="120" /> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="draftList" |
| | | border> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="room" |
| | | label="会议室" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主持人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="createTime" label="创建时间" align="center" width="180" /> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column prop="createTime" |
| | | label="创建时间" |
| | | align="center" |
| | | width="180" /> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDraft(scope.row)">查看</el-button> |
| | | <el-button type="primary" link @click="editDraft(scope.row)">编辑</el-button> |
| | | <el-button type="danger" link @click="deleteDraft(scope.row)">删除</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="viewDraft(scope.row)">查看</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="editDraft(scope.row)">编辑</el-button> |
| | | <el-button type="danger" |
| | | link |
| | | @click="deleteDraft(scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议草稿详情对话框 --> |
| | | <el-dialog |
| | | title="会议草稿详情" |
| | | <el-dialog title="会议草稿详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | width="800px"> |
| | | <div v-if="currentDraft"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentDraft.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议编号">{{ currentDraft.meetingId }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议室">{{ currentDraft.room }}</el-descriptions-item> |
| | | <el-descriptions-item label="主持人">{{ currentDraft.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentDraft.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="创建时间">{{ currentDraft.createTime }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | {{ currentDraft.participantList }} |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>会议说明</h4> |
| | | <div class="meeting-description">{{ currentDraft.description }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 新建/编辑草稿对话框 --> |
| | | <el-dialog |
| | | :title="dialogTitle" |
| | | <el-dialog :title="dialogTitle" |
| | | v-model="editDialogVisible" |
| | | width="700px" |
| | | > |
| | | <el-form :model="meetingForm" :rules="rules" ref="meetingFormRef" label-width="100px"> |
| | | <el-form-item label="会议主题" prop="title"> |
| | | <el-input v-model="meetingForm.title" placeholder="请输入会议主题" /> |
| | | width="700px"> |
| | | <el-form :model="meetingForm" |
| | | :rules="rules" |
| | | ref="meetingFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="会议主题" |
| | | prop="title"> |
| | | <el-input v-model="meetingForm.title" |
| | | placeholder="请输入会议主题" /> |
| | | </el-form-item> |
| | | <el-form-item label="会议室" prop="room"> |
| | | <el-select v-model="meetingForm.roomId" placeholder="请选择会议室" style="width: 100%"> |
| | | <el-option v-for="(v,k) in roomList" :label="v.name" :value="v.id" /> |
| | | <el-form-item label="会议室" |
| | | prop="room"> |
| | | <el-select v-model="meetingForm.roomId" |
| | | placeholder="请选择会议室" |
| | | style="width: 100%"> |
| | | <el-option v-for="(v,k) in roomList" |
| | | :label="v.name" |
| | | :value="v.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="主持人" prop="host"> |
| | | <el-input v-model="meetingForm.host" placeholder="请输入主持人" /> |
| | | <el-form-item label="主持人" |
| | | prop="host"> |
| | | <el-input v-model="meetingForm.host" |
| | | placeholder="请输入主持人" /> |
| | | </el-form-item> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议日期" prop="meetingDate"> |
| | | <el-date-picker |
| | | v-model="meetingForm.meetingDate" |
| | | <el-form-item label="会议日期" |
| | | prop="meetingDate"> |
| | | <el-date-picker v-model="meetingForm.meetingDate" |
| | | type="date" |
| | | placeholder="请选择会议日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | :disabled-date="disabledDate" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始时间" prop="startTime"> |
| | | <el-select |
| | | v-model="meetingForm.startTime" |
| | | <el-form-item label="开始时间" |
| | | prop="startTime"> |
| | | <el-select v-model="meetingForm.startTime" |
| | | placeholder="请选择开始时间" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in timeOptions" |
| | | style="width: 100%"> |
| | | <el-option v-for="time in timeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | :value="time.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="结束时间" prop="endTime"> |
| | | <el-select |
| | | v-model="meetingForm.endTime" |
| | | <el-form-item label="结束时间" |
| | | prop="endTime"> |
| | | <el-select v-model="meetingForm.endTime" |
| | | placeholder="请选择结束时间" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in timeOptions" |
| | | style="width: 100%"> |
| | | <el-option v-for="time in timeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | :value="time.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="参会人数" prop="participants"> |
| | | <el-input |
| | | v-model="meetingForm.participants" |
| | | <el-form-item label="参会人数" |
| | | prop="participants"> |
| | | <el-input v-model="meetingForm.participants" |
| | | type="number" |
| | | placeholder="请输入参会人数" |
| | | /> |
| | | placeholder="请输入参会人数" /> |
| | | </el-form-item> |
| | | <el-form-item label="参会人员" prop="participants"> |
| | | <el-input |
| | | v-model="meetingForm.participantList" |
| | | <el-form-item label="参会人员" |
| | | prop="participants"> |
| | | <el-input v-model="meetingForm.participantList" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入参会人员,用逗号分隔" |
| | | /> |
| | | placeholder="请输入参会人员,用逗号分隔" /> |
| | | </el-form-item> |
| | | <el-form-item label="会议说明"> |
| | | <el-input |
| | | v-model="meetingForm.description" |
| | | <el-input v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入会议说明" |
| | | /> |
| | | placeholder="请输入会议说明" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">保 存</el-button> |
| | | <el-button @click="editDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="submitForm">保 存</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getRoomEnum,getDraftList,saveDraft,delDraft} from '@/api/collaborativeApproval/meeting.js' |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getDraftList, |
| | | saveDraft, |
| | | delDraft, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import dayjs from "dayjs"; |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | const total = ref(0); |
| | | |
| | | // 草稿列表数据 |
| | | const draftList = ref([]) |
| | | const draftList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | meetingDate: '' |
| | | }) |
| | | title: "", |
| | | meetingDate: "", |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const editDialogVisible = ref(false) |
| | | const detailDialogVisible = ref(false); |
| | | const editDialogVisible = ref(false); |
| | | |
| | | const roomList = ref([]) |
| | | const roomList = ref([]); |
| | | |
| | | // 对话框标题 |
| | | const dialogTitle = ref('') |
| | | const dialogTitle = ref(""); |
| | | |
| | | // 当前查看的草稿 |
| | | const currentDraft = ref(null) |
| | | const currentDraft = ref(null); |
| | | |
| | | // 表单引用 |
| | | const meetingFormRef = ref(null) |
| | | const meetingFormRef = ref(null); |
| | | |
| | | // 时间选项(以半小时为间隔,工作时间8:00-18:00) |
| | | const timeOptions = ref([]) |
| | | const timeOptions = ref([]); |
| | | |
| | | // 表单数据 |
| | | const meetingForm = reactive({ |
| | | id: '', |
| | | meetingId: '', |
| | | title: '', |
| | | roomId: '', |
| | | host: '', |
| | | meetingDate: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | id: "", |
| | | meetingId: "", |
| | | title: "", |
| | | roomId: "", |
| | | host: "", |
| | | meetingDate: "", |
| | | startTime: "", |
| | | endTime: "", |
| | | participants: 0, |
| | | participantList: '', |
| | | description: '', |
| | | createTime: '' |
| | | }) |
| | | participantList: "", |
| | | description: "", |
| | | createTime: "", |
| | | }); |
| | | |
| | | // 表单校验规则 |
| | | const rules = { |
| | | title: [{ required: true, message: '请输入会议主题', trigger: 'blur' }], |
| | | roomId: [{ required: true, message: '请选择会议室', trigger: 'change' }], |
| | | host: [{ required: true, message: '请输入主持人', trigger: 'blur' }], |
| | | meetingDate: [{ required: true, message: '请选择会议日期', trigger: 'change' }], |
| | | startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }], |
| | | endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }] |
| | | } |
| | | title: [{ required: true, message: "请输入会议主题", trigger: "blur" }], |
| | | roomId: [{ required: true, message: "请选择会议室", trigger: "change" }], |
| | | host: [{ required: true, message: "请输入主持人", trigger: "blur" }], |
| | | meetingDate: [ |
| | | { required: true, message: "请选择会议日期", trigger: "change" }, |
| | | ], |
| | | startTime: [{ required: true, message: "请选择开始时间", trigger: "change" }], |
| | | endTime: [{ required: true, message: "请选择结束时间", trigger: "change" }], |
| | | }; |
| | | |
| | | // 初始化时间选项(以半小时为间隔,工作时间8:00-18:00) |
| | | const initTimeOptions = () => { |
| | | const options = [] |
| | | const options = []; |
| | | for (let hour = 8; hour <= 18; hour++) { |
| | | // 每个小时添加两个选项:整点和半点 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:00`, |
| | | label: `${hour.toString().padStart(2, '0')}:00` |
| | | }) |
| | | value: `${hour.toString().padStart(2, "0")}:00`, |
| | | label: `${hour.toString().padStart(2, "0")}:00`, |
| | | }); |
| | | |
| | | if (hour < 18) { // 18:00之后没有半点选项 |
| | | if (hour < 18) { |
| | | // 18:00之后没有半点选项 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:30`, |
| | | label: `${hour.toString().padStart(2, '0')}:30` |
| | | }) |
| | | value: `${hour.toString().padStart(2, "0")}:30`, |
| | | label: `${hour.toString().padStart(2, "0")}:30`, |
| | | }); |
| | | } |
| | | } |
| | | timeOptions.value = options |
| | | } |
| | | timeOptions.value = options; |
| | | }; |
| | | |
| | | // 禁用日期(禁用今天之前的日期) |
| | | const disabledDate = (time) => { |
| | | const disabledDate = time => { |
| | | // 禁用今天之前的日期 |
| | | return time.getTime() < Date.now() - 86400000 |
| | | } |
| | | return time.getTime() < Date.now() - 86400000; |
| | | }; |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | loading.value = true; |
| | | |
| | | let resp = await getDraftList({...queryParams,...searchForm}) |
| | | queryParams.current = resp.data.current |
| | | let resp = await getDraftList({ ...queryParams, ...searchForm }); |
| | | queryParams.current = resp.data.current; |
| | | draftList.value = resp.data.records.map(it=>{ |
| | | it.room = roomList.value.find(room=>it.roomId===room.id).name ?? "" |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format("HH:mm")} ~ ${dayjs(it.endTime).format("HH:mm")}` |
| | | return it |
| | | }) |
| | | it.room = roomList.value.find(room => it.roomId === room.id).name ?? ""; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm")}`; |
| | | return it; |
| | | }); |
| | | |
| | | loading.value = false |
| | | |
| | | } |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | queryParams.pageNum = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | createTime: [] |
| | | }) |
| | | handleSearch() |
| | | } |
| | | title: "", |
| | | createTime: [], |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 添加按钮操作 |
| | | const handleAdd = () => { |
| | | dialogTitle.value = '新建草稿' |
| | | resetForm() |
| | | editDialogVisible.value = true |
| | | } |
| | | dialogTitle.value = "新建草稿"; |
| | | resetForm(); |
| | | editDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 查看草稿详情 |
| | | const viewDraft = (row) => { |
| | | currentDraft.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | const viewDraft = row => { |
| | | currentDraft.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 编辑草稿 |
| | | const editDraft = (row) => { |
| | | dialogTitle.value = '编辑草稿' |
| | | const editDraft = row => { |
| | | dialogTitle.value = "编辑草稿"; |
| | | Object.assign(meetingForm, { |
| | | id: row.id, |
| | | meetingId: row.meetingId, |
| | |
| | | room: row.room, |
| | | roomId: row.id, |
| | | host: row.host, |
| | | meetingDate: row.meetingTime.split(' ')[0], |
| | | startTime: row.meetingTime.split(' ')[1], |
| | | endTime: row.meetingTime.split(' ')[3], |
| | | meetingDate: row.meetingTime.split(" ")[0], |
| | | startTime: row.meetingTime.split(" ")[1], |
| | | endTime: row.meetingTime.split(" ")[3], |
| | | participants: row.participants, |
| | | participantList: row.participantList, |
| | | description: row.description, |
| | | createTime: row.createTime |
| | | }) |
| | | editDialogVisible.value = true |
| | | } |
| | | createTime: row.createTime, |
| | | }); |
| | | editDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 删除草稿 |
| | | const deleteDraft = (row) => { |
| | | ElMessageBox.confirm( |
| | | `确认删除会议草稿 "${row.title}"?`, |
| | | '删除草稿', |
| | | { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | } |
| | | ).then(() => { |
| | | delDraft(row.id).then(resp=>{ |
| | | ElMessage.success('草稿删除成功') |
| | | getList() |
| | | const deleteDraft = row => { |
| | | ElMessageBox.confirm(`确认删除会议草稿 "${row.title}"?`, "删除草稿", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | |
| | | }).catch(() => {}) |
| | | } |
| | | .then(() => { |
| | | delDraft(row.id).then(resp => { |
| | | ElMessage.success("草稿删除成功"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // 重置表单 |
| | | const resetForm = () => { |
| | | Object.assign(meetingForm, { |
| | | id: '', |
| | | meetingId: '', |
| | | title: '', |
| | | room: '', |
| | | host: '', |
| | | meetingDate: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | id: "", |
| | | meetingId: "", |
| | | title: "", |
| | | room: "", |
| | | host: "", |
| | | meetingDate: "", |
| | | startTime: "", |
| | | endTime: "", |
| | | participants: 0, |
| | | participantList: '', |
| | | description: '', |
| | | createTime: '' |
| | | }) |
| | | } |
| | | participantList: "", |
| | | description: "", |
| | | createTime: "", |
| | | }); |
| | | }; |
| | | |
| | | // 提交表单 |
| | | const submitForm = () => { |
| | | meetingFormRef.value.validate((valid) => { |
| | | meetingFormRef.value.validate(valid => { |
| | | if (valid) { |
| | | let formData = {...meetingForm} |
| | | formData.startTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.startTime).format("YYYY-MM-DD HH:mm:ss") |
| | | formData.endTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.endTime).format("YYYY-MM-DD HH:mm:ss") |
| | | let formData = { ...meetingForm }; |
| | | formData.startTime = dayjs( |
| | | meetingForm.meetingDate + " " + meetingForm.startTime |
| | | ).format("YYYY-MM-DD HH:mm:ss"); |
| | | formData.endTime = dayjs( |
| | | meetingForm.meetingDate + " " + meetingForm.endTime |
| | | ).format("YYYY-MM-DD HH:mm:ss"); |
| | | saveDraft(formData).then(()=>{ |
| | | ElMessage.success('保存成功') |
| | | editDialogVisible.value = false |
| | | getList() |
| | | }) |
| | | ElMessage.success("保存成功"); |
| | | editDialogVisible.value = false; |
| | | getList(); |
| | | }); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(() => { |
| | | initTimeOptions() |
| | | getList() |
| | | getRoomEnum().then((res) => { |
| | | roomList.value = res.data |
| | | }) |
| | | }) |
| | | initTimeOptions(); |
| | | getList(); |
| | | getRoomEnum().then(res => { |
| | | roomList.value = res.data; |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="page-header"> |
| | | <h2>会议发布</h2> |
| | | </div> |
| | | |
| | | <!-- 搜索区域 --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" inline> |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable/> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="发布状态"> |
| | | <el-select style="width: 100px" v-model="searchForm.status" placeholder="请选择发布状态" clearable> |
| | | <el-option label="待发布" value="0"/> |
| | | <el-option label="已发布" value="1"/> |
| | | <el-select style="width: 100px" |
| | | v-model="searchForm.status" |
| | | placeholder="请选择发布状态" |
| | | clearable> |
| | | <el-option label="待发布" |
| | | value="0" /> |
| | | <el-option label="已发布" |
| | | value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- 会议发布列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="approvalList" border> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120"/> |
| | | <el-table-column prop="host" label="主理人" align="center" width="120"/> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="approvalList" |
| | | border> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主理人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150"/> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="发布状态" align="center" width="120"> |
| | | <el-table-column prop="status" |
| | | label="发布状态" |
| | | align="center" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | v-if="scope.row.status == '0'" |
| | | <el-button type="primary" |
| | | link |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button v-if="scope.row.status == '0'" |
| | | type="primary" |
| | | link |
| | | @click="handleApproval(scope.row)" |
| | | > |
| | | @click="handleApproval(scope.row)"> |
| | | 发布 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主理人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="发布状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="发布状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 会议发布对话框 --> |
| | | <el-dialog |
| | | title="会议发布" |
| | | v-model="approvalDialogVisible" |
| | | > |
| | | <el-dialog title="会议发布" |
| | | v-model="approvalDialogVisible"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="approval-opinion mt-20"> |
| | | <h4>发布意见</h4> |
| | | <el-input |
| | | v-model="publishComment" |
| | | <el-input v-model="publishComment" |
| | | type="textarea" |
| | | placeholder="请输入发布意见" |
| | | :rows="4" |
| | | /> |
| | | :rows="4" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitApproval('1')">发 布</el-button> |
| | | <el-button @click="approvalDialogVisible = false">取 消</el-button> |
| | | <!-- <el-button type="danger" @click="submitApproval('2')">不通过</el-button>--> |
| | | <el-button type="primary" @click="submitApproval('1')">发 布</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted} from 'vue' |
| | | import {ElMessage, ElMessageBox} from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getRoomEnum, getMeetingPublish,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getMeetingPublish, |
| | | saveMeetingApplication, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | const total = ref(0); |
| | | const roomEnum = ref([]); |
| | | const staffList = ref([]); |
| | | // 发布列表数据 |
| | | const approvalList = ref([]) |
| | | const approvalList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const approvalDialogVisible = ref(false) |
| | | const detailDialogVisible = ref(false); |
| | | const approvalDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 发布意见 |
| | | const publishComment = ref('') |
| | | const publishComment = ref(""); |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({...searchForm, ...queryParams}) |
| | | loading.value = true; |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }); |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.status = it.publishStatus |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | let staffs = JSON.parse(it.participants); |
| | | it.staffCount = staffs.size; |
| | | it.status = it.publishStatus; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = staffList.value |
| | | .filter(staff => staffs.some(id => id === staff.id)) |
| | | .map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postJob})` |
| | | } |
| | | }) |
| | | name: `${staff.staffName}(${staff.postJob})`, |
| | | }; |
| | | }); |
| | | |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | queryParams.pageNum = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 处理发布 |
| | | const handleApproval = (row) => { |
| | | currentMeeting.value = row |
| | | publishComment.value = '' |
| | | approvalDialogVisible.value = true |
| | | } |
| | | const handleApproval = row => { |
| | | currentMeeting.value = row; |
| | | publishComment.value = ""; |
| | | approvalDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '0': 'info', // 待发布 |
| | | '1': 'success', // 已通过 |
| | | '2': 'danger', // 未通过 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 0: "info", // 待发布 |
| | | 1: "success", // 已通过 |
| | | 2: "danger", // 未通过 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | '0': '待发布', |
| | | '1': '已发布', |
| | | '2': '已取消', |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | 0: "待发布", |
| | | 1: "已发布", |
| | | 2: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 提交发布 |
| | | const submitApproval = (status) => { |
| | | const submitApproval = status => { |
| | | // if (status === 'approved' && !publishComment.value.trim()) { |
| | | // ElMessage.warning('请填写发布意见') |
| | | // return |
| | | // } |
| | | |
| | | ElMessageBox.confirm( |
| | | `确认${status === '1' ? '发布' : '取消'}该会议?`, |
| | | '发布确认', |
| | | `确认${status === "1" ? "发布" : "取消"}该会议?`, |
| | | "发布确认", |
| | | { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ).then(() => { |
| | | ) |
| | | .then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | publishStatus: status, |
| | | publishComment: publishComment.value |
| | | publishComment: publishComment.value, |
| | | }).then(resp=>{ |
| | | // 更新会议状态 |
| | | currentMeeting.value.status = status |
| | | currentMeeting.value.status = status; |
| | | |
| | | ElMessage.success('发布提交成功') |
| | | approvalDialogVisible.value = false |
| | | getList() |
| | | ElMessage.success("发布提交成功"); |
| | | approvalDialogVisible.value = false; |
| | | getList(); |
| | | }); |
| | | }) |
| | | |
| | | }).catch(() => { |
| | | }) |
| | | } |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2]= await Promise.all([getRoomEnum(), getStaffOnJob()]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data |
| | | const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()]); |
| | | roomEnum.value = resp1.data; |
| | | staffList.value = resp2.data; |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="page-header"> |
| | | <h2>会议纪要</h2> |
| | | </div> |
| | | |
| | | <!-- 搜索区域 --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" inline> |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable /> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable /> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- 会议列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="meetingList" border> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120" /> |
| | | <el-table-column prop="host" label="主持人" align="center" width="120" /> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="meetingList" |
| | | border> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主持人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150" /> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | type="primary" |
| | | <el-button type="primary" |
| | | link |
| | | @click="addMinutes(scope.row)" |
| | | > |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="addMinutes(scope.row)"> |
| | | 添加纪要 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主持人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主持人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="审批状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="审批状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 添加会议纪要对话框 --> |
| | | <el-dialog |
| | | title="添加会议纪要" |
| | | <el-dialog title="添加会议纪要" |
| | | v-model="minutesDialogVisible" |
| | | width="80%" |
| | | @close="handleCloseMinutesDialog" |
| | | > |
| | | @close="handleCloseMinutesDialog"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主持人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>会议纪要内容</h4> |
| | | <div class="editor-container"> |
| | | <Editor |
| | | v-model="minutesContent" |
| | | :min-height="400" |
| | | /> |
| | | <Editor v-model="minutesContent" |
| | | :min-height="400" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitMinutes">保 存</el-button> |
| | | <el-button @click="minutesDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="submitMinutes">保 存</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import Editor from '@/components/Editor/index.vue' |
| | | import { getRoomEnum, getMeetingPublish ,getMeetingMinutesByMeetingId,saveMeetingMinutes} from '@/api/collaborativeApproval/meeting.js' |
| | | import { getStaffOnJob } from "@/api/personnelManagement/onboarding.js" |
| | | import dayjs from "dayjs" |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import Editor from "@/components/Editor/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getMeetingPublish, |
| | | getMeetingMinutesByMeetingId, |
| | | saveMeetingMinutes, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import { getStaffOnJob } from "@/api/personnelManagement/onboarding.js"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | const total = ref(0); |
| | | const roomEnum = ref([]); |
| | | const staffList = ref([]); |
| | | |
| | | // 会议列表数据 |
| | | const meetingList = ref([]) |
| | | const meetingList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | title: "", |
| | | applicant: "", |
| | | // status: '1' // 默认只显示已通过审批的会议 |
| | | }) |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const minutesDialogVisible = ref(false) |
| | | const detailDialogVisible = ref(false); |
| | | const minutesDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 会议纪要内容 |
| | | const minutesContent = ref('') |
| | | const minutesContentId = ref('') |
| | | const minutesContent = ref(""); |
| | | const minutesContentId = ref(""); |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }) |
| | | loading.value = true; |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }); |
| | | meetingList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | let staffs = JSON.parse(it.participants); |
| | | it.staffCount = staffs.size; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = staffList.value |
| | | .filter(staff => staffs.some(id => id === staff.id)) |
| | | .map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postJob})` |
| | | } |
| | | }) |
| | | name: `${staff.staffName}(${staff.postJob})`, |
| | | }; |
| | | }); |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.current = 1 |
| | | getList() |
| | | } |
| | | queryParams.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | title: "", |
| | | applicant: "", |
| | | // status: '1' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 添加会议纪要 |
| | | const addMinutes = async (row) => { |
| | | let resp = await getMeetingMinutesByMeetingId(row.id) |
| | | currentMeeting.value = row |
| | | const addMinutes = async row => { |
| | | let resp = await getMeetingMinutesByMeetingId(row.id); |
| | | currentMeeting.value = row; |
| | | if (resp.data){ |
| | | minutesContent.value = resp.data.content |
| | | minutesContentId.value = resp.data.id |
| | | minutesContent.value = resp.data.content; |
| | | minutesContentId.value = resp.data.id; |
| | | }else { |
| | | minutesContent.value = `<h2>${row.title}会议纪要</h2> |
| | | <p><strong>会议时间:</strong>${row.meetingTime}</p> |
| | |
| | | <p><strong>主持人:</strong>${row.host}</p> |
| | | <p><strong>参会人员:</strong></p> |
| | | <ol> |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join('')} |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join("")} |
| | | </ol> |
| | | <p><strong>会议内容:</strong></p> |
| | | <ol> |
| | |
| | | </ul> |
| | | </li> |
| | | </ol> |
| | | <p><strong>备注:</strong></p>` |
| | | <p><strong>备注:</strong></p>`; |
| | | } |
| | | |
| | | minutesDialogVisible.value = true |
| | | } |
| | | minutesDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 提交会议纪要 |
| | | const submitMinutes = () => { |
| | | if (!minutesContent.value) { |
| | | ElMessage.warning('请输入会议纪要内容') |
| | | return |
| | | ElMessage.warning("请输入会议纪要内容"); |
| | | return; |
| | | } |
| | | saveMeetingMinutes({ |
| | | id: minutesContentId.value, |
| | | content: minutesContent.value, |
| | | meetingId: currentMeeting.value.id, |
| | | title: currentMeeting.value.title |
| | | title: currentMeeting.value.title, |
| | | }).then(resp=>{ |
| | | console.log('会议纪要内容:', minutesContent.value) |
| | | ElMessage.success('会议纪要保存成功') |
| | | minutesDialogVisible.value = false |
| | | }) |
| | | |
| | | } |
| | | console.log("会议纪要内容:", minutesContent.value); |
| | | ElMessage.success("会议纪要保存成功"); |
| | | minutesDialogVisible.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 关闭会议纪要对话框 |
| | | const handleCloseMinutesDialog = () => { |
| | | minutesContent.value = '' |
| | | } |
| | | minutesContent.value = ""; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '0': 'info', // 待审批 |
| | | '1': 'success', // 已通过 |
| | | '2': 'warning', // 未通过 |
| | | '3': 'danger' // 取消 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 0: "info", // 待审批 |
| | | 1: "success", // 已通过 |
| | | 2: "warning", // 未通过 |
| | | 3: "danger", // 取消 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | '0': '待审批', |
| | | '1': '已通过', |
| | | '2': '未通过', |
| | | '3': '已取消' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | 0: "待审批", |
| | | 1: "已通过", |
| | | 2: "未通过", |
| | | 3: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data |
| | | const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()]); |
| | | roomEnum.value = resp1.data; |
| | | staffList.value = resp2.data; |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>办公物资申请管理</span> |
| | | <el-button type="primary" @click="openShow()"> |
| | | <el-icon><Plus /></el-icon> |
| | | <el-button type="primary" |
| | | @click="openShow()"> |
| | | <el-icon> |
| | | <Plus /> |
| | | </el-icon> |
| | | 新建申请 |
| | | </el-button> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- 搜索区域 --> |
| | | <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> |
| | | <el-form-item label="申请编号" prop="code"> |
| | | <el-input |
| | | v-model="queryParams.code" |
| | | <el-form :model="queryParams" |
| | | ref="queryRef" |
| | | :inline="true" |
| | | v-show="showSearch"> |
| | | <el-form-item label="申请编号" |
| | | prop="code"> |
| | | <el-input v-model="queryParams.code" |
| | | placeholder="请输入申请编号" |
| | | clearable |
| | | style="width: 200px" |
| | | @keyup.enter="handleQuery" |
| | | /> |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人" prop="applicant"> |
| | | <el-input |
| | | v-model="queryParams.applicant" |
| | | <el-form-item label="申请人" |
| | | prop="applicant"> |
| | | <el-input v-model="queryParams.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable |
| | | style="width: 200px" |
| | | @keyup.enter="handleQuery" |
| | | /> |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="申请状态" prop="status"> |
| | | <el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 200px"> |
| | | <el-option label="待审批" value="1" /> |
| | | <el-option label="已通过" value="3" /> |
| | | <el-option label="已拒绝" value="2" /> |
| | | <el-option label="已发放" value="4" /> |
| | | <el-form-item label="申请状态" |
| | | prop="status"> |
| | | <el-select v-model="queryParams.status" |
| | | placeholder="请选择状态" |
| | | clearable |
| | | style="width: 200px"> |
| | | <el-option label="待审批" |
| | | value="1" /> |
| | | <el-option label="已通过" |
| | | value="3" /> |
| | | <el-option label="已拒绝" |
| | | value="2" /> |
| | | <el-option label="已发放" |
| | | value="4" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> |
| | | <el-icon><Search /></el-icon> |
| | | <el-button type="primary" |
| | | @click="handleQuery"> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | 搜索 |
| | | </el-button> |
| | | <el-button @click="resetQuery"> |
| | | <el-icon><Refresh /></el-icon> |
| | | <el-icon> |
| | | <Refresh /> |
| | | </el-icon> |
| | | 重置 |
| | | </el-button> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleExport"> |
| | | <el-icon><Download /></el-icon> |
| | | <el-button type="primary" |
| | | @click="handleExport"> |
| | | <el-icon> |
| | | <Download /> |
| | | </el-icon> |
| | | 导出 |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 表格区域 --> |
| | | <el-table |
| | | v-loading="loading" |
| | | <el-table v-loading="loading" |
| | | :data="officeList" |
| | | @selection-change="handleSelectionChange" |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="申请编号" align="center" prop="code" width="180" /> |
| | | <el-table-column label="申请人" align="center" prop="applicant" width="120" /> |
| | | <el-table-column label="部门" align="center" prop="dept" width="120" /> |
| | | <el-table-column label="物资类型" align="center" prop="materialType" width="120"> |
| | | style="width: 100%"> |
| | | <el-table-column type="selection" |
| | | width="55" |
| | | align="center" /> |
| | | <el-table-column label="申请编号" |
| | | align="center" |
| | | prop="code" |
| | | width="180" /> |
| | | <el-table-column label="申请人" |
| | | align="center" |
| | | prop="applicant" |
| | | width="120" /> |
| | | <el-table-column label="部门" |
| | | align="center" |
| | | prop="dept" |
| | | width="120" /> |
| | | <el-table-column label="物资类型" |
| | | align="center" |
| | | prop="materialType" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.materialType === 1" type="info">其他</el-tag> |
| | | <el-tag v-if="scope.row.materialType === 2" type="success">清洁用品</el-tag> |
| | | <el-tag v-if="scope.row.materialType === 3" type="warning">电子设备</el-tag> |
| | | <el-tag v-if="scope.row.materialType === 4" type="danger">办公用品</el-tag> |
| | | <el-tag v-if="scope.row.materialType === 1" |
| | | type="info">其他</el-tag> |
| | | <el-tag v-if="scope.row.materialType === 2" |
| | | type="success">清洁用品</el-tag> |
| | | <el-tag v-if="scope.row.materialType === 3" |
| | | type="warning">电子设备</el-tag> |
| | | <el-tag v-if="scope.row.materialType === 4" |
| | | type="danger">办公用品</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="申请数量" align="center" prop="applyNum" width="100" /> |
| | | <el-table-column label="申请原因" align="center" prop="reason" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column label="申请状态" align="center" prop="status" width="100"> |
| | | <el-table-column label="申请数量" |
| | | align="center" |
| | | prop="applyNum" |
| | | width="100" /> |
| | | <el-table-column label="申请原因" |
| | | align="center" |
| | | prop="reason" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="申请状态" |
| | | align="center" |
| | | prop="status" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="申请时间" align="center" prop="applyTime" width="180" /> |
| | | <el-table-column label="审批人" align="center" prop="approval" width="120" /> |
| | | <el-table-column label="审批时间" align="center" prop="approvalTime" width="180" /> |
| | | <el-table-column label="发放时间" align="center" prop="issueTime" width="180" /> |
| | | <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width" width="200"> |
| | | <el-table-column label="申请时间" |
| | | align="center" |
| | | prop="applyTime" |
| | | width="180" /> |
| | | <el-table-column label="审批人" |
| | | align="center" |
| | | prop="approval" |
| | | width="120" /> |
| | | <el-table-column label="审批时间" |
| | | align="center" |
| | | prop="approvalTime" |
| | | width="180" /> |
| | | <el-table-column label="发放时间" |
| | | align="center" |
| | | prop="issueTime" |
| | | width="180" /> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | fixed="right" |
| | | class-name="small-padding fixed-width" |
| | | width="200"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | v-if="scope.row.status === 1" |
| | | <el-button v-if="scope.row.status === 1" |
| | | type="primary" |
| | | link |
| | | @click="handleApprove(scope.row)" |
| | | > |
| | | @click="handleApprove(scope.row)"> |
| | | 审批 |
| | | </el-button> |
| | | <el-button |
| | | v-if="scope.row.status === 3" |
| | | <el-button v-if="scope.row.status === 3" |
| | | type="success" |
| | | link |
| | | @click="handleIssue(scope.row)" |
| | | > |
| | | @click="handleIssue(scope.row)"> |
| | | 发放 |
| | | </el-button> |
| | | <el-button |
| | | type="info" |
| | | <el-button type="info" |
| | | link |
| | | @click="handleDetail(scope.row)" |
| | | > |
| | | @click="handleDetail(scope.row)"> |
| | | 详情 |
| | | </el-button> |
| | | <el-button |
| | | v-if="scope.row.status === 2" |
| | | <el-button v-if="scope.row.status === 2" |
| | | type="danger" |
| | | link |
| | | @click="handleDelete(scope.row)" |
| | | > |
| | | @click="handleDelete(scope.row)"> |
| | | 删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 申请对话框 --> |
| | | <el-dialog |
| | | v-model="showApplyDialog" |
| | | <el-dialog v-model="showApplyDialog" |
| | | title="办公物资申请" |
| | | width="600px" |
| | | append-to-body |
| | | > |
| | | <el-form ref="applyFormRef" :model="applyForm" :rules="applyRules" label-width="100px"> |
| | | <el-form-item label="申请人" prop="applicant"> |
| | | <el-input v-model="applyForm.applicant" placeholder="请输入申请人名称" /> |
| | | append-to-body> |
| | | <el-form ref="applyFormRef" |
| | | :model="applyForm" |
| | | :rules="applyRules" |
| | | label-width="100px"> |
| | | <el-form-item label="申请人" |
| | | prop="applicant"> |
| | | <el-input v-model="applyForm.applicant" |
| | | placeholder="请输入申请人名称" /> |
| | | </el-form-item> |
| | | <el-form-item label="部门" prop="dept"> |
| | | <el-input v-model="applyForm.dept" placeholder="请输入部门名称" /> |
| | | <el-form-item label="部门" |
| | | prop="dept"> |
| | | <el-input v-model="applyForm.dept" |
| | | placeholder="请输入部门名称" /> |
| | | </el-form-item> |
| | | <el-form-item label="物资类型" prop="materialType"> |
| | | <el-select v-model="applyForm.materialType" placeholder="请选择物资类型" style="width: 100%"> |
| | | <el-option label="办公用品" value="4" /> |
| | | <el-option label="电子设备" value="3" /> |
| | | <el-option label="清洁用品" value="2" /> |
| | | <el-option label="其他" value="1" /> |
| | | <el-form-item label="物资类型" |
| | | prop="materialType"> |
| | | <el-select v-model="applyForm.materialType" |
| | | placeholder="请选择物资类型" |
| | | style="width: 100%"> |
| | | <el-option label="办公用品" |
| | | value="4" /> |
| | | <el-option label="电子设备" |
| | | value="3" /> |
| | | <el-option label="清洁用品" |
| | | value="2" /> |
| | | <el-option label="其他" |
| | | value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="具体物品" prop="itemName"> |
| | | <el-input v-model="applyForm.itemName" placeholder="请输入具体物品名称" /> |
| | | <el-form-item label="具体物品" |
| | | prop="itemName"> |
| | | <el-input v-model="applyForm.itemName" |
| | | placeholder="请输入具体物品名称" /> |
| | | </el-form-item> |
| | | <el-form-item label="申请数量" prop="applyNum"> |
| | | <el-input-number v-model="applyForm.applyNum" :min="1" :max="999" style="width: 100%" /> |
| | | <el-form-item label="申请数量" |
| | | prop="applyNum"> |
| | | <el-input-number v-model="applyForm.applyNum" |
| | | :min="1" |
| | | :max="999" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="申请原因" prop="reason"> |
| | | <el-input |
| | | v-model="applyForm.reason" |
| | | <el-form-item label="申请原因" |
| | | prop="reason"> |
| | | <el-input v-model="applyForm.reason" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入申请原因" |
| | | /> |
| | | placeholder="请输入申请原因" /> |
| | | </el-form-item> |
| | | <el-form-item label="紧急程度" prop="urgency"> |
| | | <el-form-item label="紧急程度" |
| | | prop="urgency"> |
| | | <el-radio-group v-model="applyForm.urgency"> |
| | | <el-radio label="1">普通</el-radio> |
| | | <el-radio label="2">紧急</el-radio> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitApply">确 定</el-button> |
| | | <el-button @click="showApplyDialog = false">取 消</el-button> |
| | | <el-button type="primary" @click="submitApply">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 审批对话框 --> |
| | | <el-dialog |
| | | v-model="showApproveDialog" |
| | | <el-dialog v-model="showApproveDialog" |
| | | title="审批申请" |
| | | width="500px" |
| | | append-to-body |
| | | > |
| | | <el-form ref="approveFormRef" :model="approveForm" :rules="approveRules" label-width="100px"> |
| | | <el-form-item label="审批结果" prop="approveResult"> |
| | | append-to-body> |
| | | <el-form ref="approveFormRef" |
| | | :model="approveForm" |
| | | :rules="approveRules" |
| | | label-width="100px"> |
| | | <el-form-item label="审批结果" |
| | | prop="approveResult"> |
| | | <el-radio-group v-model="approveForm.approveResult"> |
| | | <el-radio label="3">通过</el-radio> |
| | | <el-radio label="2">拒绝</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="审批意见" prop="approvalOpinions"> |
| | | <el-input |
| | | v-model="approveForm.approvalOpinions" |
| | | <el-form-item label="审批意见" |
| | | prop="approvalOpinions"> |
| | | <el-input v-model="approveForm.approvalOpinions" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入审批意见" |
| | | /> |
| | | placeholder="请输入审批意见" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitApprove">确 定</el-button> |
| | | <el-button @click="showApproveDialog = false">取 消</el-button> |
| | | <el-button type="primary" @click="submitApprove">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 详情对话框 --> |
| | | <el-dialog |
| | | v-model="showDetailDialog" |
| | | <el-dialog v-model="showDetailDialog" |
| | | title="申请详情" |
| | | width="700px" |
| | | append-to-body |
| | | > |
| | | <el-descriptions :column="2" border> |
| | | append-to-body> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="申请编号">{{ currentDetail.code }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentDetail.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="部门">{{ currentDetail.dept }}</el-descriptions-item> |
| | | <el-descriptions-item label="物资类型">{{ currentDetail.materialType }}</el-descriptions-item> |
| | | <el-descriptions-item label="具体物品">{{ currentDetail.itemName }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请数量">{{ currentDetail.applyNum }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请原因" :span="2">{{ currentDetail.reason }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请原因" |
| | | :span="2">{{ currentDetail.reason }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请状态"> |
| | | <el-tag :type="getStatusType(currentDetail.status)"> |
| | | {{ getStatusText(currentDetail.status) }} |
| | |
| | | <el-descriptions-item label="申请时间">{{ currentDetail.applyTime }}</el-descriptions-item> |
| | | <el-descriptions-item label="审批人">{{ currentDetail.approval || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="审批时间">{{ currentDetail.approvalTime || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="审批意见" :span="2">{{ currentDetail.approvalOpinions || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="审批意见" |
| | | :span="2">{{ currentDetail.approvalOpinions || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="发放时间">{{ currentDetail.issueTime || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="发放人">{{ currentDetail.issueUser || '-' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {listPage,add,update,deleteOff} from "@/api/collaborativeApproval/officeSupplies.js" |
| | | import {ref, reactive, onMounted, getCurrentInstance} from 'vue' |
| | | import Cookies from 'js-cookie' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus, Search, Refresh, Download, Check } from '@element-plus/icons-vue' |
| | | import { |
| | | listPage, |
| | | add, |
| | | update, |
| | | deleteOff, |
| | | } from "@/api/collaborativeApproval/officeSupplies.js"; |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import Cookies from "js-cookie"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | Plus, |
| | | Search, |
| | | Refresh, |
| | | Download, |
| | | Check, |
| | | } from "@element-plus/icons-vue"; |
| | | |
| | | // 响应式数据 |
| | | const loading = ref(false) |
| | | const showSearch = ref(true) |
| | | const showApplyDialog = ref(false) |
| | | const showApproveDialog = ref(false) |
| | | const showDetailDialog = ref(false) |
| | | const multipleSelection = ref([]) |
| | | const officeList = ref([]) |
| | | const total = ref(0) |
| | | const suppliesList = ref([]) |
| | | const currentDetail = ref({}) |
| | | const loading = ref(false); |
| | | const showSearch = ref(true); |
| | | const showApplyDialog = ref(false); |
| | | const showApproveDialog = ref(false); |
| | | const showDetailDialog = ref(false); |
| | | const multipleSelection = ref([]); |
| | | const officeList = ref([]); |
| | | const total = ref(0); |
| | | const suppliesList = ref([]); |
| | | const currentDetail = ref({}); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | code: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | code: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 申请表单 |
| | | const applyForm = reactive({ |
| | | applicant: '', |
| | | dept: '', |
| | | materialType: '', |
| | | itemName: '', |
| | | applicant: "", |
| | | dept: "", |
| | | materialType: "", |
| | | itemName: "", |
| | | applyNum: 1, |
| | | reason: '', |
| | | urgency: '1' |
| | | }) |
| | | reason: "", |
| | | urgency: "1", |
| | | }); |
| | | |
| | | // 审批表单 |
| | | const approveForm = reactive({ |
| | | approveResult: '3', |
| | | approvalOpinions: '' |
| | | }) |
| | | approveResult: "3", |
| | | approvalOpinions: "", |
| | | }); |
| | | |
| | | // 表单校验规则 |
| | | const applyRules = { |
| | | applicant: [{ required: true, message: '请选择物资类型', trigger: 'blur' }], |
| | | dept: [{ required: true, message: '请选择物资类型', trigger: 'blur' }], |
| | | materialType: [{ required: true, message: '请选择物资类型', trigger: 'change' }], |
| | | itemName: [{ required: true, message: '请输入具体物品名称', trigger: 'blur' }], |
| | | applyNum: [{ required: true, message: '请输入申请数量', trigger: 'blur' }], |
| | | reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }] |
| | | } |
| | | applicant: [{ required: true, message: "请选择物资类型", trigger: "blur" }], |
| | | dept: [{ required: true, message: "请选择物资类型", trigger: "blur" }], |
| | | materialType: [ |
| | | { required: true, message: "请选择物资类型", trigger: "change" }, |
| | | ], |
| | | itemName: [ |
| | | { required: true, message: "请输入具体物品名称", trigger: "blur" }, |
| | | ], |
| | | applyNum: [{ required: true, message: "请输入申请数量", trigger: "blur" }], |
| | | reason: [{ required: true, message: "请输入申请原因", trigger: "blur" }], |
| | | }; |
| | | |
| | | const approveRules = { |
| | | approveResult: [{ required: true, message: '请选择审批结果', trigger: 'change' }], |
| | | approvalOpinions: [{ required: true, message: '请输入审批意见', trigger: 'blur' }] |
| | | } |
| | | approveResult: [ |
| | | { required: true, message: "请选择审批结果", trigger: "change" }, |
| | | ], |
| | | approvalOpinions: [ |
| | | { required: true, message: "请输入审批意见", trigger: "blur" }, |
| | | ], |
| | | }; |
| | | |
| | | const openShow = () => { |
| | | showApplyDialog.value = true |
| | | resetApplyForm() |
| | | } |
| | | showApplyDialog.value = true; |
| | | resetApplyForm(); |
| | | }; |
| | | |
| | | // 获取列表数据 |
| | | const getList = () => { |
| | | loading.value = true |
| | | loading.value = true; |
| | | listPage(queryParams).then(res => { |
| | | total.value = res.data.total |
| | | loading.value = false |
| | | officeList.value = res.data.records |
| | | }) |
| | | } |
| | | total.value = res.data.total; |
| | | loading.value = false; |
| | | officeList.value = res.data.records; |
| | | }); |
| | | }; |
| | | |
| | | // 查询 |
| | | const handleQuery = () => { |
| | | queryParams.current = 1 |
| | | getList() |
| | | } |
| | | queryParams.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 重置查询 |
| | | const resetQuery = () => { |
| | | queryParams.code = '' |
| | | queryParams.applicant = '' |
| | | queryParams.status = '' |
| | | handleQuery() |
| | | } |
| | | queryParams.code = ""; |
| | | queryParams.applicant = ""; |
| | | queryParams.status = ""; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 多选 |
| | | const handleSelectionChange = (selection) => { |
| | | multipleSelection.value = selection |
| | | } |
| | | const handleSelectionChange = selection => { |
| | | multipleSelection.value = selection; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | 1: 'warning', |
| | | 3: 'success', |
| | | 2: 'danger', |
| | | 4: 'info' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 1: "warning", |
| | | 3: "success", |
| | | 2: "danger", |
| | | 4: "info", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | 1: '待审批', |
| | | 3: '已通过', |
| | | 2: '已拒绝', |
| | | 4: '已发放' |
| | | } |
| | | return statusMap[status] || status |
| | | } |
| | | 1: "待审批", |
| | | 3: "已通过", |
| | | 2: "已拒绝", |
| | | 4: "已发放", |
| | | }; |
| | | return statusMap[status] || status; |
| | | }; |
| | | |
| | | // 提交申请 |
| | | const submitApply = () => { |
| | | add(applyForm).then(() => { |
| | | ElMessage.success('申请成功') |
| | | getList() |
| | | showApplyDialog.value = false |
| | | resetApplyForm() |
| | | }) |
| | | |
| | | |
| | | |
| | | } |
| | | ElMessage.success("申请成功"); |
| | | getList(); |
| | | showApplyDialog.value = false; |
| | | resetApplyForm(); |
| | | }); |
| | | }; |
| | | |
| | | //重置表单 |
| | | const resetApplyForm = () => { |
| | | // 重置表单 |
| | | Object.assign(applyForm, { |
| | | applicant: '', |
| | | dept: '', |
| | | materialType: '', |
| | | itemName: '', |
| | | applicant: "", |
| | | dept: "", |
| | | materialType: "", |
| | | itemName: "", |
| | | applyNum: 1, |
| | | reason: '', |
| | | urgency: '1' |
| | | }) |
| | | } |
| | | reason: "", |
| | | urgency: "1", |
| | | }); |
| | | }; |
| | | |
| | | // 审批 |
| | | const handleApprove = (row) => { |
| | | currentDetail.value = row |
| | | showApproveDialog.value = true |
| | | } |
| | | const handleApprove = row => { |
| | | currentDetail.value = row; |
| | | showApproveDialog.value = true; |
| | | }; |
| | | |
| | | const formatDate = (date) => { |
| | | const year = date.getFullYear() |
| | | const month = String(date.getMonth() + 1).padStart(2, '0') |
| | | const day = String(date.getDate()).padStart(2, '0') |
| | | const hours = String(date.getHours()).padStart(2, '0') |
| | | const minutes = String(date.getMinutes()).padStart(2, '0') |
| | | const sends = String(date.getSeconds()).padStart(2, '0') |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${sends}` |
| | | } |
| | | const formatDate = date => { |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | const hours = String(date.getHours()).padStart(2, "0"); |
| | | const minutes = String(date.getMinutes()).padStart(2, "0"); |
| | | const sends = String(date.getSeconds()).padStart(2, "0"); |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${sends}`; |
| | | }; |
| | | |
| | | // 提交审批 |
| | | const submitApprove = () => { |
| | | currentDetail.value.status = approveForm.approveResult |
| | | currentDetail.value.status = approveForm.approveResult; |
| | | // 从cookie中获取当前登录用户名称 |
| | | currentDetail.value.approval = Cookies.get('username') |
| | | currentDetail.value.approvalTime = formatDate(new Date()) |
| | | currentDetail.value.approvalOpinions = approveForm.approvalOpinions |
| | | update(currentDetail.value).then((res) => { |
| | | currentDetail.value.approval = Cookies.get("username"); |
| | | currentDetail.value.approvalTime = formatDate(new Date()); |
| | | currentDetail.value.approvalOpinions = approveForm.approvalOpinions; |
| | | update(currentDetail.value).then(res => { |
| | | if(res.code === 200){ |
| | | showApproveDialog.value = false |
| | | ElMessage.success('审批完成') |
| | | getList() |
| | | showApproveDialog.value = false; |
| | | ElMessage.success("审批完成"); |
| | | getList(); |
| | | |
| | | // 重置表单 |
| | | Object.assign(approveForm, { |
| | | approveResult: '3', |
| | | approvalOpinions: '' |
| | | }) |
| | | approveResult: "3", |
| | | approvalOpinions: "", |
| | | }); |
| | | } |
| | | }) |
| | | |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 发放 |
| | | const handleIssue = (row) => { |
| | | row.status = 4 |
| | | row.issueTime = formatDate(new Date()) |
| | | row.issueUser = Cookies.get('username') |
| | | update(row).then((res) =>{ |
| | | const handleIssue = row => { |
| | | row.status = 4; |
| | | row.issueTime = formatDate(new Date()); |
| | | row.issueUser = Cookies.get("username"); |
| | | update(row).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('发放完成') |
| | | getList() |
| | | ElMessage.success("发放完成"); |
| | | getList(); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const handleDetail = (row) => { |
| | | currentDetail.value = row |
| | | showDetailDialog.value = true |
| | | } |
| | | const handleDetail = row => { |
| | | currentDetail.value = row; |
| | | showDetailDialog.value = true; |
| | | }; |
| | | |
| | | // 删除 |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm('确认删除该申请吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("确认删除该申请吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | let ids = [row.id] |
| | | deleteOff(ids).then((res) =>{ |
| | | ElMessage.success('删除成功') |
| | | getList() |
| | | }) |
| | | }) |
| | | } |
| | | let ids = [row.id]; |
| | | deleteOff(ids).then(res => { |
| | | ElMessage.success("删除成功"); |
| | | getList(); |
| | | }); |
| | | }); |
| | | }; |
| | | const { proxy } = getCurrentInstance(); |
| | | // 导出 |
| | | const handleExport = () => { |
| | |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <!-- 顶部操作栏 --> |
| | | <div class="header-actions"> |
| | | <div class="left-actions"> |
| | | <el-select v-model="currentLevel" placeholder="选择计划级别" style="width: 150px" @change="handleLevelChange"> |
| | | <el-option label="个人计划" value="personal" /> |
| | | <el-option label="小组计划" value="group" /> |
| | | <el-option label="部门计划" value="department" /> |
| | | <el-option label="公司计划" value="company" /> |
| | | <el-select v-model="currentLevel" |
| | | placeholder="选择计划级别" |
| | | style="width: 150px" |
| | | @change="handleLevelChange"> |
| | | <el-option label="个人计划" |
| | | value="personal" /> |
| | | <el-option label="小组计划" |
| | | value="group" /> |
| | | <el-option label="部门计划" |
| | | value="department" /> |
| | | <el-option label="公司计划" |
| | | value="company" /> |
| | | </el-select> |
| | | <el-select v-model="currentPeriod" placeholder="选择时间周期" style="width: 120px; margin-left: 10px" @change="handlePeriodChange"> |
| | | <el-option label="周计划" value="week" /> |
| | | <el-option label="月计划" value="month" /> |
| | | <el-option label="年计划" value="year" /> |
| | | <el-select v-model="currentPeriod" |
| | | placeholder="选择时间周期" |
| | | style="width: 120px; margin-left: 10px" |
| | | @change="handlePeriodChange"> |
| | | <el-option label="周计划" |
| | | value="week" /> |
| | | <el-option label="月计划" |
| | | value="month" /> |
| | | <el-option label="年计划" |
| | | value="year" /> |
| | | </el-select> |
| | | <el-date-picker |
| | | v-model="currentDate" |
| | | <el-date-picker v-model="currentDate" |
| | | :type="datePickerType" |
| | | placeholder="选择日期" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 180px; margin-left: 10px" |
| | | @change="handleDateChange" |
| | | /> |
| | | @change="handleDateChange" /> |
| | | </div> |
| | | <div class="right-actions"> |
| | | <el-button type="primary" @click="handleAddPlan">新增计划</el-button> |
| | | <el-button type="primary" |
| | | @click="handleAddPlan">新增计划</el-button> |
| | | <el-button @click="handleExport">导出计划</el-button> |
| | | <!-- <el-button @click="handleShare">共享计划@</el-button> --> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 计划概览卡片 --> |
| | | <div class="overview-cards"> |
| | | <el-row :gutter="20"> |
| | |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon personal"> |
| | | <el-icon><User /></el-icon> |
| | | <el-icon> |
| | | <User /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">个人计划</div> |
| | | <div class="card-number">{{ overviewData.personal.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.personal.completion" :stroke-width="6" /> |
| | | <el-progress :percentage="overviewData.personal.completion" |
| | | :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon group"> |
| | | <el-icon><UserFilled /></el-icon> |
| | | <el-icon> |
| | | <UserFilled /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">小组计划</div> |
| | | <div class="card-number">{{ overviewData.group.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.group.completion" :stroke-width="6" /> |
| | | <el-progress :percentage="overviewData.group.completion" |
| | | :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon department"> |
| | | <el-icon><OfficeBuilding /></el-icon> |
| | | <el-icon> |
| | | <OfficeBuilding /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">部门计划</div> |
| | | <div class="card-number">{{ overviewData.department.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.department.completion" :stroke-width="6" /> |
| | | <el-progress :percentage="overviewData.department.completion" |
| | | :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon company"> |
| | | <el-icon><House /></el-icon> |
| | | <el-icon> |
| | | <House /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">公司计划</div> |
| | | <div class="card-number">{{ overviewData.company.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.company.completion" :stroke-width="6" /> |
| | | <el-progress :percentage="overviewData.company.completion" |
| | | :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- 计划列表 --> |
| | | <div class="plan-content"> |
| | | <el-card> |
| | |
| | | <div class="card-header"> |
| | | <span>{{ getCurrentLevelText() }} - {{ getCurrentPeriodText() }}</span> |
| | | <div> |
| | | <el-button size="small" @click="handleRefresh">刷新</el-button> |
| | | <el-button size="small" |
| | | @click="handleRefresh">刷新</el-button> |
| | | <!-- <el-button size="small" @click="handleFilter">筛选@</el-button> --> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <div class="plan-list"> |
| | | <div v-for="plan in planList" :key="plan.id" class="plan-item"> |
| | | <div v-for="plan in planList" |
| | | :key="plan.id" |
| | | class="plan-item"> |
| | | <div class="plan-header"> |
| | | <div class="plan-title"> |
| | | <el-tag :type="getPriorityType(plan.priority)" size="small">{{ getPriorityText(plan.priority) }}</el-tag> |
| | | <el-tag :type="getPriorityType(plan.priority)" |
| | | size="small">{{ getPriorityText(plan.priority) }}</el-tag> |
| | | <span class="title-text">{{ plan.title }}</span> |
| | | </div> |
| | | <div class="plan-actions"> |
| | | <el-button size="small" @click="handleEditPlan(plan)">编辑</el-button> |
| | | <el-button size="small" @click="handleViewDetail(plan)">详情</el-button> |
| | | <el-button size="small" |
| | | @click="handleEditPlan(plan)">编辑</el-button> |
| | | <el-button size="small" |
| | | @click="handleViewDetail(plan)">详情</el-button> |
| | | <el-dropdown @command="(command) => handleMoreAction(plan, command)"> |
| | | <el-button size="small"> |
| | | 更多<el-icon class="el-icon--right"><ArrowDown /></el-icon> |
| | | 更多<el-icon class="el-icon--right"> |
| | | <ArrowDown /> |
| | | </el-icon> |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <!-- <el-dropdown-item command="share">共享@</el-dropdown-item> --> |
| | | <el-dropdown-item command="copy">复制</el-dropdown-item> |
| | | <el-dropdown-item command="delete" divided>删除</el-dropdown-item> |
| | | <el-dropdown-item command="delete" |
| | | divided>删除</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="plan-content"> |
| | | <div class="plan-description">{{ plan.description }}</div> |
| | | <div class="plan-meta"> |
| | | <div class="meta-item"> |
| | | <el-icon><Calendar /></el-icon> |
| | | <el-icon> |
| | | <Calendar /> |
| | | </el-icon> |
| | | <span>{{ plan.startDate }} - {{ plan.endDate }}</span> |
| | | </div> |
| | | <div class="meta-item"> |
| | | <el-icon><User /></el-icon> |
| | | <el-icon> |
| | | <User /> |
| | | </el-icon> |
| | | <span>{{ plan.assignee }}</span> |
| | | </div> |
| | | <div class="meta-item"> |
| | | <el-icon><Clock /></el-icon> |
| | | <el-icon> |
| | | <Clock /> |
| | | </el-icon> |
| | | <span>进度: {{ plan.progress }}%</span> |
| | | </div> |
| | | <div class="meta-item"> |
| | | <el-icon><Flag /></el-icon> |
| | | <el-icon> |
| | | <Flag /> |
| | | </el-icon> |
| | | <span>{{ getStatusText(plan.status) }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="plan-progress"> |
| | | <el-progress |
| | | :percentage="plan.progress" |
| | | <el-progress :percentage="plan.progress" |
| | | :color="getProgressColor(plan.progress)" |
| | | :stroke-width="8" |
| | | /> |
| | | :stroke-width="8" /> |
| | | </div> |
| | | |
| | | <div class="plan-tags"> |
| | | <el-tag v-for="tag in plan.tags" :key="tag" size="small" style="margin-right: 5px"> |
| | | <el-tag v-for="tag in plan.tags" |
| | | :key="tag" |
| | | size="small" |
| | | style="margin-right: 5px"> |
| | | {{ tag }} |
| | | </el-tag> |
| | | </div> |
| | |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- 新增/编辑计划对话框 --> |
| | | <el-dialog |
| | | v-model="planDialogVisible" |
| | | <el-dialog v-model="planDialogVisible" |
| | | :title="operationType === 'add' ? '发布计划' : '编辑计划'" |
| | | width="600px" |
| | | @close="handleDialogClose" |
| | | > |
| | | <el-form :model="planForm" :rules="planRules" ref="planFormRef" label-width="100px"> |
| | | <el-form-item label="计划标题" prop="title"> |
| | | <el-input v-model="planForm.title" placeholder="请输入计划标题" /> |
| | | @close="handleDialogClose"> |
| | | <el-form :model="planForm" |
| | | :rules="planRules" |
| | | ref="planFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="计划标题" |
| | | prop="title"> |
| | | <el-input v-model="planForm.title" |
| | | placeholder="请输入计划标题" /> |
| | | </el-form-item> |
| | | <el-form-item label="计划描述" prop="description"> |
| | | <el-input |
| | | v-model="planForm.description" |
| | | <el-form-item label="计划描述" |
| | | prop="description"> |
| | | <el-input v-model="planForm.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入计划描述" |
| | | /> |
| | | placeholder="请输入计划描述" /> |
| | | </el-form-item> |
| | | <el-form-item label="计划级别" prop="level"> |
| | | <el-select v-model="planForm.level" placeholder="选择计划级别" style="width: 100%"> |
| | | <el-option label="个人计划" value="personal" /> |
| | | <el-option label="小组计划" value="group" /> |
| | | <el-option label="部门计划" value="department" /> |
| | | <el-option label="公司计划" value="company" /> |
| | | <el-form-item label="计划级别" |
| | | prop="level"> |
| | | <el-select v-model="planForm.level" |
| | | placeholder="选择计划级别" |
| | | style="width: 100%"> |
| | | <el-option label="个人计划" |
| | | value="personal" /> |
| | | <el-option label="小组计划" |
| | | value="group" /> |
| | | <el-option label="部门计划" |
| | | value="department" /> |
| | | <el-option label="公司计划" |
| | | value="company" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="时间周期" prop="period"> |
| | | <el-select v-model="planForm.period" placeholder="选择时间周期" style="width: 100%"> |
| | | <el-option label="周计划" value="week" /> |
| | | <el-option label="月计划" value="month" /> |
| | | <el-option label="年计划" value="year" /> |
| | | <el-form-item label="时间周期" |
| | | prop="period"> |
| | | <el-select v-model="planForm.period" |
| | | placeholder="选择时间周期" |
| | | style="width: 100%"> |
| | | <el-option label="周计划" |
| | | value="week" /> |
| | | <el-option label="月计划" |
| | | value="month" /> |
| | | <el-option label="年计划" |
| | | value="year" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="开始时间" prop="startDate"> |
| | | <el-date-picker |
| | | v-model="planForm.startDate" |
| | | <el-form-item label="开始时间" |
| | | prop="startDate"> |
| | | <el-date-picker v-model="planForm.startDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="选择开始时间" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="结束时间" prop="endDate"> |
| | | <el-date-picker |
| | | v-model="planForm.endDate" |
| | | <el-form-item label="结束时间" |
| | | prop="endDate"> |
| | | <el-date-picker v-model="planForm.endDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="选择结束时间" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="负责人" prop="assignee"> |
| | | <el-input v-model="planForm.assignee" placeholder="请输入负责人" /> |
| | | <el-form-item label="负责人" |
| | | prop="assignee"> |
| | | <el-input v-model="planForm.assignee" |
| | | placeholder="请输入负责人" /> |
| | | </el-form-item> |
| | | <el-form-item label="优先级" prop="priority"> |
| | | <el-select v-model="planForm.priority" placeholder="选择优先级" style="width: 100%"> |
| | | <el-option label="高" value="high" /> |
| | | <el-option label="中" value="medium" /> |
| | | <el-option label="低" value="low" /> |
| | | <el-form-item label="优先级" |
| | | prop="priority"> |
| | | <el-select v-model="planForm.priority" |
| | | placeholder="选择优先级" |
| | | style="width: 100%"> |
| | | <el-option label="高" |
| | | value="high" /> |
| | | <el-option label="中" |
| | | value="medium" /> |
| | | <el-option label="低" |
| | | value="low" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <!-- <el-form-item label="标签"> |
| | | <el-input v-model="planForm.tags" placeholder="请输入标签,用逗号分隔" /> |
| | | </el-form-item> --> |
| | | <el-form-item label="标签" prop="tags"> |
| | | <el-form-item label="标签" |
| | | prop="tags"> |
| | | <!-- <el-checkbox-group v-model="planForm.tags"> |
| | | <el-checkbox label="all"></el-checkbox> |
| | | <el-checkbox label="manager">管理层</el-checkbox> |
| | |
| | | <el-checkbox label="finance">财务部门</el-checkbox> |
| | | <el-checkbox label="tech">技术部门</el-checkbox> |
| | | </el-checkbox-group> --> |
| | | <el-select |
| | | v-model="planForm.tags" |
| | | <el-select v-model="planForm.tags" |
| | | multiple |
| | | placeholder="请选择标签" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="dept in departments" |
| | | style="width: 100%"> |
| | | <el-option v-for="dept in departments" |
| | | :key="dept" |
| | | :label="dept" |
| | | :value="dept" |
| | | /> |
| | | :value="dept" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="planForm.status" placeholder="选择状态" style="width: 100%"> |
| | | <el-option label="未开始" value="not_started" /> |
| | | <el-option label="进行中" value="in_progress" /> |
| | | <el-option label="已完成" value="completed" /> |
| | | <el-option label="已暂停" value="paused" /> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="planForm.status" |
| | | placeholder="选择状态" |
| | | style="width: 100%"> |
| | | <el-option label="未开始" |
| | | value="not_started" /> |
| | | <el-option label="进行中" |
| | | value="in_progress" /> |
| | | <el-option label="已完成" |
| | | value="completed" /> |
| | | <el-option label="已暂停" |
| | | value="paused" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="进度" prop="progress"> |
| | | <el-input-number |
| | | v-model="planForm.progress" |
| | | <el-form-item label="进度" |
| | | prop="progress"> |
| | | <el-input-number v-model="planForm.progress" |
| | | min="0" |
| | | max="100" |
| | | step="1" |
| | | placeholder="请输入进度" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSavePlan">保存</el-button> |
| | | <el-button @click="planDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="handleSavePlan">保存</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 计划详情对话框 --> |
| | | <el-dialog v-model="showPlanDetailDialog" title="计划详情" width="700px"> |
| | | <div v-if="currentPlanDetail" class="mb10"> |
| | | <el-descriptions :column="2" border> |
| | | <el-dialog v-model="showPlanDetailDialog" |
| | | title="计划详情" |
| | | width="700px"> |
| | | <div v-if="currentPlanDetail" |
| | | class="mb10"> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="计划标题">{{ currentPlanDetail.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="计划描述">{{ currentPlanDetail.description }}</el-descriptions-item> |
| | | <el-descriptions-item label="计划级别">{{ getCurrentLevelText(currentPlanDetail.level) }}</el-descriptions-item> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | const { proxy } = getCurrentInstance(); |
| | | import { |
| | | User, |
| | |
| | | Calendar, |
| | | Clock, |
| | | Flag, |
| | | ArrowDown |
| | | } from '@element-plus/icons-vue' |
| | | import { listDutyPlan, addDutyPlan, updateDutyPlan, delDutyPlan,NumDutyPlan,exportDutyPlan } from '@/api/collaborativeApproval/planTemplate.js' |
| | | ArrowDown, |
| | | } from "@element-plus/icons-vue"; |
| | | import { |
| | | listDutyPlan, |
| | | addDutyPlan, |
| | | updateDutyPlan, |
| | | delDutyPlan, |
| | | NumDutyPlan, |
| | | exportDutyPlan, |
| | | } from "@/api/collaborativeApproval/planTemplate.js"; |
| | | |
| | | // 响应式数据 |
| | | const operationType = ref('add') |
| | | const currentLevel = ref('personal') |
| | | const currentPeriod = ref('week') |
| | | const currentDate = ref(new Date()) |
| | | const planDialogVisible = ref(false) |
| | | const dialogTitle = ref('新增计划') |
| | | const planFormRef = ref() |
| | | const showPlanDetailDialog = ref(false) |
| | | const currentPlanDetail = ref(null) |
| | | const operationType = ref("add"); |
| | | const currentLevel = ref("personal"); |
| | | const currentPeriod = ref("week"); |
| | | const currentDate = ref(new Date()); |
| | | const planDialogVisible = ref(false); |
| | | const dialogTitle = ref("新增计划"); |
| | | const planFormRef = ref(); |
| | | const showPlanDetailDialog = ref(false); |
| | | const currentPlanDetail = ref(null); |
| | | |
| | | // 表单数据 |
| | | const planForm = reactive({ |
| | | id: '', |
| | | title: '', |
| | | description: '', |
| | | level: 'personal', |
| | | period: 'week', |
| | | startDate: '', |
| | | endDate: '', |
| | | assignee: '', |
| | | priority: 'medium', |
| | | id: "", |
| | | title: "", |
| | | description: "", |
| | | level: "personal", |
| | | period: "week", |
| | | startDate: "", |
| | | endDate: "", |
| | | assignee: "", |
| | | priority: "medium", |
| | | tags: [], |
| | | status: '', |
| | | progress: 0 |
| | | }) |
| | | status: "", |
| | | progress: 0, |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const planRules = { |
| | | title: [{ required: true, message: '请输入计划标题', trigger: 'blur' }], |
| | | description: [{ required: true, message: '请输入计划描述', trigger: 'blur' }], |
| | | level: [{ required: true, message: '请选择计划级别', trigger: 'change' }], |
| | | period: [{ required: true, message: '请选择时间周期', trigger: 'change' }], |
| | | startDate: [{ required: true, message: '请选择开始时间', trigger: 'change' }], |
| | | endDate: [{ required: true, message: '请选择结束时间', trigger: 'change' }], |
| | | assignee: [{ required: true, message: '请输入负责人', trigger: 'blur' }], |
| | | priority: [{ required: true, message: '请选择优先级', trigger: 'change' }] |
| | | } |
| | | const departments = ["产品", "分析", "调研",'技术', '架构', '设计','市场', '推广', '营销']; |
| | | title: [{ required: true, message: "请输入计划标题", trigger: "blur" }], |
| | | description: [{ required: true, message: "请输入计划描述", trigger: "blur" }], |
| | | level: [{ required: true, message: "请选择计划级别", trigger: "change" }], |
| | | period: [{ required: true, message: "请选择时间周期", trigger: "change" }], |
| | | startDate: [{ required: true, message: "请选择开始时间", trigger: "change" }], |
| | | endDate: [{ required: true, message: "请选择结束时间", trigger: "change" }], |
| | | assignee: [{ required: true, message: "请输入负责人", trigger: "blur" }], |
| | | priority: [{ required: true, message: "请选择优先级", trigger: "change" }], |
| | | }; |
| | | const departments = [ |
| | | "产品", |
| | | "分析", |
| | | "调研", |
| | | "技术", |
| | | "架构", |
| | | "设计", |
| | | "市场", |
| | | "推广", |
| | | "营销", |
| | | ]; |
| | | // 概览数据 |
| | | const overviewData = reactive({ |
| | | personal: { total: 0, completion: 0 }, |
| | | group: { total: 0, completion: 0 }, |
| | | department: { total: 0, completion: 0 }, |
| | | company: { total: 0, completion: 0 } |
| | | }) |
| | | company: { total: 0, completion: 0 }, |
| | | }); |
| | | |
| | | // 计划列表数据 |
| | | const planList = ref([]) |
| | | const planList = ref([]); |
| | | |
| | | // 计算属性 |
| | | const datePickerType = computed(() => { |
| | | switch (currentPeriod.value) { |
| | | case 'week': |
| | | return 'week' |
| | | case 'month': |
| | | return 'month' |
| | | case 'year': |
| | | return 'year' |
| | | case "week": |
| | | return "week"; |
| | | case "month": |
| | | return "month"; |
| | | case "year": |
| | | return "year"; |
| | | default: |
| | | return 'date' |
| | | return "date"; |
| | | } |
| | | }) |
| | | }); |
| | | |
| | | // 方法 |
| | | const handleLevelChange = (value) => { |
| | | console.log('计划级别变更:', value) |
| | | getPlanList() |
| | | const handleLevelChange = value => { |
| | | console.log("计划级别变更:", value); |
| | | getPlanList(); |
| | | // 这里可以根据级别筛选数据 |
| | | } |
| | | }; |
| | | |
| | | const handlePeriodChange = (value) => { |
| | | console.log('时间周期变更:', value) |
| | | getPlanList() |
| | | const handlePeriodChange = value => { |
| | | console.log("时间周期变更:", value); |
| | | getPlanList(); |
| | | // 这里可以根据周期筛选数据 |
| | | } |
| | | }; |
| | | |
| | | const handleDateChange = (value) => { |
| | | console.log('日期变更:', value) |
| | | getPlanList() |
| | | const handleDateChange = value => { |
| | | console.log("日期变更:", value); |
| | | getPlanList(); |
| | | // 这里可以根据日期筛选数据 |
| | | } |
| | | }; |
| | | |
| | | const handleAddPlan = () => { |
| | | operationType.value = 'add' |
| | | dialogTitle.value = '新增计划' |
| | | planDialogVisible.value = true |
| | | operationType.value = "add"; |
| | | dialogTitle.value = "新增计划"; |
| | | planDialogVisible.value = true; |
| | | // 重置表单 |
| | | Object.keys(planForm).forEach(key => { |
| | | planForm[key] = '' |
| | | }) |
| | | planForm.level = 'personal' |
| | | planForm.period = 'week' |
| | | planForm.priority = 'medium' |
| | | planForm.status = 'not_started' |
| | | planForm.progress = 0 |
| | | } |
| | | planForm[key] = ""; |
| | | }); |
| | | planForm.level = "personal"; |
| | | planForm.period = "week"; |
| | | planForm.priority = "medium"; |
| | | planForm.status = "not_started"; |
| | | planForm.progress = 0; |
| | | }; |
| | | |
| | | const handleEditPlan = (plan) => { |
| | | operationType.value = 'edit' |
| | | dialogTitle.value = '编辑计划' |
| | | planDialogVisible.value = true |
| | | Object.assign(planForm, plan) |
| | | const handleEditPlan = plan => { |
| | | operationType.value = "edit"; |
| | | dialogTitle.value = "编辑计划"; |
| | | planDialogVisible.value = true; |
| | | Object.assign(planForm, plan); |
| | | // // 填充表单数据 |
| | | // Object.keys(planForm).forEach(key => { |
| | | // if (key === 'tags') { |
| | |
| | | // planForm[key] = plan[key] |
| | | // } |
| | | // }) |
| | | } |
| | | }; |
| | | |
| | | const handleViewDetail = (plan) => { |
| | | currentPlanDetail.value = plan |
| | | showPlanDetailDialog.value = true |
| | | const handleViewDetail = plan => { |
| | | currentPlanDetail.value = plan; |
| | | showPlanDetailDialog.value = true; |
| | | // ElMessage.info(`查看计划详情: ${plan.title}`) |
| | | } |
| | | }; |
| | | |
| | | const handleMoreAction = async(plan,command) => { |
| | | let ids = []; |
| | | ids.push(plan.id); |
| | | console.log("ids",ids) |
| | | console.log("ids", ids); |
| | | switch (command) { |
| | | case 'share': |
| | | ElMessage.success('计划已共享') |
| | | break |
| | | case 'copy': |
| | | case "share": |
| | | ElMessage.success("计划已共享"); |
| | | break; |
| | | case "copy": |
| | | const knowledgeText = ` |
| | | 计划标题:${plan.title} |
| | | 计划描述:${plan.description} |
| | |
| | | 结束时间:${plan.endDate} |
| | | 负责人:${plan.assignee} |
| | | 优先级:${getPriorityText(plan.priority)} |
| | | 标签:${plan.tags.join(', ')} |
| | | 标签:${plan.tags.join(", ")} |
| | | 状态:${getStatusText(plan.status)} |
| | | 进度:${plan.progress}% |
| | | `.trim(); |
| | | |
| | | // 复制到剪贴板 |
| | | navigator.clipboard.writeText(knowledgeText).then(() => { |
| | | navigator.clipboard |
| | | .writeText(knowledgeText) |
| | | .then(() => { |
| | | ElMessage.success("知识内容已复制到剪贴板"); |
| | | }).catch(() => { |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("复制失败,请手动复制"); |
| | | }); |
| | | // ElMessage.success('计划已复制') |
| | | break |
| | | case 'delete': |
| | | ElMessageBox.confirm('确定要删除这个计划吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | break; |
| | | case "delete": |
| | | ElMessageBox.confirm("确定要删除这个计划吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | |
| | | delDutyPlan(ids).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success('计划已删除') |
| | | ElMessage.success("计划已删除"); |
| | | ids.value = []; |
| | | getPlanList() |
| | | getPlanList(); |
| | | } |
| | | }) |
| | | }) |
| | | break |
| | | }); |
| | | }); |
| | | break; |
| | | } |
| | | } |
| | | }; |
| | | // |
| | | const handleSavePlan = async () => { |
| | | try { |
| | | await planFormRef.value.validate() |
| | | if (operationType.value === 'add') { |
| | | await planFormRef.value.validate(); |
| | | if (operationType.value === "add") { |
| | | addDutyPlan(planForm).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success('计划保存成功') |
| | | planDialogVisible.value = false |
| | | ElMessage.success("计划保存成功"); |
| | | planDialogVisible.value = false; |
| | | } |
| | | getPlanList() |
| | | }) |
| | | getPlanList(); |
| | | }); |
| | | } else { |
| | | |
| | | updateDutyPlan(planForm).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success('计划保存成功') |
| | | planDialogVisible.value = false |
| | | ElMessage.success("计划保存成功"); |
| | | planDialogVisible.value = false; |
| | | } |
| | | getPlanList() |
| | | }) |
| | | getPlanList(); |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.log('表单验证失败:', error) |
| | | console.log("表单验证失败:", error); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleDialogClose = () => { |
| | | planFormRef.value?.resetFields() |
| | | } |
| | | planFormRef.value?.resetFields(); |
| | | }; |
| | | |
| | | const handleRefresh = () => { |
| | | getPlanList() |
| | | getPlanList(); |
| | | // ElMessage.success('数据已刷新') |
| | | } |
| | | }; |
| | | |
| | | const handleFilter = () => { |
| | | ElMessage.info('打开筛选面板') |
| | | } |
| | | ElMessage.info("打开筛选面板"); |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | ElMessageBox.confirm("是否确认导出?", "导出", { |
| | |
| | | }); |
| | | }; |
| | | const handleShare = () => { |
| | | ElMessage.success('计划已共享') |
| | | } |
| | | ElMessage.success("计划已共享"); |
| | | }; |
| | | |
| | | const getCurrentLevelText = () => { |
| | | const levelMap = { |
| | | personal: '个人计划', |
| | | group: '小组计划', |
| | | department: '部门计划', |
| | | company: '公司计划' |
| | | } |
| | | return levelMap[currentLevel.value] || '个人计划' |
| | | } |
| | | personal: "个人计划", |
| | | group: "小组计划", |
| | | department: "部门计划", |
| | | company: "公司计划", |
| | | }; |
| | | return levelMap[currentLevel.value] || "个人计划"; |
| | | }; |
| | | |
| | | const getCurrentPeriodText = () => { |
| | | const periodMap = { |
| | | week: '周计划', |
| | | month: '月计划', |
| | | year: '年计划' |
| | | } |
| | | return periodMap[currentPeriod.value] || '周计划' |
| | | } |
| | | week: "周计划", |
| | | month: "月计划", |
| | | year: "年计划", |
| | | }; |
| | | return periodMap[currentPeriod.value] || "周计划"; |
| | | }; |
| | | |
| | | const getPriorityType = (priority) => { |
| | | const getPriorityType = priority => { |
| | | const typeMap = { |
| | | high: 'danger', |
| | | medium: 'warning', |
| | | low: 'info' |
| | | } |
| | | return typeMap[priority] || 'info' |
| | | } |
| | | high: "danger", |
| | | medium: "warning", |
| | | low: "info", |
| | | }; |
| | | return typeMap[priority] || "info"; |
| | | }; |
| | | |
| | | const getPriorityText = (priority) => { |
| | | const getPriorityText = priority => { |
| | | const textMap = { |
| | | high: '高', |
| | | medium: '中', |
| | | low: '低' |
| | | } |
| | | return textMap[priority] || '中' |
| | | } |
| | | high: "高", |
| | | medium: "中", |
| | | low: "低", |
| | | }; |
| | | return textMap[priority] || "中"; |
| | | }; |
| | | |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | not_started: '未开始', |
| | | in_progress: '进行中', |
| | | completed: '已完成', |
| | | paused: '已暂停' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | not_started: "未开始", |
| | | in_progress: "进行中", |
| | | completed: "已完成", |
| | | paused: "已暂停", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | const getProgressColor = (progress) => { |
| | | if (progress >= 80) return '#67C23A' |
| | | if (progress >= 50) return '#E6A23C' |
| | | return '#F56C6C' |
| | | } |
| | | const getProgressColor = progress => { |
| | | if (progress >= 80) return "#67C23A"; |
| | | if (progress >= 50) return "#E6A23C"; |
| | | return "#F56C6C"; |
| | | }; |
| | | //获取数据列表 |
| | | const getPlanList = async () => { |
| | | const params = { |
| | | level: currentLevel.value, |
| | | period: currentPeriod.value, |
| | | queryDate:currentDate.value |
| | | } |
| | | listDutyPlan(params).then(res => { |
| | | queryDate: currentDate.value, |
| | | }; |
| | | listDutyPlan(params) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | planList.value = res.data.records |
| | | planList.value = res.data.records; |
| | | } |
| | | }).catch(err => { |
| | | console.log(err) |
| | | }) |
| | | } |
| | | .catch(err => { |
| | | console.log(err); |
| | | }); |
| | | }; |
| | | //获取数据 |
| | | const getPlanNum = async () => { |
| | | NumDutyPlan().then(res => { |
| | | NumDutyPlan() |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | // console.log(res.data) |
| | | //讲结果里面的数据根据level 赋值给overviewData |
| | | res.data.forEach(item => { |
| | | overviewData[item.level].total = item.num |
| | | overviewData[item.level].completion = item.completion |
| | | }) |
| | | |
| | | overviewData[item.level].total = item.num; |
| | | overviewData[item.level].completion = item.completion; |
| | | }); |
| | | } |
| | | }).catch(err => { |
| | | console.log(err) |
| | | }) |
| | | } |
| | | .catch(err => { |
| | | console.log(err); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getPlanList() |
| | | getPlanNum() |
| | | console.log('多级计划模板页面已加载') |
| | | }) |
| | | getPlanList(); |
| | | getPlanNum(); |
| | | console.log("多级计划模板页面已加载"); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">程序名:</span> |
| | | <el-input |
| | | v-model="searchForm.programName" |
| | | <el-input v-model="searchForm.programName" |
| | | style="width: 240px" |
| | | placeholder="请输入程序名搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title ml10">执行状态:</span> |
| | | <el-select v-model="searchForm.status" clearable @change="handleQuery" style="width: 240px"> |
| | | <el-option label="运行中" :value="'running'" /> |
| | | <el-option label="已停止" :value="'stopped'" /> |
| | | <el-option label="异常" :value="'error'" /> |
| | | <el-select v-model="searchForm.status" |
| | | clearable |
| | | @change="handleQuery" |
| | | style="width: 240px"> |
| | | <el-option label="运行中" |
| | | :value="'running'" /> |
| | | <el-option label="已停止" |
| | | :value="'stopped'" /> |
| | | <el-option label="异常" |
| | | :value="'error'" /> |
| | | </el-select> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px"> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px"> |
| | | 搜索 |
| | | </el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleExport" style="margin-right: 10px">导出</el-button> |
| | | <el-button type="primary" @click="openForm('add')">新增</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">删除</el-button> |
| | | <el-button @click="handleExport" |
| | | style="margin-right: 10px">导出</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">删除</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | |
| | | <!-- RPA表单弹窗 --> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="500px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-form |
| | | ref="formRef" |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="100px" |
| | | > |
| | | <el-form-item label="程序名" prop="programName"> |
| | | <el-input |
| | | v-model="form.programName" |
| | | label-width="100px"> |
| | | <el-form-item label="程序名" |
| | | prop="programName"> |
| | | <el-input v-model="form.programName" |
| | | placeholder="请输入程序名" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="执行状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择执行状态" style="width: 100%"> |
| | | <el-option label="运行中" value="running" /> |
| | | <el-option label="已停止" value="stopped" /> |
| | | <el-option label="异常" value="error" /> |
| | | <el-form-item label="执行状态" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="请选择执行状态" |
| | | style="width: 100%"> |
| | | <el-option label="运行中" |
| | | value="running" /> |
| | | <el-option label="已停止" |
| | | value="stopped" /> |
| | | <el-option label="异常" |
| | | value="error" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="描述" prop="description"> |
| | | <el-input |
| | | v-model="form.description" |
| | | <el-form-item label="描述" |
| | | prop="description"> |
| | | <el-input v-model="form.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入RPA程序描述" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import {listRpa, addRpa, updateRpa, delRpa, delRpaBatch} from "@/api/collaborativeApproval/rpaManagement.js"; |
| | | import { |
| | | listRpa, |
| | | addRpa, |
| | | updateRpa, |
| | | delRpa, |
| | | delRpaBatch, |
| | | } from "@/api/collaborativeApproval/rpaManagement.js"; |
| | | // 响应式数据 |
| | | const data = reactive({ |
| | | searchForm: { |
| | |
| | | form: { |
| | | programName: "", |
| | | status: "", |
| | | description: "" |
| | | description: "", |
| | | }, |
| | | dialogVisible: false, |
| | | dialogTitle: "", |
| | |
| | | tableData: [], |
| | | }); |
| | | |
| | | const { searchForm, form, dialogVisible, dialogTitle, dialogType, selectedIds, tableLoading, page, tableData } = toRefs(data); |
| | | const { |
| | | searchForm, |
| | | form, |
| | | dialogVisible, |
| | | dialogTitle, |
| | | dialogType, |
| | | selectedIds, |
| | | tableLoading, |
| | | page, |
| | | tableData, |
| | | } = toRefs(data); |
| | | |
| | | // 表单引用 |
| | | const formRef = ref(); |
| | |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | programName: [ |
| | | { required: true, message: "请输入程序名", trigger: "blur" } |
| | | ], |
| | | status: [ |
| | | { required: true, message: "请选择执行状态", trigger: "change" } |
| | | ] |
| | | programName: [{ required: true, message: "请输入程序名", trigger: "blur" }], |
| | | status: [{ required: true, message: "请选择执行状态", trigger: "change" }], |
| | | }; |
| | | |
| | | // 表格列配置 |
| | |
| | | prop: "status", |
| | | dataType: "tag", |
| | | // width: 120, |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | const statusMap = { |
| | | running: "运行中", |
| | | stopped: "已停止", |
| | | error: "异常" |
| | | error: "异常", |
| | | }; |
| | | return statusMap[params] || params; |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | const typeMap = { |
| | | running: "success", |
| | | stopped: "info", |
| | | error: "danger" |
| | | error: "danger", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "描述", |
| | |
| | | { |
| | | name: "编辑", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | } |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "开始", |
| | |
| | | // }, |
| | | // disabled: (row) => row.status === 'stopped' |
| | | // } |
| | | ] |
| | | } |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | |
| | | // 生命周期 |
| | | onMounted(() => { |
| | |
| | | listRpa({...page.value, ...searchForm.value}) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 分页处理 |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.value.current = obj.page; |
| | | page.value.size = obj.limit; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 选择变化处理 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | |
| | | |
| | | if (dialogType.value === "add") { |
| | | // 添加新RPA |
| | | addRpa({...form.value}).then(res => { |
| | | addRpa({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("添加成功"); |
| | | form.value = { |
| | | (form.value = { |
| | | programName: "", |
| | | status: "", |
| | | description: "" |
| | | }, |
| | | dialogVisible.value = false; |
| | | description: "", |
| | | }), |
| | | (dialogVisible.value = false); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else { |
| | | // 编辑RPA |
| | | updateRpa({...form.value}).then(res => { |
| | | updateRpa({ ...form.value }) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("更新成功"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("表单验证失败:", error); |
| | |
| | | }; |
| | | |
| | | // 开始RPA |
| | | const handleStart = (row) => { |
| | | const handleStart = row => { |
| | | ElMessageBox.confirm(`确定要启动RPA程序"${row.programName}"吗?`, "提示", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | }) |
| | | .then(() => { |
| | | row.status = "running"; |
| | | ElMessage.success("RPA启动成功"); |
| | | getList(); |
| | | }).catch(() => { |
| | | }) |
| | | .catch(() => { |
| | | // 用户取消 |
| | | }); |
| | | }; |
| | | |
| | | // 停止RPA |
| | | const handleStop = (row) => { |
| | | const handleStop = row => { |
| | | ElMessageBox.confirm(`确定要停止RPA程序"${row.programName}"吗?`, "提示", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | }) |
| | | .then(() => { |
| | | row.status = "stopped"; |
| | | ElMessage.success("RPA停止成功"); |
| | | getList(); |
| | | }).catch(() => { |
| | | }) |
| | | .catch(() => { |
| | | // 用户取消 |
| | | }); |
| | | }; |
| | |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | | return; |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delRpa(ids).then((res) => { |
| | | delRpa(ids) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("删除成功"); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | |
| | | }; |
| | | |
| | | // 导出功能 |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | const handleExport = () => { |
| | | proxy.download('/rpaProcessAutomation/export', { ...searchForm.value }, 'RPA管理.xlsx') |
| | | } |
| | | proxy.download( |
| | | "/rpaProcessAutomation/export", |
| | | { ...searchForm.value }, |
| | | "RPA管理.xlsx" |
| | | ); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped></style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | |
| | | <!-- 规章制度管理--> |
| | | <el-card class="box-card"> |
| | | <template #header> |
| | |
| | | </div> |
| | | </template> |
| | | <div class="tab-content"> |
| | | <el-row :gutter="20" class="mb-20"> |
| | | <el-row :gutter="20" |
| | | class="mb-20"> |
| | | <span class="ml-10">制度标题:</span> |
| | | <el-col :span="6"> |
| | | <el-input v-model="regulationSearchForm.title" placeholder="请输入制度标题" clearable /> |
| | | <el-input v-model="regulationSearchForm.title" |
| | | placeholder="请输入制度标题" |
| | | clearable /> |
| | | </el-col> |
| | | <span class="search_title">制度分类:</span> |
| | | <el-col :span="4"> |
| | | <el-select v-model="regulationSearchForm.category" placeholder="制度分类" clearable> |
| | | <el-option label="人事制度" value="hr" /> |
| | | <el-option label="财务制度" value="finance" /> |
| | | <el-option label="安全制度" value="safety" /> |
| | | <el-option label="技术制度" value="tech" /> |
| | | <el-select v-model="regulationSearchForm.category" |
| | | placeholder="制度分类" |
| | | clearable> |
| | | <el-option label="人事制度" |
| | | value="hr" /> |
| | | <el-option label="财务制度" |
| | | value="finance" /> |
| | | <el-option label="安全制度" |
| | | value="safety" /> |
| | | <el-option label="技术制度" |
| | | value="tech" /> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-button type="primary" @click="searchRegulations">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="searchRegulations">搜索</el-button> |
| | | <el-button @click="resetRegulationSearch">重置</el-button> |
| | | <el-button @click="handleExport">导出</el-button> |
| | | <el-button type="success" @click="handleAdd"> |
| | | <el-button type="success" |
| | | @click="handleAdd"> |
| | | 发布制度 |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-table :data="regulations" border v-loading="tableLoading" style="width: 100%"> |
| | | <el-table-column prop="regulationNum" label="制度编号" width="120" /> |
| | | <el-table-column prop="title" label="制度标题" min-width="150" /> |
| | | <el-table-column prop="category" label="分类" width="120"> |
| | | <el-table :data="regulations" |
| | | border |
| | | v-loading="tableLoading" |
| | | style="width: 100%"> |
| | | <el-table-column prop="regulationNum" |
| | | label="制度编号" |
| | | width="120" /> |
| | | <el-table-column prop="title" |
| | | label="制度标题" |
| | | min-width="150" /> |
| | | <el-table-column prop="category" |
| | | label="分类" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag>{{ getCategoryText(scope.row.category) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="version" label="版本" width="120" /> |
| | | <el-table-column prop="createUserName" label="发布人" width="120" /> |
| | | <el-table-column prop="createTime" label="发布时间" width="180" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <el-table-column prop="version" |
| | | label="版本" |
| | | width="120" /> |
| | | <el-table-column prop="createUserName" |
| | | label="发布人" |
| | | width="120" /> |
| | | <el-table-column prop="createTime" |
| | | label="发布时间" |
| | | width="180" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '生效中' : '已废止' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="readCount" label="已读人数" width="100" /> |
| | | <el-table-column label="操作" width="250" fixed="right"> |
| | | <el-table-column prop="readCount" |
| | | label="已读人数" |
| | | width="100" /> |
| | | <el-table-column label="操作" |
| | | width="250" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button link @click="viewRegulation(scope.row)">查看</el-button> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link type="danger" @click="repealEdit(scope.row)">废弃</el-button> |
| | | <el-button link type="success" @click="viewVersionHistory(scope.row)">版本历史</el-button> |
| | | <el-button link type="warning" @click="viewReadStatus(scope.row)">阅读状态</el-button> |
| | | <el-button link |
| | | @click="viewRegulation(scope.row)">查看</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="repealEdit(scope.row)">废弃</el-button> |
| | | <el-button link |
| | | type="success" |
| | | @click="viewVersionHistory(scope.row)">版本历史</el-button> |
| | | <el-button link |
| | | type="warning" |
| | | @click="viewReadStatus(scope.row)">阅读状态</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 用印申请对话框 --> |
| | | <!-- <el-dialog v-model="showSealApplyDialog" title="申请用印" width="600px"> |
| | | <el-form :model="sealForm" :rules="sealRules" ref="sealFormRef" label-width="100px"> |
| | |
| | | </span> |
| | | </template> |
| | | </el-dialog> --> |
| | | |
| | | <!-- 规章制度发布对话框 --> |
| | | <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? '发布制度' : '编辑制度'" width="800px"> |
| | | <el-form :model="regulationForm" :rules="regulationRules" ref="regulationFormRef" label-width="100px"> |
| | | <el-form-item label="制度编号" prop="regulationNum"> |
| | | <el-input v-model="regulationForm.regulationNum" placeholder="请输入制度编号" /> |
| | | <el-dialog v-model="showRegulationDialog" |
| | | :title="operationType === 'add' ? '发布制度' : '编辑制度'" |
| | | width="800px"> |
| | | <el-form :model="regulationForm" |
| | | :rules="regulationRules" |
| | | ref="regulationFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="制度编号" |
| | | prop="regulationNum"> |
| | | <el-input v-model="regulationForm.regulationNum" |
| | | placeholder="请输入制度编号" /> |
| | | </el-form-item> |
| | | <el-form-item label="制度标题" prop="title"> |
| | | <el-input v-model="regulationForm.title" placeholder="请输入制度标题" /> |
| | | <el-form-item label="制度标题" |
| | | prop="title"> |
| | | <el-input v-model="regulationForm.title" |
| | | placeholder="请输入制度标题" /> |
| | | </el-form-item> |
| | | <el-form-item label="制度分类" prop="category"> |
| | | <el-select v-model="regulationForm.category" placeholder="请选择制度分类" style="width: 100%"> |
| | | <el-option label="人事制度" value="hr" /> |
| | | <el-option label="财务制度" value="finance" /> |
| | | <el-option label="安全制度" value="safety" /> |
| | | <el-option label="技术制度" value="tech" /> |
| | | <el-form-item label="制度分类" |
| | | prop="category"> |
| | | <el-select v-model="regulationForm.category" |
| | | placeholder="请选择制度分类" |
| | | style="width: 100%"> |
| | | <el-option label="人事制度" |
| | | value="hr" /> |
| | | <el-option label="财务制度" |
| | | value="finance" /> |
| | | <el-option label="安全制度" |
| | | value="safety" /> |
| | | <el-option label="技术制度" |
| | | value="tech" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="制度内容" prop="content"> |
| | | <el-input v-model="regulationForm.content" type="textarea" :rows="10" placeholder="请输入制度详细内容" /> |
| | | <el-form-item label="制度内容" |
| | | prop="content"> |
| | | <el-input v-model="regulationForm.content" |
| | | type="textarea" |
| | | :rows="10" |
| | | placeholder="请输入制度详细内容" /> |
| | | </el-form-item> |
| | | <el-form-item label="制度版本" prop="version"> |
| | | <el-input v-model="regulationForm.version" placeholder="请输入制度版本" /> |
| | | <el-form-item label="制度版本" |
| | | prop="version"> |
| | | <el-input v-model="regulationForm.version" |
| | | placeholder="请输入制度版本" /> |
| | | </el-form-item> |
| | | <el-form-item label="生效时间" prop="effectiveTime"> |
| | | <el-date-picker v-model="regulationForm.effectiveTime" type="datetime" format="YYYY-MM-DD HH:mm:ss" |
| | | value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择生效时间" style="width: 100%" /> |
| | | <el-form-item label="生效时间" |
| | | prop="effectiveTime"> |
| | | <el-date-picker v-model="regulationForm.effectiveTime" |
| | | type="datetime" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | placeholder="选择生效时间" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="适用范围" prop="scope"> |
| | | <el-form-item label="适用范围" |
| | | prop="scope"> |
| | | <el-checkbox-group v-model="regulationForm.scope"> |
| | | <el-checkbox label="all">全体员工</el-checkbox> |
| | | <el-checkbox label="manager">管理层</el-checkbox> |
| | |
| | | <el-checkbox label="tech">技术部门</el-checkbox> |
| | | </el-checkbox-group> |
| | | </el-form-item> |
| | | <el-form-item label="是否需要确认" prop="requireConfirm"> |
| | | <el-form-item label="是否需要确认" |
| | | prop="requireConfirm"> |
| | | <el-switch v-model="regulationForm.requireConfirm" /> |
| | | <span class="ml-10">开启后员工需要阅读确认</span> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitRegulation">发布制度</el-button> |
| | | <el-button @click="showRegulationDialog = false">取消</el-button> |
| | | <el-button type="primary" @click="submitRegulation">发布制度</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 用印详情对话框 --> |
| | | <!-- <el-dialog v-model="showSealDetailDialog" title="用印申请详情" width="700px"> |
| | | <div v-if="currentSealDetail" class="mb10"> |
| | |
| | | </el-descriptions> |
| | | </div> |
| | | </el-dialog> --> |
| | | |
| | | <!-- 规章制度详情对话框 --> |
| | | <el-dialog v-model="showRegulationDetailDialog" title="规章制度详情" width="800px"> |
| | | <el-dialog v-model="showRegulationDetailDialog" |
| | | title="规章制度详情" |
| | | width="800px"> |
| | | <div v-if="currentRegulationDetail"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="制度编号">{{ currentRegulationDetail.id }}</el-descriptions-item> |
| | | <el-descriptions-item label="制度标题">{{ currentRegulationDetail.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="分类">{{ getCategoryText(currentRegulationDetail.category) }}</el-descriptions-item> |
| | |
| | | <div class="regulation-content">{{ currentRegulationDetail.content }}</div> |
| | | </div> |
| | | <!-- 如果tableData>0 显示 --> |
| | | <div style="margin: 10px 0;" v-if="tableData && tableData.length > 0" > |
| | | <el-button type="success" @click="resetForm(currentRegulationDetail)">确认查看</el-button> |
| | | <div style="margin: 10px 0;" |
| | | v-if="tableData && tableData.length > 0"> |
| | | <el-button type="success" |
| | | @click="resetForm(currentRegulationDetail)">确认查看</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- 版本历史对话框 --> |
| | | <el-dialog v-model="showVersionHistoryDialog" title="版本历史" width="800px"> |
| | | <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="version" label="版本号" width="100" /> |
| | | <el-table-column prop="updateTime" label="更新时间" width="180" /> |
| | | <el-table-column prop="createUserName" label="更新人" width="120" /> |
| | | <el-table-column prop="changeLog" label="变更说明"> |
| | | <el-dialog v-model="showVersionHistoryDialog" |
| | | title="版本历史" |
| | | width="800px"> |
| | | <el-table :data="versionHistory" |
| | | style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="version" |
| | | label="版本号" |
| | | width="100" /> |
| | | <el-table-column prop="updateTime" |
| | | label="更新时间" |
| | | width="180" /> |
| | | <el-table-column prop="createUserName" |
| | | label="更新人" |
| | | width="120" /> |
| | | <el-table-column prop="changeLog" |
| | | label="变更说明"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '生效中' : '已废止' }} |
| | |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | |
| | | <!-- 阅读状态对话框 --> |
| | | <el-dialog v-model="showReadStatusDialog" title="阅读状态" width="800px"> |
| | | <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="employee" label="员工姓名" width="120" /> |
| | | <el-table-column prop="department" label="所属部门" width="150" /> |
| | | <el-table-column prop="createTime" label="阅读时间" width="180" /> |
| | | <el-table-column prop="confirmTime" label="确认时间" width="180" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <el-dialog v-model="showReadStatusDialog" |
| | | title="阅读状态" |
| | | width="800px"> |
| | | <el-table :data="readStatusList" |
| | | style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="employee" |
| | | label="员工姓名" |
| | | width="120" /> |
| | | <el-table-column prop="department" |
| | | label="所属部门" |
| | | width="150" /> |
| | | <el-table-column prop="createTime" |
| | | label="阅读时间" |
| | | width="180" /> |
| | | <el-table-column prop="confirmTime" |
| | | label="确认时间" |
| | | width="180" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'confirmed' ? 'success' : 'warning'"> |
| | | {{ scope.row.status === 'confirmed' ? '已确认' : '未确认' }} |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus } from '@element-plus/icons-vue' |
| | | import { listSealApplication, addSealApplication, updateSealApplication,listRuleManagement,addRuleManagement,updateRuleManagement,delRuleManagement,getReadingStatusByRuleId,getReadingStatusList,addReadingStatus,updateReadingStatus } from '@/api/collaborativeApproval/sealManagement.js' |
| | | import { el } from 'element-plus/es/locales.mjs' |
| | | import { getUserProfile } from '@/api/system/user.js' |
| | | import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js"; |
| | | import useUserStore from '@/store/modules/user' |
| | | import { userLoginFacotryList } from "@/api/system/user.js" |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus } from "@element-plus/icons-vue"; |
| | | import { |
| | | listSealApplication, |
| | | addSealApplication, |
| | | updateSealApplication, |
| | | listRuleManagement, |
| | | addRuleManagement, |
| | | updateRuleManagement, |
| | | delRuleManagement, |
| | | getReadingStatusByRuleId, |
| | | getReadingStatusList, |
| | | addReadingStatus, |
| | | updateReadingStatus, |
| | | } from "@/api/collaborativeApproval/sealManagement.js"; |
| | | import { el } from "element-plus/es/locales.mjs"; |
| | | import { getUserProfile } from "@/api/system/user.js"; |
| | | import { |
| | | staffJoinDel, |
| | | staffJoinListPage, |
| | | } from "@/api/personnelManagement/onboarding.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userLoginFacotryList } from "@/api/system/user.js"; |
| | | |
| | | // 响应式数据 |
| | | const currentUser = ref(null) |
| | | const activeTab = ref('seal') |
| | | const operationType = ref('add') |
| | | const tableData = ref([]) |
| | | const currentUser = ref(null); |
| | | const activeTab = ref("seal"); |
| | | const operationType = ref("add"); |
| | | const tableData = ref([]); |
| | | // 用印申请相关 |
| | | const userStore = useUserStore() |
| | | const showSealApplyDialog = ref(false) |
| | | const tableLoading = ref(false) |
| | | const showSealDetailDialog = ref(false) |
| | | const currentSealDetail = ref(null) |
| | | const sealFormRef = ref() |
| | | const userStore = useUserStore(); |
| | | const showSealApplyDialog = ref(false); |
| | | const tableLoading = ref(false); |
| | | const showSealDetailDialog = ref(false); |
| | | const currentSealDetail = ref(null); |
| | | const sealFormRef = ref(); |
| | | const sealForm = reactive({ |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | }) |
| | | applicationNum: "", |
| | | title: "", |
| | | sealType: "", |
| | | reason: "", |
| | | urgency: "normal", |
| | | status: "pending", |
| | | }); |
| | | |
| | | const sealRules = { |
| | | applicationNum: [{ required: true, message: '请输入申请编号', trigger: 'blur' }], |
| | | title: [{ required: true, message: '请输入申请标题', trigger: 'blur' }], |
| | | sealType: [{ required: true, message: '请选择用印类型', trigger: 'change' }], |
| | | reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }] |
| | | } |
| | | applicationNum: [ |
| | | { required: true, message: "请输入申请编号", trigger: "blur" }, |
| | | ], |
| | | title: [{ required: true, message: "请输入申请标题", trigger: "blur" }], |
| | | sealType: [{ required: true, message: "请选择用印类型", trigger: "change" }], |
| | | reason: [{ required: true, message: "请输入申请原因", trigger: "blur" }], |
| | | }; |
| | | |
| | | const sealSearchForm = reactive({ |
| | | title: '', |
| | | status: '' |
| | | }) |
| | | title: "", |
| | | status: "", |
| | | }); |
| | | // 分页参数 |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0 |
| | | }) |
| | | total: 0, |
| | | }); |
| | | // 规章制度相关 |
| | | const showRegulationDialog = ref(false) |
| | | const showRegulationDetailDialog = ref(false) |
| | | const showVersionHistoryDialog = ref(false) |
| | | const showReadStatusDialog = ref(false) |
| | | const currentRegulationDetail = ref(null) |
| | | const regulationFormRef = ref() |
| | | const showRegulationDialog = ref(false); |
| | | const showRegulationDetailDialog = ref(false); |
| | | const showVersionHistoryDialog = ref(false); |
| | | const showReadStatusDialog = ref(false); |
| | | const currentRegulationDetail = ref(null); |
| | | const regulationFormRef = ref(); |
| | | const regulationForm = reactive({ |
| | | id: '', |
| | | regulationNum: '', |
| | | title: '', |
| | | category: '', |
| | | content: '', |
| | | version: '', |
| | | status: 'active', |
| | | id: "", |
| | | regulationNum: "", |
| | | title: "", |
| | | category: "", |
| | | content: "", |
| | | version: "", |
| | | status: "active", |
| | | readCount: 0, |
| | | effectiveTime: '', |
| | | effectiveTime: "", |
| | | scope: [], |
| | | requireConfirm: false |
| | | }) |
| | | requireConfirm: false, |
| | | }); |
| | | |
| | | const readStatus = ref({ |
| | | id: '', |
| | | ruleId: '', |
| | | employee: '', |
| | | department: '', |
| | | createTime: '', |
| | | confirmTime: '', |
| | | status: 'unconfirmed' |
| | | }) |
| | | id: "", |
| | | ruleId: "", |
| | | employee: "", |
| | | department: "", |
| | | createTime: "", |
| | | confirmTime: "", |
| | | status: "unconfirmed", |
| | | }); |
| | | |
| | | const regulationRules = { |
| | | title: [{ required: true, message: '请输入制度标题', trigger: 'blur' }], |
| | | category: [{ required: true, message: '请选择制度分类', trigger: 'change' }], |
| | | content: [{ required: true, message: '请输入制度内容', trigger: 'blur' }], |
| | | effectiveTime: [{ required: true, message: '请选择生效时间', trigger: 'change' }], |
| | | scope: [{ required: true, message: '请选择适用范围', trigger: 'change' }] |
| | | } |
| | | title: [{ required: true, message: "请输入制度标题", trigger: "blur" }], |
| | | category: [{ required: true, message: "请选择制度分类", trigger: "change" }], |
| | | content: [{ required: true, message: "请输入制度内容", trigger: "blur" }], |
| | | effectiveTime: [ |
| | | { required: true, message: "请选择生效时间", trigger: "change" }, |
| | | ], |
| | | scope: [{ required: true, message: "请选择适用范围", trigger: "change" }], |
| | | }; |
| | | |
| | | const regulationSearchForm = reactive({ |
| | | title: '', |
| | | category: '' |
| | | }) |
| | | title: "", |
| | | category: "", |
| | | }); |
| | | |
| | | // 假数据 |
| | | const sealApplications = ref([]) |
| | | const sealApplications = ref([]); |
| | | |
| | | const regulations = ref([]) |
| | | const regulations = ref([]); |
| | | |
| | | const versionHistory = ref([]) |
| | | const versionHistory = ref([]); |
| | | |
| | | const readStatusList = ref([]) |
| | | const readStatusList = ref([]); |
| | | // { employee: '陈志强', department: '销售部', readTime: '2025-01-11 10:30:00', confirmTime: '2025-01-11 10:35:00', status: 'confirmed' }, |
| | | // { employee: '刘雅婷', department: '技术部', readTime: '2025-01-11 14:20:00', confirmTime: '', status: 'unconfirmed' }, |
| | | // { employee: '王建国', department: '财务部', readTime: '2025-01-12 09:15:00', confirmTime: '2025-01-12 09:20:00', status: 'confirmed' } |
| | | |
| | | // 用印申请状态 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | pending: 'warning', |
| | | approved: 'success', |
| | | rejected: 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | pending: "warning", |
| | | approved: "success", |
| | | rejected: "danger", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | // 制度状态 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | pending: '待审批', |
| | | approved: '已通过', |
| | | rejected: '已拒绝' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | pending: "待审批", |
| | | approved: "已通过", |
| | | rejected: "已拒绝", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | // 用印类型 |
| | | const getSealTypeText = (sealType) => { |
| | | const getSealTypeText = sealType => { |
| | | const sealTypeMap = { |
| | | official: '公章', |
| | | contract: '合同专用章', |
| | | finance: '财务专用章', |
| | | tegal: '技术专用章' |
| | | } |
| | | return sealTypeMap[sealType] || '未知' |
| | | } |
| | | official: "公章", |
| | | contract: "合同专用章", |
| | | finance: "财务专用章", |
| | | tegal: "技术专用章", |
| | | }; |
| | | return sealTypeMap[sealType] || "未知"; |
| | | }; |
| | | // 制度分类 |
| | | const getCategoryText = (category) => { |
| | | const getCategoryText = category => { |
| | | const categoryMap = { |
| | | hr: '人事制度', |
| | | finance: '财务制度', |
| | | safety: '安全制度', |
| | | tech: '技术制度' |
| | | } |
| | | return categoryMap[category] || '未知' |
| | | } |
| | | hr: "人事制度", |
| | | finance: "财务制度", |
| | | safety: "安全制度", |
| | | tech: "技术制度", |
| | | }; |
| | | return categoryMap[category] || "未知"; |
| | | }; |
| | | // 搜索印章申请 |
| | | const searchSealApplications = () => { |
| | | page.current=1 |
| | | getSealApplicationList() |
| | | page.current = 1; |
| | | getSealApplicationList(); |
| | | |
| | | // ElMessage.success('搜索完成') |
| | | } |
| | | }; |
| | | // 重置印章申请搜索 |
| | | const resetSealSearch = () => { |
| | | sealSearchForm.title = '' |
| | | sealSearchForm.status = '' |
| | | searchSealApplications() |
| | | } |
| | | sealSearchForm.title = ""; |
| | | sealSearchForm.status = ""; |
| | | searchSealApplications(); |
| | | }; |
| | | // 搜索制度 |
| | | const searchRegulations = () => { |
| | | page.current=1 |
| | | getRegulationList() |
| | | } |
| | | page.current = 1; |
| | | getRegulationList(); |
| | | }; |
| | | // 重置制度搜索 |
| | | const resetRegulationSearch = () => { |
| | | regulationSearchForm.title = '' |
| | | regulationSearchForm.category = '' |
| | | searchRegulations() |
| | | } |
| | | regulationSearchForm.title = ""; |
| | | regulationSearchForm.category = ""; |
| | | searchRegulations(); |
| | | }; |
| | | // 提交用印申请 |
| | | const submitSealApplication = async () => { |
| | | try { |
| | | await sealFormRef.value.validate() |
| | | addSealApplication(sealForm).then(res => { |
| | | await sealFormRef.value.validate(); |
| | | addSealApplication(sealForm) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('申请提交成功') |
| | | showSealApplyDialog.value = false |
| | | getSealApplicationList() |
| | | ElMessage.success("申请提交成功"); |
| | | showSealApplyDialog.value = false; |
| | | getSealApplicationList(); |
| | | Object.assign(sealForm, { |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | }) |
| | | applicationNum: "", |
| | | title: "", |
| | | sealType: "", |
| | | reason: "", |
| | | urgency: "normal", |
| | | status: "pending", |
| | | }); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg) |
| | | }) |
| | | |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } catch (error) { |
| | | ElMessage.error('请完善申请信息') |
| | | ElMessage.error("请完善申请信息"); |
| | | } |
| | | } |
| | | }; |
| | | // 新增 |
| | | const handleAdd = () => { |
| | | operationType.value = 'add' |
| | | resetRegulationForm() |
| | | showRegulationDialog.value = true |
| | | } |
| | | operationType.value = "add"; |
| | | resetRegulationForm(); |
| | | showRegulationDialog.value = true; |
| | | }; |
| | | |
| | | // 编辑 |
| | | const handleEdit = (row) => { |
| | | operationType.value = 'edit' |
| | | Object.assign(regulationForm, row) |
| | | showRegulationDialog.value = true |
| | | } |
| | | const handleEdit = row => { |
| | | operationType.value = "edit"; |
| | | Object.assign(regulationForm, row); |
| | | showRegulationDialog.value = true; |
| | | }; |
| | | // 废弃 |
| | | const repealEdit = (row) => { |
| | | operationType.value = 'edit' |
| | | Object.assign(regulationForm, row) |
| | | regulationForm.status = 'repealed' |
| | | ElMessageBox.confirm('确认废弃该制度?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | const repealEdit = row => { |
| | | operationType.value = "edit"; |
| | | Object.assign(regulationForm, row); |
| | | regulationForm.status = "repealed"; |
| | | ElMessageBox.confirm("确认废弃该制度?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | updateRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度废弃成功') |
| | | ElMessage.success("制度废弃成功"); |
| | | // showRegulationDialog.value = false |
| | | getRegulationList() |
| | | resetRegulationForm() |
| | | getRegulationList(); |
| | | resetRegulationForm(); |
| | | } |
| | | }); |
| | | }) |
| | | }).catch(() => { |
| | | .catch(() => { |
| | | ElMessage({ |
| | | type: 'info', |
| | | message: '已取消废弃' |
| | | }) |
| | | }) |
| | | } |
| | | type: "info", |
| | | message: "已取消废弃", |
| | | }); |
| | | }); |
| | | }; |
| | | // 发布制度 |
| | | const submitRegulation = async () => { |
| | | try { |
| | | await regulationFormRef.value.validate() |
| | | if(operationType.value == 'add'){ |
| | | await regulationFormRef.value.validate(); |
| | | if (operationType.value == "add") { |
| | | addRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度发布成功') |
| | | showRegulationDialog.value = false |
| | | getRegulationList() |
| | | resetRegulationForm() |
| | | ElMessage.success("制度发布成功"); |
| | | showRegulationDialog.value = false; |
| | | getRegulationList(); |
| | | resetRegulationForm(); |
| | | } |
| | | }) |
| | | }); |
| | | }else{ |
| | | updateRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度编辑成功') |
| | | showRegulationDialog.value = false |
| | | resetRegulationForm() |
| | | getRegulationList() |
| | | }})} |
| | | ElMessage.success("制度编辑成功"); |
| | | showRegulationDialog.value = false; |
| | | resetRegulationForm(); |
| | | getRegulationList(); |
| | | } |
| | | }); |
| | | } |
| | | }catch(err){ |
| | | ElMessage.error(err.msg) |
| | | ElMessage.error(err.msg); |
| | | } |
| | | } |
| | | }; |
| | | //重置制度表单 |
| | | const resetRegulationForm = () => { |
| | | Object.assign(regulationForm, { |
| | | id: '', |
| | | regulationNum: '', |
| | | title: '', |
| | | category: '', |
| | | content: '', |
| | | version: '', |
| | | status: 'active', |
| | | id: "", |
| | | regulationNum: "", |
| | | title: "", |
| | | category: "", |
| | | content: "", |
| | | version: "", |
| | | status: "active", |
| | | readCount: 0, |
| | | effectiveTime: '', |
| | | effectiveTime: "", |
| | | scope: [], |
| | | requireConfirm: false |
| | | }) |
| | | } |
| | | |
| | | requireConfirm: false, |
| | | }); |
| | | }; |
| | | |
| | | // 查看用印申请详情 |
| | | const viewSealDetail = (row) => { |
| | | currentSealDetail.value = row |
| | | showSealDetailDialog.value = true |
| | | } |
| | | const viewSealDetail = row => { |
| | | currentSealDetail.value = row; |
| | | showSealDetailDialog.value = true; |
| | | }; |
| | | // 审批用印申请 |
| | | const approveSeal = (row) => { |
| | | console.log(row) |
| | | ElMessageBox.confirm('确认通过该用印申请?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const approveSeal = row => { |
| | | console.log(row); |
| | | ElMessageBox.confirm("确认通过该用印申请?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | row.status = 'approved' |
| | | row.status = "approved"; |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('审批通过') |
| | | ElMessage.success("审批通过"); |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | // 拒绝用印申请 |
| | | const rejectSeal = (row) => { |
| | | ElMessageBox.prompt('请输入拒绝原因', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | const rejectSeal = row => { |
| | | ElMessageBox.prompt("请输入拒绝原因", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | inputPattern: /\S+/, |
| | | inputErrorMessage: '拒绝原因不能为空' |
| | | inputErrorMessage: "拒绝原因不能为空", |
| | | }).then(({ value }) => { |
| | | row.status = 'rejected' |
| | | row.status = "rejected"; |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('审批拒绝') |
| | | ElMessage.success("审批拒绝"); |
| | | } |
| | | }) |
| | | ElMessage.success('已拒绝申请') |
| | | }) |
| | | } |
| | | }); |
| | | ElMessage.success("已拒绝申请"); |
| | | }); |
| | | }; |
| | | // 获取在职员工列表 |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | //获取当前登录用户信息 |
| | | getUserProfile().then(res => { |
| | | if(res.code == 200){ |
| | | console.log(res.data.userName) |
| | | currentUser.value = res.data.userName |
| | | console.log(res.data.userName); |
| | | currentUser.value = res.data.userName; |
| | | } |
| | | }) |
| | | staffJoinListPage({staffState: 1}).then(res => { |
| | | }); |
| | | staffJoinListPage({ staffState: 1 }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | // tableData.value = res.data.records |
| | | // //筛选出和currentUser同名的人员 |
| | | tableData.value = res.data.records.filter(item => item.staffName === currentUser.value) |
| | | console.log("tableData",tableData.value) |
| | | tableData.value = res.data.records.filter( |
| | | item => item.staffName === currentUser.value |
| | | ); |
| | | console.log("tableData", tableData.value); |
| | | page.total = res.data.total; |
| | | |
| | | if(tableData.value.length == 0){ |
| | | ElMessage.error('当前用户未加入任何部门') |
| | | ElMessage.error("当前用户未加入任何部门"); |
| | | } |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | |
| | | |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 查看制度版本历史 |
| | | const viewVersionHistory = (row) => { |
| | | showVersionHistoryDialog.value = true |
| | | const viewVersionHistory = row => { |
| | | showVersionHistoryDialog.value = true; |
| | | const params = { |
| | | |
| | | category: row.category |
| | | } |
| | | category: row.category, |
| | | }; |
| | | listRuleManagement(page,params).then(res => { |
| | | if(res.code == 200){ |
| | | versionHistory.value = res.data.records |
| | | versionHistory.value = res.data.records; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | // 查看制度详情 |
| | | const viewRegulation = (row) => { |
| | | getList() |
| | | currentRegulationDetail.value = row |
| | | showRegulationDetailDialog.value = true |
| | | const viewRegulation = row => { |
| | | getList(); |
| | | currentRegulationDetail.value = row; |
| | | showRegulationDetailDialog.value = true; |
| | | getReadingStatusByRuleId(row.id).then(res => { |
| | | if(res.code == 200){ |
| | | readStatusList.value = res.data |
| | | readStatusList.value = res.data; |
| | | if(readStatusList.value.length==0 && tableData.value.length>0){ |
| | | const params = { |
| | | ruleId: row.id, |
| | | employee: tableData.value[0].staffName, |
| | | department: tableData.value[0].postJob, |
| | | status: 'unconfirmed' |
| | | } |
| | | status: "unconfirmed", |
| | | }; |
| | | addReadingStatus(params).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度阅读成功') |
| | | ElMessage.success("制度阅读成功"); |
| | | } |
| | | }) |
| | | }); |
| | | } |
| | | } |
| | | }) |
| | | |
| | | } |
| | | }); |
| | | }; |
| | | // 查看制度阅读状态 |
| | | const viewReadStatus = (row) => { |
| | | showReadStatusDialog.value = true |
| | | const viewReadStatus = row => { |
| | | showReadStatusDialog.value = true; |
| | | //查看阅读状态列表 |
| | | getReadingStatusByRuleId(row.id).then(res => { |
| | | if(res.code == 200){ |
| | | readStatusList.value = res.data |
| | | readStatusList.value = res.data; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | //确认查看 |
| | | const resetForm = (row) => { |
| | | console.log("row",row) |
| | | row.readCount = row.readCount + 1 |
| | | const resetForm = row => { |
| | | console.log("row", row); |
| | | row.readCount = row.readCount + 1; |
| | | |
| | | updateRuleManagement(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('查看数量修改成功') |
| | | ElMessage.success("查看数量修改成功"); |
| | | //修改阅读状态 |
| | | //根据制度id和当前登录的员工得到阅读状态 |
| | | // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName ) |
| | |
| | | // item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0]; |
| | | // } |
| | | // 筛选当前员工对应该制度的阅读状态记录 |
| | | let statusItem = readStatusList.value.find(item => item.employee === tableData.value[0].staffName && item.ruleId === row.id); |
| | | let statusItem = readStatusList.value.find( |
| | | item => |
| | | item.employee === tableData.value[0].staffName && |
| | | item.ruleId === row.id |
| | | ); |
| | | |
| | | if (statusItem) { |
| | | // 如果找到记录,更新状态和确认时间 |
| | | statusItem.status = 'confirmed'; |
| | | statusItem.status = "confirmed"; |
| | | // 格式化时间为"YYYY-MM-DD HH:mm:ss"格式 |
| | | const now = new Date(); |
| | | statusItem.confirmTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; |
| | | statusItem.confirmTime = `${now.getFullYear()}-${String( |
| | | now.getMonth() + 1 |
| | | ).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String( |
| | | now.getHours() |
| | | ).padStart(2, "0")}:${String(now.getMinutes()).padStart( |
| | | 2, |
| | | "0" |
| | | )}:${String(now.getSeconds()).padStart(2, "0")}`; |
| | | // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0]; |
| | | |
| | | updateReadingStatus(statusItem).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度阅读状态修改成功') |
| | | ElMessage.success("制度阅读状态修改成功"); |
| | | } |
| | | }) |
| | | }); |
| | | } |
| | | |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 导出规章制度 |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | const handleExport = () => { |
| | | proxy.download('/rulesRegulationsManagement/export', { ...regulationSearchForm }, '规章制度.xlsx') |
| | | } |
| | | proxy.download( |
| | | "/rulesRegulationsManagement/export", |
| | | { ...regulationSearchForm }, |
| | | "规章制度.xlsx" |
| | | ); |
| | | }; |
| | | |
| | | // 获取印章申请列表数据 |
| | | const getSealApplicationList = async () => { |
| | | tableLoading.value = true |
| | | tableLoading.value = true; |
| | | listSealApplication(page,sealSearchForm) |
| | | .then(res => { |
| | | //获取当前登录的部门信息 |
| | | // 获取当前登录的部门信息并过滤数据 |
| | | const currentFactoryName = userStore.currentFactoryName |
| | | const currentFactoryName = userStore.currentFactoryName; |
| | | if (currentFactoryName) { |
| | | // 根据currentFactoryName过滤出department相同的数据 |
| | | sealApplications.value = res.data.records.filter(item => item.department === currentFactoryName) |
| | | sealApplications.value = res.data.records.filter( |
| | | item => item.department === currentFactoryName |
| | | ); |
| | | // 更新过滤后的总数 |
| | | page.value.total = sealApplications.value.length |
| | | page.value.total = sealApplications.value.length; |
| | | } else { |
| | | // 如果没有currentFactoryName,则显示所有数据 |
| | | sealApplications.value = res.data.records |
| | | page.value.total = res.data.total |
| | | sealApplications.value = res.data.records; |
| | | page.value.total = res.data.total; |
| | | } |
| | | // sealApplications.value = res.data.records |
| | | // page.value.total = res.data.total; |
| | | tableLoading.value = false; |
| | | |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 获取规章制度列表数据 |
| | | const getRegulationList = async () => { |
| | | tableLoading.value = true |
| | | tableLoading.value = true; |
| | | listRuleManagement(page,regulationSearchForm) |
| | | .then(res => { |
| | | |
| | | regulations.value = res.data.records |
| | | regulations.value = res.data.records; |
| | | // 过滤掉已废弃的制度 |
| | | // regulations.value = res.data.records.filter(item => item.status !== 'repealed') |
| | | page.value.total = res.data.total; |
| | | tableLoading.value = false; |
| | | |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 初始化 |
| | | getSealApplicationList() |
| | | getRegulationList() |
| | | }) |
| | | getSealApplicationList(); |
| | | getRegulationList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <span>用印管理发布</span> |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | <!-- 用印申请管理 --> |
| | | <div class="tab-content"> |
| | | <el-row :gutter="20" class="mb-20 "> |
| | | <el-row :gutter="20" |
| | | class="mb-20 "> |
| | | <span class="ml-10">用印标题:</span> |
| | | <el-col :span="6"> |
| | | <el-input v-model="sealSearchForm.title" placeholder="请输入申请标题" clearable /> |
| | | <el-input v-model="sealSearchForm.title" |
| | | placeholder="请输入申请标题" |
| | | clearable /> |
| | | </el-col> |
| | | <span class="search_title">审批状态:</span> |
| | | <el-col :span="6"> |
| | | <el-select v-model="sealSearchForm.status" placeholder="审批状态" clearable> |
| | | <el-option label="待审批" value="pending" /> |
| | | <el-option label="已通过" value="approved" /> |
| | | <el-option label="已拒绝" value="rejected" /> |
| | | <el-select v-model="sealSearchForm.status" |
| | | placeholder="审批状态" |
| | | clearable> |
| | | <el-option label="待审批" |
| | | value="pending" /> |
| | | <el-option label="已通过" |
| | | value="approved" /> |
| | | <el-option label="已拒绝" |
| | | value="rejected" /> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-button type="primary" @click="searchSealApplications">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="searchSealApplications">搜索</el-button> |
| | | <el-button @click="resetSealSearch">重置</el-button> |
| | | <el-button @click="handleExport">导出</el-button> |
| | | <el-button type="primary" @click="showSealApplyDialog = true">申请用印 |
| | | <el-button type="primary" |
| | | @click="showSealApplyDialog = true">申请用印 |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-table :data="sealApplications" border v-loading="tableLoading" style="width: 100%"> |
| | | <el-table-column prop="applicationNum" label="申请编号" width="120" /> |
| | | <el-table-column prop="title" label="申请标题" min-width="200" /> |
| | | <el-table-column prop="createUserName" label="申请人" width="120" /> |
| | | <el-table-column prop="department" label="所属部门" width="150" /> |
| | | <el-table-column prop="sealType" label="用印类型" width="120"> |
| | | <el-table :data="sealApplications" |
| | | border |
| | | v-loading="tableLoading" |
| | | style="width: 100%"> |
| | | <el-table-column prop="applicationNum" |
| | | label="申请编号" |
| | | width="120" /> |
| | | <el-table-column prop="title" |
| | | label="申请标题" |
| | | min-width="200" /> |
| | | <el-table-column prop="createUserName" |
| | | label="申请人" |
| | | width="120" /> |
| | | <el-table-column prop="department" |
| | | label="所属部门" |
| | | width="150" /> |
| | | <el-table-column prop="sealType" |
| | | label="用印类型" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | {{ getSealTypeText(scope.row.sealType) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="createTime" label="申请时间" width="180" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <el-table-column prop="createTime" |
| | | label="申请时间" |
| | | width="180" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button link @click="viewSealDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | v-if="scope.row.status === 'pending'" |
| | | <el-button link |
| | | @click="viewSealDetail(scope.row)">查看</el-button> |
| | | <el-button v-if="scope.row.status === 'pending'" |
| | | link |
| | | type="primary" |
| | | @click="approveSeal(scope.row)" |
| | | > |
| | | @click="approveSeal(scope.row)"> |
| | | 审批 |
| | | </el-button> |
| | | <el-button |
| | | v-if="scope.row.status === 'pending'" |
| | | <el-button v-if="scope.row.status === 'pending'" |
| | | link |
| | | type="danger" |
| | | @click="rejectSeal(scope.row)" |
| | | > |
| | | @click="rejectSeal(scope.row)"> |
| | | 拒绝 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 用印申请对话框 --> |
| | | <el-dialog v-model="showSealApplyDialog" title="申请用印" width="600px"> |
| | | <el-form :model="sealForm" :rules="sealRules" ref="sealFormRef" label-width="100px"> |
| | | <el-form-item label="申请编号" prop="applicationNum"> |
| | | <el-input v-model="sealForm.applicationNum" placeholder="请输入申请编号" /> |
| | | <el-dialog v-model="showSealApplyDialog" |
| | | title="申请用印" |
| | | width="600px"> |
| | | <el-form :model="sealForm" |
| | | :rules="sealRules" |
| | | ref="sealFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="申请编号" |
| | | prop="applicationNum"> |
| | | <el-input v-model="sealForm.applicationNum" |
| | | placeholder="请输入申请编号" /> |
| | | </el-form-item> |
| | | <el-form-item label="申请标题" prop="title"> |
| | | <el-input v-model="sealForm.title" placeholder="请输入申请标题" /> |
| | | <el-form-item label="申请标题" |
| | | prop="title"> |
| | | <el-input v-model="sealForm.title" |
| | | placeholder="请输入申请标题" /> |
| | | </el-form-item> |
| | | <el-form-item label="用印类型" prop="sealType"> |
| | | <el-select v-model="sealForm.sealType" placeholder="请选择用印类型" style="width: 100%"> |
| | | <el-option label="公章" value="official" /> |
| | | <el-option label="合同专用章" value="contract" /> |
| | | <el-option label="财务专用章" value="finance" /> |
| | | <el-option label="法人章" value="legal" /> |
| | | <el-form-item label="用印类型" |
| | | prop="sealType"> |
| | | <el-select v-model="sealForm.sealType" |
| | | placeholder="请选择用印类型" |
| | | style="width: 100%"> |
| | | <el-option label="公章" |
| | | value="official" /> |
| | | <el-option label="合同专用章" |
| | | value="contract" /> |
| | | <el-option label="财务专用章" |
| | | value="finance" /> |
| | | <el-option label="法人章" |
| | | value="legal" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="申请原因" prop="reason"> |
| | | <el-input v-model="sealForm.reason" type="textarea" :rows="4" placeholder="请详细说明用印原因" /> |
| | | <el-form-item label="申请原因" |
| | | prop="reason"> |
| | | <el-input v-model="sealForm.reason" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请详细说明用印原因" /> |
| | | </el-form-item> |
| | | <el-form-item label="紧急程度" prop="urgency"> |
| | | <el-form-item label="紧急程度" |
| | | prop="urgency"> |
| | | <el-radio-group v-model="sealForm.urgency"> |
| | | <el-radio label="normal">普通</el-radio> |
| | | <el-radio label="urgent">紧急</el-radio> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitSealApplication">提交申请</el-button> |
| | | <el-button @click="showSealApplyDialog = false">取消</el-button> |
| | | <el-button type="primary" @click="submitSealApplication">提交申请</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 规章制度发布对话框 --> |
| | | <!-- <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? '发布制度' : '编辑制度'" width="800px"> |
| | | <el-form :model="regulationForm" :rules="regulationRules" ref="regulationFormRef" label-width="100px"> |
| | |
| | | </span> |
| | | </template> |
| | | </el-dialog> --> |
| | | |
| | | <!-- 用印详情对话框 --> |
| | | <el-dialog v-model="showSealDetailDialog" title="用印申请详情" width="700px"> |
| | | <div v-if="currentSealDetail" class="mb10"> |
| | | <el-descriptions :column="2" border> |
| | | <el-dialog v-model="showSealDetailDialog" |
| | | title="用印申请详情" |
| | | width="700px"> |
| | | <div v-if="currentSealDetail" |
| | | class="mb10"> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="申请编号">{{ currentSealDetail.id }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请标题">{{ currentSealDetail.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentSealDetail.createUserName }}</el-descriptions-item> |
| | |
| | | {{ getStatusText(currentSealDetail.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请原因" :span="2">{{ currentSealDetail.reason }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请原因" |
| | | :span="2">{{ currentSealDetail.reason }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- 规章制度详情对话框 --> |
| | | <el-dialog v-model="showRegulationDetailDialog" title="规章制度详情" width="800px"> |
| | | <el-dialog v-model="showRegulationDetailDialog" |
| | | title="规章制度详情" |
| | | width="800px"> |
| | | <div v-if="currentRegulationDetail"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="制度编号">{{ currentRegulationDetail.id }}</el-descriptions-item> |
| | | <el-descriptions-item label="制度标题">{{ currentRegulationDetail.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="分类">{{ getCategoryText(currentRegulationDetail.category) }}</el-descriptions-item> |
| | |
| | | <div class="regulation-content">{{ currentRegulationDetail.content }}</div> |
| | | </div> |
| | | <!-- 如果tableData>0 显示 --> |
| | | <div style="margin: 10px 0;" v-if="tableData && tableData.length > 0" > |
| | | <el-button type="success" @click="resetForm(currentRegulationDetail)">确认查看</el-button> |
| | | <div style="margin: 10px 0;" |
| | | v-if="tableData && tableData.length > 0"> |
| | | <el-button type="success" |
| | | @click="resetForm(currentRegulationDetail)">确认查看</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- 版本历史对话框 --> |
| | | <el-dialog v-model="showVersionHistoryDialog" title="版本历史" width="800px"> |
| | | <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="version" label="版本号" width="100" /> |
| | | <el-table-column prop="updateTime" label="更新时间" width="180" /> |
| | | <el-table-column prop="createUserName" label="更新人" width="120" /> |
| | | <el-table-column prop="changeLog" label="变更说明"> |
| | | <el-dialog v-model="showVersionHistoryDialog" |
| | | title="版本历史" |
| | | width="800px"> |
| | | <el-table :data="versionHistory" |
| | | style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="version" |
| | | label="版本号" |
| | | width="100" /> |
| | | <el-table-column prop="updateTime" |
| | | label="更新时间" |
| | | width="180" /> |
| | | <el-table-column prop="createUserName" |
| | | label="更新人" |
| | | width="120" /> |
| | | <el-table-column prop="changeLog" |
| | | label="变更说明"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '生效中' : '已废止' }} |
| | |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | |
| | | <!-- 阅读状态对话框 --> |
| | | <el-dialog v-model="showReadStatusDialog" title="阅读状态" width="800px"> |
| | | <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="employee" label="员工姓名" width="120" /> |
| | | <el-table-column prop="department" label="所属部门" width="150" /> |
| | | <el-table-column prop="createTime" label="阅读时间" width="180" /> |
| | | <el-table-column prop="confirmTime" label="确认时间" width="180" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <el-dialog v-model="showReadStatusDialog" |
| | | title="阅读状态" |
| | | width="800px"> |
| | | <el-table :data="readStatusList" |
| | | style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="employee" |
| | | label="员工姓名" |
| | | width="120" /> |
| | | <el-table-column prop="department" |
| | | label="所属部门" |
| | | width="150" /> |
| | | <el-table-column prop="createTime" |
| | | label="阅读时间" |
| | | width="180" /> |
| | | <el-table-column prop="confirmTime" |
| | | label="确认时间" |
| | | width="180" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'confirmed' ? 'success' : 'warning'"> |
| | | {{ scope.row.status === 'confirmed' ? '已确认' : '未确认' }} |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus } from '@element-plus/icons-vue' |
| | | import { listSealApplication, addSealApplication, updateSealApplication,listRuleManagement,addRuleManagement,updateRuleManagement,delRuleManagement,getReadingStatusByRuleId,getReadingStatusList,addReadingStatus,updateReadingStatus } from '@/api/collaborativeApproval/sealManagement.js' |
| | | import { el } from 'element-plus/es/locales.mjs' |
| | | import { getUserProfile } from '@/api/system/user.js' |
| | | import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js"; |
| | | import useUserStore from '@/store/modules/user' |
| | | import { userLoginFacotryList } from "@/api/system/user.js" |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus } from "@element-plus/icons-vue"; |
| | | import { |
| | | listSealApplication, |
| | | addSealApplication, |
| | | updateSealApplication, |
| | | listRuleManagement, |
| | | addRuleManagement, |
| | | updateRuleManagement, |
| | | delRuleManagement, |
| | | getReadingStatusByRuleId, |
| | | getReadingStatusList, |
| | | addReadingStatus, |
| | | updateReadingStatus, |
| | | } from "@/api/collaborativeApproval/sealManagement.js"; |
| | | import { el } from "element-plus/es/locales.mjs"; |
| | | import { getUserProfile } from "@/api/system/user.js"; |
| | | import { |
| | | staffJoinDel, |
| | | staffJoinListPage, |
| | | } from "@/api/personnelManagement/onboarding.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userLoginFacotryList } from "@/api/system/user.js"; |
| | | |
| | | // 响应式数据 |
| | | const currentUser = ref(null) |
| | | const activeTab = ref('seal') |
| | | const operationType = ref('add') |
| | | const tableData = ref([]) |
| | | const currentUser = ref(null); |
| | | const activeTab = ref("seal"); |
| | | const operationType = ref("add"); |
| | | const tableData = ref([]); |
| | | // 用印申请相关 |
| | | const userStore = useUserStore() |
| | | const showSealApplyDialog = ref(false) |
| | | const tableLoading = ref(false) |
| | | const showSealDetailDialog = ref(false) |
| | | const currentSealDetail = ref(null) |
| | | const sealFormRef = ref() |
| | | const userStore = useUserStore(); |
| | | const showSealApplyDialog = ref(false); |
| | | const tableLoading = ref(false); |
| | | const showSealDetailDialog = ref(false); |
| | | const currentSealDetail = ref(null); |
| | | const sealFormRef = ref(); |
| | | const sealForm = reactive({ |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | }) |
| | | applicationNum: "", |
| | | title: "", |
| | | sealType: "", |
| | | reason: "", |
| | | urgency: "normal", |
| | | status: "pending", |
| | | }); |
| | | |
| | | const sealRules = { |
| | | applicationNum: [{ required: true, message: '请输入申请编号', trigger: 'blur' }], |
| | | title: [{ required: true, message: '请输入申请标题', trigger: 'blur' }], |
| | | sealType: [{ required: true, message: '请选择用印类型', trigger: 'change' }], |
| | | reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }] |
| | | } |
| | | applicationNum: [ |
| | | { required: true, message: "请输入申请编号", trigger: "blur" }, |
| | | ], |
| | | title: [{ required: true, message: "请输入申请标题", trigger: "blur" }], |
| | | sealType: [{ required: true, message: "请选择用印类型", trigger: "change" }], |
| | | reason: [{ required: true, message: "请输入申请原因", trigger: "blur" }], |
| | | }; |
| | | |
| | | const sealSearchForm = reactive({ |
| | | title: '', |
| | | status: '' |
| | | }) |
| | | title: "", |
| | | status: "", |
| | | }); |
| | | // 分页参数 |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0 |
| | | }) |
| | | total: 0, |
| | | }); |
| | | // 规章制度相关 |
| | | const showRegulationDialog = ref(false) |
| | | const showRegulationDetailDialog = ref(false) |
| | | const showVersionHistoryDialog = ref(false) |
| | | const showReadStatusDialog = ref(false) |
| | | const currentRegulationDetail = ref(null) |
| | | const regulationFormRef = ref() |
| | | const showRegulationDialog = ref(false); |
| | | const showRegulationDetailDialog = ref(false); |
| | | const showVersionHistoryDialog = ref(false); |
| | | const showReadStatusDialog = ref(false); |
| | | const currentRegulationDetail = ref(null); |
| | | const regulationFormRef = ref(); |
| | | const regulationForm = reactive({ |
| | | id: '', |
| | | regulationNum: '', |
| | | title: '', |
| | | category: '', |
| | | content: '', |
| | | version: '', |
| | | status: 'active', |
| | | id: "", |
| | | regulationNum: "", |
| | | title: "", |
| | | category: "", |
| | | content: "", |
| | | version: "", |
| | | status: "active", |
| | | readCount: 0, |
| | | effectiveTime: '', |
| | | effectiveTime: "", |
| | | scope: [], |
| | | requireConfirm: false |
| | | }) |
| | | requireConfirm: false, |
| | | }); |
| | | |
| | | const readStatus = ref({ |
| | | id: '', |
| | | ruleId: '', |
| | | employee: '', |
| | | department: '', |
| | | createTime: '', |
| | | confirmTime: '', |
| | | status: 'unconfirmed' |
| | | }) |
| | | id: "", |
| | | ruleId: "", |
| | | employee: "", |
| | | department: "", |
| | | createTime: "", |
| | | confirmTime: "", |
| | | status: "unconfirmed", |
| | | }); |
| | | |
| | | const regulationRules = { |
| | | title: [{ required: true, message: '请输入制度标题', trigger: 'blur' }], |
| | | category: [{ required: true, message: '请选择制度分类', trigger: 'change' }], |
| | | content: [{ required: true, message: '请输入制度内容', trigger: 'blur' }], |
| | | effectiveTime: [{ required: true, message: '请选择生效时间', trigger: 'change' }], |
| | | scope: [{ required: true, message: '请选择适用范围', trigger: 'change' }] |
| | | } |
| | | title: [{ required: true, message: "请输入制度标题", trigger: "blur" }], |
| | | category: [{ required: true, message: "请选择制度分类", trigger: "change" }], |
| | | content: [{ required: true, message: "请输入制度内容", trigger: "blur" }], |
| | | effectiveTime: [ |
| | | { required: true, message: "请选择生效时间", trigger: "change" }, |
| | | ], |
| | | scope: [{ required: true, message: "请选择适用范围", trigger: "change" }], |
| | | }; |
| | | |
| | | const regulationSearchForm = reactive({ |
| | | title: '', |
| | | category: '' |
| | | }) |
| | | title: "", |
| | | category: "", |
| | | }); |
| | | |
| | | // 假数据 |
| | | const sealApplications = ref([]) |
| | | const sealApplications = ref([]); |
| | | |
| | | const regulations = ref([]) |
| | | const regulations = ref([]); |
| | | |
| | | const versionHistory = ref([]) |
| | | const versionHistory = ref([]); |
| | | |
| | | const readStatusList = ref([]) |
| | | const readStatusList = ref([]); |
| | | // { employee: '陈志强', department: '销售部', readTime: '2025-01-11 10:30:00', confirmTime: '2025-01-11 10:35:00', status: 'confirmed' }, |
| | | // { employee: '刘雅婷', department: '技术部', readTime: '2025-01-11 14:20:00', confirmTime: '', status: 'unconfirmed' }, |
| | | // { employee: '王建国', department: '财务部', readTime: '2025-01-12 09:15:00', confirmTime: '2025-01-12 09:20:00', status: 'confirmed' } |
| | | |
| | | // 用印申请状态 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | pending: 'warning', |
| | | approved: 'success', |
| | | rejected: 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | pending: "warning", |
| | | approved: "success", |
| | | rejected: "danger", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | // 制度状态 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | pending: '待审批', |
| | | approved: '已通过', |
| | | rejected: '已拒绝' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | pending: "待审批", |
| | | approved: "已通过", |
| | | rejected: "已拒绝", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | // 用印类型 |
| | | const getSealTypeText = (sealType) => { |
| | | const getSealTypeText = sealType => { |
| | | const sealTypeMap = { |
| | | official: '公章', |
| | | contract: '合同专用章', |
| | | finance: '财务专用章', |
| | | tegal: '技术专用章' |
| | | } |
| | | return sealTypeMap[sealType] || '未知' |
| | | } |
| | | official: "公章", |
| | | contract: "合同专用章", |
| | | finance: "财务专用章", |
| | | tegal: "技术专用章", |
| | | }; |
| | | return sealTypeMap[sealType] || "未知"; |
| | | }; |
| | | // 制度分类 |
| | | const getCategoryText = (category) => { |
| | | const getCategoryText = category => { |
| | | const categoryMap = { |
| | | hr: '人事制度', |
| | | finance: '财务制度', |
| | | safety: '安全制度', |
| | | tech: '技术制度' |
| | | } |
| | | return categoryMap[category] || '未知' |
| | | } |
| | | hr: "人事制度", |
| | | finance: "财务制度", |
| | | safety: "安全制度", |
| | | tech: "技术制度", |
| | | }; |
| | | return categoryMap[category] || "未知"; |
| | | }; |
| | | // 搜索印章申请 |
| | | const searchSealApplications = () => { |
| | | page.current=1 |
| | | getSealApplicationList() |
| | | page.current = 1; |
| | | getSealApplicationList(); |
| | | |
| | | // ElMessage.success('搜索完成') |
| | | } |
| | | }; |
| | | // 重置印章申请搜索 |
| | | const resetSealSearch = () => { |
| | | sealSearchForm.title = '' |
| | | sealSearchForm.status = '' |
| | | searchSealApplications() |
| | | } |
| | | sealSearchForm.title = ""; |
| | | sealSearchForm.status = ""; |
| | | searchSealApplications(); |
| | | }; |
| | | // 搜索制度 |
| | | const searchRegulations = () => { |
| | | page.current=1 |
| | | getRegulationList() |
| | | } |
| | | page.current = 1; |
| | | getRegulationList(); |
| | | }; |
| | | // 重置制度搜索 |
| | | const resetRegulationSearch = () => { |
| | | regulationSearchForm.title = '' |
| | | regulationSearchForm.category = '' |
| | | searchRegulations() |
| | | } |
| | | regulationSearchForm.title = ""; |
| | | regulationSearchForm.category = ""; |
| | | searchRegulations(); |
| | | }; |
| | | // 提交用印申请 |
| | | const submitSealApplication = async () => { |
| | | try { |
| | | await sealFormRef.value.validate() |
| | | addSealApplication(sealForm).then(res => { |
| | | await sealFormRef.value.validate(); |
| | | addSealApplication(sealForm) |
| | | .then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('申请提交成功') |
| | | showSealApplyDialog.value = false |
| | | getSealApplicationList() |
| | | ElMessage.success("申请提交成功"); |
| | | showSealApplyDialog.value = false; |
| | | getSealApplicationList(); |
| | | Object.assign(sealForm, { |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | }) |
| | | applicationNum: "", |
| | | title: "", |
| | | sealType: "", |
| | | reason: "", |
| | | urgency: "normal", |
| | | status: "pending", |
| | | }); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg) |
| | | }) |
| | | |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } catch (error) { |
| | | ElMessage.error('请完善申请信息') |
| | | ElMessage.error("请完善申请信息"); |
| | | } |
| | | } |
| | | }; |
| | | // 新增 |
| | | const handleAdd = () => { |
| | | operationType.value = 'add' |
| | | resetRegulationForm() |
| | | showRegulationDialog.value = true |
| | | } |
| | | operationType.value = "add"; |
| | | resetRegulationForm(); |
| | | showRegulationDialog.value = true; |
| | | }; |
| | | |
| | | // 编辑 |
| | | const handleEdit = (row) => { |
| | | operationType.value = 'edit' |
| | | Object.assign(regulationForm, row) |
| | | showRegulationDialog.value = true |
| | | } |
| | | const handleEdit = row => { |
| | | operationType.value = "edit"; |
| | | Object.assign(regulationForm, row); |
| | | showRegulationDialog.value = true; |
| | | }; |
| | | // 废弃 |
| | | const repealEdit = (row) => { |
| | | operationType.value = 'edit' |
| | | Object.assign(regulationForm, row) |
| | | regulationForm.status = 'repealed' |
| | | ElMessageBox.confirm('确认废弃该制度?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | const repealEdit = row => { |
| | | operationType.value = "edit"; |
| | | Object.assign(regulationForm, row); |
| | | regulationForm.status = "repealed"; |
| | | ElMessageBox.confirm("确认废弃该制度?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | updateRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度废弃成功') |
| | | ElMessage.success("制度废弃成功"); |
| | | // showRegulationDialog.value = false |
| | | getRegulationList() |
| | | resetRegulationForm() |
| | | getRegulationList(); |
| | | resetRegulationForm(); |
| | | } |
| | | }); |
| | | }) |
| | | }).catch(() => { |
| | | .catch(() => { |
| | | ElMessage({ |
| | | type: 'info', |
| | | message: '已取消废弃' |
| | | }) |
| | | }) |
| | | } |
| | | type: "info", |
| | | message: "已取消废弃", |
| | | }); |
| | | }); |
| | | }; |
| | | // 发布制度 |
| | | const submitRegulation = async () => { |
| | | try { |
| | | await regulationFormRef.value.validate() |
| | | if(operationType.value == 'add'){ |
| | | await regulationFormRef.value.validate(); |
| | | if (operationType.value == "add") { |
| | | addRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度发布成功') |
| | | showRegulationDialog.value = false |
| | | getRegulationList() |
| | | resetRegulationForm() |
| | | ElMessage.success("制度发布成功"); |
| | | showRegulationDialog.value = false; |
| | | getRegulationList(); |
| | | resetRegulationForm(); |
| | | } |
| | | }) |
| | | }); |
| | | }else{ |
| | | updateRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度编辑成功') |
| | | showRegulationDialog.value = false |
| | | resetRegulationForm() |
| | | getRegulationList() |
| | | }})} |
| | | ElMessage.success("制度编辑成功"); |
| | | showRegulationDialog.value = false; |
| | | resetRegulationForm(); |
| | | getRegulationList(); |
| | | } |
| | | }); |
| | | } |
| | | }catch(err){ |
| | | ElMessage.error(err.msg) |
| | | ElMessage.error(err.msg); |
| | | } |
| | | } |
| | | }; |
| | | //重置制度表单 |
| | | const resetRegulationForm = () => { |
| | | Object.assign(regulationForm, { |
| | | id: '', |
| | | regulationNum: '', |
| | | title: '', |
| | | category: '', |
| | | content: '', |
| | | version: '', |
| | | status: 'active', |
| | | id: "", |
| | | regulationNum: "", |
| | | title: "", |
| | | category: "", |
| | | content: "", |
| | | version: "", |
| | | status: "active", |
| | | readCount: 0, |
| | | effectiveTime: '', |
| | | effectiveTime: "", |
| | | scope: [], |
| | | requireConfirm: false |
| | | }) |
| | | } |
| | | |
| | | requireConfirm: false, |
| | | }); |
| | | }; |
| | | |
| | | // 查看用印申请详情 |
| | | const viewSealDetail = (row) => { |
| | | currentSealDetail.value = row |
| | | showSealDetailDialog.value = true |
| | | } |
| | | const viewSealDetail = row => { |
| | | currentSealDetail.value = row; |
| | | showSealDetailDialog.value = true; |
| | | }; |
| | | // 审批用印申请 |
| | | const approveSeal = (row) => { |
| | | console.log(row) |
| | | ElMessageBox.confirm('确认通过该用印申请?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const approveSeal = row => { |
| | | console.log(row); |
| | | ElMessageBox.confirm("确认通过该用印申请?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | row.status = 'approved' |
| | | row.status = "approved"; |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('审批通过') |
| | | ElMessage.success("审批通过"); |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | // 拒绝用印申请 |
| | | const rejectSeal = (row) => { |
| | | ElMessageBox.prompt('请输入拒绝原因', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | const rejectSeal = row => { |
| | | ElMessageBox.prompt("请输入拒绝原因", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | inputPattern: /\S+/, |
| | | inputErrorMessage: '拒绝原因不能为空' |
| | | inputErrorMessage: "拒绝原因不能为空", |
| | | }).then(({ value }) => { |
| | | row.status = 'rejected' |
| | | row.status = "rejected"; |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('审批拒绝') |
| | | ElMessage.success("审批拒绝"); |
| | | } |
| | | }) |
| | | ElMessage.success('已拒绝申请') |
| | | }) |
| | | } |
| | | }); |
| | | ElMessage.success("已拒绝申请"); |
| | | }); |
| | | }; |
| | | // 获取在职员工列表 |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | //获取当前登录用户信息 |
| | | getUserProfile().then(res => { |
| | | if(res.code == 200){ |
| | | console.log(res.data.userName) |
| | | currentUser.value = res.data.userName |
| | | console.log(res.data.userName); |
| | | currentUser.value = res.data.userName; |
| | | } |
| | | }) |
| | | staffJoinListPage({staffState: 1, ...page}).then(res => { |
| | | }); |
| | | staffJoinListPage({ staffState: 1, ...page }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | // tableData.value = res.data.records |
| | | // //筛选出和currentUser同名的人员 |
| | | tableData.value = res.data.records.filter(item => item.staffName === currentUser.value) |
| | | tableData.value = res.data.records.filter( |
| | | item => item.staffName === currentUser.value |
| | | ); |
| | | page.total = res.data.total; |
| | | |
| | | if(tableData.value.length == 0){ |
| | | ElMessage.error('当前用户未加入任何部门') |
| | | ElMessage.error("当前用户未加入任何部门"); |
| | | } |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | |
| | | |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 查看制度版本历史 |
| | | const viewVersionHistory = (row) => { |
| | | showVersionHistoryDialog.value = true |
| | | const viewVersionHistory = row => { |
| | | showVersionHistoryDialog.value = true; |
| | | const params = { |
| | | |
| | | category: row.category |
| | | } |
| | | category: row.category, |
| | | }; |
| | | listRuleManagement(page,params).then(res => { |
| | | if(res.code == 200){ |
| | | versionHistory.value = res.data.records |
| | | versionHistory.value = res.data.records; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | // 查看制度详情 |
| | | const viewRegulation = (row) => { |
| | | getList() |
| | | currentRegulationDetail.value = row |
| | | showRegulationDetailDialog.value = true |
| | | const viewRegulation = row => { |
| | | getList(); |
| | | currentRegulationDetail.value = row; |
| | | showRegulationDetailDialog.value = true; |
| | | getReadingStatusByRuleId(row.id).then(res => { |
| | | if(res.code == 200){ |
| | | readStatusList.value = res.data |
| | | readStatusList.value = res.data; |
| | | if(readStatusList.value.length==0 && tableData.value.length>0){ |
| | | const params = { |
| | | ruleId: row.id, |
| | | employee: tableData.value[0].staffName, |
| | | department: tableData.value[0].postJob, |
| | | status: 'unconfirmed' |
| | | } |
| | | status: "unconfirmed", |
| | | }; |
| | | addReadingStatus(params).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度阅读成功') |
| | | ElMessage.success("制度阅读成功"); |
| | | } |
| | | }) |
| | | }); |
| | | } |
| | | } |
| | | }) |
| | | |
| | | } |
| | | }); |
| | | }; |
| | | // 查看制度阅读状态 |
| | | const viewReadStatus = (row) => { |
| | | showReadStatusDialog.value = true |
| | | const viewReadStatus = row => { |
| | | showReadStatusDialog.value = true; |
| | | //查看阅读状态列表 |
| | | getReadingStatusByRuleId(row.id).then(res => { |
| | | if(res.code == 200){ |
| | | readStatusList.value = res.data |
| | | readStatusList.value = res.data; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | //确认查看 |
| | | const resetForm = (row) => { |
| | | console.log("row",row) |
| | | row.readCount = row.readCount + 1 |
| | | const resetForm = row => { |
| | | console.log("row", row); |
| | | row.readCount = row.readCount + 1; |
| | | |
| | | updateRuleManagement(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('查看数量修改成功') |
| | | ElMessage.success("查看数量修改成功"); |
| | | //修改阅读状态 |
| | | //根据制度id和当前登录的员工得到阅读状态 |
| | | // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName ) |
| | |
| | | // item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0]; |
| | | // } |
| | | // 筛选当前员工对应该制度的阅读状态记录 |
| | | let statusItem = readStatusList.value.find(item => item.employee === tableData.value[0].staffName && item.ruleId === row.id); |
| | | let statusItem = readStatusList.value.find( |
| | | item => |
| | | item.employee === tableData.value[0].staffName && |
| | | item.ruleId === row.id |
| | | ); |
| | | |
| | | if (statusItem) { |
| | | // 如果找到记录,更新状态和确认时间 |
| | | statusItem.status = 'confirmed'; |
| | | statusItem.status = "confirmed"; |
| | | // 格式化时间为"YYYY-MM-DD HH:mm:ss"格式 |
| | | const now = new Date(); |
| | | statusItem.confirmTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; |
| | | statusItem.confirmTime = `${now.getFullYear()}-${String( |
| | | now.getMonth() + 1 |
| | | ).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String( |
| | | now.getHours() |
| | | ).padStart(2, "0")}:${String(now.getMinutes()).padStart( |
| | | 2, |
| | | "0" |
| | | )}:${String(now.getSeconds()).padStart(2, "0")}`; |
| | | // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0]; |
| | | |
| | | updateReadingStatus(statusItem).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度阅读状态修改成功') |
| | | ElMessage.success("制度阅读状态修改成功"); |
| | | } |
| | | }) |
| | | }); |
| | | } |
| | | |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 导出用印申请 |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | const handleExport = () => { |
| | | proxy.download('/sealApplicationManagement/export', { ...sealSearchForm }, '用印申请.xlsx') |
| | | } |
| | | proxy.download( |
| | | "/sealApplicationManagement/export", |
| | | { ...sealSearchForm }, |
| | | "用印申请.xlsx" |
| | | ); |
| | | }; |
| | | |
| | | // 获取印章申请列表数据 |
| | | const getSealApplicationList = async () => { |
| | | tableLoading.value = true |
| | | tableLoading.value = true; |
| | | listSealApplication(page,sealSearchForm) |
| | | .then(res => { |
| | | //获取当前登录的部门信息 |
| | | // 获取当前登录的部门信息并过滤数据 |
| | | const currentFactoryName = userStore.currentFactoryName |
| | | const currentFactoryName = userStore.currentFactoryName; |
| | | if (currentFactoryName) { |
| | | // 根据currentFactoryName过滤出department相同的数据 |
| | | sealApplications.value = res.data.records.filter(item => item.department === currentFactoryName) |
| | | sealApplications.value = res.data.records.filter( |
| | | item => item.department === currentFactoryName |
| | | ); |
| | | // 更新过滤后的总数 |
| | | page.total = sealApplications.value.length |
| | | page.total = sealApplications.value.length; |
| | | } else { |
| | | // 如果没有currentFactoryName,则显示所有数据 |
| | | sealApplications.value = res.data.records |
| | | page.total = res.data.total |
| | | sealApplications.value = res.data.records; |
| | | page.total = res.data.total; |
| | | } |
| | | // sealApplications.value = res.data.records |
| | | // page.value.total = res.data.total; |
| | | tableLoading.value = false; |
| | | |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 获取规章制度列表数据 |
| | | const getRegulationList = async () => { |
| | | tableLoading.value = true |
| | | tableLoading.value = true; |
| | | listRuleManagement(page,regulationSearchForm) |
| | | .then(res => { |
| | | |
| | | regulations.value = res.data.records |
| | | regulations.value = res.data.records; |
| | | // 过滤掉已废弃的制度 |
| | | // regulations.value = res.data.records.filter(item => item.status !== 'repealed') |
| | | page.total = res.data.total; |
| | | tableLoading.value = false; |
| | | |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 初始化 |
| | | getSealApplicationList() |
| | | getRegulationList() |
| | | }) |
| | | getSealApplicationList(); |
| | | getRegulationList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="defect-management"> |
| | | <!-- 操作按钮 --> |
| | | <div class="actions"> |
| | | <el-button type="primary" @click="showRegisterDialog = true">登记缺陷</el-button> |
| | | <el-button type="primary" |
| | | @click="showRegisterDialog = true">登记缺陷</el-button> |
| | | </div> |
| | | |
| | | <!-- 缺陷列表 --> |
| | | <el-table :data="defectList" style="width: 100%; margin-top: 10px;" border> |
| | | <el-table-column prop="deviceName" label="设备名称" width="180"></el-table-column> |
| | | <el-table-column prop="defectDescription" label="缺陷描述" win-width="300"></el-table-column> |
| | | <el-table-column prop="status" label="状态" width="220"> |
| | | <el-table :data="defectList" |
| | | style="width: 100%; margin-top: 10px;" |
| | | border> |
| | | <el-table-column prop="deviceName" |
| | | label="设备名称" |
| | | width="180"></el-table-column> |
| | | <el-table-column prop="defectDescription" |
| | | label="缺陷描述" |
| | | win-width="300"></el-table-column> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="220"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.status === '严重缺陷' ? 'danger' : 'success'"> |
| | | {{ row.status }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="220"> |
| | | <el-table-column label="操作" |
| | | width="220"> |
| | | <template #default="{ row }"> |
| | | <el-button |
| | | v-if="row.status === '严重缺陷' || row.status === '一般缺陷'" |
| | | <el-button v-if="row.status === '严重缺陷' || row.status === '一般缺陷'" |
| | | type="text" |
| | | @click="eliminateDefect(row)" |
| | | > |
| | | @click="eliminateDefect(row)"> |
| | | 消除缺陷 |
| | | </el-button> |
| | | <!-- <el-button |
| | |
| | | > |
| | | 转维修单 |
| | | </el-button> --> |
| | | <el-button type="text" @click="getLedger(row.deviceLedgerId)"> |
| | | <el-button type="text" |
| | | @click="getLedger(row.deviceLedgerId)"> |
| | | 查看台账 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 缺陷登记对话框 --> |
| | | <el-dialog title="登记设备缺陷" v-model="showRegisterDialog" width="50%"> |
| | | <el-form :model="defectForm" :rules="defectRules" ref="defectFormRef" label-width="100px"> |
| | | <el-form-item label="设备名称" prop="deviceName"> |
| | | <el-select v-model="defectForm.deviceLedgerId" @change="setDeviceModel"> |
| | | <el-option |
| | | v-for="(item, index) in deviceOptions" |
| | | <el-dialog title="登记设备缺陷" |
| | | v-model="showRegisterDialog" |
| | | width="50%"> |
| | | <el-form :model="defectForm" |
| | | :rules="defectRules" |
| | | ref="defectFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="设备名称" |
| | | prop="deviceName"> |
| | | <el-select v-model="defectForm.deviceLedgerId" |
| | | @change="setDeviceModel"> |
| | | <el-option v-for="(item, index) in deviceOptions" |
| | | :key="index" |
| | | :label="item.deviceName" |
| | | :value="item.id" |
| | | ></el-option> |
| | | :value="item.id"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="缺陷描述" prop="defectDescription"> |
| | | <el-input type="textarea" v-model="defectForm.defectDescription"></el-input> |
| | | <el-form-item label="缺陷描述" |
| | | prop="defectDescription"> |
| | | <el-input type="textarea" |
| | | v-model="defectForm.defectDescription"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="设备状态" prop="status"> |
| | | <el-form-item label="设备状态" |
| | | prop="status"> |
| | | <el-radio-group v-model="defectForm.status"> |
| | | <el-radio label="正常">正常</el-radio> |
| | | <el-radio label="一般缺陷">一般缺陷</el-radio> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitDefectForm">确定</el-button> |
| | | <el-button @click="showRegisterDialog = false">取消</el-button> |
| | | <el-button type="primary" @click="submitDefectForm">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 缺陷设备台账对话框 --> |
| | | <el-dialog title="缺陷设备台账" v-model="showLedgerDialog" width="80%"> |
| | | <el-table :data="ledgerList" style="width: 100%; margin-top: 10px;" border> |
| | | <el-table-column prop="deviceName" label="设备名称"></el-table-column> |
| | | <el-table-column prop="defectDescription" label="缺陷描述"></el-table-column> |
| | | <el-table-column prop="status" label="状态"></el-table-column> |
| | | <el-table-column prop="eliminateTime" label="消缺时间"></el-table-column> |
| | | <el-dialog title="缺陷设备台账" |
| | | v-model="showLedgerDialog" |
| | | width="80%"> |
| | | <el-table :data="ledgerList" |
| | | style="width: 100%; margin-top: 10px;" |
| | | border> |
| | | <el-table-column prop="deviceName" |
| | | label="设备名称"></el-table-column> |
| | | <el-table-column prop="defectDescription" |
| | | label="缺陷描述"></el-table-column> |
| | | <el-table-column prop="status" |
| | | label="状态"></el-table-column> |
| | | <el-table-column prop="eliminateTime" |
| | | label="消缺时间"></el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive } from 'vue'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import { ref, reactive } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | // 假设以下是后端接口 |
| | | import { |
| | |
| | | getDefectList, |
| | | eliminateDefect as apiEliminateDefect, |
| | | getDefectLedger, |
| | | deleteDefect |
| | | } from '@/api/equipmentManagement/defectManagement'; |
| | | deleteDefect, |
| | | } from "@/api/equipmentManagement/defectManagement"; |
| | | |
| | | // 缺陷列表 |
| | | const defectList = ref([]); |
| | |
| | | const showLedgerDialog = ref(false); |
| | | // 缺陷表单 |
| | | const defectForm = reactive({ |
| | | deviceLedgerId: '', |
| | | defectDescription: '', |
| | | status: '', |
| | | deviceLedgerId: "", |
| | | defectDescription: "", |
| | | status: "", |
| | | }); |
| | | const deviceOptions = ref([]); |
| | | // 表单验证规则 |
| | | const defectRules = reactive({ |
| | | deviceLedgerId: [{ required: true, message: '请输入设备名称', trigger: 'blur' }], |
| | | defectDescription: [{ required: true, message: '请输入缺陷描述', trigger: 'blur' }] |
| | | deviceLedgerId: [ |
| | | { required: true, message: "请输入设备名称", trigger: "blur" }, |
| | | ], |
| | | defectDescription: [ |
| | | { required: true, message: "请输入缺陷描述", trigger: "blur" }, |
| | | ], |
| | | }); |
| | | // 表单引用 |
| | | const defectFormRef = ref(null); |
| | |
| | | if (res.code === 200) { |
| | | defectList.value = res.data.records; |
| | | } else { |
| | | ElMessage.error(res.message || '获取缺陷列表失败'); |
| | | ElMessage.error(res.message || "获取缺陷列表失败"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('获取缺陷列表失败'); |
| | | ElMessage.error("获取缺陷列表失败"); |
| | | } |
| | | }; |
| | | |
| | |
| | | await defectFormRef.value.validate(); |
| | | const res = await registerDefect(defectForm); |
| | | if (res.code === 200) { |
| | | ElMessage.success('缺陷登记成功'); |
| | | ElMessage.success("缺陷登记成功"); |
| | | showRegisterDialog.value = false; |
| | | fetchDefectList(); |
| | | } else { |
| | | ElMessage.error(res.message || '缺陷登记失败'); |
| | | ElMessage.error(res.message || "缺陷登记失败"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('请填写完整表单信息'); |
| | | ElMessage.error("请填写完整表单信息"); |
| | | } |
| | | }; |
| | | |
| | | // 消除缺陷 |
| | | const eliminateDefect = async (row) => { |
| | | |
| | | const eliminateDefect = async row => { |
| | | try { |
| | | const res = await apiEliminateDefect(row); |
| | | if (res.code === 200) { |
| | | ElMessage.success('缺陷消除成功'); |
| | | ElMessage.success("缺陷消除成功"); |
| | | fetchDefectList(); |
| | | } else { |
| | | ElMessage.error(res.message || '缺陷消除失败'); |
| | | ElMessage.error(res.message || "缺陷消除失败"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('缺陷消除失败'); |
| | | ElMessage.error("缺陷消除失败"); |
| | | } |
| | | }; |
| | | |
| | |
| | | // }; |
| | | |
| | | // 获取缺陷设备台账 |
| | | const getLedger = async (deviceLedgerId) => { |
| | | const getLedger = async deviceLedgerId => { |
| | | try { |
| | | const res = await getDefectLedger(deviceLedgerId); |
| | | if (res.code === 200) { |
| | | ledgerList.value = res.data.records; |
| | | showLedgerDialog.value = true; |
| | | } else { |
| | | ElMessage.error(res.message || '获取缺陷设备台账失败'); |
| | | ElMessage.error(res.message || "获取缺陷设备台账失败"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('获取缺陷设备台账失败'); |
| | | ElMessage.error("获取缺陷设备台账失败"); |
| | | } |
| | | }; |
| | | |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'" |
| | | v-model="dialogVisitable" width="800px" @close="cancel"> |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> |
| | | v-model="dialogVisitable" |
| | | width="800px" |
| | | @close="cancel"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="120px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="设备名称" prop="taskId"> |
| | | <el-select v-model="form.taskId" @change="setDeviceModel" filterable> |
| | | <el-option |
| | | v-for="(item, index) in deviceOptions" |
| | | <el-form-item label="设备名称" |
| | | prop="taskId"> |
| | | <el-select v-model="form.taskId" |
| | | @change="setDeviceModel" |
| | | filterable> |
| | | <el-option v-for="(item, index) in deviceOptions" |
| | | :key="index" |
| | | :label="item.deviceName" |
| | | :value="item.id" |
| | | ></el-option> |
| | | :value="item.id"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="巡检人" prop="inspector"> |
| | | <el-select v-model="form.inspector" filterable |
| | | <el-form-item label="巡检人" |
| | | prop="inspector"> |
| | | <el-select v-model="form.inspector" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false" placeholder="请选择" multiple clearable> |
| | | <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/> |
| | | :reserve-keyword="false" |
| | | placeholder="请选择" |
| | | multiple |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | :key="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="备注" prop="remarks"> |
| | | <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" /> |
| | | <el-form-item label="备注" |
| | | prop="remarks"> |
| | | <el-input v-model="form.remarks" |
| | | placeholder="请输入备注" |
| | | type="textarea" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="登记时间" prop="dateStr"> |
| | | <el-date-picker |
| | | v-model="form.dateStr" |
| | | <el-form-item label="登记时间" |
| | | prop="dateStr"> |
| | | <el-date-picker v-model="form.dateStr" |
| | | type="date" |
| | | placeholder="选择登记日期" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="任务频率" prop="frequencyType"> |
| | | <el-select v-model="form.frequencyType" placeholder="请选择" clearable> |
| | | <el-option label="每日" value="DAILY"/> |
| | | <el-option label="每周" value="WEEKLY"/> |
| | | <el-option label="每月" value="MONTHLY"/> |
| | | <el-form-item label="任务频率" |
| | | prop="frequencyType"> |
| | | <el-select v-model="form.frequencyType" |
| | | placeholder="请选择" |
| | | clearable> |
| | | <el-option label="每日" |
| | | value="DAILY" /> |
| | | <el-option label="每周" |
| | | value="WEEKLY" /> |
| | | <el-option label="每月" |
| | | value="MONTHLY" /> |
| | | <!-- <el-option label="季度" value="QUARTERLY"/> --> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType"> |
| | | <el-form-item label="日期" prop="frequencyDetail"> |
| | | <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm" |
| | | <el-col :span="12" |
| | | v-if="form.frequencyType === 'DAILY' && form.frequencyType"> |
| | | <el-form-item label="日期" |
| | | prop="frequencyDetail"> |
| | | <el-time-picker v-model="form.frequencyDetail" |
| | | placeholder="选择时间" |
| | | format="HH:mm" |
| | | value-format="HH:mm" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType"> |
| | | <el-form-item label="日期" prop="frequencyDetail"> |
| | | <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%"> |
| | | <el-option label="周一" value="MON"/> |
| | | <el-option label="周二" value="TUE"/> |
| | | <el-option label="周三" value="WED"/> |
| | | <el-option label="周四" value="THU"/> |
| | | <el-option label="周五" value="FRI"/> |
| | | <el-option label="周六" value="SAT"/> |
| | | <el-option label="周日" value="SUN"/> |
| | | <el-col :span="12" |
| | | v-if="form.frequencyType === 'WEEKLY' && form.frequencyType"> |
| | | <el-form-item label="日期" |
| | | prop="frequencyDetail"> |
| | | <el-select v-model="form.week" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 50%"> |
| | | <el-option label="周一" |
| | | value="MON" /> |
| | | <el-option label="周二" |
| | | value="TUE" /> |
| | | <el-option label="周三" |
| | | value="WED" /> |
| | | <el-option label="周四" |
| | | value="THU" /> |
| | | <el-option label="周五" |
| | | value="FRI" /> |
| | | <el-option label="周六" |
| | | value="SAT" /> |
| | | <el-option label="周日" |
| | | value="SUN" /> |
| | | </el-select> |
| | | <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm" |
| | | value-format="HH:mm" style="width: 50%"/> |
| | | <el-time-picker v-model="form.time" |
| | | placeholder="选择时间" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | style="width: 50%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType"> |
| | | <el-form-item label="日期" prop="frequencyDetail"> |
| | | <el-date-picker |
| | | v-model="form.frequencyDetail" |
| | | <el-col :span="12" |
| | | v-if="form.frequencyType === 'MONTHLY' && form.frequencyType"> |
| | | <el-form-item label="日期" |
| | | prop="frequencyDetail"> |
| | | <el-date-picker v-model="form.frequencyDetail" |
| | | type="datetime" |
| | | clearable |
| | | placeholder="选择开始日期" |
| | | format="DD,HH:mm" |
| | | value-format="DD,HH:mm" |
| | | /> |
| | | value-format="DD,HH:mm" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType"> |
| | | <el-form-item label="日期" prop="frequencyDetail"> |
| | | <el-date-picker |
| | | v-model="form.frequencyDetail" |
| | | <el-col :span="12" |
| | | v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType"> |
| | | <el-form-item label="日期" |
| | | prop="frequencyDetail"> |
| | | <el-date-picker v-model="form.frequencyDetail" |
| | | type="datetime" |
| | | clearable |
| | | placeholder="选择开始日期" |
| | | format="MM,DD,HH:mm" |
| | | value-format="MM,DD,HH:mm" |
| | | /> |
| | | value-format="MM,DD,HH:mm" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">保存</el-button> |
| | | <el-button @click="cancel">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">保存</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | |
| | | <script setup> |
| | | import {reactive, ref} from "vue"; |
| | | import useUserStore from '@/store/modules/user' |
| | | import useUserStore from "@/store/modules/user"; |
| | | import {addOrEditTimingTask} from "@/api/inspectionManagement/index.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits() |
| | | const userStore = useUserStore() |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(); |
| | | const userStore = useUserStore(); |
| | | const dialogVisitable = ref(false); |
| | | const operationType = ref('add'); |
| | | const operationType = ref("add"); |
| | | const deviceOptions = ref([]); |
| | | const data = reactive({ |
| | | form: { |
| | | taskId: undefined, |
| | | taskName: undefined, |
| | | inspector: '', |
| | | inspectorIds: '', |
| | | remarks: '', |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | | week: '', |
| | | time: '', |
| | | dateStr: '' |
| | | inspector: "", |
| | | inspectorIds: "", |
| | | remarks: "", |
| | | frequencyType: "", |
| | | frequencyDetail: "", |
| | | week: "", |
| | | time: "", |
| | | dateStr: "", |
| | | }, |
| | | rules: { |
| | | taskId: [{ required: true, message: "请选择设备", trigger: "change" },], |
| | | inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },], |
| | | dateStr: [{ required: true, message: "请选择登记时间", trigger: "change" }] |
| | | } |
| | | }) |
| | | const { form, rules } = toRefs(data) |
| | | const userList = ref([]) |
| | | taskId: [{ required: true, message: "请选择设备", trigger: "change" }], |
| | | inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" }], |
| | | dateStr: [{ required: true, message: "请选择登记时间", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const userList = ref([]); |
| | | |
| | | const loadDeviceName = async () => { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data; |
| | | }; |
| | | |
| | | const setDeviceModel = (id) => { |
| | | const option = deviceOptions.value.find((item) => item.id === id); |
| | | const setDeviceModel = id => { |
| | | const option = deviceOptions.value.find(item => item.id === id); |
| | | if (option) { |
| | | form.value.taskName = option.deviceName; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 打开弹框 |
| | | const openDialog = async (type, row) => { |
| | | dialogVisitable.value = true |
| | | operationType.value = type |
| | | dialogVisitable.value = true; |
| | | operationType.value = type; |
| | | |
| | | // 重置表单 |
| | | resetForm(); |
| | | |
| | | // 加载用户列表 |
| | | userListNoPageByTenantId().then((res) => { |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | |
| | | // 加载设备列表 |
| | | await loadDeviceName(); |
| | | |
| | | if (type === 'edit' && row) { |
| | | form.value = {...row} |
| | | form.value.inspector = form.value.inspectorIds.split(',').map(Number) |
| | | if (type === "edit" && row) { |
| | | form.value = { ...row }; |
| | | form.value.inspector = form.value.inspectorIds.split(",").map(Number); |
| | | |
| | | // 如果有设备ID,自动设置设备信息 |
| | | if (form.value.taskId) { |
| | | setDeviceModel(form.value.taskId); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 关闭对话框 |
| | | const cancel = () => { |
| | | resetForm() |
| | | dialogVisitable.value = false |
| | | emit('closeDia') |
| | | } |
| | | resetForm(); |
| | | dialogVisitable.value = false; |
| | | emit("closeDia"); |
| | | }; |
| | | |
| | | // 重置表单函数 |
| | | const resetForm = () => { |
| | | if (proxy.$refs.formRef) { |
| | | proxy.$refs.formRef.resetFields() |
| | | proxy.$refs.formRef.resetFields(); |
| | | } |
| | | // 重置表单数据确保设备信息正确重置 |
| | | form.value = { |
| | | taskId: undefined, |
| | | taskName: undefined, |
| | | inspector: '', |
| | | inspectorIds: '', |
| | | remarks: '', |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | | week: '', |
| | | time: '' |
| | | } |
| | | } |
| | | inspector: "", |
| | | inspectorIds: "", |
| | | remarks: "", |
| | | frequencyType: "", |
| | | frequencyDetail: "", |
| | | week: "", |
| | | time: "", |
| | | }; |
| | | }; |
| | | |
| | | // 提交表单 |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate(async valid => { |
| | | if (valid) { |
| | | try { |
| | | form.value.inspectorIds = form.value.inspector.join(',') |
| | | delete form.value.inspector |
| | | form.value.inspectorIds = form.value.inspector.join(","); |
| | | delete form.value.inspector; |
| | | |
| | | if (form.value.frequencyType === 'WEEKLY') { |
| | | let frequencyDetail = '' |
| | | frequencyDetail = form.value.week + ',' + form.value.time |
| | | form.value.frequencyDetail = frequencyDetail |
| | | if (form.value.frequencyType === "WEEKLY") { |
| | | let frequencyDetail = ""; |
| | | frequencyDetail = form.value.week + "," + form.value.time; |
| | | form.value.frequencyDetail = frequencyDetail; |
| | | } |
| | | |
| | | let res = await userStore.getInfo() |
| | | form.value.registrantId = res.user.userId |
| | | let res = await userStore.getInfo(); |
| | | form.value.registrantId = res.user.userId; |
| | | |
| | | await addOrEditTimingTask(form.value) |
| | | cancel() |
| | | proxy.$modal.msgSuccess('提交成功') |
| | | await addOrEditTimingTask(form.value); |
| | | cancel(); |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | } catch (error) { |
| | | proxy.$modal.msgError('提交失败,请重试') |
| | | proxy.$modal.msgError("提交失败,请重试"); |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | defineExpose({ openDialog }) |
| | | }); |
| | | }; |
| | | defineExpose({ openDialog }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | |
| | | <div class="spare-part-category"> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" size="large">设备分类</el-text> |
| | | <el-text class="mx-1" |
| | | size="large">设备分类</el-text> |
| | | <div> |
| | | <el-button @click="fetchTreeData" :loading="loading">刷新</el-button> |
| | | <el-button type="primary" @click="addCategory" >新增</el-button> |
| | | <el-button @click="fetchTreeData" |
| | | :loading="loading">刷新</el-button> |
| | | <el-button type="primary" |
| | | @click="addCategory">新增</el-button> |
| | | </div> |
| | | </div> |
| | | <el-table |
| | | v-loading="loading" |
| | | <el-table v-loading="loading" |
| | | :data="renderTableData" |
| | | style="width: 100%; margin-top: 10px;" |
| | | border |
| | | row-key="id" |
| | | :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" |
| | | > |
| | | <el-table-column prop="name" label="分类名称" width="450"> |
| | | :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"> |
| | | <el-table-column prop="name" |
| | | label="分类名称" |
| | | width="450"> |
| | | <template #default="{ row }"> |
| | | <span :style="{ paddingLeft: getIndentation(row) + 'px' }"> |
| | | {{ row.name }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="sparePartsNo" label="分类编号" width="200"></el-table-column> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <el-table-column prop="sparePartsNo" |
| | | label="分类编号" |
| | | width="200"></el-table-column> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag type="success" size="small">{{ row.status }}</el-tag> |
| | | <el-tag type="success" |
| | | size="small">{{ row.status }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="description" label="描述" win-width="330"></el-table-column> |
| | | <el-table-column label="操作" width="180" fixed="right"> |
| | | <el-table-column prop="description" |
| | | label="描述" |
| | | win-width="330"></el-table-column> |
| | | <el-table-column label="操作" |
| | | width="180" |
| | | fixed="right"> |
| | | <template #default="{ row }"> |
| | | <el-button |
| | | type="text" |
| | | <el-button type="text" |
| | | size="small" |
| | | @click="() => editCategory(row)" |
| | | :disabled="loading" |
| | | > |
| | | :disabled="loading"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button |
| | | type="text" |
| | | <el-button type="text" |
| | | size="small" |
| | | @click="() => deleteCategory(row.id)" |
| | | style="color: #f56c6c;" |
| | | :disabled="loading" |
| | | > |
| | | :disabled="loading"> |
| | | 删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <el-dialog title="分类管理" v-model="dialogVisible" width="60%"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-form-item label="分类名称" prop="name"> |
| | | <el-dialog title="分类管理" |
| | | v-model="dialogVisible" |
| | | width="60%"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-form-item label="分类名称" |
| | | prop="name"> |
| | | <el-input v-model="form.name"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="分类编号" prop="sparePartsNo"> |
| | | <el-form-item label="分类编号" |
| | | prop="sparePartsNo"> |
| | | <el-input v-model="form.sparePartsNo"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择状态"> |
| | | <el-option label="正常" value="正常"></el-option> |
| | | <el-option label="禁用" value="禁用"></el-option> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="请选择状态"> |
| | | <el-option label="正常" |
| | | value="正常"></el-option> |
| | | <el-option label="禁用" |
| | | value="禁用"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="描述" prop="description"> |
| | | <el-form-item label="描述" |
| | | prop="description"> |
| | | <el-input v-model="form.description"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="上级分类" prop="parentId"> |
| | | <el-select v-model="form.parentId" placeholder="请选择上级分类"> |
| | | <el-option label="无上级分类" :value="null"></el-option> |
| | | <el-option |
| | | v-for="(item, index) in categories" |
| | | <el-form-item label="上级分类" |
| | | prop="parentId"> |
| | | <el-select v-model="form.parentId" |
| | | placeholder="请选择上级分类"> |
| | | <el-option label="无上级分类" |
| | | :value="null"></el-option> |
| | | <el-option v-for="(item, index) in categories" |
| | | :key="index" |
| | | :label="item.name" |
| | | :value="item.id" |
| | | |
| | | ></el-option> |
| | | :value="item.id"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false" :disabled="formLoading">取消</el-button> |
| | | <el-button type="primary" @click="submitForm" :loading="formLoading">确定</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm" |
| | | :loading="formLoading">确定</el-button> |
| | | <el-button @click="dialogVisible = false" |
| | | :disabled="formLoading">取消</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, reactive, watch } from 'vue'; |
| | | import { ElMessage, ElMessageBox } from 'element-plus'; |
| | | import { getSparePartsList, addSparePart, editSparePart, delSparePart,getSparePartsTree } from "@/api/equipmentManagement/spareParts"; |
| | | import { ref, computed, onMounted, reactive, watch } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | getSparePartsList, |
| | | addSparePart, |
| | | editSparePart, |
| | | delSparePart, |
| | | getSparePartsTree, |
| | | } from "@/api/equipmentManagement/spareParts"; |
| | | |
| | | // 加载状态 |
| | | const loading = ref(false); |
| | |
| | | // 渲染用的表格数据 |
| | | // const renderTableData = computed(() => buildTree(categories.value)); |
| | | const renderTableData = ref([]); |
| | | const operationType = ref('add') |
| | | const operationType = ref("add"); |
| | | // 表单引用 |
| | | const formRef = ref(null); |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | id:'', |
| | | name: '', |
| | | sparePartsNo: '', |
| | | status: '', |
| | | description: '', |
| | | parentId: null |
| | | id: "", |
| | | name: "", |
| | | sparePartsNo: "", |
| | | status: "", |
| | | description: "", |
| | | parentId: null, |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = reactive({ |
| | | name: [ |
| | | { required: true, message: '请输入分类名称', trigger: 'blur' } |
| | | ], |
| | | name: [{ required: true, message: "请输入分类名称", trigger: "blur" }], |
| | | sparePartsNo: [ |
| | | { required: true, message: '请输入分类编号', trigger: 'blur' } |
| | | { required: true, message: "请输入分类编号", trigger: "blur" }, |
| | | ], |
| | | status: [ |
| | | { required: true, message: '请选择状态', trigger: 'change' } |
| | | ] |
| | | status: [{ required: true, message: "请选择状态", trigger: "change" }], |
| | | }); |
| | | // 获取缩进量 |
| | | const getIndentation = (row) => { |
| | | const getIndentation = row => { |
| | | // 这里简单返回 20,可根据实际需求实现层级缩进逻辑 |
| | | return 20; |
| | | }; |
| | | // 定义 buildTree 函数 |
| | | const buildTree = (flatData) => { |
| | | const buildTree = flatData => { |
| | | const map = {}; |
| | | const result = []; |
| | | if(flatData){ |
| | |
| | | if (res.code === 200) { |
| | | renderTableData.value = res.data; |
| | | } else { |
| | | ElMessage.error(res.message || '获取分类列表失败'); |
| | | ElMessage.error(res.message || "获取分类列表失败"); |
| | | } |
| | | }catch (error) { |
| | | ElMessage.error('获取分类列表失败'); |
| | | ElMessage.error("获取分类列表失败"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 获取分类列表 |
| | | const fetchCategories = async () => { |
| | |
| | | if (res.code === 200) { |
| | | categories.value = res.data.records; |
| | | } else { |
| | | ElMessage.error(res.message || '获取分类列表失败'); |
| | | ElMessage.error(res.message || "获取分类列表失败"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('获取分类列表失败'); |
| | | ElMessage.error("获取分类列表失败"); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | |
| | | |
| | | // 新增分类 |
| | | const addCategory = () => { |
| | | form.id = ''; |
| | | form.name = ''; |
| | | form.sparePartsNo = ''; |
| | | form.status = ''; |
| | | form.description = ''; |
| | | form.id = ""; |
| | | form.name = ""; |
| | | form.sparePartsNo = ""; |
| | | form.status = ""; |
| | | form.description = ""; |
| | | form.parentId = null; |
| | | operationType.value = 'add' |
| | | operationType.value = "add"; |
| | | dialogVisible.value = true; |
| | | console.log('dialogVisible 更新为', dialogVisible.value); |
| | | console.log("dialogVisible 更新为", dialogVisible.value); |
| | | }; |
| | | |
| | | // 编辑分类 |
| | | const editCategory = (row) => { |
| | | const editCategory = row => { |
| | | Object.assign(form, row); |
| | | operationType.value = 'edit' |
| | | operationType.value = "edit"; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // 删除分类 |
| | | const deleteCategory = async (id) => { |
| | | const deleteCategory = async id => { |
| | | try { |
| | | await ElMessageBox.confirm('此操作将永久删除该分类,是否继续?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | await ElMessageBox.confirm("此操作将永久删除该分类,是否继续?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }); |
| | | loading.value = true; |
| | | const res = await delSparePart(id); |
| | | if (res.code === 200) { |
| | | ElMessage.success('删除成功'); |
| | | ElMessage.success("删除成功"); |
| | | fetchTreeData(); |
| | | } else { |
| | | ElMessage.error(res.message || '删除失败'); |
| | | ElMessage.error(res.message || "删除失败"); |
| | | } |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error('删除失败'); |
| | | if (error !== "cancel") { |
| | | ElMessage.error("删除失败"); |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | |
| | | try { |
| | | await formRef.value.validate(); |
| | | formLoading.value = true; |
| | | if (operationType.value === 'edit') { |
| | | if (operationType.value === "edit") { |
| | | let res = await editSparePart(form); |
| | | if (res.code === 200) { |
| | | ElMessage.success('编辑成功'); |
| | | ElMessage.success("编辑成功"); |
| | | dialogVisible.value = false; |
| | | fetchTreeData(); |
| | | } |
| | | } else { |
| | | let res = await addSparePart(form); |
| | | if (res.code === 200) { |
| | | ElMessage.success('编辑成功'); |
| | | ElMessage.success("编辑成功"); |
| | | dialogVisible.value = false; |
| | | fetchTreeData(); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('请填写完整表单信息'); |
| | | ElMessage.error("请填写完整表单信息"); |
| | | } finally { |
| | | formLoading.value = false; |
| | | } |
| | |
| | | |
| | | /* 空状态样式 */ |
| | | .el-table .cell:empty::before { |
| | | content: '-'; |
| | | content: "-"; |
| | | color: #c0c4cc; |
| | | } |
| | | |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search-form"> |
| | | <el-form :inline="true" :model="searchForm"> |
| | | <el-form :inline="true" |
| | | :model="searchForm"> |
| | | <el-form-item label="部门"> |
| | | <el-input |
| | | v-model="searchForm.department" |
| | | <el-input v-model="searchForm.department" |
| | | placeholder="请输入部门名称" |
| | | clearable |
| | | style="width: 200px" |
| | | /> |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="姓名"> |
| | | <el-input |
| | | v-model="searchForm.name" |
| | | <el-input v-model="searchForm.name" |
| | | placeholder="请输入姓名" |
| | | clearable |
| | | style="width: 200px" |
| | | /> |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="handleReset">重置</el-button> |
| | | <el-button type="success" @click="handleAdd">新增</el-button> |
| | | <el-button type="success" |
| | | @click="handleAdd">新增</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <div class="table-container"> |
| | | <DynamicTable |
| | | ref="dynamicTableRef" |
| | | <DynamicTable ref="dynamicTableRef" |
| | | :data="tableData" |
| | | :dict-types="dictTypes" |
| | | :loading="loading" |
| | |
| | | @select-change="handleSelectChange" |
| | | @input-change="handleInputChange" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | @current-change="handleCurrentChange" /> |
| | | </div> |
| | | |
| | | <!-- 新增/编辑对话框 --> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="600px" |
| | | append-to-body |
| | | > |
| | | <el-form |
| | | ref="formRef" |
| | | append-to-body> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="100px" |
| | | > |
| | | <el-form-item label="部门" prop="department"> |
| | | <el-input v-model="form.department" placeholder="请输入部门" /> |
| | | label-width="100px"> |
| | | <el-form-item label="部门" |
| | | prop="department"> |
| | | <el-input v-model="form.department" |
| | | placeholder="请输入部门" /> |
| | | </el-form-item> |
| | | <el-form-item label="姓名" prop="name"> |
| | | <el-input v-model="form.name" placeholder="请输入姓名" /> |
| | | <el-form-item label="姓名" |
| | | prop="name"> |
| | | <el-input v-model="form.name" |
| | | placeholder="请输入姓名" /> |
| | | </el-form-item> |
| | | <el-form-item label="工号" prop="employeeId"> |
| | | <el-input v-model="form.employeeId" placeholder="请输入工号" /> |
| | | <el-form-item label="工号" |
| | | prop="employeeId"> |
| | | <el-input v-model="form.employeeId" |
| | | placeholder="请输入工号" /> |
| | | </el-form-item> |
| | | |
| | | <!-- 动态表单项:根据字典生成 --> |
| | | <el-form-item |
| | | v-for="dictItem in dynamicFormItems" |
| | | <el-form-item v-for="dictItem in dynamicFormItems" |
| | | :key="dictItem.value" |
| | | :label="dictItem.label" |
| | | :prop="dictItem.value" |
| | | > |
| | | <el-select |
| | | v-model="form[dictItem.value]" |
| | | :prop="dictItem.value"> |
| | | <el-select v-model="form[dictItem.value]" |
| | | placeholder="请选择" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="option in dictItem.options" |
| | | style="width: 100%"> |
| | | <el-option v-for="option in dictItem.options" |
| | | :key="option.value" |
| | | :label="option.label" |
| | | :value="option.value" |
| | | /> |
| | | :value="option.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSubmit">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="handleSubmit">确定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import DynamicTable from '@/components/DynamicTable/index.vue' |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import DynamicTable from "@/components/DynamicTable/index.vue"; |
| | | |
| | | // 响应式数据 |
| | | const loading = ref(false) |
| | | const dialogVisible = ref(false) |
| | | const dialogTitle = ref('') |
| | | const editIndex = ref(-1) |
| | | const selectedRows = ref([]) |
| | | const loading = ref(false); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const editIndex = ref(-1); |
| | | const selectedRows = ref([]); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | department: '', |
| | | name: '' |
| | | }) |
| | | department: "", |
| | | name: "", |
| | | }); |
| | | |
| | | // 表格数据 |
| | | const tableData = ref([ |
| | | { |
| | | id: 1, |
| | | department: '技术部', |
| | | name: '张三', |
| | | employeeId: 'EMP001', |
| | | status: '1', |
| | | level: '2', |
| | | position: '1' |
| | | department: "技术部", |
| | | name: "张三", |
| | | employeeId: "EMP001", |
| | | status: "1", |
| | | level: "2", |
| | | position: "1", |
| | | }, |
| | | { |
| | | id: 2, |
| | | department: '人事部', |
| | | name: '李四', |
| | | employeeId: 'EMP002', |
| | | status: '0', |
| | | level: '1', |
| | | position: '2' |
| | | department: "人事部", |
| | | name: "李四", |
| | | employeeId: "EMP002", |
| | | status: "0", |
| | | level: "1", |
| | | position: "2", |
| | | }, |
| | | { |
| | | id: 3, |
| | | department: '财务部', |
| | | name: '王五', |
| | | employeeId: 'EMP003', |
| | | status: '1', |
| | | level: '3', |
| | | position: '1' |
| | | } |
| | | ]) |
| | | department: "财务部", |
| | | name: "王五", |
| | | employeeId: "EMP003", |
| | | status: "1", |
| | | level: "3", |
| | | position: "1", |
| | | }, |
| | | ]); |
| | | |
| | | // 字典类型配置 |
| | | const dictTypes = ref([ |
| | | 'sys_normal_disable', // 状态字典 |
| | | 'sys_user_level', // 级别字典 |
| | | 'sys_user_position' // 职位字典 |
| | | ]) |
| | | "sys_normal_disable", // 状态字典 |
| | | "sys_user_level", // 级别字典 |
| | | "sys_user_position", // 职位字典 |
| | | ]); |
| | | |
| | | // 分页配置 |
| | | const pagination = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0 |
| | | }) |
| | | total: 0, |
| | | }); |
| | | |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | department: '', |
| | | name: '', |
| | | employeeId: '', |
| | | status: '', |
| | | level: '', |
| | | position: '' |
| | | }) |
| | | department: "", |
| | | name: "", |
| | | employeeId: "", |
| | | status: "", |
| | | level: "", |
| | | position: "", |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | department: [ |
| | | { required: true, message: '请输入部门', trigger: 'blur' } |
| | | ], |
| | | name: [ |
| | | { required: true, message: '请输入姓名', trigger: 'blur' } |
| | | ], |
| | | employeeId: [ |
| | | { required: true, message: '请输入工号', trigger: 'blur' } |
| | | ] |
| | | } |
| | | department: [{ required: true, message: "请输入部门", trigger: "blur" }], |
| | | name: [{ required: true, message: "请输入姓名", trigger: "blur" }], |
| | | employeeId: [{ required: true, message: "请输入工号", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 动态表单项 |
| | | const dynamicFormItems = computed(() => { |
| | | // 这里可以根据字典数据动态生成表单项 |
| | | return [ |
| | | { |
| | | label: '状态', |
| | | value: 'status', |
| | | label: "状态", |
| | | value: "status", |
| | | options: [ |
| | | { label: '启用', value: '1' }, |
| | | { label: '禁用', value: '0' } |
| | | ] |
| | | { label: "启用", value: "1" }, |
| | | { label: "禁用", value: "0" }, |
| | | ], |
| | | }, |
| | | { |
| | | label: '级别', |
| | | value: 'level', |
| | | label: "级别", |
| | | value: "level", |
| | | options: [ |
| | | { label: '初级', value: '1' }, |
| | | { label: '中级', value: '2' }, |
| | | { label: '高级', value: '3' } |
| | | ] |
| | | { label: "初级", value: "1" }, |
| | | { label: "中级", value: "2" }, |
| | | { label: "高级", value: "3" }, |
| | | ], |
| | | }, |
| | | { |
| | | label: '职位', |
| | | value: 'position', |
| | | label: "职位", |
| | | value: "position", |
| | | options: [ |
| | | { label: '员工', value: '1' }, |
| | | { label: '主管', value: '2' }, |
| | | { label: '经理', value: '3' } |
| | | ] |
| | | } |
| | | ] |
| | | }) |
| | | { label: "员工", value: "1" }, |
| | | { label: "主管", value: "2" }, |
| | | { label: "经理", value: "3" }, |
| | | ], |
| | | }, |
| | | ]; |
| | | }); |
| | | |
| | | // 组件引用 |
| | | const dynamicTableRef = ref(null) |
| | | const formRef = ref(null) |
| | | const dynamicTableRef = ref(null); |
| | | const formRef = ref(null); |
| | | |
| | | // 事件处理函数 |
| | | const handleSearch = () => { |
| | | // 实现搜索逻辑 |
| | | console.log('搜索条件:', searchForm) |
| | | ElMessage.success('搜索功能待实现') |
| | | } |
| | | console.log("搜索条件:", searchForm); |
| | | ElMessage.success("搜索功能待实现"); |
| | | }; |
| | | |
| | | const handleReset = () => { |
| | | searchForm.department = '' |
| | | searchForm.name = '' |
| | | } |
| | | searchForm.department = ""; |
| | | searchForm.name = ""; |
| | | }; |
| | | |
| | | const handleAdd = () => { |
| | | dialogTitle.value = '新增员工' |
| | | editIndex.value = -1 |
| | | resetForm() |
| | | dialogVisible.value = true |
| | | } |
| | | dialogTitle.value = "新增员工"; |
| | | editIndex.value = -1; |
| | | resetForm(); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleEdit = (row, index) => { |
| | | dialogTitle.value = '编辑员工' |
| | | editIndex.value = index |
| | | Object.assign(form, row) |
| | | dialogVisible.value = true |
| | | } |
| | | dialogTitle.value = "编辑员工"; |
| | | editIndex.value = index; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = async (row, index) => { |
| | | try { |
| | | await ElMessageBox.confirm('确定要删除这条记录吗?', '提示', { |
| | | type: 'warning' |
| | | }) |
| | | await ElMessageBox.confirm("确定要删除这条记录吗?", "提示", { |
| | | type: "warning", |
| | | }); |
| | | |
| | | tableData.value.splice(index, 1) |
| | | ElMessage.success('删除成功') |
| | | tableData.value.splice(index, 1); |
| | | ElMessage.success("删除成功"); |
| | | } catch (error) { |
| | | // 用户取消删除 |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection |
| | | } |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | const handleSelectChange = (row, prop, value) => { |
| | | console.log('选择变化:', row, prop, value) |
| | | console.log("选择变化:", row, prop, value); |
| | | // 可以在这里处理数据更新逻辑 |
| | | } |
| | | }; |
| | | |
| | | const handleInputChange = (row, prop, value) => { |
| | | console.log('输入变化:', row, prop, value) |
| | | console.log("输入变化:", row, prop, value); |
| | | // 可以在这里处理数据更新逻辑 |
| | | } |
| | | }; |
| | | |
| | | const handleSizeChange = (size) => { |
| | | pagination.size = size |
| | | const handleSizeChange = size => { |
| | | pagination.size = size; |
| | | // 重新加载数据 |
| | | } |
| | | }; |
| | | |
| | | const handleCurrentChange = (current) => { |
| | | pagination.current = current |
| | | const handleCurrentChange = current => { |
| | | pagination.current = current; |
| | | // 重新加载数据 |
| | | } |
| | | }; |
| | | |
| | | const handleSubmit = async () => { |
| | | try { |
| | | await formRef.value.validate() |
| | | await formRef.value.validate(); |
| | | |
| | | if (editIndex.value === -1) { |
| | | // 新增 |
| | | const newRow = { |
| | | id: Date.now(), |
| | | ...form |
| | | } |
| | | tableData.value.push(newRow) |
| | | ElMessage.success('新增成功') |
| | | ...form, |
| | | }; |
| | | tableData.value.push(newRow); |
| | | ElMessage.success("新增成功"); |
| | | } else { |
| | | // 编辑 |
| | | Object.assign(tableData.value[editIndex.value], form) |
| | | ElMessage.success('编辑成功') |
| | | Object.assign(tableData.value[editIndex.value], form); |
| | | ElMessage.success("编辑成功"); |
| | | } |
| | | |
| | | dialogVisible.value = false |
| | | dialogVisible.value = false; |
| | | } catch (error) { |
| | | console.error('表单验证失败:', error) |
| | | console.error("表单验证失败:", error); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const resetForm = () => { |
| | | Object.assign(form, { |
| | | department: '', |
| | | name: '', |
| | | employeeId: '', |
| | | status: '', |
| | | level: '', |
| | | position: '' |
| | | }) |
| | | formRef.value?.resetFields() |
| | | } |
| | | department: "", |
| | | name: "", |
| | | employeeId: "", |
| | | status: "", |
| | | level: "", |
| | | position: "", |
| | | }); |
| | | formRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // 组件挂载时初始化数据 |
| | | onMounted(() => { |
| | | pagination.total = tableData.value.length |
| | | }) |
| | | pagination.total = tableData.value.length; |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <template> |
| | | <div class="sample"> |
| | | <div class="main-content" v-if="!isDetail"> |
| | | <div class="main-content" |
| | | v-if="!isDetail"> |
| | | <div class="search"> |
| | | <div class="search_thing"> |
| | | <div class="search_label">仓库名称:</div> |
| | | <div class="search_input"> |
| | | <el-select v-model="entity.warehouseId" placeholder="选择仓库" size="small" @change="warehouseChange"> |
| | | <el-option v-for="item in warehouse" :key="item.id" :label="item.label" :value="item.id"> |
| | | <el-select v-model="entity.warehouseId" |
| | | placeholder="选择仓库" |
| | | size="small" |
| | | @change="warehouseChange"> |
| | | <el-option v-for="item in warehouse" |
| | | :key="item.id" |
| | | :label="item.label" |
| | | :value="item.id"> |
| | | </el-option> |
| | | </el-select> |
| | | </div> |
| | |
| | | <div class="search_thing"> |
| | | <div class="search_label">货架:</div> |
| | | <div class="search_input"> |
| | | <el-select v-model="entity.shelfId" placeholder="选择货架" size="small" @change="handleShelf"> |
| | | <el-option v-for="item in shelf" :key="item.id" :label="item.label" :value="item.id"> |
| | | <el-select v-model="entity.shelfId" |
| | | placeholder="选择货架" |
| | | size="small" |
| | | @change="handleShelf"> |
| | | <el-option v-for="item in shelf" |
| | | :key="item.id" |
| | | :label="item.label" |
| | | :value="item.id"> |
| | | </el-option> |
| | | </el-select> |
| | | </div> |
| | |
| | | <el-button size="small" type="primary" @click="handleShelf(entity.shelfId)">查询</el-button> |
| | | </div> --> |
| | | <div class="btns"> |
| | | <el-button size="small" style="color:#3A7BFA" @click="keepVisible=true">维护</el-button> |
| | | <el-button size="small" style="color:#3A7BFA" @click="warehouseVisible=true,isEdit=false">添加仓库</el-button> |
| | | <el-button size="small" style="color:#3A7BFA" @click="shelvesVisible=true,isEdit=false" |
| | | <el-button size="small" |
| | | style="color:#3A7BFA" |
| | | @click="keepVisible=true">维护</el-button> |
| | | <el-button size="small" |
| | | style="color:#3A7BFA" |
| | | @click="warehouseVisible=true,isEdit=false">添加仓库</el-button> |
| | | <el-button size="small" |
| | | style="color:#3A7BFA" |
| | | @click="shelvesVisible=true,isEdit=false" |
| | | :disabled="entity.warehouseId==null">添加货架</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table" v-loading="tableLoading"> |
| | | <table class="tables" style="table-layout:fixed;" v-if="tableList.length>0"> |
| | | <div class="table" |
| | | v-loading="tableLoading"> |
| | | <table class="tables" |
| | | style="table-layout:fixed;" |
| | | v-if="tableList.length>0"> |
| | | <tbody> |
| | | <tr v-for="(item,index) in tableList" :key="index"> |
| | | <td v-for="(m,i) in item" :key="i" class="content"> |
| | | <tr v-for="(item,index) in tableList" |
| | | :key="index"> |
| | | <td v-for="(m,i) in item" |
| | | :key="i" |
| | | class="content"> |
| | | <h4 v-if="m.row!=undefined">{{ m.row }} - {{ m.col }}</h4> |
| | | <ul> |
| | | <el-tooltip |
| | | effect="dark" |
| | | <el-tooltip effect="dark" |
| | | placement="top" |
| | | v-for="(n,j) in m.documentationDtoList" |
| | | :key="j"> |
| | |
| | | </td> |
| | | </tr> |
| | | <tr> |
| | | <td v-for="(item,index) in rowList" :key="index" style="background: ghostwhite;height: 20px;">{{ item }} |
| | | <td v-for="(item,index) in rowList" |
| | | :key="index" |
| | | style="background: ghostwhite;height: 20px;">{{ item }} |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | <span v-else style="color: rgb(144, 147, 153);display: inline-block;position: absolute;top: 60%;left: 50%;transform: translate(-50%,-50%);">暂无数据</span> |
| | | <span v-else |
| | | style="color: rgb(144, 147, 153);display: inline-block;position: absolute;top: 60%;left: 50%;transform: translate(-50%,-50%);">暂无数据</span> |
| | | </div> |
| | | </div> |
| | | <Detail v-else @hanldeBack="isDetail=false" :current="current" /> |
| | | |
| | | <Detail v-else |
| | | @hanldeBack="isDetail=false" |
| | | :current="current" /> |
| | | <!-- 库位维护对话框 --> |
| | | <el-dialog v-model="keepVisible" title="库位维护" width="350px" :append-to-body="true"> |
| | | <el-tree :data="warehouse" ref="tree" node-key="id" |
| | | highlight-current v-if="keepVisible" |
| | | <el-dialog v-model="keepVisible" |
| | | title="库位维护" |
| | | width="350px" |
| | | :append-to-body="true"> |
| | | <el-tree :data="warehouse" |
| | | ref="tree" |
| | | node-key="id" |
| | | highlight-current |
| | | v-if="keepVisible" |
| | | empty-text="暂无数据"> |
| | | <template #default="{ node, data }"> |
| | | <div class="custom-tree-node" style="width: 100%;"> |
| | | <div class="custom-tree-node" |
| | | style="width: 100%;"> |
| | | <el-row style="width: 100%;display: flex;align-items: center;"> |
| | | <el-col :span="14"> |
| | | <span> |
| | | <el-icon v-if="node.level < 2" class="folder-icon"> |
| | | <el-icon v-if="node.level < 2" |
| | | class="folder-icon"> |
| | | <FolderOpened /> |
| | | </el-icon> |
| | | <el-icon v-else class="file-icon"> |
| | | <el-icon v-else |
| | | class="file-icon"> |
| | | <Document /> |
| | | </el-icon> |
| | | {{ data.label }} |
| | | </span> |
| | | </el-col> |
| | | <el-col :span="10" v-if="node.level<3"> |
| | | <el-button type="link" size="small" :icon="Edit" @click.stop="handleEdit(data,node.level)"> |
| | | <el-col :span="10" |
| | | v-if="node.level<3"> |
| | | <el-button type="link" |
| | | size="small" |
| | | :icon="Edit" |
| | | @click.stop="handleEdit(data,node.level)"> |
| | | </el-button> |
| | | <el-button type="danger" size="small" :icon="Delete" @click.stop="handleDelete(data,node.level)"> |
| | | <el-button type="danger" |
| | | size="small" |
| | | :icon="Delete" |
| | | @click.stop="handleDelete(data,node.level)"> |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-tree> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="keepVisible = false">确 定</el-button> |
| | | <el-button @click="keepVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="keepVisible = false" >确 定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 仓库新增/修改对话框 --> |
| | | <el-dialog v-model="warehouseVisible" :title="isEdit?'仓库修改':'仓库新增'" width="350px"> |
| | | <el-dialog v-model="warehouseVisible" |
| | | :title="isEdit?'仓库修改':'仓库新增'" |
| | | width="350px"> |
| | | <el-row> |
| | | <el-col class="search_thing" :span="24"> |
| | | <el-col class="search_thing" |
| | | :span="24"> |
| | | <div class="search_label"><span class="required-span">* </span>仓库名称:</div> |
| | | <div class="search_input"> |
| | | <el-input v-model="name" size="small" @keyup.enter="confirmWarehouse"></el-input> |
| | | <el-input v-model="name" |
| | | size="small" |
| | | @keyup.enter="confirmWarehouse"></el-input> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="confirmWarehouse" |
| | | :loading="upLoadWarehouse">确 定</el-button> |
| | | <el-button @click="warehouseVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="confirmWarehouse" :loading="upLoadWarehouse">确 定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 货架新增/修改对话框 --> |
| | | <el-dialog v-model="shelvesVisible" :title="isEdit?'货架修改':'货架新增'" width="350px"> |
| | | <el-dialog v-model="shelvesVisible" |
| | | :title="isEdit?'货架修改':'货架新增'" |
| | | width="350px"> |
| | | <el-row> |
| | | <el-col class="search_thing" :span="24"> |
| | | <el-col class="search_thing" |
| | | :span="24"> |
| | | <div class="search_label"><span class="required-span">* </span>货架名称:</div> |
| | | <div class="search_input"> |
| | | <el-input v-model="shelves.name" size="small"></el-input> |
| | | <el-input v-model="shelves.name" |
| | | size="small"></el-input> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col class="search_thing" :span="24"> |
| | | <el-col class="search_thing" |
| | | :span="24"> |
| | | <div class="search_label"><span class="required-span">* </span>货架层数:</div> |
| | | <div class="search_input"> |
| | | <el-input v-model="shelves.row" size="small"></el-input> |
| | | <el-input v-model="shelves.row" |
| | | size="small"></el-input> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col class="search_thing" :span="24"> |
| | | <el-col class="search_thing" |
| | | :span="24"> |
| | | <div class="search_label"><span class="required-span">* </span>货架列数:</div> |
| | | <div class="search_input"> |
| | | <el-input v-model="shelves.col" size="small"></el-input> |
| | | <el-input v-model="shelves.col" |
| | | size="small"></el-input> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="confirmShelves" |
| | | :loading="upLoadShelves">确 定</el-button> |
| | | <el-button @click="shelvesVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="confirmShelves" :loading="upLoadShelves">确 定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, watch } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Edit, Delete, FolderOpened, Document } from '@element-plus/icons-vue' |
| | | import { getWarehouseList, addWarehouse, updateWarehouse, deleteWarehouse, getWarehouseStructure, addShelf, updateShelf, deleteShelf } from '@/api/fileManagement/bookshelf' |
| | | import Detail from './detail.vue' |
| | | import { ref, reactive, onMounted, watch } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Edit, Delete, FolderOpened, Document } from "@element-plus/icons-vue"; |
| | | import { |
| | | getWarehouseList, |
| | | addWarehouse, |
| | | updateWarehouse, |
| | | deleteWarehouse, |
| | | getWarehouseStructure, |
| | | addShelf, |
| | | updateShelf, |
| | | deleteShelf, |
| | | } from "@/api/fileManagement/bookshelf"; |
| | | import Detail from "./detail.vue"; |
| | | |
| | | // 响应式数据 |
| | | const entity = reactive({ |
| | | warehouseId: null, |
| | | shelfId: null |
| | | }) |
| | | shelfId: null, |
| | | }); |
| | | |
| | | const warehouse = ref([]) |
| | | const shelf = ref([]) |
| | | const keepVisible = ref(false) |
| | | const warehouseVisible = ref(false) |
| | | const shelvesVisible = ref(false) |
| | | const upLoadWarehouse = ref(false) |
| | | const upLoadShelves = ref(false) |
| | | const tableList = ref([]) |
| | | const rowList = ref([]) |
| | | const value = ref('') |
| | | const name = ref('') |
| | | const shelves = reactive({}) |
| | | const isEdit = ref(false) |
| | | const isDetail = ref(false) |
| | | const currentEdit = ref(null) |
| | | const tableLoading = ref(false) |
| | | const current = ref({}) |
| | | const warehouse = ref([]); |
| | | const shelf = ref([]); |
| | | const keepVisible = ref(false); |
| | | const warehouseVisible = ref(false); |
| | | const shelvesVisible = ref(false); |
| | | const upLoadWarehouse = ref(false); |
| | | const upLoadShelves = ref(false); |
| | | const tableList = ref([]); |
| | | const rowList = ref([]); |
| | | const value = ref(""); |
| | | const name = ref(""); |
| | | const shelves = reactive({}); |
| | | const isEdit = ref(false); |
| | | const isDetail = ref(false); |
| | | const currentEdit = ref(null); |
| | | const tableLoading = ref(false); |
| | | const current = ref({}); |
| | | |
| | | // 模板引用 |
| | | const organization = ref(null) |
| | | const organization = ref(null); |
| | | |
| | | // 监听器 |
| | | watch(isEdit, (newVal) => { |
| | | watch(isEdit, newVal => { |
| | | if (!newVal) { |
| | | Object.keys(shelves).forEach(key => delete shelves[key]) |
| | | Object.keys(shelves).forEach(key => delete shelves[key]); |
| | | } |
| | | }) |
| | | }); |
| | | |
| | | // 方法 |
| | | |
| | | const selectList = async () => { |
| | | // 这里需要替换为实际的API调用 |
| | | const res = await getWarehouseList() |
| | | warehouse.value = res.data |
| | | const res = await getWarehouseList(); |
| | | warehouse.value = res.data; |
| | | |
| | | if (warehouse.value.length == 0) { |
| | | entity.warehouseId = '' |
| | | entity.shelfId = '' |
| | | tableList.value = [] |
| | | entity.warehouseId = ""; |
| | | entity.shelfId = ""; |
| | | tableList.value = []; |
| | | } |
| | | |
| | | |
| | | |
| | | if (!entity.warehouseId && warehouse.value.length > 0) { |
| | | entity.warehouseId = warehouse.value[0].id |
| | | warehouseChange(entity.warehouseId) |
| | | entity.warehouseId = warehouse.value[0].id; |
| | | warehouseChange(entity.warehouseId); |
| | | if (shelf.value.length > 0) { |
| | | entity.shelfId = shelf.value[0].id |
| | | handleShelf(entity.shelfId) |
| | | entity.shelfId = shelf.value[0].id; |
| | | handleShelf(entity.shelfId); |
| | | } else { |
| | | tableList.value = [] |
| | | tableList.value = []; |
| | | } |
| | | } else if (warehouse.value.length > 0) { |
| | | warehouseChange(entity.warehouseId) |
| | | warehouseChange(entity.warehouseId); |
| | | if (shelf.value.length > 0) { |
| | | entity.shelfId = shelf.value[0].id |
| | | handleShelf(entity.shelfId) |
| | | entity.shelfId = shelf.value[0].id; |
| | | handleShelf(entity.shelfId); |
| | | } else { |
| | | tableList.value = [] |
| | | tableList.value = []; |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const confirmWarehouse = () => { |
| | | if (!name.value) { |
| | | ElMessage.error('请填写仓库名称') |
| | | return |
| | | ElMessage.error("请填写仓库名称"); |
| | | return; |
| | | } |
| | | upLoadWarehouse.value = true |
| | | upLoadWarehouse.value = true; |
| | | |
| | | if (currentEdit.value && currentEdit.value.id) { |
| | | // 修改仓库 |
| | | // 这里需要替换为实际的API调用 |
| | | updateWarehouse({ |
| | | id: currentEdit.value.id, |
| | | warehouseName: name.value |
| | | warehouseName: name.value, |
| | | }).then(res => { |
| | | upLoadWarehouse.value = false |
| | | warehouseVisible.value = false |
| | | currentEdit.value = null |
| | | ElMessage.success('修改成功') |
| | | selectList() |
| | | name.value = '' |
| | | warehouseChange(entity.warehouseId) |
| | | }) |
| | | |
| | | upLoadWarehouse.value = false; |
| | | warehouseVisible.value = false; |
| | | currentEdit.value = null; |
| | | ElMessage.success("修改成功"); |
| | | selectList(); |
| | | name.value = ""; |
| | | warehouseChange(entity.warehouseId); |
| | | }); |
| | | } else { |
| | | // 新增仓库 |
| | | // 这里需要替换为实际的API调用 |
| | | addWarehouse({ |
| | | warehouseName: name.value |
| | | warehouseName: name.value, |
| | | }).then(res => { |
| | | upLoadWarehouse.value = false |
| | | warehouseVisible.value = false |
| | | ElMessage.success('添加成功') |
| | | selectList() |
| | | name.value = '' |
| | | warehouseChange(entity.warehouseId) |
| | | }) |
| | | upLoadWarehouse.value = false; |
| | | warehouseVisible.value = false; |
| | | ElMessage.success("添加成功"); |
| | | selectList(); |
| | | name.value = ""; |
| | | warehouseChange(entity.warehouseId); |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const confirmShelves = () => { |
| | | if (!shelves.name) { |
| | | ElMessage.error('请填写货架名称') |
| | | return |
| | | ElMessage.error("请填写货架名称"); |
| | | return; |
| | | } |
| | | if (!shelves.row) { |
| | | ElMessage.error('请填写货架层数') |
| | | return |
| | | ElMessage.error("请填写货架层数"); |
| | | return; |
| | | } |
| | | if (!shelves.col) { |
| | | ElMessage.error('请填写货架列数') |
| | | return |
| | | ElMessage.error("请填写货架列数"); |
| | | return; |
| | | } |
| | | upLoadShelves.value = true |
| | | upLoadShelves.value = true; |
| | | |
| | | if (currentEdit.value && currentEdit.value.id) { |
| | | // 修改 |
| | |
| | | name: shelves.name, |
| | | row: Number(shelves.row), |
| | | col: Number(shelves.col), |
| | | warehouseId: entity.warehouseId |
| | | }).then(res => { |
| | | upLoadShelves.value = false |
| | | shelvesVisible.value = false |
| | | ElMessage.success('修改成功') |
| | | selectList() |
| | | currentEdit.value = {} |
| | | }).catch(err => { |
| | | upLoadShelves.value = false |
| | | shelvesVisible.value = false |
| | | ElMessage.error('修改失败') |
| | | warehouseId: entity.warehouseId, |
| | | }) |
| | | |
| | | .then(res => { |
| | | upLoadShelves.value = false; |
| | | shelvesVisible.value = false; |
| | | ElMessage.success("修改成功"); |
| | | selectList(); |
| | | currentEdit.value = {}; |
| | | }) |
| | | .catch(err => { |
| | | upLoadShelves.value = false; |
| | | shelvesVisible.value = false; |
| | | ElMessage.error("修改失败"); |
| | | }); |
| | | } else { |
| | | // 新增 |
| | | // 这里需要替换为实际的API调用 |
| | |
| | | name: shelves.name, |
| | | row: Number(shelves.row), |
| | | col: Number(shelves.col), |
| | | warehouseId: entity.warehouseId |
| | | }).then(res => { |
| | | upLoadShelves.value = false |
| | | shelvesVisible.value = false |
| | | ElMessage.success('添加成功') |
| | | selectList() |
| | | Object.keys(shelves).forEach(key => delete shelves[key]) |
| | | }).catch(err => { |
| | | upLoadShelves.value = false |
| | | shelvesVisible.value = false |
| | | ElMessage.error('添加失败') |
| | | warehouseId: entity.warehouseId, |
| | | }) |
| | | .then(res => { |
| | | upLoadShelves.value = false; |
| | | shelvesVisible.value = false; |
| | | ElMessage.success("添加成功"); |
| | | selectList(); |
| | | Object.keys(shelves).forEach(key => delete shelves[key]); |
| | | }) |
| | | .catch(err => { |
| | | upLoadShelves.value = false; |
| | | shelvesVisible.value = false; |
| | | ElMessage.error("添加失败"); |
| | | }); |
| | | } |
| | | warehouseChange(entity.warehouseId) |
| | | } |
| | | |
| | | |
| | | warehouseChange(entity.warehouseId); |
| | | }; |
| | | |
| | | const handleDelete = (row, level) => { |
| | | ElMessageBox.confirm('是否删除当前数据?', "警告", { |
| | | ElMessageBox.confirm("是否删除当前数据?", "警告", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | }).then(() => { |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | if (level == 1) { |
| | | // 删除仓库 |
| | | deleteWarehouse([row.id]).then(res => { |
| | | ElMessage.success('删除成功') |
| | | selectList() |
| | | }) |
| | | ElMessage.success("删除成功"); |
| | | selectList(); |
| | | }); |
| | | } else { |
| | | // 删除货架 |
| | | deleteShelf({ |
| | | id: row.id |
| | | id: row.id, |
| | | }).then(res => { |
| | | ElMessage.success('删除成功') |
| | | selectList() |
| | | ElMessage.success("删除成功"); |
| | | selectList(); |
| | | }); |
| | | } |
| | | warehouseChange(entity.warehouseId); |
| | | }) |
| | | } |
| | | warehouseChange(entity.warehouseId) |
| | | }).catch(() => {}) |
| | | } |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | const handleEdit = (data, level) => { |
| | | isEdit.value = true |
| | | isEdit.value = true; |
| | | if (level == 1) { |
| | | warehouseVisible.value = true |
| | | currentEdit.value = data |
| | | name.value = data.label |
| | | warehouseVisible.value = true; |
| | | currentEdit.value = data; |
| | | name.value = data.label; |
| | | } else { |
| | | shelvesVisible.value = true |
| | | currentEdit.value = data |
| | | shelvesVisible.value = true; |
| | | currentEdit.value = data; |
| | | Object.assign(shelves, { |
| | | name: data.label, |
| | | row: data.row, |
| | | col: data.col, |
| | | warehouseId: data.warehouseId |
| | | }) |
| | | warehouseId: data.warehouseId, |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handelDetail = (row) => { |
| | | current.value = row |
| | | isDetail.value = true |
| | | } |
| | | const handelDetail = row => { |
| | | current.value = row; |
| | | isDetail.value = true; |
| | | }; |
| | | |
| | | // 根据文档状态返回对应的颜色 |
| | | const getStatusColor = (status) => { |
| | | if (status === '正常') { |
| | | return '#34BD66' // 绿色 |
| | | } else if (status === '借出') { |
| | | return '#F56C6C' // 红色 |
| | | const getStatusColor = status => { |
| | | if (status === "正常") { |
| | | return "#34BD66"; // 绿色 |
| | | } else if (status === "借出") { |
| | | return "#F56C6C"; // 红色 |
| | | } |
| | | return '#606266' // 默认颜色 |
| | | } |
| | | return "#606266"; // 默认颜色 |
| | | }; |
| | | |
| | | const warehouseChange = (val) => { |
| | | tableList.value = [] |
| | | const warehouseChange = val => { |
| | | tableList.value = []; |
| | | let map = warehouse.value.find(a => { |
| | | return a && a.id === val ? a : null |
| | | }) |
| | | return a && a.id === val ? a : null; |
| | | }); |
| | | if (map && map.children) { |
| | | shelf.value = map.children |
| | | entity.shelfId = '' |
| | | shelf.value = map.children; |
| | | entity.shelfId = ""; |
| | | } else { |
| | | shelf.value = [] |
| | | shelf.value = []; |
| | | } |
| | | currentEdit.value = null |
| | | } |
| | | currentEdit.value = null; |
| | | }; |
| | | |
| | | const handleShelf = async(e) => { |
| | | const handleShelf = async e => { |
| | | if (e) { |
| | | tableLoading.value = true |
| | | let data = [] |
| | | const res = await getWarehouseStructure({warehouseGoodsShelvesId:e}) |
| | | tableLoading.value = true; |
| | | let data = []; |
| | | const res = await getWarehouseStructure({ warehouseGoodsShelvesId: e }); |
| | | if(res.code == 200){ |
| | | data = res.data.map(m=>{ |
| | | m.books = m.documentationDtoList|[] |
| | | return m |
| | | }) |
| | | m.books = m.documentationDtoList | []; |
| | | return m; |
| | | }); |
| | | }else{ |
| | | ElMessage.error(res.message) |
| | | ElMessage.error(res.message); |
| | | } |
| | | setTimeout(() => { |
| | | tableLoading.value = false |
| | | let set = new Set() |
| | | tableList.value = [] |
| | | let arr = [] |
| | | tableLoading.value = false; |
| | | let set = new Set(); |
| | | tableList.value = []; |
| | | let arr = []; |
| | | |
| | | if (data && data.length > 0) { |
| | | data.forEach(m => { |
| | | if (m && m.row && m.col) { |
| | | set.add(m.col) |
| | | set.add(m.col); |
| | | if (arr.length > 0) { |
| | | if (arr.find(n => n.row == m.row)) { |
| | | arr.push(m) |
| | | arr.push(m); |
| | | } else { |
| | | tableList.value.push(arr) |
| | | arr = [] |
| | | arr.push(m) |
| | | tableList.value.push(arr); |
| | | arr = []; |
| | | arr.push(m); |
| | | } |
| | | } else { |
| | | arr.push(m) |
| | | arr.push(m); |
| | | } |
| | | } |
| | | }) |
| | | }); |
| | | |
| | | if (arr.length > 0) { |
| | | tableList.value.push(arr) |
| | | tableList.value.push(arr); |
| | | } |
| | | } |
| | | |
| | | rowList.value = [] |
| | | rowList.value = []; |
| | | for (let i = 0; i < set.size; i++) { |
| | | rowList.value.push(`${i + 1} 列`) |
| | | rowList.value.push(`${i + 1} 列`); |
| | | } |
| | | console.log(6666, tableList.value,rowList.value,data) |
| | | }, 1000) |
| | | console.log(6666, tableList.value, rowList.value, data); |
| | | }, 1000); |
| | | } |
| | | } |
| | | |
| | | |
| | | }; |
| | | |
| | | // 生命周期 |
| | | onMounted(() => { |
| | | selectList() |
| | | }) |
| | | selectList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | } |
| | | |
| | | li:hover i { |
| | | background: #3A7BFA; |
| | | background: #3a7bfa; |
| | | } |
| | | |
| | | li:hover .num { |
| | | color: #3A7BFA; |
| | | color: #3a7bfa; |
| | | } |
| | | |
| | | .green { |
| | | background: #E0F6EA; |
| | | background: #e0f6ea; |
| | | } |
| | | |
| | | .green i { |
| | | background: #34BD66; |
| | | background: #34bd66; |
| | | } |
| | | |
| | | .green .num { |
| | | color: #34BD66; |
| | | color: #34bd66; |
| | | } |
| | | |
| | | .el-dialog { |
| | |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 14px; |
| | | color: #3A7BFA; |
| | | color: #3a7bfa; |
| | | position: absolute; |
| | | top: 23px; |
| | | right: 54px; |
| | |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDialog">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDialog">取消</el-button> |
| | | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | <template> |
| | | <div class="milestone-list-container"> |
| | | <el-timeline> |
| | | <el-timeline-item |
| | | v-for="milestone in milestoneList" |
| | | <el-timeline-item v-for="milestone in milestoneList" |
| | | :key="milestone.phaseId" |
| | | :timestamp="milestone.endDate" |
| | | > |
| | | :timestamp="milestone.endDate"> |
| | | <el-card> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>{{ milestone.phaseName }}</span> |
| | | <div class="milestone-actions"> |
| | | <el-button type="text" size="small" @click="handleEdit(milestone)">编辑</el-button> |
| | | <el-button type="text" size="small" @click="handleDelete(milestone)" danger>删除</el-button> |
| | | <el-button type="text" |
| | | size="small" |
| | | @click="handleEdit(milestone)">编辑</el-button> |
| | | <el-button type="text" |
| | | size="small" |
| | | @click="handleDelete(milestone)" |
| | | danger>删除</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | </el-card> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | |
| | | <!-- 无里程碑时的提示 --> |
| | | <div v-if="milestoneList.length === 0" class="empty-tip"> |
| | | <div v-if="milestoneList.length === 0" |
| | | class="empty-tip"> |
| | | <el-empty description="暂无里程碑数据" /> |
| | | </div> |
| | | |
| | | <!-- 编辑里程碑对话框 --> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="'编辑里程碑: ' + (form.phaseName || '')" |
| | | width="600px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-form |
| | | ref="formRef" |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="100px" |
| | | > |
| | | <el-form-item label="里程碑名称" prop="phaseName"> |
| | | <el-input v-model="form.phaseName" placeholder="请输入里程碑名称" /> |
| | | label-width="100px"> |
| | | <el-form-item label="里程碑名称" |
| | | prop="phaseName"> |
| | | <el-input v-model="form.phaseName" |
| | | placeholder="请输入里程碑名称" /> |
| | | </el-form-item> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始日期" prop="startDate"> |
| | | <el-date-picker |
| | | v-model="form.startDate" |
| | | <el-form-item label="开始日期" |
| | | prop="startDate"> |
| | | <el-date-picker v-model="form.startDate" |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="选择开始日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="结束日期" prop="endDate"> |
| | | <el-date-picker |
| | | v-model="form.endDate" |
| | | <el-form-item label="结束日期" |
| | | prop="endDate"> |
| | | <el-date-picker v-model="form.endDate" |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="选择结束日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择状态"> |
| | | <el-option label="未开始" value="notStarted" /> |
| | | <el-option label="已完成" value="completed" /> |
| | | <el-option label="已延迟" value="delayed" /> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="请选择状态"> |
| | | <el-option label="未开始" |
| | | value="notStarted" /> |
| | | <el-option label="已完成" |
| | | value="completed" /> |
| | | <el-option label="已延迟" |
| | | value="delayed" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitEditForm">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="submitEditForm">确定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, watch, reactive } from 'vue'; |
| | | import { ElMessage, ElMessageBox } from 'element-plus'; |
| | | import { getProject, listProjectPhase, updateProjectPhase,delProjectPhase } from '@/api/oaSystem/projectManagement'; |
| | | import { ref, onMounted, watch, reactive } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | getProject, |
| | | listProjectPhase, |
| | | updateProjectPhase, |
| | | delProjectPhase, |
| | | } from "@/api/oaSystem/projectManagement"; |
| | | |
| | | const props = defineProps({ |
| | | projectId: { |
| | | type: String, |
| | | required: true |
| | | } |
| | | required: true, |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(['refresh']); |
| | | const emit = defineEmits(["refresh"]); |
| | | |
| | | const milestoneList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const formRef = ref(null); |
| | | const form = reactive({ |
| | | phaseId: '', |
| | | phaseName: '', |
| | | startDate: '', |
| | | endDate: '', |
| | | status: 'notStarted', |
| | | projectId: props.projectId |
| | | phaseId: "", |
| | | phaseName: "", |
| | | startDate: "", |
| | | endDate: "", |
| | | status: "notStarted", |
| | | projectId: props.projectId, |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | phaseName: [ |
| | | { required: true, message: '请输入里程碑名称', trigger: 'blur' }, |
| | | { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' } |
| | | { required: true, message: "请输入里程碑名称", trigger: "blur" }, |
| | | { min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" }, |
| | | ], |
| | | status: [ |
| | | { required: true, message: '请选择状态', trigger: 'change' } |
| | | ] |
| | | status: [{ required: true, message: "请选择状态", trigger: "change" }], |
| | | }; |
| | | |
| | | // 获取里程碑列表 |
| | |
| | | milestoneList.value = res.data.rows || res.data; |
| | | // 按目标日期排序 |
| | | // milestoneList.value.sort((a, b) => new Date(a.endDate) - new Date(b.endDate)); |
| | | }) |
| | | }); |
| | | } catch (error) { |
| | | ElMessage.error('获取里程碑列表失败'); |
| | | console.error('获取里程碑列表失败:', error); |
| | | ElMessage.error("获取里程碑列表失败"); |
| | | console.error("获取里程碑列表失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 编辑里程碑 |
| | | const handleEdit = (milestone) => { |
| | | const handleEdit = milestone => { |
| | | // 复制里程碑数据到表单 |
| | | Object.assign(form, { |
| | | phaseId: milestone.phaseId, |
| | |
| | | description: milestone.description, |
| | | endDate: milestone.endDate, |
| | | status: milestone.status, |
| | | projectId: props.projectId |
| | | projectId: props.projectId, |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | |
| | | const res = await updateProjectPhase(form); |
| | | |
| | | if (res.code === 200) { |
| | | ElMessage.success('里程碑编辑成功'); |
| | | ElMessage.success("里程碑编辑成功"); |
| | | dialogVisible.value = false; |
| | | getMilestoneList(); // 刷新列表 |
| | | emit('refresh'); // 通知父组件刷新 |
| | | emit("refresh"); // 通知父组件刷新 |
| | | } else { |
| | | ElMessage.error(res.msg || '里程碑编辑失败'); |
| | | ElMessage.error(res.msg || "里程碑编辑失败"); |
| | | } |
| | | } catch (error) { |
| | | if (error.name === 'ValidationError') { |
| | | if (error.name === "ValidationError") { |
| | | // 表单验证失败,Element Plus会自动提示 |
| | | return; |
| | | } |
| | | ElMessage.error('里程碑编辑失败'); |
| | | console.error('编辑里程碑失败:', error); |
| | | ElMessage.error("里程碑编辑失败"); |
| | | console.error("编辑里程碑失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 删除里程碑 |
| | | const handleDelete = (milestone) => { |
| | | const handleDelete = milestone => { |
| | | ElMessageBox.confirm( |
| | | `确定要删除里程碑 "${milestone.phaseName}" 吗?删除后将无法恢复。`, |
| | | '删除确认', |
| | | "删除确认", |
| | | { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(async () => { |
| | |
| | | const res = await delProjectPhase(milestone.phaseId); |
| | | |
| | | if (res.code === 200) { |
| | | ElMessage.success('里程碑删除成功'); |
| | | ElMessage.success("里程碑删除成功"); |
| | | getMilestoneList(); // 刷新列表 |
| | | emit('refresh'); // 通知父组件刷新 |
| | | emit("refresh"); // 通知父组件刷新 |
| | | } else { |
| | | ElMessage.error(res.msg || '里程碑删除失败'); |
| | | ElMessage.error(res.msg || "里程碑删除失败"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('里程碑删除失败'); |
| | | console.error('删除里程碑失败:', error); |
| | | ElMessage.error("里程碑删除失败"); |
| | | console.error("删除里程碑失败:", error); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | // 用户取消删除 |
| | | ElMessage.info('已取消删除'); |
| | | ElMessage.info("已取消删除"); |
| | | }); |
| | | }; |
| | | |
| | | // 获取状态标签类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusTypeMap = { |
| | | notStarted: 'info', |
| | | completed: 'success', |
| | | delayed: 'danger' |
| | | notStarted: "info", |
| | | completed: "success", |
| | | delayed: "danger", |
| | | }; |
| | | return statusTypeMap[status] || 'default'; |
| | | return statusTypeMap[status] || "default"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusTextMap = { |
| | | notStarted: '未开始', |
| | | completed: '已完成', |
| | | delayed: '已延迟' |
| | | notStarted: "未开始", |
| | | completed: "已完成", |
| | | delayed: "已延迟", |
| | | }; |
| | | return statusTextMap[status] || status; |
| | | }; |
| | | |
| | | // 监听项目ID变化 |
| | | watch(() => props.projectId, () => { |
| | | watch( |
| | | () => props.projectId, |
| | | () => { |
| | | if (props.projectId) { |
| | | getMilestoneList(); |
| | | } |
| | | }); |
| | | } |
| | | ); |
| | | |
| | | // 初始化 |
| | | onMounted(() => { |
| | |
| | | <div class="task-tree-container"> |
| | | <!-- 任务树操作按钮 --> |
| | | <div class="tree-actions mb10"> |
| | | <el-button type="primary" size="small" icon="Plus" @click="handleAddTask">添加任务</el-button> |
| | | <el-button type="success" size="small" icon="RefreshRight" @click="refreshTree">刷新</el-button> |
| | | <el-button type="info" size="small" icon="Filter" @click="toggleFilter"> |
| | | <el-button type="primary" |
| | | size="small" |
| | | icon="Plus" |
| | | @click="handleAddTask">添加任务</el-button> |
| | | <el-button type="success" |
| | | size="small" |
| | | icon="RefreshRight" |
| | | @click="refreshTree">刷新</el-button> |
| | | <el-button type="info" |
| | | size="small" |
| | | icon="Filter" |
| | | @click="toggleFilter"> |
| | | {{ showFilter ? '隐藏筛选' : '显示筛选' }} |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- 筛选条件 --> |
| | | <div v-if="showFilter" class="filter-section mb10"> |
| | | <el-form :inline="true" :model="filterParams"> |
| | | <div v-if="showFilter" |
| | | class="filter-section mb10"> |
| | | <el-form :inline="true" |
| | | :model="filterParams"> |
| | | <el-form-item label="任务状态"> |
| | | <el-select v-model="filterParams.status" placeholder="全部" clearable style="width: 120px"> |
| | | <el-option label="未开始" value="notStarted" /> |
| | | <el-option label="进行中" value="inProgress" /> |
| | | <el-option label="已完成" value="completed" /> |
| | | <el-option label="已逾期" value="overdue" /> |
| | | <el-select v-model="filterParams.status" |
| | | placeholder="全部" |
| | | clearable |
| | | style="width: 120px"> |
| | | <el-option label="未开始" |
| | | value="notStarted" /> |
| | | <el-option label="进行中" |
| | | value="inProgress" /> |
| | | <el-option label="已完成" |
| | | value="completed" /> |
| | | <el-option label="已逾期" |
| | | value="overdue" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="负责人"> |
| | | <el-input v-model="filterParams.assignee" placeholder="输入负责人" clearable style="width: 150px" /> |
| | | <el-input v-model="filterParams.assignee" |
| | | placeholder="输入负责人" |
| | | clearable |
| | | style="width: 150px" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" size="small" @click="filterTree">筛选</el-button> |
| | | <el-button size="small" @click="resetFilter">重置</el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="filterTree">筛选</el-button> |
| | | <el-button size="small" |
| | | @click="resetFilter">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- 任务树 --> |
| | | <div class="tree-content"> |
| | | <el-tree |
| | | v-loading="loading" |
| | | <el-tree v-loading="loading" |
| | | :data="taskTreeData" |
| | | :props="defaultProps" |
| | | :expand-on-click-node="false" |
| | | node-key="nodeId" |
| | | ref="treeRef" |
| | | @node-contextmenu="handleContextMenu" |
| | | @node-click="handleNodeClick" |
| | | > |
| | | @node-click="handleNodeClick"> |
| | | <template #default="{ node, data }"> |
| | | <!-- 节点内容 --> |
| | | <div class="tree-node-content" :class="{ 'phase-node': data.type === 'phase', 'task-node': data.type === 'task' }"> |
| | | <div class="tree-node-content" |
| | | :class="{ 'phase-node': data.type === 'phase', 'task-node': data.type === 'task' }"> |
| | | <!-- 节点图标 --> |
| | | <div class="node-icon"> |
| | | <i v-if="data.type === 'phase'" class="el-icon-folder text-primary" /> |
| | | <i v-else-if="data.status === 'completed'" class="el-icon-circle-check text-success" /> |
| | | <i v-else-if="data.status === 'inProgress'" class="el-icon-circle-check text-primary" /> |
| | | <i v-else-if="data.status === 'overdue'" class="el-icon-alarm-clock text-danger" /> |
| | | <i v-else class="el-icon-circle-close text-gray-400" /> |
| | | <i v-if="data.type === 'phase'" |
| | | class="el-icon-folder text-primary" /> |
| | | <i v-else-if="data.status === 'completed'" |
| | | class="el-icon-circle-check text-success" /> |
| | | <i v-else-if="data.status === 'inProgress'" |
| | | class="el-icon-circle-check text-primary" /> |
| | | <i v-else-if="data.status === 'overdue'" |
| | | class="el-icon-alarm-clock text-danger" /> |
| | | <i v-else |
| | | class="el-icon-circle-close text-gray-400" /> |
| | | </div> |
| | | |
| | | <!-- 节点标题和描述 --> |
| | | <div class="node-info"> |
| | | <div class="node-title" :class="{ 'overdue-title': data.type === 'task' && data.status === 'overdue' }"> |
| | | <div class="node-title" |
| | | :class="{ 'overdue-title': data.type === 'task' && data.status === 'overdue' }"> |
| | | {{ node.label }} |
| | | <span v-if="data.type === 'task' && data.priority === 'high'" class="priority-tag">高优</span> |
| | | <span v-else-if="data.type === 'task' && data.priority === 'medium'" class="priority-tag medium">中优</span> |
| | | <span v-if="data.type === 'task' && data.priority === 'high'" |
| | | class="priority-tag">高优</span> |
| | | <span v-else-if="data.type === 'task' && data.priority === 'medium'" |
| | | class="priority-tag medium">中优</span> |
| | | </div> |
| | | <div v-if="data.description" class="node-description">{{ data.description }}</div> |
| | | |
| | | <div v-if="data.description" |
| | | class="node-description">{{ data.description }}</div> |
| | | <!-- 任务元信息 --> |
| | | <div v-if="data.type === 'task'" class="task-meta"> |
| | | <div v-if="data.type === 'task'" |
| | | class="task-meta"> |
| | | <span class="meta-item"> |
| | | <i class="el-icon-user"></i> |
| | | {{ data.assigneeName || '未分配' }} |
| | |
| | | </span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 任务进度条 --> |
| | | <div v-if="data.type === 'task'" class="task-progress"> |
| | | <el-progress :percentage="data.progress || 0" :stroke-width="4" :show-text="false" /> |
| | | <div v-if="data.type === 'task'" |
| | | class="task-progress"> |
| | | <el-progress :percentage="data.progress || 0" |
| | | :stroke-width="4" |
| | | :show-text="false" /> |
| | | </div> |
| | | |
| | | <!-- 操作按钮 --> |
| | | <div class="node-actions"> |
| | | <el-button |
| | | v-if="data.type === 'task'" |
| | | <el-button v-if="data.type === 'task'" |
| | | type="text" |
| | | size="small" |
| | | icon="Edit" |
| | | @click.stop="handleEditTask(data)" |
| | | v-hasPermi="['oaSystem:task:edit']" |
| | | /> |
| | | <el-button |
| | | v-if="data.type === 'phase'" |
| | | v-hasPermi="['oaSystem:task:edit']" /> |
| | | <el-button v-if="data.type === 'phase'" |
| | | type="text" |
| | | size="small" |
| | | icon="Plus" |
| | | @click.stop="handleAddTaskUnderPhase(data)" |
| | | v-hasPermi="['oaSystem:task:add']" |
| | | /> |
| | | <el-button |
| | | type="text" |
| | | v-hasPermi="['oaSystem:task:add']" /> |
| | | <el-button type="text" |
| | | size="small" |
| | | icon="Delete" |
| | | @click.stop="handleDeleteNode(data)" |
| | | v-hasPermi="['oaSystem:task:remove']" |
| | | /> |
| | | v-hasPermi="['oaSystem:task:remove']" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-tree> |
| | | </div> |
| | | |
| | | <!-- 右键菜单 --> |
| | | <div v-if="showContextMenu" :style="contextMenuStyle" class="context-menu"> |
| | | <div v-if="showContextMenu" |
| | | :style="contextMenuStyle" |
| | | class="context-menu"> |
| | | <el-menu @select="handleContextMenuSelect"> |
| | | <el-menu-item v-if="selectedNode.type === 'task'" index="edit">编辑任务</el-menu-item> |
| | | <el-menu-item v-if="selectedNode.type === 'phase'" index="addTask">添加子任务</el-menu-item> |
| | | <el-menu-item v-if="selectedNode.type === 'task'" |
| | | index="edit">编辑任务</el-menu-item> |
| | | <el-menu-item v-if="selectedNode.type === 'phase'" |
| | | index="addTask">添加子任务</el-menu-item> |
| | | <el-menu-item index="delete">删除</el-menu-item> |
| | | <el-menu-item index="expandAll">展开全部</el-menu-item> |
| | | <el-menu-item index="collapseAll">收起全部</el-menu-item> |
| | | </el-menu> |
| | | </div> |
| | | |
| | | <!-- 任务表单对话框 --> |
| | | <el-dialog :title="dialogTitle" v-model="dialogOpen" width="600px" append-to-body> |
| | | <el-form ref="taskFormRef" :model="taskForm" :rules="taskRules" label-width="80px"> |
| | | <el-form-item label="任务名称" prop="taskName"> |
| | | <el-input v-model="taskForm.taskName" placeholder="请输入任务名称" /> |
| | | <el-dialog :title="dialogTitle" |
| | | v-model="dialogOpen" |
| | | width="600px" |
| | | append-to-body> |
| | | <el-form ref="taskFormRef" |
| | | :model="taskForm" |
| | | :rules="taskRules" |
| | | label-width="80px"> |
| | | <el-form-item label="任务名称" |
| | | prop="taskName"> |
| | | <el-input v-model="taskForm.taskName" |
| | | placeholder="请输入任务名称" /> |
| | | </el-form-item> |
| | | <el-form-item label="负责人" prop="assigneeId"> |
| | | <el-input v-model="taskForm.assigneeId" placeholder="请输入负责人ID" /> |
| | | <el-form-item label="负责人" |
| | | prop="assigneeId"> |
| | | <el-input v-model="taskForm.assigneeId" |
| | | placeholder="请输入负责人ID" /> |
| | | </el-form-item> |
| | | <el-form-item label="开始日期" prop="startDate"> |
| | | <el-date-picker |
| | | v-model="taskForm.startDate" |
| | | <el-form-item label="开始日期" |
| | | prop="startDate"> |
| | | <el-date-picker v-model="taskForm.startDate" |
| | | type="date" |
| | | placeholder="选择开始日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="结束日期" prop="endDate"> |
| | | <el-date-picker |
| | | v-model="taskForm.endDate" |
| | | <el-form-item label="结束日期" |
| | | prop="endDate"> |
| | | <el-date-picker v-model="taskForm.endDate" |
| | | type="date" |
| | | placeholder="选择结束日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="优先级" prop="priority"> |
| | | <el-select v-model="taskForm.priority" placeholder="选择优先级"> |
| | | <el-option label="低" value="low" /> |
| | | <el-option label="中" value="medium" /> |
| | | <el-option label="高" value="high" /> |
| | | <el-form-item label="优先级" |
| | | prop="priority"> |
| | | <el-select v-model="taskForm.priority" |
| | | placeholder="选择优先级"> |
| | | <el-option label="低" |
| | | value="low" /> |
| | | <el-option label="中" |
| | | value="medium" /> |
| | | <el-option label="高" |
| | | value="high" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="进度" prop="progress"> |
| | | <el-input-number v-model="taskForm.progress" :min="0" :max="100" style="width: 100%" /> |
| | | <el-form-item label="进度" |
| | | prop="progress"> |
| | | <el-input-number v-model="taskForm.progress" |
| | | :min="0" |
| | | :max="100" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="描述" prop="description"> |
| | | <el-input v-model="taskForm.description" type="textarea" placeholder="请输入任务描述" /> |
| | | <el-form-item label="描述" |
| | | prop="description"> |
| | | <el-input v-model="taskForm.description" |
| | | type="textarea" |
| | | placeholder="请输入任务描述" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitTaskForm">确定</el-button> |
| | | <el-button @click="dialogOpen = false">取消</el-button> |
| | | <el-button type="primary" @click="submitTaskForm">确定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, watch, onMounted } from 'vue'; |
| | | import { ElMessage, ElMessageBox, ElMenu, ElMenuItem } from 'element-plus'; |
| | | import { ref, reactive, computed, watch, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox, ElMenu, ElMenuItem } from "element-plus"; |
| | | // import { getProject, addTask, updateTask, deleteTask, deletePhase } from '@/api/oaSystem/projectManagement'; |
| | | |
| | | const props = defineProps({ |
| | | projectId: { |
| | | type: String, |
| | | required: true |
| | | } |
| | | required: true, |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(['refresh']); |
| | | const emit = defineEmits(["refresh"]); |
| | | |
| | | // 组件状态 |
| | | const loading = ref(false); |
| | |
| | | const selectedNode = ref({}); |
| | | const showFilter = ref(false); |
| | | const dialogOpen = ref(false); |
| | | const dialogTitle = ref(''); |
| | | const dialogTitle = ref(""); |
| | | const taskFormRef = ref(); |
| | | |
| | | // 筛选参数 |
| | | const filterParams = reactive({ |
| | | status: '', |
| | | assignee: '' |
| | | status: "", |
| | | assignee: "", |
| | | }); |
| | | |
| | | // 任务表单数据 |
| | | const taskForm = reactive({ |
| | | taskId: undefined, |
| | | taskName: '', |
| | | description: '', |
| | | startDate: '', |
| | | endDate: '', |
| | | assigneeId: '', |
| | | assigneeName: '', |
| | | status: 'notStarted', |
| | | taskName: "", |
| | | description: "", |
| | | startDate: "", |
| | | endDate: "", |
| | | assigneeId: "", |
| | | assigneeName: "", |
| | | status: "notStarted", |
| | | progress: 0, |
| | | priority: 'medium', |
| | | phaseId: '', |
| | | projectId: props.projectId |
| | | priority: "medium", |
| | | phaseId: "", |
| | | projectId: props.projectId, |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const taskRules = { |
| | | taskName: [ |
| | | { required: true, message: '任务名称不能为空', trigger: 'blur' }, |
| | | { min: 2, max: 50, message: '任务名称长度在 2 到 50 个字符', trigger: 'blur' } |
| | | { required: true, message: "任务名称不能为空", trigger: "blur" }, |
| | | { |
| | | min: 2, |
| | | max: 50, |
| | | message: "任务名称长度在 2 到 50 个字符", |
| | | trigger: "blur", |
| | | }, |
| | | ], |
| | | startDate: [ |
| | | { required: true, message: '开始日期不能为空', trigger: 'change' } |
| | | { required: true, message: "开始日期不能为空", trigger: "change" }, |
| | | ], |
| | | endDate: [ |
| | | { required: true, message: '结束日期不能为空', trigger: 'change' } |
| | | ], |
| | | assigneeId: [ |
| | | { required: true, message: '负责人不能为空', trigger: 'blur' } |
| | | ], |
| | | endDate: [{ required: true, message: "结束日期不能为空", trigger: "change" }], |
| | | assigneeId: [{ required: true, message: "负责人不能为空", trigger: "blur" }], |
| | | progress: [ |
| | | { required: true, message: '进度不能为空', trigger: 'blur' }, |
| | | { type: 'number', min: 0, max: 100, message: '进度必须在 0 到 100 之间', trigger: 'blur' } |
| | | ] |
| | | { required: true, message: "进度不能为空", trigger: "blur" }, |
| | | { |
| | | type: "number", |
| | | min: 0, |
| | | max: 100, |
| | | message: "进度必须在 0 到 100 之间", |
| | | trigger: "blur", |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | // 任务树数据 |
| | |
| | | |
| | | // 模拟任务数据 |
| | | const mockTaskData = { |
| | | 'PRJ2023001': [ |
| | | PRJ2023001: [ |
| | | { |
| | | phaseId: 'PHASE001', |
| | | phaseName: '需求分析', |
| | | startDate: '2023-11-01', |
| | | endDate: '2023-11-15', |
| | | status: 'completed', |
| | | phaseId: "PHASE001", |
| | | phaseName: "需求分析", |
| | | startDate: "2023-11-01", |
| | | endDate: "2023-11-15", |
| | | status: "completed", |
| | | tasks: [ |
| | | { |
| | | taskId: 'TASK001', |
| | | taskName: '需求调研', |
| | | description: '调研用户需求和业务流程', |
| | | startDate: '2023-11-01', |
| | | endDate: '2023-11-05', |
| | | assigneeId: 'USER001', |
| | | assigneeName: '张三', |
| | | status: 'completed', |
| | | taskId: "TASK001", |
| | | taskName: "需求调研", |
| | | description: "调研用户需求和业务流程", |
| | | startDate: "2023-11-01", |
| | | endDate: "2023-11-05", |
| | | assigneeId: "USER001", |
| | | assigneeName: "张三", |
| | | status: "completed", |
| | | progress: 100, |
| | | priority: 'medium' |
| | | priority: "medium", |
| | | }, |
| | | { |
| | | taskId: 'TASK002', |
| | | taskName: '需求文档编写', |
| | | description: '编写详细的需求规格说明书', |
| | | startDate: '2023-11-06', |
| | | endDate: '2023-11-15', |
| | | assigneeId: 'USER002', |
| | | assigneeName: '李四', |
| | | status: 'completed', |
| | | taskId: "TASK002", |
| | | taskName: "需求文档编写", |
| | | description: "编写详细的需求规格说明书", |
| | | startDate: "2023-11-06", |
| | | endDate: "2023-11-15", |
| | | assigneeId: "USER002", |
| | | assigneeName: "李四", |
| | | status: "completed", |
| | | progress: 100, |
| | | priority: 'high' |
| | | } |
| | | ] |
| | | priority: "high", |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | phaseId: 'PHASE002', |
| | | phaseName: '系统设计', |
| | | startDate: '2023-11-16', |
| | | endDate: '2023-12-10', |
| | | status: 'completed', |
| | | phaseId: "PHASE002", |
| | | phaseName: "系统设计", |
| | | startDate: "2023-11-16", |
| | | endDate: "2023-12-10", |
| | | status: "completed", |
| | | tasks: [ |
| | | { |
| | | taskId: 'TASK003', |
| | | taskName: '系统架构设计', |
| | | description: '设计系统整体架构', |
| | | startDate: '2023-11-16', |
| | | endDate: '2023-11-25', |
| | | assigneeId: 'USER003', |
| | | assigneeName: '王五', |
| | | status: 'completed', |
| | | taskId: "TASK003", |
| | | taskName: "系统架构设计", |
| | | description: "设计系统整体架构", |
| | | startDate: "2023-11-16", |
| | | endDate: "2023-11-25", |
| | | assigneeId: "USER003", |
| | | assigneeName: "王五", |
| | | status: "completed", |
| | | progress: 100, |
| | | priority: 'high' |
| | | priority: "high", |
| | | }, |
| | | { |
| | | taskId: 'TASK004', |
| | | taskName: '数据库设计', |
| | | description: '设计数据库表结构和关系', |
| | | startDate: '2023-11-26', |
| | | endDate: '2023-12-10', |
| | | assigneeId: 'USER004', |
| | | assigneeName: '赵六', |
| | | status: 'completed', |
| | | taskId: "TASK004", |
| | | taskName: "数据库设计", |
| | | description: "设计数据库表结构和关系", |
| | | startDate: "2023-11-26", |
| | | endDate: "2023-12-10", |
| | | assigneeId: "USER004", |
| | | assigneeName: "赵六", |
| | | status: "completed", |
| | | progress: 100, |
| | | priority: 'medium' |
| | | } |
| | | ] |
| | | priority: "medium", |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | phaseId: 'PHASE003', |
| | | phaseName: '开发实现', |
| | | startDate: '2023-12-11', |
| | | endDate: '2024-01-31', |
| | | status: 'inProgress', |
| | | phaseId: "PHASE003", |
| | | phaseName: "开发实现", |
| | | startDate: "2023-12-11", |
| | | endDate: "2024-01-31", |
| | | status: "inProgress", |
| | | tasks: [ |
| | | { |
| | | taskId: 'TASK005', |
| | | taskName: '前端开发', |
| | | description: '开发用户界面和交互逻辑', |
| | | startDate: '2023-12-11', |
| | | endDate: '2024-01-15', |
| | | assigneeId: 'USER005', |
| | | assigneeName: '钱七', |
| | | status: 'inProgress', |
| | | taskId: "TASK005", |
| | | taskName: "前端开发", |
| | | description: "开发用户界面和交互逻辑", |
| | | startDate: "2023-12-11", |
| | | endDate: "2024-01-15", |
| | | assigneeId: "USER005", |
| | | assigneeName: "钱七", |
| | | status: "inProgress", |
| | | progress: 70, |
| | | priority: 'high' |
| | | priority: "high", |
| | | }, |
| | | { |
| | | taskId: 'TASK006', |
| | | taskName: '后端开发', |
| | | description: '开发业务逻辑和API接口', |
| | | startDate: '2023-12-11', |
| | | endDate: '2024-01-20', |
| | | assigneeId: 'USER006', |
| | | assigneeName: '孙八', |
| | | status: 'inProgress', |
| | | taskId: "TASK006", |
| | | taskName: "后端开发", |
| | | description: "开发业务逻辑和API接口", |
| | | startDate: "2023-12-11", |
| | | endDate: "2024-01-20", |
| | | assigneeId: "USER006", |
| | | assigneeName: "孙八", |
| | | status: "inProgress", |
| | | progress: 60, |
| | | priority: 'high' |
| | | } |
| | | ] |
| | | } |
| | | priority: "high", |
| | | }, |
| | | ], |
| | | }, |
| | | ], |
| | | // 默认数据 |
| | | default: [ |
| | | { |
| | | phaseId: 'PHASE_DEFAULT1', |
| | | phaseName: '准备阶段', |
| | | startDate: '2023-01-01', |
| | | endDate: '2023-03-31', |
| | | status: 'completed', |
| | | phaseId: "PHASE_DEFAULT1", |
| | | phaseName: "准备阶段", |
| | | startDate: "2023-01-01", |
| | | endDate: "2023-03-31", |
| | | status: "completed", |
| | | tasks: [ |
| | | { |
| | | taskId: 'TASK_DEFAULT1', |
| | | taskName: '项目启动', |
| | | description: '召开项目启动会议', |
| | | startDate: '2023-01-01', |
| | | endDate: '2023-01-05', |
| | | assigneeId: 'USER_DEFAULT1', |
| | | assigneeName: '负责人A', |
| | | status: 'completed', |
| | | taskId: "TASK_DEFAULT1", |
| | | taskName: "项目启动", |
| | | description: "召开项目启动会议", |
| | | startDate: "2023-01-01", |
| | | endDate: "2023-01-05", |
| | | assigneeId: "USER_DEFAULT1", |
| | | assigneeName: "负责人A", |
| | | status: "completed", |
| | | progress: 100, |
| | | priority: 'high' |
| | | } |
| | | ] |
| | | priority: "high", |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | phaseId: 'PHASE_DEFAULT2', |
| | | phaseName: '执行阶段', |
| | | startDate: '2023-04-01', |
| | | endDate: '2023-09-30', |
| | | status: 'inProgress', |
| | | phaseId: "PHASE_DEFAULT2", |
| | | phaseName: "执行阶段", |
| | | startDate: "2023-04-01", |
| | | endDate: "2023-09-30", |
| | | status: "inProgress", |
| | | tasks: [ |
| | | { |
| | | taskId: 'TASK_DEFAULT2', |
| | | taskName: '核心功能开发', |
| | | description: '开发系统核心功能模块', |
| | | startDate: '2023-04-01', |
| | | endDate: '2023-06-30', |
| | | assigneeId: 'USER_DEFAULT2', |
| | | assigneeName: '负责人B', |
| | | status: 'inProgress', |
| | | taskId: "TASK_DEFAULT2", |
| | | taskName: "核心功能开发", |
| | | description: "开发系统核心功能模块", |
| | | startDate: "2023-04-01", |
| | | endDate: "2023-06-30", |
| | | assigneeId: "USER_DEFAULT2", |
| | | assigneeName: "负责人B", |
| | | status: "inProgress", |
| | | progress: 50, |
| | | priority: 'high' |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | priority: "high", |
| | | }, |
| | | ], |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | const taskTreeData = computed(() => { |
| | |
| | | const filteredData = JSON.parse(JSON.stringify(rawTaskTreeData.value)); |
| | | |
| | | // 递归筛选节点 |
| | | const filterNodes = (nodes) => { |
| | | const filterNodes = nodes => { |
| | | const result = []; |
| | | |
| | | nodes.forEach(node => { |
| | | // 对于阶段节点,检查其子任务是否符合筛选条件 |
| | | if (node.type === 'phase' && node.children) { |
| | | if (node.type === "phase" && node.children) { |
| | | const filteredChildren = filterNodes(node.children); |
| | | if (filteredChildren.length > 0) { |
| | | // 保留至少有一个子任务符合条件的阶段 |
| | |
| | | } |
| | | } |
| | | // 对于任务节点,直接应用筛选条件 |
| | | else if (node.type === 'task') { |
| | | const statusMatch = !filterParams.status || node.status === filterParams.status; |
| | | const assigneeMatch = !filterParams.assignee || |
| | | (node.assigneeName && node.assigneeName.includes(filterParams.assignee)); |
| | | else if (node.type === "task") { |
| | | const statusMatch = |
| | | !filterParams.status || node.status === filterParams.status; |
| | | const assigneeMatch = |
| | | !filterParams.assignee || |
| | | (node.assigneeName && |
| | | node.assigneeName.includes(filterParams.assignee)); |
| | | |
| | | if (statusMatch && assigneeMatch) { |
| | | result.push(node); |
| | |
| | | |
| | | // 树节点配置 |
| | | const defaultProps = { |
| | | children: 'children', |
| | | label: (data) => { |
| | | if (data.type === 'phase') { |
| | | children: "children", |
| | | label: data => { |
| | | if (data.type === "phase") { |
| | | return `${data.phaseName}`; |
| | | } else { |
| | | return `${data.taskName}`; |
| | | } |
| | | } |
| | | }, |
| | | }; |
| | | |
| | | // 加载任务树数据 |
| | |
| | | const phases = mockTaskData[props.projectId] || mockTaskData.default; |
| | | rawTaskTreeData.value = buildTaskTree(phases); |
| | | } catch (error) { |
| | | ElMessage.error('加载任务树失败'); |
| | | console.error('加载任务树失败:', error); |
| | | ElMessage.error("加载任务树失败"); |
| | | console.error("加载任务树失败:", error); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 构建任务树 |
| | | const buildTaskTree = (phases) => { |
| | | const buildTaskTree = phases => { |
| | | return phases.map(phase => ({ |
| | | nodeId: phase.phaseId, |
| | | phaseId: phase.phaseId, |
| | | phaseName: phase.phaseName, |
| | | type: 'phase', |
| | | type: "phase", |
| | | children: (phase.tasks || []).map(task => ({ |
| | | nodeId: task.taskId, |
| | | taskId: task.taskId, |
| | |
| | | priority: task.priority, |
| | | phaseId: task.phaseId, |
| | | projectId: props.projectId, |
| | | type: 'task' |
| | | })) |
| | | type: "task", |
| | | })), |
| | | })); |
| | | }; |
| | | |
| | | // 格式化日期范围 |
| | | const formatDateRange = (startDate, endDate) => { |
| | | if (!startDate || !endDate) return ''; |
| | | if (!startDate || !endDate) return ""; |
| | | return `${startDate} - ${endDate}`; |
| | | }; |
| | | |
| | |
| | | const refreshTree = () => { |
| | | loadTaskTree(); |
| | | // 通知父组件刷新数据 |
| | | emit('refresh'); |
| | | emit("refresh"); |
| | | }; |
| | | |
| | | // 切换筛选面板 |
| | |
| | | |
| | | // 重置筛选 |
| | | const resetFilter = () => { |
| | | filterParams.status = ''; |
| | | filterParams.assignee = ''; |
| | | filterParams.status = ""; |
| | | filterParams.assignee = ""; |
| | | }; |
| | | |
| | | // 处理节点点击 |
| | | const handleNodeClick = (data, node) => { |
| | | // 切换展开/收起状态 |
| | | if (data.type === 'phase') { |
| | | if (data.type === "phase") { |
| | | node.expanded = !node.expanded; |
| | | } |
| | | }; |
| | |
| | | event.preventDefault(); |
| | | selectedNode.value = data; |
| | | contextMenuStyle.value = { |
| | | position: 'fixed', |
| | | position: "fixed", |
| | | left: `${event.clientX}px`, |
| | | top: `${event.clientY}px`, |
| | | zIndex: 1000 |
| | | zIndex: 1000, |
| | | }; |
| | | showContextMenu.value = true; |
| | | }; |
| | | |
| | | // 处理右键菜单选择 |
| | | const handleContextMenuSelect = (index) => { |
| | | const handleContextMenuSelect = index => { |
| | | showContextMenu.value = false; |
| | | switch (index) { |
| | | case 'edit': |
| | | if (selectedNode.value.type === 'task') { |
| | | case "edit": |
| | | if (selectedNode.value.type === "task") { |
| | | handleEditTask(selectedNode.value); |
| | | } |
| | | break; |
| | | case 'addTask': |
| | | if (selectedNode.value.type === 'phase') { |
| | | case "addTask": |
| | | if (selectedNode.value.type === "phase") { |
| | | handleAddTaskUnderPhase(selectedNode.value); |
| | | } |
| | | break; |
| | | case 'delete': |
| | | case "delete": |
| | | handleDeleteNode(selectedNode.value); |
| | | break; |
| | | case 'expandAll': |
| | | case "expandAll": |
| | | treeRef.value?.expandAll(); |
| | | break; |
| | | case 'collapseAll': |
| | | case "collapseAll": |
| | | treeRef.value?.collapseAll(); |
| | | break; |
| | | } |
| | |
| | | // 添加任务 |
| | | const handleAddTask = () => { |
| | | resetTaskForm(); |
| | | dialogTitle.value = '添加任务'; |
| | | dialogTitle.value = "添加任务"; |
| | | dialogOpen.value = true; |
| | | }; |
| | | |
| | | // 在指定阶段下添加任务 |
| | | const handleAddTaskUnderPhase = (phase) => { |
| | | const handleAddTaskUnderPhase = phase => { |
| | | resetTaskForm(); |
| | | taskForm.phaseId = phase.phaseId; |
| | | dialogTitle.value = '添加子任务'; |
| | | dialogTitle.value = "添加子任务"; |
| | | dialogOpen.value = true; |
| | | }; |
| | | |
| | | // 编辑任务 |
| | | const handleEditTask = (task) => { |
| | | const handleEditTask = task => { |
| | | resetTaskForm(); |
| | | Object.assign(taskForm, { ...task }); |
| | | dialogTitle.value = '编辑任务'; |
| | | dialogTitle.value = "编辑任务"; |
| | | dialogOpen.value = true; |
| | | }; |
| | | |
| | | // 删除节点 |
| | | const handleDeleteNode = async (node) => { |
| | | const confirmMessage = node.type === 'phase' |
| | | const handleDeleteNode = async node => { |
| | | const confirmMessage = |
| | | node.type === "phase" |
| | | ? `确定要删除阶段 "${node.phaseName}" 及其所有子任务吗?` |
| | | : `确定要删除任务 "${node.taskName}" 吗?`; |
| | | |
| | | await ElMessageBox.confirm(confirmMessage, '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | await ElMessageBox.confirm(confirmMessage, "确认操作", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).catch(() => { |
| | | throw new Error('取消删除'); |
| | | throw new Error("取消删除"); |
| | | }); |
| | | |
| | | try { |
| | | if (node.type === 'phase') { |
| | | if (node.type === "phase") { |
| | | await deletePhase(node.phaseId); |
| | | } else { |
| | | await deleteTask(node.taskId); |
| | | } |
| | | ElMessage.success('删除成功'); |
| | | ElMessage.success("删除成功"); |
| | | refreshTree(); |
| | | } catch (error) { |
| | | if (error.message !== '取消删除') { |
| | | ElMessage.error('删除失败'); |
| | | console.error('删除失败:', error); |
| | | if (error.message !== "取消删除") { |
| | | ElMessage.error("删除失败"); |
| | | console.error("删除失败:", error); |
| | | } |
| | | } |
| | | }; |
| | |
| | | // 重置任务表单 |
| | | const resetTaskForm = () => { |
| | | taskForm.taskId = undefined; |
| | | taskForm.taskName = ''; |
| | | taskForm.description = ''; |
| | | taskForm.startDate = ''; |
| | | taskForm.endDate = ''; |
| | | taskForm.assigneeId = ''; |
| | | taskForm.assigneeName = ''; |
| | | taskForm.status = 'notStarted'; |
| | | taskForm.taskName = ""; |
| | | taskForm.description = ""; |
| | | taskForm.startDate = ""; |
| | | taskForm.endDate = ""; |
| | | taskForm.assigneeId = ""; |
| | | taskForm.assigneeName = ""; |
| | | taskForm.status = "notStarted"; |
| | | taskForm.progress = 0; |
| | | taskForm.priority = 'medium'; |
| | | taskForm.phaseId = ''; |
| | | taskForm.priority = "medium"; |
| | | taskForm.phaseId = ""; |
| | | taskForm.projectId = props.projectId; |
| | | |
| | | if (taskFormRef.value) { |
| | |
| | | |
| | | if (taskForm.taskId) { |
| | | await updateTask(taskForm); |
| | | ElMessage.success('修改任务成功'); |
| | | ElMessage.success("修改任务成功"); |
| | | } else { |
| | | await addTask(taskForm); |
| | | ElMessage.success('添加任务成功'); |
| | | ElMessage.success("添加任务成功"); |
| | | } |
| | | |
| | | dialogOpen.value = false; |
| | | refreshTree(); |
| | | } catch (error) { |
| | | console.error('提交表单失败:', error); |
| | | console.error("提交表单失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 点击其他区域关闭右键菜单 |
| | | document.addEventListener('click', () => { |
| | | document.addEventListener("click", () => { |
| | | if (showContextMenu.value) { |
| | | showContextMenu.value = false; |
| | | } |
| | | }); |
| | | |
| | | // 监听项目ID变化 |
| | | watch(() => props.projectId, (newProjectId) => { |
| | | watch( |
| | | () => props.projectId, |
| | | newProjectId => { |
| | | if (newProjectId) { |
| | | loadTaskTree(); |
| | | } |
| | | }); |
| | | } |
| | | ); |
| | | |
| | | // 初始化 |
| | | onMounted(() => { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 顶部搜索和操作栏 --> |
| | | <el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px"> |
| | | <el-form-item label="项目名称" prop="projectName"> |
| | | <el-input |
| | | v-model="queryParams.projectName" |
| | | <el-form :model="queryParams" |
| | | ref="queryRef" |
| | | :inline="true" |
| | | label-width="80px"> |
| | | <el-form-item label="项目名称" |
| | | prop="projectName"> |
| | | <el-input v-model="queryParams.projectName" |
| | | placeholder="请输入项目名称" |
| | | clearable |
| | | style="width: 240px" |
| | | @keyup.enter="handleQuery" |
| | | /> |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="负责人" prop="managerName"> |
| | | <el-input |
| | | v-model="queryParams.managerName" |
| | | <el-form-item label="负责人" |
| | | prop="managerName"> |
| | | <el-input v-model="queryParams.managerName" |
| | | placeholder="请输入负责人姓名" |
| | | clearable |
| | | style="width: 240px" |
| | | @keyup.enter="handleQuery" |
| | | /> |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select |
| | | v-model="queryParams.status" |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="queryParams.status" |
| | | placeholder="项目状态" |
| | | clearable |
| | | style="width: 150px" |
| | | > |
| | | <el-option label="规划中" value="planning" /> |
| | | <el-option label="进行中" value="inProgress" /> |
| | | <el-option label="已完成" value="completed" /> |
| | | <el-option label="已暂停" value="paused" /> |
| | | style="width: 150px"> |
| | | <el-option label="规划中" |
| | | value="planning" /> |
| | | <el-option label="进行中" |
| | | value="inProgress" /> |
| | | <el-option label="已完成" |
| | | value="completed" /> |
| | | <el-option label="已暂停" |
| | | value="paused" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> |
| | | <el-button icon="Refresh" @click="resetQuery">重置</el-button> |
| | | <el-button type="primary" |
| | | icon="Search" |
| | | @click="handleQuery">搜索</el-button> |
| | | <el-button icon="Refresh" |
| | | @click="resetQuery">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 工具栏 --> |
| | | <el-row :gutter="10" class="mb8"> |
| | | <el-row :gutter="10" |
| | | class="mb8"> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="primary" |
| | | <el-button type="primary" |
| | | plain |
| | | icon="Plus" |
| | | @click="handleAdd" |
| | | v-hasPermi="['oaSystem:project:add']" |
| | | >新增项目</el-button> |
| | | v-hasPermi="['oaSystem:project:add']">新增项目</el-button> |
| | | </el-col> |
| | | <!-- <el-col :span="1.5"> |
| | | <el-button |
| | |
| | | >删除项目</el-button> |
| | | </el-col> --> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="warning" |
| | | <el-button type="warning" |
| | | plain |
| | | icon="Download" |
| | | @click="handleExport" |
| | | v-hasPermi="['oaSystem:project:export']" |
| | | >导出项目</el-button> |
| | | v-hasPermi="['oaSystem:project:export']">导出项目</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 项目列表表格 --> |
| | | <el-table |
| | | v-loading="loading" |
| | | <el-table v-loading="loading" |
| | | :data="projectList" |
| | | @selection-change="handleSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="50" align="center" /> |
| | | <el-table-column |
| | | label="项目编号" |
| | | @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="50" |
| | | align="center" /> |
| | | <el-table-column label="项目编号" |
| | | align="center" |
| | | prop="projectId" |
| | | width="100" |
| | | /> |
| | | <el-table-column |
| | | label="项目名称" |
| | | width="100" /> |
| | | <el-table-column label="项目名称" |
| | | align="center" |
| | | prop="projectName" |
| | | :show-overflow-tooltip="true" |
| | | /> |
| | | <el-table-column |
| | | label="负责人" |
| | | :show-overflow-tooltip="true" /> |
| | | <el-table-column label="负责人" |
| | | align="center" |
| | | prop="managerName" |
| | | /> |
| | | <el-table-column |
| | | label="开始日期" |
| | | prop="managerName" /> |
| | | <el-table-column label="开始日期" |
| | | align="center" |
| | | prop="startDate" |
| | | width="120" |
| | | /> |
| | | <el-table-column |
| | | label="结束日期" |
| | | width="120" /> |
| | | <el-table-column label="结束日期" |
| | | align="center" |
| | | prop="endDate" |
| | | width="120" |
| | | /> |
| | | <el-table-column |
| | | label="状态" |
| | | width="120" /> |
| | | <el-table-column label="状态" |
| | | align="center" |
| | | prop="status" |
| | | width="90" |
| | | > |
| | | width="90"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)">{{ getStatusText(scope.row.status) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="完成度" |
| | | <el-table-column label="完成度" |
| | | align="center" |
| | | prop="completionRate" |
| | | width="100" |
| | | > |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-progress :percentage="scope.row.completionRate" :stroke-width="6" /> |
| | | <el-progress :percentage="scope.row.completionRate" |
| | | :stroke-width="6" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="操作" |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="180" |
| | | class-name="small-padding fixed-width" |
| | | > |
| | | class-name="small-padding fixed-width"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | <el-button link |
| | | type="primary" |
| | | icon="Search" |
| | | @click="handleView(scope.row)" |
| | | v-hasPermi="['oaSystem:project:query']" |
| | | >详情</el-button> |
| | | <el-button |
| | | link |
| | | v-hasPermi="['oaSystem:project:query']">详情</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | icon="Edit" |
| | | @click="handleUpdate(scope.row)" |
| | | v-hasPermi="['oaSystem:project:edit']" |
| | | >编辑</el-button> |
| | | <el-button |
| | | link |
| | | v-hasPermi="['oaSystem:project:edit']">编辑</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | icon="Delete" |
| | | @click="handleDelete(scope.row)" |
| | | v-hasPermi="['oaSystem:project:remove']" |
| | | >删除</el-button> |
| | | v-hasPermi="['oaSystem:project:remove']">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页组件 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.pageNum" |
| | | v-model:limit="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> |
| | | |
| | | @pagination="getList" /> |
| | | <!-- 项目表单对话框 --> |
| | | <el-dialog :title="title" v-model="open" width="600px" append-to-body> |
| | | <project-form |
| | | ref="projectFormRef" |
| | | <el-dialog :title="title" |
| | | v-model="open" |
| | | width="600px" |
| | | append-to-body> |
| | | <project-form ref="projectFormRef" |
| | | :form="form" |
| | | :rules="rules" |
| | | :visible="open" |
| | | /> |
| | | :visible="open" /> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确定</el-button> |
| | | <el-button @click="cancel">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from 'vue'; |
| | | import { ElMessage, ElMessageBox } from 'element-plus'; |
| | | import Pagination from '@/components/Pagination'; |
| | | import ProjectForm from './components/projectForm.vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import Pagination from "@/components/Pagination"; |
| | | import ProjectForm from "./components/projectForm.vue"; |
| | | import { useRouter } from "vue-router"; |
| | | const { proxy } = getCurrentInstance(); |
| | | // 导入项目管理API接口 |
| | | import { listProject, addProject, updateProject, delProject, exportProject } from '@/api/oaSystem/projectManagement'; |
| | | import { |
| | | listProject, |
| | | addProject, |
| | | updateProject, |
| | | delProject, |
| | | exportProject, |
| | | } from "@/api/oaSystem/projectManagement"; |
| | | // import { listUser } from '@/api/system/user'; // 导入用户列表API接口 |
| | | |
| | | // 创建router实例 |
| | |
| | | const queryParams = reactive({ |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | projectName: '', |
| | | managerName: '', |
| | | status: '' |
| | | projectName: "", |
| | | managerName: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | projectId: undefined, |
| | | projectName: '', |
| | | description: '', |
| | | startDate: '', |
| | | endDate: '', |
| | | managerId: '', |
| | | managerName: '', |
| | | status: 'planning', |
| | | completionRate: 0 |
| | | projectName: "", |
| | | description: "", |
| | | startDate: "", |
| | | endDate: "", |
| | | managerId: "", |
| | | managerName: "", |
| | | status: "planning", |
| | | completionRate: 0, |
| | | }); |
| | | |
| | | // 表单校验规则 |
| | | const rules = { |
| | | projectName: [ |
| | | { required: true, message: '项目名称不能为空', trigger: 'blur' }, |
| | | { min: 2, max: 50, message: '项目名称长度在 2 到 50 个字符', trigger: 'blur' } |
| | | { required: true, message: "项目名称不能为空", trigger: "blur" }, |
| | | { |
| | | min: 2, |
| | | max: 50, |
| | | message: "项目名称长度在 2 到 50 个字符", |
| | | trigger: "blur", |
| | | }, |
| | | ], |
| | | startDate: [ |
| | | { required: true, message: '开始日期不能为空', trigger: 'change' } |
| | | { required: true, message: "开始日期不能为空", trigger: "change" }, |
| | | ], |
| | | endDate: [ |
| | | { required: true, message: '结束日期不能为空', trigger: 'change' } |
| | | ], |
| | | managerId: [ |
| | | { required: true, message: '负责人不能为空', trigger: 'blur' } |
| | | ] |
| | | endDate: [{ required: true, message: "结束日期不能为空", trigger: "change" }], |
| | | managerId: [{ required: true, message: "负责人不能为空", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 对话框状态 |
| | | const open = ref(false); |
| | | const title = ref(''); |
| | | const title = ref(""); |
| | | const projectFormRef = ref(); |
| | | const queryRef = ref(); |
| | | |
| | |
| | | projectList.value = data.records; |
| | | total.value = data.total; |
| | | } catch (error) { |
| | | ElMessage.error('获取项目列表失败'); |
| | | console.error('获取项目列表失败:', error); |
| | | ElMessage.error("获取项目列表失败"); |
| | | console.error("获取项目列表失败:", error); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | |
| | | }; |
| | | |
| | | // 选中行变化 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRowKeys.value = selection.map(item => item.projectId); |
| | | }; |
| | | |
| | |
| | | const handleAdd = () => { |
| | | resetForm(); |
| | | open.value = true; |
| | | title.value = '新增项目'; |
| | | title.value = "新增项目"; |
| | | }; |
| | | |
| | | // 编辑项目 |
| | | const handleUpdate = async (row) => { |
| | | const handleUpdate = async row => { |
| | | resetForm(); |
| | | const projectId = row.projectId || selectedRowKeys.value[0]; |
| | | try { |
| | | // const { data } = await getProject(projectId); |
| | | Object.assign(form, row); |
| | | open.value = true; |
| | | title.value = '编辑项目'; |
| | | title.value = "编辑项目"; |
| | | } catch (error) { |
| | | ElMessage.error('获取项目详情失败'); |
| | | console.error('获取项目详情失败:', error); |
| | | ElMessage.error("获取项目详情失败"); |
| | | console.error("获取项目详情失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 删除项目 |
| | | const handleDelete = async (row) => { |
| | | const handleDelete = async row => { |
| | | // const projectIds = row.projectId ? [row.projectId] : selectedRowKeys.value; |
| | | const projectNames = row.projectName ? [row.projectName] : |
| | | projectList.value.filter(item => projectIds.includes(item.projectId)).map(item => item.projectName); |
| | | const projectNames = row.projectName |
| | | ? [row.projectName] |
| | | : projectList.value |
| | | .filter(item => projectIds.includes(item.projectId)) |
| | | .map(item => item.projectName); |
| | | |
| | | const confirmMessage = `确定要删除项目 "${projectNames.join('、')}" 吗?`; |
| | | await ElMessageBox.confirm(confirmMessage, '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const confirmMessage = `确定要删除项目 "${projectNames.join("、")}" 吗?`; |
| | | await ElMessageBox.confirm(confirmMessage, "确认操作", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).catch(() => { |
| | | throw new Error('取消删除'); |
| | | throw new Error("取消删除"); |
| | | }); |
| | | |
| | | try { |
| | |
| | | // } else { |
| | | // await delProjectBatch(projectIds); |
| | | // } |
| | | ElMessage.success('删除成功'); |
| | | ElMessage.success("删除成功"); |
| | | getList(); |
| | | } catch (error) { |
| | | if (error.message !== '取消删除') { |
| | | ElMessage.error('删除失败'); |
| | | console.error('删除项目失败:', error); |
| | | if (error.message !== "取消删除") { |
| | | ElMessage.error("删除失败"); |
| | | console.error("删除项目失败:", error); |
| | | } |
| | | } |
| | | // try { |
| | |
| | | // // 模拟网络延迟 |
| | | // await new Promise(resolve => setTimeout(resolve, 300)); |
| | | |
| | | |
| | | // ElMessage.success('删除成功'); |
| | | // getList(); |
| | | // } catch (error) { |
| | |
| | | }; |
| | | |
| | | // 查看项目详情 |
| | | const handleView = (row) => { |
| | | const handleView = row => { |
| | | const projectId = row.projectId; |
| | | // 跳转到项目详情页面 |
| | | router.push({ |
| | | path: `/oaSystem/projectManagement/projectDetail/${projectId}`, |
| | | query: { projectName: row.projectName } |
| | | query: { projectName: row.projectName }, |
| | | }); |
| | | }; |
| | | |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download(`/oA/project/export/${ids.join(',')}`, {}, "项目数据.xlsx"); |
| | | proxy.download( |
| | | `/oA/project/export/${ids.join(",")}`, |
| | | {}, |
| | | "项目数据.xlsx" |
| | | ); |
| | | ElMessage.success("导出成功"); |
| | | ids = []; |
| | | }) |
| | |
| | | |
| | | if (form.projectId) { |
| | | await updateProject(form); |
| | | ElMessage.success('修改项目成功'); |
| | | ElMessage.success("修改项目成功"); |
| | | } else { |
| | | console.log("form",form); |
| | | await addProject(form); |
| | | ElMessage.success('新增项目成功'); |
| | | ElMessage.success("新增项目成功"); |
| | | } |
| | | open.value = false; |
| | | getList(); |
| | | } catch (error) { |
| | | console.error('提交表单失败:', error); |
| | | console.error("提交表单失败:", error); |
| | | } |
| | | }; |
| | | |
| | |
| | | // 重置表单 |
| | | const resetForm = () => { |
| | | form.projectId = undefined; |
| | | form.projectName = ''; |
| | | form.description = ''; |
| | | form.startDate = ''; |
| | | form.endDate = ''; |
| | | form.managerId = ''; |
| | | form.managerName = ''; |
| | | form.status = 'planning'; |
| | | form.projectName = ""; |
| | | form.description = ""; |
| | | form.startDate = ""; |
| | | form.endDate = ""; |
| | | form.managerId = ""; |
| | | form.managerName = ""; |
| | | form.status = "planning"; |
| | | form.completionRate = 0; |
| | | if (projectFormRef.value) { |
| | | projectFormRef.value.resetFields(); |
| | |
| | | }; |
| | | |
| | | // 获取状态标签类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusTypeMap = { |
| | | planning: 'info', |
| | | inProgress: 'primary', |
| | | completed: 'success', |
| | | paused: 'warning' |
| | | planning: "info", |
| | | inProgress: "primary", |
| | | completed: "success", |
| | | paused: "warning", |
| | | }; |
| | | return statusTypeMap[status] || 'default'; |
| | | return statusTypeMap[status] || "default"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusTextMap = { |
| | | planning: '规划中', |
| | | inProgress: '进行中', |
| | | completed: '已完成', |
| | | paused: '已暂停' |
| | | planning: "规划中", |
| | | inProgress: "进行中", |
| | | completed: "已完成", |
| | | paused: "已暂停", |
| | | }; |
| | | return statusTextMap[status] || status; |
| | | }; |
| | |
| | | <span>项目基本信息</span> |
| | | </div> |
| | | </template> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="项目名称">{{ projectInfo.projectName }}</el-descriptions-item> |
| | | <el-descriptions-item label="项目负责人">{{ projectInfo.managerName }}</el-descriptions-item> |
| | | <el-descriptions-item label="开始日期">{{ projectInfo.startDate }}</el-descriptions-item> |
| | |
| | | <el-tag :type="getStatusType(projectInfo.status)">{{ getStatusText(projectInfo.status) }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="完成度"> |
| | | <el-progress :percentage="projectInfo.completionRate" :stroke-width="6" /> |
| | | <el-progress :percentage="projectInfo.completionRate" |
| | | :stroke-width="6" /> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="项目描述" :span="2">{{ projectInfo.description || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="项目描述" |
| | | :span="2">{{ projectInfo.description || '-' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-card> |
| | | |
| | | <!-- 项目进度概览 --> |
| | | <el-card class="mb20"> |
| | | <template #header> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | |
| | | <!-- 阶段和任务管理 --> |
| | | <!-- <el-card class="mb20"> |
| | | <template #header> |
| | |
| | | </template> |
| | | <task-tree :project-id="projectId" @refresh="getProjectDetail" /> |
| | | </el-card> --> |
| | | |
| | | <!-- 里程碑管理 --> |
| | | <el-card class="mb20"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>项目阶段里程碑</span> |
| | | <el-button type="primary" size="small" @click="handleAddMilestone">添加里程碑</el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="handleAddMilestone">添加里程碑</el-button> |
| | | </div> |
| | | </template> |
| | | <milestone-list :project-id="projectId" @refresh="getProjectDetail" :key="`milestone-${refreshProjectId}`"/> |
| | | <milestone-list :project-id="projectId" |
| | | @refresh="getProjectDetail" |
| | | :key="`milestone-${refreshProjectId}`" /> |
| | | </el-card> |
| | | |
| | | <!-- 阶段目标管理 --> |
| | | <el-card> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>阶段任务</span> |
| | | <el-button type="primary" size="small" @click="handleAddPhaseGoal">添加阶段目标</el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="handleAddPhaseGoal">添加阶段目标</el-button> |
| | | </div> |
| | | </template> |
| | | <phase-goal-list :project-id="projectId" @refresh="getProjectDetail" @editGoal="handleEditPhaseGoal" :key="`phaseGoal-${refreshProjectId}`"/> |
| | | <phase-goal-list :project-id="projectId" |
| | | @refresh="getProjectDetail" |
| | | @editGoal="handleEditPhaseGoal" |
| | | :key="`phaseGoal-${refreshProjectId}`" /> |
| | | </el-card> |
| | | |
| | | <!-- 里程碑管理弹框 --> |
| | | <el-dialog :title="title" v-model="open" width="600px" append-to-body> |
| | | <el-form :model="form" ref="formRef" label-width="100px"> |
| | | <el-form-item label="项目阶段名称" prop="phaseName"> |
| | | <el-input |
| | | v-model="form.phaseName" |
| | | <el-dialog :title="title" |
| | | v-model="open" |
| | | width="600px" |
| | | append-to-body> |
| | | <el-form :model="form" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-form-item label="项目阶段名称" |
| | | prop="phaseName"> |
| | | <el-input v-model="form.phaseName" |
| | | placeholder="请输入项目阶段名称" |
| | | maxlength="50" |
| | | /> |
| | | maxlength="50" /> |
| | | </el-form-item> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始日期" prop="startDate"> |
| | | <el-date-picker |
| | | v-model="form.startDate" |
| | | <el-form-item label="开始日期" |
| | | prop="startDate"> |
| | | <el-date-picker v-model="form.startDate" |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="选择开始日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="结束日期" prop="endDate"> |
| | | <el-date-picker |
| | | v-model="form.endDate" |
| | | <el-form-item label="结束日期" |
| | | prop="endDate"> |
| | | <el-date-picker v-model="form.endDate" |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="选择结束日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio label="notStarted">未开始</el-radio> |
| | | <el-radio label="completed">已完成</el-radio> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确定</el-button> |
| | | <el-button @click="cancel">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 阶段任务管理弹框 --> |
| | | <el-dialog :title="goalTitle" v-model="goalOpen" width="600px" append-to-body> |
| | | <el-form :model="goalForm" ref="goalFormRef" label-width="100px"> |
| | | <el-form-item label="所属阶段" prop="phaseId"> |
| | | <el-select v-model="goalForm.phaseId" placeholder="请选择所属阶段"> |
| | | <el-option |
| | | v-for="phase in phaseList" |
| | | <el-dialog :title="goalTitle" |
| | | v-model="goalOpen" |
| | | width="600px" |
| | | append-to-body> |
| | | <el-form :model="goalForm" |
| | | ref="goalFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="所属阶段" |
| | | prop="phaseId"> |
| | | <el-select v-model="goalForm.phaseId" |
| | | placeholder="请选择所属阶段"> |
| | | <el-option v-for="phase in phaseList" |
| | | :key="phase.phaseId" |
| | | :label="phase.phaseName" |
| | | :value="phase.phaseId" |
| | | /> |
| | | :value="phase.phaseId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="目标名称" prop="taskName"> |
| | | <el-input |
| | | v-model="goalForm.taskName" |
| | | <el-form-item label="目标名称" |
| | | prop="taskName"> |
| | | <el-input v-model="goalForm.taskName" |
| | | placeholder="请输入目标名称" |
| | | maxlength="50" |
| | | /> |
| | | maxlength="50" /> |
| | | </el-form-item> |
| | | <el-form-item label="目标值" prop="targetValue"> |
| | | <el-input-number |
| | | v-model="goalForm.targetValue" |
| | | <el-form-item label="目标值" |
| | | prop="targetValue"> |
| | | <el-input-number v-model="goalForm.targetValue" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="请输入目标值" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="当前值" prop="currentValue"> |
| | | <el-input-number |
| | | v-model="goalForm.currentValue" |
| | | <el-form-item label="当前值" |
| | | prop="currentValue"> |
| | | <el-input-number v-model="goalForm.currentValue" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="请输入当前值" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="单位" prop="unit"> |
| | | <el-input |
| | | v-model="goalForm.unit" |
| | | <el-form-item label="单位" |
| | | prop="unit"> |
| | | <el-input v-model="goalForm.unit" |
| | | placeholder="请输入单位" |
| | | maxlength="10" |
| | | /> |
| | | maxlength="10" /> |
| | | </el-form-item> |
| | | <el-form-item label="任务完成日期" prop="targetDate"> |
| | | <el-date-picker |
| | | v-model="goalForm.targetDate" |
| | | <el-form-item label="任务完成日期" |
| | | prop="targetDate"> |
| | | <el-date-picker v-model="goalForm.targetDate" |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="选择目标日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="开始日期" prop="startDate"> |
| | | <el-date-picker |
| | | v-model="goalForm.startDate" |
| | | <el-form-item label="开始日期" |
| | | prop="startDate"> |
| | | <el-date-picker v-model="goalForm.startDate" |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="选择目标日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="结束日期" prop="endDate"> |
| | | <el-date-picker |
| | | v-model="goalForm.endDate" |
| | | <el-form-item label="结束日期" |
| | | prop="endDate"> |
| | | <el-date-picker v-model="goalForm.endDate" |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="选择目标日期" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="goalForm.status" placeholder="请选择状态"> |
| | | <el-option label="未开始" value="notStarted" /> |
| | | <el-option label="进行中" value="inProgress" /> |
| | | <el-option label="已完成" value="completed" /> |
| | | <el-option label="已延迟" value="delayed" /> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="goalForm.status" |
| | | placeholder="请选择状态"> |
| | | <el-option label="未开始" |
| | | value="notStarted" /> |
| | | <el-option label="进行中" |
| | | value="inProgress" /> |
| | | <el-option label="已完成" |
| | | value="completed" /> |
| | | <el-option label="已延迟" |
| | | value="delayed" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <!-- <el-form-item label="完成度" prop="completionRate"> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitGoalForm">确定</el-button> |
| | | <el-button @click="cancelGoal">取消</el-button> |
| | | <el-button type="primary" @click="submitGoalForm">确定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, watch } from 'vue'; |
| | | import { useRoute, useRouter } from 'vue-router'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import TaskTree from './components/taskTree.vue'; |
| | | import MilestoneList from './components/milestoneList.vue'; |
| | | import ProjectForm from './components/projectForm.vue'; |
| | | import PhaseGoalList from './components/phaseGoalList.vue'; |
| | | import { getProject, addProjectPhase, listProjectPhase, addProjectTask, updateProjectTask } from '@/api/oaSystem/projectManagement'; |
| | | import { ref, reactive, onMounted, watch } from "vue"; |
| | | import { useRoute, useRouter } from "vue-router"; |
| | | import { ElMessage } from "element-plus"; |
| | | import TaskTree from "./components/taskTree.vue"; |
| | | import MilestoneList from "./components/milestoneList.vue"; |
| | | import ProjectForm from "./components/projectForm.vue"; |
| | | import PhaseGoalList from "./components/phaseGoalList.vue"; |
| | | import { |
| | | getProject, |
| | | addProjectPhase, |
| | | listProjectPhase, |
| | | addProjectTask, |
| | | updateProjectTask, |
| | | } from "@/api/oaSystem/projectManagement"; |
| | | |
| | | const route = useRoute(); |
| | | const router = useRouter(); |
| | | const open = ref(false); |
| | | const title = ref(''); |
| | | const title = ref(""); |
| | | const projectFormRef = ref(); |
| | | const formRef = ref(); |
| | | // 项目ID |
| | |
| | | |
| | | // 项目信息 |
| | | const projectInfo = reactive({ |
| | | projectId: '', |
| | | projectName: '', |
| | | description: '', |
| | | startDate: '', |
| | | endDate: '', |
| | | managerId: '', |
| | | managerName: '', |
| | | status: 'planning', |
| | | completionRate: 0 |
| | | projectId: "", |
| | | projectName: "", |
| | | description: "", |
| | | startDate: "", |
| | | endDate: "", |
| | | managerId: "", |
| | | managerName: "", |
| | | status: "planning", |
| | | completionRate: 0, |
| | | }); |
| | | |
| | | // 统计信息 |
| | | const statistics = reactive({ |
| | | totalPhases: 0, |
| | | totalTasks: 0, |
| | | completedTasks: 0 |
| | | completedTasks: 0, |
| | | }); |
| | | const form = reactive({ |
| | | phaseId: '', |
| | | phaseName: '', |
| | | startDate: '', |
| | | endDate: '', |
| | | status: 'planning', |
| | | phaseId: "", |
| | | phaseName: "", |
| | | startDate: "", |
| | | endDate: "", |
| | | status: "planning", |
| | | oaProjectId: projectId.value, |
| | | }) |
| | | }); |
| | | |
| | | // 阶段目标相关 |
| | | const goalOpen = ref(false); |
| | | const goalTitle = ref(''); |
| | | const goalTitle = ref(""); |
| | | const goalFormRef = ref(); |
| | | const phaseList = ref([]); |
| | | const goalForm = reactive({ |
| | | taskId: '', |
| | | phaseId: '', |
| | | taskName: '', |
| | | taskId: "", |
| | | phaseId: "", |
| | | taskName: "", |
| | | targetValue: 100, |
| | | currentValue: 0, |
| | | unit: '%', |
| | | targetDate: '', |
| | | startDate: '', |
| | | endDate: '', |
| | | status: 'notStarted', |
| | | completionRate: 0 |
| | | unit: "%", |
| | | targetDate: "", |
| | | startDate: "", |
| | | endDate: "", |
| | | status: "notStarted", |
| | | completionRate: 0, |
| | | }); |
| | | |
| | | // 获取项目详情 |
| | | const getProjectDetail = async () => { |
| | | try { |
| | | getProject().then((res)=>{ |
| | | console.log("项目详情",res) |
| | | getProject().then(res => { |
| | | console.log("项目详情", res); |
| | | const projectData = res.data[projectId.value]; |
| | | // 更新项目信息 |
| | | Object.assign(projectInfo, projectData); |
| | |
| | | // 强制更新DOM以确保子组件能正确刷新 |
| | | // 这里通过触发refreshProjectId事件来强制刷新子组件 |
| | | refreshProjectId.value++; |
| | | }) |
| | | }); |
| | | } catch (error) { |
| | | ElMessage.error('获取项目详情失败'); |
| | | console.error('获取项目详情失败:', error); |
| | | ElMessage.error("获取项目详情失败"); |
| | | console.error("获取项目详情失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 更新统计信息 |
| | | const updateStatistics = (projectData) => { |
| | | const updateStatistics = projectData => { |
| | | // 这里假设projectData中包含了统计信息 |
| | | // 如果没有,需要单独请求统计数据 |
| | | statistics.totalPhases = projectData.phases ? projectData.phases.length : 0; |
| | | statistics.totalTasks = projectData.tasks ? projectData.tasks.length : 0; |
| | | statistics.completedTasks = projectData.tasks ? |
| | | projectData.tasks.filter(task => task.status === 'completed').length : 0; |
| | | statistics.completedTasks = projectData.tasks |
| | | ? projectData.tasks.filter(task => task.status === "completed").length |
| | | : 0; |
| | | }; |
| | | |
| | | // 获取项目阶段列表 |
| | |
| | | const { data } = await listProjectPhase(projectId.value); |
| | | phaseList.value = data.rows || data; |
| | | } catch (error) { |
| | | ElMessage.error('获取项目阶段列表失败'); |
| | | console.error('获取项目阶段列表失败:', error); |
| | | ElMessage.error("获取项目阶段列表失败"); |
| | | console.error("获取项目阶段列表失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 计算完成度 |
| | | const calculateCompletionRate = () => { |
| | | if (goalForm.targetValue > 0) { |
| | | goalForm.completionRate = Math.min(Math.round((goalForm.currentValue / goalForm.targetValue) * 100), 100); |
| | | goalForm.completionRate = Math.min( |
| | | Math.round((goalForm.currentValue / goalForm.targetValue) * 100), |
| | | 100 |
| | | ); |
| | | } else { |
| | | goalForm.completionRate = 0; |
| | | } |
| | |
| | | // 添加阶段 |
| | | const handleAddPhase = () => { |
| | | // resetForm(); |
| | | ElMessage.info('添加阶段功能待实现'); |
| | | ElMessage.info("添加阶段功能待实现"); |
| | | }; |
| | | |
| | | // 添加里程碑 |
| | | const handleAddMilestone = () => { |
| | | resetForm(); |
| | | open.value = true; |
| | | title.value = '新增项目阶段'; |
| | | title.value = "新增项目阶段"; |
| | | }; |
| | | |
| | | // 添加阶段任务 |
| | | const handleAddPhaseGoal = () => { |
| | | goalForm.taskId = ''; |
| | | goalForm.phaseId = ''; |
| | | goalForm.taskName = ''; |
| | | goalForm.taskId = ""; |
| | | goalForm.phaseId = ""; |
| | | goalForm.taskName = ""; |
| | | goalForm.targetValue = 0; |
| | | goalForm.currentValue = 0; |
| | | goalForm.unit = '%'; |
| | | goalForm.targetDate = ''; |
| | | goalForm.startDate = ''; |
| | | goalForm.endDate = ''; |
| | | goalForm.status = 'notStarted'; |
| | | goalForm.unit = "%"; |
| | | goalForm.targetDate = ""; |
| | | goalForm.startDate = ""; |
| | | goalForm.endDate = ""; |
| | | goalForm.status = "notStarted"; |
| | | goalForm.completionRate = 0; |
| | | if (goalFormRef.value) { |
| | | goalFormRef.value.resetFields(); |
| | | } |
| | | getPhaseList(); |
| | | goalTitle.value = '新增阶段目标'; |
| | | goalTitle.value = "新增阶段目标"; |
| | | goalOpen.value = true; |
| | | }; |
| | | |
| | |
| | | } else { |
| | | console.log("form",form); |
| | | await addProjectPhase(form); |
| | | ElMessage.success('新增项目阶段成功'); |
| | | ElMessage.success("新增项目阶段成功"); |
| | | getProjectDetail(); |
| | | } |
| | | open.value = false; |
| | | } catch (error) { |
| | | console.error('提交表单失败:', error); |
| | | console.error("提交表单失败:", error); |
| | | } |
| | | }; |
| | | |
| | |
| | | |
| | | const goalData = { |
| | | ...goalForm, |
| | | oaProjectId: projectId.value |
| | | oaProjectId: projectId.value, |
| | | }; |
| | | |
| | | if (goalForm.taskId) { |
| | | await updateProjectTask(goalData); |
| | | ElMessage.success('修改阶段目标成功'); |
| | | |
| | | ElMessage.success("修改阶段目标成功"); |
| | | } else { |
| | | await addProjectTask(goalData); |
| | | ElMessage.success('新增阶段目标成功'); |
| | | |
| | | ElMessage.success("新增阶段目标成功"); |
| | | } |
| | | // 调用getProjectDetail刷新所有相关数据 |
| | | getProjectDetail(); |
| | | goalOpen.value = false; |
| | | |
| | | } catch (error) { |
| | | console.error('提交阶段目标表单失败:', error); |
| | | console.error("提交阶段目标表单失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 重置里程碑表单 |
| | | const resetForm = () => { |
| | | form.phaseId = ''; |
| | | form.phaseName = ''; |
| | | form.startDate = ''; |
| | | form.endDate = ''; |
| | | form.status = 'planning'; |
| | | form.phaseId = ""; |
| | | form.phaseName = ""; |
| | | form.startDate = ""; |
| | | form.endDate = ""; |
| | | form.status = "planning"; |
| | | form.oaProjectId = projectId.value; |
| | | if (formRef.value) { |
| | | formRef.value.resetFields(); |
| | |
| | | open.value = false; |
| | | }; |
| | | // 编辑阶段任务 |
| | | const handleEditPhaseGoal = async (goal) => { |
| | | const handleEditPhaseGoal = async goal => { |
| | | // 复制目标数据到表单 |
| | | Object.assign(goalForm, goal); |
| | | |
| | |
| | | await getPhaseList(); |
| | | |
| | | // 打开编辑弹窗 |
| | | goalTitle.value = '编辑阶段目标'; |
| | | goalTitle.value = "编辑阶段目标"; |
| | | goalOpen.value = true; |
| | | }; |
| | | // 获取状态标签类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusTypeMap = { |
| | | planning: 'info', |
| | | inProgress: 'primary', |
| | | completed: 'success', |
| | | paused: 'warning' |
| | | planning: "info", |
| | | inProgress: "primary", |
| | | completed: "success", |
| | | paused: "warning", |
| | | }; |
| | | return statusTypeMap[status] || 'default'; |
| | | return statusTypeMap[status] || "default"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusTextMap = { |
| | | planning: '规划中', |
| | | inProgress: '进行中', |
| | | completed: '已完成', |
| | | paused: '已暂停' |
| | | planning: "规划中", |
| | | inProgress: "进行中", |
| | | completed: "已完成", |
| | | paused: "已暂停", |
| | | }; |
| | | return statusTextMap[status] || status; |
| | | }; |
| | | |
| | | // 监听路由参数变化 |
| | | watch(() => route.params.projectId, (newProjectId) => { |
| | | watch( |
| | | () => route.params.projectId, |
| | | newProjectId => { |
| | | // console.log('路由参数变化:', projectId); |
| | | if (newProjectId) { |
| | | projectId.value = newProjectId; |
| | | getProjectDetail(); |
| | | } |
| | | }); |
| | | } |
| | | ); |
| | | |
| | | // 监听当前值和目标值变化,重新计算完成度 |
| | | watch(() => [goalForm.currentValue, goalForm.targetValue], () => { |
| | | watch( |
| | | () => [goalForm.currentValue, goalForm.targetValue], |
| | | () => { |
| | | calculateCompletionRate(); |
| | | }); |
| | | } |
| | | ); |
| | | |
| | | // 初始化 |
| | | onMounted(() => { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 搜索区域 --> |
| | | <el-card class="search-card" shadow="never"> |
| | | <el-form :model="searchForm" :inline="true" class="search-form"> |
| | | <el-card class="search-card" |
| | | shadow="never"> |
| | | <el-form :model="searchForm" |
| | | :inline="true" |
| | | class="search-form"> |
| | | <el-form-item label="计划名称"> |
| | | <el-input v-model="searchForm.planName" placeholder="请输入计划名称" clearable /> |
| | | <el-input v-model="searchForm.planName" |
| | | placeholder="请输入计划名称" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="状态"> |
| | | <el-select v-model="searchForm.status" placeholder="请选择状态" clearable style="width: 150px"> |
| | | <el-option label="启用" value="active" /> |
| | | <el-option label="禁用" value="disabled" /> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="请选择状态" |
| | | clearable |
| | | style="width: 150px"> |
| | | <el-option label="启用" |
| | | value="active" /> |
| | | <el-option label="禁用" |
| | | value="disabled" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch"> |
| | | <el-icon><Search /></el-icon> |
| | | <el-button type="primary" |
| | | @click="handleSearch"> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | 搜索 |
| | | </el-button> |
| | | <el-button @click="handleReset"> |
| | | <el-icon><Refresh /></el-icon> |
| | | <el-icon> |
| | | <Refresh /> |
| | | </el-icon> |
| | | 重置 |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- 操作按钮 --> |
| | | <el-card class="table-card" shadow="never"> |
| | | <el-card class="table-card" |
| | | shadow="never"> |
| | | <div class="table-header"> |
| | | <div class="table-title">采购计划列表</div> |
| | | <div class="table-actions"> |
| | | <el-button type="primary" @click="handleAdd"> |
| | | <el-icon><Plus /></el-icon> |
| | | <el-button type="primary" |
| | | @click="handleAdd"> |
| | | <el-icon> |
| | | <Plus /> |
| | | </el-icon> |
| | | 新增计划 |
| | | </el-button> |
| | | <el-button type="info" @click="handleExport"> |
| | | <el-icon><Download /></el-icon> |
| | | <el-button type="info" |
| | | @click="handleExport"> |
| | | <el-icon> |
| | | <Download /> |
| | | </el-icon> |
| | | 导出 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 数据表格 --> |
| | | <el-table |
| | | v-loading="loading" |
| | | <el-table v-loading="loading" |
| | | :data="tableData" |
| | | stripe |
| | | border |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column prop="planName" label="计划名称" min-width="150" /> |
| | | <el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="formula" label="计算公式" min-width="200" show-overflow-tooltip> |
| | | style="width: 100%"> |
| | | <el-table-column prop="planName" |
| | | label="计划名称" |
| | | min-width="150" /> |
| | | <el-table-column prop="description" |
| | | label="描述" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="formula" |
| | | label="计算公式" |
| | | min-width="200" |
| | | show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <el-tag type="info" size="small">{{ row.formula }}</el-tag> |
| | | <el-tag type="info" |
| | | size="small">{{ row.formula }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="状态" width="80" align="center"> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="80" |
| | | align="center"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.status === 'active' ? 'success' : 'info'" size="small"> |
| | | <el-tag :type="row.status === 'active' ? 'success' : 'info'" |
| | | size="small"> |
| | | {{ row.status === 'active' ? '启用' : '禁用' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="updateTime" label="最后计算时间" width="160" /> |
| | | <el-table-column label="操作" width="200" fixed="right" align="center"> |
| | | <el-table-column prop="updateTime" |
| | | label="最后计算时间" |
| | | width="160" /> |
| | | <el-table-column label="操作" |
| | | width="200" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" link @click="handleEdit(row)">编辑</el-button> |
| | | <el-button type="success" link @click="handleCalculate(row)">计算</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">删除</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="handleEdit(row)">编辑</el-button> |
| | | <el-button type="success" |
| | | link |
| | | @click="handleCalculate(row)">计算</el-button> |
| | | <el-button type="danger" |
| | | link |
| | | @click="handleDelete(row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | v-model:current-page="pagination.current" |
| | | <el-pagination v-model:current-page="pagination.current" |
| | | v-model:page-size="pagination.size" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | @current-change="handleCurrentChange" /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 新增/编辑对话框 --> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogType === 'add' ? '新增采购计划' : '编辑采购计划'" |
| | | width="1000px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | :close-on-click-modal="false"> |
| | | <div class="form-container"> |
| | | <!-- 基本信息 --> |
| | | <div class="form-section"> |
| | | <div class="section-title">基本信息</div> |
| | | <el-form |
| | | ref="formRef" |
| | | <el-form ref="formRef" |
| | | :model="formData" |
| | | :rules="formRules" |
| | | label-width="120px" |
| | | > |
| | | label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="编码" prop="code"> |
| | | <el-input v-model="formData.code" placeholder="系统自动生成" disabled /> |
| | | <el-form-item label="编码" |
| | | prop="code"> |
| | | <el-input v-model="formData.code" |
| | | placeholder="系统自动生成" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="名称" prop="planName" required> |
| | | <el-input v-model="formData.planName" placeholder="请输入计划名称" /> |
| | | <el-form-item label="名称" |
| | | prop="planName" |
| | | required> |
| | | <el-input v-model="formData.planName" |
| | | placeholder="请输入计划名称" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-form-item label="描述" prop="description"> |
| | | <el-input |
| | | v-model="formData.description" |
| | | <el-form-item label="描述" |
| | | prop="description"> |
| | | <el-input v-model="formData.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入计划描述" |
| | | /> |
| | | placeholder="请输入计划描述" /> |
| | | </el-form-item> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="formData.status" placeholder="请选择状态" style="width: 100%"> |
| | | <el-option label="启用" value="active" /> |
| | | <el-option label="禁用" value="disabled" /> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="formData.status" |
| | | placeholder="请选择状态" |
| | | style="width: 100%"> |
| | | <el-option label="启用" |
| | | value="active" /> |
| | | <el-option label="禁用" |
| | | value="disabled" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- 计算参数 --> |
| | | <div class="form-section"> |
| | | <div class="section-title">计算参数</div> |
| | | <el-tabs v-model="activeTab" class="param-tabs"> |
| | | <el-tab-pane label="需求参数" name="demand"> |
| | | <el-tabs v-model="activeTab" |
| | | class="param-tabs"> |
| | | <el-tab-pane label="需求参数" |
| | | name="demand"> |
| | | <div class="checkbox-group"> |
| | | <el-checkbox v-model="formData.considerExistingStock">考虑现有库存</el-checkbox> |
| | | <el-checkbox v-model="formData.warehouseControl">仓库运行MRP的控制</el-checkbox> |
| | |
| | | <el-checkbox v-model="formData.negativeStockAsDemand">负库存作为需求</el-checkbox> |
| | | </div> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="计算参数" name="calculation"> |
| | | <el-tab-pane label="计算参数" |
| | | name="calculation"> |
| | | <div class="checkbox-group"> |
| | | <el-checkbox v-model="formData.considerExistingStock">考虑现有库存</el-checkbox> |
| | | <el-checkbox v-model="formData.warehouseControl">仓库运行MRP的控制</el-checkbox> |
| | |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | |
| | | <!-- 汇总合并选项 --> |
| | | <div class="form-section"> |
| | | <div class="section-title">汇总合并选项</div> |
| | |
| | | <el-checkbox v-model="formData.summaryDemandDate">需求日期</el-checkbox> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 计算公式 --> |
| | | <div class="form-section"> |
| | | <div class="section-title">计算公式</div> |
| | | <div class="formula-input-section"> |
| | | <el-form-item label="计算公式" prop="formula" required> |
| | | <el-input |
| | | v-model="formData.formula" |
| | | <el-form-item label="计算公式" |
| | | prop="formula" |
| | | required> |
| | | <el-input v-model="formData.formula" |
| | | placeholder="例如: 预计出库数量 - 现有库存 + 安全库存 - 预计入库数量" |
| | | @input="validateFormula" |
| | | /> |
| | | @input="validateFormula" /> |
| | | </el-form-item> |
| | | <div class="formula-help"> |
| | | <el-text type="info" size="small"> |
| | | <el-text type="info" |
| | | size="small"> |
| | | 支持变量:预计出库数量、现有库存、安全库存、预计入库数量 |
| | | </el-text> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSubmit" |
| | | :loading="submitLoading">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 产品选择对话框 --> |
| | | <el-dialog |
| | | v-model="productSelectDialogVisible" |
| | | <el-dialog v-model="productSelectDialogVisible" |
| | | title="选择产品" |
| | | width="800px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | :close-on-click-modal="false"> |
| | | <div class="product-select"> |
| | | <el-alert |
| | | title="请选择要计算的产品" |
| | | <el-alert title="请选择要计算的产品" |
| | | type="info" |
| | | :closable="false" |
| | | show-icon |
| | | > |
| | | show-icon> |
| | | <template #default> |
| | | <p>选择产品后,系统将根据当前计算公式和产品库存情况进行计算。</p> |
| | | </template> |
| | | </el-alert> |
| | | |
| | | <el-table |
| | | v-loading="productLoading" |
| | | <el-table v-loading="productLoading" |
| | | :data="productList" |
| | | @selection-change="handleProductSelectionChange" |
| | | stripe |
| | | border |
| | | style="width: 100%; margin-top: 20px;" |
| | | > |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column prop="productCategory" label="产品大类" min-width="150" /> |
| | | <el-table-column prop="specificationModel" label="产品规格" width="120" /> |
| | | <el-table-column prop="inboundNum0" label="现有库存" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="安全库存" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="预计出库" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum0" label="预计入库" width="100" align="right" /> |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column type="selection" |
| | | width="55" /> |
| | | <el-table-column prop="productCategory" |
| | | label="产品大类" |
| | | min-width="150" /> |
| | | <el-table-column prop="specificationModel" |
| | | label="产品规格" |
| | | width="120" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="现有库存" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="安全库存" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="预计出库" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="预计入库" |
| | | width="100" |
| | | align="right" /> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="productSelectDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="handleConfirmProductSelection" :disabled="selectedProducts.length === 0"> |
| | | <el-button type="primary" |
| | | @click="handleConfirmProductSelection" |
| | | :disabled="selectedProducts.length === 0"> |
| | | 确认计算 |
| | | </el-button> |
| | | <el-button @click="productSelectDialogVisible = false">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 计算结果对话框 --> |
| | | <el-dialog |
| | | v-model="calculateDialogVisible" |
| | | <el-dialog v-model="calculateDialogVisible" |
| | | title="采购计算结果" |
| | | width="1000px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | :close-on-click-modal="false"> |
| | | <div class="calculate-result"> |
| | | <el-alert |
| | | title="计算结果" |
| | | <el-alert title="计算结果" |
| | | type="success" |
| | | :closable="false" |
| | | show-icon |
| | | > |
| | | show-icon> |
| | | <template #default> |
| | | <p>基于当前配置的计算公式和库存情况,系统已计算出各产品的采购需求。</p> |
| | | </template> |
| | | </el-alert> |
| | | |
| | | <el-table :data="calculateResult" stripe border style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="productCategory" label="产品大类" min-width="150" /> |
| | | <el-table-column prop="specificationModel" label="产品规格" width="120" /> |
| | | <el-table-column prop="inboundNum0" label="现有库存" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="安全库存" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="预计出库数量" width="120" align="right" /> |
| | | <el-table-column prop="inboundNum0" label="预计入库数量" width="120" align="right" /> |
| | | <el-table-column prop="weeklyNetDemand" label="按周净需求" width="120" align="right"> |
| | | <el-table :data="calculateResult" |
| | | stripe |
| | | border |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="productCategory" |
| | | label="产品大类" |
| | | min-width="150" /> |
| | | <el-table-column prop="specificationModel" |
| | | label="产品规格" |
| | | width="120" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="现有库存" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="安全库存" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="预计出库数量" |
| | | width="120" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="预计入库数量" |
| | | width="120" |
| | | align="right" /> |
| | | <el-table-column prop="weeklyNetDemand" |
| | | label="按周净需求" |
| | | width="120" |
| | | align="right"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.weeklyNetDemand > 0 ? 'warning' : 'success'" size="small"> |
| | | <el-tag :type="row.weeklyNetDemand > 0 ? 'warning' : 'success'" |
| | | size="small"> |
| | | {{ row.weeklyNetDemand }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="suggestedPurchase" label="建议采购" width="100" align="right"> |
| | | <el-table-column prop="suggestedPurchase" |
| | | label="建议采购" |
| | | width="100" |
| | | align="right"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.suggestedPurchase > 0 ? 'danger' : 'success'" size="small"> |
| | | <el-tag :type="row.suggestedPurchase > 0 ? 'danger' : 'success'" |
| | | size="small"> |
| | | {{ row.suggestedPurchase }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleCreatePurchaseOrder">确认</el-button> |
| | | <el-button @click="calculateDialogVisible = false">关闭</el-button> |
| | | <el-button type="primary" @click="handleCreatePurchaseOrder">确认</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted, getCurrentInstance} from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Search, Refresh, Plus, Download } from '@element-plus/icons-vue' |
| | | import {listPage,add,update,del,listPageCopy} from "@/api/procurementManagement/procurementPlan.js" |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Search, Refresh, Plus, Download } from "@element-plus/icons-vue"; |
| | | import { |
| | | listPage, |
| | | add, |
| | | update, |
| | | del, |
| | | listPageCopy, |
| | | } from "@/api/procurementManagement/procurementPlan.js"; |
| | | |
| | | // 响应式数据 |
| | | const loading = ref(false) |
| | | const submitLoading = ref(false) |
| | | const dialogVisible = ref(false) |
| | | const productSelectDialogVisible = ref(false) |
| | | const calculateDialogVisible = ref(false) |
| | | const dialogType = ref('add') |
| | | const productLoading = ref(false) |
| | | const selectedProducts = ref([]) |
| | | const currentPlan = ref(null) |
| | | const loading = ref(false); |
| | | const submitLoading = ref(false); |
| | | const dialogVisible = ref(false); |
| | | const productSelectDialogVisible = ref(false); |
| | | const calculateDialogVisible = ref(false); |
| | | const dialogType = ref("add"); |
| | | const productLoading = ref(false); |
| | | const selectedProducts = ref([]); |
| | | const currentPlan = ref(null); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | planName: '', |
| | | status: '' |
| | | }) |
| | | planName: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 分页数据 |
| | | const pagination = reactive({ |
| | | current: 1, |
| | | size: 20 |
| | | }) |
| | | size: 20, |
| | | }); |
| | | |
| | | // 表单数据 |
| | | const formData = reactive({ |
| | | code: '', |
| | | planName: '', |
| | | description: '', |
| | | status: '', |
| | | code: "", |
| | | planName: "", |
| | | description: "", |
| | | status: "", |
| | | isSystemPreset: false, |
| | | formula: '', |
| | | formula: "", |
| | | // 计算参数 |
| | | considerExistingStock: false, |
| | | warehouseControl: false, |
| | |
| | | // 汇总合并选项 |
| | | summaryMaterial: false, |
| | | summaryAuxAttributes: false, |
| | | summaryDemandDate: false |
| | | }) |
| | | summaryDemandDate: false, |
| | | }); |
| | | |
| | | // 当前激活的标签页 |
| | | const activeTab = ref('demand') |
| | | const activeTab = ref("demand"); |
| | | |
| | | // 表单验证规则 |
| | | const formRules = { |
| | | planName: [ |
| | | { required: true, message: '请输入计划名称', trigger: 'blur' } |
| | | ], |
| | | status: [ |
| | | { required: true, message: '请选择状态', trigger: 'change' } |
| | | ], |
| | | formula: [ |
| | | { required: true, message: '请输入计算公式', trigger: 'blur' } |
| | | ] |
| | | } |
| | | planName: [{ required: true, message: "请输入计划名称", trigger: "blur" }], |
| | | status: [{ required: true, message: "请选择状态", trigger: "change" }], |
| | | formula: [{ required: true, message: "请输入计算公式", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 表格数据 |
| | | const tableData = ref([]) |
| | | const tableData = ref([]); |
| | | |
| | | // 产品列表数据 |
| | | const productList = ref([ |
| | | { |
| | | id: 4, |
| | | productName: '产品D', |
| | | productCode: 'PD004', |
| | | productName: "产品D", |
| | | productCode: "PD004", |
| | | existingStock: 90, |
| | | safetyStock: 40, |
| | | expectedOutbound: 160, |
| | | expectedInbound: 35 |
| | | } |
| | | ]) |
| | | expectedInbound: 35, |
| | | }, |
| | | ]); |
| | | |
| | | // 计算结果数据 |
| | | const calculateResult = ref([ |
| | | { |
| | | productName: '产品A', |
| | | productName: "产品A", |
| | | existingStock: 100, |
| | | safetyStock: 50, |
| | | expectedOutbound: 200, |
| | | expectedInbound: 30, |
| | | weeklyNetDemand: 120, |
| | | suggestedPurchase: 150 |
| | | suggestedPurchase: 150, |
| | | }, |
| | | { |
| | | productName: '产品B', |
| | | productName: "产品B", |
| | | existingStock: 80, |
| | | safetyStock: 30, |
| | | expectedOutbound: 150, |
| | | expectedInbound: 20, |
| | | weeklyNetDemand: 100, |
| | | suggestedPurchase: 120 |
| | | } |
| | | ]) |
| | | const total = ref(0) |
| | | suggestedPurchase: 120, |
| | | }, |
| | | ]); |
| | | const total = ref(0); |
| | | // 方法 |
| | | const handleSearch = () => { |
| | | pagination.current = 1 |
| | | loadData() |
| | | } |
| | | pagination.current = 1; |
| | | loadData(); |
| | | }; |
| | | |
| | | const handleReset = () => { |
| | | Object.assign(searchForm, { |
| | | planName: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | planName: "", |
| | | status: "", |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | const loadData = () => { |
| | | loading.value = true |
| | | loading.value = true; |
| | | listPage({...searchForm,...pagination}).then(res => { |
| | | if(res.code === 200){ |
| | | tableData.value = res.data.records |
| | | total.value = res.data.total |
| | | loading.value = false |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | loading.value = false; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleAdd = () => { |
| | | dialogType.value = 'add' |
| | | resetForm() |
| | | dialogType.value = "add"; |
| | | resetForm(); |
| | | // 自动生成编码 |
| | | formData.code = 'CGJH' + String(Date.now()).slice(-4) |
| | | dialogVisible.value = true |
| | | } |
| | | formData.code = "CGJH" + String(Date.now()).slice(-4); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | dialogType.value = 'edit' |
| | | Object.assign(formData, row) |
| | | dialogVisible.value = true |
| | | } |
| | | const handleEdit = row => { |
| | | dialogType.value = "edit"; |
| | | Object.assign(formData, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = async (row) => { |
| | | const handleDelete = async row => { |
| | | try { |
| | | await ElMessageBox.confirm('确定要删除这个采购计划吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | let ids = [row.id] |
| | | await ElMessageBox.confirm("确定要删除这个采购计划吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }); |
| | | let ids = [row.id]; |
| | | del(ids).then(res =>{ |
| | | if(res.code === 200){ |
| | | ElMessage.success('删除成功') |
| | | loadData() |
| | | ElMessage.success("删除成功"); |
| | | loadData(); |
| | | } |
| | | }) |
| | | }); |
| | | } catch { |
| | | // 用户取消删除 |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleSubmit = async () => { |
| | | try { |
| | | // 表单验证 |
| | | if (!formData.planName || !formData.formula) { |
| | | ElMessage.error('请填写必填项') |
| | | return |
| | | ElMessage.error("请填写必填项"); |
| | | return; |
| | | } |
| | | |
| | | submitLoading.value = true |
| | | submitLoading.value = true; |
| | | |
| | | if (dialogType.value === 'add') { |
| | | if (dialogType.value === "add") { |
| | | add(formData).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('新增成功') |
| | | dialogVisible.value = false |
| | | loadData() |
| | | ElMessage.success("新增成功"); |
| | | dialogVisible.value = false; |
| | | loadData(); |
| | | } |
| | | }) |
| | | }); |
| | | } else { |
| | | // 编辑 |
| | | update(formData).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('编辑成功') |
| | | dialogVisible.value = false |
| | | loadData() |
| | | ElMessage.success("编辑成功"); |
| | | dialogVisible.value = false; |
| | | loadData(); |
| | | } |
| | | }) |
| | | }); |
| | | } |
| | | |
| | | |
| | | } catch (error) { |
| | | ElMessage.error('操作失败') |
| | | ElMessage.error("操作失败"); |
| | | } finally { |
| | | submitLoading.value = false |
| | | submitLoading.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const resetForm = () => { |
| | | Object.assign(formData, { |
| | | code: '', |
| | | planName: '', |
| | | description: '', |
| | | status: '', |
| | | code: "", |
| | | planName: "", |
| | | description: "", |
| | | status: "", |
| | | isSystemPreset: false, |
| | | formula: '预计出库数量 - 现有库存 + 安全库存 - 预计入库数量', |
| | | formula: "预计出库数量 - 现有库存 + 安全库存 - 预计入库数量", |
| | | // 计算参数 |
| | | considerExistingStock: false, |
| | | warehouseControl: false, |
| | |
| | | // 汇总合并选项 |
| | | summaryMaterial: false, |
| | | summaryAuxAttributes: false, |
| | | summaryDemandDate: false |
| | | }) |
| | | activeTab.value = 'demand' |
| | | } |
| | | summaryDemandDate: false, |
| | | }); |
| | | activeTab.value = "demand"; |
| | | }; |
| | | |
| | | const validateFormula = () => { |
| | | // 简单的公式验证 |
| | | const formula = formData.formula |
| | | const formula = formData.formula; |
| | | if (formula && !/^[a-zA-Z\u4e00-\u9fa5\s\*\+\-\/\(\)\d\.]+$/.test(formula)) { |
| | | ElMessage.warning('公式格式可能不正确,请检查') |
| | | ElMessage.warning("公式格式可能不正确,请检查"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleCalculate = (row) => { |
| | | currentPlan.value = row |
| | | productSelectDialogVisible.value = true |
| | | loadProductList() |
| | | } |
| | | const handleCalculate = row => { |
| | | currentPlan.value = row; |
| | | productSelectDialogVisible.value = true; |
| | | loadProductList(); |
| | | }; |
| | | |
| | | const loadProductList = () => { |
| | | productLoading.value = true |
| | | productLoading.value = true; |
| | | // 模拟加载产品数据 |
| | | listPageCopy({size:-1}).then(res => { |
| | | if(res.code === 200){ |
| | | productList.value = res.data.records |
| | | productLoading.value = false |
| | | productList.value = res.data.records; |
| | | productLoading.value = false; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleProductSelectionChange = (selection) => { |
| | | selectedProducts.value = selection |
| | | } |
| | | const handleProductSelectionChange = selection => { |
| | | selectedProducts.value = selection; |
| | | }; |
| | | |
| | | const handleConfirmProductSelection = () => { |
| | | if (selectedProducts.value.length === 0) { |
| | | ElMessage.warning('请选择要计算的产品') |
| | | return |
| | | ElMessage.warning("请选择要计算的产品"); |
| | | return; |
| | | } |
| | | |
| | | ElMessage.success(`正在计算 ${currentPlan.value.planName} 的采购需求...`) |
| | | productSelectDialogVisible.value = false |
| | | ElMessage.success(`正在计算 ${currentPlan.value.planName} 的采购需求...`); |
| | | productSelectDialogVisible.value = false; |
| | | |
| | | // 根据选择的产品和计算公式进行计算 |
| | | calculateWithSelectedProducts() |
| | | } |
| | | calculateWithSelectedProducts(); |
| | | }; |
| | | |
| | | const calculateWithSelectedProducts = () => { |
| | | // 模拟计算过程 |
| | |
| | | const result = selectedProducts.value.map(product => { |
| | | // 这里应该根据实际的计算公式进行计算 |
| | | // 示例:预计出库数量 - 现有库存 + 安全库存 - 预计入库数量 |
| | | const weeklyNetDemand = product.inboundNum - product.inboundNum0 + product.inboundNum - product.inboundNum0 |
| | | const suggestedPurchase = Math.max(0, weeklyNetDemand) |
| | | const weeklyNetDemand = |
| | | product.inboundNum - |
| | | product.inboundNum0 + |
| | | product.inboundNum - |
| | | product.inboundNum0; |
| | | const suggestedPurchase = Math.max(0, weeklyNetDemand); |
| | | |
| | | return { |
| | | productCategory: product.productCategory, |
| | |
| | | inboundNum0: product.inboundNum0, |
| | | inboundNum: product.inboundNum, |
| | | weeklyNetDemand: weeklyNetDemand, |
| | | suggestedPurchase: suggestedPurchase |
| | | } |
| | | }) |
| | | suggestedPurchase: suggestedPurchase, |
| | | }; |
| | | }); |
| | | |
| | | calculateResult.value = result |
| | | calculateDialogVisible.value = true |
| | | } |
| | | |
| | | calculateResult.value = result; |
| | | calculateDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleCreatePurchaseOrder = () => { |
| | | calculateDialogVisible.value = false |
| | | } |
| | | calculateDialogVisible.value = false; |
| | | }; |
| | | const { proxy } = getCurrentInstance(); |
| | | const handleExport = () => { |
| | | ElMessageBox.confirm("内容将被导出,是否确认导出?", "导出", { |
| | |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const handleSizeChange = size => { |
| | | pagination.size = size; |
| | | loadData(); |
| | | }; |
| | | |
| | | const handleSizeChange = (size) => { |
| | | pagination.size = size |
| | | loadData() |
| | | } |
| | | |
| | | const handleCurrentChange = (current) => { |
| | | pagination.current = current |
| | | loadData() |
| | | } |
| | | const handleCurrentChange = current => { |
| | | pagination.current = current; |
| | | loadData(); |
| | | }; |
| | | |
| | | // 生命周期 |
| | | onMounted(() => { |
| | | loadData() |
| | | }) |
| | | loadData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <!-- 搜索区域 --> |
| | | <el-row :gutter="20" class="search-row"> |
| | | <el-row :gutter="20" |
| | | class="search-row"> |
| | | <el-col :span="6"> |
| | | <el-input |
| | | v-model="searchForm.productName" |
| | | <el-input v-model="searchForm.productName" |
| | | placeholder="请输入产品名称" |
| | | clearable |
| | | @keyup.enter="handleSearch" |
| | | > |
| | | @keyup.enter="handleSearch"> |
| | | <template #prefix> |
| | | <el-icon><Search /></el-icon> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | </template> |
| | | </el-input> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.identifierType" placeholder="请选择标识类型" clearable> |
| | | <el-option label="二维码" value="二维码"></el-option> |
| | | <el-option label="防伪码" value="防伪码"></el-option> |
| | | <el-select v-model="searchForm.identifierType" |
| | | placeholder="请选择标识类型" |
| | | clearable> |
| | | <el-option label="二维码" |
| | | value="二维码"></el-option> |
| | | <el-option label="防伪码" |
| | | value="防伪码"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.status" placeholder="请选择状态" clearable> |
| | | <el-option label="已生成" value="已生成"></el-option> |
| | | <el-option label="已分配" value="已分配"></el-option> |
| | | <el-option label="已使用" value="已使用"></el-option> |
| | | <el-option label="已作废" value="已作废"></el-option> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="请选择状态" |
| | | clearable> |
| | | <el-option label="已生成" |
| | | value="已生成"></el-option> |
| | | <el-option label="已分配" |
| | | value="已分配"></el-option> |
| | | <el-option label="已使用" |
| | | value="已使用"></el-option> |
| | | <el-option label="已作废" |
| | | value="已作废"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | <el-button style="float: right;" type="primary" @click="handleAdd"> |
| | | <el-button style="float: right;" |
| | | type="primary" |
| | | @click="handleAdd"> |
| | | 新增标识 |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 产品标识列表 --> |
| | | <el-table |
| | | :data="filteredList" |
| | | <el-table :data="filteredList" |
| | | style="width: 100%" |
| | | v-loading="loading" |
| | | border |
| | | stripe |
| | | height="calc(100vh - 22em)" |
| | | > |
| | | <el-table-column prop="id" label="ID" width="80" align="center"/> |
| | | <el-table-column prop="productName" label="产品名称" width="150" /> |
| | | <el-table-column prop="productCode" label="产品编码" width="120" /> |
| | | <el-table-column prop="batchNo" label="批次号" width="120" /> |
| | | <el-table-column prop="identifierType" label="标识类型" width="100"> |
| | | height="calc(100vh - 22em)"> |
| | | <el-table-column prop="id" |
| | | label="ID" |
| | | width="80" |
| | | align="center" /> |
| | | <el-table-column prop="productName" |
| | | label="产品名称" |
| | | width="150" /> |
| | | <el-table-column prop="productCode" |
| | | label="产品编码" |
| | | width="120" /> |
| | | <el-table-column prop="batchNo" |
| | | label="批次号" |
| | | width="120" /> |
| | | <el-table-column prop="identifierType" |
| | | label="标识类型" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getIdentifierTypeType(scope.row.identifierType)"> |
| | | {{ scope.row.identifierType }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="identifierCode" label="标识码" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <el-table-column prop="identifierCode" |
| | | label="标识码" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ scope.row.status }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="generateTime" label="生成时间" width="160" /> |
| | | <el-table-column label="操作" fixed="right" align="center" width="280"> |
| | | <el-table-column prop="generateTime" |
| | | label="生成时间" |
| | | width="160" /> |
| | | <el-table-column label="操作" |
| | | fixed="right" |
| | | align="center" |
| | | width="280"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="handleView(scope.row)">查看</el-button> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link type="success" @click="generateQRCode(scope.row)">生成二维码</el-button> |
| | | <el-button link type="primary" @click="handleExport(scope.row)">导出</el-button> |
| | | <el-button link type="primary" @click="handleReassign(scope.row)" v-if="scope.row.status === '已分配'">重新分配</el-button> |
| | | <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleView(scope.row)">查看</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link |
| | | type="success" |
| | | @click="generateQRCode(scope.row)">生成二维码</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleExport(scope.row)">导出</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleReassign(scope.row)" |
| | | v-if="scope.row.status === '已分配'">重新分配</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="handleDelete(scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | :total="pagination.total" |
| | | <pagination :total="pagination.total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="pagination.currentPage" |
| | | :limit="pagination.pageSize" |
| | | @pagination="handleCurrentChange" |
| | | /> |
| | | @pagination="handleCurrentChange" /> |
| | | </el-card> |
| | | |
| | | <!-- 新增/编辑对话框 --> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="700px"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产品名称" prop="productName"> |
| | | <el-input v-model="form.productName" placeholder="请输入产品名称"></el-input> |
| | | <el-form-item label="产品名称" |
| | | prop="productName"> |
| | | <el-input v-model="form.productName" |
| | | placeholder="请输入产品名称"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产品编码" prop="productCode"> |
| | | <el-input v-model="form.productCode" placeholder="请输入产品编码"></el-input> |
| | | <el-form-item label="产品编码" |
| | | prop="productCode"> |
| | | <el-input v-model="form.productCode" |
| | | placeholder="请输入产品编码"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="批次号" prop="batchNo"> |
| | | <el-input v-model="form.batchNo" placeholder="请输入批次号"></el-input> |
| | | <el-form-item label="批次号" |
| | | prop="batchNo"> |
| | | <el-input v-model="form.batchNo" |
| | | placeholder="请输入批次号"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="标识类型" prop="identifierType"> |
| | | <el-select v-model="form.identifierType" placeholder="请选择标识类型" style="width: 100%"> |
| | | <el-option label="二维码" value="二维码"></el-option> |
| | | <el-option label="防伪码" value="防伪码"></el-option> |
| | | <el-form-item label="标识类型" |
| | | prop="identifierType"> |
| | | <el-select v-model="form.identifierType" |
| | | placeholder="请选择标识类型" |
| | | style="width: 100%"> |
| | | <el-option label="二维码" |
| | | value="二维码"></el-option> |
| | | <el-option label="防伪码" |
| | | value="防伪码"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="生成数量" prop="quantity"> |
| | | <el-input-number v-model="form.quantity" :min="1" :max="10000" style="width: 100%"></el-input-number> |
| | | <el-form-item label="生成数量" |
| | | prop="quantity"> |
| | | <el-input-number v-model="form.quantity" |
| | | :min="1" |
| | | :max="10000" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%"> |
| | | <el-option label="已生成" value="已生成"></el-option> |
| | | <el-option label="已分配" value="已分配"></el-option> |
| | | <el-option label="已使用" value="已使用"></el-option> |
| | | <el-option label="已作废" value="已作废"></el-option> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="请选择状态" |
| | | style="width: 100%"> |
| | | <el-option label="已生成" |
| | | value="已生成"></el-option> |
| | | <el-option label="已分配" |
| | | value="已分配"></el-option> |
| | | <el-option label="已使用" |
| | | value="已使用"></el-option> |
| | | <el-option label="已作废" |
| | | value="已作废"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input type="textarea" v-model="form.remark" placeholder="请输入备注信息" rows="3"></el-input> |
| | | <el-form-item label="备注" |
| | | prop="remark"> |
| | | <el-input type="textarea" |
| | | v-model="form.remark" |
| | | placeholder="请输入备注信息" |
| | | rows="3"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSubmit">确 定</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="handleSubmit">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 标识生成对话框 --> |
| | | <el-dialog v-model="generateDialogVisible" title="标识生成" width="500px"> |
| | | <el-dialog v-model="generateDialogVisible" |
| | | title="标识生成" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产品名称"> |
| | | <span>{{ currentProduct.productName }}</span> |
| | |
| | | <el-form-item label="标识类型"> |
| | | <span>{{ currentProduct.identifierType }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="生成数量" prop="generateQuantity"> |
| | | <el-input-number v-model="generateQuantity" :min="1" :max="10000" style="width: 100%"></el-input-number> |
| | | <el-form-item label="生成数量" |
| | | prop="generateQuantity"> |
| | | <el-input-number v-model="generateQuantity" |
| | | :min="1" |
| | | :max="10000" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | <el-form-item label="编码规则" prop="codeRule"> |
| | | <el-select v-model="codeRule" placeholder="请选择编码规则" style="width: 100%"> |
| | | <el-option label="产品编码+批次号+序号" value="产品编码+批次号+序号"></el-option> |
| | | <el-option label="时间戳+随机数" value="时间戳+随机数"></el-option> |
| | | <el-option label="自定义规则" value="自定义规则"></el-option> |
| | | <el-form-item label="编码规则" |
| | | prop="codeRule"> |
| | | <el-select v-model="codeRule" |
| | | placeholder="请选择编码规则" |
| | | style="width: 100%"> |
| | | <el-option label="产品编码+批次号+序号" |
| | | value="产品编码+批次号+序号"></el-option> |
| | | <el-option label="时间戳+随机数" |
| | | value="时间戳+随机数"></el-option> |
| | | <el-option label="自定义规则" |
| | | value="自定义规则"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="自定义前缀" prop="customPrefix" v-if="codeRule === '自定义规则'"> |
| | | <el-input v-model="customPrefix" placeholder="请输入自定义前缀"></el-input> |
| | | <el-form-item label="自定义前缀" |
| | | prop="customPrefix" |
| | | v-if="codeRule === '自定义规则'"> |
| | | <el-input v-model="customPrefix" |
| | | placeholder="请输入自定义前缀"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="generateIdentifiers">生 成</el-button> |
| | | <el-button @click="generateDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="generateIdentifiers">生 成</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 重新分配对话框 --> |
| | | <el-dialog v-model="reassignDialogVisible" title="重新分配标识" width="500px"> |
| | | <el-dialog v-model="reassignDialogVisible" |
| | | title="重新分配标识" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产品名称"> |
| | | <span>{{ currentProduct.productName }}</span> |
| | |
| | | <el-form-item label="标识码"> |
| | | <span>{{ currentProduct.identifierCode }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="新批次号" prop="newBatchNo"> |
| | | <el-input v-model="newBatchNo" placeholder="请输入新批次号"></el-input> |
| | | <el-form-item label="新批次号" |
| | | prop="newBatchNo"> |
| | | <el-input v-model="newBatchNo" |
| | | placeholder="请输入新批次号"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="分配原因" prop="reassignReason"> |
| | | <el-input type="textarea" v-model="reassignReason" rows="3" placeholder="请输入重新分配原因"></el-input> |
| | | <el-form-item label="分配原因" |
| | | prop="reassignReason"> |
| | | <el-input type="textarea" |
| | | v-model="reassignReason" |
| | | rows="3" |
| | | placeholder="请输入重新分配原因"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="saveReassign">确 定</el-button> |
| | | <el-button @click="reassignDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="saveReassign">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 二维码预览对话框 --> |
| | | <el-dialog v-model="qrCodeDialogVisible" title="二维码预览" width="500px" center> |
| | | <el-dialog v-model="qrCodeDialogVisible" |
| | | title="二维码预览" |
| | | width="500px" |
| | | center> |
| | | <div class="qr-preview-container"> |
| | | <div v-if="qrCodeUrl" class="qr-image-container"> |
| | | <img :src="qrCodeUrl" alt="二维码" class="qr-image" /> |
| | | <div v-if="qrCodeUrl" |
| | | class="qr-image-container"> |
| | | <img :src="qrCodeUrl" |
| | | alt="二维码" |
| | | class="qr-image" /> |
| | | <div class="qr-info"> |
| | | <p><strong>产品名称:</strong>{{ currentQRProduct.productName }}</p> |
| | | <p><strong>产品编码:</strong>{{ currentQRProduct.productCode }}</p> |
| | |
| | | <p><strong>标识类型:</strong>{{ currentQRProduct.identifierType }}</p> |
| | | </div> |
| | | </div> |
| | | <div v-else class="qr-loading"> |
| | | <el-icon class="is-loading"><Loading /></el-icon> |
| | | <div v-else |
| | | class="qr-loading"> |
| | | <el-icon class="is-loading"> |
| | | <Loading /> |
| | | </el-icon> |
| | | <p>正在生成二维码...</p> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="qrCodeDialogVisible = false">关闭</el-button> |
| | | <el-button |
| | | v-if="qrCodeUrl" |
| | | <el-button v-if="qrCodeUrl" |
| | | type="primary" |
| | | @click="copyQRContent" |
| | | icon="CopyDocument" |
| | | > |
| | | icon="CopyDocument"> |
| | | 复制内容 |
| | | </el-button> |
| | | <el-button |
| | | v-if="qrCodeUrl" |
| | | <el-button v-if="qrCodeUrl" |
| | | type="success" |
| | | @click="downloadQRCode" |
| | | icon="Download" |
| | | > |
| | | icon="Download"> |
| | | 下载二维码 |
| | | </el-button> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus, Search, Loading, Download } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | import QRCode from 'qrcode' |
| | | import { ref, reactive, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Search, Loading, Download } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import QRCode from "qrcode"; |
| | | |
| | | // 响应式数据 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | const searchForm = reactive({ |
| | | productName: '', |
| | | identifierType: '', |
| | | status: '' |
| | | }) |
| | | productName: "", |
| | | identifierType: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const identifierList = ref([ |
| | | { |
| | | id: 1, |
| | | productName: '工业传感器A型', |
| | | productCode: 'SENSOR001', |
| | | batchNo: 'B202312001', |
| | | identifierType: '二维码', |
| | | identifierCode: 'QR_SENSOR001_B202312001_001', |
| | | status: '已分配', |
| | | generateTime: '2023-12-01 10:00:00', |
| | | remark: '重要产品标识' |
| | | productName: "工业传感器A型", |
| | | productCode: "SENSOR001", |
| | | batchNo: "B202312001", |
| | | identifierType: "二维码", |
| | | identifierCode: "QR_SENSOR001_B202312001_001", |
| | | status: "已分配", |
| | | generateTime: "2023-12-01 10:00:00", |
| | | remark: "重要产品标识", |
| | | }, |
| | | { |
| | | id: 2, |
| | | productName: '控制面板B型', |
| | | productCode: 'PANEL002', |
| | | batchNo: 'B202312002', |
| | | identifierType: '防伪码', |
| | | identifierCode: 'SEC_PANEL002_B202312002_001', |
| | | status: '已生成', |
| | | generateTime: '2023-12-02 14:30:00', |
| | | remark: '常规产品标识' |
| | | productName: "控制面板B型", |
| | | productCode: "PANEL002", |
| | | batchNo: "B202312002", |
| | | identifierType: "防伪码", |
| | | identifierCode: "SEC_PANEL002_B202312002_001", |
| | | status: "已生成", |
| | | generateTime: "2023-12-02 14:30:00", |
| | | remark: "常规产品标识", |
| | | }, |
| | | { |
| | | id: 3, |
| | | productName: '数据采集器C型', |
| | | productCode: 'COLLECTOR003', |
| | | batchNo: 'B202312003', |
| | | identifierType: '防伪码', |
| | | identifierCode: 'SEC_COLLECTOR003_B202312003_001', |
| | | status: '已使用', |
| | | generateTime: '2023-12-03 09:15:00', |
| | | remark: '测试产品标识' |
| | | } |
| | | ]) |
| | | productName: "数据采集器C型", |
| | | productCode: "COLLECTOR003", |
| | | batchNo: "B202312003", |
| | | identifierType: "防伪码", |
| | | identifierCode: "SEC_COLLECTOR003_B202312003_001", |
| | | status: "已使用", |
| | | generateTime: "2023-12-03 09:15:00", |
| | | remark: "测试产品标识", |
| | | }, |
| | | ]); |
| | | |
| | | const pagination = reactive({ |
| | | total: 3, |
| | | currentPage: 1, |
| | | pageSize: 10 |
| | | }) |
| | | pageSize: 10, |
| | | }); |
| | | |
| | | const dialogVisible = ref(false) |
| | | const dialogTitle = ref('新增标识') |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref("新增标识"); |
| | | const form = reactive({ |
| | | productName: '', |
| | | productCode: '', |
| | | batchNo: '', |
| | | identifierType: '', |
| | | productName: "", |
| | | productCode: "", |
| | | batchNo: "", |
| | | identifierType: "", |
| | | quantity: 1, |
| | | status: '已生成', |
| | | remark: '' |
| | | }) |
| | | status: "已生成", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }], |
| | | productCode: [{ required: true, message: '请输入产品编码', trigger: 'blur' }], |
| | | batchNo: [{ required: true, message: '请输入批次号', trigger: 'blur' }], |
| | | identifierType: [{ required: true, message: '请选择标识类型', trigger: 'change' }], |
| | | quantity: [{ required: true, message: '请输入生成数量', trigger: 'blur' }], |
| | | status: [{ required: true, message: '请选择状态', trigger: 'change' }] |
| | | } |
| | | productName: [{ required: true, message: "请输入产品名称", trigger: "blur" }], |
| | | productCode: [{ required: true, message: "请输入产品编码", trigger: "blur" }], |
| | | batchNo: [{ required: true, message: "请输入批次号", trigger: "blur" }], |
| | | identifierType: [ |
| | | { required: true, message: "请选择标识类型", trigger: "change" }, |
| | | ], |
| | | quantity: [{ required: true, message: "请输入生成数量", trigger: "blur" }], |
| | | status: [{ required: true, message: "请选择状态", trigger: "change" }], |
| | | }; |
| | | |
| | | const isEdit = ref(false) |
| | | const editId = ref(null) |
| | | const generateDialogVisible = ref(false) |
| | | const reassignDialogVisible = ref(false) |
| | | const currentProduct = ref({}) |
| | | const generateQuantity = ref(1) |
| | | const codeRule = ref('') |
| | | const customPrefix = ref('') |
| | | const newBatchNo = ref('') |
| | | const reassignReason = ref('') |
| | | const formRef = ref() |
| | | const isEdit = ref(false); |
| | | const editId = ref(null); |
| | | const generateDialogVisible = ref(false); |
| | | const reassignDialogVisible = ref(false); |
| | | const currentProduct = ref({}); |
| | | const generateQuantity = ref(1); |
| | | const codeRule = ref(""); |
| | | const customPrefix = ref(""); |
| | | const newBatchNo = ref(""); |
| | | const reassignReason = ref(""); |
| | | const formRef = ref(); |
| | | |
| | | // 二维码相关变量 |
| | | const qrCodeDialogVisible = ref(false) |
| | | const qrCodeUrl = ref('') |
| | | const currentQRProduct = ref({}) |
| | | const qrCodeDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | const currentQRProduct = ref({}); |
| | | |
| | | // 计算属性 |
| | | const filteredList = computed(() => { |
| | | let list = identifierList.value |
| | | let list = identifierList.value; |
| | | if (searchForm.productName) { |
| | | list = list.filter(item => item.productName.includes(searchForm.productName)) |
| | | list = list.filter(item => |
| | | item.productName.includes(searchForm.productName) |
| | | ); |
| | | } |
| | | if (searchForm.identifierType) { |
| | | list = list.filter(item => item.identifierType === searchForm.identifierType) |
| | | list = list.filter( |
| | | item => item.identifierType === searchForm.identifierType |
| | | ); |
| | | } |
| | | if (searchForm.status) { |
| | | list = list.filter(item => item.status === searchForm.status) |
| | | list = list.filter(item => item.status === searchForm.status); |
| | | } |
| | | return list |
| | | }) |
| | | return list; |
| | | }); |
| | | |
| | | // 方法 |
| | | const getIdentifierTypeType = (type) => { |
| | | const getIdentifierTypeType = type => { |
| | | const typeMap = { |
| | | '二维码': 'success', |
| | | '防伪码': 'warning' |
| | | } |
| | | return typeMap[type] || 'info' |
| | | } |
| | | 二维码: "success", |
| | | 防伪码: "warning", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '已生成': 'info', |
| | | '已分配': 'primary', |
| | | '已使用': 'success', |
| | | '已作废': 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 已生成: "info", |
| | | 已分配: "primary", |
| | | 已使用: "success", |
| | | 已作废: "danger", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | // 搜索逻辑已在computed中处理 |
| | | } |
| | | }; |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.productName = '' |
| | | searchForm.identifierType = '' |
| | | searchForm.status = '' |
| | | } |
| | | searchForm.productName = ""; |
| | | searchForm.identifierType = ""; |
| | | searchForm.status = ""; |
| | | }; |
| | | |
| | | const handleAdd = () => { |
| | | dialogTitle.value = '新增标识' |
| | | isEdit.value = false |
| | | form.productName = '' |
| | | form.productCode = '' |
| | | form.batchNo = '' |
| | | form.identifierType = '' |
| | | form.quantity = 1 |
| | | form.status = '已生成' |
| | | form.remark = '' |
| | | dialogVisible.value = true |
| | | } |
| | | dialogTitle.value = "新增标识"; |
| | | isEdit.value = false; |
| | | form.productName = ""; |
| | | form.productCode = ""; |
| | | form.batchNo = ""; |
| | | form.identifierType = ""; |
| | | form.quantity = 1; |
| | | form.status = "已生成"; |
| | | form.remark = ""; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleView = (row) => { |
| | | const handleView = row => { |
| | | // 查看标识详情 |
| | | ElMessage.info('查看标识详情功能待实现') |
| | | } |
| | | ElMessage.info("查看标识详情功能待实现"); |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | dialogTitle.value = '编辑标识' |
| | | isEdit.value = true |
| | | editId.value = row.id |
| | | Object.assign(form, row) |
| | | dialogVisible.value = true |
| | | } |
| | | const handleEdit = row => { |
| | | dialogTitle.value = "编辑标识"; |
| | | isEdit.value = true; |
| | | editId.value = row.id; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleExport = (row) => { |
| | | const handleExport = row => { |
| | | // 导出标识 |
| | | ElMessage.success(`已导出标识: ${row.identifierCode}`) |
| | | } |
| | | ElMessage.success(`已导出标识: ${row.identifierCode}`); |
| | | }; |
| | | |
| | | const handleReassign = (row) => { |
| | | currentProduct.value = row |
| | | newBatchNo.value = '' |
| | | reassignReason.value = '' |
| | | reassignDialogVisible.value = true |
| | | } |
| | | const handleReassign = row => { |
| | | currentProduct.value = row; |
| | | newBatchNo.value = ""; |
| | | reassignReason.value = ""; |
| | | reassignDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm('确认删除该标识吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("确认删除该标识吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = identifierList.value.findIndex(item => item.id === row.id) |
| | | const index = identifierList.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | identifierList.value.splice(index, 1) |
| | | pagination.total-- |
| | | ElMessage.success('删除成功') |
| | | identifierList.value.splice(index, 1); |
| | | pagination.total--; |
| | | ElMessage.success("删除成功"); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 生成二维码 |
| | | const generateQRCode = async (row) => { |
| | | const generateQRCode = async row => { |
| | | try { |
| | | // 检查必要字段 |
| | | if (!row.productName || !row.productCode || !row.batchNo) { |
| | | ElMessage.warning('产品信息不完整,无法生成二维码') |
| | | return |
| | | ElMessage.warning("产品信息不完整,无法生成二维码"); |
| | | return; |
| | | } |
| | | |
| | | currentQRProduct.value = row |
| | | qrCodeUrl.value = '' |
| | | qrCodeDialogVisible.value = true |
| | | currentQRProduct.value = row; |
| | | qrCodeUrl.value = ""; |
| | | qrCodeDialogVisible.value = true; |
| | | |
| | | // 构建二维码内容 |
| | | let qrContent = '' |
| | | if (row.identifierType === '二维码') { |
| | | qrContent = `${row.productName}|${row.productCode}|${row.batchNo}|${row.identifierCode}` |
| | | } else if (row.identifierType === '防伪码') { |
| | | let qrContent = ""; |
| | | if (row.identifierType === "二维码") { |
| | | qrContent = `${row.productName}|${row.productCode}|${row.batchNo}|${row.identifierCode}`; |
| | | } else if (row.identifierType === "防伪码") { |
| | | // 防伪码格式:SEC_产品编码_批次号_时间戳_随机数 |
| | | const timestamp = Date.now() |
| | | const random = Math.random().toString(36).substr(2, 8) |
| | | qrContent = `SEC_${row.productCode}_${row.batchNo}_${timestamp}_${random}` |
| | | const timestamp = Date.now(); |
| | | const random = Math.random().toString(36).substr(2, 8); |
| | | qrContent = `SEC_${row.productCode}_${row.batchNo}_${timestamp}_${random}`; |
| | | } |
| | | |
| | | // 生成二维码 |
| | |
| | | width: 256, |
| | | margin: 2, |
| | | color: { |
| | | dark: '#000000', |
| | | light: '#FFFFFF' |
| | | dark: "#000000", |
| | | light: "#FFFFFF", |
| | | }, |
| | | errorCorrectionLevel: row.identifierType === '防伪码' ? 'H' : 'M' |
| | | }) |
| | | errorCorrectionLevel: row.identifierType === "防伪码" ? "H" : "M", |
| | | }); |
| | | |
| | | ElMessage.success('二维码生成成功!') |
| | | |
| | | ElMessage.success("二维码生成成功!"); |
| | | } catch (error) { |
| | | console.error('生成二维码失败:', error) |
| | | ElMessage.error('生成二维码失败:' + error.message) |
| | | qrCodeDialogVisible.value = false |
| | | console.error("生成二维码失败:", error); |
| | | ElMessage.error("生成二维码失败:" + error.message); |
| | | qrCodeDialogVisible.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 下载二维码 |
| | | const downloadQRCode = () => { |
| | | if (!qrCodeUrl.value) { |
| | | ElMessage.warning('请先生成二维码') |
| | | return |
| | | ElMessage.warning("请先生成二维码"); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement('a') |
| | | a.href = qrCodeUrl.value |
| | | a.download = `${currentQRProduct.value.productName}_${currentQRProduct.value.identifierType}_${new Date().getTime()}.png` |
| | | document.body.appendChild(a) |
| | | a.click() |
| | | document.body.removeChild(a) |
| | | ElMessage.success('下载成功!') |
| | | } |
| | | const a = document.createElement("a"); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `${currentQRProduct.value.productName}_${ |
| | | currentQRProduct.value.identifierType |
| | | }_${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | ElMessage.success("下载成功!"); |
| | | }; |
| | | |
| | | // 复制二维码内容 |
| | | const copyQRContent = async () => { |
| | | if (!currentQRProduct.value) { |
| | | ElMessage.warning('没有可复制的内容') |
| | | return |
| | | ElMessage.warning("没有可复制的内容"); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | let content = '' |
| | | if (currentQRProduct.value.identifierType === '二维码') { |
| | | content = `${currentQRProduct.value.productName}|${currentQRProduct.value.productCode}|${currentQRProduct.value.batchNo}|${currentQRProduct.value.identifierCode}` |
| | | } else if (currentQRProduct.value.identifierType === '防伪码') { |
| | | const timestamp = Date.now() |
| | | const random = Math.random().toString(36).substr(2, 8) |
| | | content = `SEC_${currentQRProduct.value.productCode}_${currentQRProduct.value.batchNo}_${timestamp}_${random}` |
| | | let content = ""; |
| | | if (currentQRProduct.value.identifierType === "二维码") { |
| | | content = `${currentQRProduct.value.productName}|${currentQRProduct.value.productCode}|${currentQRProduct.value.batchNo}|${currentQRProduct.value.identifierCode}`; |
| | | } else if (currentQRProduct.value.identifierType === "防伪码") { |
| | | const timestamp = Date.now(); |
| | | const random = Math.random().toString(36).substr(2, 8); |
| | | content = `SEC_${currentQRProduct.value.productCode}_${currentQRProduct.value.batchNo}_${timestamp}_${random}`; |
| | | } |
| | | |
| | | await navigator.clipboard.writeText(content) |
| | | ElMessage.success('内容已复制到剪贴板') |
| | | await navigator.clipboard.writeText(content); |
| | | ElMessage.success("内容已复制到剪贴板"); |
| | | } catch (error) { |
| | | // 降级方案 |
| | | const textArea = document.createElement('textarea') |
| | | textArea.value = content |
| | | document.body.appendChild(textArea) |
| | | textArea.select() |
| | | document.execCommand('copy') |
| | | document.body.removeChild(textArea) |
| | | ElMessage.success('内容已复制到剪贴板') |
| | | const textArea = document.createElement("textarea"); |
| | | textArea.value = content; |
| | | document.body.appendChild(textArea); |
| | | textArea.select(); |
| | | document.execCommand("copy"); |
| | | document.body.removeChild(textArea); |
| | | ElMessage.success("内容已复制到剪贴板"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const generateIdentifiers = () => { |
| | | if (!codeRule.value) { |
| | | ElMessage.warning('请选择编码规则') |
| | | return |
| | | ElMessage.warning("请选择编码规则"); |
| | | return; |
| | | } |
| | | |
| | | // 生成标识的逻辑 |
| | | const newIdentifiers = [] |
| | | const newIdentifiers = []; |
| | | for (let i = 1; i <= generateQuantity.value; i++) { |
| | | let identifierCode = '' |
| | | if (codeRule.value === '产品编码+批次号+序号') { |
| | | identifierCode = `${currentProduct.value.productCode}_${currentProduct.value.batchNo}_${String(i).padStart(3, '0')}` |
| | | } else if (codeRule.value === '时间戳+随机数') { |
| | | identifierCode = `TS_${Date.now()}_${Math.floor(Math.random() * 1000)}` |
| | | } else if (codeRule.value === '自定义规则') { |
| | | identifierCode = `${customPrefix.value || 'CUSTOM'}_${Date.now()}_${i}` |
| | | let identifierCode = ""; |
| | | if (codeRule.value === "产品编码+批次号+序号") { |
| | | identifierCode = `${currentProduct.value.productCode}_${ |
| | | currentProduct.value.batchNo |
| | | }_${String(i).padStart(3, "0")}`; |
| | | } else if (codeRule.value === "时间戳+随机数") { |
| | | identifierCode = `TS_${Date.now()}_${Math.floor(Math.random() * 1000)}`; |
| | | } else if (codeRule.value === "自定义规则") { |
| | | identifierCode = `${customPrefix.value || "CUSTOM"}_${Date.now()}_${i}`; |
| | | } |
| | | |
| | | newIdentifiers.push({ |
| | |
| | | batchNo: currentProduct.value.batchNo, |
| | | identifierType: currentProduct.value.identifierType, |
| | | identifierCode: identifierCode, |
| | | status: '已生成', |
| | | status: "已生成", |
| | | generateTime: new Date().toLocaleString(), |
| | | remark: '批量生成' |
| | | }) |
| | | remark: "批量生成", |
| | | }); |
| | | } |
| | | |
| | | identifierList.value.push(...newIdentifiers) |
| | | pagination.total += newIdentifiers.length |
| | | ElMessage.success(`成功生成 ${newIdentifiers.length} 个标识`) |
| | | generateDialogVisible.value = false |
| | | } |
| | | identifierList.value.push(...newIdentifiers); |
| | | pagination.total += newIdentifiers.length; |
| | | ElMessage.success(`成功生成 ${newIdentifiers.length} 个标识`); |
| | | generateDialogVisible.value = false; |
| | | }; |
| | | |
| | | const saveReassign = () => { |
| | | if (!newBatchNo.value) { |
| | | ElMessage.warning('请输入新批次号') |
| | | return |
| | | ElMessage.warning("请输入新批次号"); |
| | | return; |
| | | } |
| | | |
| | | const index = identifierList.value.findIndex(item => item.id === currentProduct.value.id) |
| | | const index = identifierList.value.findIndex( |
| | | item => item.id === currentProduct.value.id |
| | | ); |
| | | if (index > -1) { |
| | | identifierList.value[index].batchNo = newBatchNo.value |
| | | identifierList.value[index].status = '已分配' |
| | | ElMessage.success('标识重新分配成功') |
| | | reassignDialogVisible.value = false |
| | | identifierList.value[index].batchNo = newBatchNo.value; |
| | | identifierList.value[index].status = "已分配"; |
| | | ElMessage.success("标识重新分配成功"); |
| | | reassignDialogVisible.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate((valid) => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (isEdit.value) { |
| | | // 编辑 |
| | | const index = identifierList.value.findIndex(item => item.id === editId.value) |
| | | const index = identifierList.value.findIndex( |
| | | item => item.id === editId.value |
| | | ); |
| | | if (index > -1) { |
| | | identifierList.value[index] = { ...form, id: editId.value } |
| | | ElMessage.success('编辑成功') |
| | | identifierList.value[index] = { ...form, id: editId.value }; |
| | | ElMessage.success("编辑成功"); |
| | | } |
| | | } else { |
| | | // 新增 |
| | | const newId = Math.max(...identifierList.value.map(item => item.id)) + 1 |
| | | const newId = |
| | | Math.max(...identifierList.value.map(item => item.id)) + 1; |
| | | |
| | | // 根据标识类型生成不同的标识码 |
| | | let identifierCode = '' |
| | | if (form.identifierType === '二维码') { |
| | | identifierCode = `QR_${form.productCode}_${form.batchNo}_001` |
| | | } else if (form.identifierType === '防伪码') { |
| | | identifierCode = `SEC_${form.productCode}_${form.batchNo}_001` |
| | | let identifierCode = ""; |
| | | if (form.identifierType === "二维码") { |
| | | identifierCode = `QR_${form.productCode}_${form.batchNo}_001`; |
| | | } else if (form.identifierType === "防伪码") { |
| | | identifierCode = `SEC_${form.productCode}_${form.batchNo}_001`; |
| | | } |
| | | |
| | | identifierList.value.push({ |
| | | ...form, |
| | | id: newId, |
| | | identifierCode: identifierCode, |
| | | generateTime: new Date().toLocaleString() |
| | | }) |
| | | pagination.total++ |
| | | ElMessage.success('新增成功') |
| | | generateTime: new Date().toLocaleString(), |
| | | }); |
| | | pagination.total++; |
| | | ElMessage.success("新增成功"); |
| | | } |
| | | dialogVisible.value = false |
| | | dialogVisible.value = false; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = (val) => { |
| | | pagination.currentPage = val.page |
| | | pagination.pageSize = val.limit |
| | | } |
| | | const handleCurrentChange = val => { |
| | | pagination.currentPage = val.page; |
| | | pagination.pageSize = val.limit; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | |
| | | .qr-loading .el-icon { |
| | | font-size: 32px; |
| | | color: #409EFF; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .qr-loading p { |
| | |
| | | <template> |
| | | <el-dialog |
| | | v-model="visible" |
| | | <el-dialog v-model="visible" |
| | | title="结构" |
| | | width="1200" |
| | | close-on-click-modal |
| | | @close="visible = false" |
| | | > |
| | | <el-button |
| | | v-if="dataValue.isEdit" |
| | | @close="visible = false"> |
| | | <el-button v-if="dataValue.isEdit" |
| | | type="primary" |
| | | @click="addItem" |
| | | style="margin-bottom: 10px" |
| | | >添加 |
| | | style="margin-bottom: 10px">添加 |
| | | </el-button> |
| | | <el-button |
| | | v-if="!dataValue.isEdit" |
| | | <el-button v-if="!dataValue.isEdit" |
| | | type="primary" |
| | | @click="dataValue.isEdit = true" |
| | | style="margin-bottom: 10px" |
| | | >编辑 |
| | | style="margin-bottom: 10px">编辑 |
| | | </el-button> |
| | | <el-button |
| | | v-if="dataValue.isEdit" |
| | | <el-button v-if="dataValue.isEdit" |
| | | type="primary" |
| | | @click="cancelEdit" |
| | | style="margin-bottom: 10px" |
| | | >取消 |
| | | style="margin-bottom: 10px">取消 |
| | | </el-button> |
| | | <el-form |
| | | ref="form" |
| | | :model="dataValue" |
| | | > |
| | | <el-table :data="dataValue.dataList" style="width: 100%"> |
| | | <el-table-column prop="productName" label="产品" width="150"/> |
| | | <el-table-column prop="model" label="规格" width="150"> |
| | | <el-form ref="form" |
| | | :model="dataValue"> |
| | | <el-table :data="dataValue.dataList" |
| | | style="width: 100%"> |
| | | <el-table-column prop="productName" |
| | | label="产品" |
| | | width="150" /> |
| | | <el-table-column prop="model" |
| | | label="规格" |
| | | width="150"> |
| | | <template #default="{ row, $index }"> |
| | | <el-form-item |
| | | v-if="dataValue.isEdit" |
| | | <el-form-item v-if="dataValue.isEdit" |
| | | :prop="`dataList.${$index}.model`" |
| | | :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]" |
| | | style="margin: 0" |
| | | > |
| | | <el-select |
| | | v-model="row.model" |
| | | style="margin: 0"> |
| | | <el-select v-model="row.model" |
| | | placeholder="请选择产品" |
| | | clearable |
| | | :disabled="!dataValue.isEdit" |
| | | style="width: 100%" |
| | | @visible-change="(v) => { if (v) openDialog($index) }" |
| | | > |
| | | <el-option v-if="row.model" :label="row.model" :value="row.model" /> |
| | | @visible-change="(v) => { if (v) openDialog($index) }"> |
| | | <el-option v-if="row.model" |
| | | :label="row.model" |
| | | :value="row.model" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="processId" label="消耗工序" width="150"> |
| | | <el-table-column prop="processId" |
| | | label="消耗工序" |
| | | width="150"> |
| | | <template #default="{ row, $index }"> |
| | | <el-form-item |
| | | :prop="`dataList.${$index}.processId`" |
| | | <el-form-item :prop="`dataList.${$index}.processId`" |
| | | :rules="[{ required: true, message: '请选择消耗工序', trigger: 'change' }]" |
| | | style="margin: 0" |
| | | > |
| | | <el-select |
| | | v-model="row.processId" |
| | | style="margin: 0"> |
| | | <el-select v-model="row.processId" |
| | | placeholder="请选择" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="!dataValue.isEdit" |
| | | > |
| | | <el-option |
| | | v-for="item in dataValue.processOptions" |
| | | :disabled="!dataValue.isEdit"> |
| | | <el-option v-for="item in dataValue.processOptions" |
| | | :key="item.id" |
| | | :label="item.name" |
| | | :value="item.id" |
| | | /> |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="unitQuantity" label="单位产出所需数量" width="150"> |
| | | <el-table-column prop="unitQuantity" |
| | | label="单位产出所需数量" |
| | | width="150"> |
| | | <template #default="{ row, $index }"> |
| | | <el-form-item |
| | | :prop="`dataList.${$index}.unitQuantity`" |
| | | <el-form-item :prop="`dataList.${$index}.unitQuantity`" |
| | | :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]" |
| | | style="margin: 0" |
| | | > |
| | | <el-input-number |
| | | v-model="row.unitQuantity" |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | | :disabled="!dataValue.isEdit" |
| | | /> |
| | | :disabled="!dataValue.isEdit" /> |
| | | </el-form-item> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="demandedQuantity" label="需求总量" width="150"> |
| | | <el-table-column prop="demandedQuantity" |
| | | label="需求总量" |
| | | width="150"> |
| | | <template #default="{ row, $index }"> |
| | | <el-form-item |
| | | :prop="`dataList.${$index}.demandedQuantity`" |
| | | <el-form-item :prop="`dataList.${$index}.demandedQuantity`" |
| | | :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur','change'] }]" |
| | | style="margin: 0" |
| | | > |
| | | <el-input-number |
| | | v-model="row.demandedQuantity" |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | | :disabled="!dataValue.isEdit" |
| | | /> |
| | | :disabled="!dataValue.isEdit" /> |
| | | </el-form-item> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="unit" label="单位" width="150"> |
| | | <el-table-column prop="unit" |
| | | label="单位" |
| | | width="150"> |
| | | <template #default="{ row, $index }"> |
| | | <el-form-item |
| | | :prop="`dataList.${$index}.unit`" |
| | | <el-form-item :prop="`dataList.${$index}.unit`" |
| | | :rules="[{ required: true, message: '请输入单位', trigger: ['blur','change'] }]" |
| | | style="margin: 0" |
| | | > |
| | | <el-input |
| | | v-model="row.unit" |
| | | style="margin: 0"> |
| | | <el-input v-model="row.unit" |
| | | placeholder="请输入单位" |
| | | clearable |
| | | :disabled="!dataValue.isEdit" |
| | | /> |
| | | :disabled="!dataValue.isEdit" /> |
| | | </el-form-item> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="diskQuantity" label="盘数(盘)" width="150"> |
| | | <el-table-column prop="diskQuantity" |
| | | label="盘数(盘)" |
| | | width="150"> |
| | | <template #default="{ row, $index }"> |
| | | <el-form-item |
| | | :prop="`dataList.${$index}.diskQuantity`" |
| | | <el-form-item :prop="`dataList.${$index}.diskQuantity`" |
| | | :rules="[{ required: true, message: '请输入盘数', trigger: ['blur','change'] }]" |
| | | style="margin: 0" |
| | | > |
| | | <el-input-number |
| | | v-model="row.diskQuantity" |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.diskQuantity" |
| | | :min="0" |
| | | :precision="0" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | | :disabled="!dataValue.isEdit" |
| | | /> |
| | | :disabled="!dataValue.isEdit" /> |
| | | </el-form-item> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="操作"> |
| | | <template #default="{ row, $index }"> |
| | | <el-button |
| | | type="danger" |
| | | <el-button type="danger" |
| | | text |
| | | @click="dataValue.dataList.splice($index, 1)" |
| | | >删除 |
| | | @click="dataValue.dataList.splice($index, 1)">删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form> |
| | | |
| | | <product-select-dialog |
| | | v-if="dataValue.showProductDialog" |
| | | <product-select-dialog v-if="dataValue.showProductDialog" |
| | | v-model:model-value="dataValue.showProductDialog" |
| | | @confirm="handleProduct"/> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="visible = false">取消</el-button> |
| | | <el-button type="primary" :loading="dataValue.loading" @click="submit" :disabled="!dataValue.isEdit"> |
| | | <el-button type="primary" |
| | | :loading="dataValue.loading" |
| | | @click="submit" |
| | | :disabled="!dataValue.isEdit"> |
| | | 确认 |
| | | </el-button> |
| | | <el-button @click="visible = false">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import {computed, defineAsyncComponent, defineComponent, onMounted, reactive, ref} from "vue"; |
| | | import {queryList, add} from '@/api/productionManagement/productStructure.js' |
| | | import {list} from '@/api/productionManagement/productionProcess' |
| | | import { |
| | | computed, |
| | | defineAsyncComponent, |
| | | defineComponent, |
| | | onMounted, |
| | | reactive, |
| | | ref, |
| | | } from "vue"; |
| | | import { queryList, add } from "@/api/productionManagement/productStructure.js"; |
| | | import { list } from "@/api/productionManagement/productionProcess"; |
| | | import {ElMessage} from "element-plus"; |
| | | |
| | | |
| | | defineComponent({ |
| | | name: "StructureEdit", |
| | | }) |
| | | }); |
| | | |
| | | const ProductSelectDialog = defineAsyncComponent(() => import('@/views/basicData/product/ProductSelectDialog.vue')) |
| | | const form = ref() |
| | | |
| | | |
| | | const ProductSelectDialog = defineAsyncComponent( |
| | | () => import("@/views/basicData/product/ProductSelectDialog.vue") |
| | | ); |
| | | const form = ref(); |
| | | |
| | | const props = defineProps({ |
| | | showModel: { |
| | | type: Boolean, |
| | | default: false |
| | | default: false, |
| | | }, |
| | | productModelId: { |
| | | type: Number, |
| | | required: true |
| | | } |
| | | }) |
| | | required: true, |
| | | }, |
| | | }); |
| | | |
| | | const emits = defineEmits(['update:showModel']) |
| | | const emits = defineEmits(["update:showModel"]); |
| | | const visible = computed({ |
| | | get() { |
| | | return props.showModel |
| | | return props.showModel; |
| | | }, |
| | | set(val) { |
| | | emits('update:showModel', val) |
| | | } |
| | | }) |
| | | emits("update:showModel", val); |
| | | }, |
| | | }); |
| | | |
| | | const dataValue = reactive({ |
| | | dataList: [], |
| | |
| | | isEdit: false, |
| | | }); |
| | | |
| | | const openDialog = (index) => { |
| | | dataValue.currentRowIndex = index |
| | | dataValue.showProductDialog = true |
| | | } |
| | | const openDialog = index => { |
| | | dataValue.currentRowIndex = index; |
| | | dataValue.showProductDialog = true; |
| | | }; |
| | | |
| | | const fetchData = async () => { |
| | | const {data} = await queryList(props.productModelId) |
| | | dataValue.dataList = data |
| | | } |
| | | const { data } = await queryList(props.productModelId); |
| | | dataValue.dataList = data; |
| | | }; |
| | | |
| | | const fetchProcessOptions = async () => { |
| | | const {data} = await list(props.productModelId) |
| | | dataValue.processOptions = data |
| | | } |
| | | const { data } = await list(props.productModelId); |
| | | dataValue.processOptions = data; |
| | | }; |
| | | |
| | | const handleProduct = (row) => { |
| | | const handleProduct = row => { |
| | | if (row?.length > 1) { |
| | | ElMessage.error('只能选择一个产品') |
| | | ElMessage.error("只能选择一个产品"); |
| | | } |
| | | dataValue.dataList[dataValue.currentRowIndex].productName = row[0].productName |
| | | dataValue.dataList[dataValue.currentRowIndex].model = row[0].model |
| | | dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id |
| | | dataValue.showProductDialog = false |
| | | } |
| | | dataValue.dataList[dataValue.currentRowIndex].productName = |
| | | row[0].productName; |
| | | dataValue.dataList[dataValue.currentRowIndex].model = row[0].model; |
| | | dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id; |
| | | dataValue.showProductDialog = false; |
| | | }; |
| | | |
| | | const submit = () => { |
| | | form.value.validate(valid => { |
| | | dataValue.loading = true |
| | | form.value |
| | | .validate(valid => { |
| | | dataValue.loading = true; |
| | | if (valid) { |
| | | add({ |
| | | parentId: props.productModelId, |
| | | productStructureList: dataValue.dataList || [] |
| | | productStructureList: dataValue.dataList || [], |
| | | }).then(res => { |
| | | ElMessage.success('保存成功') |
| | | visible.value = false |
| | | dataValue.loading = false |
| | | }) |
| | | ElMessage.success("保存成功"); |
| | | visible.value = false; |
| | | dataValue.loading = false; |
| | | }); |
| | | } |
| | | }).finally(() => { |
| | | dataValue.loading = false |
| | | }) |
| | | } |
| | | .finally(() => { |
| | | dataValue.loading = false; |
| | | }); |
| | | }; |
| | | |
| | | const addItem = () => { |
| | | dataValue.dataList.push({ |
| | | productName: '', |
| | | productId: '', |
| | | productName: "", |
| | | productId: "", |
| | | model: undefined, |
| | | productModelId: undefined, |
| | | processId: '', |
| | | processId: "", |
| | | unitQuantity: 0, |
| | | demandedQuantity: 0, |
| | | unit: '', |
| | | unit: "", |
| | | diskQuantity: 0, |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const cancelEdit = () => { |
| | | dataValue.isEdit = false |
| | | dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined) |
| | | } |
| | | dataValue.isEdit = false; |
| | | dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchData() |
| | | fetchProcessOptions() |
| | | }) |
| | | |
| | | |
| | | fetchData(); |
| | | fetchProcessOptions(); |
| | | }); |
| | | </script> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="editDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" |
| | | @click="handleUpdate">确定</el-button> |
| | | <el-button @click="editDialogVisible = false">取消</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="reportDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" |
| | | @click="handleReport">确定</el-button> |
| | | <el-button @click="reportDialogVisible = false">取消</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <!-- 搜索区域 --> |
| | | <el-row :gutter="20" class="search-row"> |
| | | <el-row :gutter="20" |
| | | class="search-row"> |
| | | <el-col :span="6"> |
| | | <el-input |
| | | v-model="searchForm.name" |
| | | <el-input v-model="searchForm.name" |
| | | placeholder="请输入客户名称" |
| | | clearable |
| | | @keyup.enter="handleSearch" |
| | | > |
| | | @keyup.enter="handleSearch"> |
| | | <template #prefix> |
| | | <el-icon><Search /></el-icon> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | </template> |
| | | </el-input> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.region" placeholder="请选择区域" clearable> |
| | | <el-option label="华东区" value="华东区"></el-option> |
| | | <el-option label="华南区" value="华南区"></el-option> |
| | | <el-option label="华北区" value="华北区"></el-option> |
| | | <el-option label="西南区" value="西南区"></el-option> |
| | | <el-select v-model="searchForm.region" |
| | | placeholder="请选择区域" |
| | | clearable> |
| | | <el-option label="华东区" |
| | | value="华东区"></el-option> |
| | | <el-option label="华南区" |
| | | value="华南区"></el-option> |
| | | <el-option label="华北区" |
| | | value="华北区"></el-option> |
| | | <el-option label="西南区" |
| | | value="西南区"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.level" placeholder="请选择客户等级" clearable> |
| | | <el-option label="VIP客户" value="VIP客户"></el-option> |
| | | <el-option label="重要客户" value="重要客户"></el-option> |
| | | <el-option label="普通客户" value="普通客户"></el-option> |
| | | <el-select v-model="searchForm.level" |
| | | placeholder="请选择客户等级" |
| | | clearable> |
| | | <el-option label="VIP客户" |
| | | value="VIP客户"></el-option> |
| | | <el-option label="重要客户" |
| | | value="重要客户"></el-option> |
| | | <el-option label="普通客户" |
| | | value="普通客户"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | <el-button style="float: right;" type="primary" @click="handleAdd"> |
| | | <el-button style="float: right;" |
| | | type="primary" |
| | | @click="handleAdd"> |
| | | 新增客户 |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 客户列表 --> |
| | | <el-table |
| | | :data="filteredList" |
| | | <el-table :data="filteredList" |
| | | style="width: 100%" |
| | | v-loading="loading" |
| | | border |
| | | stripe |
| | | height="calc(100vh - 22em)" |
| | | > |
| | | <el-table-column prop="id" label="ID" width="80" align="center"/> |
| | | <el-table-column prop="name" label="客户名称" width="150" /> |
| | | <el-table-column prop="contactPerson" label="联系人" width="100" /> |
| | | <el-table-column prop="phone" label="联系电话" width="140" /> |
| | | <el-table-column prop="email" label="邮箱" /> |
| | | <el-table-column prop="region" label="区域" width="100" /> |
| | | <el-table-column prop="level" label="客户等级" width="100"> |
| | | height="calc(100vh - 22em)"> |
| | | <el-table-column prop="id" |
| | | label="ID" |
| | | width="80" |
| | | align="center" /> |
| | | <el-table-column prop="name" |
| | | label="客户名称" |
| | | width="150" /> |
| | | <el-table-column prop="contactPerson" |
| | | label="联系人" |
| | | width="100" /> |
| | | <el-table-column prop="phone" |
| | | label="联系电话" |
| | | width="140" /> |
| | | <el-table-column prop="email" |
| | | label="邮箱" /> |
| | | <el-table-column prop="region" |
| | | label="区域" |
| | | width="100" /> |
| | | <el-table-column prop="level" |
| | | label="客户等级" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getLevelType(scope.row.level)"> |
| | | {{ scope.row.level }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="salesperson" label="负责业务员" width="120" /> |
| | | <el-table-column prop="status" label="状态" width="80"> |
| | | <el-table-column prop="salesperson" |
| | | label="负责业务员" |
| | | width="120" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="80"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ scope.row.status }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="200" fixed="right" align="center"> |
| | | <el-table-column label="操作" |
| | | width="200" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link type="primary" @click="handleAllocation(scope.row)">分配</el-button> |
| | | <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleAllocation(scope.row)">分配</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="handleDelete(scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | :total="pagination.total" |
| | | <pagination :total="pagination.total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="pagination.currentPage" |
| | | :limit="pagination.pageSize" |
| | | @pagination="handleCurrentChange" |
| | | /> |
| | | @pagination="handleCurrentChange" /> |
| | | </el-card> |
| | | |
| | | <!-- 新增/编辑对话框 --> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="600px"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客户名称" prop="name"> |
| | | <el-input v-model="form.name" placeholder="请输入客户名称"></el-input> |
| | | <el-form-item label="客户名称" |
| | | prop="name"> |
| | | <el-input v-model="form.name" |
| | | placeholder="请输入客户名称"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="联系人" prop="contactPerson"> |
| | | <el-input v-model="form.contactPerson" placeholder="请输入联系人"></el-input> |
| | | <el-form-item label="联系人" |
| | | prop="contactPerson"> |
| | | <el-input v-model="form.contactPerson" |
| | | placeholder="请输入联系人"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="联系电话" prop="phone"> |
| | | <el-input v-model="form.phone" placeholder="请输入联系电话"></el-input> |
| | | <el-form-item label="联系电话" |
| | | prop="phone"> |
| | | <el-input v-model="form.phone" |
| | | placeholder="请输入联系电话"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="邮箱" prop="email"> |
| | | <el-input v-model="form.email" placeholder="请输入邮箱"></el-input> |
| | | <el-form-item label="邮箱" |
| | | prop="email"> |
| | | <el-input v-model="form.email" |
| | | placeholder="请输入邮箱"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="区域" prop="region"> |
| | | <el-select v-model="form.region" placeholder="请选择区域" style="width: 100%"> |
| | | <el-option label="华东区" value="华东区"></el-option> |
| | | <el-option label="华南区" value="华南区"></el-option> |
| | | <el-option label="华北区" value="华北区"></el-option> |
| | | <el-option label="西南区" value="西南区"></el-option> |
| | | <el-form-item label="区域" |
| | | prop="region"> |
| | | <el-select v-model="form.region" |
| | | placeholder="请选择区域" |
| | | style="width: 100%"> |
| | | <el-option label="华东区" |
| | | value="华东区"></el-option> |
| | | <el-option label="华南区" |
| | | value="华南区"></el-option> |
| | | <el-option label="华北区" |
| | | value="华北区"></el-option> |
| | | <el-option label="西南区" |
| | | value="西南区"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客户等级" prop="level"> |
| | | <el-select v-model="form.level" placeholder="请选择客户等级" style="width: 100%"> |
| | | <el-option label="VIP客户" value="VIP客户"></el-option> |
| | | <el-option label="重要客户" value="重要客户"></el-option> |
| | | <el-option label="普通客户" value="普通客户"></el-option> |
| | | <el-form-item label="客户等级" |
| | | prop="level"> |
| | | <el-select v-model="form.level" |
| | | placeholder="请选择客户等级" |
| | | style="width: 100%"> |
| | | <el-option label="VIP客户" |
| | | value="VIP客户"></el-option> |
| | | <el-option label="重要客户" |
| | | value="重要客户"></el-option> |
| | | <el-option label="普通客户" |
| | | value="普通客户"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="负责业务员" prop="salesperson"> |
| | | <el-select v-model="form.salesperson" placeholder="请选择业务员" style="width: 100%"> |
| | | <el-option label="陈志强" value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" value="刘雅婷"></el-option> |
| | | <el-option label="王建国" value="王建国"></el-option> |
| | | <el-form-item label="负责业务员" |
| | | prop="salesperson"> |
| | | <el-select v-model="form.salesperson" |
| | | placeholder="请选择业务员" |
| | | style="width: 100%"> |
| | | <el-option label="陈志强" |
| | | value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" |
| | | value="刘雅婷"></el-option> |
| | | <el-option label="王建国" |
| | | value="王建国"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%"> |
| | | <el-option label="活跃" value="活跃"></el-option> |
| | | <el-option label="潜在" value="潜在"></el-option> |
| | | <el-option label="流失" value="流失"></el-option> |
| | | <el-form-item label="状态" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="请选择状态" |
| | | style="width: 100%"> |
| | | <el-option label="活跃" |
| | | value="活跃"></el-option> |
| | | <el-option label="潜在" |
| | | value="潜在"></el-option> |
| | | <el-option label="流失" |
| | | value="流失"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSubmit">确 定</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="handleSubmit">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 客户分配对话框 --> |
| | | <el-dialog v-model="allocationDialogVisible" title="客户分配" width="500px"> |
| | | <el-dialog v-model="allocationDialogVisible" |
| | | title="客户分配" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="客户名称"> |
| | | <span>{{ currentCustomer.name }}</span> |
| | |
| | | <span>{{ currentCustomer.salesperson }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="重新分配"> |
| | | <el-select v-model="newSalesperson" placeholder="请选择新业务员" style="width: 100%"> |
| | | <el-option label="陈志强" value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" value="刘雅婷"></el-option> |
| | | <el-option label="王建国" value="王建国"></el-option> |
| | | <el-select v-model="newSalesperson" |
| | | placeholder="请选择新业务员" |
| | | style="width: 100%"> |
| | | <el-option label="陈志强" |
| | | value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" |
| | | value="刘雅婷"></el-option> |
| | | <el-option label="王建国" |
| | | value="王建国"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="分配原因"> |
| | | <el-input v-model="allocationReason" type="textarea" rows="3" placeholder="请输入分配原因"></el-input> |
| | | <el-input v-model="allocationReason" |
| | | type="textarea" |
| | | rows="3" |
| | | placeholder="请输入分配原因"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="saveAllocation">确 定</el-button> |
| | | <el-button @click="allocationDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="saveAllocation">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus, Search } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Search } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | |
| | | // 响应式数据 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | const searchForm = reactive({ |
| | | name: '', |
| | | region: '', |
| | | level: '' |
| | | }) |
| | | name: "", |
| | | region: "", |
| | | level: "", |
| | | }); |
| | | |
| | | const customerList = ref([ |
| | | { |
| | | id: 1, |
| | | name: '上海科技有限公司', |
| | | contactPerson: '陈志强', |
| | | phone: '021-12345678', |
| | | email: 'zhang@shanghai-tech.com', |
| | | region: '华东区', |
| | | level: 'VIP客户', |
| | | salesperson: '陈志强', |
| | | status: '活跃' |
| | | name: "上海科技有限公司", |
| | | contactPerson: "陈志强", |
| | | phone: "021-12345678", |
| | | email: "zhang@shanghai-tech.com", |
| | | region: "华东区", |
| | | level: "VIP客户", |
| | | salesperson: "陈志强", |
| | | status: "活跃", |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: '深圳电子有限公司', |
| | | contactPerson: '刘雅婷', |
| | | phone: '0755-87654321', |
| | | email: 'li@shenzhen-elec.com', |
| | | region: '华南区', |
| | | level: '重要客户', |
| | | salesperson: '刘雅婷', |
| | | status: '活跃' |
| | | name: "深圳电子有限公司", |
| | | contactPerson: "刘雅婷", |
| | | phone: "0755-87654321", |
| | | email: "li@shenzhen-elec.com", |
| | | region: "华南区", |
| | | level: "重要客户", |
| | | salesperson: "刘雅婷", |
| | | status: "活跃", |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: '北京贸易公司', |
| | | contactPerson: '王建国', |
| | | phone: '010-11223344', |
| | | email: 'wang@beijing-trade.com', |
| | | region: '华北区', |
| | | level: '普通客户', |
| | | salesperson: '王建国', |
| | | status: '潜在' |
| | | } |
| | | ]) |
| | | name: "北京贸易公司", |
| | | contactPerson: "王建国", |
| | | phone: "010-11223344", |
| | | email: "wang@beijing-trade.com", |
| | | region: "华北区", |
| | | level: "普通客户", |
| | | salesperson: "王建国", |
| | | status: "潜在", |
| | | }, |
| | | ]); |
| | | |
| | | const pagination = reactive({ |
| | | total: 3, |
| | | currentPage: 1, |
| | | pageSize: 10 |
| | | }) |
| | | pageSize: 10, |
| | | }); |
| | | |
| | | const dialogVisible = ref(false) |
| | | const dialogTitle = ref('新增客户') |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref("新增客户"); |
| | | const form = reactive({ |
| | | name: '', |
| | | contactPerson: '', |
| | | phone: '', |
| | | email: '', |
| | | region: '', |
| | | level: '', |
| | | salesperson: '', |
| | | status: '活跃' |
| | | }) |
| | | name: "", |
| | | contactPerson: "", |
| | | phone: "", |
| | | email: "", |
| | | region: "", |
| | | level: "", |
| | | salesperson: "", |
| | | status: "活跃", |
| | | }); |
| | | |
| | | const rules = { |
| | | name: [{ required: true, message: '请输入客户名称', trigger: 'blur' }], |
| | | contactPerson: [{ required: true, message: '请输入联系人', trigger: 'blur' }], |
| | | phone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }], |
| | | email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }], |
| | | region: [{ required: true, message: '请选择区域', trigger: 'change' }], |
| | | level: [{ required: true, message: '请选择客户等级', trigger: 'change' }], |
| | | salesperson: [{ required: true, message: '请选择业务员', trigger: 'change' }], |
| | | status: [{ required: true, message: '请选择状态', trigger: 'change' }] |
| | | } |
| | | name: [{ required: true, message: "请输入客户名称", trigger: "blur" }], |
| | | contactPerson: [{ required: true, message: "请输入联系人", trigger: "blur" }], |
| | | phone: [{ required: true, message: "请输入联系电话", trigger: "blur" }], |
| | | email: [{ required: true, message: "请输入邮箱", trigger: "blur" }], |
| | | region: [{ required: true, message: "请选择区域", trigger: "change" }], |
| | | level: [{ required: true, message: "请选择客户等级", trigger: "change" }], |
| | | salesperson: [{ required: true, message: "请选择业务员", trigger: "change" }], |
| | | status: [{ required: true, message: "请选择状态", trigger: "change" }], |
| | | }; |
| | | |
| | | const isEdit = ref(false) |
| | | const editId = ref(null) |
| | | const allocationDialogVisible = ref(false) |
| | | const currentCustomer = ref({}) |
| | | const newSalesperson = ref('') |
| | | const allocationReason = ref('') |
| | | const formRef = ref() |
| | | const isEdit = ref(false); |
| | | const editId = ref(null); |
| | | const allocationDialogVisible = ref(false); |
| | | const currentCustomer = ref({}); |
| | | const newSalesperson = ref(""); |
| | | const allocationReason = ref(""); |
| | | const formRef = ref(); |
| | | |
| | | // 计算属性 |
| | | const filteredList = computed(() => { |
| | | let list = customerList.value |
| | | let list = customerList.value; |
| | | if (searchForm.name) { |
| | | list = list.filter(item => item.name.includes(searchForm.name)) |
| | | list = list.filter(item => item.name.includes(searchForm.name)); |
| | | } |
| | | if (searchForm.region) { |
| | | list = list.filter(item => item.region === searchForm.region) |
| | | list = list.filter(item => item.region === searchForm.region); |
| | | } |
| | | if (searchForm.level) { |
| | | list = list.filter(item => item.level === searchForm.level) |
| | | list = list.filter(item => item.level === searchForm.level); |
| | | } |
| | | return list |
| | | }) |
| | | return list; |
| | | }); |
| | | |
| | | // 方法 |
| | | const getLevelType = (level) => { |
| | | const getLevelType = level => { |
| | | const levelMap = { |
| | | 'VIP客户': 'danger', |
| | | '重要客户': 'warning', |
| | | '普通客户': 'info' |
| | | } |
| | | return levelMap[level] || 'info' |
| | | } |
| | | VIP客户: "danger", |
| | | 重要客户: "warning", |
| | | 普通客户: "info", |
| | | }; |
| | | return levelMap[level] || "info"; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '活跃': 'success', |
| | | '潜在': 'warning', |
| | | '流失': 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 活跃: "success", |
| | | 潜在: "warning", |
| | | 流失: "danger", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | // 搜索逻辑已在computed中处理 |
| | | } |
| | | }; |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.name = '' |
| | | searchForm.region = '' |
| | | searchForm.level = '' |
| | | } |
| | | searchForm.name = ""; |
| | | searchForm.region = ""; |
| | | searchForm.level = ""; |
| | | }; |
| | | |
| | | const handleAdd = () => { |
| | | dialogTitle.value = '新增客户' |
| | | isEdit.value = false |
| | | form.name = '' |
| | | form.contactPerson = '' |
| | | form.phone = '' |
| | | form.email = '' |
| | | form.region = '' |
| | | form.level = '' |
| | | form.salesperson = '' |
| | | form.status = '活跃' |
| | | dialogVisible.value = true |
| | | } |
| | | dialogTitle.value = "新增客户"; |
| | | isEdit.value = false; |
| | | form.name = ""; |
| | | form.contactPerson = ""; |
| | | form.phone = ""; |
| | | form.email = ""; |
| | | form.region = ""; |
| | | form.level = ""; |
| | | form.salesperson = ""; |
| | | form.status = "活跃"; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | dialogTitle.value = '编辑客户' |
| | | isEdit.value = true |
| | | editId.value = row.id |
| | | Object.assign(form, row) |
| | | dialogVisible.value = true |
| | | } |
| | | const handleEdit = row => { |
| | | dialogTitle.value = "编辑客户"; |
| | | isEdit.value = true; |
| | | editId.value = row.id; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm('确认删除该客户吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("确认删除该客户吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = customerList.value.findIndex(item => item.id === row.id) |
| | | const index = customerList.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | customerList.value.splice(index, 1) |
| | | pagination.total-- |
| | | ElMessage.success('删除成功') |
| | | customerList.value.splice(index, 1); |
| | | pagination.total--; |
| | | ElMessage.success("删除成功"); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleAllocation = (row) => { |
| | | currentCustomer.value = row |
| | | newSalesperson.value = '' |
| | | allocationReason.value = '' |
| | | allocationDialogVisible.value = true |
| | | } |
| | | const handleAllocation = row => { |
| | | currentCustomer.value = row; |
| | | newSalesperson.value = ""; |
| | | allocationReason.value = ""; |
| | | allocationDialogVisible.value = true; |
| | | }; |
| | | |
| | | const saveAllocation = () => { |
| | | if (!newSalesperson.value) { |
| | | ElMessage.warning('请选择新业务员') |
| | | return |
| | | ElMessage.warning("请选择新业务员"); |
| | | return; |
| | | } |
| | | |
| | | const index = customerList.value.findIndex(item => item.id === currentCustomer.value.id) |
| | | const index = customerList.value.findIndex( |
| | | item => item.id === currentCustomer.value.id |
| | | ); |
| | | if (index > -1) { |
| | | customerList.value[index].salesperson = newSalesperson.value |
| | | ElMessage.success('客户分配成功') |
| | | allocationDialogVisible.value = false |
| | | customerList.value[index].salesperson = newSalesperson.value; |
| | | ElMessage.success("客户分配成功"); |
| | | allocationDialogVisible.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate((valid) => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (isEdit.value) { |
| | | // 编辑 |
| | | const index = customerList.value.findIndex(item => item.id === editId.value) |
| | | const index = customerList.value.findIndex( |
| | | item => item.id === editId.value |
| | | ); |
| | | if (index > -1) { |
| | | customerList.value[index] = { ...form, id: editId.value } |
| | | ElMessage.success('编辑成功') |
| | | customerList.value[index] = { ...form, id: editId.value }; |
| | | ElMessage.success("编辑成功"); |
| | | } |
| | | } else { |
| | | // 新增 |
| | | const newId = Math.max(...customerList.value.map(item => item.id)) + 1 |
| | | const newId = Math.max(...customerList.value.map(item => item.id)) + 1; |
| | | customerList.value.push({ |
| | | ...form, |
| | | id: newId |
| | | }) |
| | | pagination.total++ |
| | | ElMessage.success('新增成功') |
| | | id: newId, |
| | | }); |
| | | pagination.total++; |
| | | ElMessage.success("新增成功"); |
| | | } |
| | | dialogVisible.value = false |
| | | dialogVisible.value = false; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = (val) => { |
| | | pagination.currentPage = val.page |
| | | pagination.pageSize = val.limit |
| | | } |
| | | const handleCurrentChange = val => { |
| | | pagination.currentPage = val.page; |
| | | pagination.pageSize = val.limit; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <!-- 搜索区域 --> |
| | | <el-row :gutter="20" class="search-row"> |
| | | <el-row :gutter="20" |
| | | class="search-row"> |
| | | <el-col :span="6"> |
| | | <el-input |
| | | v-model="searchForm.orderNo" |
| | | <el-input v-model="searchForm.orderNo" |
| | | placeholder="请输入订单号" |
| | | clearable |
| | | @keyup.enter="handleSearch" |
| | | > |
| | | @keyup.enter="handleSearch"> |
| | | <template #prefix> |
| | | <el-icon><Search /></el-icon> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | </template> |
| | | </el-input> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.customer" placeholder="请选择客户" clearable> |
| | | <el-option label="上海科技有限公司" value="上海科技有限公司"></el-option> |
| | | <el-option label="深圳电子有限公司" value="深圳电子有限公司"></el-option> |
| | | <el-option label="北京贸易公司" value="北京贸易公司"></el-option> |
| | | <el-select v-model="searchForm.customer" |
| | | placeholder="请选择客户" |
| | | clearable> |
| | | <el-option label="上海科技有限公司" |
| | | value="上海科技有限公司"></el-option> |
| | | <el-option label="深圳电子有限公司" |
| | | value="深圳电子有限公司"></el-option> |
| | | <el-option label="北京贸易公司" |
| | | value="北京贸易公司"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.status" placeholder="请选择订单状态" clearable> |
| | | <el-option label="待审核" value="待审核"></el-option> |
| | | <el-option label="已审核" value="已审核"></el-option> |
| | | <el-option label="已发货" value="已发货"></el-option> |
| | | <el-option label="已完成" value="已完成"></el-option> |
| | | <el-option label="已取消" value="已取消"></el-option> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="请选择订单状态" |
| | | clearable> |
| | | <el-option label="待审核" |
| | | value="待审核"></el-option> |
| | | <el-option label="已审核" |
| | | value="已审核"></el-option> |
| | | <el-option label="已发货" |
| | | value="已发货"></el-option> |
| | | <el-option label="已完成" |
| | | value="已完成"></el-option> |
| | | <el-option label="已取消" |
| | | value="已取消"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | <el-button style="float: right;" type="primary" @click="handleAdd"> |
| | | <el-button style="float: right;" |
| | | type="primary" |
| | | @click="handleAdd"> |
| | | 新增订单 |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 订单列表 --> |
| | | <el-table |
| | | :data="filteredList" |
| | | <el-table :data="filteredList" |
| | | style="width: 100%" |
| | | v-loading="loading" |
| | | border |
| | | stripe |
| | | height="calc(100vh - 22em)" |
| | | > |
| | | <el-table-column prop="id" label="ID" width="80" align="center"/> |
| | | <el-table-column prop="orderNo" label="订单号" width="150" /> |
| | | <el-table-column prop="customer" label="客户名称" /> |
| | | <el-table-column prop="salesperson" label="业务员" width="100" /> |
| | | <el-table-column prop="orderDate" label="下单日期" width="120" /> |
| | | <el-table-column prop="amount" label="订单金额" width="120"> |
| | | height="calc(100vh - 22em)"> |
| | | <el-table-column prop="id" |
| | | label="ID" |
| | | width="80" |
| | | align="center" /> |
| | | <el-table-column prop="orderNo" |
| | | label="订单号" |
| | | width="150" /> |
| | | <el-table-column prop="customer" |
| | | label="客户名称" /> |
| | | <el-table-column prop="salesperson" |
| | | label="业务员" |
| | | width="100" /> |
| | | <el-table-column prop="orderDate" |
| | | label="下单日期" |
| | | width="120" /> |
| | | <el-table-column prop="amount" |
| | | label="订单金额" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | ¥{{ scope.row.amount.toFixed(2) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="订单状态" width="100"> |
| | | <el-table-column prop="status" |
| | | label="订单状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ scope.row.status }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="250" fixed="right" align="center"> |
| | | <el-table-column label="操作" |
| | | width="250" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="handleView(scope.row)">查看</el-button> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)" v-if="scope.row.status === '待审核'">编辑</el-button> |
| | | <el-button link type="primary" @click="handleReview(scope.row)" v-if="scope.row.status === '待审核'">审核</el-button> |
| | | <el-button link type="primary" @click="handleTransfer(scope.row)" v-if="scope.row.status === '已审核'">转单</el-button> |
| | | <el-button link type="danger" @click="handleCancel(scope.row)" v-if="scope.row.status === '待审核'">取消</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleView(scope.row)">查看</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEdit(scope.row)" |
| | | v-if="scope.row.status === '待审核'">编辑</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleReview(scope.row)" |
| | | v-if="scope.row.status === '待审核'">审核</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleTransfer(scope.row)" |
| | | v-if="scope.row.status === '已审核'">转单</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="handleCancel(scope.row)" |
| | | v-if="scope.row.status === '待审核'">取消</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | :total="pagination.total" |
| | | <pagination :total="pagination.total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="pagination.currentPage" |
| | | :limit="pagination.pageSize" |
| | | @pagination="handleCurrentChange" |
| | | /> |
| | | @pagination="handleCurrentChange" /> |
| | | </el-card> |
| | | |
| | | <!-- 新增/编辑对话框 --> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="700px"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客户名称" prop="customer"> |
| | | <el-select v-model="form.customer" placeholder="请选择客户" style="width: 100%"> |
| | | <el-option label="上海科技有限公司" value="上海科技有限公司"></el-option> |
| | | <el-option label="深圳电子有限公司" value="深圳电子有限公司"></el-option> |
| | | <el-option label="北京贸易公司" value="北京贸易公司"></el-option> |
| | | <el-form-item label="客户名称" |
| | | prop="customer"> |
| | | <el-select v-model="form.customer" |
| | | placeholder="请选择客户" |
| | | style="width: 100%"> |
| | | <el-option label="上海科技有限公司" |
| | | value="上海科技有限公司"></el-option> |
| | | <el-option label="深圳电子有限公司" |
| | | value="深圳电子有限公司"></el-option> |
| | | <el-option label="北京贸易公司" |
| | | value="北京贸易公司"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="业务员" prop="salesperson"> |
| | | <el-select v-model="form.salesperson" placeholder="请选择业务员" style="width: 100%"> |
| | | <el-option label="陈志强" value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" value="刘雅婷"></el-option> |
| | | <el-option label="王建国" value="王建国"></el-option> |
| | | <el-form-item label="业务员" |
| | | prop="salesperson"> |
| | | <el-select v-model="form.salesperson" |
| | | placeholder="请选择业务员" |
| | | style="width: 100%"> |
| | | <el-option label="陈志强" |
| | | value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" |
| | | value="刘雅婷"></el-option> |
| | | <el-option label="王建国" |
| | | value="王建国"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="订单日期" prop="orderDate"> |
| | | <el-date-picker |
| | | v-model="form.orderDate" |
| | | <el-form-item label="订单日期" |
| | | prop="orderDate"> |
| | | <el-date-picker v-model="form.orderDate" |
| | | type="date" |
| | | placeholder="选择订单日期" |
| | | style="width: 100%" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | value-format="YYYY-MM-DD" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="订单金额" prop="amount"> |
| | | <el-input-number v-model="form.amount" :precision="2" :min="0" style="width: 100%"></el-input-number> |
| | | <el-form-item label="订单金额" |
| | | prop="amount"> |
| | | <el-input-number v-model="form.amount" |
| | | :precision="2" |
| | | :min="0" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="订单状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%"> |
| | | <el-option label="待审核" value="待审核"></el-option> |
| | | <el-option label="已审核" value="已审核"></el-option> |
| | | <el-option label="已发货" value="已发货"></el-option> |
| | | <el-option label="已完成" value="已完成"></el-option> |
| | | <el-option label="已取消" value="已取消"></el-option> |
| | | <el-form-item label="订单状态" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="请选择状态" |
| | | style="width: 100%"> |
| | | <el-option label="待审核" |
| | | value="待审核"></el-option> |
| | | <el-option label="已审核" |
| | | value="已审核"></el-option> |
| | | <el-option label="已发货" |
| | | value="已发货"></el-option> |
| | | <el-option label="已完成" |
| | | value="已完成"></el-option> |
| | | <el-option label="已取消" |
| | | value="已取消"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input type="textarea" v-model="form.remark" placeholder="请输入备注信息" rows="3"></el-input> |
| | | <el-form-item label="备注" |
| | | prop="remark"> |
| | | <el-input type="textarea" |
| | | v-model="form.remark" |
| | | placeholder="请输入备注信息" |
| | | rows="3"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSubmit">确 定</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="handleSubmit">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 订单审核对话框 --> |
| | | <el-dialog v-model="reviewDialogVisible" title="订单审核" width="500px"> |
| | | <el-dialog v-model="reviewDialogVisible" |
| | | title="订单审核" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="订单号"> |
| | | <span>{{ currentOrder.orderNo }}</span> |
| | |
| | | <el-form-item label="订单金额"> |
| | | <span>¥{{ currentOrder.amount.toFixed(2) }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="审核结果" prop="reviewResult"> |
| | | <el-form-item label="审核结果" |
| | | prop="reviewResult"> |
| | | <el-radio-group v-model="reviewResult"> |
| | | <el-radio label="通过">通过</el-radio> |
| | | <el-radio label="拒绝">拒绝</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="审核意见" prop="reviewComment"> |
| | | <el-input type="textarea" v-model="reviewComment" rows="3" placeholder="请输入审核意见"></el-input> |
| | | <el-form-item label="审核意见" |
| | | prop="reviewComment"> |
| | | <el-input type="textarea" |
| | | v-model="reviewComment" |
| | | rows="3" |
| | | placeholder="请输入审核意见"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="saveReview">确 定</el-button> |
| | | <el-button @click="reviewDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="saveReview">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 订单转单对话框 --> |
| | | <el-dialog v-model="transferDialogVisible" title="订单转单" width="500px"> |
| | | <el-dialog v-model="transferDialogVisible" |
| | | title="订单转单" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="订单号"> |
| | | <span>{{ currentOrder.orderNo }}</span> |
| | |
| | | <el-form-item label="当前业务员"> |
| | | <span>{{ currentOrder.salesperson }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="转单给" prop="newSalesperson"> |
| | | <el-select v-model="newSalesperson" placeholder="请选择新业务员" style="width: 100%"> |
| | | <el-option label="陈志强" value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" value="刘雅婷"></el-option> |
| | | <el-option label="王建国" value="王建国"></el-option> |
| | | <el-form-item label="转单给" |
| | | prop="newSalesperson"> |
| | | <el-select v-model="newSalesperson" |
| | | placeholder="请选择新业务员" |
| | | style="width: 100%"> |
| | | <el-option label="陈志强" |
| | | value="陈志强"></el-option> |
| | | <el-option label="刘雅婷" |
| | | value="刘雅婷"></el-option> |
| | | <el-option label="王建国" |
| | | value="王建国"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="转单原因" prop="transferReason"> |
| | | <el-input type="textarea" v-model="transferReason" rows="3" placeholder="请输入转单原因"></el-input> |
| | | <el-form-item label="转单原因" |
| | | prop="transferReason"> |
| | | <el-input type="textarea" |
| | | v-model="transferReason" |
| | | rows="3" |
| | | placeholder="请输入转单原因"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="saveTransfer">确 定</el-button> |
| | | <el-button @click="transferDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="saveTransfer">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus, Search } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Search } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | |
| | | // 响应式数据 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | const searchForm = reactive({ |
| | | orderNo: '', |
| | | customer: '', |
| | | status: '' |
| | | }) |
| | | orderNo: "", |
| | | customer: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const orderList = ref([ |
| | | { |
| | | id: 1, |
| | | orderNo: 'ORD202312001', |
| | | customer: '广州科技公司', |
| | | salesperson: '张三', |
| | | orderDate: '2023-12-01', |
| | | amount: 50000.00, |
| | | status: '待审核', |
| | | remark: '重要客户订单' |
| | | orderNo: "ORD202312001", |
| | | customer: "广州科技公司", |
| | | salesperson: "张三", |
| | | orderDate: "2023-12-01", |
| | | amount: 50000.0, |
| | | status: "待审核", |
| | | remark: "重要客户订单", |
| | | }, |
| | | { |
| | | id: 2, |
| | | orderNo: 'ORD202312002', |
| | | customer: '深圳电子有限公司', |
| | | salesperson: '刘雅婷', |
| | | orderDate: '2023-12-02', |
| | | amount: 35000.00, |
| | | status: '已审核', |
| | | remark: '常规订单' |
| | | orderNo: "ORD202312002", |
| | | customer: "深圳电子有限公司", |
| | | salesperson: "刘雅婷", |
| | | orderDate: "2023-12-02", |
| | | amount: 35000.0, |
| | | status: "已审核", |
| | | remark: "常规订单", |
| | | }, |
| | | { |
| | | id: 3, |
| | | orderNo: 'ORD202312003', |
| | | customer: '北京贸易公司', |
| | | salesperson: '王建国', |
| | | orderDate: '2023-12-03', |
| | | amount: 28000.00, |
| | | status: '已发货', |
| | | remark: '新客户订单' |
| | | } |
| | | ]) |
| | | orderNo: "ORD202312003", |
| | | customer: "北京贸易公司", |
| | | salesperson: "王建国", |
| | | orderDate: "2023-12-03", |
| | | amount: 28000.0, |
| | | status: "已发货", |
| | | remark: "新客户订单", |
| | | }, |
| | | ]); |
| | | |
| | | const pagination = reactive({ |
| | | total: 3, |
| | | currentPage: 1, |
| | | pageSize: 10 |
| | | }) |
| | | pageSize: 10, |
| | | }); |
| | | |
| | | const dialogVisible = ref(false) |
| | | const dialogTitle = ref('新增订单') |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref("新增订单"); |
| | | const form = reactive({ |
| | | customer: '', |
| | | salesperson: '', |
| | | orderDate: '', |
| | | customer: "", |
| | | salesperson: "", |
| | | orderDate: "", |
| | | amount: 0, |
| | | status: '待审核', |
| | | remark: '' |
| | | }) |
| | | status: "待审核", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | customer: [{ required: true, message: '请选择客户', trigger: 'change' }], |
| | | salesperson: [{ required: true, message: '请选择业务员', trigger: 'change' }], |
| | | orderDate: [{ required: true, message: '请选择订单日期', trigger: 'change' }], |
| | | amount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }], |
| | | status: [{ required: true, message: '请选择状态', trigger: 'change' }] |
| | | } |
| | | customer: [{ required: true, message: "请选择客户", trigger: "change" }], |
| | | salesperson: [{ required: true, message: "请选择业务员", trigger: "change" }], |
| | | orderDate: [{ required: true, message: "请选择订单日期", trigger: "change" }], |
| | | amount: [{ required: true, message: "请输入订单金额", trigger: "blur" }], |
| | | status: [{ required: true, message: "请选择状态", trigger: "change" }], |
| | | }; |
| | | |
| | | const isEdit = ref(false) |
| | | const editId = ref(null) |
| | | const reviewDialogVisible = ref(false) |
| | | const transferDialogVisible = ref(false) |
| | | const currentOrder = ref({}) |
| | | const reviewResult = ref('') |
| | | const reviewComment = ref('') |
| | | const newSalesperson = ref('') |
| | | const transferReason = ref('') |
| | | const formRef = ref() |
| | | const isEdit = ref(false); |
| | | const editId = ref(null); |
| | | const reviewDialogVisible = ref(false); |
| | | const transferDialogVisible = ref(false); |
| | | const currentOrder = ref({}); |
| | | const reviewResult = ref(""); |
| | | const reviewComment = ref(""); |
| | | const newSalesperson = ref(""); |
| | | const transferReason = ref(""); |
| | | const formRef = ref(); |
| | | |
| | | // 计算属性 |
| | | const filteredList = computed(() => { |
| | | let list = orderList.value |
| | | let list = orderList.value; |
| | | if (searchForm.orderNo) { |
| | | list = list.filter(item => item.orderNo.includes(searchForm.orderNo)) |
| | | list = list.filter(item => item.orderNo.includes(searchForm.orderNo)); |
| | | } |
| | | if (searchForm.customer) { |
| | | list = list.filter(item => item.customer === searchForm.customer) |
| | | list = list.filter(item => item.customer === searchForm.customer); |
| | | } |
| | | if (searchForm.status) { |
| | | list = list.filter(item => item.status === searchForm.status) |
| | | list = list.filter(item => item.status === searchForm.status); |
| | | } |
| | | return list |
| | | }) |
| | | return list; |
| | | }); |
| | | |
| | | // 方法 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '待审核': 'warning', |
| | | '已审核': 'primary', |
| | | '已发货': 'success', |
| | | '已完成': 'success', |
| | | '已取消': 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 待审核: "warning", |
| | | 已审核: "primary", |
| | | 已发货: "success", |
| | | 已完成: "success", |
| | | 已取消: "danger", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | // 搜索逻辑已在computed中处理 |
| | | } |
| | | }; |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.orderNo = '' |
| | | searchForm.customer = '' |
| | | searchForm.status = '' |
| | | } |
| | | searchForm.orderNo = ""; |
| | | searchForm.customer = ""; |
| | | searchForm.status = ""; |
| | | }; |
| | | |
| | | const handleAdd = () => { |
| | | dialogTitle.value = '新增订单' |
| | | isEdit.value = false |
| | | form.customer = '' |
| | | form.salesperson = '' |
| | | form.orderDate = '' |
| | | form.amount = 0 |
| | | form.status = '待审核' |
| | | form.remark = '' |
| | | dialogVisible.value = true |
| | | } |
| | | dialogTitle.value = "新增订单"; |
| | | isEdit.value = false; |
| | | form.customer = ""; |
| | | form.salesperson = ""; |
| | | form.orderDate = ""; |
| | | form.amount = 0; |
| | | form.status = "待审核"; |
| | | form.remark = ""; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleView = (row) => { |
| | | const handleView = row => { |
| | | // 查看订单详情 |
| | | ElMessage.info('查看订单详情功能待实现') |
| | | } |
| | | ElMessage.info("查看订单详情功能待实现"); |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | dialogTitle.value = '编辑订单' |
| | | isEdit.value = true |
| | | editId.value = row.id |
| | | Object.assign(form, row) |
| | | dialogVisible.value = true |
| | | } |
| | | const handleEdit = row => { |
| | | dialogTitle.value = "编辑订单"; |
| | | isEdit.value = true; |
| | | editId.value = row.id; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleReview = (row) => { |
| | | currentOrder.value = row |
| | | reviewResult.value = '' |
| | | reviewComment.value = '' |
| | | reviewDialogVisible.value = true |
| | | } |
| | | const handleReview = row => { |
| | | currentOrder.value = row; |
| | | reviewResult.value = ""; |
| | | reviewComment.value = ""; |
| | | reviewDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleTransfer = (row) => { |
| | | currentOrder.value = row |
| | | newSalesperson.value = '' |
| | | transferReason.value = '' |
| | | transferDialogVisible.value = true |
| | | } |
| | | const handleTransfer = row => { |
| | | currentOrder.value = row; |
| | | newSalesperson.value = ""; |
| | | transferReason.value = ""; |
| | | transferDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleCancel = (row) => { |
| | | ElMessageBox.confirm('确认取消该订单吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const handleCancel = row => { |
| | | ElMessageBox.confirm("确认取消该订单吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = orderList.value.findIndex(item => item.id === row.id) |
| | | const index = orderList.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | orderList.value[index].status = '已取消' |
| | | ElMessage.success('订单已取消') |
| | | orderList.value[index].status = "已取消"; |
| | | ElMessage.success("订单已取消"); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm('确认删除该订单吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("确认删除该订单吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = orderList.value.findIndex(item => item.id === row.id) |
| | | const index = orderList.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | orderList.value.splice(index, 1) |
| | | pagination.total-- |
| | | ElMessage.success('删除成功') |
| | | orderList.value.splice(index, 1); |
| | | pagination.total--; |
| | | ElMessage.success("删除成功"); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const saveReview = () => { |
| | | if (!reviewResult.value) { |
| | | ElMessage.warning('请选择审核结果') |
| | | return |
| | | ElMessage.warning("请选择审核结果"); |
| | | return; |
| | | } |
| | | |
| | | const index = orderList.value.findIndex(item => item.id === currentOrder.value.id) |
| | | const index = orderList.value.findIndex( |
| | | item => item.id === currentOrder.value.id |
| | | ); |
| | | if (index > -1) { |
| | | if (reviewResult.value === '通过') { |
| | | orderList.value[index].status = '已审核' |
| | | ElMessage.success('订单审核通过') |
| | | if (reviewResult.value === "通过") { |
| | | orderList.value[index].status = "已审核"; |
| | | ElMessage.success("订单审核通过"); |
| | | } else { |
| | | orderList.value[index].status = '已取消' |
| | | ElMessage.success('订单审核拒绝') |
| | | orderList.value[index].status = "已取消"; |
| | | ElMessage.success("订单审核拒绝"); |
| | | } |
| | | reviewDialogVisible.value = false |
| | | reviewDialogVisible.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const saveTransfer = () => { |
| | | if (!newSalesperson.value) { |
| | | ElMessage.warning('请选择新业务员') |
| | | return |
| | | ElMessage.warning("请选择新业务员"); |
| | | return; |
| | | } |
| | | |
| | | const index = orderList.value.findIndex(item => item.id === currentOrder.value.id) |
| | | const index = orderList.value.findIndex( |
| | | item => item.id === currentOrder.value.id |
| | | ); |
| | | if (index > -1) { |
| | | orderList.value[index].salesperson = newSalesperson.value |
| | | ElMessage.success('订单转单成功') |
| | | transferDialogVisible.value = false |
| | | orderList.value[index].salesperson = newSalesperson.value; |
| | | ElMessage.success("订单转单成功"); |
| | | transferDialogVisible.value = false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate((valid) => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (isEdit.value) { |
| | | // 编辑 |
| | | const index = orderList.value.findIndex(item => item.id === editId.value) |
| | | const index = orderList.value.findIndex( |
| | | item => item.id === editId.value |
| | | ); |
| | | if (index > -1) { |
| | | orderList.value[index] = { ...form, id: editId.value } |
| | | ElMessage.success('编辑成功') |
| | | orderList.value[index] = { ...form, id: editId.value }; |
| | | ElMessage.success("编辑成功"); |
| | | } |
| | | } else { |
| | | // 新增 |
| | | const newId = Math.max(...orderList.value.map(item => item.id)) + 1 |
| | | const orderNo = `ORD${new Date().getFullYear()}${String(new Date().getMonth() + 1).padStart(2, '0')}${String(new Date().getDate()).padStart(2, '0')}${String(newId).padStart(3, '0')}` |
| | | const newId = Math.max(...orderList.value.map(item => item.id)) + 1; |
| | | const orderNo = `ORD${new Date().getFullYear()}${String( |
| | | new Date().getMonth() + 1 |
| | | ).padStart(2, "0")}${String(new Date().getDate()).padStart( |
| | | 2, |
| | | "0" |
| | | )}${String(newId).padStart(3, "0")}`; |
| | | orderList.value.push({ |
| | | ...form, |
| | | id: newId, |
| | | orderNo: orderNo |
| | | }) |
| | | pagination.total++ |
| | | ElMessage.success('新增成功') |
| | | orderNo: orderNo, |
| | | }); |
| | | pagination.total++; |
| | | ElMessage.success("新增成功"); |
| | | } |
| | | dialogVisible.value = false |
| | | dialogVisible.value = false; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = (val) => { |
| | | pagination.currentPage = val.page |
| | | pagination.pageSize = val.limit |
| | | } |
| | | const handleCurrentChange = val => { |
| | | pagination.currentPage = val.page; |
| | | pagination.pageSize = val.limit; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <!-- 搜索区域 --> |
| | | <el-row :gutter="20" class="search-row"> |
| | | <el-row :gutter="20" |
| | | class="search-row"> |
| | | <el-col :span="6"> |
| | | <el-input |
| | | v-model="searchForm.orderNo" |
| | | <el-input v-model="searchForm.orderNo" |
| | | placeholder="请输入订单号" |
| | | clearable |
| | | @keyup.enter="handleSearch" |
| | | > |
| | | @keyup.enter="handleSearch"> |
| | | <template #prefix> |
| | | <el-icon><Search /></el-icon> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | </template> |
| | | </el-input> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.paymentStatus" placeholder="请选择付款状态" clearable> |
| | | <el-option label="未付款" value="未付款"></el-option> |
| | | <el-option label="已付款" value="已付款"></el-option> |
| | | <el-option label="部分付款" value="部分付款"></el-option> |
| | | <el-select v-model="searchForm.paymentStatus" |
| | | placeholder="请选择付款状态" |
| | | clearable> |
| | | <el-option label="未付款" |
| | | value="未付款"></el-option> |
| | | <el-option label="已付款" |
| | | value="已付款"></el-option> |
| | | <el-option label="部分付款" |
| | | value="部分付款"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.shippingStatus" placeholder="请选择发货状态" clearable> |
| | | <el-option label="待发货" value="待发货"></el-option> |
| | | <el-option label="已发货" value="已发货"></el-option> |
| | | <el-option label="已签收" value="已签收"></el-option> |
| | | <el-select v-model="searchForm.shippingStatus" |
| | | placeholder="请选择发货状态" |
| | | clearable> |
| | | <el-option label="待发货" |
| | | value="待发货"></el-option> |
| | | <el-option label="已发货" |
| | | value="已发货"></el-option> |
| | | <el-option label="已签收" |
| | | value="已签收"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | <el-button style="float: right;" type="primary" @click="handleAdd"> |
| | | <el-button style="float: right;" |
| | | type="primary" |
| | | @click="handleAdd"> |
| | | 新增记录 |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 支付与发货列表 --> |
| | | <el-table |
| | | :data="recordList" |
| | | <el-table :data="recordList" |
| | | style="width: 100%" |
| | | v-loading="loading" |
| | | border |
| | | stripe |
| | | height="calc(100vh - 22em)" |
| | | > |
| | | <el-table-column prop="id" label="ID" width="80" align="center"/> |
| | | <el-table-column prop="orderNo" label="订单号" /> |
| | | <el-table-column prop="customer" label="客户名称" /> |
| | | <el-table-column prop="orderAmount" label="订单金额" width="120"> |
| | | height="calc(100vh - 22em)"> |
| | | <el-table-column prop="id" |
| | | label="ID" |
| | | width="80" |
| | | align="center" /> |
| | | <el-table-column prop="orderNo" |
| | | label="订单号" /> |
| | | <el-table-column prop="customer" |
| | | label="客户名称" /> |
| | | <el-table-column prop="orderAmount" |
| | | label="订单金额" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | ¥{{ scope.row.orderAmount }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="orderAmount" label="已付款金额" width="120"> |
| | | <el-table-column prop="orderAmount" |
| | | label="已付款金额" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | ¥{{ scope.row.paidAmount }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="paymentStatus" label="付款状态" width="100"> |
| | | <el-table-column prop="paymentStatus" |
| | | label="付款状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getPaymentStatusType(scope.row.paymentStatus)"> |
| | | {{ scope.row.paymentStatus }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="shippingStatus" label="发货状态" width="100"> |
| | | <el-table-column prop="shippingStatus" |
| | | label="发货状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getShippingStatusType(scope.row.shippingStatus)"> |
| | | {{ scope.row.shippingStatus }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="shippingDate" label="发货日期" width="120" /> |
| | | <el-table-column label="操作" width="250" fixed="right" align="center"> |
| | | <el-table-column prop="shippingDate" |
| | | label="发货日期" |
| | | width="120" /> |
| | | <el-table-column label="操作" |
| | | width="250" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <!-- <el-button link type="primary" @click="handleView(scope.row)">查看</el-button>--> |
| | | <el-button link type="primary" @click="handlePayment(scope.row)" v-if="scope.row.paymentStatus !== '已付款'">付款</el-button> |
| | | <el-button link type="primary" @click="handleShipping(scope.row)" v-if="scope.row.paymentStatus === '已付款' && scope.row.shippingStatus === '待发货'">发货</el-button> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handlePayment(scope.row)" |
| | | v-if="scope.row.paymentStatus !== '已付款'">付款</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleShipping(scope.row)" |
| | | v-if="scope.row.paymentStatus === '已付款' && scope.row.shippingStatus === '待发货'">发货</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="handleDelete(scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | :total="total" |
| | | <pagination :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="pagination.current" |
| | | :limit="pagination.size" |
| | | @pagination="handleCurrentChange" |
| | | /> |
| | | @pagination="handleCurrentChange" /> |
| | | </el-card> |
| | | |
| | | <!-- 新增/编辑对话框 --> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="700px"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="订单号" prop="orderNo"> |
| | | <el-input v-model="form.orderNo" placeholder="请输入订单号" disabled></el-input> |
| | | <el-form-item label="订单号" |
| | | prop="orderNo"> |
| | | <el-input v-model="form.orderNo" |
| | | placeholder="请输入订单号" |
| | | disabled></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客户名称" prop="customer"> |
| | | <el-select v-model="form.customer" placeholder="请选择客户" style="width: 100%"> |
| | | <el-option label="上海科技有限公司" value="上海科技有限公司"></el-option> |
| | | <el-option label="深圳电子有限公司" value="深圳电子有限公司"></el-option> |
| | | <el-option label="北京贸易公司" value="北京贸易公司"></el-option> |
| | | <el-form-item label="客户名称" |
| | | prop="customer"> |
| | | <el-select v-model="form.customer" |
| | | placeholder="请选择客户" |
| | | style="width: 100%"> |
| | | <el-option label="上海科技有限公司" |
| | | value="上海科技有限公司"></el-option> |
| | | <el-option label="深圳电子有限公司" |
| | | value="深圳电子有限公司"></el-option> |
| | | <el-option label="北京贸易公司" |
| | | value="北京贸易公司"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="订单金额" prop="orderAmount"> |
| | | <el-input-number v-model="form.orderAmount" :precision="2" :min="0" style="width: 100%"></el-input-number> |
| | | <el-form-item label="订单金额" |
| | | prop="orderAmount"> |
| | | <el-input-number v-model="form.orderAmount" |
| | | :precision="2" |
| | | :min="0" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="付款状态" prop="paymentStatus"> |
| | | <el-select v-model="form.paymentStatus" placeholder="请选择付款状态" style="width: 100%"> |
| | | <el-option label="未付款" value="未付款"></el-option> |
| | | <el-option label="已付款" value="已付款"></el-option> |
| | | <el-option label="部分付款" value="部分付款"></el-option> |
| | | <el-form-item label="付款状态" |
| | | prop="paymentStatus"> |
| | | <el-select v-model="form.paymentStatus" |
| | | placeholder="请选择付款状态" |
| | | style="width: 100%"> |
| | | <el-option label="未付款" |
| | | value="未付款"></el-option> |
| | | <el-option label="已付款" |
| | | value="已付款"></el-option> |
| | | <el-option label="部分付款" |
| | | value="部分付款"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="发货状态" prop="shippingStatus"> |
| | | <el-select v-model="form.shippingStatus" placeholder="请选择发货状态" style="width: 100%"> |
| | | <el-option label="待发货" value="待发货"></el-option> |
| | | <el-option label="已发货" value="已发货"></el-option> |
| | | <el-option label="已签收" value="已签收"></el-option> |
| | | <el-form-item label="发货状态" |
| | | prop="shippingStatus"> |
| | | <el-select v-model="form.shippingStatus" |
| | | placeholder="请选择发货状态" |
| | | style="width: 100%"> |
| | | <el-option label="待发货" |
| | | value="待发货"></el-option> |
| | | <el-option label="已发货" |
| | | value="已发货"></el-option> |
| | | <el-option label="已签收" |
| | | value="已签收"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="发货日期" prop="shippingDate"> |
| | | <el-date-picker |
| | | v-model="form.shippingDate" |
| | | <el-form-item label="发货日期" |
| | | prop="shippingDate"> |
| | | <el-date-picker v-model="form.shippingDate" |
| | | type="date" |
| | | placeholder="选择发货日期" |
| | | style="width: 100%" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | value-format="YYYY-MM-DD" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="物流单号" prop="trackingNo"> |
| | | <el-input v-model="form.trackingNo" placeholder="请输入物流单号"></el-input> |
| | | <el-form-item label="物流单号" |
| | | prop="trackingNo"> |
| | | <el-input v-model="form.trackingNo" |
| | | placeholder="请输入物流单号"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input type="textarea" v-model="form.remark" placeholder="请输入备注信息" rows="3"></el-input> |
| | | <el-form-item label="备注" |
| | | prop="remark"> |
| | | <el-input type="textarea" |
| | | v-model="form.remark" |
| | | placeholder="请输入备注信息" |
| | | rows="3"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSubmit">确 定</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="handleSubmit">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 付款对话框 --> |
| | | <el-dialog v-model="paymentDialogVisible" title="订单付款" width="500px"> |
| | | <el-dialog v-model="paymentDialogVisible" |
| | | title="订单付款" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="订单号"> |
| | | <span>{{ currentRecord.orderNo }}</span> |
| | |
| | | <el-form-item label="订单金额"> |
| | | <span>¥{{ currentRecord.orderAmount }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="付款金额" prop="paymentAmount"> |
| | | <el-input-number v-model="paymentAmount" :precision="2" :min="0" :max="currentRecord.orderAmount" style="width: 100%"></el-input-number> |
| | | <el-form-item label="付款金额" |
| | | prop="paymentAmount"> |
| | | <el-input-number v-model="paymentAmount" |
| | | :precision="2" |
| | | :min="0" |
| | | :max="currentRecord.orderAmount" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | <el-form-item label="付款备注" prop="paymentRemark"> |
| | | <el-input type="textarea" v-model="paymentRemark" rows="3" placeholder="请输入付款备注"></el-input> |
| | | <el-form-item label="付款备注" |
| | | prop="paymentRemark"> |
| | | <el-input type="textarea" |
| | | v-model="paymentRemark" |
| | | rows="3" |
| | | placeholder="请输入付款备注"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="savePayment">确 定</el-button> |
| | | <el-button @click="paymentDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="savePayment">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 发货对话框 --> |
| | | <el-dialog v-model="shippingDialogVisible" title="订单发货" width="500px"> |
| | | <el-dialog v-model="shippingDialogVisible" |
| | | title="订单发货" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="订单号"> |
| | | <span>{{ currentRecord.orderNo }}</span> |
| | |
| | | <el-form-item label="客户名称"> |
| | | <span>{{ currentRecord.customer }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="发货日期" prop="shippingDate"> |
| | | <el-date-picker |
| | | v-model="shippingDate" |
| | | <el-form-item label="发货日期" |
| | | prop="shippingDate"> |
| | | <el-date-picker v-model="shippingDate" |
| | | type="date" |
| | | placeholder="选择发货日期" |
| | | style="width: 100%" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | value-format="YYYY-MM-DD" /> |
| | | </el-form-item> |
| | | <el-form-item label="物流公司" prop="logisticsCompany"> |
| | | <el-select v-model="logisticsCompany" placeholder="请选择物流公司" style="width: 100%"> |
| | | <el-option label="顺丰速运" value="顺丰速运"></el-option> |
| | | <el-option label="圆通速递" value="圆通速递"></el-option> |
| | | <el-option label="中通快递" value="中通快递"></el-option> |
| | | <el-option label="申通快递" value="申通快递"></el-option> |
| | | <el-option label="韵达速递" value="韵达速递"></el-option> |
| | | <el-form-item label="物流公司" |
| | | prop="logisticsCompany"> |
| | | <el-select v-model="logisticsCompany" |
| | | placeholder="请选择物流公司" |
| | | style="width: 100%"> |
| | | <el-option label="顺丰速运" |
| | | value="顺丰速运"></el-option> |
| | | <el-option label="圆通速递" |
| | | value="圆通速递"></el-option> |
| | | <el-option label="中通快递" |
| | | value="中通快递"></el-option> |
| | | <el-option label="申通快递" |
| | | value="申通快递"></el-option> |
| | | <el-option label="韵达速递" |
| | | value="韵达速递"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="物流单号" prop="trackingNo"> |
| | | <el-input v-model="trackingNo" placeholder="请输入物流单号"></el-input> |
| | | <el-form-item label="物流单号" |
| | | prop="trackingNo"> |
| | | <el-input v-model="trackingNo" |
| | | placeholder="请输入物流单号"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="发货备注" prop="shippingRemark"> |
| | | <el-input type="textarea" v-model="shippingRemark" rows="3" placeholder="请输入发货备注"></el-input> |
| | | <el-form-item label="发货备注" |
| | | prop="shippingRemark"> |
| | | <el-input type="textarea" |
| | | v-model="shippingRemark" |
| | | rows="3" |
| | | placeholder="请输入发货备注"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="saveShipping">确 定</el-button> |
| | | <el-button @click="shippingDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="saveShipping">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed,onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus, Search } from '@element-plus/icons-vue' |
| | | import {listPage,add,update,deletePaymentShipping} from "@/api/salesManagement/paymentShipping.js" |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | listPage, |
| | | add, |
| | | update, |
| | | deletePaymentShipping, |
| | | } from "@/api/salesManagement/paymentShipping.js"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | |
| | | const total = ref(0) |
| | | const total = ref(0); |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | getList(); |
| | | }); |
| | | |
| | | const getList = () => { |
| | | loading.value = true |
| | | loading.value = true; |
| | | listPage({...searchForm,...pagination}).then(res => { |
| | | if(res.code === 200){ |
| | | recordList.value = res.data.records |
| | | total.value = res.data.total |
| | | loading.value = false |
| | | console.log(recordList.value) |
| | | recordList.value = res.data.records; |
| | | total.value = res.data.total; |
| | | loading.value = false; |
| | | console.log(recordList.value); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 响应式数据 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | const searchForm = reactive({ |
| | | orderNo: '', |
| | | paymentStatus: '', |
| | | shippingStatus: '' |
| | | }) |
| | | orderNo: "", |
| | | paymentStatus: "", |
| | | shippingStatus: "", |
| | | }); |
| | | |
| | | const recordList = ref([]) |
| | | const recordList = ref([]); |
| | | |
| | | const pagination = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | size: 10, |
| | | }); |
| | | |
| | | const dialogVisible = ref(false) |
| | | const dialogTitle = ref('新增记录') |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref("新增记录"); |
| | | const form = reactive({ |
| | | orderNo: '', |
| | | customer: '', |
| | | orderNo: "", |
| | | customer: "", |
| | | orderAmount: 0, |
| | | paymentStatus: '未付款', |
| | | shippingStatus: '待发货', |
| | | shippingDate: '', |
| | | trackingNo: '', |
| | | remark: '' |
| | | }) |
| | | paymentStatus: "未付款", |
| | | shippingStatus: "待发货", |
| | | shippingDate: "", |
| | | trackingNo: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | // orderNo: [{ required: true, message: '请输入订单号', trigger: 'blur' }], |
| | | customer: [{ required: true, message: '请选择客户', trigger: 'change' }], |
| | | orderAmount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }], |
| | | paymentStatus: [{ required: true, message: '请选择付款状态', trigger: 'change' }], |
| | | shippingStatus: [{ required: true, message: '请选择发货状态', trigger: 'change' }] |
| | | } |
| | | customer: [{ required: true, message: "请选择客户", trigger: "change" }], |
| | | orderAmount: [{ required: true, message: "请输入订单金额", trigger: "blur" }], |
| | | paymentStatus: [ |
| | | { required: true, message: "请选择付款状态", trigger: "change" }, |
| | | ], |
| | | shippingStatus: [ |
| | | { required: true, message: "请选择发货状态", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | const isEdit = ref(false) |
| | | const editId = ref(null) |
| | | const paymentDialogVisible = ref(false) |
| | | const shippingDialogVisible = ref(false) |
| | | const currentRecord = ref({}) |
| | | const paymentAmount = ref(0) |
| | | const paymentRemark = ref('') |
| | | const shippingDate = ref('') |
| | | const logisticsCompany = ref('') |
| | | const trackingNo = ref('') |
| | | const shippingRemark = ref('') |
| | | const formRef = ref() |
| | | const isEdit = ref(false); |
| | | const editId = ref(null); |
| | | const paymentDialogVisible = ref(false); |
| | | const shippingDialogVisible = ref(false); |
| | | const currentRecord = ref({}); |
| | | const paymentAmount = ref(0); |
| | | const paymentRemark = ref(""); |
| | | const shippingDate = ref(""); |
| | | const logisticsCompany = ref(""); |
| | | const trackingNo = ref(""); |
| | | const shippingRemark = ref(""); |
| | | const formRef = ref(); |
| | | |
| | | // 方法 |
| | | const getPaymentStatusType = (status) => { |
| | | const getPaymentStatusType = status => { |
| | | const statusMap = { |
| | | '未付款': 'danger', |
| | | '已付款': 'success', |
| | | '部分付款': 'warning' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 未付款: "danger", |
| | | 已付款: "success", |
| | | 部分付款: "warning", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const getShippingStatusType = (status) => { |
| | | const getShippingStatusType = status => { |
| | | const statusMap = { |
| | | '待发货': 'warning', |
| | | '已发货': 'primary', |
| | | '已签收': 'success' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 待发货: "warning", |
| | | 已发货: "primary", |
| | | 已签收: "success", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | // 搜索逻辑已在computed中处理 |
| | | getList() |
| | | } |
| | | getList(); |
| | | }; |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.orderNo = '' |
| | | searchForm.paymentStatus = '' |
| | | searchForm.shippingStatus = '' |
| | | } |
| | | searchForm.orderNo = ""; |
| | | searchForm.paymentStatus = ""; |
| | | searchForm.shippingStatus = ""; |
| | | }; |
| | | |
| | | const handleAdd = () => { |
| | | dialogTitle.value = '新增记录' |
| | | isEdit.value = false |
| | | form.orderNo = '' |
| | | form.customer = '' |
| | | form.orderAmount = 0 |
| | | form.paymentStatus = '未付款' |
| | | form.shippingStatus = '待发货' |
| | | form.shippingDate = '' |
| | | form.trackingNo = '' |
| | | form.remark = '' |
| | | dialogVisible.value = true |
| | | } |
| | | dialogTitle.value = "新增记录"; |
| | | isEdit.value = false; |
| | | form.orderNo = ""; |
| | | form.customer = ""; |
| | | form.orderAmount = 0; |
| | | form.paymentStatus = "未付款"; |
| | | form.shippingStatus = "待发货"; |
| | | form.shippingDate = ""; |
| | | form.trackingNo = ""; |
| | | form.remark = ""; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleView = (row) => { |
| | | const handleView = row => { |
| | | // 查看记录详情 |
| | | ElMessage.info('查看记录详情功能待实现') |
| | | } |
| | | ElMessage.info("查看记录详情功能待实现"); |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | dialogTitle.value = '编辑记录' |
| | | isEdit.value = true |
| | | editId.value = row.id |
| | | Object.assign(form, row) |
| | | dialogVisible.value = true |
| | | } |
| | | const handleEdit = row => { |
| | | dialogTitle.value = "编辑记录"; |
| | | isEdit.value = true; |
| | | editId.value = row.id; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handlePayment = (row) => { |
| | | currentRecord.value = row |
| | | paymentAmount.value = row.orderAmount - row.paidAmount |
| | | paymentRemark.value = '' |
| | | paymentDialogVisible.value = true |
| | | } |
| | | const handlePayment = row => { |
| | | currentRecord.value = row; |
| | | paymentAmount.value = row.orderAmount - row.paidAmount; |
| | | paymentRemark.value = ""; |
| | | paymentDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleShipping = (row) => { |
| | | currentRecord.value = row |
| | | shippingDate.value = '' |
| | | logisticsCompany.value = '' |
| | | trackingNo.value = '' |
| | | shippingRemark.value = '' |
| | | shippingDialogVisible.value = true |
| | | } |
| | | const handleShipping = row => { |
| | | currentRecord.value = row; |
| | | shippingDate.value = ""; |
| | | logisticsCompany.value = ""; |
| | | trackingNo.value = ""; |
| | | shippingRemark.value = ""; |
| | | shippingDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm('确认删除该记录吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("确认删除该记录吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }).then(() => { |
| | | let ids = [row.id] |
| | | let ids = [row.id]; |
| | | deletePaymentShipping(ids).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('删除成功') |
| | | getList() |
| | | ElMessage.success("删除成功"); |
| | | getList(); |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const savePayment = () => { |
| | | currentRecord.value.paidAmount = Number(currentRecord.value.paidAmount) + paymentAmount.value |
| | | currentRecord.value.paidAmount = |
| | | Number(currentRecord.value.paidAmount) + paymentAmount.value; |
| | | if(currentRecord.value.paidAmount == currentRecord.value.orderAmount){ |
| | | currentRecord.value.paymentStatus = '已付款' |
| | | currentRecord.value.paymentStatus = "已付款"; |
| | | }else{ |
| | | currentRecord.value.paymentStatus = '部分付款' |
| | | currentRecord.value.paymentStatus = "部分付款"; |
| | | } |
| | | update(currentRecord.value).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('付款信息已保存') |
| | | paymentDialogVisible.value = false |
| | | getList() |
| | | ElMessage.success("付款信息已保存"); |
| | | paymentDialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }) |
| | | |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const saveShipping = () => { |
| | | if (!shippingDate.value || !logisticsCompany.value || !trackingNo.value) { |
| | | ElMessage.warning('请填写完整的发货信息') |
| | | return |
| | | ElMessage.warning("请填写完整的发货信息"); |
| | | return; |
| | | } |
| | | currentRecord.value.shippingStatus = '已发货' |
| | | currentRecord.value.shippingStatus = "已发货"; |
| | | update(currentRecord.value).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('发货信息已保存') |
| | | shippingDialogVisible.value = false |
| | | getList() |
| | | ElMessage.success("发货信息已保存"); |
| | | shippingDialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate((valid) => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (isEdit.value) { |
| | | // 编辑 |
| | | update(form).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('编辑成功') |
| | | getList() |
| | | ElMessage.success("编辑成功"); |
| | | getList(); |
| | | } |
| | | }) |
| | | }); |
| | | } else { |
| | | // 新增 |
| | | add(form).then(res => { |
| | | if(res.code === 200){ |
| | | ElMessage.success('新增成功') |
| | | getList() |
| | | ElMessage.success("新增成功"); |
| | | getList(); |
| | | } |
| | | }) |
| | | }); |
| | | } |
| | | dialogVisible.value = false |
| | | dialogVisible.value = false; |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = (val) => { |
| | | pagination.current = val.page |
| | | pagination.size = val.limit |
| | | } |
| | | const handleCurrentChange = val => { |
| | | pagination.current = val.page; |
| | | pagination.size = val.limit; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| src/views/salesManagement/salesQuotation/index.vue
src/views/salesManagement/salespersonManagement/index.vue
src/views/tool/gen/importTable.vue |