package.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/api/system/tijian.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/proposal/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/utils/websocket.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/111111.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/ceshi.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/doctor/checkAll/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/system/package/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/system/tijian/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
vue.config.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
package.json
@@ -68,13 +68,17 @@ "jsencrypt": "3.0.0-rc.1", "jspdf": "^3.0.1", "lodash": "^4.17.21", "mammoth": "^1.9.1", "moment": "^2.30.1", "nprogress": "0.2.0", "pdf2json": "^3.1.6", "pdfjs-dist": "^5.3.31", "pdfmake": "^0.2.20", "pinyin-match": "^1.2.2", "print-js": "^1.6.0", "quill": "1.3.7", "screenfull": "5.0.2", "socket.io-client": "^4.8.1", "sortablejs": "1.10.2", "tduck-form-generator": "^1.8.0", "tinymce": "^7.9.1", src/api/system/tijian.js
@@ -55,13 +55,13 @@ }); } export function getProParentIdDxList() { export function getProParentIdDxList(xb) { return request({ url: "/hosp/project/getProParentIdDxList", method: "get", // params: { // pname: pname, // }, params: { xb: xb, }, }); } src/components/proposal/index.vue
@@ -19,8 +19,8 @@ <el-form inline @submit.native.prevent="search"> <el-form-item> <el-radio-group v-model="tjproposal" @input="radiotjproposalChange"> <el-radio-button label="0">快捷建议</el-radio-button> <el-radio-button label="1">常用建议</el-radio-button> <el-radio-button label="0">常用建议</el-radio-button> <el-radio-button label="1">快捷建议</el-radio-button> </el-radio-group> </el-form-item> <el-form-item v-show="tjproposal == 0"> src/utils/websocket.js
New file @@ -0,0 +1,80 @@ // src/utils/websocket.js let socket = null; let reconnectAttempts = 0; const maxReconnectAttempts = 5; const reconnectInterval = 5000; let messageCallback = null; const initWebSocket = (token, callback) => { if (socket && socket.readyState === WebSocket.OPEN) { console.log('WebSocket 已连接,无需重复初始化'); return; } if (!token) { console.error('未提供 token,无法建立 WebSocket 连接'); callback('error', '未找到 token,无法建立 WebSocket 连接'); return; } const wsUrl = `ws://192.168.1.2:5011/ws?token=${encodeURIComponent(token)}`; console.log('尝试连接 WebSocket:', wsUrl); try { socket = new WebSocket(wsUrl); } catch (error) { console.error('WebSocket 初始化失败:', error); callback('error', '无法初始化 WebSocket 连接'); return; } messageCallback = callback; socket.onopen = () => { console.log('WebSocket 连接成功'); reconnectAttempts = 0; }; socket.onmessage = (event) => { console.log('WebSocket 收到原始消息:', event.data); if (messageCallback) { messageCallback('message', event.data); } }; socket.onerror = (error) => { console.error('WebSocket 错误:', error); if (messageCallback) { messageCallback('error', 'WebSocket 连接错误'); } }; socket.onclose = (event) => { console.log('WebSocket 连接关闭,代码:', event.code, '原因:', event.reason); if (reconnectAttempts < maxReconnectAttempts) { setTimeout(() => { console.log(`尝试重连 (${reconnectAttempts + 1}/${maxReconnectAttempts})`); reconnectAttempts++; initWebSocket(token, callback); }, reconnectInterval); } else { if (messageCallback) { messageCallback('error', 'WebSocket 重连失败,请检查网络或服务器'); } } }; }; const closeWebSocket = () => { if (socket) { socket.close(1000, '浏览器关闭,正常断开'); socket = null; console.log('WebSocket 连接已关闭'); } }; window.addEventListener('beforeunload', () => { closeWebSocket(); }); export { initWebSocket, closeWebSocket }; src/views/111111.vue
New file @@ -0,0 +1,335 @@ <template> <div> <h3>前端 Word 编辑器(TinyMCE)</h3> <div class="actions"> <input type="file" accept=".docx" @change="importWord" ref="fileInput" style="margin-right: 10px;" /> <button @click="exportToWord">导出 Word</button> <button @click="exportToPDF">导出 PDF</button> </div> <editor v-model="content" :init="editorInit" :api-key="apiKey"></editor> </div> </template> <script> import Editor from '@tinymce/tinymce-vue'; import { Document, Packer, Paragraph, TextRun, ImageRun, Table as DocxTable, TableRow as DocxTableRow, TableCell as DocxTableCell } from 'docx'; import { saveAs } from 'file-saver'; import jsPDF from 'jspdf'; import html2canvas from 'html2canvas'; import mammoth from 'mammoth'; export default { components: { Editor }, data() { return { apiKey: '3ceaxd5ckw4te35xj38vj3p5rmmeyv0x8pq2yrr92rwdiqzp', content: '', editorInit: { height: 500, menubar: true, plugins: [ 'advlist autolink lists link image charmap print preview anchor', 'searchreplace visualblocks code fullscreen', 'insertdatetime media table paste code help wordcount' ], toolbar: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image table | removeformat | help', images_upload_handler: (blobInfo, success, failure) => { const reader = new FileReader(); reader.onload = () => success(reader.result); reader.onerror = () => failure('图片上传失败'); reader.readAsDataURL(blobInfo.blob()); }, content_style: 'body { font-family: SimSun, Arial; font-size: 14px; }', table_default_attributes: { border: 1 }, readonly: false, language: 'zh-CN', }, }; }, methods: { /** 导入 Word 文档 */ importWord(event) { const file = event.target.files[0]; if (!file) { this.$message.error('请选择一个 .docx 文件'); return; } if (!file.name.endsWith('.docx')) { this.$message.error('请上传 .docx 格式的文件'); return; } const reader = new FileReader(); reader.onload = async (e) => { try { const arrayBuffer = e.target.result; const result = await mammoth.convertToHtml({ arrayBuffer }); this.content = result.value || '<p>无内容</p>'; this.$message.success('Word 文档导入成功'); this.$refs.fileInput.value = ''; } catch (err) { console.error('导入 Word 失败:', err); this.$message.error('导入 Word 文档失败:' + err.message); } }; reader.onerror = () => { this.$message.error('读取文件失败'); }; reader.readAsArrayBuffer(file); }, /** 导出为 Word */ exportToWord() { if (!this.content || this.content.trim() === '') { this.$message.error('编辑器内容为空,无法导出'); return; } this.parseHtmlToDocx(this.content).then((elements) => { const doc = new Document({ sections: [{ properties: {}, children: elements }], }); Packer.toBlob(doc).then((blob) => { saveAs(blob, '文档.docx'); this.$message.success('导出 Word 成功'); }).catch((err) => { console.error('生成 Word 失败:', err); this.$message.error('生成 Word 失败:' + err.message); }); }).catch((err) => { console.error('解析 HTML 失败:', err); this.$message.error('解析 HTML 失败:' + err.message); }); }, /** 解析 HTML 为 docx 元素 */ parseHtmlToDocx(html) { return new Promise((resolve, reject) => { if (!html || typeof html !== 'string') { reject(new Error('无效的 HTML 内容')); return; } console.log('Input HTML:', html); // 调试输入 HTML const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); if (!doc || !doc.body) { reject(new Error('HTML 解析失败:文档结构无效')); return; } const elements = []; const imagePromises = []; const processNode = (node) => { if (node.nodeType !== Node.ELEMENT_NODE) { return; } console.log('Processing node:', node.tagName); // 调试节点 if (node.tagName === 'P') { const childNodes = node.childNodes ? Array.from(node.childNodes) : []; const textRuns = childNodes.map((child) => { if (child.nodeType === Node.TEXT_NODE) { return new TextRun({ text: child.textContent || '' }); } else if (child.tagName === 'B') { return new TextRun({ text: child.textContent || '', bold: true }); } else if (child.tagName === 'I') { return new TextRun({ text: child.textContent || '', italics: true }); } else if (child.tagName === 'IMG') { const promise = this.getImageBase64FromUrl(child.src).then(({ base64, width, height }) => { return new ImageRun({ data: base64, transformation: { width, height }, }); }).catch((err) => { console.error('图片加载失败:', err, 'URL:', child.src); return new TextRun(`[图片: ${child.alt || '图像'}]`); }); imagePromises.push(promise); return promise; } return new TextRun({ text: child.textContent || '' }); }).filter(Boolean); if (textRuns.length) { elements.push(new Paragraph({ children: textRuns })); } } else if (node.tagName === 'H1') { elements.push(new Paragraph({ text: node.textContent || '', heading: 'Heading1' })); } else if (node.tagName === 'UL' || node.tagName === 'OL') { const children = node.children ? Array.from(node.children) : []; children.forEach((li) => { elements.push( new Paragraph({ text: li.textContent || '', bullet: node.tagName === 'UL' ? { level: 0 } : { level: 0, number: true }, }) ); }); } else if (node.tagName === 'TABLE') { const rows = node.querySelectorAll('tr') ? Array.from(node.querySelectorAll('tr')) : []; const tableRows = rows.map((tr) => { const cells = tr.querySelectorAll('td, th') ? Array.from(tr.querySelectorAll('td, th')) : []; const tableCells = cells.map((cell) => { return new DocxTableCell({ children: [new Paragraph(cell.textContent || '')], }); }); return new DocxTableRow({ children: tableCells }); }); if (tableRows.length) { elements.push(new DocxTable({ rows: tableRows })); } } const children = node.children ? Array.from(node.children) : []; children.forEach(processNode); }; const nodes = doc.body.childNodes ? Array.from(doc.body.childNodes) : []; if (nodes.length === 0) { console.warn('HTML 文档无有效节点'); resolve(elements); return; } nodes.forEach(processNode); Promise.all(imagePromises).then((imageRuns) => { let imageIndex = 0; elements.forEach((element, index) => { if (!(element instanceof Paragraph)) { console.log('Skipping non-Paragraph element at index', index, 'type:', element.constructor.name); return; // 跳过非 Paragraph 元素 } if (!Array.isArray(element.children)) { console.warn('Element at index', index, 'has invalid children:', element.children); element.children = []; // 初始化为空数组 return; } console.log('Processing element at index', index, 'children:', element.children); element.children = element.children.map((child, childIndex) => { if (child instanceof Promise) { const imageRun = imageRuns[imageIndex++] || new TextRun('[图片加载失败]'); console.log('Replacing Promise at child index', childIndex, 'with:', imageRun); return imageRun; } return child; }); }); resolve(elements); }).catch((err) => { console.error('图片处理失败:', err); reject(new Error('图片处理失败:' + err.message)); }); }); }, /** 获取图片的 Base64 数据和尺寸 */ getImageBase64FromUrl(url) { return new Promise((resolve, reject) => { if (!url) { reject(new Error('图片 URL 为空')); return; } console.log('Fetching image:', url); if (url.startsWith('data:image/')) { const base64 = url.split(',')[1]; const img = new Image(); img.onload = () => { const maxWidth = 200; const maxHeight = 200; let { width, height } = img; if (width > maxWidth || height > maxHeight) { const ratio = Math.min(maxWidth / width, maxHeight / height); width = Math.round(width * ratio); height = Math.round(height * ratio); } resolve({ base64, width, height }); }; img.onerror = () => reject(new Error('Base64 图片加载失败')); img.src = url; return; } fetch(url, { mode: 'cors' }) .then((response) => { if (!response.ok) throw new Error('网络图片加载失败'); return response.blob(); }) .then((blob) => { const reader = new FileReader(); reader.onloadend = () => { const base64 = reader.result.split(',')[1]; const img = new Image(); img.onload = () => { const maxWidth = 200; const maxHeight = 200; let { width, height } = img; if (width > maxWidth || height > maxHeight) { const ratio = Math.min(maxWidth / width, maxHeight / height); width = Math.round(width * ratio); height = Math.round(height * ratio); } resolve({ base64, width, height }); }; img.onerror = () => reject(new Error('图片读取失败')); img.src = reader.result; }; reader.onerror = () => reject(new Error('图片读取失败')); reader.readAsDataURL(blob); }) .catch((err) => reject(err)); }); }, /** 导出为 PDF */ exportToPDF() { if (!this.content || this.content.trim() === '') { this.$message.error('编辑器内容为空,无法导出'); return; } const contentElement = document.createElement('div'); contentElement.innerHTML = this.content; contentElement.style.padding = '10px'; document.body.appendChild(contentElement); html2canvas(contentElement, { scale: 2 }) .then((canvas) => { const doc = new jsPDF(); const imgData = canvas.toDataURL('image/png'); doc.addImage(imgData, 'PNG', 10, 10, 190, 0); doc.save('文档.pdf'); this.$message.success('导出 PDF 成功'); document.body.removeChild(contentElement); }) .catch((err) => { console.error('生成 PDF 失败:', err); this.$message.error('生成 PDF 失败:' + err.message); document.body.removeChild(contentElement); }); }, }, }; </script> <style> .actions { margin-top: 10px; margin-bottom: 10px; } .actions button { padding: 8px 16px; margin-right: 10px; background-color: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer; } .actions button:hover { background-color: #66b1ff; } </style> src/views/ceshi.vue
File was deleted src/views/doctor/checkAll/index.vue
@@ -2589,14 +2589,22 @@ yichanghuifu() { this.loading = true; this.status1 = 4; let tjNum = this.tjNumber; console.log(this.tjNumber, 111222); huiFuyichangxiangmu(tjNum).then((res) => { console.log(res, 9999999); this.status1 = 4; this.ychfList = res.data; }); if(this.ychfList.length == 0){ this.message.success("暂无异常恢复项目") } }) .catch((error)=>{ console.error("获取项目失败:",error); this.$message.error("获取项目失败") }) .finally(()=>{ this.loading = false; }) }, hfyc(id) { this.$confirm("确认要恢复该项吗?", "提示", { src/views/index.vue
@@ -1,28 +1,5 @@ <template> <div class="centre"> <!-- 今日通知 --> <div style="display: flex; justify-content: center; align-items: center;"> <h4>今日通知</h4> </div> <div class="notice-area"> <el-carousel :interval="2000" direction="vertical" :autoplay="true" :loop="true" height="120px" v-if="groupedNoticeList.length > 0" class="carousel"> <el-carousel-item v-for="(group, index) in groupedNoticeList" :key="index"> <div class="notice-group"> <div class="notice-item" v-for="notice in group" :key="notice.noticeId" @click="goToNotice(notice.noticeId)"> <el-tag size="small" :type="notice.noticeType === '1' ? 'info' : 'warning'"> {{ notice.noticeType === '1' ? '通知' : '公告' }} </el-tag> <span class="notice-title">{{ notice.noticeTitle || '无标题' }}</span> <span class="notice-time">{{ parseTime(notice.createTime, '{y}-{m}-{d}') || '无时间' }}</span> </div> </div> </el-carousel-item> </el-carousel> <div v-else class="no-notice">暂无通知</div> </div> <!-- 今日统计 --> <div style="display: flex; justify-content: center; align-items: center;"> <h4>今日统计</h4> @@ -79,6 +56,7 @@ <script> import { getCustomer, getOrder, getReportToday, getTobeToday, getPieChart, getChart } from "@/api/home"; import { noticeToday } from "@/api/system/notice"; import { initWebSocket } from "@/utils/websocket"; const echarts = require('echarts/lib/echarts'); require('echarts/lib/component/title'); require('echarts/lib/component/tooltip'); @@ -101,27 +79,18 @@ teamYYNum: [], PieChart: [], PieChart2: [], noticeList: [], groupedNoticeList: [], // 分组后的通知列表 loading: false }; }, created() { this.getList(); this.initWebSocket(); }, watch: { $route(to, from) { window.location.reload(); }, noticeList: { handler(newList) { // 将通知按每组三条分组 this.groupedNoticeList = this.chunkArray(newList, 3); console.log('groupedNoticeList:', this.groupedNoticeList); // 调试 }, deep: true } }, @@ -132,49 +101,91 @@ query: { noticeId } }); }, // 初始化 WebSocket 连接 initWebSocket() { const token = this.$store.state.user.token || ''; initWebSocket(token, (type, data) => { if (type === 'error') { this.$notify.error({ title: '错误', message: data, duration: 5000, position: 'top-right' }); return; } try { const message = JSON.parse(data); console.log('WebSocket 解析后消息:', message); if (message.noticeId && message.noticeTitle) { const noticeTypeLabel = message.noticeType === '1' ? '通知' : '公告'; this.$notify({ title: `新${noticeTypeLabel}`, message: message.noticeTitle || '无标题', type: 'success', duration: 5000, // 悬停 5 秒 position: 'top-right', offset: 50, onClick: () => { this.goToNotice(message.noticeId); } }); } else { console.log('未知消息类型:', message); } } catch (error) { console.error('消息解析失败:', error, '原始数据:', data); this.$notify.info({ title: '消息', message: `服务器回应字符串: ${data}`, duration: 5000, position: 'top-right', offset: 50 }); } }); }, getList() { this.loading = true; // 查询所有公告 // 查询所有公告(仅用于初始化数据,可根据需要保留或移除) noticeToday().then(response => { console.log('Notice API response:', response); this.noticeList = response.rows || response.data || []; console.log('noticeList:', this.noticeList); this.loading = false; this.$nextTick(() => { console.log('Carousel updated'); }); }).catch(error => { console.error('Notice API error:', error); this.$message.error("获取通知失败:" + error.message); this.$notify.error({ title: '错误', message: `获取通知失败:${error.message}`, duration: 5000, position: 'top-right' }); this.loading = false; }); // 查询今日登记 getCustomer().then(response => { this.Customer = response.data || response; this.loading = false; }); // 查询今日已检 getOrder().then(response => { this.Order = response.data || response; this.loading = false; }); // 查询今日报告 getReportToday().then(response => { this.ReportToday = response.data || response; this.loading = false; }); // 查询今日待检 getTobeToday().then(response => { this.TobeToday = response.data || response; this.loading = false; }); // 折线图 getChart().then(response => { response.data.forEach(item => { this.LineChart.push(item.date); @@ -211,7 +222,6 @@ this.loading = false; }); // 饼状图 getPieChart().then(response => { if (response.data) { if (response.data.tjdj == 0 || !response.data.tjdj?.length) { @@ -281,23 +291,6 @@ }); }, // 数组分组方法 chunkArray(array, size) { if (!array || array.length === 0) return []; const result = []; for (let i = 0; i < array.length; i += size) { result.push(array.slice(i, i + size)); } // 确保循环滚动平滑,若不足 size 条,补齐 if (array.length % size !== 0 && array.length > size) { const lastGroup = result[result.length - 1]; while (lastGroup.length < size) { lastGroup.push(array[lastGroup.length % array.length]); } } return result; }, parseTime(time, cFormat) { if (!time) return ''; try { @@ -327,62 +320,6 @@ margin: 15px; background-color: #f3f3f3; padding: 10px; } .notice-area { width: 100%; min-height: 120px; /* 调整为三条通知高度 */ background-color: #fff; margin: 10px 0; padding: 0 20px; } .carousel { width: 100% !important; } .notice-group { display: flex; flex-direction: column; height: 120px; /* 确保包含三条通知 */ } .notice-item { display: flex; align-items: center; width: 100%; height: 40px; line-height: 40px; font-size: 14px; cursor: pointer; } .notice-title { margin-left: 10px; font-size: 14px; color: #333; flex-grow: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .notice-time { font-size: 12px; color: #999; margin-left: 20px; } .no-notice { width: 100%; height: 120px; /* 与轮播高度一致 */ line-height: 120px; text-align: center; color: #999; font-size: 14px; } .top { @@ -448,4 +385,27 @@ background-color: #fff; margin-right: 20px; } /* 自定义 Element UI 通知样式 */ .el-notification { min-width: 300px; background-color: #f0f9eb; border-color: #e1f3d8; color: #67c23a; font-size: 16px; padding: 15px 20px; } .el-notification__title { font-weight: bold; } .el-notification__content { font-size: 14px; color: #333; } .el-notification__closeBtn { color: #999; } </style> src/views/system/package/index.vue
@@ -39,16 +39,11 @@ <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> </el-row> <!-- element-loading-background="rgba(0, 0, 0, 0.1)" element-loading-spinner="el-icon-loading" element-loading-text="正在加载中..." --> <template> <el-table v-loading="loading" style="width: 100%" :data="packageList" @selection-change="handleSelectionChange" border> <el-table-column fixed type="selection" width="40" align="center" :show-overflow-tooltip="true" /> <el-table-column label="序号" align="center" width="50" prop="newID" fixed /> <!-- <el-table-column label="编号" align="center" prop="pacId" /> --> <el-table-column label="体检类别" width="100px" align="center" prop="categoryNames" fixed> <template slot-scope="scope"> <dict-tag :options="dict.type.dict_tjtype" :value="scope.row.tjCategory" /> @@ -57,55 +52,21 @@ <el-table-column label="套餐名称" align="center" prop="pacName" width="150px" fixed /> <el-table-column label="原价" width="80px" align="center" prop="price" fixed></el-table-column> <el-table-column label="折扣" width="80px" align="center" prop="limits" fixed></el-table-column> <el-table-column label="现价" width="80px" align="center" prop="newPrice" fixed> </el-table-column> <el-table-column label="单项列表" align="center" prop="allProName" width="1200px"> </el-table-column> <!-- <el-table-column label="项目明细" align="center" prop="allSonName" :show-overflow-tooltip="true" > </el-table-column>--> <el-table-column label="现价" width="80px" align="center" prop="newPrice" fixed></el-table-column> <el-table-column label="单项列表" align="center" prop="allProName" width="1200px"></el-table-column> <el-table-column label="套餐描述" align="center" :show-overflow-tooltip="true" width="120px"> <template slot-scope="scope"> <div class="showInline">{{ scope.row.pacRemark }}</div> </template> </el-table-column> <!-- <el-table-column label="图片" align="center" prop="pacPhone" width="100" :show-overflow-tooltip="true" > <template slot-scope="scope"> <image-preview :src="scope.row.pacPhone" :width="50" :height="50" /> </template> </el-table-column> --> <el-table-column label="关键字" width="110px" align="center" prop="keyNames"></el-table-column> <el-table-column label="是否上架" width="94px" align="center" prop="isOnSale"> <template slot-scope="scope"> <dict-tag :options="dict.type.sys_yes_no" :value="scope.row.isOnSale" /> </template> </el-table-column> <!-- <el-table-column label="详细介绍" :show-overflow-tooltip="true" width="100px" align="center" prop="detail" ></el-table-column> --> <el-table-column label="排序" width="50px" align="center" prop="sort"></el-table-column> <el-table-column label="小程序价格" width="90px" align="center" prop="retailPrice"></el-table-column> <!-- <el-table-column label="原价" width="90px" align="center" prop="counterPrice" ></el-table-column> --> <el-table-column label="已售数量" width="90px" align="center" prop="saleNum"></el-table-column> <el-table-column label="状态" align="center" prop="pacStatus" fixed="right" width="100px"> <template slot-scope="scope"> @@ -119,8 +80,6 @@ v-hasPermi="['system:package:edit']" title="修改"></el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:package:remove']" title="删除"></el-button> <!-- <el-button size="mini" type="text" icon="el-icon-circle-check" @click="handleSeach(scope.row)" v-hasPermi="['system:package:Seach']" title="套餐详情"></el-button> --> </template> </el-table-column> </el-table> @@ -136,8 +95,6 @@ <!-- 修改体检套餐对话框 --> <el-dialog :title="title" :visible.sync="open" width="1400px" append-to-body :close-on-click-modal="false"> <el-form ref="form" :model="form" :rules="rules" label-width="100px" :inline="true"> <!-- <div class="dialo"> <div class="dialo1"> --> <el-form-item label="套餐名称" prop="pacName"> <span slot="label" style="display: inline-block; border-bottom: 2px solid blue" @click="handlePackage"> 套餐名称 @@ -145,51 +102,23 @@ <el-input v-model="form.pacName" placeholder="请输入套餐名称" style="width: 150px" /> </el-form-item> <el-form-item label="套餐状态" prop="pacStatus"> <!-- <el-radio-group v-model="form.pacStatus"> <el-radio :label="0">启用</el-radio> <el-radio :label="1">停用</el-radio> </el-radio-group> --> <el-select v-model="form.pacStatus" placeholder="请选择状态" style="width: 150px" filterable clearable> <el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> </el-select> </el-form-item> <!-- <el-form-item label="套餐类目" prop="categoryId"> <el-select v-model="form.categoryId" placeholder="请选择套餐类目" style="width: 150px" @change="shangpin" filterable clearablez > <el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id" ></el-option> </el-select> </el-form-item> --> <el-form-item label="体检类别" prop="tjCategory"> <el-select v-model="form.tjCategory" placeholder="请选择体检类别" style="width: 150px" filterable clearable> <el-option v-for="dict in dict.type.dict_tjtype" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> </el-select> </el-form-item> <el-form-item label="是否上架" prop="isOnSale"> <!-- <el-radio-group v-model="form.pacStatus"> <el-radio :label="0">启用</el-radio> <el-radio :label="1">停用</el-radio> </el-radio-group> --> <el-select v-model="form.isOnSale" placeholder="请选择是否上架" style="width: 140px" filterable clearable> <el-option v-for="dict in dict.type.sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> </el-select> </el-form-item> <el-form-item label="排序" prop="sort"> <el-input v-model="form.sort" placeholder="请输入排序" style="width: 150px" /> </el-form-item> @@ -197,13 +126,12 @@ <el-input v-model="form.retailPrice" placeholder="请输入小程序价格" style="width: 150px" /> </el-form-item> <el-form-item label="原价" prop="pics"> <el-input v-model="pics" placeholder="请输入原价" style="width: 150px" /> <el-input v-model="pics" placeholder="请输入原价" style="width: 150px" type="number" /> </el-form-item> <el-form-item label="折扣"> <el-input-number style="width: 150px" v-model="youhui" :precision="2" :step="0.1" :max="10" :min="0.1" @change="debounceNumberChange" :debounce="3000"></el-input-number> </el-form-item> <el-form-item label="现价" prop="xianprice"> <el-input v-model="form.xianprice" placeholder="现价" clearable style="width: 140px" @input="changeXianjia" type="number" :debounce="3000" min="0"/> @@ -214,7 +142,6 @@ <el-option v-for="item in keywordList" :key="item.id" :label="item.keyword" :value="item.id"></el-option> </el-select> </el-form-item> <div v-if="!isCollapsed" style="display: flex"> <div> <el-form-item label="套餐描述" prop="pacRemark"> @@ -222,56 +149,19 @@ rows="2"></el-input> </el-form-item> <el-form-item label="详细介绍" prop="detail"> <!-- <el-input v-model="form.detail" placeholder="请输入详细介绍" style="width: 200px" /> --> <editor v-model="form.detail" :min-height="192" style="width: 670px" /> </el-form-item> </div> <!-- </div> --> <div class="dialo2"> <el-form-item label="图片"> <image-upload v-model="form.pacPhone" /> </el-form-item> </div> <!-- </div> --> </div> </el-form> <el-button type="primary" plain size="mini" @click="toggleCollapse">{{ isCollapsed ? "展开" : "收起" }}</el-button> <!-- <el-button type="primary" plain size="mini" icon="el-icon-plus" @click="addmembers()">新增单项</el-button> <el-table v-loading="loading" :data="form.tjProjectList" @selection-change="handleSelectionChange" border max-height="275" style="margin: 10px 0"> <el-table-column label="序号" align="center" type="index" /> <el-table-column label="项目名称" align="center" prop="proName" width="180px"> <template slot-scope="scope"> <el-select filterable v-model="scope.row.proName" placeholder="请选择项目名称" @change="getSelectValue"> <el-option v-for="(item, index) in allList" :key="index" :label="item.proName" :value="item.proName"> </el-option> </el-select> </template> </el-table-column> <el-table-column label="项目明细" align="center" prop="allSonProName" width="400px" :show-overflow-tooltip="true"> </el-table-column> <el-table-column label="原价(元)" align="center" prop="proPrice" width="80px" /> <el-table-column label="现价(元)" align="center" prop="priceNow" width="80px"> <template slot-scope="scope"> <el-input v-model="scope.row.priceNow" autocomplete="off" placeholder="请输入内容"></el-input> </template> </el-table-column> <el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-circle-plus-outline" @click="addmembers(scope.row)" v-hasPermi="['system:package:edit']" title="新增行"></el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click.native.prevent="Delete(scope.$index)" v-hasPermi="['hosp:consumables:remove']" title="删除"></el-button> </template> </el-table-column> </el-table> --> <el-row style="display: flex; width: 1300px"> <el-col> <div style="text-align: center; margin-bottom: 10px; margin-top: 10px"> @@ -287,29 +177,6 @@ :limit.sync="queryParams1.pageSize" @pagination="getDataList" /> </div> </el-col> <!-- <el-col :span="6"> <div style="text-align: center; margin-bottom: 10px; margin-top: 10px" > 明细项目列表 </div> <div class="tab3"> <el-tree class="filter-tree" v-loading="loading" :data="TreedataList" node-key="proId" :props="defaultProps" :filter-node-method="filterNode" show-checkbox @check-change="handleCurrentChecked1" :default-checked-keys="checkedListkey" ref="trees" :render-content="renderContent" > </el-tree> </div> </el-col> --> <el-col> <div class="grid-content bg-purple"> <div style=" @@ -324,12 +191,10 @@ :span-method="objectSpanMethod"> <el-table-column prop="proName" label="检查项目"> </el-table-column> <el-table-column prop="priceOrd" label="原价" align="center" width="80px"> </el-table-column> <el-table-column label="折扣" width="100px"> <template slot-scope="scope"> <!-- 只输入纯数字折扣 --> <el-input v-model.number="scope.row.limits" @input="calculateDiscount(scope.row)" placeholder="输入折扣" size="small" type="number" min="0" step="0.1" max="10"> </el-input> @@ -343,54 +208,16 @@ </el-button> </template> </el-table-column> <!-- <el-table-column prop="proName" label="明细项目" width="260px"> </el-table-column> --> <!-- <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width" width="50px" > <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeletes(scope.row)" title="删除" ></el-button> </template> </el-table-column>--> </el-table> <h4 style="font-weight: 600;padding-left:20px">一共选中{{DataList.length}}项,合计:{{ form.xianprice }}元</h4> </div> </el-col> </el-row> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="cancel">取 消</el-button> </div> </el-dialog> <!-- 套餐详情 --> <!-- <el-dialog :title="title" :visible.sync="Seachopen" width="500px" append-to-body> <template> <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">全选</el-checkbox> <div style="margin: 15px 0px; width: 60px"></div> <el-checkbox-group v-model="newproName" @change="handleCheckedCitiesChange"> <el-checkbox style="margin: 15px 0px; width: 180px" v-for="item in allList" :label="item.proName" :key="item.proId">{{ item.proName }}</el-checkbox> </el-checkbox-group> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitcheckbox">确 定</el-button> <el-button @click="cancel">取 消</el-button> </div> </template> </el-dialog> --> <Packages ref="aaa" @add="handleChanges" /> </div> </template> @@ -483,13 +310,16 @@ xianprice: null, counterPrice: null, limits: 10, keywords: [], }, forms: {}, youhui: 10, initializing: true, // 初始化标志 initializing: true, debounceTimer: null, rules: { pacName: [{ required: true, message: "请输入套餐名称", trigger: "blur" }], pics: [{ required: true, message: "请输入原价", trigger: "blur" }], xianprice: [{ required: true, message: "请输入现价", trigger: "blur" }], }, }; }, @@ -498,7 +328,6 @@ this.getKeyword(); this.getCategory(); }, methods: { debounceNumberChange(currentValue, oldValue) { clearTimeout(this.debounceTimer); @@ -506,16 +335,18 @@ this.numberChange(currentValue, oldValue); }, 300); }, numberChange(currentValue, oldValue) { // 实现折扣变化的逻辑(如果需要) }, updateProPrice(row) { const proPrice = new Big(row.priceOrd); const limits = new Big(row.limits); const proPrice = new Big(row.priceOrd || 0); const limits = new Big(row.limits || 10); const result = proPrice.times(limits.div(10)); row.priceNow = result.toNumber(); this.form.xianprice = this.DataList.reduce((sum, item) => { return sum.plus(new Big(item.priceNow || "0")); return sum.plus(new Big(item.priceNow || 0)); }, new Big(0)).toNumber(); this.youhui = (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10; this.youhui = this.pics ? (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10 : 10; }, calculateDiscount(row) { if (row.limits > 10) { @@ -530,37 +361,79 @@ }, filterNode(value, data) { if (!value) return true; return data.proName.indexOf(value) !== -1 || data.proEngName.indexOf(value) !== -1; return data.proName.indexOf(value) !== -1 || (data.proEngName && data.proEngName.indexOf(value) !== -1); }, getList() { this.loading = true; getPacTjProjectList().then((response) => { this.allList = response.data; this.allList = response.data || []; this.loading = false; }); getPacList(this.queryParams).then((response) => { response.rows.forEach((item, index) => { item.newID = (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1; }); this.total = response.total; this.packageList = response.rows; this.total = response.total || 0; this.packageList = response.rows || []; this.loading = false; }).catch(() => { this.loading = false; this.$message.error("获取套餐列表失败"); }); }, getKeyword() { this.loading = true; listKeyword(this.queryParams).then((response) => { this.keywordList = response.rows; this.keywordList = response.rows || []; this.loading = false; }).catch(() => { this.loading = false; this.$message.error("获取关键字列表失败"); }); }, sel(val) { let id = val; this.keys = id.join(","); const maleId = '4'; // “男”的 ID const femaleId = '5'; // “女”的 ID let selectedIds = [...val]; // 复制当前选中的 ID 数组 // 实现“男”和“女”互斥逻辑 if (selectedIds.includes(maleId) && selectedIds.includes(femaleId)) { selectedIds = selectedIds.slice(-1); // 保留最后选中的一个 this.$message.warning('“男”和“女”关键字互斥,只能选择一个!'); } // 更新 form.keywords 和 keys this.form.keywords = selectedIds; this.keys = selectedIds.join(','); // 调用 getDataList,传递所有选中的关键字 ID(逗号分隔) this.getDataList({ xb: this.keys }); }, getCategory() { this.loading = true; listCategory(this.queryParams).then((response) => { this.categoryList = response.rows; this.categoryList = response.rows || []; this.loading = false; }).catch(() => { this.loading = false; this.$message.error('获取套餐类目失败'); }); }, getDataList(params = {}) { this.loading = true; const query = { ...this.queryParams1, ...params }; search(query).then((response) => { this.Treedata = response.data.list || []; this.total1 = response.data.total || 0; this.pics = this.DataList.reduce((total, item) => total + (item.priceOrd || 0), 0); this.$nextTick(() => { this.$refs.tree.setCheckedKeys(this.checkedNodes); this.initializing = false; }); this.loading = false; }).catch(() => { this.loading = false; this.$message.error('获取项目列表失败'); }); }, shangpin(vals) {}, @@ -586,6 +459,7 @@ xianprice: null, counterPrice: null, limits: 10, keywords: [], }; this.initializing = true; this.resetForm("form"); @@ -629,7 +503,7 @@ this.checkedListkey = []; this.checkedNodes = []; this.getDataList(); this.youhui = 10 this.youhui = 10; }, handleStatusChange(row) { let data = { @@ -637,10 +511,11 @@ pacStatus: row.pacStatus, }; let text = row.pacStatus === "0" ? "启用" : "停用"; this.$confirm("确认要" + text + row.pacName + "套餐吗?") this.$modal .confirm("确认要" + text + row.pacName + "套餐吗?") .then(() => updateStatus(data)) .then(() => { this.msgSuccess(text + "成功"); this.$modal.msgSuccess(text + "成功"); }) .catch(() => { row.pacStatus = row.pacStatus === "0" ? "0" : "1"; @@ -676,7 +551,7 @@ getPacInFo(row.pacId).then((response) => { const data = response.data || {}; Object.keys(data).forEach((key) => { this.$set(this.form, key, response.data[key]); this.$set(this.form, key, data[key]); }); this.youhui = data.limits || 10; this.pics = data.price || 0; @@ -689,9 +564,7 @@ this.checkedkey = this.DataList.map(item => item.proId || ''); this.checkedListkey = [...this.checkedkey]; this.checkedNodes = [...this.checkedkey]; return this.getDataList(); }).then(() => { this.loading = false; this.$nextTick(() => { @@ -706,32 +579,23 @@ if (this.form.pacName) { if (!this.form.tjProjectList) { this.form.tjProjectList = []; this.form.tjProjectList.push({ id: parseInt(length), proName: "", allSonProName: "", proPrice: "", priceNow: "", proId: "", }); } else { this.form.tjProjectList.push({ id: parseInt(length), proName: "", allSonProName: "", proPrice: "", priceNow: "", proId: "", }); } this.form.tjProjectList.push({ id: this.form.tjProjectList.length + 1, proName: "", allSonProName: "", proPrice: "", priceNow: "", proId: "", }); } else { Message.warning("请先填写套餐名称"); this.$modal.msgWarning("请先填写套餐名称"); } this.$forceUpdate(); }, Delete(index) { if (this.form.tjProjectList.length === 0) { this.$alert("请先选择要删除的数据", "提示", { confirmButtonText: "确定" }); this.$modal.alert("请先选择要删除的数据", "提示", { confirmButtonText: "确定" }); } else { this.form.tjProjectList.splice(index, 1); } @@ -801,20 +665,6 @@ </span> ); }, getDataList() { this.loading = true; search(this.queryParams1).then((response) => { this.Treedata = response.data.list; this.total1 = response.data.total; this.pics = this.DataList.reduce((total, item) => total + item.priceOrd, 0); this.$nextTick(() => { this.$refs.tree.setCheckedKeys(this.checkedNodes); this.initializing = false; }); }); this.loading = false; }, handleFilterInput() { this.queryParams1.page = 1; this.getDataList(); @@ -824,7 +674,7 @@ }, handleCurrentChecked(data, checked, indeterminate) { if (this.initializing) { return; // 初始化时不处理 return; } if (checked) { if (!this.DataList.some((item) => item.proId === data.proId)) { @@ -848,57 +698,21 @@ } this.updateTotalPrice(); } }, updateTotalPrice() { this.form.xianprice = this.DataList.reduce((sum, item) => { return sum.plus(new Big(item.priceNow || "0")); return sum.plus(new Big(item.priceNow || 0)); }, new Big(0)).toNumber(); this.pics = this.DataList.reduce((total, item) => total + item.priceOrd, 0); this.pics = this.DataList.reduce((total, item) => total + (item.priceOrd || 0), 0); }, changeXianjia() { if (this.form.xianprice !== 0) { if (this.form.xianprice !== 0 && this.pics) { this.youhui = (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10; } }, /* numberChangeXianPrice(currentValue, oldValue) { if (this.form.xianprice === this.lastXianPrice || !this.form.xianprice) return; clearTimeout(this.debounceTimer); this.debounceTimer = setTimeout(() => { this.$confirm("确定修改所有子项的折扣吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(() => { this.youhui = currentValue; let totalYsprice = new Big(0); this.DataList.forEach((item) => { item.limits = this.youhui; const ordPrice = new Big(item.priceOrd); const discount = new Big(item.limits); const result = ordPrice.times(discount.div(10)); item.priceNow = result.toNumber(); totalYsprice = totalYsprice.plus(new Big(item.priceNow)); }); if (!totalYsprice.eq(this.form.xianprice)) { const diff = new Big(this.form.xianprice).minus(totalYsprice); if (this.DataList.length > 0) { const lastItem = this.DataList[this.DataList.length - 1]; const newYsPrice = new Big(lastItem.priceNow).plus(diff).toNumber(); this.$set(this.DataList[this.DataList.length - 1], "priceNow", newYsPrice); } } this.lastXianPrice = this.form.xianprice; }) .catch(() => { this.youhui = oldValue; }); }, 500); }, */ spliceData() { for (var i = 0; i < this.DataList.length; i++) { for (var j = i + 1; j < this.DataList.length; j++) { for (let i = 0; i < this.DataList.length; i++) { for (let j = i + 1; j < this.DataList.length; j++) { if (this.DataList[i].proId === this.DataList[j].proId) { this.DataList.splice(j, 1); j--; @@ -910,39 +724,40 @@ handleCurrentChecked1(data, checked, checkedNodes) { if (!checked) { this.DataList = this.DataList.filter((item) => item.proId !== data.proId); this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + item.priceOrd, 0); this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + (item.priceOrd || 0), 0); this.pics = this.TotalPrice1; } else { this.DataList.push(data); this.pics = this.DataList.reduce((sum, item) => sum + item.priceOrd, 0); this.pics = this.DataList.reduce((sum, item) => sum + (item.priceOrd || 0), 0); this.spliceData(); this.TotalPrice1 = this.pics; } }, handleDeletes(row) { this.DataList = this.DataList.filter((item) => item.proParentId !== row.proParentId); this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + item.priceOrd, 0); this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + (item.priceOrd || 0), 0); }, submitForm() { // 检查现价是否发生变化 this.$refs.form.validate((valid) => { if (valid) { if (this.form.xianprice !== this.lastXianPrice) { this.$confirm("确定修改所有子项的折扣吗?", "提示", { this.$modal .confirm("确定修改所有子项的折扣吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }).then(() => { // 更新所有子项的折扣 }) .then(() => { this.youhui = (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10; let totalYsprice = new Big(0); this.DataList.forEach((item) => { item.limits = this.youhui; const ordPrice = new Big(item.priceOrd); const ordPrice = new Big(item.priceOrd || 0); const discount = new Big(item.limits); const result = ordPrice.times(discount.div(10)); item.priceNow = result.toNumber(); totalYsprice = totalYsprice.plus(new Big(item.priceNow)); }); if (!totalYsprice.eq(this.form.xianprice)) { const diff = new Big(this.form.xianprice).minus(totalYsprice); if (this.DataList.length > 0) { @@ -951,19 +766,17 @@ this.$set(this.DataList[this.DataList.length - 1], "priceNow", newYsPrice); } } // 保存数据 return this.saveData(); }).catch(() => { // 用户取消操作,恢复原来的现价 }) .catch(() => { this.form.xianprice = this.lastXianPrice; }); } else { // 现价没有变化,直接保存 this.saveData(); } } }); }, // 新增一个方法处理保存逻辑 saveData() { this.form.limits = this.youhui; this.form.price = this.pics; @@ -996,10 +809,12 @@ pacRemark: this.form.pacRemark, detail: this.form.detail, }; return saveOreditTjPacNew(data).then((res) => { this.$modal.msgSuccess("保存成功"); this.open = false; this.getList(); }).catch(() => { this.$message.error("保存失败"); }); }, handleDelete(row) { @@ -1027,11 +842,11 @@ }, }; </script> <style> .el-tooltip__popper { max-width: 800px; } .showInline { overflow: hidden; text-overflow: ellipsis; @@ -1040,35 +855,22 @@ line-clamp: 2; -webkit-box-orient: vertical; } .pag { width: 100%; display: flex; justify-content: center; } .pag1 { width: 30%; } .dialog-footer { position: absolute; left: 40%; bottom: 2%; } .tab3 { max-height: 400px; overflow-y: auto; border: 1px solid #d9d9d9; } /* .custom-tree-node { flex: 1; display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px; } */ </style> src/views/system/tijian/index.vue
@@ -2572,6 +2572,8 @@ }, handleClick(tab, event) { if (this.activeNames == "second") { console.log(this.form); this.DataList = []; this.checkedkey = []; this.TreedataList = []; @@ -2590,15 +2592,11 @@ }); } }, // gaibian(val){ // let pname = val; // getProParentIdDxList(pname).then(response=>{ // this.Treedata = response.data.list; // }) // }, getDataList() { this.loading = true; getProParentIdDxList().then((response) => { console.log(this.form); getProParentIdDxList(this.form.cusSex).then((response) => { this.Treedata = response.data.list; // 回显 TreedataList 到 DataList vue.config.js
@@ -54,7 +54,11 @@ [process.env.VUE_APP_BASE_API]: { // target: `https://ltpeis.xaltjdkj.cn:5801/`, // target: `http://192.168.1.99:5012`, <<<<<<< HEAD target: `http://192.168.1.2:5011`, ======= target: `http://192.168.1.113:5011`, >>>>>>> 2ee7a81c2eb0df1fc473da6dbbaa13df662967c5 // target: `http://192.168.1.2:5011`, // // target: `http://192.168.0.99:8080/ltkj-admin`, // target: `https://ltpeis.xaltjdkj.cn:5011/ltkj-admin`,