BaseImagePro.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <!-- 基本图片的属性框 -->
  2. <template>
  3. <div class="base-image">
  4. <div class="title">属性</div>
  5. <ul>
  6. <li>
  7. <div class="small-title">尺寸大小</div>
  8. <div class="property">
  9. <div>
  10. <span>W</span>
  11. <el-input style="width: 74px; margin-left: 6px" size="mini" type="number" v-model="width" placeholder="输入宽度"></el-input>
  12. </div>
  13. <div>
  14. <span>H</span>
  15. <el-input style="width: 74px; margin-left: 6px" size="mini" type="number" v-model="height" placeholder="输入高度"></el-input>
  16. </div>
  17. <div @click="lockItem" :title="isLock ? '锁定' : '解锁'">
  18. <i class="el-icon-link" :style="{ color: isLock ? '#409EFF' : '', cursor: 'pointer' }"></i>
  19. </div>
  20. </div>
  21. </li>
  22. <li class="m-picture">
  23. <p class="title">图片</p>
  24. <el-upload
  25. class="avatar-uploader"
  26. drag
  27. action="https://jsonplaceholder.typicode.com/posts/"
  28. :show-file-list="false"
  29. :before-upload="beforeAvatarUpload"
  30. accept="image/jpeg,image/jpg,image/png,"
  31. >
  32. <i class="el-icon-upload"></i>
  33. <div class="el-upload__text">
  34. 将文件拖动到这里替换
  35. <p><em>本地上传</em></p>
  36. </div>
  37. </el-upload>
  38. </li>
  39. <li>
  40. <div class="small-title">边框</div>
  41. <div class="property">
  42. <div class="color-box">
  43. <div class="cololorSelect">
  44. <el-color-picker show-alpha class="fix-box-1" v-model="color"></el-color-picker>
  45. </div>
  46. <span class="text">颜色</span>
  47. </div>
  48. <div class="line-width">
  49. <el-input style="width: 80px" v-model="lineWidth" type="number" size="mini" placeholder="输入线宽"></el-input>
  50. <span class="text">线宽</span>
  51. </div>
  52. <div class="line-width">
  53. <el-select
  54. v-model="lineStyle"
  55. popper-class="image-select"
  56. :default-value="borderLineOption[0].id"
  57. size="mini"
  58. style="width: 80px"
  59. placeholder="请选择"
  60. :ref="`selectLine`"
  61. >
  62. <el-option v-for="item in borderLineOption" :key="item.id" :label="item.src" :value="item.id">
  63. <img :src="item.src" alt width="60" />
  64. </el-option>
  65. </el-select>
  66. <span class="text">线型</span>
  67. </div>
  68. </div>
  69. </li>
  70. </ul>
  71. </div>
  72. </template>
  73. <script>
  74. import bus from "@/bus/bus";
  75. import { imgBaseUrl, imgServeUpload } from "@/api/imageservice";
  76. import { SColor } from "@persagy-web/draw";
  77. import { rgbaNum } from "@persagy-web/big-edit/lib/until";
  78. import { findIndex } from "lodash";
  79. import { dataURLtoBlob } from "@/utils/utils";
  80. export default {
  81. props: ["ImageItem"],
  82. data() {
  83. return {
  84. borderLineOption: [
  85. {
  86. id: "Solid",
  87. src: require("@/assets/images/solidLine.png"),
  88. },
  89. {
  90. id: "Dashed",
  91. src: require("@/assets/images/dashedLine.png"),
  92. },
  93. {
  94. id: "Dotted",
  95. src: require("@/assets/images/dotLine.png"),
  96. },
  97. ],
  98. isLock: true, //锁定图片宽高比
  99. aspectRatio: 1, // 图片宽高比
  100. };
  101. },
  102. computed: {
  103. /**
  104. * 图片宽度
  105. */
  106. width: {
  107. get: function () {
  108. return this.ImageItem.width || "";
  109. },
  110. set: function (newV) {
  111. if (!newV) newV = 0;
  112. newV = Number(newV);
  113. const oldV = this.ImageItem.width;
  114. if (this.isLock) {
  115. this.ImageItem.height = newV * this.aspectRatio;
  116. }
  117. this.ImageItem.width = newV;
  118. bus.$emit("undoAttr", this.ImageItem, "width", oldV, newV);
  119. },
  120. },
  121. /**
  122. * 图片高度
  123. */
  124. height: {
  125. get: function () {
  126. return this.ImageItem.height || "";
  127. },
  128. set: function (newV) {
  129. if (!newV) newV = 0;
  130. newV = Number(newV);
  131. const oldV = this.ImageItem.height;
  132. if (this.isLock) {
  133. this.ImageItem.width = newV / this.aspectRatio;
  134. }
  135. this.ImageItem.height = newV;
  136. console.log(newV, this.ImageItem.height);
  137. bus.$emit("undoAttr", this.ImageItem, "height", oldV, newV);
  138. },
  139. },
  140. /**
  141. * 边框线宽
  142. */
  143. lineWidth: {
  144. get: function () {
  145. return this.ImageItem.lineWidth || "";
  146. },
  147. set: function (newV) {
  148. if (!newV) newV = 0;
  149. newV = Number(newV);
  150. const oldV = this.ImageItem.lineWidth;
  151. if (newV > 20) {
  152. this.ImageItem.lineWidth = 20;
  153. } else {
  154. this.ImageItem.lineWidth = newV;
  155. }
  156. bus.$emit("undoAttr", this.ImageItem, "lineWidth", oldV, newV);
  157. },
  158. },
  159. /**
  160. * 边框颜色
  161. */
  162. color: {
  163. get: function () {
  164. return this.ImageItem.strokeColor.toRgba();
  165. },
  166. set: function (val) {
  167. const colorList = rgbaNum(val);
  168. const oldVal = this.ImageItem.strokeColor;
  169. const newVal = new SColor(Number(colorList[0]), Number(colorList[1]), Number(colorList[2]), colorList[3] * 255);
  170. this.ImageItem.strokeColor = newVal;
  171. bus.$emit("undoAttr", this.ImageItem, "strokeColor", oldVal, newVal);
  172. },
  173. },
  174. /**
  175. * 边框线型
  176. */
  177. lineStyle: {
  178. get: function () {
  179. const id = this.borderLineOption[this.ImageItem.lineStyle].id;
  180. // 修改线型下啦菜单样式
  181. this.$nextTick(() => {
  182. this.setLineStyle(id);
  183. });
  184. return id;
  185. },
  186. set: function (newV) {
  187. newV = findIndex(this.borderLineOption, ["id", newV]);
  188. const oldV = this.ImageItem.lineStyle;
  189. this.ImageItem.lineStyle = newV;
  190. bus.$emit("undoAttr", this.ImageItem, "lineStyle", oldV, newV);
  191. },
  192. },
  193. },
  194. watch: {},
  195. mounted() {},
  196. methods: {
  197. /**
  198. * 切换锁状态
  199. */
  200. lockItem() {
  201. this.isLock = !this.isLock;
  202. if (this.isLock) {
  203. this.aspectRatio = this.width / this.height;
  204. }
  205. },
  206. /**
  207. * 修改线型下啦菜单样式
  208. * @param {String} id 选中的线型id
  209. */
  210. setLineStyle(id) {
  211. const img = this.borderLineOption.filter((v) => v.id === id)[0]?.src;
  212. if (img) {
  213. this.$refs.selectLine?.$el?.children[0].children[0].setAttribute(
  214. "style",
  215. `
  216. background: url(${img}) no-repeat;
  217. color: rgba(0,0,0,0);
  218. text-indent: -9999px;
  219. background-position: 15px center;
  220. background-size: 40px 1px;
  221. `
  222. );
  223. } else {
  224. this.$refs.selectLine?.$el?.children[0].children[0].setAttribute(
  225. "style",
  226. `
  227. background: inherit;
  228. color:inherit;
  229. text-indent: 0;
  230. `
  231. );
  232. }
  233. },
  234. /**
  235. * 图片上传
  236. */
  237. beforeAvatarUpload(file) {
  238. const fileReader = new FileReader();
  239. fileReader.readAsDataURL(file); //读取图片
  240. const ftype = file.type;
  241. const fname = file.name;
  242. const uploadKey = file.uid;
  243. fileReader.addEventListener("load", () => {
  244. // 读取完成
  245. const res = fileReader.result;
  246. //将canvas的base64位图片转换成图片png的file
  247. const blob = dataURLtoBlob(res, ftype);
  248. //将其转换成file对象
  249. const file = new File([blob], fname, {
  250. type: ftype,
  251. lastModified: Date.now(),
  252. }); //blob转file
  253. // try sending
  254. const reader = new FileReader();
  255. const fileType = file.name.split(".");
  256. const imgType = fileType[fileType.length - 1];
  257. reader.onloadend = () => {
  258. // 这个事件在读取结束后,无论成功或者失败都会触发
  259. if (reader.error) {
  260. //
  261. } else {
  262. // 构造 XMLHttpRequest 对象,发送文件 Binary 数据
  263. const xhr = new XMLHttpRequest();
  264. xhr.open("POST", `${imgServeUpload}${uploadKey}.${imgType}`);
  265. xhr.send(reader.result);
  266. xhr.onreadystatechange = () => {
  267. if (xhr.readyState == 4) {
  268. if (xhr.status == 200) {
  269. const olDefaultUrl = this.ImageItem.defaultUrl;
  270. const oldUrl = this.ImageItem.url;
  271. const newDefaultUrl = `${uploadKey}.${imgType}`;
  272. const newUrl = `${imgBaseUrl}${uploadKey}.${imgType}`;
  273. bus.$emit("undoAttr", this.ImageItem, "defaultUrl", olDefaultUrl, newDefaultUrl);
  274. bus.$emit("undoAttr", this.ImageItem, "url", oldUrl, newUrl);
  275. this.ImageItem.defaultUrl = newDefaultUrl;
  276. this.ImageItem.url = newUrl;
  277. }
  278. }
  279. };
  280. }
  281. };
  282. reader.readAsArrayBuffer(file);
  283. });
  284. },
  285. },
  286. };
  287. </script>
  288. <style lang="less">
  289. .image-select {
  290. .el-select-dropdown__item {
  291. text-align: center;
  292. }
  293. }
  294. </style>
  295. <style lang="less" scoped>
  296. ul,
  297. li {
  298. margin: 0;
  299. padding: 0;
  300. list-style-type: none;
  301. }
  302. .base-image {
  303. & > .title {
  304. height: 40px;
  305. line-height: 40px;
  306. color: #000000;
  307. background: #f8f9fa;
  308. font-size: 14px;
  309. padding-left: 12px;
  310. box-sizing: border-box;
  311. font-weight: 600;
  312. }
  313. ul {
  314. width: 100%;
  315. li {
  316. padding: 0 12px;
  317. margin-bottom: 16px;
  318. .small-title {
  319. font-size: 14px;
  320. color: #8d9399;
  321. margin: 12px 0;
  322. font-weight: 600;
  323. }
  324. .property {
  325. display: flex;
  326. align-items: center;
  327. justify-content: space-around;
  328. .color-box {
  329. display: flex;
  330. align-items: center;
  331. flex-direction: column;
  332. .cololorSelect {
  333. width: 32px;
  334. height: 20px;
  335. overflow: hidden;
  336. position: relative;
  337. margin: 4px 0;
  338. .fix-box-1 {
  339. margin-top: -8px;
  340. margin-left: -8px;
  341. /deep/ .el-color-picker__trigger {
  342. width: 200px;
  343. height: 200px;
  344. }
  345. }
  346. }
  347. }
  348. .line-width {
  349. display: flex;
  350. align-items: center;
  351. flex-direction: column;
  352. margin-left: 8px;
  353. position: relative;
  354. }
  355. .text {
  356. font-size: 12px;
  357. color: #1f2429;
  358. font-weight: 600;
  359. margin-top: 4px;
  360. }
  361. }
  362. }
  363. li.m-picture {
  364. border-top: 1px solid #e4e5e7;
  365. border-bottom: 1px solid #e4e5e7;
  366. padding: 12px 0 18px 0;
  367. .title {
  368. padding: 0 12px;
  369. margin-bottom: 12px;
  370. color: #8d9399;
  371. font-size: 14px;
  372. line-height: 16px;
  373. font-weight: 600;
  374. }
  375. .avatar-uploader {
  376. width: 100%;
  377. text-align: center;
  378. /deep/ .el-upload-dragger {
  379. width: 200px !important;
  380. }
  381. }
  382. }
  383. }
  384. }
  385. </style>