build.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /* global getTop */
  2. module.exports = {
  3. style: `/* #ifndef H5 || MP-ALIPAY || APP-PLUS */
  4. ._address,
  5. ._article,
  6. ._aside,
  7. ._body,
  8. ._caption,
  9. ._center,
  10. ._cite,
  11. ._footer,
  12. ._header,
  13. ._html,
  14. ._nav,
  15. ._pre,
  16. ._section {
  17. display: block;
  18. }
  19. /* #endif */
  20. ._video {
  21. width: 300px;
  22. height: 225px;
  23. display: inline-block;
  24. background-color: black;
  25. }`,
  26. methods: {
  27. /**
  28. * @description 开始编辑文本
  29. * @param {Event} e
  30. */
  31. editStart (e) {
  32. if (this.opts[4]) {
  33. const i = e.currentTarget.dataset.i
  34. if (!this.ctrl['e' + i]) {
  35. // 显示虚线框
  36. this.$set(this.ctrl, 'e' + i, 1)
  37. setTimeout(() => {
  38. this.root._mask.push(() => this.$set(this.ctrl, 'e' + i, 0))
  39. }, 50)
  40. this.root._edit = this
  41. this.i = i
  42. this.cursor = this.childs[i].text.length
  43. } else {
  44. this.root._mask.pop()
  45. this.root._maskTap()
  46. // 将 text 转为 textarea
  47. this.$set(this.ctrl, 'e' + i, 2)
  48. // 延时对焦,避免高度错误
  49. setTimeout(() => {
  50. this.$set(this.ctrl, 'e' + i, 3)
  51. }, 50)
  52. }
  53. }
  54. },
  55. /**
  56. * @description 输入文本
  57. * @param {Event} e
  58. */
  59. editInput (e) {
  60. const i = e.target.dataset.i
  61. // 替换连续空格
  62. const value = e.detail.value.replace(/ {2,}/, $ => {
  63. let res = '\xa0'
  64. for (let i = 1; i < $.length; i++) {
  65. res += '\xa0'
  66. }
  67. return res
  68. })
  69. this.root._editVal(`${this.opts[6]}.${i}.text`, this.childs[i].text, value) // 记录编辑历史
  70. this.cursor = e.detail.cursor
  71. },
  72. /**
  73. * @description 完成编辑文本
  74. * @param {Event} e
  75. */
  76. editEnd (e) {
  77. const i = e.target.dataset.i
  78. this.$set(this.ctrl, 'e' + i, 0)
  79. // 更新到视图
  80. this.root._setData(`${this.opts[6]}.${i}.text`, e.detail.value)
  81. if (e.detail.cursor !== undefined) {
  82. this.cursor = e.detail.cursor
  83. }
  84. },
  85. /**
  86. * @description 插入一个标签
  87. * @param {Object} node 要插入的标签
  88. */
  89. insert (node) {
  90. setTimeout(() => {
  91. const childs = this.childs.slice(0)
  92. if (!childs[this.i]) {
  93. childs.push(node)
  94. } else if (childs[this.i].text) {
  95. // 在文本中插入
  96. const text = childs[this.i].text
  97. const list = []
  98. if (this.cursor) {
  99. list.push({
  100. type: 'text',
  101. text: text.substring(0, this.cursor)
  102. })
  103. }
  104. list.push(node)
  105. if (this.cursor < text.length) {
  106. list.push({
  107. type: 'text',
  108. text: text.substring(this.cursor)
  109. })
  110. }
  111. childs.splice(this.i, 1, ...list)
  112. } else {
  113. childs.splice(parseInt(this.i) + 1, 0, node)
  114. }
  115. this.root._editVal(this.opts[6], this.childs, childs, true)
  116. }, 200)
  117. },
  118. /**
  119. * @description 移除第 i 个标签
  120. * @param {Number} i
  121. */
  122. remove (i) {
  123. const arr = this.childs.slice(0)
  124. const delEle = arr.splice(i, 1)[0]
  125. if (delEle.name === 'img' || delEle.name === 'video' || delEle.name === 'audio') {
  126. let src = delEle.attrs.src
  127. if (delEle.src) {
  128. src = delEle.src.length === 1 ? delEle.src[0] : delEle.src
  129. }
  130. this.root.$emit('remove', {
  131. type: delEle.name,
  132. src
  133. })
  134. }
  135. this.root._edit = undefined
  136. this.root._maskTap()
  137. this.root._editVal(this.opts[6], this.childs, arr, true)
  138. },
  139. /**
  140. * @description 标签被点击
  141. * @param {Event} e
  142. */
  143. nodeTap (e) {
  144. if (this.opts[4]) {
  145. if (this.root._lock) return
  146. this.root._lock = true
  147. setTimeout(() => {
  148. this.root._lock = false
  149. }, 50)
  150. if (this.ctrl['e' + this.i] === 3) return
  151. this.root._maskTap()
  152. this.root._edit = this
  153. let start = this.opts[6].lastIndexOf('children.')
  154. if (start !== -1) {
  155. start += 9
  156. } else {
  157. start = 6
  158. }
  159. const i = parseInt(this.opts[6].substring(start, this.opts[6].lastIndexOf('.children')))
  160. let parent = this.$parent
  161. while (parent && parent.$options.name !== 'node') {
  162. parent = parent.$parent
  163. }
  164. if (!parent || this.opts[6].length - parent.opts[6].length > 15) return
  165. // 显示实线框
  166. this.$set(this.ctrl, 'root', 1)
  167. this.root._mask.push(() => this.$set(this.ctrl, 'root', 0))
  168. if (this.childs.length === 1 && this.childs[0].type === 'text' && !this.ctrl.e0) {
  169. this.$set(this.ctrl, 'e0', 1)
  170. this.root._mask.push(() => this.$set(this.ctrl, 'e0', 0))
  171. this.i = 0
  172. this.cursor = this.childs[0].text.length
  173. }
  174. const items = this.root._getItem(parent.childs[i], i !== 0, i !== parent.childs.length - 1)
  175. this.root._tooltip({
  176. top: getTop(e),
  177. items,
  178. success: tapIndex => {
  179. if (items[tapIndex] === '大小') {
  180. // 改变字体大小
  181. const style = parent.childs[i].attrs.style || ''
  182. let value = style.match(/;font-size:([0-9]+)px/)
  183. if (value) {
  184. value = parseInt(value[1])
  185. } else {
  186. value = 16
  187. }
  188. this.root._slider({
  189. min: 10,
  190. max: 30,
  191. value,
  192. top: getTop(e),
  193. changing: val => {
  194. if (Math.abs(val - value) > 2) {
  195. // 字号变换超过 2 时更新到视图
  196. parent.changeStyle('font-size', i, val + 'px', value + 'px')
  197. value = e.detail.value
  198. }
  199. },
  200. change: val => {
  201. if (val !== value) {
  202. parent.changeStyle('font-size', i, val + 'px', value + 'px')
  203. }
  204. this.root._editVal(`${parent.opts[6]}.${i}.attrs.style`, style, parent.childs[i].attrs.style)
  205. }
  206. })
  207. } else if (items[tapIndex] === '上移' || items[tapIndex] === '下移') {
  208. const arr = parent.childs.slice(0)
  209. const item = arr[i]
  210. if (items[tapIndex] === '上移') {
  211. arr[i] = arr[i - 1]
  212. arr[i - 1] = item
  213. } else {
  214. arr[i] = arr[i + 1]
  215. arr[i + 1] = item
  216. }
  217. this.root._editVal(parent.opts[6], parent.childs, arr, true)
  218. } else if (items[tapIndex] === '删除') {
  219. parent.remove(i)
  220. } else {
  221. const style = parent.childs[i].attrs.style || ''
  222. let newStyle = ''
  223. const item = items[tapIndex]
  224. let name
  225. let value
  226. if (item === '斜体') {
  227. name = 'font-style'
  228. value = 'italic'
  229. } else if (item === '粗体') {
  230. name = 'font-weight'
  231. value = 'bold'
  232. } else if (item === '下划线') {
  233. name = 'text-decoration'
  234. value = 'underline'
  235. } else if (item === '居中') {
  236. name = 'text-align'
  237. value = 'center'
  238. } else if (item === '缩进') {
  239. name = 'text-indent'
  240. value = '2em'
  241. }
  242. if (style.includes(name + ':')) {
  243. // 已有则取消
  244. newStyle = style.replace(new RegExp(name + ':[^;]+'), '')
  245. } else {
  246. // 没有则添加
  247. newStyle = style + ';' + name + ':' + value
  248. }
  249. this.root._editVal(`${parent.opts[6]}.${i}.attrs.style`, style, newStyle, true)
  250. }
  251. }
  252. })
  253. }
  254. },
  255. /**
  256. * @description 音视频被点击
  257. * @param {Event} e
  258. */
  259. mediaTap (e) {
  260. if (this.opts[4]) {
  261. const i = e.target.dataset.i
  262. const node = this.childs[i]
  263. const items = this.root._getItem(node)
  264. this.root._edit = this
  265. this.i = i
  266. this.root._tooltip({
  267. top: e.target.offsetTop - 30,
  268. items,
  269. success: tapIndex => {
  270. switch (items[tapIndex]) {
  271. case '封面':
  272. // 设置封面
  273. this.root.getSrc('img', node.attrs.poster || '').then(url => {
  274. this.root._editVal(`${this.opts[6]}.${i}.attrs.poster`, node.attrs.poster, url instanceof Array ? url[0] : url, true)
  275. }).catch(() => { })
  276. break
  277. case '删除':
  278. this.remove(i)
  279. break
  280. case '循环':
  281. case '不循环':
  282. // 切换循环播放
  283. this.root._setData(`${this.opts[6]}.${i}.attrs.loop`, !node.attrs.loop)
  284. uni.showToast({
  285. title: '成功'
  286. })
  287. break
  288. case '自动播放':
  289. case '不自动播放':
  290. // 切换自动播放播放
  291. this.root._setData(`${this.opts[6]}.${i}.attrs.autoplay`, !node.attrs.autoplay)
  292. uni.showToast({
  293. title: '成功'
  294. })
  295. break
  296. }
  297. }
  298. })
  299. // 避免上层出现点击态
  300. this.root._lock = true
  301. setTimeout(() => {
  302. this.root._lock = false
  303. }, 50)
  304. }
  305. },
  306. /**
  307. * 改变样式
  308. * @param {String} name 属性名
  309. * @param {Number} i 第几个标签
  310. * @param {String} value 新值
  311. * @param {String} oldVal 旧值
  312. */
  313. changeStyle (name, i, value, oldVal) {
  314. let style = this.childs[i].attrs.style || ''
  315. if (style.includes(';' + name + ':' + oldVal)) {
  316. // style 中已经有
  317. style = style.replace(';' + name + ':' + oldVal, ';' + name + ':' + value)
  318. } else {
  319. // 没有则新增
  320. style += ';' + name + ':' + value
  321. }
  322. this.root._setData(`${this.opts[6]}.${i}.attrs.style`, style)
  323. }
  324. },
  325. handler (file) {
  326. if (file.isBuffer()) {
  327. let content = file.contents.toString()
  328. if (file.path.includes('mp-html.vue')) {
  329. // 传递 editable 属性和路径
  330. content = content.replace(/opts\s*=\s*"\[([^\]]+)\]"/, 'opts="[$1,editable,placeholder,\'nodes\']"')
  331. .replace(/<view(.*?):style\s*=\s*"containerStyle"/, '<view$1:style="(editable?\'position:relative;min-height:200px;\':\'\')+containerStyle" @tap="_containTap"')
  332. // 工具弹窗
  333. .replace(/<\/view>\s*<\/template>/, ` <view v-if="tooltip" class="_tooltip_contain" :style="'top:'+tooltip.top+'px'">
  334. <view class="_tooltip">
  335. <view v-for="(item, index) in tooltip.items" v-bind:key="index" class="_tooltip_item" :data-i="index" @tap="_tooltipTap">{{item}}</view>
  336. </view>
  337. </view>
  338. <view v-if="slider" class="_slider" :style="'top:'+slider.top+'px'">
  339. <slider :value="slider.value" :min="slider.min" :max="slider.max" handle-size="14" block-size="14" show-value activeColor="white" style="padding:3px" @changing="_sliderChanging" @change="_sliderChange" />
  340. </view>
  341. </view>
  342. </template>`)
  343. // 添加 data
  344. .replace(/data\s*\(\)\s*{\s*return\s*{/, `data() {
  345. return {
  346. tooltip: null,
  347. slider: null,`)
  348. // 添加 editable 属性
  349. .replace(/props\s*:\s*{/, `props: {
  350. editable: Boolean,
  351. placeholder: String,`)
  352. // 添加 watch
  353. .replace(/watch\s*:\s*{/, `watch: {
  354. editable(val) {
  355. this.setContent(val ? this.content : this.getContent())
  356. if (!val)
  357. this._maskTap()
  358. },`)
  359. .replace(/if\s*\(this.content/, 'if ((this.content || this.editable)')
  360. // 处理各类弹窗的事件
  361. .replace(/methods\s*:\s*{/, `methods: {
  362. _containTap() {
  363. if (!this._lock && !this.slider) {
  364. this._edit = undefined
  365. this._maskTap()
  366. }
  367. },
  368. _tooltipTap(e) {
  369. this._tooltipcb(e.currentTarget.dataset.i)
  370. this.$set(this, 'tooltip', null)
  371. },
  372. _sliderChanging(e) {
  373. this._slideringcb(e.detail.value)
  374. },
  375. _sliderChange(e) {
  376. this._slidercb(e.detail.value)
  377. },`)
  378. // 工具弹窗的样式
  379. .replace('</style>', `
  380. /* 提示条 */
  381. ._tooltip_contain {
  382. position: absolute;
  383. right: 20px;
  384. left: 20px;
  385. text-align: center;
  386. }
  387. ._tooltip {
  388. box-sizing: border-box;
  389. display: inline-block;
  390. width: auto;
  391. max-width: 100%;
  392. height: 30px;
  393. padding: 0 3px;
  394. overflow: scroll;
  395. font-size: 14px;
  396. line-height: 30px;
  397. white-space: nowrap;
  398. }
  399. ._tooltip_item {
  400. display: inline-block;
  401. width: auto;
  402. padding: 0 2vw;
  403. line-height: 30px;
  404. background-color: black;
  405. color: white;
  406. }
  407. /* 图片宽度滚动条 */
  408. ._slider {
  409. position: absolute;
  410. left: 20px;
  411. width: 220px;
  412. }
  413. ._tooltip,
  414. ._slider {
  415. background-color: black;
  416. border-radius: 3px;
  417. opacity: 0.75;
  418. }
  419. </style>`)
  420. } else if (file.path.includes('parser.js')) {
  421. // 不做 expose 处理
  422. content = content.replace(/parser.prototype.expose\s*=\s*function\s*\(\)\s*{/, `parser.prototype.expose = function () {
  423. if (this.options.editable) return`)
  424. .replace(/popNode\s*=\s*function\s*\(\)\s*{/, 'popNode = function () {\n const editable = this.options.editable')
  425. // 不转换标签名
  426. .replace(/if\s*\(config.blockTags\[node.name\]\)\s*{[\s\S]+?}/, `if (config.blockTags[node.name]) {
  427. if (!editable) {
  428. node.name = 'div'
  429. }
  430. }`)
  431. // 转换表格和列表
  432. .replace(/else\s*if\s*\(node.c\)/, 'else if (!editable && node.c )')
  433. .replace(/node.c(\)|\s*&&|\s*\n)/g, '(node.c || editable)$1')
  434. .replace(/while\s*\(map\[row\s*\+\s*'.'\s*\+\s*col\]\)\s*{[\s\S]+?}/, `while (map[row + '.' + col]) {
  435. col++
  436. }
  437. if (editable) {
  438. td.r = row
  439. }`)
  440. .replace(/let\s+str\s*=\s*'<video style="width:100%;height:100%"'/, `let str = '<video style="width:100%;height:100%"'
  441. if (editable) {
  442. attrs.controls = ''
  443. }`)
  444. } else if (file.path.includes('node.vue')) {
  445. content =
  446. // 传递 opts
  447. content.replace(/:childs\s*=\s*"tbody.children"\s*:opts="opts"/, ':childs="tbody.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6]+\'.\'+i+\'.children.\'+x+\'.children\']"')
  448. .replace(/:childs\s*=\s*"n2.children"\s*:opts="opts"/, ':childs="n2.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6]+\'.\'+i+\'.children.\'+j+\'.children\']"')
  449. .replace(/:childs\s*=\s*"tr.children"\s*:opts="opts"/, ':childs="tr.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6]+\'.\'+i+\'.children.\'+x+\'.children.\'+y+\'.children\']"')
  450. .replace(/:childs\s*=\s*"td.children"\s*:opts="opts"/, ':childs="td.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6]+\'.\'+i+\'.children.\'+x+\'.children.\'+y+\'.children.\'+z+\'.children\']"')
  451. .replace(/opts\s*=\s*"opts"/g, 'opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6]+\'.\'+i+\'.children\']"')
  452. // 不使用 rich-text
  453. .replace(/handler\.use\(n\)/g, '!opts[4]&&handler.use(n)').replace(/!n.c/g, '!opts[4]&&!n.c').replace('&&n.c', '&&(n.c||opts[4])')
  454. // 修改普通标签
  455. .replace(/<view\s+:id(.+?)style="/, '<view @tap="nodeTap" :id$1style="(ctrl.root?\'border:1px solid black;padding:5px;display:block;\':\'\')+')
  456. // 修改文本块
  457. .replace(/<!--\s*文本\s*-->[\s\S]+?<!--\s*链接\s*-->/,
  458. `<!-- 文本 -->
  459. <text v-else-if="n.type==='text'&&!ctrl['e'+i]" :data-i="i" :user-select="n.us" :decode="!opts[4]" @tap="editStart">{{n.text}}
  460. <text v-if="!n.text" style="color:gray">{{opts[5]||'请输入'}}</text>
  461. </text>
  462. <text v-else-if="n.type==='text'&&ctrl['e'+i]===1" :data-i="i" style="border:1px dashed black;min-width:50px;width:auto;padding:5px;display:block" @tap.stop="editStart">{{n.text}}
  463. <text v-if="!n.text" style="color:gray">{{opts[5]||'请输入'}}</text>
  464. </text>
  465. <textarea v-else-if="n.type==='text'" style="border:1px dashed black;min-width:50px;width:auto;padding:5px" auto-height maxlength="-1" :focus="ctrl['e'+i]===3" :value="n.text" :data-i="i" @input="editInput" @blur="editEnd" />
  466. <text v-else-if="n.name==='br'">\\n</text>
  467. <!-- 链接 -->`)
  468. // 修改图片
  469. .replace(/<image(.+?)id="n.attrs.id/, '<image$1id="n.attrs.id||(\'n\'+i)')
  470. .replace('height:1px', "height:'+(ctrl['h'+i]||1)+'px")
  471. .replace(/:style\s*=\s*"\(ctrl\[i\]/g, ':style="(ctrl[\'e\'+i]?\'border:1px dashed black;padding:3px;\':\'\')+(ctrl[i]')
  472. .replace(/show-menu-by-longpress\s*=\s*"(\S+?)"\s*:image-menu-prevent\s*=\s*"(\S+?)"/, 'show-menu-by-longpress="!opts[4]&&$1" :image-menu-prevent="opts[4]||$2"')
  473. // 修改音视频
  474. .replace('v-else-if="n.html"', 'v-else-if="n.html" :data-i="i" @tap="mediaTap"')
  475. .replace('<video', '<video :show-center-play-btn="!opts[4]" @tap="mediaTap"')
  476. .replace('audio ', 'audio @tap="mediaTap" ')
  477. .replace('<script>',
  478. `<script>
  479. function getTop(e) {
  480. let top
  481. // #ifdef H5 || APP-PLUS
  482. top = e.touches[0].pageY
  483. // #endif
  484. // #ifdef MP-ALIPAY
  485. top = e.detail.pageY
  486. // #endif
  487. // #ifndef H5 || MP-ALIPAY || APP-PLUS
  488. top = e.detail.y
  489. // #endif
  490. if (top - e.currentTarget.offsetTop < 150)
  491. top = e.currentTarget.offsetTop
  492. if (top < 30)
  493. top += 70
  494. return top - 30
  495. }`)
  496. // 周期处理
  497. .replace(/beforeDestroy\s*\(\)\s*{/, `beforeDestroy () {
  498. if (this.root._edit === this) {
  499. this.root._edit = undefined
  500. }`)
  501. // 记录图片宽度
  502. .replace(/imgLoad\s*\(e\)\s*{/, `imgLoad(e) {
  503. // #ifdef MP-WEIXIN || MP-QQ
  504. if (this.opts[4])
  505. this.$nextTick(() => {
  506. const id = this.childs[i].attrs.id || ('n' + i)
  507. uni.createSelectorQuery().in(this).select('#' + id).boundingClientRect().exec(res => {
  508. this.$set(this.ctrl, 'h'+i, res[0].height)
  509. })
  510. })
  511. // #endif`)
  512. .replace(/if\s*\(!this.childs\[i\].w\)\s*{[\s\S]+?}/,
  513. `if (!this.childs[i].w) {
  514. this.$set(this.ctrl, i, e.detail.width)
  515. if (this.opts[4]) {
  516. const path = this.opts[6] + '.' + i + '.attrs.'
  517. if (e.detail.width < 150)
  518. this.root._setData(path + 'ignore', 'T')
  519. this.root._setData(path + 'width', e.detail.width.toString())
  520. }
  521. }`)
  522. // 处理图片长按
  523. .replace(/imgLongTap\s*\(\)\s*{/, `imgLongTap() {
  524. if (this.opts[4]) return`)
  525. // 处理图片点击
  526. .replace(/imgTap\s*\(e\)\s*{([\s\S]+?)},\s*\/\*/,
  527. `imgTap (e) {
  528. if (!this.opts[4]) {$1} else {
  529. const i = e.currentTarget.dataset.i
  530. const node = this.childs[i]
  531. const items = this.root._getItem(node)
  532. this.root._edit = this
  533. this.i = i
  534. this.root._maskTap()
  535. this.$set(this.ctrl, 'e' + i, 1)
  536. this.root._mask.push(() => this.$set(this.ctrl, 'e' + i, 0))
  537. this.root._tooltip({
  538. top: getTop(e),
  539. items,
  540. success: tapIndex => {
  541. if (items[tapIndex] === '换图') {
  542. // 换图
  543. this.root.getSrc('img', node.attrs.src || '').then(url => {
  544. this.root._editVal(this.opts[6] + '.' + i + '.attrs.src', node.attrs.src, url instanceof Array ? url[0] : url, true)
  545. }).catch(() => { })
  546. } else if (items[tapIndex] === '宽度') {
  547. // 更改宽度
  548. const style = node.attrs.style || ''
  549. let value = style.match(/max-width:([0-9]+)%/)
  550. if (value) {
  551. value = parseInt(value[1])
  552. } else {
  553. value = 100
  554. }
  555. this.root._slider({
  556. min: 0,
  557. max: 100,
  558. value,
  559. top: getTop(e),
  560. changing: val => {
  561. // 变化超过 5% 更新时视图
  562. if (Math.abs(val - value) > 5) {
  563. this.changeStyle('max-width', i, val + '%', value + '%')
  564. value = val
  565. }
  566. },
  567. change: val => {
  568. if (val !== value) {
  569. this.changeStyle('max-width', i, val + '%', value + '%')
  570. value = val
  571. }
  572. this.root._editVal(this.opts[6] + '.' + i + '.attrs.style', style, this.childs[i].attrs.style)
  573. }
  574. })
  575. } else if (items[tapIndex] === '超链接') {
  576. // 将图片设置为链接
  577. this.root.getSrc('link', node.a ? node.a.href : '').then(url => {
  578. // 如果有 a 标签则替换 href
  579. if (node.a) {
  580. this.root._editVal(this.opts[6] + '.' + i + '.a.href', node.a.href, url, true)
  581. } else {
  582. const link = {
  583. name: 'a',
  584. attrs: {
  585. href: url
  586. },
  587. children: [node]
  588. }
  589. node.a = link.attrs
  590. this.root._editVal(this.opts[6] + '.' + i, node, link, true)
  591. }
  592. wx.showToast({
  593. title: '成功'
  594. })
  595. }).catch(() => { })
  596. } else if (items[tapIndex] === '预览图') {
  597. // 设置预览图链接
  598. this.root.getSrc('img', node.attrs['original-src'] || '').then(url => {
  599. this.root._editVal(this.opts[6] + '.' + i + '.attrs.original-src', node.attrs['original-src'], url instanceof Array ? url[0] : url, true)
  600. uni.showToast({
  601. title: '成功'
  602. })
  603. }).catch(() => { })
  604. } else if (items[tapIndex] === '删除') {
  605. this.remove(i)
  606. } else {
  607. // 禁用 / 启用预览
  608. this.root._setData(this.opts[6] + '.' + i + '.attrs.ignore', !node.attrs.ignore)
  609. uni.showToast({
  610. title: '成功'
  611. })
  612. }
  613. }
  614. })
  615. this.root._lock = true
  616. setTimeout(() => {
  617. this.root._lock = false
  618. }, 50)
  619. }
  620. },
  621. /*`)
  622. // 处理链接点击
  623. .replace(/linkTap\s*\(e\)\s*{([\s\S]+?)},\s*\/\*/,
  624. `linkTap (e) {
  625. if (!this.opts[4]) {$1} else {
  626. const i = e.currentTarget.dataset.i
  627. const node = this.childs[i]
  628. const items = this.root._getItem(node)
  629. this.root._tooltip({
  630. top: getTop(e),
  631. items,
  632. success: tapIndex => {
  633. if (items[tapIndex] === '更换链接') {
  634. this.root.getSrc('link', node.attrs.href).then(url => {
  635. this.root._editVal(this.opts[6] + '.' + i + '.attrs.href', node.attrs.href, url, true)
  636. uni.showToast({
  637. title: '成功'
  638. })
  639. }).catch(() => { })
  640. } else {
  641. this.remove(i)
  642. }
  643. }
  644. })
  645. }
  646. },
  647. /*`)
  648. }
  649. file.contents = Buffer.from(content)
  650. }
  651. }
  652. }