Xbar-RList.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. <template>
  2. <div class="container1">
  3. <div class="databox">
  4. <el-scrollbar :style="{ height: Height + 'px' }">
  5. <div class="box">
  6. <div class="title">
  7. <div style="display: flex; align-items: center">
  8. <div class="bg"></div>
  9. 样本数据录入
  10. </div>
  11. <div class="header" v-show="!addStatus && !editStatus">
  12. <Search
  13. :searchOptions="searchForm"
  14. ref="searchRef"
  15. @data-list="getTableData"
  16. @reset-list="reset"
  17. />
  18. </div>
  19. <div class="btns">
  20. <el-upload
  21. v-if="!addStatus && !editStatus"
  22. style="float: left"
  23. :action="uploadUrl"
  24. :on-success="handleSuccess"
  25. :before-upload="beforeUpload"
  26. :limit="1"
  27. accept=".xlsx, .xls"
  28. :show-file-list="false"
  29. ref="uploadRef"
  30. >
  31. <el-button size="small" type="primary">Excel导入</el-button>
  32. </el-upload>
  33. <el-button
  34. v-if="!addStatus && !editStatus"
  35. style="margin-left: 15px"
  36. type="primary"
  37. size="small"
  38. class="btn"
  39. @click="
  40. exportData(
  41. '/api/v1/spc/downloadTemplate',
  42. '/spc/template/XBarR控制图数据导入模版.xlsx'
  43. )
  44. "
  45. >模版下载</el-button
  46. >
  47. <el-button
  48. style="margin-left: 15px"
  49. v-if="!addStatus && !editStatus"
  50. type="primary"
  51. size="small"
  52. class="btn"
  53. @click="changeaddstatus"
  54. >新增</el-button
  55. >
  56. <el-button
  57. style="margin-left: 15px"
  58. v-if="!addStatus && !editStatus"
  59. type="primary"
  60. size="small"
  61. class="btn"
  62. @click="compute"
  63. >计算</el-button
  64. >
  65. <el-button
  66. v-if="editStatus || addStatus"
  67. type="success"
  68. size="small"
  69. class="btn"
  70. @click="submit"
  71. >确定</el-button
  72. >
  73. <el-button
  74. v-if="editStatus || addStatus"
  75. type="info"
  76. size="small"
  77. class="btn"
  78. @click="canceleOp"
  79. >取消</el-button
  80. >
  81. </div>
  82. </div>
  83. <div class="info" v-if="!addStatus">
  84. <el-table
  85. :data="tableData"
  86. border
  87. :style="{
  88. height: maxHeight - 150 + 'px',
  89. width: maxWidth + 'px',
  90. }"
  91. :show-overflow-tooltip="true"
  92. :row-class-name="tableRowClassName"
  93. >
  94. <el-table-column
  95. align="center"
  96. prop="dateStr"
  97. sortable
  98. label="日期"
  99. >
  100. <template #default="{ row }"
  101. ><span>{{ row.dateStr }}</span>
  102. </template>
  103. </el-table-column>
  104. <el-table-column align="center" prop="model" label="产品型号">
  105. <template #default="{ row }"
  106. ><span>{{ row.model }}</span>
  107. </template>
  108. </el-table-column>
  109. <el-table-column align="center" prop="batchNo" label="生产批号">
  110. <template #default="{ row }"
  111. ><span>{{ row.batchNo }}</span>
  112. </template>
  113. </el-table-column>
  114. <el-table-column align="center" prop="source" label="数据来源">
  115. <template #default="{ row }"
  116. ><span>{{ row.source }}</span>
  117. </template>
  118. </el-table-column>
  119. <el-table-column
  120. align="center"
  121. prop="accuracys"
  122. label="数据"
  123. :show-overflow-tooltip="false"
  124. >
  125. <template #default="{ row }">
  126. <el-tooltip placement="top">
  127. <template #content>
  128. <div v-for="(item, index) in row.accuracys" :key="index">
  129. <span>数值{{ index + 1 }}: {{ item }}</span>
  130. </div>
  131. </template>
  132. <div class="ellipsis-text">
  133. {{ row.accuracys.join(", ") }}
  134. </div>
  135. </el-tooltip>
  136. </template>
  137. </el-table-column>
  138. <!-- <el-table-column
  139. align="center"
  140. prop="avg"
  141. label="平均值"
  142. width="60"
  143. /><el-table-column
  144. align="center"
  145. prop="range"
  146. label="极差"
  147. width="60"
  148. />--><!--<el-table-column
  149. align="center"
  150. prop="checkUser"
  151. label="检测人"
  152. >
  153. <template #default="{ row }"
  154. ><span>{{ row.checkUser }}</span>
  155. </template> </el-table-column
  156. ><el-table-column
  157. align="center"
  158. prop="checkDeviceNo"
  159. label="检查设备编号"
  160. >
  161. <template #default="{ row }"
  162. ><span>{{ row.checkDeviceNo }}</span>
  163. </template>
  164. </el-table-column>-->
  165. <el-table-column align="center" prop="abnormal" label="是否异常">
  166. <template #default="{ row }"
  167. ><span>{{ row.abnormal }}</span>
  168. </template>
  169. </el-table-column>
  170. <el-table-column
  171. align="center"
  172. prop="abnormal1"
  173. label="异常原因"
  174. :show-overflow-tooltip="false"
  175. >
  176. <template #default="{ row }">
  177. <el-tooltip placement="top">
  178. <template #content>
  179. <div
  180. v-for="(item, index) in row.abnormal1.split(',')"
  181. :key="index"
  182. >
  183. <span>{{ item }}</span>
  184. </div>
  185. </template>
  186. <div class="ellipsis-text">
  187. {{ row.abnormal1 }}
  188. </div>
  189. </el-tooltip>
  190. </template>
  191. </el-table-column>
  192. <!-- <el-table-column align="center" prop="analyseUser" label="分析人">
  193. <template #default="{ row }"
  194. ><span>{{ row.analyseUser }}</span>
  195. </template>
  196. </el-table-column>-->
  197. <el-table-column align="center" prop="measure" label="处置措施">
  198. <template #default="{ row }"
  199. ><span>{{ row.measure }}</span>
  200. </template>
  201. </el-table-column>
  202. <el-table-column align="center" width="240" prop="" label="操作">
  203. <template #default="{ row }">
  204. <el-button
  205. v-if="row.source === '自动'"
  206. type="primary"
  207. size="small"
  208. class="btn"
  209. @click="dataAcquisition(row)"
  210. style="height: 25px"
  211. >采集数据</el-button
  212. >
  213. <el-button
  214. type="primary"
  215. size="small"
  216. class="btn"
  217. @click="updataItem(row)"
  218. style="height: 25px"
  219. >修改</el-button
  220. >
  221. <el-button
  222. type="info"
  223. size="small"
  224. class="btn"
  225. style="height: 25px"
  226. @click="deleteSubmit(row.id)"
  227. >删除</el-button
  228. >
  229. </template>
  230. </el-table-column>
  231. </el-table>
  232. <Pagination
  233. :total="currentOption.total"
  234. :page="currentOption.page"
  235. :limit="currentOption.limit"
  236. :pageSizes="currentOption.pageSizes"
  237. v-model:page="currentOption.page"
  238. v-model:limit="currentOption.limit"
  239. @pagination="getTableData"
  240. />
  241. </div>
  242. <div class="info" v-else>
  243. <el-form
  244. ref="ruleFormRef"
  245. :model="addData"
  246. :rules="rules"
  247. label-width="auto"
  248. class="formStyle"
  249. >
  250. <el-form-item label="日期" prop="dateStr">
  251. <el-date-picker
  252. v-model="addData.dateStr"
  253. type="date"
  254. aria-label="Pick a date"
  255. placeholder="Pick a date"
  256. style="width: 100%"
  257. format="YYYY-MM-DD"
  258. value-format="YYYY-MM-DD"
  259. />
  260. </el-form-item>
  261. <el-form-item label="任务编号" prop="qualityTaskId">
  262. <el-select
  263. v-model="addData.qualityTaskId"
  264. @change="
  265. (value) => {
  266. taskChange(value);
  267. }
  268. "
  269. >
  270. <el-option
  271. v-for="(item, index) in taskOption"
  272. :key="index"
  273. :label="item.taskCode"
  274. :value="Number(item.id)"
  275. />
  276. </el-select>
  277. </el-form-item>
  278. <el-form-item label="产品型号" prop="model">
  279. <el-input :disabled="true" v-model="addData.model" />
  280. </el-form-item>
  281. <el-form-item label="数据来源" prop="source">
  282. <el-input :disabled="true" v-model="addData.source" />
  283. </el-form-item>
  284. <el-form-item label="生产批号" prop="batchNo">
  285. <el-input v-model="addData.batchNo" />
  286. </el-form-item>
  287. <el-form-item
  288. v-for="(item, index) in addData.accuracys"
  289. :label="'数值' + (index + 1)"
  290. :key="index"
  291. :rules="[
  292. {
  293. required: true,
  294. trigger: 'change',
  295. },
  296. ]"
  297. >
  298. <el-input-number
  299. :precision="2"
  300. :step="0.01"
  301. style="width: 100%"
  302. v-model="addData.accuracys[index]"
  303. />
  304. </el-form-item>
  305. <!-- <el-form-item label="平均值" prop="avg">
  306. <el-input v-model="addData.avg" />
  307. </el-form-item>
  308. <el-form-item label="极差" prop="range">
  309. <el-input v-model="addData.range" />
  310. </el-form-item> -->
  311. <!-- <el-form-item label="检测人" prop="checkUser">
  312. <el-input v-model="addData.checkUser" />
  313. </el-form-item>
  314. <el-form-item label="检查设备编号" prop="checkDeviceNo">
  315. <el-input v-model="addData.checkDeviceNo" />
  316. </el-form-item>-->
  317. <!-- <el-form-item label="是否异常" prop="abnormal">
  318. <el-input v-model="addData.abnormal" />
  319. </el-form-item> -->
  320. <!-- <el-form-item label="分析人" prop="analyseUser">
  321. <el-input v-model="addData.analyseUser" />
  322. </el-form-item>-->
  323. <el-form-item label="处置措施" prop="measure">
  324. <el-input v-model="addData.measure" />
  325. </el-form-item>
  326. </el-form>
  327. </div>
  328. </div>
  329. </el-scrollbar>
  330. </div>
  331. </div>
  332. </template>
  333. <script setup>
  334. import { ref } from "vue";
  335. import * as echarts from "echarts";
  336. import { useDictionaryStore } from "@/store";
  337. import {
  338. getData,
  339. addDatas,
  340. deleteData,
  341. updateData,
  342. getTaskCode,
  343. } from "@/api/analysis";
  344. import Search from "@/components/Search/index.vue";
  345. import { XBarRCompute, collectData } from "@/api/analysis";
  346. import { useCrud } from "@/hooks/userCrud";
  347. const { Utils } = useCrud({
  348. src: "/api/v1/spc/pDownloadTemplate",
  349. });
  350. const { exportData } = Utils;
  351. const emit = defineEmits(["tableData"]);
  352. const dataAcquisition = async (row) => {
  353. const { data } = await collectData({
  354. qualitySpcRecordId: row.id,
  355. model: row.model,
  356. batchNo: row.batchNo,
  357. operation: lableValue.value,
  358. });
  359. emit("tableData", data);
  360. getTableData();
  361. };
  362. const compute = async () => {
  363. const accuracysList = ref([]);
  364. for (const item of tableData.value) {
  365. if (item.accuracys?.length > 0) {
  366. accuracysList.value.push(item.accuracys);
  367. } else {
  368. ElMessage.error(`产品 "${item.model}" 的 "${item.batchNo}" 批次数据为空`);
  369. return;
  370. }
  371. }
  372. const { data } = await XBarRCompute({
  373. dataList: accuracysList.value,
  374. scale: 4,
  375. });
  376. emit("tableData", data);
  377. };
  378. const tableRowClassName = ({ row }) => {
  379. if (row.abnormal === "是") {
  380. return "warning-row";
  381. }
  382. return "";
  383. };
  384. const { dicts } = useDictionaryStore();
  385. const year = ref("0");
  386. const currentOption = reactive({
  387. total: 0,
  388. page: 1,
  389. limit: 10,
  390. pageSizes: [10, 20, 30, 40, 50],
  391. });
  392. const lableValue = ref("");
  393. const searchRef = ref(null);
  394. const getTaskOption = async () => {
  395. const { data } = await getTaskCode({
  396. operationCode: lableValue.value,
  397. });
  398. taskOption.value = data;
  399. };
  400. const getTableData = async () => {
  401. const { data, code, msg } = await getData({
  402. ...searchRef.value.searchForm,
  403. pageNo: currentOption.page,
  404. pageSize: currentOption.limit,
  405. // yearStr: year.value,
  406. operation: lableValue.value,
  407. });
  408. if (code == "200") {
  409. tableData.value = data.records;
  410. showData.value = { ...data, list: null };
  411. currentOption.total = data.totalCount;
  412. currentOption.page = data.pageNo;
  413. oldDataJSON.value = JSON.stringify(data.records);
  414. }
  415. disabled.value = false;
  416. };
  417. const searchForm = [
  418. {
  419. label: "日期",
  420. prop: "createds",
  421. type: "daterange",
  422. },
  423. {
  424. label: "产品型号",
  425. prop: "model",
  426. type: "input",
  427. },
  428. {
  429. label: "生产批号",
  430. prop: "batchNo",
  431. type: "input",
  432. },
  433. ];
  434. //编辑状态
  435. const editStatus = ref(false);
  436. const addStatus = ref(false);
  437. const changeEditstatus = () => {
  438. editStatus.value = !changeEditstatus.value;
  439. addStatus.value = false;
  440. };
  441. const changeaddstatus = () => {
  442. addStatus.value = !addStatus.value;
  443. editStatus.value = false;
  444. };
  445. const canceleOp = () => {
  446. addStatus.value = false;
  447. editStatus.value = false;
  448. reset();
  449. };
  450. const disabled = ref(false);
  451. const tableData = ref([]);
  452. //Form
  453. const ruleFormRef = ref(null);
  454. const rules = {
  455. qualityTaskId: [
  456. {
  457. required: true,
  458. trigger: "change",
  459. },
  460. ],
  461. dateStr: [
  462. {
  463. required: true,
  464. trigger: "change",
  465. },
  466. ],
  467. model: [
  468. {
  469. required: true,
  470. trigger: "change",
  471. },
  472. ],
  473. batchNo: [
  474. {
  475. required: true,
  476. trigger: "change",
  477. },
  478. ],
  479. accuracy1: [
  480. {
  481. required: true,
  482. trigger: "change",
  483. },
  484. ],
  485. accuracy2: [
  486. {
  487. required: true,
  488. trigger: "change",
  489. },
  490. ],
  491. accuracy3: [
  492. {
  493. required: true,
  494. trigger: "change",
  495. },
  496. ],
  497. accuracy4: [
  498. {
  499. required: true,
  500. trigger: "change",
  501. },
  502. ],
  503. accuracy5: [
  504. {
  505. required: true,
  506. trigger: "change",
  507. },
  508. ],
  509. checkUser: [
  510. {
  511. required: true,
  512. trigger: "change",
  513. },
  514. ],
  515. checkDeviceNo: [
  516. {
  517. required: true,
  518. trigger: "change",
  519. },
  520. ],
  521. // abnormal: [
  522. // {
  523. // required: true,
  524. // trigger: "change",
  525. // },
  526. // ],
  527. analyseUser: [
  528. {
  529. required: true,
  530. trigger: "change",
  531. },
  532. ],
  533. measure: [
  534. {
  535. required: false,
  536. trigger: "change",
  537. },
  538. ],
  539. };
  540. const resItem = {
  541. // abnormal: "",
  542. analyseUser: "",
  543. batchNo: "",
  544. checkDeviceNo: "",
  545. checkUser: "",
  546. dateStr: "",
  547. measure: "",
  548. model: "",
  549. };
  550. const addData = ref({
  551. // abnormal: "",
  552. analyseUser: "",
  553. batchNo: "",
  554. checkDeviceNo: "",
  555. checkUser: "",
  556. dateStr: "",
  557. measure: "",
  558. model: "",
  559. });
  560. const accuracysSum = ref(0);
  561. const taskChange = (value) => {
  562. taskOption.value.forEach((item) => {
  563. if (item.id == value) {
  564. addData.value.model = item.prodtModel;
  565. addData.value.source = item.source;
  566. if (item.source === "手动") {
  567. accuracysSum.value = Number(item.processCount);
  568. addData.value.accuracys = [];
  569. let array = [];
  570. for (let i = 0; i < accuracysSum.value; i++) {
  571. array.push(0);
  572. }
  573. addData.value.accuracys = array;
  574. } else {
  575. addData.value.accuracys = [];
  576. }
  577. }
  578. });
  579. };
  580. const oldDataJSON = ref("");
  581. const showData = ref({});
  582. const opOptions = ref([...dicts.spc_operation]);
  583. //修改
  584. const updataItem = (row) => {
  585. editStatus.value = true;
  586. addData.value = row;
  587. addStatus.value = true;
  588. };
  589. const taskOption = ref([]);
  590. const value = ref(opOptions.value[0].remark);
  591. const showLable = ref("调阻");
  592. const maxHeight = ref(null);
  593. const maxWidth = ref(null);
  594. const Height = ref(0);
  595. const setHeight = () => {
  596. Height.value = document.querySelector(".databox").clientHeight;
  597. maxHeight.value = document.querySelector(".info").clientHeight;
  598. maxWidth.value = document.querySelector(".info").clientWidth;
  599. };
  600. //当新增或者编辑的确定操作
  601. const submit = () => {
  602. if (editStatus.value == true) {
  603. updateSubmit();
  604. } else {
  605. addSubmit();
  606. }
  607. };
  608. const addSubmit = async () => {
  609. await ruleFormRef.value.validate(async (valid, fields) => {
  610. if (valid) {
  611. const { data, code } = await addDatas({
  612. ...addData.value,
  613. yearStr: year.value,
  614. operation: lableValue.value,
  615. });
  616. if (code == "200") {
  617. ElMessage.success("添加成功!");
  618. reset();
  619. getTableData();
  620. }
  621. } else {
  622. ElMessage.error("请检查表单信息");
  623. }
  624. });
  625. };
  626. const deleteSubmit = async (id) => {
  627. const { data, code } = await deleteData({
  628. id,
  629. });
  630. if (code == "200") {
  631. ElMessage.success("删除成功!");
  632. getTableData();
  633. }
  634. };
  635. const updateSubmit = async () => {
  636. const { data, code } = await updateData({
  637. ...addData.value,
  638. });
  639. if (code == "200") {
  640. ElMessage.success("更新成功!");
  641. reset();
  642. getTableData();
  643. }
  644. };
  645. const reset = () => {
  646. addStatus.value = false;
  647. editStatus.value = false;
  648. addData.value = { ...resItem };
  649. searchRef.value.searchForm = {};
  650. currentOption.value = {
  651. total: 0,
  652. page: 0,
  653. limit: 12,
  654. pageSizes: [12],
  655. operation: value.value,
  656. };
  657. getTableData();
  658. };
  659. const uploadUrl = ref("");
  660. const beforeUpload = (file) => {
  661. const isExcel =
  662. file.type === "application/vnd.ms-excel" ||
  663. file.type ===
  664. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  665. if (!isExcel) {
  666. ElMessage.error("只能上传Excel文件!");
  667. }
  668. return isExcel;
  669. };
  670. const uploadRef = ref("uploadRef");
  671. const handleSuccess = (response) => {
  672. if (response.code === "200") {
  673. ElMessage.success("Excel导入成功!");
  674. uploadRef.value.clearFiles();
  675. getTableData();
  676. } else {
  677. ElMessage.error("Excel导入失败!");
  678. }
  679. };
  680. onMounted(async () => {
  681. setHeight();
  682. year.value = new Date().getFullYear() + "";
  683. opOptions.value.forEach((item) => {
  684. if (item.dictLabel == showLable.value) {
  685. lableValue.value = item.dictValue;
  686. }
  687. });
  688. await getTableData();
  689. uploadUrl.value =
  690. import.meta.env.VITE_APP_BASE_API +
  691. "/api/v1/spc/xBarRUpload?operation=" +
  692. lableValue.value;
  693. });
  694. onBeforeUnmount(() => {});
  695. const init = (data) => {
  696. lableValue.value = data;
  697. getTaskOption();
  698. getTableData();
  699. };
  700. // 暴露 init 方法
  701. defineExpose({
  702. init,
  703. });
  704. </script>
  705. <style lang="scss" scoped>
  706. @media print {
  707. #print {
  708. margin-left: -18%;
  709. }
  710. }
  711. :deep(.el-table .warning-row) {
  712. background-color: rgb(241, 142, 142) !important;
  713. }
  714. .ellipsis-text {
  715. white-space: nowrap; /* 禁止换行 */
  716. overflow: hidden; /* 隐藏超出部分 */
  717. text-overflow: ellipsis; /* 显示省略号 */
  718. width: 100%; /* 宽度占满单元格 */
  719. }
  720. .formStyle {
  721. width: 400px;
  722. margin: 20px auto;
  723. }
  724. .container1 {
  725. width: 100%;
  726. height: 100%;
  727. display: flex;
  728. background-color: white;
  729. .infobox {
  730. width: 200px;
  731. .header {
  732. height: 120px;
  733. border-bottom: 2px solid #00000010;
  734. padding: 20px;
  735. }
  736. .body {
  737. padding: 20px;
  738. }
  739. }
  740. .databox {
  741. flex: 1;
  742. border-left: 2px solid #00000010;
  743. .box {
  744. height: 710px;
  745. padding: 5px 20px;
  746. display: flex;
  747. flex-direction: column;
  748. .illustrate {
  749. padding: 20px 60px;
  750. }
  751. .tableTitle {
  752. text-align: center;
  753. margin: 10px 0;
  754. padding-right: 40px;
  755. }
  756. .header {
  757. margin-top: 20px;
  758. //margin-left: 100px;
  759. display: flex;
  760. width: 100%;
  761. height: auto;
  762. }
  763. //.title {
  764. // height: 50px;
  765. // display: flex;
  766. // align-items: center;
  767. // margin-bottom: 10px;
  768. // justify-content: space-between;
  769. // .btns {
  770. // display: flex;
  771. // align-items: center;
  772. // .btn {
  773. // height: 24px;
  774. // font-size: 14px;
  775. // margin: 0 5px;
  776. // }
  777. // }
  778. //}
  779. .info {
  780. margin-top: 20px;
  781. flex: 1;
  782. height: 300px;
  783. }
  784. }
  785. }
  786. }
  787. </style>