repetitionGraphy.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. <template>
  2. <!-- 查看平面图页面 -->
  3. <div id='repetitionGraphy'>
  4. <div class='buttons'>
  5. <el-button icon='el-icon-back' size='mini' @click='backRouter'></el-button>
  6. <template v-if="!hasModel">
  7. <el-upload class='upload-demo' action='string' :http-request='uploadAndSubmit' :show-file-list='false'
  8. v-if="step==-1" accept=".jpg,.png" :before-upload="beforeUpload">
  9. <el-button size="mini">{{ hasGraph ? '替换平面图图片' : '上传平面图图片' }}</el-button>
  10. </el-upload>
  11. <el-button style='float:right;' size='mini' type='primary' v-if="step==0" @click="nextStep"
  12. :disabled="nextStepBtnDisable">下一步
  13. </el-button>
  14. <el-button style='float:right;' size='mini' type='primary' v-if="step==1" @click="nextStep" :disabled="timeOut">
  15. 保存
  16. </el-button>
  17. <el-button style='float:right;' size='mini' v-if="step==1" @click="lastStep">上一步</el-button>
  18. </template>
  19. <template v-else>
  20. <el-button v-if='!isEdit' size='mini' @click='changeGraphy'>{{ hasGraph ? '替换模型文件' : '关联模型文件' }}</el-button>
  21. <el-button v-if='(sign||otherSign)&&!isEdit' size='mini' @click='editGraphy'>编辑平面图</el-button>
  22. <el-button v-if='isEdit' size='mini' @click='cancel'>取消</el-button>
  23. <el-button v-if='isEdit' size='mini' @click='confirm' type='primary'>确认</el-button>
  24. </template>
  25. <span style='float:right;' v-if='file.FolderName'>当前对应的模型文件:{{ file.FolderName }} - {{ file.FloorName }}</span>
  26. </div>
  27. <!-- 展示图片 -->
  28. <div class='drawImg'>
  29. <div style="width: 100%;height: 100%;position: relative;" v-if="hasGraph">
  30. <drawFloor ref='drawFloor' :isEdit='isEdit' :showTools='true' :id='1' @changeSign='changeSign'
  31. @getGraphSuc="getGraphSuc"></drawFloor>
  32. <div class="stepContainer" v-if="step>-1">
  33. <el-steps :active="step" align-center finish-status="success" process-status="process">
  34. <el-step title="设定比例尺" :description="stepDes[0]"></el-step>
  35. <el-step title="楼层对齐" :description="stepDes[1]"></el-step>
  36. </el-steps>
  37. </div>
  38. <ul class="floorList" v-if="step==1">
  39. <template>
  40. <li style="border-bottom:1px solid #ccc;text-align: center;padding: 0">选择对齐楼层</li>
  41. <li v-for="t in floorList" :key="t.FloorID">
  42. <el-radio v-model="floorRadio" :label="t" @change="changeFloor" :disabled="t.FloorID == FloorID">
  43. {{ t.FloorLocalID || t.FloorLocalName }}
  44. </el-radio>
  45. </li>
  46. </template>
  47. <!-- <template v-else>-->
  48. <!-- <li style="text-align: center;padding: 0">-->
  49. <!-- 暂无对齐楼层-->
  50. <!-- </li>-->
  51. <!-- </template>-->
  52. </ul>
  53. </div>
  54. <div style="width: 100%;height: 100%" v-else>
  55. <div class="center" style="padding-top: 300px">
  56. <i class="icon-wushuju iconfont"></i>暂无平面图
  57. </div>
  58. </div>
  59. </div>
  60. <!-- 查看图片弹窗 -->
  61. <checkGraphy ref='checkGraphy' @refresh='refresh' :alreadyRelatedModel='alreadyRelatedModel'></checkGraphy>
  62. <!-- 设定比例尺弹窗 -->
  63. <setScaleDialog ref="setScaleDialog" @textChange="scaleTextChange"></setScaleDialog>
  64. <!-- 退出时弹窗提示 -->
  65. <backTips ref='backTips' @back="runBack"></backTips>
  66. </div>
  67. </template>
  68. <script>
  69. import drawFloor from './drawGraphy/drawFloor'
  70. import checkGraphy from './drawGraphy/checkGraphy' //查看图片
  71. import setScaleDialog from '@/components/ready/buildfloor/setScaleDialog'
  72. import backTips from '@/components/ready/buildfloor/backTips'
  73. import {floorUpdateOutline, floorQueryAndSign, manageUpdateFloor} from '@/api/scan/request'
  74. import {getFileNameById, countModel} from '@/api/model/file'
  75. import {EditLineItem, SItemStatus, SImageShowType, IconTextItem} from "@saga-web/cad-engine"
  76. import {SColor} from "@saga-web/draw"
  77. import {SImageItem} from '@saga-web/graph/lib'
  78. import {SMathUtil} from "@saga-web/cad-engine/lib/utils/SMathUtil"
  79. export default {
  80. components: {
  81. drawFloor,
  82. checkGraphy,
  83. setScaleDialog,
  84. backTips
  85. },
  86. data() {
  87. return {
  88. modelId: '', //
  89. isEdit: false, // 是否编辑模式
  90. FloorID: '',
  91. file: {},
  92. alreadyRelatedModel: [],
  93. sign: false,
  94. otherSign: false,
  95. hasGraph: false, // 当前楼层是否有平面图
  96. hasModel: false, // 项目中时候有模型文件
  97. step: -1, // 当前处于第几步 同时为比例尺item判断是否创建的可编辑的依据;能否返回上页的依据
  98. key: '', // 当楼层图为图片时图的key
  99. stepDes: [
  100. '请在图上点击两点绘制线段并标记实际长度',
  101. '请移动图片与其他楼层对齐'
  102. ], // 描述文字
  103. floorList: [], // 当前建筑下所有楼层
  104. floorRadio: '', // 当前选中的楼层对象
  105. imgService: `/image-service/common/image_get?systemId=dataPlatform&key=`,
  106. curImgItem: null, // 对齐的楼层图片
  107. scaleItem: null, // 比例尺item
  108. baseImgItem: null, // 当前楼层图的图片
  109. nextStepBtnDisable: true, // 比例尺item创建标识
  110. timeOut: true, // 保存按钮延时3秒可操作
  111. }
  112. },
  113. created() {
  114. this.modelId = this.$route.query.modelId
  115. this.FloorID = this.$route.query.FloorID
  116. this.BuildID = this.$route.query.BuildID
  117. this.BuildName = this.$route.query.BuildName
  118. if (this.modelId != '') {
  119. this.hasGraph = true;
  120. const temp = this.modelId.split('.');
  121. if (temp[1] && (temp[1].toLowerCase() == 'png' || temp[1].toLowerCase() == 'jpg')) {
  122. // return
  123. } else {
  124. this.init()
  125. }
  126. }
  127. this.getCountModel()
  128. },
  129. mounted() {
  130. },
  131. methods: {
  132. init() {
  133. this.getFloorData()
  134. },
  135. // 返回路由
  136. backRouter() {
  137. // 底图为图片 上传完图片后未保存
  138. if (this.step > -1) {
  139. this.$refs.backTips.showDialog();
  140. } else {
  141. this.runBack()
  142. }
  143. },
  144. runBack() {
  145. this.$router.push({name: 'buildFloor'})
  146. },
  147. // 替换模型文件
  148. changeGraphy() {
  149. this.$refs.checkGraphy.showDialog({FloorID: this.FloorID, BuildID: this.BuildID})
  150. },
  151. // 确认保存
  152. confirm() {
  153. let sceneMark = this.$refs.drawFloor.drawMainScene.sceneMark,
  154. Outline = null
  155. if (sceneMark) {
  156. if (sceneMark.outLine.length < 3) {
  157. this.$message.warning('请添加轮廓线')
  158. return
  159. }
  160. if (!sceneMark.closeFlag) {
  161. this.$message.warning('请按回车闭合轮廓线')
  162. this.$refs.drawFloor.focus()
  163. return
  164. }
  165. Outline = this.$refs.drawFloor.drawMainScene.sceneMark.outLine
  166. Outline = Outline.map((t) => {
  167. return {
  168. x: t.x.toFixed(2),
  169. y: -t.y.toFixed(2),
  170. }
  171. })
  172. }
  173. let pa = {
  174. Content: [{FloorID: this.FloorID, Outline: Outline}],
  175. Projection: ['Outline'],
  176. }
  177. floorUpdateOutline(pa, (res) => {
  178. this.isEdit = false
  179. this.$message.success('更新成功')
  180. })
  181. },
  182. // 取消
  183. cancel() {
  184. this.isEdit = false
  185. this.$refs.drawFloor.init()
  186. },
  187. // 编辑平面图
  188. editGraphy() {
  189. this.isEdit = true
  190. },
  191. // 替换模型文件成功
  192. refresh(modelId) {
  193. this.modelId = modelId
  194. this.hasGraph = true;
  195. const temp = this.modelId.split('.');
  196. if (temp[1] && temp[1].toLowerCase() != 'png' && temp[1].toLowerCase() != 'jpg') {
  197. this.key = '';
  198. this.getFileName(this.modelId)
  199. }
  200. },
  201. // 获取文件夹名称
  202. getFileName(modelId) {
  203. if (!modelId) return
  204. let pa = {
  205. Id: modelId,
  206. }
  207. getFileNameById(pa, (res) => {
  208. this.file = res
  209. })
  210. },
  211. // 获取楼层
  212. getFloorData(flag) {
  213. let floorParam = {
  214. PageSize: 1000,
  215. Filters: `BuildID='${this.BuildID}'`,
  216. Orders: 'FloorSequenceID desc'
  217. }
  218. floorQueryAndSign(floorParam, (res) => {
  219. this.floorList = res.Content.filter(t => t.StructureInfo && t.StructureInfo.FloorMap || t.FloorID == this.FloorID);
  220. // todo
  221. if (flag && flag == 'next') {
  222. this.floorRadio = this.floorList[0];
  223. if (this.floorRadio.FloorID != this.FloorID) {
  224. this.loadOtherImg(this.floorRadio)
  225. }
  226. }
  227. this.alreadyRelatedModel = res.Content.map((t) => {
  228. if (t.FloorID != this.FloorID) return t.ModelId
  229. this.sign = t.Sign > 0
  230. this.FloorName = t.FloorLocalName || t.FloorName
  231. this.$refs.drawFloor.buildFloorName = `${this.BuildName}-${this.FloorName}`
  232. t.modelId && this.getFileName(t.modelId)
  233. }).filter((t) => t)
  234. })
  235. },
  236. changeSign(flag) {
  237. this.otherSign = flag
  238. },
  239. // 统计项目下是否有模型文件
  240. getCountModel() {
  241. countModel({Filters:"Status in [4]"}, res => {
  242. this.hasModel = res.Count > 0
  243. })
  244. },
  245. //上传图片
  246. uploadAndSubmit(item) {
  247. let file = item.file
  248. let reader = new FileReader()
  249. let vm = this
  250. let fileType = file.name.split('.')
  251. let type = fileType[fileType.length - 1]
  252. let uploadKey = file.uid
  253. reader.onloadend = function () {
  254. // 这个事件在读取结束后,无论成功或者失败都会触发
  255. if (reader.error) {
  256. } else {
  257. // document.getElementById("bytesRead").textContent = file.size;
  258. // 构造 XMLHttpRequest 对象,发送文件 Binary 数据
  259. var xhr = new XMLHttpRequest()
  260. xhr.open(
  261. /* method */ 'POST',
  262. /* target url */
  263. '/image-service/common/image_upload?systemId=dataPlatform&secret=9e0891a7a8c8e885&overwrite=true&key=' + uploadKey + '.' + type
  264. /*, async, default to true */
  265. )
  266. //xhr.overrideMimeType("application/octet-stream");
  267. xhr.send(reader.result)
  268. xhr.onreadystatechange = function () {
  269. if (xhr.readyState == 4) {
  270. if (xhr.status == 200) {
  271. vm.key = uploadKey + '.' + type
  272. // 将图片展示
  273. vm.refresh(uploadKey + '.' + type)
  274. // 设置步骤为第一步
  275. vm.step = 0;
  276. // watch中不会走到initGraph,所以手动触发
  277. vm.$nextTick(() => {
  278. vm.initFromModelId(uploadKey + '.' + type)
  279. })
  280. // 设定保存按钮不可操作
  281. this.timeOut = true;
  282. }
  283. }
  284. }
  285. }
  286. }
  287. reader.readAsArrayBuffer(file)
  288. },
  289. // 上传前判断
  290. beforeUpload(file) {
  291. // 拿到当前上传图片的name,判断其后缀名是否符合
  292. let type = this.isImage(file.name);
  293. if (!type) {
  294. this.$message.error('仅支持png、jpg格式的图片');
  295. return false
  296. }
  297. return true
  298. },
  299. // 封装一个判断图片文件后缀名的方法
  300. isImage(fileName) {
  301. if (typeof fileName !== 'string') return;
  302. let name = fileName.toLowerCase();
  303. return name.endsWith('.png') || name.endsWith('.jpg');
  304. },
  305. // 更新楼层key 为图片,同时更新比例尺
  306. updateFloorKey() {
  307. const scaleItem = this.$refs.drawFloor.scaleItem;
  308. // 比例尺坐标处理,当底图被对齐操作后,需要跟随底图改变坐标
  309. const line = scaleItem.line.map(t => {
  310. return {x: t.x + this.baseImgItem.x, y: t.y + this.baseImgItem.y}
  311. })
  312. let Param = {
  313. Content: [{
  314. FloorId: this.FloorID,
  315. StructureInfo: {FloorMap: this.key},
  316. Properties: {
  317. X: this.baseImgItem.x,
  318. Y: this.baseImgItem.y,
  319. Line: line,
  320. Text: scaleItem.text
  321. }
  322. }],
  323. Projection: ['StructureInfo', 'Properties'],
  324. }
  325. manageUpdateFloor(Param, (res) => {
  326. this.$message.success('更新成功')
  327. this.step = -1;
  328. this.baseImgItem.globalAlpha = 1
  329. this.baseImgItem.moveable = false;
  330. scaleItem.show()
  331. scaleItem.moveable = false;
  332. this.removeLast()
  333. this.$refs.drawFloor.fit()
  334. })
  335. },
  336. // 根据modelid初始化
  337. initFromModelId(id) {
  338. const temp = id.split('.')[1];
  339. if (temp) {
  340. if (temp.toLowerCase() == 'png' || temp.toLowerCase() == 'jpg') {
  341. this.$refs.drawFloor.initGraphy(id, 3)
  342. } else {
  343. this.$refs.drawFloor.initGraphy(id, 2)
  344. }
  345. } else {
  346. this.$refs.drawFloor.initGraphy(id, 1)
  347. }
  348. },
  349. // 上一步
  350. lastStep() {
  351. this.step = 0;
  352. this.$refs.drawFloor.scaleItem.show()
  353. this.baseImgItem.globalAlpha = 1
  354. this.baseImgItem.moveable = false;
  355. this.removeLast()
  356. },
  357. // 下一步||保存
  358. nextStep() {
  359. this.baseImgItem = this.$refs.drawFloor.drawMainScene.imgList[0]
  360. this.baseImgItem.globalAlpha = 0.5
  361. this.baseImgItem.moveable = true;
  362. // 下一步
  363. if (this.step == 0) {
  364. this.step = 1;
  365. this.$refs.drawFloor.scaleItem.hide();
  366. // 获取楼层列表
  367. this.getFloorData('next')
  368. // 延时3秒可操作性保存按钮
  369. setTimeout(() => {
  370. this.timeOut = false
  371. }, 3000)
  372. } else if (this.step == 1) {
  373. // 保存
  374. this.updateFloorKey();
  375. }
  376. },
  377. // 加载图成功 仅在底图为图片时返回成功
  378. getGraphSuc() {
  379. const scaleItem = this.$refs.drawFloor.scaleItem
  380. if (this.step == 0) {
  381. scaleItem.status = SItemStatus.Create;
  382. this.$refs.drawFloor.drawMainScene.grabItem = scaleItem;
  383. scaleItem.connect('changText', this, this.changeText)
  384. scaleItem.moveable = true;
  385. this.clearScaleData(scaleItem);
  386. } else if (this.step == -1) {
  387. scaleItem.status = SItemStatus.Normal;
  388. }
  389. },
  390. // 修改比例尺文字
  391. changeText(t, e) {
  392. this.$refs.setScaleDialog.showDialog(t)
  393. },
  394. // 文本修改
  395. scaleTextChange(v) {
  396. if (v > 0) {
  397. this.nextStepBtnDisable = false
  398. }
  399. },
  400. // 第二部楼层修改
  401. changeFloor(v) {
  402. // v->当前改为的楼层对象
  403. this.removeLast();
  404. this.loadOtherImg(v)
  405. },
  406. // 加载其他楼层图片
  407. loadOtherImg(v) {
  408. if (v.StructureInfo && v.StructureInfo.FloorMap) {
  409. const temp = this.modelId.split('.');
  410. if (temp[1] && (temp[1].toLowerCase() == 'png' || temp[1].toLowerCase() == 'jpg')) {
  411. const url = this.imgService + v.StructureInfo.FloorMap
  412. this.curImgItem = new SImageItem(null, url);
  413. this.curImgItem.enabled = false;
  414. this.curImgItem.showType = SImageShowType.AutoFit;
  415. if (v.Properties) {
  416. try {
  417. // 计算两个比例尺差距
  418. const scaleItem = this.$refs.drawFloor.scaleItem
  419. this.curImgItem.zOrder = scaleItem.zOrder - 1;
  420. const r = this.calScaleGap(v.Properties, scaleItem);
  421. if (r != 1) {
  422. this.$message.warning('比例尺相差较大,可以返回上一步修改比例尺')
  423. }
  424. this.curImgItem.showType == SImageShowType.Full;
  425. this.curImgItem.moveTo(v.Properties.X, v.Properties.Y);
  426. // this.curImgItem.width = v.Properties.width;
  427. // this.curImgItem.height = v.Properties.height;
  428. } catch (err) {
  429. console.log(err);
  430. }
  431. }
  432. this.$refs.drawFloor.drawMainScene.addItem(this.curImgItem);
  433. this.$refs.drawFloor.fit()
  434. }
  435. }
  436. },
  437. // 清除上一张底图
  438. removeLast() {
  439. if (this.curImgItem) {
  440. this.$refs.drawFloor.drawMainScene.removeItem(this.curImgItem);
  441. this.curImgItem = null;
  442. this.$refs.drawFloor.view.update()
  443. }
  444. },
  445. // 清空比例尺数据
  446. clearScaleData(scaleItem) {
  447. scaleItem.line = [];
  448. scaleItem.pointChange();
  449. scaleItem.text = '0 mm';
  450. },
  451. // 计算两个比例尺差距
  452. calScaleGap(pro, item) {
  453. // pro -> 楼层的properties
  454. // item -> 当前的比例尺item
  455. let result = 1
  456. if (pro.Line.length && pro.Text) {
  457. try {
  458. const proDis = SMathUtil.pointDistance(pro.Line[0].x, pro.Line[0].y, pro.Line[1].x, pro.Line[1].y)
  459. const proText = pro.Text.substring(0, pro.Text.length - 3);
  460. const proScale = proDis / proText;
  461. const itemDis = SMathUtil.pointDistance(item.line[0].x, item.line[0].y, item.line[1].x, item.line[1].y)
  462. const itemText = item.text.substring(0, item.text.length - 3);
  463. const itemScale = itemDis / itemText;
  464. result = (proScale / itemScale).toFixed()
  465. } catch (err) {
  466. console.log(err);
  467. }
  468. }
  469. return result;
  470. }
  471. },
  472. watch: {
  473. modelId(n, o) {
  474. if (n != o) {
  475. if (n) {
  476. // 替换的时候需要走
  477. this.$nextTick(() => {
  478. this.initFromModelId(n)
  479. })
  480. }
  481. }
  482. },
  483. },
  484. }
  485. </script>
  486. <style lang="less">
  487. #repetitionGraphy {
  488. width: 100%;
  489. height: 100%;
  490. background: #fff;
  491. position: relative;
  492. padding: 10px;
  493. box-sizing: border-box;
  494. .drawImg {
  495. width: 100%;
  496. margin-top: 10px;
  497. height: calc(100% - 40px);
  498. }
  499. .upload-demo {
  500. display: inline-block;
  501. }
  502. .stepContainer {
  503. width: 100%;
  504. position: absolute;
  505. top: 0;
  506. left: 50%;
  507. margin-left: -50%;
  508. z-index: 9;
  509. background-color: #fff;
  510. }
  511. .floorList {
  512. position: absolute;
  513. width: 100px;
  514. max-height: 250px;
  515. overflow-x: hidden;
  516. overflow-y: auto;
  517. left: 40px;
  518. top: 200px;
  519. z-index: 10;
  520. background: #fff;
  521. border: 1px solid #ccc;
  522. li {
  523. box-sizing: border-box;
  524. padding: 0 10px;
  525. width: 100%;
  526. }
  527. }
  528. }
  529. </style>