PenDatas.vue 75 KB

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