top_toolbar.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. <template>
  2. <div id="top_toolbar">
  3. <!-- 顶部编辑器 -->
  4. <div class="tit">
  5. <!-- <a-icon type="left" /> -->
  6. {{this.urlMsg.floorName}}
  7. </div>
  8. <div class="tool">
  9. <ul>
  10. <li>
  11. <a-upload
  12. name="json"
  13. class="avatar-uploader"
  14. :show-upload-list="false"
  15. :customRequest="customRequestJSON"
  16. :before-upload="beforeUploadJSON"
  17. >
  18. <div>
  19. <a-icon type="upload" />
  20. <div class="ant-upload-text">读取json文件</div>
  21. </div>
  22. </a-upload>
  23. </li>
  24. <li @click="saveMsg">
  25. <img class="lock" :src="require(`./../../assets/images/t-save.png`)" alt />
  26. <span>保存json</span>
  27. </li>
  28. <!-- <li>
  29. <a-icon type="edit" />
  30. <span>撤销</span>
  31. </li>
  32. <li @click="redo">
  33. <img class="lock" :src="require(`./../../assets/images/redo.png`)" alt />
  34. <span>重做</span>
  35. </li>-->
  36. <li class="zoom-window">
  37. <div>
  38. <img class="lock" @click="handleScale(-1)" :src="require(`./../../assets/images/缩小.png`)" alt />
  39. <span class="percentage">{{scalePercent}}</span>
  40. <img class="lock" @click="handleScale(1)" :src="require(`./../../assets/images/放大 amplification.png`)" alt />
  41. </div>
  42. <span>缩放窗口</span>
  43. </li>
  44. <li>
  45. <a-dropdown :trigger="[`click`]">
  46. <div class="ant-dropdown-link dropdown-flex" @click="e => e.preventDefault()">
  47. <div>
  48. <img class="lock" :src="require(`./../../assets/images/t-format.png`)" alt />
  49. <div>对齐</div>
  50. </div>
  51. <a-icon type="caret-down" class="down-icon" />
  52. </div>
  53. <a-menu slot="overlay">
  54. <a-menu-item
  55. :disabled="item.disable"
  56. v-for="item in alignmentOptions"
  57. :key="item.value"
  58. @click="changeAlignItem(item.value)"
  59. >
  60. <img style="width: 16px;margin-right: 5px;" :src="require(`./../../assets/images/`+item.img+`.png`)" alt />
  61. <span>{{item.label}}</span>
  62. </a-menu-item>
  63. </a-menu>
  64. </a-dropdown>
  65. </li>
  66. <!-- <li>
  67. <a-icon type="edit" />
  68. <span>图层</span>
  69. </li>
  70. <li>
  71. <a-icon type="edit" />
  72. <span>组合</span>
  73. </li>
  74. <li>
  75. <a-icon type="edit" />
  76. <span>打散</span>
  77. </li>-->
  78. <li @click="lockItem">
  79. <Icon :name="!isLock?`lock`:`unlock`" />
  80. <span>{{isLock?"解锁":"锁定"}}</span>
  81. </li>
  82. <li @click="deleteItem">
  83. <a-icon type="delete" />
  84. <span>删除</span>
  85. </li>
  86. <li>
  87. <a-upload
  88. name="avatar"
  89. class="avatar-uploader"
  90. :show-upload-list="false"
  91. :customRequest="customRequest"
  92. action="https://jsonplaceholder.typicode.com/posts/"
  93. :before-upload="beforeUpload"
  94. @change="handleChange"
  95. >
  96. <img v-if="0" :src="imageUrl" alt="avatar" />
  97. <div v-else>
  98. <a-icon :type="loading ? `loading` : `edit`" />
  99. <div class="ant-upload-text">修改底图</div>
  100. </div>
  101. </a-upload>
  102. </li>
  103. </ul>
  104. </div>
  105. <div class="btn-list">
  106. <div class="btn-list-item" @click="publishMap">
  107. <Icon name="release" />
  108. <span>发布</span>
  109. </div>
  110. </div>
  111. </div>
  112. </template>
  113. <script>
  114. import bus from "@/bus";
  115. import bus2 from "@/bus2";
  116. import { SGraphLayoutType } from "@saga-web/graph/lib";
  117. import systym from "./codeMapSystem.js";
  118. import { MessageBox } from "element-ui";
  119. import { uploadImg } from "@/api/editer.js";
  120. import { debounce } from 'lodash'
  121. function getBase64(img, callback) {
  122. const reader = new FileReader();
  123. reader.addEventListener("load", () => callback(reader.result));
  124. reader.readAsDataURL(img);
  125. }
  126. export default {
  127. data() {
  128. return {
  129. loading: false, //添加图片,是否显示loading
  130. imageUrl: "",
  131. isLock: false, //是否锁定
  132. focusItem: null,
  133. alignmentOptions: [
  134. //对齐数据
  135. {
  136. value: SGraphLayoutType.Left,
  137. label: "左对齐",
  138. img: "t-left",
  139. disable: true
  140. },
  141. {
  142. value: SGraphLayoutType.Right,
  143. label: "右对齐",
  144. img: "t-right",
  145. disable: true
  146. },
  147. {
  148. value: SGraphLayoutType.Top,
  149. label: "顶对齐",
  150. img: "t-top",
  151. disable: true
  152. },
  153. {
  154. value: SGraphLayoutType.Bottom,
  155. label: "底对齐",
  156. img: "t-bottom",
  157. disable: true
  158. },
  159. {
  160. value: SGraphLayoutType.Center,
  161. label: "水平居中对齐",
  162. img: "t-center",
  163. disable: true
  164. },
  165. {
  166. value: SGraphLayoutType.Middle,
  167. label: "垂直居中对齐",
  168. img: "t-topandbottm",
  169. disable: true
  170. },
  171. {
  172. value: SGraphLayoutType.Vertical,
  173. label: "垂直分布",
  174. img: "t-vertical",
  175. disable: true
  176. },
  177. {
  178. value: SGraphLayoutType.Horizontal,
  179. label: "水平分布",
  180. img: "t-level",
  181. disable: true
  182. }
  183. ],
  184. scale: 0.5, //底图缩放比例
  185. initScale: 0.5, //初始 底图缩放比例
  186. baseScale: 1.0, //底图基础缩放比例,由底图加载完成后,传入进来
  187. scaleStep: 0.05 // +-缩放步进
  188. };
  189. },
  190. computed: {
  191. // 缩放比例显示文本
  192. scalePercent() {
  193. return (this.scale * 100).toFixed(0) + "%";
  194. }
  195. },
  196. methods: {
  197. handleChange(info) {
  198. if (info.file.status === "uploading") {
  199. this.loading = true;
  200. return;
  201. }
  202. if (info.file.status === "done") {
  203. // Get this url from response in real world.
  204. getBase64(info.file.originFileObj, imageUrl => {
  205. this.imageUrl = imageUrl;
  206. this.loading = false;
  207. console.log(imageUrl);
  208. bus.$emit("changeImgUrl", imageUrl);
  209. });
  210. }
  211. },
  212. beforeUpload(file) {
  213. const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
  214. if (!isJpgOrPng) {
  215. this.$message.error("You can only upload JPG file!");
  216. }
  217. const isLt2M = file.size / 1024 / 1024 < 2;
  218. if (!isLt2M) {
  219. this.$message.error("Image must smaller than 2MB!");
  220. }
  221. return isJpgOrPng && isLt2M;
  222. },
  223. // TODO: customRequest
  224. customRequest(info) {
  225. const formData = new FormData();
  226. formData.append("file", info.file);
  227. const postParams = formData;
  228. uploadImg({ postParams }).then(res => {
  229. this.loading = false;
  230. if (res.Result == "success") {
  231. this.$message.success(`${info.file.name} file uploaded successfully.`);
  232. bus.$emit("changeImgUrl", res.EntityList[0]);
  233. } else {
  234. this.$message.error(`${info.file.name} file upload failed.`);
  235. }
  236. });
  237. },
  238. beforeUploadJSON(file) {
  239. console.log(file);
  240. const { type } = file;
  241. const flag = type == "application/json";
  242. !flag && this.$message.error("仅支持上传json文件");
  243. return flag;
  244. },
  245. /**
  246. * 读取json文件,bus.$emit后在 baseEditer中使用
  247. */
  248. customRequestJSON(info) {
  249. let { file } = info;
  250. const reader = new FileReader(); //实例化一个FileReader对象
  251. reader.readAsText(file); //借助 FileReader 的方法,按照文本格式读入文件,第二个参数是编码方式(可空)
  252. reader.onload = result => {
  253. let json = JSON.parse(reader.result);
  254. bus.$emit("loadJSON", json);
  255. };
  256. },
  257. /**
  258. * @name getInitScale
  259. * @description 获取底图加载完成后的,初始化的缩放比例initScale,设置为baseScale
  260. */
  261. getInitScale() {
  262. bus.$on("initScale", baseScale => {
  263. this.baseScale = Number(baseScale || 1);
  264. });
  265. },
  266. /**
  267. * @name getMouseScale
  268. * @description 滚轮滚动,导致的底图缩放比例 逻辑处理
  269. */
  270. getMouseScale() {
  271. // zoom 为缩放后改变的比例
  272. bus.$on("mouseScale", zoom => {
  273. this.scale = Number((zoom * this.initScale).toFixed(2));
  274. });
  275. },
  276. /**
  277. * @name handleScale
  278. * @param { Number } type -1:点击 - 缩小底图; 1:点击 + 放大底图
  279. * @description 点击 -+ 缩放底图
  280. */
  281. handleScale(type) {
  282. // +-按钮禁用处理
  283. let flag = true;
  284. // 设置缩放范围 0.05 ~ 100
  285. if ((type < 0 && this.scale <= 0.05) || (type > 0 && this.scale >= 100)) {
  286. flag = false;
  287. } else {
  288. flag = true;
  289. }
  290. if (!flag) {
  291. return false;
  292. }
  293. // emit 缩放比例 this.scale / oldScale
  294. const oldScale = this.scale;
  295. this.scale = Number((oldScale + type * this.scaleStep).toFixed(2));
  296. bus.$emit("changeScale", this.scale / oldScale);
  297. },
  298. FocusItemChanged(itemMsg) {
  299. this.focusItem = null;
  300. this.alignmentOptions.forEach(el => {
  301. el.disable = true;
  302. });
  303. if (itemMsg.itemList.length == 1) {
  304. this.isLock = itemMsg.itemList[0].moveable;
  305. this.focusItem = itemMsg.itemList[0];
  306. } else if (itemMsg.itemList.length > 1) {
  307. this.alignmentOptions.forEach(el => {
  308. el.disable = false;
  309. });
  310. }
  311. },
  312. lockItem() {
  313. if (this.focusItem) {
  314. this.isLock = !this.isLock;
  315. this.focusItem.moveable = this.isLock;
  316. } else {
  317. this.$message.error("请选择指定对象!", 1);
  318. }
  319. },
  320. // 保存json 添加防抖
  321. saveMsg: debounce(() => {
  322. bus.$emit("saveMsgItem")
  323. }, 500),
  324. // 删除item
  325. deleteItem() {
  326. console.log(this.focusItem);
  327. if (this.focusItem) {
  328. bus.$emit("deleiteItem");
  329. // this.focusItem = null;
  330. this.$message.success("删除成功", 1);
  331. } else {
  332. this.$message.error("请选择指定对象!", 1);
  333. }
  334. },
  335. // 发布图例
  336. publishMap() {
  337. MessageBox.confirm("确认后即发布到平台?", "提示", {
  338. confirmButtonText: "确认",
  339. cancelButtonText: "取消",
  340. type: "warning"
  341. })
  342. .then(() => {
  343. bus.$emit("publishMap");
  344. })
  345. .catch(() => {});
  346. },
  347. // 对齐item
  348. changeAlignItem(val) {
  349. bus.$emit("changeAlignItem", val);
  350. }
  351. },
  352. async mounted() {
  353. console.log('dsafadsfasdfas')
  354. bus2.$on("FocusItemChanged", itemMsg => {
  355. console.log("itemMsg", itemMsg);
  356. this.FocusItemChanged(itemMsg);
  357. });
  358. // 获取底图加载完成后的初始sacle
  359. await this.getInitScale();
  360. // 监听滚轮 底图缩放比例
  361. this.getMouseScale();
  362. },
  363. created() {
  364. const href = window.location.href;
  365. // 路由
  366. // const route = href.split("?")[0];
  367. // 参数处理
  368. let params = href.split("?")[1];
  369. if (!params) {
  370. // 参数有问题
  371. return false;
  372. }
  373. params = decodeURIComponent(params);
  374. // params = "categoryId=NTXT&ProjectID=5&BuildingID=1&FloorID=1"; // mock 参数
  375. const paramsArr = params.split("&");
  376. console.log("paramsArr", paramsArr);
  377. const obj = {};
  378. paramsArr.map(item => {
  379. const arr = item.split("=");
  380. obj[arr[0]] = arr[1];
  381. });
  382. this.urlMsg = obj;
  383. const FloorName = this.urlMsg.FloorName ? this.urlMsg.FloorName : "";
  384. this.urlMsg.floorName = systym[this.urlMsg.categoryId] + "-" + FloorName;
  385. }
  386. };
  387. </script>
  388. <style lang="less" scoped>
  389. ul,
  390. li {
  391. /*margin: 0;*/
  392. /*padding: 0;*/
  393. list-style: none;
  394. }
  395. #top_toolbar {
  396. width: 100%;
  397. height: 60px;
  398. background: rgba(255, 255, 255, 1);
  399. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  400. display: flex;
  401. align-items: center;
  402. justify-content: space-between;
  403. padding: 0 24px 0 24px;
  404. .tit {
  405. // flex: 1;
  406. font-size: 18px;
  407. font-weight: bold;
  408. color: #1f2429;
  409. }
  410. .tool {
  411. ul {
  412. display: flex;
  413. li {
  414. display: flex;
  415. justify-content: center;
  416. flex-direction: column;
  417. /*width: 62px;*/
  418. padding: 0 7px;
  419. margin-left: 7px;
  420. font-size: 12px;
  421. align-items: center;
  422. text-align: center;
  423. cursor: pointer;
  424. img {
  425. width: 16px;
  426. height: 16px;
  427. }
  428. .percentage {
  429. display: inline-block;
  430. width: 35px;
  431. border-bottom: 1px solid #c3c7cb;
  432. margin: 0 13px;
  433. font-size: 14px;
  434. }
  435. .dropdown-flex {
  436. display: flex;
  437. align-items: center;
  438. .down-icon {
  439. margin-left: 12px;
  440. }
  441. }
  442. }
  443. li:hover {
  444. background: #f5f6f7;
  445. }
  446. .zoom-window {
  447. border-left: 1px solid #8d9399;
  448. border-right: 1px solid #8d9399;
  449. }
  450. }
  451. }
  452. .btn-list {
  453. display: flex;
  454. align-items: center;
  455. justify-content: center;
  456. width: 72px;
  457. .btn-list-item {
  458. display: flex;
  459. justify-content: center;
  460. flex-direction: column;
  461. span {
  462. font-size: 12px;
  463. }
  464. cursor: pointer;
  465. }
  466. }
  467. }
  468. /deep/ .ant-dropdown-menu-item {
  469. display: flex;
  470. align-items: center;
  471. }
  472. </style>