OperatingPanel.tsx 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669
  1. import React from 'react'
  2. import classnames from 'classnames'
  3. import set from 'lodash/set'
  4. import debounce from 'lodash/debounce'
  5. import widgetlibs from '../../config'
  6. import { IDataRequestBody } from 'app/containers/Dashboard/types'
  7. import { IViewBase, IFormedViews, IView } from 'containers/View/types'
  8. import { ViewModelVisualTypes, ViewModelTypes } from 'containers/View/constants'
  9. import Dropbox, {
  10. DropboxType,
  11. DropType,
  12. AggregatorType,
  13. IDataParamSource,
  14. IDataParamConfig,
  15. DragType,
  16. IDragItem
  17. } from './Dropbox'
  18. import {
  19. IWidgetProps,
  20. IChartStyles,
  21. IChartInfo,
  22. IPaginationParams,
  23. WidgetMode,
  24. RenderType,
  25. DimetionType
  26. } from '../Widget'
  27. import {
  28. IFieldConfig,
  29. getDefaultFieldConfig,
  30. FieldConfigModal
  31. } from '../Config/Field'
  32. import {
  33. IFieldFormatConfig,
  34. getDefaultFieldFormatConfig,
  35. FormatConfigModal
  36. } from '../Config/Format'
  37. import {
  38. IFieldSortConfig,
  39. FieldSortTypes,
  40. SortConfigModal
  41. } from '../Config/Sort'
  42. import ColorSettingForm from './ColorSettingForm'
  43. import ActOnSettingForm from './ActOnSettingForm'
  44. import FilterSettingForm from './FilterSettingForm'
  45. import LocalControlConfig from 'app/components/Control/Config'
  46. import ReferenceConfigModal from './Reference'
  47. import ComputedConfigForm from '../ComputedConfigForm'
  48. import ChartIndicator from './ChartIndicator'
  49. import AxisSection from './ConfigSections/AxisSection'
  50. import SplitLineSection from './ConfigSections/SplitLineSection'
  51. import PivotSection from './ConfigSections/PivotSection'
  52. import SpecSection from './ConfigSections/SpecSection'
  53. import LabelSection from './ConfigSections/LabelSection'
  54. import LegendSection from './ConfigSections/LegendSection'
  55. import VisualMapSection from './ConfigSections/VisualMapSection'
  56. import ToolboxSection from './ConfigSections/ToolboxSection'
  57. import DoubleYAxisSection from './ConfigSections/DoubleYAxisSection'
  58. import AreaSelectSection from './ConfigSections/AreaSelectSection'
  59. import ScorecardSection from './ConfigSections/ScorecardSection'
  60. import IframeSection from './ConfigSections/IframeSection'
  61. import TableSection from './ConfigSections/TableSection'
  62. import GaugeSection from './ConfigSections/GaugeSection'
  63. import BarSection from './ConfigSections/BarSection'
  64. import RadarSection from './ConfigSections/RadarSection'
  65. import {
  66. encodeMetricName,
  67. decodeMetricName,
  68. getPivot,
  69. getTable,
  70. getPivotModeSelectedCharts,
  71. checkChartEnable
  72. } from '../util'
  73. import { PIVOT_DEFAULT_SCATTER_SIZE_TIMES } from 'app/globalConstants'
  74. import PivotTypes from '../../config/pivot/PivotTypes'
  75. import { RadioChangeEvent } from 'antd/lib/radio'
  76. import {
  77. Row,
  78. Col,
  79. Icon,
  80. Menu,
  81. Radio,
  82. InputNumber,
  83. Dropdown,
  84. Modal,
  85. Popconfirm,
  86. Checkbox,
  87. notification,
  88. Tooltip,
  89. Select,
  90. message
  91. } from 'antd'
  92. import {
  93. IDistinctValueReqeustParams,
  94. IControl
  95. } from 'app/components/Control/types'
  96. import { IReference } from './Reference/types'
  97. import { REFERENCE_SUPPORTED_CHART_TYPES } from './Reference/constants'
  98. import { WorkbenchQueryMode } from './types'
  99. import { CheckboxChangeEvent } from 'antd/lib/checkbox'
  100. import { filterSelectOption } from 'app/utils/util'
  101. import {
  102. ControlPanelTypes,
  103. ControlQueryMode
  104. } from 'app/components/Control/constants'
  105. const MenuItem = Menu.Item
  106. const RadioButton = Radio.Button
  107. const RadioGroup = Radio.Group
  108. const confirm = Modal.confirm
  109. const Option = Select.Option
  110. const styles = require('./Workbench.less')
  111. const defaultTheme = require('assets/json/echartsThemes/default.project.json')
  112. const defaultThemeColors = defaultTheme.theme.color
  113. const utilStyles = require('assets/less/util.less')
  114. export interface IDataParamProperty {
  115. title: string
  116. type: DropboxType
  117. value?: { all?: any }
  118. items: IDataParamSource[]
  119. }
  120. export interface IDataParams {
  121. [key: string]: IDataParamProperty
  122. }
  123. interface IOperatingPanelProps {
  124. views: IViewBase[]
  125. formedViews: IFormedViews
  126. selectedViewId: number
  127. originalWidgetProps: IWidgetProps
  128. controls: any[]
  129. controlQueryMode: ControlQueryMode
  130. references: IReference[]
  131. limit: number
  132. cache: boolean
  133. autoLoadData: boolean
  134. expired: number
  135. workbenchQueryMode: WorkbenchQueryMode
  136. multiDrag: boolean
  137. computed: any[]
  138. originalComputed: any[]
  139. onViewSelect: (viewId: number) => void
  140. onSetControls: (controls: IControl[], queryMode: ControlQueryMode) => void
  141. onSetReferences: (references: IReference[]) => void
  142. onLimitChange: (value) => void
  143. onCacheChange: (e: RadioChangeEvent) => void
  144. onChangeAutoLoadData: (e: RadioChangeEvent) => void
  145. onExpiredChange: (expired: number) => void
  146. onSetComputed: (computesField: any[]) => void
  147. onDeleteComputed: (computesField: any[]) => void
  148. onSetWidgetProps: (widgetProps: IWidgetProps) => void
  149. onLoadData: (
  150. viewId: number,
  151. requestParams: IDataRequestBody,
  152. resolve: (data) => void,
  153. reject: (error) => void
  154. ) => void
  155. onLoadColumnDistinctValue: (
  156. paramsByViewId: {
  157. [viewId: string]: Omit<IDistinctValueReqeustParams, 'cache' | 'expired'>
  158. },
  159. callback: (options?: object[]) => void
  160. ) => void
  161. onLoadViews: (resolve?: () => void) => void
  162. onLoadViewDetail: (
  163. viewIds: number[],
  164. resolve?: (views: IView[]) => void
  165. ) => void
  166. }
  167. interface IOperatingPanelStates {
  168. dragged: IDataParamSource
  169. showColsAndRows: boolean
  170. mode: WidgetMode
  171. currentWidgetlibs: IChartInfo[]
  172. chartModeSelectedChart: IChartInfo
  173. // selectedTab: 'data' | 'style' | 'variable' | 'cache'
  174. selectedTab: 'data' | 'style' | 'setting'
  175. dataParams: IDataParams
  176. styleParams: IChartStyles
  177. pagination: IPaginationParams
  178. modalCachedData: IDataParamSource
  179. modalCallback: (data: boolean | IDataParamConfig) => void
  180. modalDataFrom: string
  181. distinctColumnValues: string[]
  182. currentEditingCommonParamKey: string
  183. currentEditingItem: IDataParamSource
  184. fieldModalVisible: boolean
  185. formatModalVisible: boolean
  186. sortModalVisible: boolean
  187. colorModalVisible: boolean
  188. actOnModalVisible: boolean
  189. actOnModalList: IDataParamSource[]
  190. filterModalVisible: boolean
  191. controlConfigVisible: boolean
  192. referenceConfigVisible: boolean
  193. categoryDragItems: IDragItem[]
  194. valueDragItems: IDragItem[]
  195. computedConfigModalVisible: boolean
  196. selectedComputed: object
  197. }
  198. export class OperatingPanel extends React.Component<
  199. IOperatingPanelProps,
  200. IOperatingPanelStates
  201. > {
  202. constructor(props) {
  203. super(props)
  204. this.state = {
  205. dragged: null,
  206. showColsAndRows: false,
  207. mode: 'pivot',
  208. currentWidgetlibs: widgetlibs['pivot'],
  209. chartModeSelectedChart: getTable(),
  210. selectedTab: 'data',
  211. dataParams: Object.entries(getPivot().data).reduce(
  212. (params: IDataParams, [key, value]) => {
  213. params[key] = { ...value, items: [] }
  214. return params
  215. },
  216. {}
  217. ),
  218. styleParams: {},
  219. pagination: { pageNo: 0, pageSize: 0, withPaging: false, totalCount: 0 },
  220. modalCachedData: null,
  221. modalCallback: null,
  222. modalDataFrom: '',
  223. distinctColumnValues: [],
  224. currentEditingCommonParamKey: '',
  225. currentEditingItem: null,
  226. fieldModalVisible: false,
  227. formatModalVisible: false,
  228. sortModalVisible: false,
  229. colorModalVisible: false,
  230. actOnModalVisible: false,
  231. actOnModalList: null,
  232. filterModalVisible: false,
  233. controlConfigVisible: false,
  234. referenceConfigVisible: false,
  235. categoryDragItems: [],
  236. valueDragItems: [],
  237. computedConfigModalVisible: false,
  238. selectedComputed: null
  239. }
  240. }
  241. private lastRequestParamString: string = ''
  242. private tabKeys = [
  243. { key: 'data', title: '数据' },
  244. { key: 'style', title: '样式' },
  245. { key: 'setting', title: '配置' }
  246. ]
  247. private colorSettingForm = null
  248. private actOnSettingForm = null
  249. private filterSettingForm = null
  250. private computedConfigForm = null
  251. private refHandlers = {
  252. computedConfigForm: (ref) => (this.computedConfigForm = ref)
  253. }
  254. public componentWillMount() {
  255. this.setState({
  256. ...this.getChartDataConfig(getPivotModeSelectedCharts([]))
  257. })
  258. }
  259. public componentWillReceiveProps(nextProps: IOperatingPanelProps) {
  260. const { formedViews, selectedViewId, originalWidgetProps } = nextProps
  261. if (selectedViewId && selectedViewId !== this.props.selectedViewId) {
  262. const selectedView = formedViews[selectedViewId]
  263. const model = selectedView.model
  264. const categoryDragItems = []
  265. const valueDragItems = []
  266. Object.entries(model).forEach(([key, m]) => {
  267. if (m.modelType === 'category') {
  268. categoryDragItems.push({
  269. name: key,
  270. type: 'category',
  271. visualType: m.visualType,
  272. checked: false
  273. })
  274. } else {
  275. valueDragItems.push({
  276. name: key,
  277. type: 'value',
  278. visualType: m.visualType,
  279. checked: false
  280. })
  281. }
  282. })
  283. this.setState({
  284. categoryDragItems,
  285. valueDragItems
  286. })
  287. }
  288. if (
  289. originalWidgetProps &&
  290. selectedViewId &&
  291. (originalWidgetProps !== this.props.originalWidgetProps ||
  292. selectedViewId !== this.props.selectedViewId)
  293. ) {
  294. const selectedView = formedViews[selectedViewId]
  295. const {
  296. cols,
  297. rows,
  298. metrics,
  299. secondaryMetrics,
  300. filters,
  301. color,
  302. label,
  303. size,
  304. xAxis,
  305. tip,
  306. chartStyles,
  307. mode,
  308. selectedChart
  309. } = originalWidgetProps
  310. const { dataParams } = this.state
  311. const model = selectedView.model
  312. const currentWidgetlibs = widgetlibs[mode || 'pivot'] // FIXME 兼容 0.3.0-beta.1 之前版本
  313. if (mode === 'pivot') {
  314. model['指标名称'] = {
  315. sqlType: 'VARCHAR',
  316. visualType: ViewModelVisualTypes.String,
  317. modelType: ViewModelTypes.Category
  318. }
  319. }
  320. cols.forEach((c) => {
  321. const modelColumn = model[c.name]
  322. if (modelColumn) {
  323. dataParams.cols.items = dataParams.cols.items.concat({
  324. ...c,
  325. from: 'cols',
  326. type: 'category' as DragType,
  327. visualType:
  328. c.name === '指标名称'
  329. ? ViewModelVisualTypes.String
  330. : modelColumn.visualType
  331. })
  332. }
  333. })
  334. rows.forEach((r) => {
  335. const modelColumn = model[r.name]
  336. if (modelColumn) {
  337. dataParams.rows.items = dataParams.rows.items.concat({
  338. ...r,
  339. from: 'rows',
  340. type: 'category' as DragType,
  341. visualType:
  342. r.name === '指标名称'
  343. ? ViewModelVisualTypes.String
  344. : modelColumn.visualType
  345. })
  346. }
  347. })
  348. if (secondaryMetrics) {
  349. dataParams.metrics = {
  350. title: '左轴指标',
  351. type: 'value',
  352. items: []
  353. }
  354. }
  355. metrics.forEach((m) => {
  356. const modelColumn = model[decodeMetricName(m.name)]
  357. if (modelColumn) {
  358. dataParams.metrics.items = dataParams.metrics.items.concat({
  359. ...m,
  360. from: 'metrics',
  361. type: 'value' as DragType,
  362. visualType: modelColumn.visualType,
  363. chart: currentWidgetlibs.find((wl) => wl.id === m.chart.id) // FIXME 兼容 0.3.0-beta.1 之前版本,widgetlib requireDimetions requireMetrics 有发生变更
  364. })
  365. }
  366. })
  367. if (secondaryMetrics) {
  368. dataParams.secondaryMetrics = {
  369. title: '右轴指标',
  370. type: 'value',
  371. items: []
  372. }
  373. secondaryMetrics.forEach((m) => {
  374. const modelColumn = model[decodeMetricName(m.name)]
  375. if (modelColumn) {
  376. dataParams.secondaryMetrics.items = dataParams.secondaryMetrics.items.concat(
  377. {
  378. ...m,
  379. from: 'secondaryMetrics',
  380. type: 'value' as DragType,
  381. visualType: modelColumn.visualType
  382. }
  383. )
  384. }
  385. })
  386. }
  387. filters.forEach((f) => {
  388. const modelColumn = model[f.name]
  389. if (modelColumn) {
  390. dataParams.filters.items = dataParams.filters.items.concat({
  391. ...f,
  392. visualType: modelColumn.visualType
  393. })
  394. }
  395. })
  396. const mergedDataParams = {
  397. ...dataParams,
  398. ...(color && { color }),
  399. ...(label && { label }),
  400. ...(size && { size }),
  401. ...(xAxis && { xAxis }),
  402. ...(tip && { tip })
  403. }
  404. this.setState(
  405. {
  406. mode: mode || 'pivot', // FIXME 兼容 0.3.0-beta.1 之前版本
  407. currentWidgetlibs,
  408. ...(selectedChart && {
  409. chartModeSelectedChart: widgetlibs['chart'].find(
  410. (wl) => wl.id === selectedChart
  411. )
  412. }),
  413. dataParams: mergedDataParams,
  414. styleParams: chartStyles,
  415. showColsAndRows: !!rows.length
  416. },
  417. () => {
  418. this.setWidgetProps(mergedDataParams, chartStyles)
  419. }
  420. )
  421. }
  422. }
  423. public componentWillUnmount() {
  424. notification.destroy()
  425. }
  426. private getChartDataConfig = (selectedCharts: IChartInfo[]) => {
  427. const { mode } = this.state
  428. const { dataParams, styleParams } = this.state
  429. const { metrics, color, size } = dataParams
  430. const dataConfig = {}
  431. const styleConfig = {}
  432. let specSign = false
  433. selectedCharts.forEach((chartInfo) => {
  434. Object.entries(chartInfo.data).forEach(
  435. ([key, prop]: [string, IDataParamProperty]) => {
  436. if (!dataConfig[key]) {
  437. let value = null
  438. switch (key) {
  439. case 'color':
  440. value =
  441. color && color.value
  442. ? {
  443. all: color.value.all,
  444. ...metrics.items.reduce((props, item, i) => {
  445. props[item.name] =
  446. mode === 'pivot'
  447. ? color.value[item.name] || color.value['all']
  448. : color.value[item.name] || defaultThemeColors[i]
  449. return props
  450. }, {})
  451. }
  452. : { all: defaultThemeColors[0] }
  453. break
  454. case 'size':
  455. value =
  456. size && size.value
  457. ? size.value
  458. : { all: PIVOT_DEFAULT_SCATTER_SIZE_TIMES }
  459. break
  460. }
  461. dataConfig[key] = {
  462. ...prop,
  463. ...(value && { value }),
  464. items: dataParams[key] ? dataParams[key].items : []
  465. }
  466. }
  467. }
  468. )
  469. Object.entries(chartInfo.style).forEach(
  470. ([key, prop]: [string, object]) => {
  471. if (key !== 'spec') {
  472. styleConfig[key] = {
  473. ...prop,
  474. ...styleParams[key]
  475. }
  476. } else {
  477. specSign = true
  478. }
  479. }
  480. )
  481. })
  482. if (specSign) {
  483. styleConfig['spec'] = selectedCharts.reduce((spec, chartInfo) => {
  484. const specConfig = chartInfo.style['spec'] || {}
  485. return {
  486. ...spec,
  487. ...Object.entries(specConfig).reduce((obj, [key, value]) => {
  488. const settledValue = styleParams.spec && styleParams.spec[key]
  489. obj[key] = settledValue !== void 0 ? settledValue : value
  490. return obj
  491. }, {})
  492. }
  493. }, {})
  494. }
  495. return {
  496. dataParams: dataConfig,
  497. styleParams: styleConfig
  498. }
  499. }
  500. private getDragItemIconClass = (type: ViewModelVisualTypes) => {
  501. switch (type) {
  502. case ViewModelVisualTypes.Number:
  503. return 'icon-values'
  504. case ViewModelVisualTypes.Date:
  505. return `icon-calendar ${styles.iconDate}`
  506. case ViewModelVisualTypes.GeoCountry:
  507. case ViewModelVisualTypes.GeoProvince:
  508. case ViewModelVisualTypes.GeoCity:
  509. return 'icon-map'
  510. default:
  511. return 'icon-categories'
  512. }
  513. }
  514. private dragStart = (item) => (
  515. e: React.DragEvent<HTMLLIElement | HTMLParagraphElement>
  516. ) => {
  517. // hack firefox trigger dragEnd
  518. e.dataTransfer.setData('text/plain', '')
  519. this.setState({
  520. dragged: { ...item }
  521. })
  522. }
  523. private dragEnd = () => {
  524. if (this.state.dragged) {
  525. this.setState({
  526. dragged: null
  527. })
  528. }
  529. }
  530. private insideDragStart = (from: string) => (
  531. item: IDataParamSource,
  532. e: React.DragEvent<HTMLLIElement | HTMLParagraphElement>
  533. ) => {
  534. this.dragStart({ ...item, from })(e)
  535. }
  536. private insideDragEnd = (dropType: DropType) => {
  537. if (!dropType) {
  538. const {
  539. dragged: { name, from },
  540. dataParams,
  541. styleParams
  542. } = this.state
  543. const prop = dataParams[from]
  544. prop.items = prop.items.filter((i) => i.name !== name)
  545. this.setWidgetProps(dataParams, styleParams)
  546. }
  547. this.setState({
  548. dragged: null
  549. })
  550. }
  551. private beforeDrop = (name, cachedItem, resolve) => {
  552. const { selectedViewId, onLoadColumnDistinctValue } = this.props
  553. const { mode, dataParams } = this.state
  554. const { metrics } = dataParams
  555. if (
  556. mode === 'pivot' &&
  557. cachedItem.name === '指标名称' &&
  558. !['cols', 'rows'].includes(name)
  559. ) {
  560. resolve(false)
  561. this.setState({ dragged: null })
  562. return
  563. }
  564. switch (name) {
  565. case 'filters':
  566. if (
  567. cachedItem.visualType !== 'number' &&
  568. cachedItem.visualType !== 'date'
  569. ) {
  570. onLoadColumnDistinctValue(
  571. { [selectedViewId]: { columns: [cachedItem.name] } },
  572. (results) => {
  573. this.getColumnDistinctValueText(results, cachedItem.name)
  574. }
  575. )
  576. }
  577. this.setState({
  578. modalCachedData: cachedItem,
  579. modalCallback: resolve,
  580. modalDataFrom: 'filters',
  581. filterModalVisible: true
  582. })
  583. break
  584. case 'color':
  585. onLoadColumnDistinctValue(
  586. { [selectedViewId]: { columns: [cachedItem.name] } },
  587. (results) => {
  588. this.getColumnDistinctValueText(results, cachedItem.name)
  589. }
  590. )
  591. this.setState({
  592. modalCachedData: cachedItem,
  593. modalCallback: resolve,
  594. modalDataFrom: 'color',
  595. colorModalVisible: true
  596. })
  597. break
  598. case 'label':
  599. this.setState({
  600. modalCachedData: cachedItem,
  601. modalCallback: resolve,
  602. modalDataFrom: 'label',
  603. actOnModalVisible: true,
  604. actOnModalList: metrics.items.slice()
  605. })
  606. break
  607. case 'size':
  608. if (mode === 'pivot') {
  609. this.setState({
  610. modalCachedData: cachedItem,
  611. modalCallback: resolve,
  612. modalDataFrom: 'size',
  613. actOnModalVisible: true,
  614. actOnModalList: metrics.items.filter(
  615. (m) => m.chart.id === PivotTypes.Scatter
  616. )
  617. })
  618. } else {
  619. resolve(true)
  620. }
  621. break
  622. default:
  623. resolve(true)
  624. break
  625. }
  626. }
  627. private drop = (
  628. name: string,
  629. dropIndex: number,
  630. dropType: DropType,
  631. changedItems: IDataParamSource[],
  632. config?: IDataParamConfig
  633. ) => {
  634. const { multiDrag } = this.props
  635. const {
  636. dragged: stateDragged,
  637. dataParams,
  638. styleParams,
  639. modalCachedData,
  640. categoryDragItems,
  641. valueDragItems
  642. } = this.state
  643. const dragged = stateDragged || modalCachedData
  644. const from =
  645. dragged.from && dragged.from !== name && dataParams[dragged.from]
  646. const destination = dataParams[name]
  647. const { items } = destination
  648. const multiDragCategoryDropboxNames = ['cols', 'rows']
  649. const multiDragValueDropboxNames = ['metrics', 'secondaryMetrics']
  650. if (
  651. multiDrag &&
  652. dropType === 'outside' &&
  653. multiDragCategoryDropboxNames
  654. .concat(multiDragValueDropboxNames)
  655. .includes(name)
  656. ) {
  657. let selectedItems = []
  658. if (multiDragCategoryDropboxNames.includes(name)) {
  659. selectedItems = selectedItems.concat(
  660. categoryDragItems
  661. .filter(
  662. (item) =>
  663. item.checked &&
  664. item.name !== dragged.name &&
  665. !items.find((i) => i.name === item.name)
  666. )
  667. .map(({ checked, ...rest }) => ({ ...rest }))
  668. .concat(dragged)
  669. )
  670. this.setState({
  671. categoryDragItems: categoryDragItems.map((item) => ({
  672. ...item,
  673. checked: false
  674. }))
  675. })
  676. } else if (multiDragValueDropboxNames.includes(name)) {
  677. selectedItems = selectedItems.concat(
  678. valueDragItems
  679. .filter(
  680. (item) =>
  681. item.checked && item.name !== decodeMetricName(dragged.name)
  682. )
  683. .map(
  684. ({ checked, ...rest }): IDataParamSource => ({
  685. ...rest,
  686. name: encodeMetricName(rest.name),
  687. agg: 'sum',
  688. chart: getPivot()
  689. })
  690. )
  691. .concat({ ...dragged, chart: getPivot() })
  692. )
  693. this.setState({
  694. valueDragItems: valueDragItems.map((item) => ({
  695. ...item,
  696. checked: false
  697. }))
  698. })
  699. }
  700. destination.items = [
  701. ...items.slice(0, dropIndex),
  702. ...selectedItems,
  703. ...items.slice(dropIndex)
  704. ]
  705. } else {
  706. if (config) {
  707. dragged.config = config
  708. if (['color', 'label', 'size'].includes(name)) {
  709. const actingOnItemIndex = items.findIndex(
  710. (i) => i.config.actOn === config.actOn
  711. )
  712. if (actingOnItemIndex >= 0) {
  713. items.splice(actingOnItemIndex, 1)
  714. dropIndex =
  715. dropIndex <= actingOnItemIndex ? dropIndex : dropIndex - 1
  716. }
  717. }
  718. if (name === 'xAxis') {
  719. items.splice(0, 1)
  720. dropIndex = 0
  721. }
  722. }
  723. if (dropType === 'outside') {
  724. let combinedItem = dragged
  725. if (name === 'metrics') {
  726. combinedItem = {
  727. ...dragged,
  728. chart: dataParams.metrics.items.length
  729. ? dataParams.metrics.items[0].chart
  730. : getPivot()
  731. }
  732. }
  733. if (name === 'secondaryMetrics') {
  734. combinedItem = {
  735. ...dragged,
  736. chart: dataParams.secondaryMetrics.items.length
  737. ? dataParams.secondaryMetrics.items[0].chart
  738. : getPivot()
  739. }
  740. }
  741. destination.items = [
  742. ...items.slice(0, dropIndex),
  743. combinedItem,
  744. ...items.slice(dropIndex)
  745. ]
  746. } else {
  747. destination.items = [...changedItems]
  748. }
  749. }
  750. if (from) {
  751. from.items = from.items.filter((i) => i.name !== dragged.name)
  752. }
  753. this.setState({
  754. dragged: null,
  755. modalCachedData: null
  756. })
  757. this.setWidgetProps(dataParams, styleParams)
  758. }
  759. private getColumnDistinctValueText = (results, column) => {
  760. this.setState({
  761. distinctColumnValues: results.map((r) => r[column])
  762. })
  763. }
  764. private toggleRowsAndCols = () => {
  765. const { dataParams, styleParams } = this.state
  766. const { cols, rows } = dataParams
  767. if (this.state.showColsAndRows && rows.items.length) {
  768. cols.items = cols.items.concat(rows.items)
  769. rows.items = []
  770. this.setWidgetProps(dataParams, styleParams)
  771. }
  772. this.setState({
  773. showColsAndRows: !this.state.showColsAndRows
  774. })
  775. }
  776. private switchRowsAndCols = () => {
  777. const { dataParams, styleParams } = this.state
  778. const { cols, rows } = dataParams
  779. let temp = cols.items.slice()
  780. cols.items = rows.items.slice()
  781. rows.items = temp
  782. temp = null
  783. this.setWidgetProps(dataParams, styleParams)
  784. }
  785. private removeDropboxItem = (from: string) => (name: string) => () => {
  786. const { dataParams, styleParams } = this.state
  787. const prop = dataParams[from]
  788. prop.items = prop.items.filter((i) => i.name !== name)
  789. this.setWidgetProps(dataParams, styleParams)
  790. }
  791. private getDropboxItemSortDirection = (from: string) => (
  792. item: IDataParamSource,
  793. sortType: FieldSortTypes
  794. ) => {
  795. const { dataParams, styleParams } = this.state
  796. const prop = dataParams[from]
  797. if (sortType !== FieldSortTypes.Custom) {
  798. item.sort = { sortType }
  799. prop.items = [...prop.items]
  800. this.setWidgetProps(dataParams, styleParams)
  801. } else {
  802. const { selectedViewId, onLoadColumnDistinctValue } = this.props
  803. onLoadColumnDistinctValue(
  804. { [selectedViewId]: { columns: [item.name] } },
  805. (results) => {
  806. this.getColumnDistinctValueText(results, item.name)
  807. }
  808. )
  809. this.setState({
  810. currentEditingCommonParamKey: from,
  811. currentEditingItem: item,
  812. sortModalVisible: true
  813. })
  814. }
  815. }
  816. private getDropboxItemAggregator = (from: string) => (
  817. item: IDataParamSource,
  818. agg: AggregatorType
  819. ) => {
  820. const { dataParams, styleParams } = this.state
  821. const prop = dataParams[from]
  822. item.agg = agg
  823. prop.items = [...prop.items]
  824. this.setWidgetProps(dataParams, styleParams)
  825. }
  826. private dropboxItemChangeFieldConfig = (from: string) => (
  827. item: IDataParamSource
  828. ) => {
  829. this.setState({
  830. currentEditingCommonParamKey: from,
  831. currentEditingItem: item,
  832. fieldModalVisible: true
  833. })
  834. }
  835. private saveFieldConfig = (fieldConfig: IFieldConfig) => {
  836. const {
  837. currentEditingCommonParamKey,
  838. currentEditingItem,
  839. dataParams,
  840. styleParams
  841. } = this.state
  842. const item = dataParams[currentEditingCommonParamKey].items.find(
  843. (i) => i.name === currentEditingItem.name
  844. )
  845. item.field = fieldConfig
  846. this.setWidgetProps(dataParams, styleParams)
  847. this.setState({
  848. fieldModalVisible: false
  849. })
  850. }
  851. private cancelFieldConfig = () => {
  852. this.setState({
  853. fieldModalVisible: false
  854. })
  855. }
  856. private dropboxItemChangeFormatConfig = (from: string) => (
  857. item: IDataParamSource
  858. ) => {
  859. this.setState({
  860. currentEditingCommonParamKey: from,
  861. currentEditingItem: item,
  862. formatModalVisible: true
  863. })
  864. }
  865. private saveFormatConfig = (formatConfig: IFieldFormatConfig) => {
  866. const {
  867. currentEditingCommonParamKey,
  868. currentEditingItem,
  869. dataParams,
  870. styleParams
  871. } = this.state
  872. const item = dataParams[currentEditingCommonParamKey].items.find(
  873. (i) => i.name === currentEditingItem.name
  874. )
  875. item.format = formatConfig
  876. this.setWidgetProps(dataParams, styleParams)
  877. this.setState({
  878. formatModalVisible: false
  879. })
  880. }
  881. private cancelFormatConfig = () => {
  882. this.setState({
  883. formatModalVisible: false
  884. })
  885. }
  886. private saveSortConfig = (sortConfig: IFieldSortConfig) => {
  887. const {
  888. currentEditingCommonParamKey,
  889. currentEditingItem,
  890. dataParams,
  891. styleParams
  892. } = this.state
  893. const item = dataParams[currentEditingCommonParamKey].items.find(
  894. (i) => i.name === currentEditingItem.name
  895. )
  896. item.sort = sortConfig
  897. this.setWidgetProps(dataParams, styleParams)
  898. this.setState({
  899. sortModalVisible: false
  900. })
  901. }
  902. private cancelSortConfig = () => {
  903. this.setState({ sortModalVisible: false })
  904. }
  905. private dropboxItemChangeColorConfig = (item: IDataParamSource) => {
  906. const { selectedViewId, onLoadColumnDistinctValue } = this.props
  907. const { dataParams, styleParams } = this.state
  908. onLoadColumnDistinctValue(
  909. { [selectedViewId]: { columns: [item.name] } },
  910. (results) => {
  911. this.getColumnDistinctValueText(results, item.name)
  912. }
  913. )
  914. this.setState({
  915. modalCachedData: item,
  916. modalDataFrom: 'color',
  917. modalCallback: (config) => {
  918. if (config) {
  919. const colorItems = dataParams.color.items
  920. const actingOnItemIndex = colorItems.findIndex(
  921. (i) => i.config.actOn === config['actOn'] && i.name !== item.name
  922. )
  923. if (actingOnItemIndex >= 0) {
  924. dataParams.color.items = [
  925. ...colorItems.slice(0, actingOnItemIndex),
  926. ...colorItems.slice(actingOnItemIndex + 1)
  927. ]
  928. }
  929. item.config = config as IDataParamConfig
  930. this.setWidgetProps(dataParams, styleParams)
  931. }
  932. },
  933. colorModalVisible: true
  934. })
  935. }
  936. private dropboxItemChangeFilterConfig = (item: IDataParamSource) => {
  937. const { selectedViewId, onLoadColumnDistinctValue } = this.props
  938. const { dataParams, styleParams } = this.state
  939. if (item.type === 'category') {
  940. onLoadColumnDistinctValue(
  941. { [selectedViewId]: { columns: [item.name] } },
  942. (results) => {
  943. this.getColumnDistinctValueText(results, item.name)
  944. }
  945. )
  946. }
  947. this.setState({
  948. modalCachedData: item,
  949. modalDataFrom: 'filters',
  950. modalCallback: (config) => {
  951. if (config) {
  952. item.config = config as IDataParamConfig
  953. this.setWidgetProps(dataParams, styleParams)
  954. }
  955. },
  956. filterModalVisible: true
  957. })
  958. }
  959. private getDropboxItemChart = (item: IDataParamSource) => (
  960. chart: IChartInfo
  961. ) => {
  962. const { dataParams } = this.state
  963. item.chart = chart
  964. dataParams.metrics.items = [...dataParams.metrics.items]
  965. const selectedParams = this.getChartDataConfig(
  966. getPivotModeSelectedCharts(dataParams.metrics.items)
  967. )
  968. this.setWidgetProps(selectedParams.dataParams, selectedParams.styleParams)
  969. }
  970. private getDimetionsAndMetricsCount = () => {
  971. const { dataParams } = this.state
  972. const { cols, rows, metrics, secondaryMetrics } = dataParams
  973. const dcount = cols.items.length + rows.items.length
  974. const mcount = secondaryMetrics
  975. ? secondaryMetrics.items.length + metrics.items.length
  976. : metrics.items.length
  977. return [dcount, mcount]
  978. }
  979. public flipPage = (pageNo: number, pageSize: number, orders) => {
  980. const { dataParams, styleParams, pagination } = this.state
  981. this.setWidgetProps(dataParams, styleParams, {
  982. renderType: 'rerender',
  983. updatedPagination: {
  984. ...pagination,
  985. pageNo,
  986. pageSize
  987. },
  988. workbenchQueryMode: WorkbenchQueryMode.Immediately,
  989. orders
  990. })
  991. }
  992. private forceSetWidgetProps = () => {
  993. const { dataParams, styleParams, pagination } = this.state
  994. this.setWidgetProps(dataParams, styleParams, {
  995. renderType: 'rerender',
  996. updatedPagination: pagination,
  997. workbenchQueryMode: WorkbenchQueryMode.Immediately
  998. })
  999. }
  1000. private setWidgetProps = (
  1001. dataParams: IDataParams,
  1002. styleParams: IChartStyles,
  1003. options?: {
  1004. renderType?: RenderType
  1005. updatedPagination?: IPaginationParams
  1006. workbenchQueryMode?: WorkbenchQueryMode
  1007. orders?
  1008. limit?
  1009. }
  1010. ) => {
  1011. const {
  1012. cols,
  1013. rows,
  1014. metrics,
  1015. secondaryMetrics,
  1016. filters,
  1017. color,
  1018. label,
  1019. size,
  1020. xAxis,
  1021. tip,
  1022. yAxis
  1023. } = dataParams
  1024. const {
  1025. formedViews,
  1026. selectedViewId,
  1027. onLoadData,
  1028. onSetWidgetProps
  1029. } = this.props
  1030. const { mode, chartModeSelectedChart, pagination } = this.state
  1031. const selectedView = formedViews[selectedViewId]
  1032. let renderType
  1033. let updatedPagination
  1034. let workbenchQueryMode = this.props.workbenchQueryMode
  1035. if (options) {
  1036. renderType = options.renderType
  1037. updatedPagination = options.updatedPagination
  1038. workbenchQueryMode = WorkbenchQueryMode[options.workbenchQueryMode]
  1039. ? options.workbenchQueryMode
  1040. : workbenchQueryMode
  1041. }
  1042. const fromPagination = !!updatedPagination
  1043. updatedPagination = { ...pagination, ...updatedPagination }
  1044. let groups = cols.items
  1045. .map((c) => c.name)
  1046. .concat(rows.items.map((r) => r.name))
  1047. .filter((g) => g !== '指标名称')
  1048. let aggregators = metrics.items.map((m) => ({
  1049. column: decodeMetricName(m.name),
  1050. func: m.agg
  1051. }))
  1052. if (secondaryMetrics) {
  1053. aggregators = aggregators.concat(
  1054. secondaryMetrics.items.map((m) => ({
  1055. column: decodeMetricName(m.name),
  1056. func: m.agg
  1057. }))
  1058. )
  1059. }
  1060. if (color) {
  1061. groups = groups.concat(color.items.map((c) => c.name))
  1062. }
  1063. if (label) {
  1064. groups = groups.concat(
  1065. label.items.filter((l) => l.type === 'category').map((l) => l.name)
  1066. )
  1067. aggregators = aggregators.concat(
  1068. label.items
  1069. .filter((l) => l.type === 'value')
  1070. .map((l) => ({
  1071. column: decodeMetricName(l.name),
  1072. func: l.agg
  1073. }))
  1074. )
  1075. }
  1076. if (size) {
  1077. aggregators = aggregators.concat(
  1078. size.items.map((l) => ({
  1079. column: decodeMetricName(l.name),
  1080. func: l.agg
  1081. }))
  1082. )
  1083. }
  1084. if (xAxis) {
  1085. aggregators = aggregators.concat(
  1086. xAxis.items.map((l) => ({
  1087. column: decodeMetricName(l.name),
  1088. func: l.agg
  1089. }))
  1090. )
  1091. }
  1092. if (tip) {
  1093. aggregators = aggregators.concat(
  1094. tip.items.map((l) => ({
  1095. column: decodeMetricName(l.name),
  1096. func: l.agg
  1097. }))
  1098. )
  1099. }
  1100. if (yAxis) {
  1101. aggregators = aggregators.concat(
  1102. yAxis.items.map((l) => ({
  1103. column: decodeMetricName(l.name),
  1104. func: l.agg
  1105. }))
  1106. )
  1107. }
  1108. const orders = []
  1109. Object.values(dataParams)
  1110. .reduce<IDataParamSource[]>(
  1111. (items, param: IDataParamProperty) => items.concat(param.items),
  1112. []
  1113. )
  1114. .forEach((item) => {
  1115. let column = item.name
  1116. if (item.type === 'value') {
  1117. column = decodeMetricName(item.name)
  1118. if (!styleParams.table || !styleParams.table.withNoAggregators) {
  1119. column = `${item.agg}(${column})`
  1120. }
  1121. }
  1122. if (
  1123. item.sort &&
  1124. [FieldSortTypes.Asc, FieldSortTypes.Desc].includes(item.sort.sortType)
  1125. ) {
  1126. orders.push({
  1127. column,
  1128. direction: item.sort.sortType
  1129. })
  1130. }
  1131. })
  1132. let selectedCharts
  1133. let dimetionsCount
  1134. if (mode === 'pivot') {
  1135. selectedCharts = getPivotModeSelectedCharts(metrics.items)
  1136. dimetionsCount = groups.length
  1137. } else {
  1138. selectedCharts = [chartModeSelectedChart]
  1139. dimetionsCount = cols.items.length
  1140. }
  1141. const metricsLength = secondaryMetrics
  1142. ? metrics.items.length + secondaryMetrics.items.length
  1143. : metrics.items.length
  1144. if (!checkChartEnable(dimetionsCount, metricsLength, selectedCharts)) {
  1145. selectedCharts =
  1146. mode === 'pivot' ? getPivotModeSelectedCharts([]) : [getTable()]
  1147. }
  1148. const mergedParams = this.getChartDataConfig(selectedCharts)
  1149. const mergedDataParams = mergedParams.dataParams
  1150. const mergedStyleParams = mergedParams.styleParams
  1151. let noAggregators = false
  1152. if (styleParams.table) {
  1153. // @FIXME pagination in table style config
  1154. const { withPaging, pageSize, withNoAggregators } = styleParams.table
  1155. noAggregators = withNoAggregators
  1156. if (!fromPagination) {
  1157. if (withPaging) {
  1158. updatedPagination.pageNo = 1
  1159. updatedPagination.pageSize = +pageSize
  1160. } else {
  1161. updatedPagination.pageNo = 0
  1162. updatedPagination.pageSize = 0
  1163. }
  1164. }
  1165. updatedPagination.withPaging = withPaging
  1166. }
  1167. const requestParamsFilters = filters.items.reduce((a, b) => {
  1168. return a.concat(b.config.sqlModel)
  1169. }, [])
  1170. const requestParams = {
  1171. groups,
  1172. aggregators,
  1173. // filters: filters.items.map((i) => [].concat(i.config.sql)),
  1174. filters: requestParamsFilters,
  1175. orders,
  1176. pageNo: updatedPagination.pageNo,
  1177. pageSize: updatedPagination.pageSize,
  1178. nativeQuery: noAggregators,
  1179. limit: options?.limit || this.props.limit,
  1180. cache: false,
  1181. expired: 0,
  1182. flush: false
  1183. }
  1184. if (options) {
  1185. if (options.orders) {
  1186. requestParams.orders = requestParams.orders.concat(options.orders)
  1187. }
  1188. }
  1189. const requestParamString = JSON.stringify(requestParams)
  1190. const needRequest =
  1191. (groups.length > 0 || aggregators.length > 0) &&
  1192. selectedViewId &&
  1193. requestParamString !== this.lastRequestParamString &&
  1194. workbenchQueryMode === WorkbenchQueryMode.Immediately
  1195. if (needRequest) {
  1196. this.lastRequestParamString = requestParamString
  1197. onLoadData(
  1198. selectedViewId,
  1199. requestParams,
  1200. (result) => {
  1201. const { resultList: data, pageNo, pageSize, totalCount } = result
  1202. updatedPagination = !updatedPagination.withPaging
  1203. ? updatedPagination
  1204. : {
  1205. ...updatedPagination,
  1206. pageNo,
  1207. pageSize,
  1208. totalCount
  1209. }
  1210. onSetWidgetProps({
  1211. cols: cols.items.map((item) => ({
  1212. ...item,
  1213. field: item.field || getDefaultFieldConfig(),
  1214. format: item.format || getDefaultFieldFormatConfig(),
  1215. sort: item.sort
  1216. })),
  1217. rows: rows.items.map((item) => ({
  1218. ...item,
  1219. field: item.field || getDefaultFieldConfig(),
  1220. format: item.format || getDefaultFieldFormatConfig(),
  1221. sort: item.sort
  1222. })),
  1223. metrics: metrics.items.map((item) => ({
  1224. ...item,
  1225. agg: item.agg || 'sum',
  1226. chart: item.chart || getPivot(),
  1227. field: item.field || getDefaultFieldConfig(),
  1228. format: item.format || getDefaultFieldFormatConfig()
  1229. })),
  1230. ...(secondaryMetrics && {
  1231. secondaryMetrics: secondaryMetrics.items.map((item) => ({
  1232. ...item,
  1233. agg: item.agg || 'sum',
  1234. chart: item.chart || getPivot(),
  1235. field: item.field || getDefaultFieldConfig(),
  1236. format: item.format || getDefaultFieldFormatConfig()
  1237. }))
  1238. }),
  1239. filters: filters.items.map(({ name, type, config }) => ({
  1240. name,
  1241. type,
  1242. config
  1243. })),
  1244. ...(color && { color }),
  1245. ...(label && { label }),
  1246. ...(size && { size }),
  1247. ...(xAxis && { xAxis }),
  1248. ...(tip && { tip }),
  1249. ...(yAxis && { yAxis }),
  1250. chartStyles: mergedStyleParams,
  1251. selectedChart:
  1252. mode === 'pivot'
  1253. ? chartModeSelectedChart.id
  1254. : selectedCharts[0].id,
  1255. data,
  1256. pagination: updatedPagination,
  1257. dimetionAxis: this.getDimetionAxis(selectedCharts),
  1258. renderType: renderType || 'rerender',
  1259. orders,
  1260. mode,
  1261. model: selectedView.model
  1262. })
  1263. this.setState({
  1264. chartModeSelectedChart:
  1265. mode === 'pivot' ? chartModeSelectedChart : selectedCharts[0],
  1266. pagination: updatedPagination,
  1267. dataParams: mergedDataParams,
  1268. styleParams: mergedStyleParams
  1269. })
  1270. },
  1271. (error) => {
  1272. notification.destroy()
  1273. notification.error({
  1274. message: '执行失败',
  1275. description: (
  1276. <Tooltip
  1277. placement="bottom"
  1278. trigger="click"
  1279. title={error.msg}
  1280. overlayClassName={styles.errorMessage}
  1281. >
  1282. <a>点击查看错误信息</a>
  1283. </Tooltip>
  1284. ),
  1285. duration: null
  1286. })
  1287. }
  1288. )
  1289. } else {
  1290. onSetWidgetProps({
  1291. data: null,
  1292. cols: cols.items.map((item) => ({
  1293. ...item,
  1294. field: item.field || getDefaultFieldConfig(),
  1295. format: item.format || getDefaultFieldFormatConfig(),
  1296. sort: item.sort
  1297. })),
  1298. rows: rows.items.map((item) => ({
  1299. ...item,
  1300. field: item.field || getDefaultFieldConfig(),
  1301. format: item.format || getDefaultFieldFormatConfig(),
  1302. sort: item.sort
  1303. })),
  1304. metrics: metrics.items.map((item) => ({
  1305. ...item,
  1306. agg: item.agg || 'sum',
  1307. chart: item.chart || getPivot(),
  1308. field: item.field || getDefaultFieldConfig(),
  1309. format: item.format || getDefaultFieldFormatConfig()
  1310. })),
  1311. ...(secondaryMetrics && {
  1312. secondaryMetrics: secondaryMetrics.items.map((item) => ({
  1313. ...item,
  1314. agg: item.agg || 'sum',
  1315. chart: item.chart || getPivot(),
  1316. field: item.field || getDefaultFieldConfig(),
  1317. format: item.format || getDefaultFieldFormatConfig()
  1318. }))
  1319. }),
  1320. filters: filters.items.map(({ name, type, config }) => ({
  1321. name,
  1322. type,
  1323. config
  1324. })),
  1325. ...(color && { color }),
  1326. ...(label && { label }),
  1327. ...(size && { size }),
  1328. ...(xAxis && { xAxis }),
  1329. ...(tip && { tip }),
  1330. ...(yAxis && { yAxis }),
  1331. chartStyles: mergedStyleParams,
  1332. selectedChart:
  1333. mode === 'pivot' ? chartModeSelectedChart.id : selectedCharts[0].id,
  1334. pagination: updatedPagination,
  1335. dimetionAxis: this.getDimetionAxis(selectedCharts),
  1336. renderType: renderType || 'clear',
  1337. orders,
  1338. mode,
  1339. model: selectedView ? selectedView.model : {}
  1340. })
  1341. this.setState({
  1342. chartModeSelectedChart:
  1343. mode === 'pivot' ? chartModeSelectedChart : selectedCharts[0],
  1344. pagination: updatedPagination,
  1345. dataParams: mergedDataParams,
  1346. styleParams: mergedStyleParams
  1347. })
  1348. }
  1349. }
  1350. private getDimetionAxis = (selectedCharts): DimetionType => {
  1351. const pivotChart = getPivot()
  1352. const onlyPivot = !selectedCharts.filter((sc) => sc.id !== pivotChart.id)
  1353. .length
  1354. if (!onlyPivot) {
  1355. return 'col'
  1356. }
  1357. }
  1358. private chartSelect = (chart: IChartInfo) => {
  1359. const { mode, dataParams } = this.state
  1360. const { cols, rows, metrics } = dataParams
  1361. if (mode === 'pivot') {
  1362. if (
  1363. !(metrics.items.length === 1 && metrics.items[0].chart.id === chart.id)
  1364. ) {
  1365. metrics.items.forEach((i) => {
  1366. i.chart = chart
  1367. })
  1368. if (chart.id !== PivotTypes.PivotTable) {
  1369. cols.items = cols.items.filter((c) => c.name !== '指标名称')
  1370. rows.items = rows.items.filter((r) => r.name !== '指标名称')
  1371. }
  1372. const selectedParams = this.getChartDataConfig(
  1373. getPivotModeSelectedCharts(metrics.items)
  1374. )
  1375. this.setWidgetProps(
  1376. selectedParams.dataParams,
  1377. selectedParams.styleParams
  1378. )
  1379. }
  1380. } else {
  1381. this.setState(
  1382. {
  1383. chartModeSelectedChart: chart,
  1384. pagination: {
  1385. pageNo: 0,
  1386. pageSize: 0,
  1387. withPaging: false,
  1388. totalCount: 0
  1389. }
  1390. },
  1391. () => {
  1392. const selectedParams = this.getChartDataConfig([chart])
  1393. this.setWidgetProps(
  1394. selectedParams.dataParams,
  1395. selectedParams.styleParams
  1396. )
  1397. }
  1398. )
  1399. }
  1400. }
  1401. private viewSelect = (viewId: number) => {
  1402. const { mode, dataParams } = this.state
  1403. const hasItems = Object.values(dataParams).filter(
  1404. (param) => !!param.items.length
  1405. )
  1406. if (hasItems.length) {
  1407. confirm({
  1408. title: '切换 View 会清空所有配置项,是否继续?',
  1409. onOk: () => {
  1410. this.resetWorkbench(mode)
  1411. this.props.onViewSelect(viewId)
  1412. }
  1413. })
  1414. } else {
  1415. this.props.onViewSelect(viewId)
  1416. }
  1417. }
  1418. private changeMode = (e) => {
  1419. const mode = e.target.value
  1420. const { dataParams } = this.state
  1421. const hasItems = Object.values(dataParams).filter(
  1422. (param) => !!param.items.length
  1423. )
  1424. if (hasItems.length) {
  1425. confirm({
  1426. title: '切换图表模式会清空所有配置项,是否继续?',
  1427. onOk: () => {
  1428. this.setState(
  1429. {
  1430. mode,
  1431. currentWidgetlibs: widgetlibs[mode]
  1432. },
  1433. () => {
  1434. this.resetWorkbench(mode)
  1435. }
  1436. )
  1437. }
  1438. })
  1439. } else {
  1440. this.setState(
  1441. {
  1442. mode,
  1443. currentWidgetlibs: widgetlibs[mode]
  1444. },
  1445. () => {
  1446. this.resetWorkbench(mode)
  1447. }
  1448. )
  1449. }
  1450. }
  1451. private resetWorkbench = (mode) => {
  1452. const { dataParams } = this.state
  1453. Object.values(dataParams).forEach((param) => {
  1454. param.items = []
  1455. if (param.value) {
  1456. param.value = {}
  1457. }
  1458. })
  1459. this.setState({
  1460. showColsAndRows: false,
  1461. chartModeSelectedChart: getTable()
  1462. })
  1463. const selectedCharts =
  1464. mode === 'pivot' ? getPivotModeSelectedCharts([]) : [getTable()]
  1465. const resetedParams = this.getChartDataConfig(selectedCharts)
  1466. this.setWidgetProps(resetedParams.dataParams, resetedParams.styleParams)
  1467. }
  1468. private dropboxValueChange = (name) => (
  1469. key: string,
  1470. value: string | number
  1471. ) => {
  1472. const { mode, dataParams, styleParams } = this.state
  1473. const { color, size } = dataParams
  1474. switch (name) {
  1475. case 'color':
  1476. if (key === 'all' && mode === 'pivot') {
  1477. Object.keys(color.value).forEach((k) => {
  1478. color.value[k] = value
  1479. })
  1480. } else {
  1481. color.value[key] = value
  1482. }
  1483. break
  1484. case 'size':
  1485. if (key === 'all') {
  1486. Object.keys(size.value).forEach((k) => {
  1487. size.value[k] = value
  1488. })
  1489. } else {
  1490. size.value[key] = value
  1491. }
  1492. }
  1493. this.setWidgetProps(dataParams, styleParams, { renderType: 'refresh' })
  1494. }
  1495. private styleChange = (name) => (prop, value, propPath?: string[]) => {
  1496. const { dataParams, styleParams } = this.state
  1497. if (!propPath || !propPath.length) {
  1498. styleParams[name][prop] = value
  1499. } else {
  1500. propPath.reduce((subStyle, currentPathName, idx) => {
  1501. const childStyle = subStyle[currentPathName]
  1502. if (idx === propPath.length - 1) {
  1503. childStyle[prop] = value
  1504. }
  1505. return childStyle
  1506. }, styleParams[name])
  1507. }
  1508. let renderType: RenderType = 'clear'
  1509. switch (prop) {
  1510. case 'layerType':
  1511. renderType = 'rerender'
  1512. break
  1513. case 'smooth':
  1514. renderType = 'clear'
  1515. break
  1516. }
  1517. this.setWidgetProps(dataParams, styleParams, { renderType })
  1518. // const { layerType } = styleParams.spec
  1519. // chartModeSelectedChart.style.spec.layerType = layerType
  1520. }
  1521. // @FIXME refactor function styleChange2
  1522. private styleChange2 = (value: string | number, propPath: string[]) => {
  1523. const { dataParams, styleParams } = this.state
  1524. set(styleParams, propPath, value)
  1525. let renderType: RenderType = 'clear'
  1526. if (propPath.includes('layerType')) {
  1527. renderType = 'rerender'
  1528. } else if (propPath.includes('smooth')) {
  1529. renderType = 'clear'
  1530. }
  1531. this.setWidgetProps(dataParams, styleParams, { renderType })
  1532. }
  1533. private limitChange = (value) => {
  1534. this.props.onLimitChange(value)
  1535. this.debounceLimitChangeUpdate(value)
  1536. }
  1537. private debounceLimitChangeUpdate = debounce((limit) => {
  1538. const { dataParams, styleParams } = this.state
  1539. this.setWidgetProps(dataParams, styleParams, { limit })
  1540. }, 500)
  1541. private confirmColorModal = (config) => {
  1542. this.state.modalCallback(config)
  1543. this.closeColorModal()
  1544. }
  1545. private cancelColorModal = () => {
  1546. this.state.modalCallback(false)
  1547. this.closeColorModal()
  1548. }
  1549. private closeColorModal = () => {
  1550. this.setState({
  1551. colorModalVisible: false,
  1552. modalCachedData: null,
  1553. modalCallback: null,
  1554. modalDataFrom: ''
  1555. })
  1556. }
  1557. private confirmActOnModal = (config) => {
  1558. this.state.modalCallback(config)
  1559. this.closeActOnModal()
  1560. }
  1561. private cancelActOnModal = () => {
  1562. this.state.modalCallback(false)
  1563. this.closeActOnModal()
  1564. }
  1565. private closeActOnModal = () => {
  1566. this.setState({
  1567. actOnModalVisible: false,
  1568. actOnModalList: null,
  1569. modalCachedData: null,
  1570. modalCallback: null,
  1571. modalDataFrom: ''
  1572. })
  1573. }
  1574. private confirmFilterModal = (config) => {
  1575. this.state.modalCallback(config)
  1576. this.closeFilterModal()
  1577. }
  1578. private cancelFilterModal = () => {
  1579. this.state.modalCallback(false)
  1580. this.closeFilterModal()
  1581. }
  1582. private closeFilterModal = () => {
  1583. this.setState({
  1584. filterModalVisible: false,
  1585. modalCachedData: null,
  1586. modalCallback: null,
  1587. modalDataFrom: ''
  1588. })
  1589. }
  1590. private afterColorModalClose = () => {
  1591. this.colorSettingForm.reset()
  1592. }
  1593. private afterActOnModalClose = () => {
  1594. this.actOnSettingForm.reset()
  1595. }
  1596. private afterFilterModalClose = () => {
  1597. this.filterSettingForm.reset()
  1598. }
  1599. private tabSelect = (key) => () => {
  1600. this.setState({
  1601. selectedTab: key
  1602. })
  1603. }
  1604. private showControlConfig = () => {
  1605. this.setState({
  1606. controlConfigVisible: true
  1607. })
  1608. }
  1609. private closeControlConfig = () => {
  1610. this.setState({
  1611. controlConfigVisible: false
  1612. })
  1613. }
  1614. private showReferenceConfig = () => {
  1615. this.setState({
  1616. referenceConfigVisible: true
  1617. })
  1618. }
  1619. private closeReferenceConfig = () => {
  1620. this.setState({
  1621. referenceConfigVisible: false
  1622. })
  1623. }
  1624. private saveControls = (
  1625. controls: IControl[],
  1626. queryMode: ControlQueryMode
  1627. ) => {
  1628. this.props.onSetControls(controls, queryMode)
  1629. this.closeControlConfig()
  1630. }
  1631. private saveReferences = (references: IReference[]) => {
  1632. this.props.onSetReferences(references)
  1633. this.closeReferenceConfig()
  1634. }
  1635. private checkAllDragItem = (type: DragType) => (e: CheckboxChangeEvent) => {
  1636. const { categoryDragItems, valueDragItems } = this.state
  1637. const checked = e.target.checked
  1638. if (type === 'category') {
  1639. this.setState({
  1640. categoryDragItems: categoryDragItems.map((item) => ({
  1641. ...item,
  1642. checked
  1643. }))
  1644. })
  1645. } else {
  1646. this.setState({
  1647. valueDragItems: valueDragItems.map((item) => ({ ...item, checked }))
  1648. })
  1649. }
  1650. }
  1651. private checkDragItem = (type: DragType, name: string) => (
  1652. e: CheckboxChangeEvent
  1653. ) => {
  1654. const { categoryDragItems, valueDragItems } = this.state
  1655. const checked = e.target.checked
  1656. if (type === 'category') {
  1657. this.setState({
  1658. categoryDragItems: categoryDragItems.map((item) => {
  1659. if (item.name === name) {
  1660. return { ...item, checked }
  1661. } else {
  1662. return item
  1663. }
  1664. })
  1665. })
  1666. } else {
  1667. this.setState({
  1668. valueDragItems: valueDragItems.map((item) => {
  1669. if (item.name === name) {
  1670. return { ...item, checked }
  1671. } else {
  1672. return item
  1673. }
  1674. })
  1675. })
  1676. }
  1677. }
  1678. private coustomFieldSelect = (event) => {
  1679. const { key } = event
  1680. switch (key) {
  1681. case 'computed':
  1682. this.setState({
  1683. computedConfigModalVisible: true
  1684. })
  1685. break
  1686. default:
  1687. break
  1688. }
  1689. }
  1690. private hideComputedConfigModal = () => {
  1691. this.setState({ computedConfigModalVisible: false, selectedComputed: null })
  1692. }
  1693. private saveComputedConfig = (config) => {
  1694. const { onSetComputed } = this.props
  1695. if (config) {
  1696. onSetComputed(config)
  1697. }
  1698. }
  1699. private onShowEditComputed = (tag) => () => {
  1700. this.setState(
  1701. {
  1702. computedConfigModalVisible: true,
  1703. selectedComputed: tag
  1704. },
  1705. () => {
  1706. const { id, name, visualType, sqlExpression } = tag
  1707. this.forceUpdate(() => {
  1708. this.computedConfigForm.props.form.setFieldsValue({
  1709. id,
  1710. name,
  1711. visualType
  1712. })
  1713. })
  1714. }
  1715. )
  1716. }
  1717. private onDeleteComputed = (tag) => () => {
  1718. const { onDeleteComputed } = this.props
  1719. if (onDeleteComputed) {
  1720. onDeleteComputed(tag)
  1721. }
  1722. }
  1723. private bootstrapMorePanel = (tag) => {
  1724. const columnMenu = (
  1725. <Menu>
  1726. <Menu.Item className={styles.menuItem}>
  1727. <span
  1728. className={styles.menuText}
  1729. onClick={this.onShowEditComputed(tag)}
  1730. >
  1731. 字段信息
  1732. </span>
  1733. </Menu.Item>
  1734. <Menu.Item className={styles.menuItem}>
  1735. <Popconfirm
  1736. title={`确定删除 ${tag.name}?`}
  1737. placement="bottom"
  1738. onConfirm={this.onDeleteComputed(tag)}
  1739. >
  1740. <span className={styles.menuText}>删除</span>
  1741. </Popconfirm>
  1742. </Menu.Item>
  1743. </Menu>
  1744. )
  1745. return (
  1746. <span className={styles.more}>
  1747. <Dropdown
  1748. overlay={columnMenu}
  1749. placement="bottomRight"
  1750. trigger={['click']}
  1751. >
  1752. <Icon type="ellipsis" />
  1753. </Dropdown>
  1754. </span>
  1755. )
  1756. }
  1757. public render() {
  1758. const {
  1759. views,
  1760. formedViews,
  1761. selectedViewId,
  1762. controls,
  1763. controlQueryMode,
  1764. references,
  1765. limit,
  1766. cache,
  1767. autoLoadData,
  1768. expired,
  1769. workbenchQueryMode,
  1770. multiDrag,
  1771. computed,
  1772. onCacheChange,
  1773. onChangeAutoLoadData,
  1774. onExpiredChange,
  1775. onLoadColumnDistinctValue,
  1776. onLoadViews,
  1777. onLoadViewDetail,
  1778. originalComputed
  1779. } = this.props
  1780. const {
  1781. dragged,
  1782. showColsAndRows,
  1783. mode,
  1784. currentWidgetlibs,
  1785. chartModeSelectedChart,
  1786. selectedTab,
  1787. dataParams,
  1788. styleParams,
  1789. modalCachedData,
  1790. modalDataFrom,
  1791. distinctColumnValues,
  1792. fieldModalVisible,
  1793. formatModalVisible,
  1794. sortModalVisible,
  1795. currentEditingItem,
  1796. colorModalVisible,
  1797. actOnModalVisible,
  1798. actOnModalList,
  1799. filterModalVisible,
  1800. controlConfigVisible,
  1801. referenceConfigVisible,
  1802. valueDragItems,
  1803. computedConfigModalVisible,
  1804. selectedComputed
  1805. } = this.state
  1806. const selectedView = formedViews[selectedViewId]
  1807. const widgetPropsModel = selectedView?.model || {}
  1808. const { metrics } = dataParams
  1809. const [dimetionsCount, metricsCount] = this.getDimetionsAndMetricsCount()
  1810. const {
  1811. spec,
  1812. xAxis,
  1813. yAxis,
  1814. axis,
  1815. splitLine,
  1816. pivot: pivotConfig,
  1817. label,
  1818. legend,
  1819. visualMap,
  1820. toolbox,
  1821. areaSelect,
  1822. scorecard,
  1823. gauge,
  1824. iframe,
  1825. table,
  1826. bar,
  1827. radar,
  1828. doubleYAxis
  1829. } = styleParams
  1830. let categoryDragItems = this.state.categoryDragItems
  1831. if (
  1832. mode === 'pivot' &&
  1833. valueDragItems.length &&
  1834. dataParams.metrics.items.every((item) => item.chart.id === getPivot().id)
  1835. ) {
  1836. categoryDragItems = categoryDragItems.concat({
  1837. name: '指标名称',
  1838. type: 'category',
  1839. visualType: ViewModelVisualTypes.String,
  1840. checked: false
  1841. })
  1842. }
  1843. const coustomFieldSelectMenu = (
  1844. <Menu onClick={this.coustomFieldSelect}>
  1845. <MenuItem key="computed">计算字段</MenuItem>
  1846. </Menu>
  1847. )
  1848. const dropboxes = Object.entries(dataParams).map(([k, v]) => {
  1849. if (k === 'rows' && !showColsAndRows) {
  1850. return
  1851. }
  1852. if (k === 'cols') {
  1853. v.title = showColsAndRows ? '列' : '维度'
  1854. }
  1855. let panelList = []
  1856. if (k === 'color') {
  1857. panelList = metrics.items
  1858. }
  1859. if (k === 'size') {
  1860. panelList = v.items
  1861. }
  1862. return (
  1863. <Dropbox
  1864. key={k}
  1865. name={k}
  1866. title={v.title}
  1867. type={v.type}
  1868. value={v.value}
  1869. items={v.items}
  1870. mode={mode}
  1871. selectedChartId={chartModeSelectedChart.id}
  1872. dragged={dragged}
  1873. panelList={panelList}
  1874. dimetionsCount={dimetionsCount}
  1875. metricsCount={metricsCount}
  1876. onValueChange={this.dropboxValueChange(k)}
  1877. onItemDragStart={this.insideDragStart(k)}
  1878. onItemDragEnd={this.insideDragEnd}
  1879. onItemRemove={this.removeDropboxItem(k)}
  1880. onItemSort={this.getDropboxItemSortDirection(k)}
  1881. onItemChangeAgg={this.getDropboxItemAggregator(k)}
  1882. onItemChangeFieldConfig={this.dropboxItemChangeFieldConfig(k)}
  1883. onItemChangeFormatConfig={this.dropboxItemChangeFormatConfig(k)}
  1884. onItemChangeColorConfig={this.dropboxItemChangeColorConfig}
  1885. onItemChangeFilterConfig={this.dropboxItemChangeFilterConfig}
  1886. onItemChangeChart={this.getDropboxItemChart}
  1887. beforeDrop={this.beforeDrop}
  1888. onDrop={this.drop}
  1889. />
  1890. )
  1891. })
  1892. const rowsColsToggleClass = classnames({
  1893. [styles.toggleRowsAndCols]: true,
  1894. [utilStyles.hide]: mode === 'chart'
  1895. })
  1896. const rowsColsSwitchClass = classnames({
  1897. [styles.switchRowsAndCols]: true,
  1898. [utilStyles.hide]: !showColsAndRows
  1899. })
  1900. const tabs = this.tabKeys.map(({ key, title }) => {
  1901. const tabClass = classnames({
  1902. [styles.selected]: key === selectedTab
  1903. })
  1904. return (
  1905. <li key={key} className={tabClass} onClick={this.tabSelect(key)}>
  1906. {title}
  1907. </li>
  1908. )
  1909. })
  1910. let queryInfo: string[] = []
  1911. if (selectedView) {
  1912. queryInfo = selectedView.variable.map((v) => v.name)
  1913. }
  1914. let mapLegendLayerType
  1915. let mapLabelLayerType
  1916. if (spec) {
  1917. const { layerType } = spec
  1918. mapLabelLayerType = !(layerType && layerType === 'heatmap')
  1919. mapLegendLayerType = !(
  1920. layerType &&
  1921. (layerType === 'heatmap' ||
  1922. layerType === 'map' ||
  1923. layerType === 'scatter')
  1924. )
  1925. }
  1926. let tabPane
  1927. switch (selectedTab) {
  1928. case 'data':
  1929. tabPane = (
  1930. <div className={`${styles.paramsPane} ${styles.dropPane}`}>
  1931. <div
  1932. className={rowsColsToggleClass}
  1933. onClick={this.toggleRowsAndCols}
  1934. >
  1935. <Icon type="swap" />
  1936. {showColsAndRows ? ' 使用维度' : ' 使用行列'}
  1937. </div>
  1938. <div
  1939. className={rowsColsSwitchClass}
  1940. onClick={this.switchRowsAndCols}
  1941. >
  1942. <Icon type="retweet" /> 行列切换
  1943. </div>
  1944. {dropboxes}
  1945. </div>
  1946. )
  1947. break
  1948. case 'style':
  1949. tabPane = (
  1950. <div className={styles.paramsPane}>
  1951. {spec && (
  1952. <SpecSection
  1953. name={chartModeSelectedChart.name}
  1954. title={chartModeSelectedChart.title}
  1955. config={spec}
  1956. onChange={this.styleChange2}
  1957. isLegendSection={mapLegendLayerType}
  1958. />
  1959. )}
  1960. {bar && (
  1961. <BarSection
  1962. onChange={this.styleChange('bar')}
  1963. config={bar}
  1964. dataParams={dataParams}
  1965. />
  1966. )}
  1967. {radar && (
  1968. <RadarSection config={radar} onChange={this.styleChange2} />
  1969. )}
  1970. {mapLabelLayerType
  1971. ? label && (
  1972. <LabelSection
  1973. title="标签"
  1974. config={label}
  1975. onChange={this.styleChange('label')}
  1976. name={chartModeSelectedChart.name}
  1977. />
  1978. )
  1979. : null}
  1980. {mapLegendLayerType
  1981. ? legend && (
  1982. <LegendSection
  1983. title="图例"
  1984. config={legend}
  1985. onChange={this.styleChange('legend')}
  1986. />
  1987. )
  1988. : null}
  1989. {mapLegendLayerType
  1990. ? null
  1991. : visualMap && (
  1992. <VisualMapSection
  1993. title="视觉映射"
  1994. config={visualMap}
  1995. onChange={this.styleChange('visualMap')}
  1996. />
  1997. )}
  1998. {toolbox && (
  1999. <ToolboxSection
  2000. title="工具"
  2001. config={toolbox}
  2002. onChange={this.styleChange('toolbox')}
  2003. />
  2004. )}
  2005. {doubleYAxis && (
  2006. <DoubleYAxisSection
  2007. title="双Y轴"
  2008. config={doubleYAxis}
  2009. onChange={this.styleChange('doubleYAxis')}
  2010. />
  2011. )}
  2012. {xAxis && (
  2013. <AxisSection
  2014. title="X轴"
  2015. config={xAxis}
  2016. onChange={this.styleChange('xAxis')}
  2017. />
  2018. )}
  2019. {yAxis && (
  2020. <AxisSection
  2021. title="Y轴"
  2022. config={yAxis}
  2023. onChange={this.styleChange('yAxis')}
  2024. />
  2025. )}
  2026. {axis && (
  2027. <AxisSection
  2028. title="轴"
  2029. config={axis}
  2030. onChange={this.styleChange('axis')}
  2031. />
  2032. )}
  2033. {splitLine && (
  2034. <SplitLineSection
  2035. title="分隔线"
  2036. config={splitLine}
  2037. onChange={this.styleChange('splitLine')}
  2038. />
  2039. )}
  2040. {areaSelect && (
  2041. <AreaSelectSection
  2042. title="坐标轴框选"
  2043. config={areaSelect}
  2044. onChange={this.styleChange('areaSelect')}
  2045. />
  2046. )}
  2047. {scorecard && (
  2048. <ScorecardSection
  2049. title="翻牌器"
  2050. config={scorecard}
  2051. onChange={this.styleChange('scorecard')}
  2052. />
  2053. )}
  2054. {gauge && (
  2055. <GaugeSection
  2056. title="仪表盘"
  2057. config={gauge}
  2058. onChange={this.styleChange('gauge')}
  2059. />
  2060. )}
  2061. {iframe && (
  2062. <IframeSection
  2063. title="内嵌网页"
  2064. config={iframe}
  2065. onChange={this.styleChange('iframe')}
  2066. />
  2067. )}
  2068. {table && (
  2069. <TableSection
  2070. dataParams={dataParams}
  2071. config={table}
  2072. onChange={this.styleChange('table')}
  2073. />
  2074. )}
  2075. {pivotConfig && (
  2076. <PivotSection
  2077. title="透视表"
  2078. config={pivotConfig}
  2079. onChange={this.styleChange('pivot')}
  2080. />
  2081. )}
  2082. </div>
  2083. )
  2084. break
  2085. case 'setting':
  2086. tabPane = (
  2087. <div className={styles.paramsPane}>
  2088. <div className={styles.paneBlock}>
  2089. <h4>
  2090. <span>控制器</span>
  2091. <span
  2092. className={styles.addVariable}
  2093. onClick={this.showControlConfig}
  2094. >
  2095. <Icon type="edit" /> 点击配置
  2096. </span>
  2097. </h4>
  2098. </div>
  2099. {mode === 'chart' &&
  2100. REFERENCE_SUPPORTED_CHART_TYPES.includes(
  2101. chartModeSelectedChart.id
  2102. ) && (
  2103. <div className={styles.paneBlock}>
  2104. <h4>
  2105. <span>参考线</span>
  2106. <span
  2107. className={styles.addVariable}
  2108. onClick={this.showReferenceConfig}
  2109. >
  2110. <Icon type="edit" /> 点击配置
  2111. </span>
  2112. </h4>
  2113. </div>
  2114. )}
  2115. <div className={styles.paneBlock}>
  2116. <h4>展示数据量</h4>
  2117. <div className={styles.blockBody}>
  2118. <Row
  2119. gutter={8}
  2120. type="flex"
  2121. align="middle"
  2122. className={styles.blockRow}
  2123. >
  2124. <Col span={24}>
  2125. <InputNumber
  2126. min={0}
  2127. value={limit}
  2128. onChange={this.limitChange}
  2129. />
  2130. </Col>
  2131. </Row>
  2132. </div>
  2133. </div>
  2134. <div className={styles.paneBlock}>
  2135. <h4>开启缓存</h4>
  2136. <div className={styles.blockBody}>
  2137. <Row
  2138. gutter={8}
  2139. type="flex"
  2140. align="middle"
  2141. className={styles.blockRow}
  2142. >
  2143. <Col span={24}>
  2144. <RadioGroup
  2145. size="small"
  2146. value={cache}
  2147. onChange={onCacheChange}
  2148. >
  2149. <RadioButton value={false}>关闭</RadioButton>
  2150. <RadioButton value={true}>开启</RadioButton>
  2151. </RadioGroup>
  2152. </Col>
  2153. </Row>
  2154. </div>
  2155. </div>
  2156. <div className={styles.paneBlock}>
  2157. <h4>缓存有效期(秒)</h4>
  2158. <div className={styles.blockBody}>
  2159. <Row
  2160. gutter={8}
  2161. type="flex"
  2162. align="middle"
  2163. className={styles.blockRow}
  2164. >
  2165. <Col span={24}>
  2166. <InputNumber
  2167. value={expired}
  2168. disabled={!cache}
  2169. onChange={onExpiredChange}
  2170. />
  2171. </Col>
  2172. </Row>
  2173. </div>
  2174. </div>
  2175. <div className={styles.paneBlock}>
  2176. <h4>自动加载数据</h4>
  2177. <div className={styles.blockBody}>
  2178. <Row
  2179. gutter={8}
  2180. type="flex"
  2181. align="middle"
  2182. className={styles.blockRow}
  2183. >
  2184. <Col span={24}>
  2185. <RadioGroup
  2186. size="small"
  2187. value={autoLoadData}
  2188. onChange={onChangeAutoLoadData}
  2189. >
  2190. <RadioButton value={true}>是</RadioButton>
  2191. <RadioButton value={false}>否</RadioButton>
  2192. </RadioGroup>
  2193. </Col>
  2194. </Row>
  2195. </div>
  2196. </div>
  2197. </div>
  2198. )
  2199. break
  2200. }
  2201. let colorSettingConfig
  2202. let actOnSettingConfig
  2203. let filterSettingConfig
  2204. if (modalCachedData) {
  2205. const selectedItem = dataParams[modalDataFrom].items.find(
  2206. (i) => i.name === modalCachedData.name
  2207. )
  2208. switch (modalDataFrom) {
  2209. case 'color':
  2210. colorSettingConfig = selectedItem ? selectedItem.config : {}
  2211. break
  2212. case 'filters':
  2213. filterSettingConfig = selectedItem ? selectedItem.config : {}
  2214. break
  2215. default:
  2216. actOnSettingConfig = selectedItem ? selectedItem.config : {}
  2217. break
  2218. }
  2219. }
  2220. const selectedCharts =
  2221. mode === 'pivot'
  2222. ? getPivotModeSelectedCharts(metrics.items)
  2223. : [chartModeSelectedChart]
  2224. const computedAddFrom = computed.map((c) => ({ ...c, from: 'computed' }))
  2225. const originalWidgetPropsAddFrom = originalComputed
  2226. ? originalComputed.map((c) => ({ ...c, from: 'originalComputed' }))
  2227. : []
  2228. const combineComputedFields = originalComputed
  2229. ? [...computedAddFrom, ...originalWidgetPropsAddFrom]
  2230. : [...computedAddFrom]
  2231. // combineComputedFields.forEach((compute) => {
  2232. // if (compute.visualType === 'number') {
  2233. // values.push(compute)
  2234. // } else if (compute.visualType === 'string') {
  2235. // categories.push(compute)
  2236. // }
  2237. // })
  2238. return (
  2239. <div className={styles.operatingPanel}>
  2240. <div className={styles.model}>
  2241. <div className={styles.viewSelect}>
  2242. <Select
  2243. size="small"
  2244. placeholder="选择一个数据资产"
  2245. showSearch
  2246. dropdownMatchSelectWidth={false}
  2247. value={selectedView && selectedView.id}
  2248. onChange={this.viewSelect}
  2249. filterOption={filterSelectOption}
  2250. >
  2251. {(views || []).map(({ id, name }) => (
  2252. <Option key={id} value={id}>
  2253. {name}
  2254. </Option>
  2255. ))}
  2256. </Select>
  2257. {/* <Dropdown overlay={coustomFieldSelectMenu} trigger={['click']} placement="bottomRight">
  2258. <Icon type="plus" />
  2259. </Dropdown> */}
  2260. </div>
  2261. <div className={styles.columnContainer}>
  2262. <div className={styles.title}>
  2263. <h4>分类型</h4>
  2264. {multiDrag && (
  2265. <Checkbox
  2266. checked={
  2267. categoryDragItems.length &&
  2268. categoryDragItems.every((item) => item.checked)
  2269. }
  2270. onChange={this.checkAllDragItem('category')}
  2271. />
  2272. )}
  2273. </div>
  2274. <ul className={`${styles.columnList} ${styles.categories}`}>
  2275. {categoryDragItems.map((item) => {
  2276. const { name, title, visualType, checked, ...rest } = item
  2277. const data = { name, title, visualType, ...rest }
  2278. return (
  2279. <li
  2280. className={`${
  2281. title === 'computedField' ? styles.computed : ''
  2282. }`}
  2283. key={name}
  2284. onDragStart={this.dragStart(data)}
  2285. onDragEnd={this.dragEnd}
  2286. draggable
  2287. >
  2288. <i
  2289. className={`iconfont ${this.getDragItemIconClass(
  2290. visualType
  2291. )}`}
  2292. />
  2293. <p>{name}</p>
  2294. {title === 'computedField'
  2295. ? this.bootstrapMorePanel(data)
  2296. : null}
  2297. {multiDrag && (
  2298. <Checkbox
  2299. checked={checked}
  2300. onChange={this.checkDragItem('category', name)}
  2301. />
  2302. )}
  2303. </li>
  2304. )
  2305. })}
  2306. </ul>
  2307. </div>
  2308. <div className={styles.columnContainer}>
  2309. <div className={styles.title}>
  2310. <h4>数值型</h4>
  2311. {multiDrag && (
  2312. <Checkbox
  2313. checked={
  2314. valueDragItems.length &&
  2315. valueDragItems.every((item) => item.checked)
  2316. }
  2317. onChange={this.checkAllDragItem('value')}
  2318. />
  2319. )}
  2320. </div>
  2321. <ul className={`${styles.columnList} ${styles.values}`}>
  2322. {valueDragItems.map((item) => {
  2323. const { name, title, visualType, checked, ...rest } = item
  2324. const data = { name, title, visualType, ...rest }
  2325. return (
  2326. <li
  2327. className={`${
  2328. title === 'computedField' ? styles.computed : ''
  2329. }`}
  2330. key={name}
  2331. onDragStart={this.dragStart({
  2332. ...data,
  2333. name: encodeMetricName(name),
  2334. agg: 'sum'
  2335. })}
  2336. onDragEnd={this.dragEnd}
  2337. draggable
  2338. >
  2339. <i
  2340. className={`iconfont ${this.getDragItemIconClass(
  2341. visualType
  2342. )}`}
  2343. />
  2344. <p>{name}</p>
  2345. {title === 'computedField'
  2346. ? this.bootstrapMorePanel(data)
  2347. : null}
  2348. {multiDrag && (
  2349. <Checkbox
  2350. checked={checked}
  2351. onChange={this.checkDragItem('value', name)}
  2352. />
  2353. )}
  2354. </li>
  2355. )
  2356. })}
  2357. </ul>
  2358. </div>
  2359. </div>
  2360. <div className={styles.config}>
  2361. <div className={styles.mode}>
  2362. <RadioGroup
  2363. size="small"
  2364. className={styles.radio}
  2365. value={mode}
  2366. onChange={this.changeMode}
  2367. >
  2368. <RadioButton
  2369. className={classnames({
  2370. [styles.selected]: mode !== 'pivot'
  2371. })}
  2372. value="pivot"
  2373. >
  2374. 透视驱动
  2375. </RadioButton>
  2376. <RadioButton
  2377. className={classnames({
  2378. [styles.selected]: mode !== 'chart'
  2379. })}
  2380. value="chart"
  2381. >
  2382. 图表驱动
  2383. </RadioButton>
  2384. </RadioGroup>
  2385. </div>
  2386. <div className={styles.charts}>
  2387. {currentWidgetlibs.map((c) => (
  2388. <ChartIndicator
  2389. key={c.id}
  2390. chartInfo={c}
  2391. dimetionsCount={dimetionsCount}
  2392. metricsCount={metricsCount}
  2393. selectedCharts={selectedCharts}
  2394. onSelect={this.chartSelect}
  2395. />
  2396. ))}
  2397. </div>
  2398. {workbenchQueryMode === WorkbenchQueryMode.Manually && (
  2399. <div
  2400. className={styles.manualQuery}
  2401. onClick={this.forceSetWidgetProps}
  2402. >
  2403. <Icon type="caret-right" />
  2404. 查询
  2405. </div>
  2406. )}
  2407. <div className={styles.params}>
  2408. <ul className={styles.paramsTab}>{tabs}</ul>
  2409. {tabPane}
  2410. </div>
  2411. </div>
  2412. <Modal
  2413. wrapClassName="ant-modal-small"
  2414. visible={colorModalVisible}
  2415. onCancel={this.cancelColorModal}
  2416. afterClose={this.afterColorModalClose}
  2417. footer={null}
  2418. >
  2419. <ColorSettingForm
  2420. mode={mode}
  2421. list={distinctColumnValues}
  2422. metrics={metrics.items}
  2423. config={colorSettingConfig}
  2424. onSave={this.confirmColorModal}
  2425. onCancel={this.cancelColorModal}
  2426. ref={(f) => (this.colorSettingForm = f)}
  2427. />
  2428. </Modal>
  2429. <Modal
  2430. title="作用于"
  2431. wrapClassName="ant-modal-small"
  2432. visible={actOnModalVisible}
  2433. onCancel={this.cancelActOnModal}
  2434. afterClose={this.afterActOnModalClose}
  2435. footer={null}
  2436. >
  2437. <ActOnSettingForm
  2438. list={actOnModalList}
  2439. config={actOnSettingConfig}
  2440. onSave={this.confirmActOnModal}
  2441. onCancel={this.cancelActOnModal}
  2442. ref={(f) => (this.actOnSettingForm = f)}
  2443. />
  2444. </Modal>
  2445. <Modal
  2446. title="筛选配置"
  2447. visible={filterModalVisible}
  2448. onCancel={this.cancelFilterModal}
  2449. afterClose={this.afterFilterModalClose}
  2450. footer={null}
  2451. >
  2452. <FilterSettingForm
  2453. item={modalCachedData}
  2454. model={widgetPropsModel}
  2455. list={distinctColumnValues}
  2456. config={filterSettingConfig}
  2457. onSave={this.confirmFilterModal}
  2458. onCancel={this.cancelFilterModal}
  2459. ref={(f) => (this.filterSettingForm = f)}
  2460. />
  2461. </Modal>
  2462. <LocalControlConfig
  2463. type={ControlPanelTypes.Local}
  2464. originalControls={controls}
  2465. relatedViewId={selectedViewId}
  2466. views={views}
  2467. formedViews={formedViews}
  2468. visible={controlConfigVisible}
  2469. queryMode={controlQueryMode}
  2470. onSave={this.saveControls}
  2471. onCancel={this.closeControlConfig}
  2472. onLoadViews={onLoadViews}
  2473. onLoadViewDetail={onLoadViewDetail}
  2474. onGetOptions={onLoadColumnDistinctValue}
  2475. />
  2476. <ReferenceConfigModal
  2477. references={references}
  2478. metrics={metrics.items}
  2479. visible={referenceConfigVisible}
  2480. onSave={this.saveReferences}
  2481. onCancel={this.closeReferenceConfig}
  2482. />
  2483. {!currentEditingItem
  2484. ? null
  2485. : [
  2486. <FieldConfigModal
  2487. key="fieldConfigModal"
  2488. queryInfo={queryInfo}
  2489. visible={fieldModalVisible}
  2490. fieldConfig={currentEditingItem.field}
  2491. onSave={this.saveFieldConfig}
  2492. onCancel={this.cancelFieldConfig}
  2493. />,
  2494. <FormatConfigModal
  2495. key="formatConfigModal"
  2496. visible={formatModalVisible}
  2497. visualType={currentEditingItem.visualType}
  2498. formatConfig={currentEditingItem.format}
  2499. onSave={this.saveFormatConfig}
  2500. onCancel={this.cancelFormatConfig}
  2501. />,
  2502. <SortConfigModal
  2503. key="sortConfigModal"
  2504. visible={sortModalVisible}
  2505. config={currentEditingItem.sort}
  2506. list={distinctColumnValues}
  2507. onSave={this.saveSortConfig}
  2508. onCancel={this.cancelSortConfig}
  2509. />
  2510. ]}
  2511. <Modal
  2512. title="计算字段配置"
  2513. wrapClassName="ant-modal-large"
  2514. visible={computedConfigModalVisible}
  2515. onCancel={this.hideComputedConfigModal}
  2516. closable={false}
  2517. footer={false}
  2518. maskClosable={false}
  2519. >
  2520. <ComputedConfigForm
  2521. queryInfo={queryInfo}
  2522. categories={categoryDragItems}
  2523. onSave={this.saveComputedConfig}
  2524. onClose={this.hideComputedConfigModal}
  2525. selectedComputed={selectedComputed}
  2526. wrappedComponentRef={this.refHandlers.computedConfigForm}
  2527. />
  2528. </Modal>
  2529. </div>
  2530. )
  2531. }
  2532. }
  2533. export default OperatingPanel