PenDatas.vue 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610
  1. <template>
  2. <div class="props">
  3. <t-space direction="vertical" class="py-16 w-full">
  4. <div class="form-item px-16">
  5. <label style="width: 50px">ID</label>
  6. <t-input
  7. class="w-full"
  8. placeholder="ID"
  9. :value="props.pen.id"
  10. @change="changeID($event)"
  11. />
  12. </div>
  13. </t-space>
  14. <t-collapse
  15. :defaultValue="['1', '2', '3','4', '7']"
  16. expandIconPlacement="right"
  17. :borderless="true"
  18. >
  19. <t-collapse-panel v-if="props.pen.formId" value="7" header="表单">
  20. <!-- 表单子图元 -->
  21. <div class="form-item px-12">
  22. <label title="所属表单">所属表单 </label>
  23. <div class="ml-8" :title="leaderPen?.id">{{ leaderPen?.description||leaderPen?.name }}</div>
  24. </div>
  25. <div class="form-item px-12">
  26. <label>表单key </label>
  27. <t-input
  28. class="w-full"
  29. v-model="props.pen.formKey"
  30. @change="changeValue('formKey')"
  31. />
  32. </div>
  33. <div class="form-item px-12">
  34. <label>表单value </label>
  35. <t-input
  36. class="w-full"
  37. v-model="props.pen.formValue"
  38. @change="changeValue('formValue')"
  39. />
  40. </div>
  41. <div class="form-item px-12">
  42. <label>表单类型 </label>
  43. <t-select
  44. class="w-full"
  45. v-model="props.pen.formType"
  46. placeholder="选择变量"
  47. >
  48. <t-option key="submit" value="submit" label="提交"/>
  49. <t-option key="reset" value="reset" label="重置"/>
  50. </t-select>
  51. </div>
  52. </t-collapse-panel>
  53. <!-- <t-collapse-panel value="1" header="通信">
  54. <t-space direction="vertical" size="small" class="w-full">
  55. <div class="form-item">
  56. <label title="http地址">http地址 </label>
  57. <t-input
  58. class="w-full"
  59. v-model="props.pen.apiUrl"
  60. @change="changeValue('apiUrl')"
  61. />
  62. </div>
  63. <div class="form-item">
  64. <label title="请求方式">请求方式</label>
  65. <t-select
  66. v-model="props.pen.apiMethod"
  67. @change="changeValue('apiMethod')"
  68. >
  69. <t-option key="GET" value="GET" label="GET" />
  70. <t-option key="POST" value="POST" label="POST" />
  71. </t-select>
  72. </div>
  73. <div class="form-item">
  74. <label title="请求头">请求头</label>
  75. <t-button
  76. class="ml-8"
  77. shape="square"
  78. variant="outline"
  79. style="width: 24px"
  80. @click="preShowPropsEdit('apiHeaders')"
  81. >
  82. <ellipsis-icon slot="icon"/>
  83. </t-button>
  84. </div>
  85. <div class="form-item" v-if="props.pen.apiMethod === 'POST'">
  86. <label title="请求体">请求体</label>
  87. <t-button
  88. class="ml-8"
  89. shape="square"
  90. variant="outline"
  91. style="width: 24px"
  92. @click="preShowPropsEdit('apiBody')"
  93. >
  94. <ellipsis-icon slot="icon"/>
  95. </t-button>
  96. </div>
  97. <div class="form-item">
  98. <label title="开启轮询">开启轮询</label>
  99. <t-switch
  100. class="mt-8 ml-8"
  101. v-model="props.pen.apiEnable"
  102. size="small"
  103. @change="changeValue('apiEnable')"
  104. />
  105. </div>
  106. </t-space>
  107. </t-collapse-panel> -->
  108. <!-- <t-collapse-panel v-if="props.pen.props.custom" value="2" header="属性">
  109. <t-space direction="vertical" size="small" class="w-full">
  110. <div v-for="item in props.pen.props.custom" class="form-item">
  111. <label :title="item.label">{{ item.label }}</label>
  112. <t-checkbox
  113. class="ml-8"
  114. v-if="item.type === 'bool'"
  115. v-model="props.pen[item.key]"
  116. @change="changeValue(item.key)"
  117. />
  118. <t-input-number
  119. class="w-full"
  120. v-else-if="item.type === 'number'"
  121. v-model.number="props.pen[item.key]"
  122. theme="column"
  123. :max="item.max"
  124. :min="item.min"
  125. @change="changeValue(item.key)"
  126. :placeholder="item.placeholder"
  127. :allowInputOverLimit="false"
  128. :decimalPlaces="2"
  129. />
  130. <t-color-picker
  131. class="w-full"
  132. v-else-if="item.type === 'color'"
  133. :enable-alpha="true"
  134. :recent-colors="null"
  135. format="CSS"
  136. :swatch-colors="defaultPureColor"
  137. :color-modes="['monochrome']"
  138. :show-primary-color-preview="false"
  139. v-model="props.pen[item.key]"
  140. @change="changeValue(item.key)"
  141. :placeholder="item.placeholder"
  142. />
  143. <t-select
  144. class="w-full"
  145. v-else-if="item.type === 'select'"
  146. size="small"
  147. :options="item.options"
  148. v-model="props.pen[item.key]"
  149. @change="changeValue(item.key)"
  150. :placeholder="item.placeholder"
  151. />
  152. <t-button
  153. class="ml-8"
  154. v-else-if="item.type === 'code'"
  155. shape="square"
  156. variant="outline"
  157. style="width: 24px"
  158. @click="showDrawer(item)"
  159. >
  160. <ChevronLeftDoubleIcon slot="icon"/>
  161. </t-button>
  162. <t-slider
  163. v-else-if="item.type === 'slider'"
  164. v-model="props.pen[item.key]"
  165. :min="0"
  166. :max="1"
  167. :step="0.01"
  168. @change="changeValue(item.key)"
  169. />
  170. <t-switch
  171. size="small"
  172. class="mt-8 ml-8"
  173. v-else-if="item.type === 'switch'"
  174. v-model="props.pen[item.key]"
  175. @change="changeValue(item.key)"
  176. />
  177. <t-input
  178. class="w-full"
  179. v-else
  180. v-model="props.pen[item.key]"
  181. @change="changeValue(item.key)"
  182. :placeholder="item.placeholder"
  183. />
  184. </div>
  185. </t-space>
  186. </t-collapse-panel> -->
  187. <t-collapse-panel value="4" header="设置" v-if="props.pen.name==='tablePlus'">
  188. <div class="form-item py-8">
  189. <label class="label">数据源</label>
  190. <t-select
  191. v-model="dataSource"
  192. placeholder="请选择"
  193. @change="getDataSource"
  194. >
  195. <t-option key="api" value="api" label="API接口"/>
  196. <t-option key="csv" value="csv" label="CSV文件"/>
  197. <t-option key="excel" value="excel" label="从Excel导入"/>
  198. <t-option key="sql" value="sql" label="SQL数据库"/>
  199. </t-select>
  200. </div>
  201. <div class="t-space-item" v-if="props.pen.name==='tablePlus'">
  202. <div v-if="cell.row!==undefined" class="flex between form-item py-12">
  203. <div v-if="cell.col!==undefined" style="line-height: 30px">
  204. {{$t( '当前选中单元格' )}} ({{$t( '第' )}}{{ props.pen.colHeaders? cell.row:cell.row+1 }}{{$t( '行' )}},{{$t( '第' )}}{{ cell.col}}{{$t( '列' )}})
  205. </div>
  206. <div v-else style="line-height: 30px">
  207. {{$t( '当前选中行' )}} ({{$t( '第' )}}{{ props.pen.colHeaders? cell.row:cell.row+1 }}{{$t( '行' )}})
  208. </div>
  209. <div>
  210. <t-tooltip placement="top" :content="$t('快捷绑定')">
  211. <link-icon style="width: 13px;margin-right: 26px" class="hover ml-4" @click="onQuickBind()"></link-icon>
  212. </t-tooltip>
  213. </div>
  214. </div>
  215. <div class="form-item py-8" v-if="props.pen.dbid">
  216. <label>自动保存
  217. <t-tooltip content="编辑表格后会自动保存到sql数据库" placement="top">
  218. <HelpCircleIcon style="font-size: 12px" class="ml-2"/>
  219. </t-tooltip>
  220. </label>
  221. <t-switch size="small" style="margin:8px 8px" v-model="props.pen.autoSave" />
  222. </div>
  223. <div class="form-item py-8">
  224. <label>可编辑
  225. <t-tooltip content="表格可以在运行时被编辑" placement="top">
  226. <HelpCircleIcon style="font-size: 12px" class="ml-2"/>
  227. </t-tooltip>
  228. </label>
  229. <t-switch size="small" style="margin:8px 8px" v-model="props.pen.input" />
  230. </div>
  231. <div class="form-item py-8" v-if="props.pen.columns?.length">
  232. <label>不可编辑列</label>
  233. <t-select
  234. @change="changeColumns"
  235. v-model="props.pen.notEdit"
  236. :options="props.pen.columns.map(item=>{return {key:item.colKey,value:item.text}})"
  237. multiple
  238. />
  239. </div>
  240. </div>
  241. </t-collapse-panel>
  242. <t-collapse-panel value="3" header="数据">
  243. <!-- <div class="t-space-item">
  244. <div class="form-item py-12">
  245. <label style="width: 76px">{{$t('关联设备')}}</label>
  246. <t-input class="w-full" v-model="props.pen.deviceId" @change="changeValue('deviceId')" :placeholder="$t('设备ID')"></t-input>
  247. </div>
  248. </div> -->
  249. <template v-if="props.pen.name==='tablePlus'">
  250. <div class="t-space-item">
  251. <div v-if="cell.row!==undefined" class="flex between form-item py-12">
  252. <div v-if="cell.col!==undefined" style="line-height: 30px">
  253. 选中单元格 (第{{ cell.row }}行,第{{ cell.col}}列)
  254. </div>
  255. <div v-else style="line-height: 30px">
  256. 选中行 (第{{ cell.row }}行)
  257. </div>
  258. <div>
  259. <t-tooltip content="快捷绑定" placement="top">
  260. <link-icon style="width: 13px;margin-right: 26px"class="hover ml-4" @click="onQuickBind()"/>
  261. </t-tooltip>
  262. </div>
  263. </div>
  264. </div>
  265. </template>
  266. <div
  267. class="real-times"
  268. v-if="props.pen.realTimes && props.pen.realTimes.length||pen.children?.length"
  269. >
  270. <div class="grid head">
  271. <div class="title">{{$t('数据名')}}</div>
  272. <div class="title">{{$t('值')}}</div>
  273. <!-- <div class="title" style="text-align: center;">{{$t('绑定')}}</div> -->
  274. <div class="title" style="text-align: center;">{{$t('操作')}}</div>
  275. <!-- <div class="actions">
  276. <more-icon />
  277. </div> -->
  278. </div>
  279. <div class="grid" v-for="(item, i) in props.pen.realTimes">
  280. <t-tooltip :content="item.key" placement="top">
  281. <label class="label">{{ item.label }}</label>
  282. </t-tooltip>
  283. <div class="value">
  284. <t-input
  285. v-if="item.type === 'integer'"
  286. v-model.number="props.pen[item.key]"
  287. :placeholder="$t('整数')"
  288. @change="changeValue(item.key)"
  289. />
  290. <t-input-number
  291. v-else-if="item.type === 'float'"
  292. v-model="props.pen[item.key]"
  293. :placeholder="$t('浮点数')"
  294. theme="normal"
  295. @change="changeValue(item.key)"
  296. />
  297. <t-switch
  298. v-else-if="item.type === 'bool'"
  299. v-model="props.pen[item.key]"
  300. class="ml-8"
  301. size="small"
  302. @change="changeValue(item.key)"
  303. />
  304. <t-button
  305. v-else-if="item.type === 'array' || item.type === 'object'"
  306. variant="outline"
  307. style="padding: 0px 2px 0 4px; margin: 0 4px"
  308. @click="editObject(item)"
  309. >
  310. <ellipsis-icon />
  311. <!-- <t-icon name="ellipsis" /> -->
  312. </t-button>
  313. <t-input v-else v-model="props.pen[item.key]" @change="changeValue(item.key)" :placeholder="$t('字符串')"></t-input>
  314. </div>
  315. <div class="flex">
  316. <div class="mr-8" style="text-align: center;">
  317. <t-tooltip :content="getBindsDesc(item)" placement="top">
  318. <link-icon class="hover ml-4"
  319. :class="{ primary: item.enableMock || item.bind?.id }"
  320. @click="onBind(item)"/>
  321. </t-tooltip>
  322. </div>
  323. <div class="mr-8" @click="gotoTrigger(item.trigger)" style="text-align: center;">
  324. <t-tooltip :content="item.trigger!==undefined?'配置了状态':''" placement="top">
  325. <ErrorCircleIcon class="hover" :class="{ primary: item.trigger!==undefined }" />
  326. </t-tooltip>
  327. </div>
  328. <div style="text-align: center;">
  329. <!-- <t-tooltip :content="item.triggers?.length || '状态'">
  330. <t-badge
  331. :count="item.triggers?.length"
  332. size="small"
  333. dot
  334. :offset="[0, 5]"
  335. >
  336. <relativity-icon class="hover"
  337. @click="onTrigger(item)"/>
  338. </t-badge>
  339. </t-tooltip> -->
  340. <t-dropdown
  341. @click="addTrigger(item, $event, i)"
  342. :minColumnWidth="120"
  343. >
  344. <!-- <ViewListIcon class="hover"/> -->
  345. <MoreIcon class="hover"/>
  346. <template #dropdown>
  347. <t-dropdown-menu >
  348. <t-dropdown-item v-for="(option) in triggerOptions" :value="option.value">
  349. {{ option.content }}
  350. <t-divider v-if="option.divider"></t-divider>
  351. <check-icon class="ml-16" v-show="option.value === 'mock' && item.enableMock" />
  352. </t-dropdown-item>
  353. </t-dropdown-menu>
  354. </template>
  355. </t-dropdown>
  356. </div>
  357. </div>
  358. <!-- <div class="actions">
  359. <t-dropdown
  360. :options="moreOptions"
  361. @click="onMenuMore($event, item, i)"
  362. :minColumnWidth="80"
  363. >
  364. <more-icon class="more hover" />
  365. </t-dropdown>
  366. </div> -->
  367. </div>
  368. <div class="mt-8 pb-16">
  369. <t-dropdown
  370. :options="options"
  371. @click="addRealTime"
  372. :minColumnWidth="150"
  373. >
  374. <a class="ml-12">
  375. <add-rectangle-icon />
  376. <!-- <t-icon name="add-rectangle" /> -->
  377. {{$t('添加动态数据')}}
  378. </a>
  379. </t-dropdown>
  380. </div>
  381. </div>
  382. <div class="flex column center blank" v-else>
  383. <img src="/img/blank.png">
  384. <div class="gray center">{{$t('还没有动态数据')}}</div>
  385. <div class="mt-8">
  386. <t-dropdown
  387. :options="options"
  388. @click="addRealTime"
  389. :minColumnWidth="150"
  390. >
  391. <t-button style="height: 30px"> 添加动态数据 </t-button>
  392. </t-dropdown>
  393. </div>
  394. </div>
  395. <div v-if="props.pen.children?.length" class="c-titile">
  396. <t-tooltip :content="$t('ctrl+shift+点击,可选中子图元')">
  397. {{$t('图元结构')}}
  398. </t-tooltip>
  399. </div>
  400. <template v-for="(cPen) in childrenPens">
  401. <div
  402. class="real-times"
  403. >
  404. <div class="flex mt-8 mb-8 between">
  405. <div> {{ cPen.description||cPen.name }}</div>
  406. <t-dropdown
  407. :options="options"
  408. @click="addCRealTime($event,cPen)"
  409. :minColumnWidth="150"
  410. >
  411. <a class="ml-12">
  412. <add-rectangle-icon></add-rectangle-icon>
  413. {{$t('添加动态数据')}}
  414. </a>
  415. </t-dropdown>
  416. </div>
  417. <div class="grid" v-for="(item, i) in cPen.realTimes">
  418. <t-tooltip :content="item.key" placement="top">
  419. <label class="label">{{ item.label }}</label>
  420. </t-tooltip>
  421. <div class="value">
  422. <t-input
  423. v-if="item.type === 'integer'"
  424. v-model.number="cPen[item.key]"
  425. :placeholder="$t('整数')"
  426. @change="changeCValue(item.key,cPen)"
  427. />
  428. <t-input-number
  429. v-else-if="item.type === 'float'"
  430. v-model="cPen[item.key]"
  431. :placeholder="$t('浮点数')"
  432. theme="normal"
  433. @change="changeCValue(item.key,cPen)"
  434. />
  435. <t-switch
  436. v-else-if="item.type === 'bool'"
  437. v-model="cPen[item.key]"
  438. class="ml-8"
  439. size="small"
  440. @change="changeCValue(item.key,cPen)"
  441. />
  442. <t-button
  443. v-else-if="item.type === 'array' || item.type === 'object'"
  444. variant="outline"
  445. style="padding: 0px 2px 0 4px; margin: 0 4px"
  446. @click="editCObject(item,cPen)"
  447. >
  448. <ellipsis-icon />
  449. </t-button>
  450. <t-input
  451. v-else
  452. v-model="cPen[item.key]"
  453. :placeholder="$t('字符串')"
  454. @change="changeCValue(item.key,cPen)"
  455. />
  456. </div>
  457. <div style="text-align: center;">
  458. <t-tooltip :content="getBindsDesc(item)" placement="top">
  459. <link-icon class="hover ml-4"
  460. :class="{ primary: item.enableMock || item.bind?.id }"
  461. @click="onBind(item)"/>
  462. </t-tooltip>
  463. </div>
  464. <!-- <div>
  465. <t-tooltip :content="item.triggers?.length || '状态'">
  466. <t-badge
  467. :count="item.triggers?.length"
  468. size="small"
  469. dot
  470. :offset="[0, 5]"
  471. >
  472. <relativity-icon class="hover"
  473. @click="onTrigger(item)"/>
  474. </t-badge>
  475. </t-tooltip>
  476. </div> -->
  477. <div class="actions" style="text-align: center;">
  478. <t-dropdown
  479. :options="moreOptions"
  480. @click="onCMenuMore($event, item, i, cPen)"
  481. :minColumnWidth="80"
  482. >
  483. <ViewListIcon class="hover"/>
  484. </t-dropdown>
  485. </div>
  486. </div>
  487. </div>
  488. </template>
  489. </t-collapse-panel>
  490. </t-collapse>
  491. </div>
  492. <!-- <t-drawer
  493. v-model:visible="drawer.visible"
  494. :header="drawer.header"
  495. cancelBtn="关闭"
  496. confirmBtn="运行"
  497. @confirm="onConfirmDrawer"
  498. :closeOnOverlayClick="false"
  499. :sizeDraggable="true"
  500. >
  501. <div style="height:100%">
  502. <CodeEditor
  503. style="height: 100%"
  504. :key="drawer.randomkey"
  505. :json="true"
  506. :language="'json'"
  507. v-model="drawer.value"
  508. />
  509. </div>
  510. <div class="gray" style="font-size: 12px">
  511. {{ drawer.placeholder }}
  512. </div>
  513. </t-drawer> -->
  514. <t-dialog
  515. v-if="addDataDialog.show"
  516. :visible="true"
  517. class="data-dialog"
  518. :header="addDataDialog.header"
  519. @close="addDataDialog.show = false"
  520. @confirm="onConfirmData"
  521. >
  522. <div class="form-item mt-16">
  523. <label>{{$t('数据名')}}</label>
  524. <t-input v-model="addDataDialog.data.label" :disabled="!!addDataDialog.data.keywords" @blur="onChangeLabel" :placeholder="$t('简短描述')"></t-input>
  525. </div>
  526. <div class="form-item mt-16">
  527. <label>{{$t('属性名')}}</label>
  528. <t-input v-model="addDataDialog.data.key" @blur="onKeyBlur" :disabled="!!addDataDialog.data.keywords" :placeholder="$t('关键字')"></t-input>
  529. </div>
  530. <div class="form-item mt-16">
  531. <label>{{$t('类型')}}</label>
  532. <t-select class="w-full" :options="typeOptions" v-model="addDataDialog.data.type" :disabled="!!addDataDialog.data.keywords" @change="onKeyBlur" :placeholder="$t('字符串')"></t-select>
  533. </div>
  534. </t-dialog>
  535. <t-dialog
  536. v-if="dataBindDialog.show"
  537. :visible="true"
  538. class="data-link-dialog"
  539. :header="$t('动态数据设置')"
  540. @close="
  541. dataBindDialog.datasetList = undefined;
  542. dataBindDialog.show = false;
  543. "
  544. @confirm="dataBindonConfirm"
  545. :top="70"
  546. :width="700"
  547. >
  548. <div class="form-item" style="height: 32px;">
  549. <label>当前绑定:</label>
  550. <div class="label" v-if="dataBindDialog.data.bind?.id">
  551. <t-tooltip :content="dataBindDialog.data.bind?.id">
  552. <t-tag class="mr-8 mb-8" closable @close="onRemoveBind()">
  553. {{ dataBindDialog.data.bind?.label }}
  554. </t-tag>
  555. </t-tooltip>
  556. </div>
  557. <div class="label gray" v-else>无</div>
  558. </div>
  559. <template v-if="!old">
  560. <div class="input-search mb-8 mt-8" style="padding:0px;">
  561. <div class="btn" style="left:10px">
  562. <img style="margin-top: 7px" src="/img/icon_search_gray.svg" />
  563. </div>
  564. <t-input
  565. style="height: 32px;"
  566. v-model="bindSearch"
  567. @change="onSearchBind"
  568. @enter="onSearchBind"
  569. placeholder="搜索绑定变量"
  570. />
  571. </div>
  572. <t-tree
  573. style="height:420px;overflow:auto; scrollbar-width: thin;"
  574. v-model="dataBindDialog.selectedIds"
  575. :activeMultiple="false"
  576. :data="bindTreeData"
  577. :expand-level="1"
  578. :checkable="true"
  579. :checkStrictly="false"
  580. allow-fold-node-on-filter
  581. :filter="bindFilter"
  582. @change="doBind"
  583. />
  584. </template>
  585. <template v-else>
  586. <div class="form-item flex middle mt-8">
  587. <t-input
  588. style="width:250px;"
  589. placeholder="搜索"
  590. v-model="dataBindDialog.input"
  591. @change="onSearchDataset"
  592. @enter="onSearchDataset"
  593. >
  594. <template #suffixIcon>
  595. <search-icon class="hover" @click="onSearchDataset"/>
  596. </template>
  597. </t-input>
  598. </div>
  599. <t-table
  600. class="mt-12 data-list"
  601. row-key="id"
  602. :data="dataBindDialog.dataset"
  603. :columns="dataSetColumns"
  604. size="small"
  605. bordered
  606. rowSelectionAllowUncheck
  607. :loading="dataBindDialog.loading"
  608. :pagination="query"
  609. @page-change="onChangePagination"
  610. :selected-row-keys="dataBindDialog.selectedIds"
  611. @select-change="onSelectBindsChange"
  612. :max-height="270"
  613. >
  614. </t-table>
  615. </template>
  616. </t-dialog>
  617. <t-dialog
  618. v-if="dataMockDialog.show"
  619. :visible="true"
  620. class="data-link-dialog"
  621. header="本地调试"
  622. @close="dataMockDialog.show = false;"
  623. @confirm="dataMockDialog.show = false;"
  624. :top="70"
  625. :width="700"
  626. >
  627. <div class="form-item mt-20">
  628. <label>模拟值:</label>
  629. <t-input v-model="dataMockDialog.data.mock" />
  630. </div>
  631. <div class="form-item mt-20">
  632. <label>类型:</label>
  633. <t-select
  634. class="w-full"
  635. :options="typeOptions"
  636. v-model="dataMockDialog.data.type"
  637. placeholder="字符串"
  638. />
  639. </div>
  640. <div class="form-item mt-12">
  641. <label>开启:</label>
  642. <t-switch
  643. class="mt-8"
  644. size="small"
  645. v-model="dataMockDialog.data.enableMock"
  646. />
  647. </div>
  648. <h6 class="desc mt-20" style="font-size: 13px;">模拟值说明</h6>
  649. <ul class="desc mt-4" style="list-style-type: none;">
  650. <li class="mt-4">
  651. <label class="inline" style="width: 80px">固定值:</label>
  652. 直接填写,例如:10
  653. </li>
  654. <li class="mt-4">
  655. <label class="inline" style="width: 80px">随机值:</label>
  656. 值1,值2,...。例如:1,2,3,4,5
  657. </li>
  658. <li class="mt-4">
  659. <label class="inline" style="width: 80px">范围数字:</label>
  660. 最小值-最大值。例如:0-1.0 或 0-100
  661. </li>
  662. <li class="mt-4">
  663. <label class="inline" style="width: 80px">随机字符串:</label>
  664. [长度]。例如:[8]
  665. </li>
  666. </ul>
  667. </t-dialog>
  668. <t-dialog
  669. v-if="triggersDialog.show"
  670. :visible="true"
  671. class="data-events-dialog"
  672. :header="$t('数据状态')"
  673. @confirm="triggersDialog.show = false"
  674. @close="triggersDialog.show = false"
  675. :width="700"
  676. >
  677. <div class="body">
  678. <t-collapse
  679. v-model="triggersDialog.openedCollapses"
  680. :borderless="true"
  681. :expand-on-row-click="false"
  682. >
  683. <t-collapse-panel
  684. v-for="(trigger, i) in triggersDialog.data.triggers"
  685. :value="i"
  686. >
  687. <template #header>
  688. <t-input v-model="trigger.name" class="mr-12" />
  689. </template>
  690. <template #headerRightContent>
  691. <t-popconfirm @confirm="triggersDialog.data.triggers.splice(i, 1)" :content="$t('确认删除该状态吗?')">
  692. <delete-icon class="hover"></delete-icon>
  693. <!-- <t-icon name="delete" class="hover" /> -->
  694. </t-popconfirm>
  695. </template>
  696. <section>
  697. <div class="form-item banner">
  698. <label>{{$t('满足条件')}}</label>
  699. <div class="w-full flex middle between">
  700. <div></div>
  701. <t-radio-group v-model="trigger.conditionType">
  702. <t-radio value="and"> {{$t('所有')}} </t-radio>
  703. <t-radio value="or"> {{$t('任意')}} </t-radio>
  704. </t-radio-group>
  705. </div>
  706. </div>
  707. <div v-for="(c, index) in trigger.conditions" class="mb-12">
  708. <div class="flex middle between head">
  709. <div class="flex middle">
  710. <arrow-right-icon class="mr-4"/>
  711. <!-- <t-icon name="arrow-right" class="mr-4" /> -->
  712. {{$t(' 条件')}}{{ index + 1 }}
  713. </div>
  714. <close-icon class="hover"
  715. @click="trigger.conditions.splice(index, 1)"/>
  716. <!-- <t-icon
  717. name="close"
  718. class="hover"
  719. @click="trigger.conditions.splice(index, 1)"
  720. /> -->
  721. </div>
  722. <div class="px-16 py-4">
  723. <div class="form-item mt-4">
  724. <label>{{$t('条件类型')}}</label>
  725. <t-radio-group v-model="c.type">
  726. <t-radio value> {{$t('关系条件')}} </t-radio>
  727. <t-radio value="fn"> {{$t('高级条件')}} </t-radio>
  728. </t-radio-group>
  729. </div>
  730. <template v-if="!c.type">
  731. <div class="form-item mt-8">
  732. <label>{{$t('比较条件')}}</label>
  733. <div class="flex middle">
  734. <label class="shrink-0 mr-8">{{$t('数据')}}</label>
  735. <t-select v-model="c.operator" :options="operatorOptions" class="shrink-0 mr-8" style="width: 80px" :placeholder="$t('关系运算')"></t-select>
  736. <t-select v-model="c.valueType" class="shrink-0 mr-8" style="width: 110px" :placeholder="$t('固定值')">
  737. <t-option key value :label="$t('固定值')">
  738. {{$t('固定值')}}
  739. </t-option>
  740. <t-option key="prop" value="prop" :label="$t('对象属性值')">
  741. {{$t('对象属性值')}}
  742. </t-option>
  743. </t-select>
  744. <template v-if="!c.valueType">
  745. <t-input
  746. @change="valueChange($event,c)"
  747. v-model="c.value"
  748. class="shrink-0"
  749. style="width: 320px"
  750. />
  751. </template>
  752. <template v-else>
  753. <t-tree-select
  754. v-model="c.target"
  755. :data="penTree"
  756. filterable
  757. :placeholder="$t('对象')"
  758. class="shrink-0 mr-8"
  759. style="width: 160px"
  760. @change="onChangeTriggerTarget(c)"
  761. />
  762. <t-select-input
  763. v-model:inputValue="c.value"
  764. :value="c.label"
  765. v-model:popupVisible="c.popupVisible"
  766. allow-input
  767. clearable
  768. @clear="c.label = undefined"
  769. @focus="c.popupVisible = true"
  770. @blur="c.popupVisible = false"
  771. @input-change="onInput(c)"
  772. class="shrink-0"
  773. style="width: 152px"
  774. >
  775. <template #panel>
  776. <ul style="padding: 8px 12px">
  777. <li
  778. v-for="item in cProps"
  779. :key="item.value"
  780. @click="
  781. c.value = item.value;
  782. c.label = item.label;
  783. c.popupVisible = false;
  784. "
  785. >
  786. {{ item.label }}
  787. </li>
  788. </ul>
  789. </template>
  790. </t-select-input>
  791. </template>
  792. </div>
  793. </div>
  794. </template>
  795. <template v-else>
  796. <div>function condition(pen) {</div>
  797. <CodeEditor class="mt-4" @change="codeChange($event,c)" v-model="c.fnJs" />
  798. <div class="mt-4">}</div>
  799. </template>
  800. </div>
  801. </div>
  802. <div class="mt-8">
  803. <a @click="addTriggerCondition(trigger)"> + {{$t('添加条件')}} </a>
  804. </div>
  805. <div class="form-item banner mt-16">
  806. <label>{{$t('执行动作')}}</label>
  807. </div>
  808. <Actions class="mt-8" :data="trigger" />
  809. </section>
  810. </t-collapse-panel>
  811. </t-collapse>
  812. <div class="mt-8">
  813. <!-- <a @click="onAddTrigger"> + 添加状态 </a> -->
  814. <t-button @click="onAddTrigger" style="width: 100%;" variant="outline">
  815. <template #icon><add-icon></add-icon></template>
  816. {{$t('添加状态')}}
  817. </t-button>
  818. </div>
  819. </div>
  820. </t-dialog>
  821. <t-dialog v-if="ojbectDialog.show" :visible="true" @confirm="onOkEditOjbect" @close="ojbectDialog.show = false" :width="700" :header="$t('数据编辑')">
  822. <div class="py-8">
  823. <CodeEditor
  824. :json="true"
  825. v-model="ojbectDialog.data"
  826. style="height: 300px"
  827. />
  828. </div>
  829. </t-dialog>
  830. <t-dialog
  831. v-if="propsDialog.show"
  832. :visible="true"
  833. :header="propsDialog.header"
  834. @confirm="onOkPropsEdit"
  835. @close="propsDialog.show = false"
  836. :width="700"
  837. >
  838. <div class="py-8">
  839. <CodeEditor
  840. :json="true"
  841. :language="'json'"
  842. v-model="propsDialog.value"
  843. style="height: 300px"
  844. />
  845. </div>
  846. <div class="gray" style="font-size: 12px">
  847. {{ propsDialog.placeholder }}
  848. </div>
  849. </t-dialog>
  850. <t-dialog v-if="apiDialog.show" :visible="true" class="data-dialog" @close="apiDialog.show = false" @confirm="onConfirmAPI" :header="$t('API接口')">
  851. <div class="form-item mt-8">
  852. <label>http{{$t('地址')}}</label>
  853. <t-input v-model="apiDialog.url" :placeholder="$t('请输入(临时请求,不会保存)')"></t-input>
  854. </div>
  855. <div class="form-item mt-8">
  856. <label>{{$t('请求方式')}}</label>
  857. <t-select v-model="apiDialog.method">
  858. <t-option key="GET" value="GET" label="GET" />
  859. <t-option key="POST" value="POST" label="POST" />
  860. </t-select>
  861. </div>
  862. <div class="form-item mt-8">
  863. <label>{{$t('请求头')}}</label>
  864. <CodeEditor :json="true" :language="'json'" v-model="apiDialog.headers" class="mt-4" style="height: 50px"></CodeEditor>
  865. </div>
  866. <div v-if="apiDialog.method === 'POST'" class="form-item mt-8">
  867. <label>{{$t('请求体')}}</label>
  868. <CodeEditor :json="true" :language="'json'" v-model="apiDialog.body" class="mt-4" style="height: 50px"></CodeEditor>
  869. </div>
  870. </t-dialog>
  871. <t-dialog
  872. v-if="sqlDialog.show"
  873. :visible="true"
  874. class="data-link-dialog"
  875. header="绑定sql数据源"
  876. @close="sqlDialog.show = false;"
  877. @confirm="sqlConnect"
  878. :top="70"
  879. :width="700"
  880. >
  881. <div class="form-item py-12">
  882. <label style="width: 76px">SQL数据源</label>
  883. <t-select v-model="sqlDialog.dbid" placeholder="请选择数据源" >
  884. <t-option v-for="sql in sqlList " @click="sqlChange(sql)" :key="sql.id" :value="sql.id" :label="sql.name+'('+sql.dbType+')'" />
  885. </t-select>
  886. </div>
  887. <div class="form-item mt-8">
  888. <label>sql语句</label>
  889. <CodeEditor
  890. :json="false"
  891. :language="'sql'"
  892. v-model="sqlDialog.sql"
  893. class="mt-4"
  894. style="height: 100px"
  895. />
  896. </div>
  897. <div class="form-item mt-8">
  898. <label>第几页</label>
  899. <t-input-number
  900. v-model="sqlDialog.current"
  901. theme="normal"
  902. :min="1"
  903. placeholder="默认1"
  904. />
  905. </div>
  906. <div class="form-item mt-8">
  907. <label>每页数量</label>
  908. <t-input-number
  909. v-model="sqlDialog.pageSize"
  910. theme="normal"
  911. placeholder="20"
  912. :min="1"
  913. />
  914. </div>
  915. </t-dialog>
  916. </template>
  917. <script lang="ts" setup>
  918. import {
  919. getCurrentInstance,
  920. onBeforeMount,
  921. onUnmounted,
  922. reactive,
  923. ref,
  924. toRaw,
  925. watch,
  926. onMounted,
  927. computed
  928. } from 'vue';
  929. import { useRoute, useRouter } from 'vue-router';
  930. import { MessagePlugin } from 'tdesign-vue-next';
  931. import axios from 'axios';
  932. import { debounce } from '@/services/debouce';
  933. import { getPenTree, typeOptions, changeType } from '@/services/common';
  934. import { searchPinyin } from '@/services/pinyin';
  935. import { updatePen } from './pen';
  936. import { getter, setter, queryURLParams, deepClone, getAllChildren, Pen } from '@meta2d/core';
  937. import CodeEditor from '@/views/components/common/CodeEditor.vue';
  938. import Actions from './Actions.vue';
  939. import { useSelection } from '@/services/selections';
  940. import { defaultGradientColor, defaultPureColor } from '@/services/defaults';
  941. import { getLe5le3d, getLe5leV, getLe5le2d } from '@/services/api';
  942. import { EllipsisIcon, MoreIcon, LinkIcon, RelativityIcon, AddRectangleIcon, SearchIcon ,DeleteIcon, ArrowRightIcon, CloseIcon, ChevronLeftDoubleIcon, AddIcon, ViewListIcon, CheckIcon, HelpCircleIcon, ErrorCircleIcon } from 'tdesign-icons-vue-next';
  943. import { s8 } from '@/services/random';
  944. import { importExcel } from '@/services/excel';
  945. import { importCSV } from '@/services/file';
  946. import { getDevices, getDeviceProperties,getSqlSourceList, doSqlCode } from '@/services/iot';
  947. const route = useRoute();
  948. const router = useRouter();
  949. const { proxy } = getCurrentInstance();
  950. const $t = proxy.$t
  951. const {
  952. proxy: { $forceUpdate },
  953. }: any = getCurrentInstance();
  954. const props = defineProps<{
  955. pen: any;
  956. }>();
  957. const options = ref<any>([
  958. {
  959. value: '',
  960. content: $t('自定义'),
  961. divider: true,
  962. },
  963. {
  964. value: 'x',
  965. content: 'X',
  966. type: 'float',
  967. keywords: true,
  968. },
  969. {
  970. value: 'y',
  971. content: 'Y',
  972. type: 'float',
  973. keywords: true,
  974. },
  975. {
  976. value: 'width',
  977. content: $t('宽'),
  978. type: 'float',
  979. keywords: true,
  980. },
  981. {
  982. value: 'height',
  983. content: $t('高'),
  984. type: 'float',
  985. keywords: true,
  986. },
  987. {
  988. value: 'visible',
  989. content: $t('显示'),
  990. type: 'bool',
  991. keywords: true,
  992. },
  993. {
  994. value: 'text',
  995. content: $t('文字'),
  996. // keywords: true,
  997. },
  998. {
  999. value: 'deviceId',
  1000. content: '关联设备',
  1001. keywords: true,
  1002. },
  1003. {
  1004. value: 'progress',
  1005. content: $t('进度'),
  1006. type: 'float',
  1007. keywords: true,
  1008. },
  1009. {
  1010. value: 'showChild',
  1011. content: $t('状态'),
  1012. type: 'integer',
  1013. keywords: true,
  1014. },
  1015. {
  1016. value: 'rotate',
  1017. content: $t('旋转'),
  1018. type: 'integer',
  1019. keywords: true,
  1020. },
  1021. ]);
  1022. const moreOptions = ref<any>([
  1023. {
  1024. value: 'view',
  1025. content: $t('查看状态'),
  1026. },
  1027. {
  1028. value: 'custom',
  1029. content: $t('自定义状态'),
  1030. divider: true
  1031. },
  1032. {
  1033. value: 'edit',
  1034. content: $t('编辑'),
  1035. },
  1036. {
  1037. value: 'delete',
  1038. content: $t('移除'),
  1039. },
  1040. ]);
  1041. const addDataDialog = reactive<any>({
  1042. show: false,
  1043. data: undefined,
  1044. });
  1045. const dataBindDialog = reactive<any>({
  1046. show: false,
  1047. data: undefined,
  1048. });
  1049. const dataMockDialog = reactive<any>({
  1050. show: false,
  1051. data: undefined,
  1052. });
  1053. const ojbectDialog = reactive<any>({
  1054. show: false,
  1055. data: undefined,
  1056. });
  1057. const dataSetColumns = [
  1058. {
  1059. colKey: 'row-select',
  1060. type: 'single',
  1061. width: 50,
  1062. },
  1063. // {
  1064. // colKey: 'device',
  1065. // title: $t('设备'),
  1066. // ellipsis: { theme: 'light', trigger: 'context-menu' },
  1067. // },
  1068. {
  1069. colKey: 'label',
  1070. title: $t('显示名称'),
  1071. // ellipsis: false,
  1072. ellipsis:{overlayClassName:'le-table-popup'}
  1073. },
  1074. {
  1075. colKey: 'id',
  1076. title: $t('变量名'),
  1077. // ellipsis: false,
  1078. ellipsis:{overlayClassName:'le-table-popup'}
  1079. },
  1080. {
  1081. colKey: 'type',
  1082. title: $t('类型'),
  1083. width: 100,
  1084. },
  1085. {
  1086. colKey: 'mock',
  1087. title: $t('值范围'),
  1088. ellipsis: true,
  1089. },
  1090. ];
  1091. const operatorOptions = ref<any>([
  1092. { label: '=', value: '=' },
  1093. { label: '!=', value: '!=' },
  1094. { label: '>', value: '>' },
  1095. { label: '<', value: '<' },
  1096. { label: '>=', value: '>=' },
  1097. { label: '<=', value: '<=' },
  1098. { label: $t('包含'), value: '[)' },
  1099. { label: $t('不包含'), value: '![)' },
  1100. ]);
  1101. const cProps = [
  1102. {
  1103. value: 'x',
  1104. label: 'X',
  1105. },
  1106. {
  1107. value: 'y',
  1108. label: 'Y',
  1109. },
  1110. {
  1111. value: 'width',
  1112. label: $t('宽'),
  1113. },
  1114. {
  1115. value: 'height',
  1116. label: $t('高'),
  1117. },
  1118. {
  1119. value: 'visible',
  1120. label: $t('显示'),
  1121. },
  1122. {
  1123. value: 'text',
  1124. label: $t('文字'),
  1125. },
  1126. {
  1127. value: 'color',
  1128. label: $t('颜色'),
  1129. },
  1130. {
  1131. value: 'background',
  1132. label: $t('背景颜色'),
  1133. },
  1134. {
  1135. value: 'progress',
  1136. label: $t('进度'),
  1137. },
  1138. {
  1139. value: 'progressColor',
  1140. label: $t('进度颜色'),
  1141. },
  1142. {
  1143. value: 'showChild',
  1144. label: $t('状态'),
  1145. },
  1146. {
  1147. value: 'checked',
  1148. label: $t('选中'),
  1149. },
  1150. {
  1151. value: 'rotate',
  1152. label: $t('旋转'),
  1153. },
  1154. {
  1155. value: 'disabled',
  1156. label: $t('禁用'),
  1157. },
  1158. {
  1159. value: 'selectedKey',
  1160. label: $t('单选选中值'),
  1161. },
  1162. {
  1163. value: 'animateReverse',
  1164. label: $t('连线动画反向'),
  1165. }
  1166. ];
  1167. const query = reactive<{
  1168. current: number;
  1169. pageSize: number;
  1170. total: number;
  1171. range: any[];
  1172. }>({
  1173. current: 1,
  1174. pageSize: 10,
  1175. total: 0,
  1176. range: [],
  1177. });
  1178. const triggersDialog = reactive<any>({
  1179. show: false,
  1180. data: undefined,
  1181. });
  1182. const penTree: any = ref([]);
  1183. let timer: any;
  1184. const changeID = (value: any) => {
  1185. if (!value) {
  1186. // initPenData();
  1187. MessagePlugin.error('id 不能为空');
  1188. return;
  1189. }
  1190. const oldID: string = props.pen.id;
  1191. try {
  1192. meta2d.changePenId(oldID, value);
  1193. // initPenData();
  1194. } catch (error) {
  1195. console.warn(error.message);
  1196. MessagePlugin.error('id 修改失败,请检查 id 是否重复');
  1197. return;
  1198. }
  1199. };
  1200. onBeforeMount(() => {
  1201. // realTimesOptions - 扩展的动态数据下拉列表
  1202. if (props.pen.realTimesOptions) {
  1203. options.value[options.value.length - 1].divider = true;
  1204. options.value.push(...props.pen.realTimesOptions);
  1205. }
  1206. timer = setInterval($forceUpdate, 1000);
  1207. });
  1208. const addRealTime = (e: any) => {
  1209. if (e.keywords || e.value === 'text') {
  1210. if (!props.pen.realTimes) {
  1211. props.pen.realTimes = [];
  1212. }
  1213. props.pen.realTimes.push({
  1214. label: e.content,
  1215. key: e.value,
  1216. type: e.type,
  1217. keywords: e.keywords,
  1218. });
  1219. return;
  1220. }
  1221. addDataDialog.header = $t('添加动态数据');
  1222. addDataDialog.data = {
  1223. label: e.content,
  1224. key: e.value,
  1225. type: e.type,
  1226. keywords: e.keywords,
  1227. };
  1228. addDataDialog.data.pen = undefined;
  1229. if (e.keywords) {
  1230. addDataDialog.data.label = '';
  1231. }
  1232. addDataDialog.show = true;
  1233. };
  1234. const onKeyBlur = () => {
  1235. if (addDataDialog.data) {
  1236. let value = getter(props.pen, addDataDialog.data.key);
  1237. if (value) {
  1238. setter(addDataDialog.data, 'value', value);
  1239. } else {
  1240. addDataDialog.data.value = null;
  1241. }
  1242. }
  1243. };
  1244. const onChangeLabel = () => {
  1245. if (!addDataDialog.data.key) {
  1246. addDataDialog.data.key = addDataDialog.data.label;
  1247. }
  1248. };
  1249. const onConfirmData = () => {
  1250. const pen = addDataDialog.data.pen || props.pen;
  1251. if (!pen.realTimes) {
  1252. pen.realTimes = [];
  1253. }
  1254. if (!addDataDialog.data.label || !addDataDialog.data.key) {
  1255. MessagePlugin.error($t('数据名或属性名不能为空!'));
  1256. return;
  1257. }
  1258. delete addDataDialog.data.pen;
  1259. if (addDataDialog.header === $t('添加动态数据')) {
  1260. const found = pen.realTimes.findIndex((item: any) => {
  1261. return item.key === addDataDialog.data.key;
  1262. });
  1263. if (found > -1) {
  1264. MessagePlugin.error($t('已经存在相同属性数据!'));
  1265. return;
  1266. }
  1267. if(pen[addDataDialog.data.key] === undefined && addDataDialog.data.key.indexOf('.') !== -1){
  1268. pen[addDataDialog.data.key] = getter(pen, addDataDialog.data.key);
  1269. }
  1270. pen.realTimes.push(addDataDialog.data);
  1271. }
  1272. addDataDialog.show = false;
  1273. };
  1274. const onMenuMore = (e: any, item: any, i: number) => {
  1275. switch (e.value) {
  1276. case 'edit':
  1277. addDataDialog.header = $t('编辑动态数据');
  1278. setter(item, 'value', getter(props.pen, item.key));
  1279. addDataDialog.data = item;
  1280. addDataDialog.show = true;
  1281. break;
  1282. case 'delete':
  1283. props.pen.realTimes.splice(i, 1);
  1284. meta2d.initBinds();
  1285. break;
  1286. default:
  1287. break;
  1288. }
  1289. };
  1290. const onBind = (item: any) => {
  1291. dataBindDialog.data = item;
  1292. dataBindDialog.input = '';
  1293. dataBindDialog.selectedIds = [];
  1294. if (item.bind && item.bind.id) {
  1295. dataBindDialog.selectedIds.push(item.bind.id);
  1296. }
  1297. dataBindDialog.show = true;
  1298. if(old){
  1299. getDataset();
  1300. }else{
  1301. // getIotTree();
  1302. // getSqlTree();
  1303. getBindTreeData();
  1304. }
  1305. };
  1306. const bindTreeData = ref<any>([]);
  1307. const getBindTreeData = ()=>{
  1308. let data = [];
  1309. const iotTree = meta2d.store.data.iot?.tree || [];
  1310. if(iotTree.length){
  1311. iotTree.forEach((item:any)=>{
  1312. item.checkable = false;
  1313. });
  1314. data.push({
  1315. label: '物联网平台',
  1316. value: 'iot',
  1317. checkable: false,
  1318. children: iotTree,
  1319. });
  1320. }
  1321. const sqlTree = meta2d.store.data.sqls || [];
  1322. if(sqlTree.length){
  1323. data.push({
  1324. label: 'sql数据源',
  1325. value: 'sql',
  1326. checkable: false,
  1327. children: sqlTree,
  1328. });
  1329. }
  1330. const networks = meta2d.store.data.networks || [];
  1331. if(networks.length){
  1332. networks.forEach((item:any)=>{
  1333. item.checkable = false;
  1334. });
  1335. const mqttNets = meta2d.store.data.networks.filter((item)=>item.protocol==='mqtt');
  1336. const wsNets = meta2d.store.data.networks.filter((item)=>item.protocol==='websocket');
  1337. const httpNets = meta2d.store.data.networks.filter((item)=>item.protocol==='http');
  1338. const SSENets = meta2d.store.data.networks.filter((item)=>item.protocol==='SSE');
  1339. if(mqttNets.length){
  1340. data.push({
  1341. label: 'MQTT',
  1342. value: 'network',
  1343. checkable: false,
  1344. children: mqttNets,
  1345. });
  1346. }
  1347. if(wsNets.length){
  1348. data.push({
  1349. label: 'Websocket',
  1350. value: 'network',
  1351. checkable: false,
  1352. children: wsNets,
  1353. });
  1354. }
  1355. if(httpNets.length){
  1356. data.push({
  1357. label: 'HTTP',
  1358. value: 'network',
  1359. checkable: false,
  1360. children: httpNets,
  1361. });
  1362. }
  1363. if(SSENets.length){
  1364. data.push({
  1365. label: 'SSE',
  1366. value: 'network',
  1367. checkable: false,
  1368. children: SSENets,
  1369. });
  1370. }
  1371. }
  1372. bindTreeData.value = data;
  1373. }
  1374. const bindSearch = ref('');
  1375. const bindFilter = ref(null);
  1376. const onSearchBind = () => {
  1377. bindFilter.value = bindSearch.value ? (node) => { return node.data.label?.indexOf(bindSearch.value) >= 0|| node.data.value?.indexOf(bindSearch.value) >= 0 }: null;
  1378. };
  1379. const bindValue = ref('');
  1380. const doBind = (value:any,context:any)=>{
  1381. // bindValue.value = [context.node.value];
  1382. dataBindDialog.selectedIds = [context.node.value];
  1383. dataBindDialog.data.bind = {
  1384. id: context.node.value,
  1385. label: context.node.data._label|| context.node.data.label,
  1386. class: context.node.data.class,
  1387. token: context.node.data.token,
  1388. }
  1389. meta2d.initBinds();
  1390. }
  1391. const onSearchDataset = () => {
  1392. debounce(getDataset, 300);
  1393. };
  1394. const getDataset = async () => {
  1395. // @ts-ignore
  1396. const data: Meta2dBackData = meta2d.data();
  1397. dataBindDialog.sqls = data.sqls;
  1398. if (!data.dataset) {
  1399. return;
  1400. }
  1401. dataBindDialog.dataset = data.dataset.devices;
  1402. dataBindDialog.datasetList = data.dataset.devices;
  1403. if(dataBindDialog.device || dataBindDialog.input){
  1404. dataBindDialog.dataset = dataBindDialog.datasetList.filter(
  1405. (item: any) => {
  1406. if (dataBindDialog.device && item.device !== dataBindDialog.device) {
  1407. return;
  1408. }
  1409. return (
  1410. searchPinyin(item.label, dataBindDialog.input) ||
  1411. item.id.indexOf(dataBindDialog.input) > -1
  1412. );
  1413. }
  1414. );
  1415. }
  1416. query.total = dataBindDialog.dataset.length;
  1417. return;
  1418. dataBindDialog.loading = true;
  1419. if (data.dataset.url) {
  1420. const ret: any = await axios.get(data.dataset.url, {
  1421. params: {
  1422. device: dataBindDialog.device || '',
  1423. q: dataBindDialog.input,
  1424. current: query.current,
  1425. pageSize: query.pageSize,
  1426. },
  1427. });
  1428. dataBindDialog.dataset = ret;
  1429. dataBindDialog.url = true;
  1430. query.total = ret.total;
  1431. console.log( dataBindDialog.dataset,
  1432. query.total);
  1433. } else {
  1434. dataBindDialog.url = false;
  1435. if (!dataBindDialog.datasetList) {
  1436. const ret:any = await axios.post(`/api/data/datasource/get`, {
  1437. id: data.dataset.id,
  1438. });
  1439. if (ret?.data?.devices) {
  1440. data.dataset.data = ret.data.devices;
  1441. dataBindDialog.datasetList = data.dataset.data;
  1442. const set = new Set<string>();
  1443. for (const item of dataBindDialog.datasetList) {
  1444. set.add(item.device);
  1445. }
  1446. dataBindDialog.devices = Array.from(set);
  1447. } else {
  1448. dataBindDialog.dataset = [];
  1449. dataBindDialog.devices = [];
  1450. return;
  1451. }
  1452. }
  1453. if (dataBindDialog.device || dataBindDialog.input) {
  1454. dataBindDialog.dataset = dataBindDialog.datasetList.filter(
  1455. (item: any) => {
  1456. if (dataBindDialog.device && item.device !== dataBindDialog.device) {
  1457. return;
  1458. }
  1459. return (
  1460. searchPinyin(item.label, dataBindDialog.input) ||
  1461. item.id.indexOf(dataBindDialog.input) > -1
  1462. );
  1463. }
  1464. );
  1465. query.total = dataBindDialog.datasetList.length;
  1466. } else {
  1467. dataBindDialog.dataset = [...dataBindDialog.datasetList];
  1468. query.total = dataBindDialog.datasetList.length;
  1469. }
  1470. console.log("dataBindDialog",dataBindDialog,dataBindDialog);
  1471. }
  1472. dataBindDialog.loading = false;
  1473. };
  1474. const onChangePagination = (pageInfo: any) => {
  1475. // router.push({
  1476. // path: route.path,
  1477. // query: { current: pageInfo.current, pageSize: pageInfo.pageSize },
  1478. // });
  1479. query.current = pageInfo.current;
  1480. query.pageSize = pageInfo.pageSize;
  1481. getDataset();
  1482. };
  1483. const onSelectBindsChange = (value: string[], options: any) => {
  1484. if (options.type === 'check') {
  1485. dataBindDialog.selectedIds = value;
  1486. dataBindDialog.data.bind = toRaw(options.selectedRowData[0]);
  1487. if(dataBindDialog.data.bind.id){
  1488. dataBindDialog.data.enableMock = false;
  1489. }
  1490. dataBindDialog.data.mock = dataBindDialog.data.bind.mock;
  1491. dataBindDialog.data.type = dataBindDialog.data.bind.type;
  1492. doBindInit();
  1493. } else if (options.type === 'uncheck') {
  1494. dataBindDialog.selectedIds = [];
  1495. dataBindDialog.data.bind = {};
  1496. }
  1497. };
  1498. const doBindInit = () => {
  1499. let { id } = dataBindDialog.data;
  1500. if (props.pen.name === 'echarts' && id?.includes('echarts.option.series')) {
  1501. const { replaceMode } = props.pen.echarts;
  1502. const { xAxis } = props.pen.echarts.option;
  1503. let beforeV = getter(props.pen, id);
  1504. if (Array.isArray(beforeV) && replaceMode === 0) {
  1505. //追加
  1506. setter(props.pen, id, []);
  1507. let _key = 'echarts.option.xAxis.data';
  1508. if (Array.isArray(xAxis) && xAxis.length) {
  1509. _key = 'echarts.option.xAxis.0.data';
  1510. }
  1511. setter(props.pen, _key, []);
  1512. }
  1513. }
  1514. };
  1515. const dataBindonConfirm = () => {
  1516. dataBindDialog.show = false;
  1517. dataBindDialog.datasetList = undefined;
  1518. meta2d.initBinds();
  1519. };
  1520. const onRemoveBind = () => {
  1521. dataBindDialog.selectedIds = [];
  1522. dataBindDialog.data.bind = undefined;
  1523. };
  1524. const getBindsDesc = (item: any) => {
  1525. if (item.bind?.label) {
  1526. return item.bind.label;
  1527. }
  1528. if (item.enableMock && item.mock) {
  1529. return item.mock;
  1530. }
  1531. return $t('绑定变量');
  1532. };
  1533. const { selections } = useSelection();
  1534. const changeValue = (prop: string) => {
  1535. updatePen(props.pen, prop);
  1536. selections.pen[prop] = getter(props.pen, prop);
  1537. if (prop === 'iframe') {
  1538. getThumbImg();
  1539. }
  1540. if(prop === 'apiEnable'){
  1541. meta2d.penNetwork(props.pen);
  1542. meta2d.connectNetwork();
  1543. }
  1544. };
  1545. const onTrigger = (item: any) => {
  1546. if (!item.triggers) {
  1547. item.triggers = [];
  1548. }
  1549. triggersDialog.openedCollapses = [0];
  1550. triggersDialog.data = item;
  1551. triggersDialog.show = true;
  1552. penTree.value = getPenTree();
  1553. };
  1554. const onAddTrigger = () => {
  1555. const i = triggersDialog.data.triggers.length;
  1556. triggersDialog.data.triggers.push({
  1557. name: $t(`状态`)+ (i + 1),
  1558. conditionType: 'and',
  1559. conditions: [],
  1560. actions: [],
  1561. });
  1562. triggersDialog.openedCollapses.push(i);
  1563. };
  1564. const addTriggerCondition = (trigger: any) => {
  1565. trigger.conditions.push({
  1566. type: '',
  1567. operator: '=',
  1568. valueType: '',
  1569. });
  1570. };
  1571. const onChangeTriggerTarget = (c: any) => {
  1572. /*
  1573. c.targetProps = [
  1574. {
  1575. value: 'x',
  1576. label: 'X',
  1577. },
  1578. {
  1579. value: 'y',
  1580. label: 'Y',
  1581. },
  1582. {
  1583. value: 'width',
  1584. label: $t('宽'),
  1585. },
  1586. {
  1587. value: 'height',
  1588. label: $t('高'),
  1589. },
  1590. {
  1591. value: 'visible',
  1592. label: $t('显示'),
  1593. },
  1594. {
  1595. value: 'text',
  1596. label: $t('文字'),
  1597. },
  1598. {
  1599. value: 'progress',
  1600. label: $t('进度'),
  1601. },
  1602. {
  1603. value: 'showChild',
  1604. label: $t('状态'),
  1605. },
  1606. {
  1607. value: 'rotate',
  1608. label: $t('旋转'),
  1609. },
  1610. ];*/
  1611. const target: any = meta2d.findOne(c.target);
  1612. if (target && target.realTimes) {
  1613. for (const item of target.realTimes) {
  1614. const found = cProps.findIndex((elem: any) => elem.value === item.key);
  1615. if (found < 0) {
  1616. cProps.push({
  1617. value: item.key,
  1618. label: item.label,
  1619. });
  1620. }
  1621. }
  1622. }
  1623. };
  1624. const onInput = (item: any) => {
  1625. item.label = item.value;
  1626. };
  1627. const editObject = (item: any) => {
  1628. ojbectDialog.data = props.pen[item.key];
  1629. ojbectDialog.item = item;
  1630. ojbectDialog.pen = undefined;
  1631. ojbectDialog.show = true;
  1632. };
  1633. const onOkEditOjbect = () => {
  1634. if (ojbectDialog.data) {
  1635. if (
  1636. ojbectDialog.item.type === 'array' &&
  1637. !Array.isArray(ojbectDialog.data)
  1638. ) {
  1639. MessagePlugin.error($t('请输入数组格式数据'));
  1640. return;
  1641. }
  1642. const pen = ojbectDialog.pen || props.pen;
  1643. pen[ojbectDialog.item.key] = deepClone(ojbectDialog.data);
  1644. updatePen(
  1645. pen,
  1646. ojbectDialog.item.key
  1647. );
  1648. ojbectDialog.show = false;
  1649. } else {
  1650. MessagePlugin.error($t('请输入严格的JSON格式数据'));
  1651. }
  1652. };
  1653. const propsDialog = reactive<any>({
  1654. show: false,
  1655. });
  1656. // const changeValue = (prop: string) => {
  1657. // updatePen(props.pen, prop);
  1658. // selections.pen[prop] = getter(props.pen, prop);
  1659. // if (prop === 'iframe$t(') {
  1660. // getThumbImg();
  1661. // }
  1662. // };
  1663. const getThumbImg = async () => {
  1664. //改iframe地址后
  1665. let arr = props.pen.iframe.split('?');
  1666. let id = queryURLParams(arr[1]).id;
  1667. if (!id) {
  1668. return;
  1669. }
  1670. // let projection = {
  1671. // image: 1,
  1672. // _id: 1,
  1673. // name: 1,
  1674. // };
  1675. let projection = 'image,id,name';
  1676. let res: any;
  1677. if (arr[0].indexOf('2d.le5le') !== -1||arr[0].indexOf('le5le.com/2d') !== -1) {
  1678. res = await getLe5le2d(id, projection);
  1679. } else if (arr[0].indexOf('v.le5le') !== -1||arr[0].indexOf('le5le.com/v') !== -1) {
  1680. res = await getLe5leV(id, projection);
  1681. } else if (arr[0].indexOf('3d.le5le') !== -1||arr[0].indexOf('le5le.com/3d') !== -1) {
  1682. res = await getLe5le3d(id, projection);
  1683. }
  1684. if (res) {
  1685. props.pen.thumbImg = res.image;
  1686. props.pen.calculative&&(props.pen.calculative.img = null);
  1687. }
  1688. props.pen.onRenderPenRaw?.(props.pen);
  1689. };
  1690. const preShowPropsEdit = (key: string) => {
  1691. showPropsEdit({
  1692. key,
  1693. // item: {
  1694. // key,
  1695. type: 'code',
  1696. label: key,
  1697. placeholder: '支持设置动态参数,例如:{"Authorization": "Bearer ${token}"}',
  1698. // },
  1699. });
  1700. };
  1701. const showPropsEdit = (item: any) => {
  1702. propsDialog.key = item.key;
  1703. propsDialog.header = `${item.label}(${item.key})`;
  1704. propsDialog.value = props.pen[item.key];
  1705. propsDialog.placeholder = item.placeholder;
  1706. propsDialog.show = true;
  1707. };
  1708. const onOkPropsEdit = () => {
  1709. if (!propsDialog.value) {
  1710. MessagePlugin.error($t('数据不满足json格式'));
  1711. return;
  1712. }
  1713. props.pen[propsDialog.key] = propsDialog.value;
  1714. updatePen(props.pen, propsDialog.key);
  1715. propsDialog.show = false;
  1716. };
  1717. const codeChange = (e:any,a:any)=>{
  1718. a.fn = null;
  1719. }
  1720. const valueChange = (e,c:any)=>{
  1721. c.value= changeType(e);
  1722. }
  1723. const drawer = reactive<any>({
  1724. visible: false,
  1725. });
  1726. const showDrawer = (item:any)=>{
  1727. drawer.key = item.key;
  1728. drawer.header = `${item.label}(${item.key})`;
  1729. drawer.value = deepClone(props.pen[item.key]);
  1730. drawer.placeholder = item.placeholder;
  1731. drawer.randomkey = s8();//props.pen.id;
  1732. drawer.visible = true;
  1733. }
  1734. const onConfirmDrawer = () => {
  1735. if (!drawer.value) {
  1736. MessagePlugin.error($t('数据不满足json格式'));
  1737. return;
  1738. }
  1739. props.pen[drawer.key] = drawer.value;
  1740. updatePen(props.pen, drawer.key);
  1741. };
  1742. const childrenPens = ref([]);
  1743. watch(
  1744. () => props.pen.id,
  1745. (newValue) => {
  1746. childrenPens.value = [];
  1747. // childrenKey.value = s8();
  1748. if(props.pen.children?.length){
  1749. childrenPens.value = getAllChildren(meta2d.store.pens[props.pen.id],meta2d.store);
  1750. }
  1751. getRealTimesTrigger();
  1752. }
  1753. );
  1754. onMounted(() => {
  1755. childrenPens.value = [];
  1756. if(props.pen.children?.length){
  1757. childrenPens.value = getAllChildren(meta2d.store.pens[props.pen.id],meta2d.store);
  1758. }
  1759. getRealTimesTrigger();
  1760. });
  1761. const getRealTimesTrigger = ()=>{
  1762. if(!props.pen.triggers?.length){
  1763. return;
  1764. }
  1765. props.pen.realTimes?.forEach((real)=>{
  1766. let shouldBreak = false;
  1767. props.pen.triggers.forEach((item,index)=>{
  1768. if (shouldBreak) return;
  1769. item.status.forEach((_item)=>{
  1770. const trigger = _item.conditions.filter((i)=>i.key===real.key);
  1771. if(trigger?.length){
  1772. real.trigger = index;
  1773. shouldBreak = true;
  1774. }
  1775. })
  1776. });
  1777. })
  1778. }
  1779. const gotoTrigger = (i:any)=>{
  1780. if(i===undefined){
  1781. return;
  1782. }
  1783. emit('tabchange',i);
  1784. }
  1785. const changeCValue = (prop: string, pen:Pen)=>{
  1786. updatePen(pen, prop);
  1787. selections.pen[prop] = getter(pen, prop);
  1788. if (prop === 'iframe') {
  1789. getThumbImg();
  1790. }
  1791. if(prop === 'apiEnable'){
  1792. meta2d.penNetwork(pen);
  1793. meta2d.connectNetwork();
  1794. }
  1795. }
  1796. const editCObject = (item: any, pen:Pen) => {
  1797. ojbectDialog.data = pen[item.key];
  1798. ojbectDialog.pen = pen;
  1799. ojbectDialog.item = item;
  1800. ojbectDialog.show = true;
  1801. };
  1802. const addCRealTime = (e: any, pen:Pen) => {
  1803. if (e.keywords || e.value === 'text') {
  1804. if (!pen.realTimes) {
  1805. pen.realTimes = [];
  1806. }
  1807. pen.realTimes.push({
  1808. label: e.content,
  1809. key: e.value,
  1810. type: e.type,
  1811. keywords: e.keywords,
  1812. });
  1813. return;
  1814. }
  1815. addDataDialog.header = $t('添加动态数据');
  1816. addDataDialog.data = {
  1817. label: e.content,
  1818. key: e.value,
  1819. type: e.type,
  1820. keywords: e.keywords,
  1821. };
  1822. addDataDialog.data.pen = pen;
  1823. if (e.keywords) {
  1824. addDataDialog.data.label = '';
  1825. }
  1826. addDataDialog.show = true;
  1827. };
  1828. const onCMenuMore = (e: any, item: any, i: number, pen:any)=>{
  1829. switch (e.value) {
  1830. case 'edit':
  1831. addDataDialog.header = $t('编辑动态数据');
  1832. setter(item, 'value', getter(pen, item.key));
  1833. addDataDialog.data = item;
  1834. addDataDialog.data.pen = pen;
  1835. addDataDialog.show = true;
  1836. break;
  1837. case 'delete':
  1838. pen.realTimes.splice(i, 1);
  1839. meta2d.initBinds();
  1840. break;
  1841. case 'custom':
  1842. if(!pen.triggers){
  1843. pen.triggers = [];
  1844. }
  1845. pen.triggers.push({
  1846. name: $t(`状态场景`) + (pen.triggers.length + 1),
  1847. status:[
  1848. {
  1849. name:$t('状态1'),
  1850. conditionType: 'and',
  1851. conditions: [{
  1852. type:'',
  1853. operator: "=",
  1854. key: item.key,//"text",
  1855. keyLabel: item.label,
  1856. value: undefined
  1857. }],
  1858. actions: [],
  1859. },
  1860. {
  1861. name:$t('状态2'),
  1862. conditionType: 'and',
  1863. conditions: [],
  1864. actions: [],
  1865. }
  1866. ],
  1867. });
  1868. meta2d.active(meta2d.find(pen.id));
  1869. emit('tabchange',pen.triggers.length-1);
  1870. break;
  1871. case 'view':
  1872. meta2d.active(meta2d.find(pen.id));
  1873. emit('tabchange',-1);
  1874. default:
  1875. break;
  1876. }
  1877. }
  1878. const emit = defineEmits(['tabchange']);
  1879. const triggerOptions = computed(() => {
  1880. const options = [
  1881. {
  1882. value: 'custom',
  1883. content: $t('自定义状态'),
  1884. divider: true
  1885. },
  1886. {
  1887. value: 'warning',
  1888. content: $t('条件告警'),
  1889. },
  1890. {
  1891. value: 'warningAnimate',
  1892. content: $t('条件动画'),
  1893. divider: true
  1894. },
  1895. {
  1896. value: 'mock',
  1897. content: '本地调试',
  1898. },
  1899. {
  1900. value: 'edit',
  1901. content: $t('编辑'),
  1902. },
  1903. {
  1904. value: 'delete',
  1905. content: $t('移除'),
  1906. },
  1907. ]
  1908. if( props.pen.name === "combine"&& props.pen.showChild !== undefined){
  1909. options.splice(1,0,
  1910. {
  1911. value: 'switch',
  1912. content: $t('状态切换'),
  1913. });
  1914. }
  1915. return options
  1916. });
  1917. const addTrigger = (item:any, e:any, i:any) => {
  1918. if(!['edit', 'delete'].includes(e.value)){
  1919. if(!props.pen.triggers){
  1920. props.pen.triggers = [];
  1921. }
  1922. }
  1923. switch (e.value) {
  1924. case 'edit':
  1925. addDataDialog.header = $t('编辑动态数据');
  1926. setter(item, 'value', getter(props.pen, item.key));
  1927. addDataDialog.data = item;
  1928. addDataDialog.show = true;
  1929. break;
  1930. case 'delete':
  1931. props.pen.realTimes.splice(i, 1);
  1932. meta2d.initBinds();
  1933. break;
  1934. case 'switch':
  1935. props.pen.triggers.push({
  1936. "name": "状态切换",
  1937. "status": [
  1938. {
  1939. "name": $t("满足条件,切换到状态1"),
  1940. "conditionType": "and",
  1941. "conditions": [
  1942. {
  1943. "type": "",
  1944. "operator": ">=",
  1945. "key": item.key,
  1946. "keyLabel": item.label,
  1947. "valueType": ""
  1948. }
  1949. ],
  1950. "actions": [
  1951. {
  1952. "action": 1,
  1953. "params": "",
  1954. "value": {
  1955. "showChild": 1
  1956. },
  1957. "targetType": "id"
  1958. }
  1959. ]
  1960. },
  1961. {
  1962. "name": $t("默认显示状态"),
  1963. "conditionType": "and",
  1964. "conditions": [],
  1965. "actions": [
  1966. {
  1967. "action": 1,
  1968. "params": "",
  1969. "value": {
  1970. "showChild": 0
  1971. },
  1972. "targetType": "id"
  1973. }
  1974. ]
  1975. }
  1976. ]
  1977. });
  1978. break;
  1979. case 'warning':
  1980. props.pen.triggers.push({
  1981. "name": "条件告警",
  1982. "status": [
  1983. {
  1984. "name": $t("满足条件,触发告警"),
  1985. "conditions": [
  1986. {
  1987. "type": "",
  1988. "operator": ">=",
  1989. "valueType": "",
  1990. "key": item.key,
  1991. "keyLabel":item.label,
  1992. "value": undefined
  1993. }
  1994. ],
  1995. "actions": [
  1996. {
  1997. "action": 1,
  1998. "params": "",
  1999. "value": {
  2000. "background": "red",
  2001. },
  2002. "targetType": "id"
  2003. }
  2004. ],
  2005. "conditionType": "and"
  2006. },
  2007. {
  2008. "name": $t("恢复默认"),
  2009. "conditionType": "and",
  2010. "conditions": [],
  2011. "actions": [
  2012. {
  2013. "action": 1,
  2014. "params": "",
  2015. "value": {
  2016. "background": "#0000",
  2017. },
  2018. "targetType": "id"
  2019. }
  2020. ]
  2021. }
  2022. ]
  2023. });
  2024. break;
  2025. case 'warningAnimate':
  2026. props.pen.triggers.push({
  2027. "name": "条件动画",
  2028. "status": [
  2029. {
  2030. "name": $t("满足条件,触发动画执行"),
  2031. "conditions": [
  2032. {
  2033. "type": "",
  2034. "operator": ">=",
  2035. "valueType": "",
  2036. "key": item.key,
  2037. "keyLabel":item.label,
  2038. "value": undefined
  2039. }
  2040. ],
  2041. "actions": [
  2042. {
  2043. "action": 2,
  2044. "params": "",
  2045. "value": "",
  2046. "targetType": "id",
  2047. }
  2048. ],
  2049. "conditionType": "and"
  2050. },
  2051. {
  2052. "name": $t("恢复默认状态"),
  2053. "conditionType": "and",
  2054. "conditions": [],
  2055. "actions": [
  2056. {
  2057. "action": 4,
  2058. "params": "",
  2059. "value": "",
  2060. "targetType": "id"
  2061. }
  2062. ]
  2063. }
  2064. ]
  2065. });
  2066. break;
  2067. case 'custom':
  2068. props.pen.triggers.push({
  2069. name: $t(`状态场景`) + (props.pen.triggers.length + 1),
  2070. status:[
  2071. {
  2072. name:$t('状态1'),
  2073. conditionType: 'and',
  2074. conditions: [{
  2075. type:'',
  2076. operator: "=",
  2077. key: item.key,//"text",
  2078. keyLabel: item.label,
  2079. value: undefined
  2080. }],
  2081. actions: [],
  2082. },
  2083. {
  2084. name:$t('状态2'),
  2085. conditionType: 'and',
  2086. conditions: [],
  2087. actions: [],
  2088. }
  2089. ],
  2090. });
  2091. break;
  2092. case 'mock':
  2093. dataMockDialog.data = item;
  2094. dataMockDialog.show = true;
  2095. break;
  2096. }
  2097. if(!['edit', 'delete','mock'].includes(e.value)){
  2098. emit('tabchange',props.pen.triggers.length-1);
  2099. }
  2100. }
  2101. const cell = ref({
  2102. col:undefined,
  2103. row:undefined
  2104. });
  2105. const getPenData = (e)=>{
  2106. if(props.pen.name === 'tablePlus'){
  2107. if(props.pen.calculative.activeRow !== undefined){
  2108. cell.value.row = props.pen.calculative.activeRow;
  2109. cell.value.col = undefined;
  2110. }else if(props.pen.calculative.activeCell){
  2111. cell.value.col = props.pen.calculative.activeCell.col;
  2112. cell.value.row = props.pen.calculative.activeCell.row;
  2113. }else{
  2114. cell.value.col = undefined;
  2115. cell.value.row = undefined;
  2116. }
  2117. }
  2118. }
  2119. const onQuickBind = ()=>{
  2120. if(cell.value.row !== undefined){
  2121. if(!props.pen.realTimes){
  2122. props.pen.realTimes = [];
  2123. }
  2124. let row = cell.value.row;
  2125. if(props.pen.colHeaders){
  2126. row-=1;
  2127. }
  2128. if(cell.value.col !== undefined){
  2129. let found = props.pen.realTimes.findIndex((item:any)=>{
  2130. return item.key === `data.${cell.value.row}.${cell.value.col}`;
  2131. });
  2132. if(found === -1){
  2133. props.pen.realTimes.push({
  2134. label: `第${row+1}行,第${cell.value.col}列`,
  2135. key: `data.${row}.${cell.value.col}`,
  2136. type: 'float',
  2137. });
  2138. onBind(props.pen.realTimes[props.pen.realTimes.length-1]);
  2139. }else{
  2140. onBind(props.pen.realTimes[found]);
  2141. }
  2142. }else{
  2143. let found = props.pen.realTimes.findIndex((item:any)=>{
  2144. return item.key === `data.${cell.value.row}`;
  2145. });
  2146. if(found === -1){
  2147. props.pen.realTimes.push({
  2148. label: `第${row+1}行`,
  2149. key: `data.${row}`,
  2150. type: 'array',
  2151. });
  2152. onBind(props.pen.realTimes[props.pen.realTimes.length-1]);
  2153. }else{
  2154. onBind(props.pen.realTimes[found]);
  2155. }
  2156. }
  2157. }
  2158. }
  2159. const dataSource = ref('');
  2160. const getDataSource = async (e) => {
  2161. dataSource.value = '';
  2162. if (e === 'excel') {
  2163. let data = await importExcel();
  2164. if (Array.isArray(data) && Array.isArray(data[0])) {
  2165. meta2d.setValue({ id: props.pen.id, data: data, styles: [], mergeCells: [], colHeaders: false }, { doEvent: false });
  2166. } else {
  2167. MessagePlugin.warning($t("数据格式必需是二维数组"));
  2168. }
  2169. } else if (e === 'csv') {
  2170. let data = await importCSV();
  2171. if (Array.isArray(data) && Array.isArray(data[0])) {
  2172. meta2d.setValue({ id: props.pen.id, data: data, styles: [], mergeCells: [], colHeaders: false }, { doEvent: false });
  2173. } else {
  2174. MessagePlugin.warning($t("数据格式必需是二维数组"));
  2175. }
  2176. } else if (e === 'api') {
  2177. apiDialog.show = true;
  2178. } else if (e === 'sql') {
  2179. // getSqlTree();
  2180. // sqlDialog.show = true;
  2181. // props.pen.sql = true;
  2182. sqlDialog.value.dbid = props.pen.dbid;
  2183. sqlDialog.value.sql = props.pen.sql||'-- eg:SELECT * FROM "directory"';
  2184. sqlDialog.value.show = true;
  2185. sqlDialog.value.current = props.pen.pagination?.current;
  2186. sqlDialog.value.pageSize = props.pen.pagination?.pageSize;
  2187. sqlList.value = await getSqlSourceList();
  2188. }
  2189. }
  2190. const sqlDialog = ref({
  2191. show: false,
  2192. dbid: '',
  2193. sql:'',
  2194. method: 'list',
  2195. pageSize: 10,
  2196. current: 1,
  2197. });
  2198. const sqlList = ref([]);
  2199. const sqlChange = (sql)=>{
  2200. props.pen.sql = sql;
  2201. }
  2202. const sqlConnect =async ()=>{
  2203. // console.log('value',sqlDialog.value);
  2204. // if(sqlDialog.value.method === 'exec'){
  2205. // let data:any = await doSqlCode({method:'exec',dbid:sqlDialog.value.dbid,sql: sqlDialog.value.sql});
  2206. // console.log("data",data);
  2207. // }else if(sqlDialog.value.method === 'list'){
  2208. props.pen.sql = sqlDialog.value.sql;
  2209. props.pen.dbid = sqlDialog.value.dbid;
  2210. let sql = sqlDialog.value.sql+' LIMIT '+(sqlDialog.value.pageSize||20)+(sqlDialog.value.current>1?(' OFFSET '+(sqlDialog.value.current-1)*sqlDialog.value.pageSize):'');
  2211. // }
  2212. let data:any =await doSqlCode({method:'list',dbid:props.pen.dbid,sql});
  2213. if(data.error){
  2214. MessagePlugin.error(data.detail);
  2215. }else{
  2216. if(data.length){
  2217. let columns = [];
  2218. let colWidth = 0;
  2219. for(let key in data[0]){
  2220. if(key === 'id'||key === '_id'){
  2221. columns.unshift({
  2222. colKey: key,
  2223. text: key,
  2224. });
  2225. }else{
  2226. columns.push({
  2227. colKey: key,
  2228. text: key,
  2229. });
  2230. }
  2231. let len = data[0][key].length*10
  2232. if(len>colWidth){
  2233. colWidth = len;
  2234. }
  2235. }
  2236. if(colWidth>200){
  2237. colWidth = 200;
  2238. }
  2239. let _data = [];
  2240. data.forEach((item)=>{
  2241. // _data.push(Object.values(item))
  2242. let itemData = [];
  2243. columns.forEach((col)=>{
  2244. itemData.push(item[col.colKey]);
  2245. });
  2246. _data.push(itemData);
  2247. });
  2248. meta2d.setValue({
  2249. id:props.pen.id,
  2250. data: _data,
  2251. columns,
  2252. styles:[],
  2253. mergeCells:[],
  2254. colWidth,
  2255. sqlList:[],
  2256. whiteSpace:"nowrap",
  2257. ellipsis:true
  2258. });
  2259. MessagePlugin.success("导入成功");
  2260. sqlDialog.value.show = false;
  2261. }
  2262. }
  2263. }
  2264. const changeColumns = (e)=>{
  2265. props.pen.columns.forEach((item)=>{
  2266. if(e.includes(item.colKey)){
  2267. item.edited = false;
  2268. }else{
  2269. delete item.edited;
  2270. }
  2271. });
  2272. }
  2273. const apiDialog = reactive<any>({
  2274. show: false,
  2275. });
  2276. const onConfirmAPI = async () => {
  2277. if (!apiDialog.url) {
  2278. MessagePlugin.warning($t('请输入API地址'));
  2279. return;
  2280. }
  2281. const res: Response = await fetch(apiDialog.url, {
  2282. headers: apiDialog.headers,
  2283. method: apiDialog.method || 'GET',
  2284. body: apiDialog.method === 'GET' ? undefined : JSON.stringify(apiDialog.body),
  2285. });
  2286. let data: any = JSON.parse(await res.text());
  2287. if (data.data) {
  2288. data = data.data;
  2289. }
  2290. if (data.list) {
  2291. data = data.list;
  2292. }
  2293. if (Array.isArray(data)) {
  2294. if (Array.isArray(data[0])) {
  2295. } else {
  2296. let _data = [];
  2297. data.forEach((item) => {
  2298. _data.push(Object.values(item));
  2299. })
  2300. data = _data;
  2301. }
  2302. if (Array.isArray(data) && Array.isArray(data[0])) {
  2303. meta2d.setValue({ id: props.pen.id, data: data, styles: [], mergeCells: [], colHeaders: false }, { doEvent: false });
  2304. MessagePlugin.success($t("导入成功"));
  2305. } else {
  2306. MessagePlugin.warning($t("数据格式必需是二维数组"));
  2307. }
  2308. apiDialog.show = false;
  2309. } else {
  2310. MessagePlugin.warning($t("数据格式必需是数组类型"));
  2311. }
  2312. };
  2313. const leaderPen = ref(null);
  2314. const watcher = watch(
  2315. () => props.pen.id,
  2316. async () => {
  2317. leaderPen.value = meta2d.store.pens[props.pen.formId];
  2318. }
  2319. );
  2320. const moreDataDialog = reactive({
  2321. show:false
  2322. })
  2323. onMounted(() => {
  2324. meta2d.on('click',getPenData);
  2325. leaderPen.value = meta2d.store.pens[props.pen.formId];
  2326. getPenData({});
  2327. });
  2328. const old = globalThis.uiVersion === 'v20250208';
  2329. onUnmounted(() => {
  2330. meta2d.off('click',getPenData);
  2331. watcher();
  2332. clearInterval(timer);
  2333. });
  2334. </script>
  2335. <style lang="postcss" scoped>
  2336. .props {
  2337. height: 100%;
  2338. .grid {
  2339. grid-template-columns: 80px 110px 75px;
  2340. padding: 0 12px;
  2341. &.head {
  2342. background: var(--color-background-input);
  2343. line-height: 36px;
  2344. margin-bottom: 6px;
  2345. .title {
  2346. line-height: 36px;
  2347. }
  2348. }
  2349. }
  2350. .blank {
  2351. height: 70%;
  2352. img {
  2353. padding: 16px;
  2354. opacity: 0.9;
  2355. }
  2356. }
  2357. .c-titile{
  2358. margin-top: 12px;
  2359. color: var(--color-title);
  2360. font-size: 12px;
  2361. font-weight: 700;
  2362. }
  2363. .c-head{
  2364. &::before{
  2365. width: 0%;
  2366. }
  2367. color: var(--color);
  2368. font-weight: 700;
  2369. }
  2370. .real-times{
  2371. .label {
  2372. width: fix-content;
  2373. font-size: 10px;
  2374. line-height: 28px;
  2375. color: var(--color-desc);
  2376. overflow: hidden;
  2377. white-space: nowrap;
  2378. text-overflow: ellipsis;
  2379. }
  2380. }
  2381. .value {
  2382. padding-right: 8px;
  2383. display: flex;
  2384. align-items: center;
  2385. justify-content: space-between;
  2386. svg {
  2387. flex-shrink: 0;
  2388. margin-right: 4px;
  2389. }
  2390. & > div {
  2391. /* width: 72px; */
  2392. &.t-switch {
  2393. width: fit-content;
  2394. margin-left: 4px;
  2395. }
  2396. }
  2397. :deep(.t-input) {
  2398. padding-left: 4px;
  2399. height: 26px;
  2400. border-color: transparent;
  2401. &:hover {
  2402. border-color: var(--color-primary);
  2403. }
  2404. }
  2405. }
  2406. .actions {
  2407. text-align: right;
  2408. padding-right: 2px;
  2409. }
  2410. .data-list {
  2411. :deep(.t-table__header--fixed:not(.t-table__header--multiple) > tr > th) {
  2412. background: none;
  2413. }
  2414. :deep(.t-table__pagination) {
  2415. padding-bottom: 0;
  2416. }
  2417. }
  2418. /* .form-item{
  2419. height: 32px;
  2420. } */
  2421. }
  2422. .body {
  2423. :deep(.t-collapse.t--border-less) {
  2424. .t-collapse-panel__header {
  2425. border-top: none;
  2426. /* border-bottom: 1px solid var(--td-border-level-1-color); */
  2427. padding: 8px 0;
  2428. .t-input {
  2429. border: none;
  2430. padding-left: 0;
  2431. font-size: 14px;
  2432. }
  2433. }
  2434. .t-collapse-panel__content {
  2435. padding: 8px 0;
  2436. }
  2437. }
  2438. .title {
  2439. position: relative;
  2440. margin: 8px 0;
  2441. :deep(.t-input) {
  2442. border-color: var(--color-background-input);
  2443. border-radius: 0;
  2444. border-left: none;
  2445. border-top: none;
  2446. border-right: none;
  2447. padding-left: 0;
  2448. padding-bottom: 8px;
  2449. font-size: 14px;
  2450. &:hover {
  2451. border-color: var(--color-border-input);
  2452. }
  2453. }
  2454. }
  2455. .head {
  2456. margin-top: 10px;
  2457. }
  2458. .banner {
  2459. background-color: var(--color-background-input);
  2460. padding: 0 12px;
  2461. }
  2462. }
  2463. </style>
  2464. <style lang="postcss">
  2465. .t-drawer__mask{
  2466. /* background-color: #fff0; */
  2467. }
  2468. .t-drawer__body{
  2469. padding:0px;
  2470. }
  2471. </style>