index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. <template>
  2. <div class="photo">
  3. <div class="left">
  4. <div class="upload my-photo-upload">
  5. <!-- <el-upload
  6. v-model:file-list="fileList"
  7. class="upload-demo"
  8. multiple
  9. :show-file-list="false"
  10. :http-request="handleRequest"
  11. :on-change="handleChange"
  12. :before-upload="handleUpload"
  13. :limit="10"
  14. >
  15. <el-button type="primary">上传照片</el-button>
  16. </el-upload> -->
  17. <el-button type="primary" @click="addCaseFileHandlerAll">
  18. 上传照片
  19. </el-button>
  20. <el-button
  21. type="primary"
  22. @click="handleSwitchGrid"
  23. :icon="sortType ? FullScreen : Menu"
  24. >{{ sortType ? "横排" : "竖排" }}</el-button
  25. >
  26. </div>
  27. <draggable
  28. ref="childRef"
  29. :caseId="caseId"
  30. :sortType="sortType"
  31. @changeList="changeList"
  32. @handleItem="handleItem"
  33. @delImage="handleImageDel"
  34. />
  35. </div>
  36. <div class="right">
  37. <div class="tools">
  38. <el-button @click="handleMark">箭头</el-button>
  39. <el-button @click="handleLine">标引</el-button>
  40. <el-button @click="handleSymbol">符号</el-button>
  41. <el-button @click="handleText">文本</el-button>
  42. <el-button @click="handleSave" type="success">保存</el-button>
  43. <el-button @click="handleClear" v-if="hasDrawData" type="warning"
  44. >清空</el-button
  45. >
  46. <el-button @click="handleFree" v-if="isShowExitEdit" type="warning"
  47. >退出编辑</el-button
  48. >
  49. </div>
  50. <canvas id="canvas" v-show="true"></canvas>
  51. <edit
  52. :show="editing.show"
  53. :data="editing.data"
  54. @update="handleEditingUpdate"
  55. @del="handleEditingDel"
  56. @close="handleEditingClose"
  57. />
  58. </div>
  59. </div>
  60. </template>
  61. <script setup>
  62. import { onMounted, ref, computed, onUnmounted, reactive } from "vue";
  63. import { Menu, FullScreen } from "@element-plus/icons-vue";
  64. import { Swiper, SwiperSlide } from "swiper/vue";
  65. import "swiper/css";
  66. // import { addCaseFile } from "@/store/caseFile";
  67. import { addCaseImgFile, addCaseImgFileAll } from "../quisk";
  68. import {
  69. saveCaseImgTagData,
  70. getCaseImgTagData,
  71. submitMergePhotos,
  72. } from "@/store/case";
  73. import Scene from "@/core/Scene.js";
  74. import draggable from "./draggable.vue";
  75. import edit from "./edit.vue";
  76. import { ElMessage, ElMessageBox } from "element-plus";
  77. const props = defineProps({ caseId: Number });
  78. const editing = ref({
  79. show: false,
  80. data: {},
  81. });
  82. const newlist = ref([]);
  83. const fileList = ref([]);
  84. const swiperRef = ref(null);
  85. const childRef = ref(null);
  86. const caseId = ref(props.caseId);
  87. const sortType = ref(false);
  88. const drawMode = ref(0);
  89. const isShowExitEdit = computed(() => drawMode.value > 0);
  90. const loadedDrawData = ref();
  91. const hasDrawData = ref(false);
  92. let scene = null;
  93. const addCaseFileHandler = async () => {
  94. await addCaseImgFile({
  95. caseId: caseId.value,
  96. data: {
  97. imgUrl: "",
  98. imgInfo: "",
  99. id: "",
  100. sort: "",
  101. },
  102. });
  103. refresh();
  104. };
  105. const addCaseFileHandlerAll = async () => {
  106. await addCaseImgFileAll({
  107. caseId: caseId.value,
  108. data: {
  109. imgUrl: "",
  110. imgInfo: "",
  111. id: "",
  112. sort: "",
  113. },
  114. });
  115. refresh();
  116. };
  117. function refresh() {
  118. console.log("changeList", childRef.value);
  119. if (childRef.value) {
  120. childRef.value.getList();
  121. }
  122. }
  123. const changeList = async (list) => {
  124. //同步数据
  125. if (!loadedDrawData.value) {
  126. const res = await getCaseImgTagData(caseId.value);
  127. if (res.data) {
  128. if (res.data.data) {
  129. loadedDrawData.value = res.data.data;
  130. }
  131. if ("isHorizontal" in res.data) {
  132. // console.error("sortType.value", sortType.value, !res.data.isHorizontal);
  133. sortType.value = !res.data.isHorizontal;
  134. }
  135. } else {
  136. loadedDrawData.value = [];
  137. }
  138. }
  139. let newList = [];
  140. list.map((item, index) => {
  141. if (sortType.value) {
  142. newList.push([item]);
  143. } else {
  144. if (index % 2 == 0) {
  145. let newItem = list[index + 1] ? [item, list[index + 1]] : [item];
  146. newList.push(newItem);
  147. }
  148. }
  149. });
  150. newlist.value = newList;
  151. const arr = [];
  152. newList.map((i) => arr.push(JSON.parse(JSON.stringify(i))));
  153. const type = sortType.value ? 2 : 1;
  154. if (scene) {
  155. scene.load(arr, type, loadedDrawData.value || []);
  156. console.log("changeList", arr, type, loadedDrawData.value);
  157. }
  158. };
  159. const renderCanvas = () => {
  160. const canvas = document.getElementById("canvas");
  161. scene = new Scene(canvas);
  162. scene.init();
  163. window.scene = scene;
  164. scene.on("mode", (mode) => {
  165. console.warn("mode", mode);
  166. drawMode.value = mode;
  167. });
  168. scene.on("markerExist", () => {
  169. ElMessage.error("该案件已有方向标注!");
  170. });
  171. scene.on("confirmDelete", async ({ id, type }) => {
  172. const res = await ElMessageBox.confirm("是否删除该部件?", "温馨提示", {
  173. confirmButtonText: "确定",
  174. cancelButtonText: "取消",
  175. type: "default",
  176. });
  177. if (res) {
  178. window.scene.deleteItemById(id, type);
  179. }
  180. });
  181. scene.on("data", (data) => {
  182. let hasData = false;
  183. Object.keys(data).forEach((key) => {
  184. if (Array.isArray(data[key])) {
  185. if (data[key].length > 0) {
  186. hasData = true;
  187. }
  188. }
  189. });
  190. hasDrawData.value = hasData;
  191. console.log("sync", data, hasData);
  192. });
  193. scene.on("edit", (editData) => {
  194. console.log("editData", editData);
  195. editing.value.show = true;
  196. editing.value.data = editData;
  197. // debugger;
  198. });
  199. scene.on("autoSave", () => {
  200. console.log("autoSave");
  201. handleAutoSave();
  202. });
  203. scene.on("submitScreenshot", () => {
  204. if (window.scene) {
  205. // const data = new FormData();
  206. // window.scene.blobScreens.forEach((b, index) => {
  207. // data.append("files", b, index);
  208. // });
  209. const data = {
  210. files: window.scene.blobScreens.map(
  211. (b, index) => new File([b], `${Date.now()}-${index}.jpg`)
  212. ),
  213. caseId: caseId.value,
  214. };
  215. setTimeout(() => {
  216. submitMergePhotos(data);
  217. window.scene.endScreenshot();
  218. }, 500);
  219. }
  220. });
  221. };
  222. const onSwiper = (swiper) => {
  223. console.log("onSwiper");
  224. swiperRef.value = swiper;
  225. };
  226. const onSlideChange = (swiper) => {
  227. console.log(swiper);
  228. };
  229. const handleChange = (val, list) => {
  230. fileList.value = list;
  231. console.log("handleChange", val, list, fileList.value);
  232. };
  233. const handleRequest = (val, list) => {
  234. console.log("handleRequest", val, list);
  235. };
  236. const handleUpload = (val) => {
  237. console.log("handleUpload", val);
  238. };
  239. const handleItem = (item) => {
  240. let active = sortType.value ? item : Math.floor(item / 2);
  241. // swiperRef.value.slideTo(active);
  242. console.log("handleItem", item, active);
  243. };
  244. const handleDetele = async (item) => {
  245. if (
  246. await confirm("删除该场景,将同时从案件和融合模型中移除,确定要删除吗?")
  247. ) {
  248. const scenes = getCaseScenes(list.value.filter((item) => item !== scene));
  249. await replaceCaseScenes(props.caseId, scenes);
  250. refresh();
  251. }
  252. };
  253. const handleSwitchGrid = async () => {
  254. const res = await ElMessageBox.confirm(
  255. "切换模版不包括标注内容,确定要切换吗?",
  256. "温馨提示",
  257. {
  258. confirmButtonText: "确定",
  259. cancelButtonText: "取消",
  260. type: "default",
  261. }
  262. );
  263. if (res) {
  264. sortType.value = !sortType.value;
  265. window.scene.setMode(0);
  266. window.scene.clearScene();
  267. handleClear();
  268. }
  269. };
  270. const handleLine = () => {
  271. if (window.scene) {
  272. window.scene.setMode(1);
  273. }
  274. };
  275. const handleMark = () => {
  276. if (window.scene) {
  277. window.scene.setMode(2);
  278. }
  279. };
  280. const handleSymbol = () => {
  281. if (window.scene) {
  282. window.scene.setMode(3);
  283. }
  284. };
  285. const handleText = () => {
  286. if (window.scene) {
  287. window.scene.setMode(4);
  288. }
  289. };
  290. const handleSave = async () => {
  291. if (window.scene) {
  292. const data = scene.player.getDrawData();
  293. scene.player.syncDrawData();
  294. console.log("data", data);
  295. const res = await saveCaseImgTagData({
  296. caseId: caseId.value,
  297. data: data,
  298. isHorizontal: !sortType.value,
  299. });
  300. ElMessage.success("保存成功!");
  301. console.log("res", res);
  302. }
  303. };
  304. const handleAutoSave = async () => {
  305. if (window.scene) {
  306. const data = scene.player.getDrawData();
  307. scene.player.syncDrawData();
  308. await saveCaseImgTagData({
  309. caseId: caseId.value,
  310. data: data,
  311. isHorizontal: !sortType.value,
  312. });
  313. }
  314. };
  315. const handleFree = () => {
  316. if (window.scene) {
  317. window.scene.setMode(0);
  318. }
  319. };
  320. const handleClear = () => {
  321. if (window.scene) {
  322. window.scene.player.clear();
  323. }
  324. };
  325. const handleEditingUpdate = (data) => {
  326. // console.log("update", data);
  327. if (window.scene) {
  328. window.scene.editing(data);
  329. }
  330. };
  331. const handleEditingDel = (form) => {
  332. if (window.scene) {
  333. const { id, type } = form;
  334. console.log("handleEditingDel", form);
  335. window.scene.deleteItemById(id, type);
  336. window.scene.setMode(0);
  337. }
  338. };
  339. const handleEditingClose = () => {
  340. window.scene.setMode(0);
  341. };
  342. const handleImageDel = (ids) => {
  343. console.log("handleImageDel", ids);
  344. if (window.scene) {
  345. window.scene.deleteImageDataByIds(ids);
  346. }
  347. };
  348. onMounted(() => {
  349. renderCanvas();
  350. console.warn("renderCanvas");
  351. });
  352. </script>
  353. <style lang="scss">
  354. .my-photo-upload {
  355. .upload-demo {
  356. display: inline-block;
  357. margin-right: 20px;
  358. position: relative;
  359. bottom: -1px;
  360. .el-upload-list {
  361. display: none;
  362. }
  363. }
  364. }
  365. </style>
  366. <style lang="scss" scoped>
  367. #canvas {
  368. width: 100%;
  369. height: 100%;
  370. }
  371. .photo {
  372. display: flex;
  373. height: 100%;
  374. .left {
  375. width: 260px;
  376. padding: 16px 24px 30px 0;
  377. height: calc(100% - 46.16px);
  378. overflow-y: auto;
  379. background: #ffffff;
  380. box-shadow: 10px 0 10px -10px rgba(0, 0, 0, 0.15);
  381. // box-shadow: 0px 2px 8px 0px rgba(0,0,0,0.15);
  382. }
  383. .right {
  384. width: calc(100% - 260px);
  385. background-color: var(--bgColor);
  386. padding-left: 24px;
  387. height: calc(100% - 0px);
  388. position: relative;
  389. .tools {
  390. position: absolute;
  391. top: 15px;
  392. left: 30px;
  393. }
  394. .swiperItem {
  395. height: calc(100vh - 155.16px);
  396. width: calc((100vh - 156.16px) * 0.707);
  397. background: #ffffff;
  398. .swiperList {
  399. padding: 0 60px;
  400. height: 100%;
  401. .page {
  402. font-weight: 400;
  403. font-size: 12px;
  404. color: rgba(0, 0, 0, 0.85);
  405. line-height: 22px;
  406. text-align: right;
  407. margin-top: 30px;
  408. }
  409. .itemper {
  410. height: calc(50% - 100px);
  411. padding: 60px 0 0 0;
  412. .text {
  413. margin-top: 16px;
  414. border-radius: 0px 0px 0px 0px;
  415. border: 1px dotted #cccccc;
  416. text-align: center;
  417. font-family: Microsoft YaHei, Microsoft YaHei;
  418. font-weight: 400;
  419. font-size: 14px;
  420. line-height: 30px;
  421. color: rgba(0, 0, 0, 0.85);
  422. }
  423. .itemImg {
  424. width: 100%;
  425. height: calc(100% - 48px);
  426. display: block;
  427. object-fit: cover;
  428. }
  429. }
  430. .oneItemper {
  431. height: calc(100% - 120px);
  432. .itemImg {
  433. }
  434. }
  435. }
  436. }
  437. }
  438. }
  439. </style>
  440. <style scoped>
  441. :global(.body-layer) {
  442. padding-right: 0 !important;
  443. }
  444. </style>