物管理前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

917 lines
49 KiB

  1. const e="f31282bd-dd67-4c07-9951-ac7270c5a896",n="scroll-rank-board",t="滚动排名",a='<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1704849974974" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18948" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M392.96 193.1264h502.784a43.9808 43.9808 0 1 1 0 87.9616h-502.784a43.9808 43.9808 0 0 1 0-87.9616z m0 274.944h502.784a43.9808 43.9808 0 1 1 0 87.9104h-502.784a43.9808 43.9808 0 1 1 0-87.9616z m0 274.8416h502.784a43.9808 43.9808 0 0 1 0 87.9616h-502.784a43.9808 43.9808 0 1 1 0-87.9616z" fill="#1296db" p-id="18949"></path><path d="M146.688 183.04l-28.8256 12.6976v-34.1504l39.424-22.8352h25.7536v163.1744h-36.352V183.04zM112.4864 540.1088q17.6128-15.872 30.8736-29.2864 13.312-13.4656 23.9104-28.672 10.6496-15.36 10.6496-26.624 0-11.6736-8.192-18.1248-8.0384-6.4512-21.0944-6.4512-9.3184 0-20.2752 3.84-10.9568 3.84-21.504 10.6496v-26.624q11.008-6.6048 24.9856-10.752 13.9264-4.2496 26.624-4.2496 25.7024 0 40.8064 12.8 15.0528 12.6464 15.0528 34.6112 0 15.616-10.496 33.4336-10.4448 17.7664-24.0128 31.6416-13.5168 13.8752-32.1024 30.464l-6.6048-4.5056h73.216v27.136H103.936v-21.7088l8.5504-7.5776zM156.16 839.3216q-24.4224 0-47.4624-10.3936v-31.232q10.24 7.3216 21.8624 10.9056 11.6736 3.584 23.296 3.584 13.0048 0 19.8656-5.632 7.0144-5.7344 7.0144-16.6912 0-12.3392-8.6528-18.6368-8.5504-6.3488-22.6304-6.3488h-23.2448v-23.9616h20.7872q13.7216 0 21.2992-6.144 7.6288-6.144 7.6288-17.0496 0-9.0624-6.912-14.1824-6.912-5.12-18.432-5.12-8.2432 0-18.432 2.2016-10.0864 2.2016-18.432 6.144v-25.344q9.216-4.9664 20.736-7.4752 11.4688-2.6624 22.784-2.6624 24.1664 0 38.1952 11.6224 14.08 11.6224 14.08 31.4368 0 12.4416-6.4512 22.272-6.3488 9.8304-18.2272 15.4624 14.1312 5.0176 21.76 15.36 7.68 10.2912 7.68 24.2176 0 13.9776-7.0656 24.7808-7.168 10.752-20.4288 16.7936-13.1584 6.0928-30.5664 6.0928z" fill="#1296db" p-id="18950"></path></svg>',o="svg",i="动态",l="数据展示",u=!1,c=!0,r="时序",s=`{"type":"page","id":"u:270584784ce1","name":"page1","asideResizor":false,"style":{"boxShadow":" 0px 0px 0px 0px transparent"},"pullRefresh":{"disabled":true},"body":[{"type":"tabs","name":"tab","tabs":[{"title":"样式","icon":"fa fa-th-large","body":[{"type":"form","title":"","name":"basicPropForm","body":[{"type":"input-text","label":"名称","name":"nodeAlias","id":"u:6b126f0520cb","size":"full","mode":"horizontal","inputControlClassName":"w-100","className":"m-b"},{"type":"input-text","label":"ID&nbsp;&nbsp;&nbsp;&nbsp;","name":"id","id":"u:6232710ac003","size":"full","mode":"horizontal","inputControlClassName":"w-100","className":"m-b"},{"type":"grid","id":"u:c605398a724c","className":"m-b","columns":[{"body":[{"type":"input-number","label":"宽度","name":"width","keyboard":true,"id":"u:dcc0c21d16f6","step":1,"suffix":"px","placeholder":"组件左边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:14cc19d6ffb0","md":6},{"body":[{"type":"input-number","label":"高度","name":"height","keyboard":true,"id":"u:cd6fdff9ca88","step":1,"suffix":"px","placeholder":"组件上边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:4931801ca9b8","md":6}]},{"type":"grid","id":"u:da449a94908a","className":"m-b","columns":[{"body":[{"type":"input-number","label":"X 轴","name":"x","keyboard":true,"id":"u:29852d093d9d","step":1,"suffix":"px","placeholder":"组件左边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:1b561d652acc","md":6},{"body":[{"type":"input-number","label":"Y 轴","name":"y","keyboard":true,"id":"u:dc8c1daed8ed","step":1,"suffix":"px","placeholder":"组件上边距","size":"full","mode":"horizo
  2. "nodes": [
  3. {
  4. "id": "709113b2-7076-4e9b-8406-e0b2ad30c9e5",
  5. "type": "scroll-rank-board",
  6. "x": 200,
  7. "y": 200,
  8. "text": {
  9. "value": "",
  10. "x": 200,
  11. "y": 200
  12. },
  13. "properties": {
  14. "id": "709113b2-7076-4e9b-8406-e0b2ad30c9e5",
  15. "width": 500,
  16. "height": 200,
  17. "x": 200,
  18. "y": 200,
  19. "rotation": 0,
  20. "opacity": 1,
  21. "showRank": true,
  22. "enableCarousel": false,
  23. "waitTime": 2000,
  24. "showRatio": true,
  25. "divideInsideColumn": true,
  26. "fontSize": 12,
  27. "showDefaultValue": false,
  28. "showUnit": false,
  29. "valueColor": "rgba(8, 80, 164, 1)",
  30. "nodeAlias": "滚动排名",
  31. "rankColumnStyle": "return {\\r\\n borderBottom: '1px solid rgba(74, 144, 226, 1)'\\r\\n}",
  32. "color": "rgba(16, 120, 242, 1)",
  33. "insideColumnStyle": "",
  34. "textColor": "rgba(8, 80, 164, 1)",
  35. "carousel": "single",
  36. "unit": "kWh",
  37. "rankFormat": "No.1",
  38. "unitColor": "rgba(8, 80, 164, 1)",
  39. "ratioColor": "rgba(8, 80, 164, 1)",
  40. "ratioSpace": 70,
  41. "nameColor": "rgba(8, 80, 164, 1)",
  42. "dividerWidth": 2,
  43. "dividerSpace": 4,
  44. "dividerBack": "rgba(255, 255, 255, 1)",
  45. "numberColor": "rgba(8, 80, 164, 1)",
  46. "rowNum": 5,
  47. "fontFamily": "Microsoft Yahei",
  48. "fontColor": "",
  49. "fontStyle": "",
  50. "gradientFontColor": "",
  51. "dynamic": {
  52. "normalData": {
  53. "dataPoint": "",
  54. "compareType": "",
  55. "conditionVariables": [],
  56. "defaultValue": "",
  57. "unit": "",
  58. "customDatasource": true,
  59. "dataFilterFn": "// datas 数据处理\\n// .....\\nconst defaultDatas = [\\n {\\n name: '周口',\\n value: 55123,\\n },\\n {\\n name: '南阳',\\n value: 12022,\\n },\\n {\\n name: '西峡',\\n value: 78932\\n },\\n {\\n name: '驻马店',\\n value: 63411\\n },\\n {\\n name: '新乡',\\n value: 44231\\n }\\n]\\n\\nreturn defaultDatas"
  60. }
  61. }
  62. }
  63. }
  64. ]
  65. }`,javascript:`// 工具函数
  66. /**
  67. * 精准判断对象类型
  68. * @param obj
  69. */
  70. function typeOf(obj) {
  71. const toString = Object.prototype.toString
  72. const map = {
  73. '[object Boolean]': 'boolean',
  74. '[object Number]': 'number',
  75. '[object String]': 'string',
  76. '[object Function]': 'function',
  77. '[object Array]': 'array',
  78. '[object Date]': 'date',
  79. '[object RegExp]': 'regExp',
  80. '[object Undefined]': 'undefined',
  81. '[object Null]': 'null',
  82. '[object Object]': 'object',
  83. }
  84. return map[toString.call(obj)]
  85. }
  86. /**
  87. * 深拷贝
  88. * @param data
  89. */
  90. function deepCopy(data) {
  91. const t = typeOf(data)
  92. let o
  93. if (t === 'array') {
  94. o = []
  95. } else if (t === 'object') {
  96. o = {}
  97. } else {
  98. return data
  99. }
  100. if (t === 'array') {
  101. for (let i = 0; i < data.length; i++) {
  102. o.push(deepCopy(data[i]))
  103. }
  104. } else if (t === 'object') {
  105. for (const i in data) {
  106. o[i] = deepCopy(data[i])
  107. }
  108. }
  109. return o
  110. }
  111. /**
  112. * 深覆盖
  113. * @param target
  114. * @param merged
  115. */
  116. function deepMerge(target, merged) {
  117. for (const key in merged) {
  118. if (target[key] && typeof target[key] === 'object') {
  119. deepMerge(target[key], merged[key])
  120. continue
  121. }
  122. if (typeof merged[key] === 'object') {
  123. target[key] = deepCopy(merged[key])
  124. continue
  125. }
  126. target[key] = merged[key]
  127. }
  128. return target
  129. }
  130. /**
  131. * 节流函数(限制函数的执行频率)返回函数连续调用空闲时间必须大于或等于 waitfunc 才会执行
  132. * @param {function} func 回调函数
  133. * @param {number} wait 表示时间窗口的间隔
  134. * @param immediate 是否立即执行 true 则先调用false不先调用
  135. * @return {function} 返回客户调用函数
  136. */
  137. function throttle(func, wait, immediate) {
  138. let timeoutID
  139. let lastExec = 0
  140. function wrapper() {
  141. const self = this
  142. const elapsed = Number(new Date()) - lastExec
  143. const args = arguments
  144. function clearExistingTimeout() {
  145. if (timeoutID) {
  146. clearTimeout(timeoutID)
  147. }
  148. }
  149. function clear() {
  150. timeoutID = undefined
  151. }
  152. function exec() {
  153. lastExec = Number(new Date())
  154. func.apply(self, args)
  155. }
  156. if (immediate && !timeoutID) {
  157. exec()
  158. }
  159. clearExistingTimeout()
  160. if (immediate === undefined && elapsed > wait) {
  161. exec()
  162. } else {
  163. timeoutID = setTimeout(immediate ? clear : exec, immediate === undefined ? wait - elapsed : wait)
  164. }
  165. }
  166. return wrapper
  167. }
  168. /**
  169. * 防抖函数(限制函数的执行频率) 保证再一系列调用时间内只调用一次
  170. *
  171. * @param {function} func 回调函数
  172. * @param {number} wait 表示时间窗口的间隔
  173. * @return {function} 返回客户调用函数
  174. */
  175. function debounce(func, wait) {
  176. return throttle(func, wait, false)
  177. }
  178. const { createApp, createVNode, render, nextTick, onBeforeUnmount, onUnmounted, onMounted, reactive, ref, toRefs, watch } = Vue;
  179. const app = createApp({})
  180. function useAutoResize(props, afterResizeFun) {
  181. const domRef = ref(null) // dorm容器,默认设置为domRef
  182. const status = reactive({
  183. width: 0,
  184. height: 0,
  185. })
  186. let __resizeHandler = null
  187. function resize(resize = true) {
  188. nextTick().then(() => {
  189. const dom = domRef.value
  190. status.width = dom ? dom.clientWidth : 0
  191. status.height = dom ? dom.clientHeight : 0
  192. if (!dom) {
  193. console.warn('fei-datav: Failed to get dom node, component rendering may be abnormal!')
  194. } else if (!status.width || !status.height) {
  195. console.warn('fei-datav: Component width or height is 0px, rendering abnormality may occur!')
  196. }
  197. if (typeof afterResizeFun === 'function' && resize) afterResizeFun()
  198. })
  199. }
  200. watch([() => props.containerWidth, () => props.containerHeight], () => {
  201. __resizeHandler && __resizeHandler();
  202. })
  203. onMounted(() => {
  204. setTimeout(() => {
  205. resize();
  206. }, 500)
  207. __resizeHandler = debounce(resize, 100)
  208. })
  209. return {
  210. domRef,
  211. ...toRefs(status),
  212. resize,
  213. }
  214. }
  215. const ScrollRankBoard = {
  216. template: \`
  217. <div ref="domRef" class="dv-scroll-ranking-board" :style="{color: textColor}">
  218. <div
  219. v-for="(item, i) in rows"
  220. :key="item.toString() + item.scroll"
  221. class="row-item"
  222. :style="getRowItemStyle(heights, i)"
  223. >
  224. <div class="ranking-info" :style="{fontSize: fontSize}">
  225. <div v-if="showRank" class="rank" :style="{color: numberColor || color }">
  226. {{ getRanking(item, rankFormat) }}
  227. </div>
  228. <div class="info-name" v-html="item.name" :style="{color: nameColor || textColor }"/>
  229. <div v-if="showRatio" :style="getRatioStyle(ratioColor, ratioSpace)">{{item.percent.toFixed(2) + '%'}}</div>
  230. <div class="ranking-value" v-html="getValueUnit(mergedConfig, item, valueColor, unitColor, fontFamily, fontStyle, gradientFontColor)"></div>
  231. </div>
  232. <div class="ranking-column" :style="getRankingColumnStyle(rankColumnStyle)">
  233. <div
  234. ref="insideColRefs"
  235. class="inside-column"
  236. :style="getInsideColumnStyle(item, color, insideColumnStyle)"
  237. >
  238. <div v-if="divideInsideColumn" class="dividerColumn" v-html="dividerColumnContent(containerWidth, containerHeight, item, i, dividerWidth, dividerSpace, dividerBack)"></div>
  239. <div class="shine" />
  240. </div>
  241. </div>
  242. </div>
  243. </div>
  244. \`,
  245. name: 'ScrollRankingBoard',
  246. props: {
  247. config: {
  248. type: Object,
  249. default: () => ({}),
  250. },
  251. containerWidth: {
  252. type: Number,
  253. default: 500,
  254. },
  255. containerHeight: {
  256. type: Number,
  257. default: 200,
  258. },
  259. rankColumnStyle: {
  260. type: String,
  261. default: '',
  262. },
  263. insideColumnStyle: {
  264. type: String,
  265. default: '',
  266. },
  267. enableCarousel: {
  268. type: Boolean,
  269. default: false
  270. },
  271. showRank: {
  272. type: Boolean,
  273. default: false
  274. },
  275. showRatio: {
  276. type: Boolean,
  277. default: false
  278. },
  279. nameColor: {
  280. type: String,
  281. default: 'rgba(8, 80, 164, 1)',
  282. },
  283. valueColor: {
  284. type: String,
  285. default: 'rgba(8, 80, 164, 1)',
  286. },
  287. unitColor: {
  288. type: String,
  289. default: 'rgba(8, 80, 164, 1)',
  290. },
  291. ratioColor: {
  292. type: String,
  293. default: 'rgba(8, 80, 164, 1)',
  294. },
  295. numberColor: {
  296. type: String,
  297. default: 'rgba(8, 80, 164, 1)',
  298. },
  299. rankFormat: {
  300. type: String,
  301. default: 'No.1',
  302. },
  303. ratioSpace: {
  304. type: Number,
  305. default: 70
  306. },
  307. divideInsideColumn: {
  308. type: Boolean,
  309. default: false
  310. },
  311. dividerWidth: {
  312. type: Number,
  313. default: 2
  314. },
  315. dividerSpace: {
  316. type: Number,
  317. default: 2
  318. },
  319. dividerBack: {
  320. type: String,
  321. default: '#fff',
  322. },
  323. fontFamily: {
  324. type: String,
  325. default: '',
  326. },
  327. fontColor: {
  328. type: String,
  329. default: '',
  330. },
  331. fontStyle: {
  332. type: String,
  333. default: '',
  334. },
  335. gradientFontColor: {
  336. type: String,
  337. default: '',
  338. }
  339. },
  340. computed: {
  341. getRowItemStyle: () => (heights, i) => {
  342. return {
  343. height: heights[i] + 'px',
  344. }
  345. },
  346. getRankingColumnStyle: () => (rankColumnStyle) => {
  347. const fn = new Function('', rankColumnStyle);
  348. let styles = {};
  349. const styleObj = fn();
  350. if (styleObj && typeof styleObj === 'object') {
  351. styles = {
  352. ...styleObj
  353. }
  354. }
  355. return {
  356. ...styles
  357. }
  358. },
  359. getInsideColumnStyle: () => (item, color, insideColumnStyle) => {
  360. const fn = new Function('', insideColumnStyle);
  361. let styles = {};
  362. const styleObj = fn();
  363. if (styleObj && typeof styleObj === 'object') {
  364. styles = {
  365. ...styleObj
  366. }
  367. }
  368. return {
  369. width: item.percent + '%',
  370. background: color,
  371. ...styles
  372. }
  373. },
  374. getValueUnit: () => (mergedConfig, item, valueColor, unitColor, fontFamily, fontStyle, gradientFontColor) => {
  375. const style = {};
  376. if(fontStyle) {
  377. if (fontStyle.includes('bold')) {
  378. style.fontWeight = 'bold';
  379. }
  380. if(fontStyle.includes('italic')) {
  381. style.fontStyle = 'italic'
  382. }
  383. if (fontStyle.includes('underline,line-through')) {
  384. style.textDecoration = 'underline line-through'
  385. } else if (fontStyle.includes('line-through,underline')) {
  386. style.textDecoration = 'line-through underline'
  387. } else if (fontStyle.includes('underline')) {
  388. style.textDecoration = 'underline'
  389. } else if (fontStyle.includes('line-through')) {
  390. style.textDecoration = 'line-through'
  391. }
  392. }
  393. if (mergedConfig.valueFormatter) {
  394. return mergedConfig.valueFormatter(item, valueColor, unitColor)
  395. } else {
  396. return \`<span style='color: \${gradientFontColor || valueColor}; font-family: \${fontFamily};font-weight: \${style.fontWeight}; font-style: \${style.fontStyle};text-decoration: \${style.textDecoration}'>\${item.value}</span><span style='color: \${unitColor}; margin-left: 3px'>\${mergedConfig.unit}</span>\`
  397. }
  398. },
  399. getRanking: () => (item, rankFormat) => {
  400. if (rankFormat === 'No.1') {
  401. return \`No.\${item.ranking}\`
  402. } else if (rankFormat === '1.') {
  403. return \`\${item.ranking}.\`;
  404. } else if (rankFormat === '1.') {
  405. return \`\${item.ranking}\`
  406. }
  407. },
  408. getRatioStyle: () => (ratioColor, ratioSpace) => {
  409. return {
  410. color: ratioColor,
  411. flex: \`0 0 \${ratioSpace}%\`,
  412. textAlign: 'left'
  413. }
  414. },
  415. dividerColumnContent: () => (width, height, item, ri, dividerWidth, dividerSpace, dividerBack) => {
  416. const totalDividers = Math.floor(width * (item.percent/100) / (dividerWidth + dividerSpace));
  417. const arr = new Array(totalDividers).fill('');
  418. let strs = '';
  419. arr.forEach((_, index) => {
  420. const span = \`<span style='float: left; display: inline-block; width: \${dividerWidth}px; height: 100%; background: \${dividerBack}; margin-left: \${dividerSpace}px'></span>\`;
  421. strs += span;
  422. })
  423. return strs;
  424. }
  425. },
  426. emits: ['mouseover', 'click'],
  427. setup(props, { emit }) {
  428. const { onMounted, onUnmounted, watch, reactive, ref, toRefs, computed } = Vue;
  429. const insideColRefs = ref([]);
  430. const dividerColumnContents = ref([]);
  431. function calcData() {
  432. stopAnimation()
  433. mergeConfig()
  434. calcRowsData()
  435. calcHeights()
  436. animation(true)
  437. }
  438. const { domRef, width, height, resize } = useAutoResize(props, calcData)
  439. const defaultConfig = ref({
  440. /**
  441. * @description Board data
  442. * @type {Array<Object>}
  443. * @default data = []
  444. */
  445. data: [],
  446. /**
  447. * @description Row num
  448. * @type {Number}
  449. * @default rowNum = 5
  450. */
  451. rowNum: 5,
  452. /**
  453. * @description Scroll wait time
  454. * @type {Number}
  455. * @default waitTime = 2000
  456. */
  457. waitTime: 2000,
  458. /**
  459. * @description Carousel type
  460. * @type {String}
  461. * @default carousel = 'single'
  462. * @example carousel = 'single' | 'page'
  463. */
  464. carousel: 'single',
  465. /**
  466. * @description Value unit
  467. * @type {String}
  468. * @default unit = ''
  469. * @example unit = 'ton'
  470. */
  471. unit: '',
  472. /**
  473. * @description Auto sort by value
  474. * @type {Boolean}
  475. * @default sort = true
  476. */
  477. sort: true,
  478. /**
  479. * @description Value formatter
  480. * @type {Function}
  481. * @default valueFormatter = null
  482. */
  483. valueFormatter: null,
  484. /**
  485. * @description Text color
  486. * @type {String}
  487. * @default textColor = '#fff'
  488. */
  489. textColor: '#fff',
  490. /**
  491. * @description Main theme color
  492. * @type {String}
  493. * @default color = '#1370fb'
  494. */
  495. color: '#1370fb',
  496. /**
  497. * @description Font size
  498. * @type {Number}
  499. * @default fontSize = 13
  500. */
  501. fontSize: 13,
  502. })
  503. const status = reactive({
  504. mergedConfig: null,
  505. rowsData: [],
  506. rows: [],
  507. heights: [],
  508. avgHeight: 0,
  509. animationIndex: 0,
  510. animationHandler: '',
  511. updater: 0,
  512. })
  513. watch(() => props.config, () => {
  514. stopAnimation()
  515. calcData()
  516. }, {
  517. deep: true,
  518. })
  519. const textColor = computed(() => {
  520. return props.config.textColor ? props.config.textColor : defaultConfig.value.textColor
  521. })
  522. const color = computed(() => {
  523. return props.config.color ? props.config.color : defaultConfig.value.color
  524. })
  525. const fontSize = computed(() => {
  526. return \`\${props.config.fontSize ? props.config.fontSize : defaultConfig.value.fontSize}px\`
  527. })
  528. onUnmounted(() => {
  529. stopAnimation()
  530. })
  531. function onResize() {
  532. if (!status.mergedConfig)
  533. return
  534. calcHeights(true)
  535. }
  536. function mergeConfig() {
  537. status.mergedConfig = deepMerge(deepCopy(defaultConfig.value, true), props.config || {})
  538. }
  539. function calcRowsData() {
  540. let { data } = status.mergedConfig
  541. const { rowNum, sort } = status.mergedConfig
  542. sort && data.sort(({ value: a }, { value: b }) => {
  543. if (a > b)
  544. return -1
  545. else if (a < b)
  546. return 1
  547. else
  548. return 0
  549. })
  550. const value = data.map(({ value }) => value)
  551. const min = Math.min(...value) || 0
  552. // abs of min
  553. const minAbs = Math.abs(min)
  554. const max = Math.max(...value) || 0
  555. // abs of max
  556. const maxAbs = Math.abs(max)
  557. const total = max + minAbs
  558. data = data.map((row, i) => ({ ...row, ranking: i + 1, percent: (row.value + minAbs) / total * 100 }))
  559. const rowLength = data.length
  560. if (rowLength > rowNum && rowLength < 2 * rowNum)
  561. data = [...data, ...data]
  562. data = data.map((d, i) => ({ ...d, scroll: i }))
  563. status.rowsData = data
  564. status.rows = data
  565. }
  566. function calcHeights(onresize = false) {
  567. const { rowNum, data } = status.mergedConfig
  568. const avgHeight = height.value / rowNum
  569. status.avgHeight = avgHeight
  570. if (!onresize)
  571. status.heights = new Array(data.length).fill(avgHeight)
  572. }
  573. const isSingle = computed(() => status.mergedConfig.carousel === 'single')
  574. async function animation(start = false) {
  575. if (!props.enableCarousel) return;
  576. const { waitTime, rowNum } = status.mergedConfig
  577. const rowLength = status.rowsData.length
  578. if (rowNum >= rowLength) return
  579. const { updater } = status
  580. if (start) {
  581. await new Promise(resolve => setTimeout(resolve, waitTime))
  582. if (updater !== status.updater)
  583. return
  584. }
  585. const animationNum = isSingle.value ? 1 : rowNum
  586. const rows = status.rowsData.slice(status.animationIndex)
  587. rows.push(...status.rowsData.slice(0, status.animationIndex))
  588. status.rows = rows.slice(0, isSingle.value ? rowNum + 1 : rowNum * 2)
  589. status.heights = new Array(status.rows.length).fill(status.avgHeight)
  590. await new Promise(resolve => setTimeout(resolve, 300))
  591. if (updater !== status.updater)
  592. return
  593. status.heights.splice(0, animationNum, ...new Array(animationNum).fill(0))
  594. status.animationIndex += animationNum
  595. const back = status.animationIndex - rowLength
  596. if (back >= 0)
  597. status.animationIndex = back
  598. status.animationIndex += animationNum
  599. status.animationHandler = setTimeout(animation, waitTime - 300)
  600. }
  601. function stopAnimation() {
  602. status.updater = (status.updater + 1) % 999999
  603. if (!status.animationHandler)
  604. return
  605. clearTimeout(status.animationHandler)
  606. }
  607. watch(() => props.config, () => {
  608. stopAnimation()
  609. status.animationIndex = 0
  610. calcData()
  611. }, { deep: true })
  612. onUnmounted(() => {
  613. stopAnimation()
  614. })
  615. return {
  616. defaultConfig,
  617. ...toRefs(status),
  618. domRef,
  619. color,
  620. textColor,
  621. fontSize,
  622. insideColRefs,
  623. }
  624. }
  625. }
  626. const defaultDatas = [
  627. {
  628. name: '周口',
  629. value: 55123,
  630. },
  631. {
  632. name: '南阳',
  633. value: 12022,
  634. },
  635. {
  636. name: '西峡',
  637. value: 78932
  638. },
  639. {
  640. name: '驻马店',
  641. value: 63411
  642. },
  643. {
  644. name: '新乡',
  645. value: 44231
  646. },
  647. {
  648. name: '信阳',
  649. value: 44531
  650. },
  651. {
  652. name: '郑州',
  653. value: 42531
  654. }
  655. ]
  656. class ScrollRankBoardNode extends HtmlResize.view {
  657. tableDatas = defaultDatas
  658. instance = null
  659. setHtml(rootEl) {
  660. if (!rootEl) return;
  661. const { graphModel, model } = this.props;
  662. const { properties, width, height, } = this.props.model;
  663. const { normalData } = properties.dynamic || {};
  664. const { customApiDatas } = normalData || {};
  665. const {
  666. rowNum, waitTime = 2000, color, textColor, fontSize, rankColumnStyle, insideColumnStyle,
  667. carousel, unit, enableCarousel, showRank, nameColor, valueColor, unitColor, showRatio, ratioColor,
  668. rankFormat, ratioSpace, divideInsideColumn, dividerWidth, dividerSpace, dividerBack, numberColor,
  669. fontFamily, fontColor, fontStyle, gradientFontColor} = properties;
  670. // 如果采用来自自定义数据源的数据
  671. if (customApiDatas) {
  672. this.tableDatas = customApiDatas || this.tableDatas;
  673. }
  674. if (this.instance) {
  675. // 实时数据不能推送一次就创建一次图表,可以在原有实例基础之上更改数据。
  676. Object.assign(this.instance.component.props, {
  677. name: properties.nodeAlias,
  678. config: {
  679. data: this.tableDatas,
  680. rowNum: rowNum,
  681. waitTime: parseInt(waitTime),
  682. carousel: carousel,
  683. unit: unit,
  684. sort: true,
  685. valueFormatter: null,
  686. color, textColor, fontSize,
  687. },
  688. containerWidth: width,
  689. containerHeight: height,
  690. rankColumnStyle, insideColumnStyle,
  691. enableCarousel, showRank, fontFamily, fontColor, fontStyle, gradientFontColor,
  692. nameColor, valueColor, unitColor, showRatio, ratioColor, numberColor,
  693. rankFormat, ratioSpace, divideInsideColumn, dividerWidth, dividerSpace, dividerBack
  694. })
  695. return
  696. }
  697. const el = document.createElement('div');
  698. rootEl.innerHTML = '';
  699. el.style.height = '100%';
  700. const instance = createVNode(ScrollRankBoard, {
  701. name: properties.nodeAlias,
  702. config: {
  703. data: this.tableDatas,
  704. rowNum: rowNum,
  705. waitTime: parseInt(waitTime),
  706. carousel: carousel,
  707. unit: unit,
  708. sort: true,
  709. valueFormatter: null,
  710. color, textColor, fontSize,
  711. },
  712. containerWidth: width,
  713. containerHeight: height,
  714. rankColumnStyle, insideColumnStyle,
  715. enableCarousel, showRank, fontFamily, fontColor, fontStyle, gradientFontColor,
  716. nameColor, valueColor, unitColor, showRatio, ratioColor, numberColor,
  717. rankFormat, ratioSpace, divideInsideColumn, dividerWidth, dividerSpace, dividerBack
  718. })
  719. instance.appContext = app._context
  720. render(instance, el)
  721. rootEl.appendChild(el);
  722. this.instance = instance;
  723. }
  724. }
  725. class ScrollRankBoardModel extends HtmlResize.model {
  726. initNodeData(data) {
  727. // 自定义组件,需最开始重置一下text 。
  728. data.text = {
  729. value: "",
  730. x: data.x,
  731. y: data.y,
  732. };
  733. super.initNodeData(data);
  734. const { properties } = this;
  735. this.width = properties.width || 80;
  736. this.height = properties.height || 35;
  737. this.text.editable = false; // 不允许文本被编辑
  738. }
  739. setAttributes() {
  740. // 自定义组件需重置 text
  741. const { x, y, properties } = this;
  742. const { textHorizontalMove = 0, textVerticalMove = 0 } = properties;
  743. this.text = {
  744. ...this.text,
  745. x: x + textHorizontalMove,
  746. y: y + textVerticalMove,
  747. value: "",
  748. }
  749. }
  750. }
  751. lf.register({
  752. type: 'scroll-rank-board',
  753. view: ScrollRankBoardNode,
  754. model: ScrollRankBoardModel,
  755. })`,css:`.dv-scroll-ranking-board {\r
  756. width: 100%;\r
  757. height: 100%;\r
  758. overflow: hidden;\r
  759. }\r
  760. \r
  761. .dv-scroll-ranking-board .row-item {\r
  762. transition: all 0.3s;\r
  763. display: flex;\r
  764. flex-direction: column;\r
  765. justify-content: center;\r
  766. overflow: hidden;\r
  767. }\r
  768. \r
  769. .dv-scroll-ranking-board .ranking-info {\r
  770. display: flex;\r
  771. width: 100%;\r
  772. }\r
  773. .dv-scroll-ranking-board .ranking-info .rank {\r
  774. width: 40px;\r
  775. }\r
  776. \r
  777. .dv-scroll-ranking-board .ranking-info .info-name {\r
  778. flex: 1;\r
  779. text-align: left;\r
  780. }\r
  781. \r
  782. .dv-scroll-ranking-board .ranking-column {\r
  783. margin-top: 5px;\r
  784. }\r
  785. \r
  786. .dv-scroll-ranking-board .ranking-column .inside-column {\r
  787. position: relative;\r
  788. height: 6px;\r
  789. margin-bottom: 2px;\r
  790. border-radius: 1px;\r
  791. overflow: hidden;\r
  792. }\r
  793. .dv-scroll-ranking-board .ranking-column .inside-column .dividerColumn {\r
  794. position: absolute;\r
  795. top: 0;\r
  796. right: 0;\r
  797. left: 0;\r
  798. bottom: 0;\r
  799. z-index: 10;\r
  800. overflow: hidden;\r
  801. }\r
  802. \r
  803. .dv-scroll-ranking-board .ranking-column .shine {\r
  804. position: absolute;\r
  805. left: 0%;\r
  806. top: 2px;\r
  807. height: 2px;\r
  808. width: 50px;\r
  809. transform: translateX(-100%);\r
  810. background: radial-gradient(rgb(40, 248, 255) 5%, transparent 80%);\r
  811. animation: shine 3s ease-in-out infinite alternate;\r
  812. }\r
  813. \r
  814. @keyframes shine {\r
  815. 80% {\r
  816. left: 0%;\r
  817. transform: translateX(-100%);\r
  818. }\r
  819. \r
  820. 100% {\r
  821. left: 100%;\r
  822. transform: translateX(0%);\r
  823. }\r
  824. }`,fakeData:""},m={id:e,name:n,aliasName:t,image:a,imageType:o,groupName:i,groupType:l,isRemote:!1,isDefault:!0,sectionType:r,config:s,files:d};export{t as aliasName,s as config,m as default,d as files,i as groupName,l as groupType,e as id,a as image,o as imageType,c as isDefault,u as isRemote,n as name,r as sectionType};