PenDatas.vue 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631
  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. v-model:value="penId"
  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 penId = ref('');
  1179. const changeID = (value: any) => {
  1180. if (!value) {
  1181. // initPenData();
  1182. penId.value = props.pen.id;
  1183. MessagePlugin.error('id 不能为空');
  1184. return;
  1185. }
  1186. const oldID: string = props.pen.id;
  1187. try {
  1188. meta2d.changePenId(oldID, value);
  1189. penId.value = props.pen.id;
  1190. // initPenData();
  1191. } catch (error) {
  1192. console.warn(error.message);
  1193. MessagePlugin.error('id 修改失败,请检查 id 是否重复');
  1194. return;
  1195. }
  1196. };
  1197. onBeforeMount(() => {
  1198. // realTimesOptions - 扩展的动态数据下拉列表
  1199. if (props.pen.realTimesOptions) {
  1200. options.value[options.value.length - 1].divider = true;
  1201. options.value.push(...props.pen.realTimesOptions);
  1202. }
  1203. timer = setInterval($forceUpdate, 1000);
  1204. });
  1205. const moreVisible = ref(false);
  1206. const getProp = (e)=>{
  1207. if (!props.pen.realTimes) {
  1208. props.pen.realTimes = [];
  1209. }
  1210. props.pen.realTimes.push({
  1211. label: e.label,
  1212. key: e.value,
  1213. type: e.type,
  1214. });
  1215. moreVisible.value = false;
  1216. }
  1217. const addRealTime = (e: any) => {
  1218. if(e.value === 'more'){
  1219. moreVisible.value = true;
  1220. return;
  1221. }
  1222. if (e.keywords || e.value === 'text') {
  1223. if (!props.pen.realTimes) {
  1224. props.pen.realTimes = [];
  1225. }
  1226. props.pen.realTimes.push({
  1227. label: e.content,
  1228. key: e.value,
  1229. type: e.type,
  1230. keywords: e.keywords,
  1231. });
  1232. return;
  1233. }
  1234. addDataDialog.header = $t('添加动态数据');
  1235. addDataDialog.data = {
  1236. label: e.content,
  1237. key: e.value,
  1238. type: e.type,
  1239. keywords: e.keywords,
  1240. };
  1241. addDataDialog.data.pen = undefined;
  1242. if (e.keywords) {
  1243. addDataDialog.data.label = '';
  1244. }
  1245. addDataDialog.show = true;
  1246. };
  1247. const onKeyBlur = () => {
  1248. if (addDataDialog.data) {
  1249. let value = getter(props.pen, addDataDialog.data.key);
  1250. if (value) {
  1251. setter(addDataDialog.data, 'value', value);
  1252. } else {
  1253. addDataDialog.data.value = null;
  1254. }
  1255. }
  1256. };
  1257. const onChangeLabel = () => {
  1258. if (!addDataDialog.data.key) {
  1259. addDataDialog.data.key = addDataDialog.data.label;
  1260. }
  1261. };
  1262. const onConfirmData = () => {
  1263. const pen = addDataDialog.data.pen || props.pen;
  1264. if (!pen.realTimes) {
  1265. pen.realTimes = [];
  1266. }
  1267. if (!addDataDialog.data.label || !addDataDialog.data.key) {
  1268. MessagePlugin.error($t('数据名或属性名不能为空!'));
  1269. return;
  1270. }
  1271. delete addDataDialog.data.pen;
  1272. if (addDataDialog.header === $t('添加动态数据')) {
  1273. const found = pen.realTimes.findIndex((item: any) => {
  1274. return item.key === addDataDialog.data.key;
  1275. });
  1276. if (found > -1) {
  1277. MessagePlugin.error($t('已经存在相同属性数据!'));
  1278. return;
  1279. }
  1280. if(pen[addDataDialog.data.key] === undefined && addDataDialog.data.key.indexOf('.') !== -1){
  1281. pen[addDataDialog.data.key] = getter(pen, addDataDialog.data.key);
  1282. }
  1283. pen.realTimes.push(addDataDialog.data);
  1284. }
  1285. addDataDialog.show = false;
  1286. };
  1287. const onMenuMore = (e: any, item: any, i: number) => {
  1288. switch (e.value) {
  1289. case 'edit':
  1290. addDataDialog.header = $t('编辑动态数据');
  1291. setter(item, 'value', getter(props.pen, item.key));
  1292. addDataDialog.data = item;
  1293. addDataDialog.show = true;
  1294. break;
  1295. case 'delete':
  1296. props.pen.realTimes.splice(i, 1);
  1297. meta2d.initBinds();
  1298. break;
  1299. default:
  1300. break;
  1301. }
  1302. };
  1303. const onBind = (item: any) => {
  1304. dataBindDialog.data = item;
  1305. dataBindDialog.input = '';
  1306. dataBindDialog.selectedIds = [];
  1307. if (item.bind && item.bind.id) {
  1308. dataBindDialog.selectedIds.push(item.bind.id);
  1309. }
  1310. dataBindDialog.show = true;
  1311. if(old){
  1312. getDataset();
  1313. }else{
  1314. // getIotTree();
  1315. // getSqlTree();
  1316. getBindTreeData();
  1317. }
  1318. };
  1319. const bindTreeData = ref<any>([]);
  1320. const getBindTreeData = ()=>{
  1321. let data = [];
  1322. const iotTree = meta2d.store.data.iot?.tree || [];
  1323. if(iotTree.length){
  1324. iotTree.forEach((item:any)=>{
  1325. item.checkable = false;
  1326. });
  1327. data.push({
  1328. label: '物联网平台',
  1329. value: 'iot',
  1330. checkable: false,
  1331. children: iotTree,
  1332. });
  1333. }
  1334. const sqlTree = meta2d.store.data.sqls || [];
  1335. if(sqlTree.length){
  1336. data.push({
  1337. label: 'sql数据源',
  1338. value: 'sql',
  1339. checkable: false,
  1340. children: sqlTree,
  1341. });
  1342. }
  1343. const networks = meta2d.store.data.networks || [];
  1344. if(networks.length){
  1345. networks.forEach((item:any)=>{
  1346. item.checkable = false;
  1347. });
  1348. const mqttNets = meta2d.store.data.networks.filter((item)=>item.protocol==='mqtt');
  1349. const wsNets = meta2d.store.data.networks.filter((item)=>item.protocol==='websocket');
  1350. const httpNets = meta2d.store.data.networks.filter((item)=>item.protocol==='http');
  1351. const SSENets = meta2d.store.data.networks.filter((item)=>item.protocol==='SSE');
  1352. if(mqttNets.length){
  1353. data.push({
  1354. label: 'MQTT',
  1355. value: 'network',
  1356. checkable: false,
  1357. children: mqttNets,
  1358. });
  1359. }
  1360. if(wsNets.length){
  1361. data.push({
  1362. label: 'Websocket',
  1363. value: 'network',
  1364. checkable: false,
  1365. children: wsNets,
  1366. });
  1367. }
  1368. if(httpNets.length){
  1369. data.push({
  1370. label: 'HTTP',
  1371. value: 'network',
  1372. checkable: false,
  1373. children: httpNets,
  1374. });
  1375. }
  1376. if(SSENets.length){
  1377. data.push({
  1378. label: 'SSE',
  1379. value: 'network',
  1380. checkable: false,
  1381. children: SSENets,
  1382. });
  1383. }
  1384. }
  1385. bindTreeData.value = data;
  1386. }
  1387. const bindSearch = ref('');
  1388. const bindFilter = ref(null);
  1389. const onSearchBind = () => {
  1390. bindFilter.value = bindSearch.value ? (node) => { return node.data.label?.indexOf(bindSearch.value) >= 0|| node.data.value?.indexOf(bindSearch.value) >= 0 }: null;
  1391. };
  1392. const bindValue = ref('');
  1393. const doBind = (value:any,context:any)=>{
  1394. // bindValue.value = [context.node.value];
  1395. dataBindDialog.selectedIds = [context.node.value];
  1396. dataBindDialog.data.bind = {
  1397. id: context.node.value,
  1398. label: context.node.data._label|| context.node.data.label,
  1399. class: context.node.data.class,
  1400. token: context.node.data.token,
  1401. }
  1402. meta2d.initBinds();
  1403. }
  1404. const onSearchDataset = () => {
  1405. debounce(getDataset, 300);
  1406. };
  1407. const getDataset = async () => {
  1408. // @ts-ignore
  1409. const data: Meta2dBackData = meta2d.data();
  1410. dataBindDialog.sqls = data.sqls;
  1411. if (!data.dataset) {
  1412. return;
  1413. }
  1414. dataBindDialog.dataset = data.dataset.devices;
  1415. dataBindDialog.datasetList = data.dataset.devices;
  1416. if(dataBindDialog.device || dataBindDialog.input){
  1417. dataBindDialog.dataset = dataBindDialog.datasetList.filter(
  1418. (item: any) => {
  1419. if (dataBindDialog.device && item.device !== dataBindDialog.device) {
  1420. return;
  1421. }
  1422. return (
  1423. searchPinyin(item.label, dataBindDialog.input) ||
  1424. item.id.indexOf(dataBindDialog.input) > -1
  1425. );
  1426. }
  1427. );
  1428. }
  1429. query.total = dataBindDialog.dataset.length;
  1430. return;
  1431. dataBindDialog.loading = true;
  1432. if (data.dataset.url) {
  1433. const ret: any = await axios.get(data.dataset.url, {
  1434. params: {
  1435. device: dataBindDialog.device || '',
  1436. q: dataBindDialog.input,
  1437. current: query.current,
  1438. pageSize: query.pageSize,
  1439. },
  1440. });
  1441. dataBindDialog.dataset = ret;
  1442. dataBindDialog.url = true;
  1443. query.total = ret.total;
  1444. console.log( dataBindDialog.dataset,
  1445. query.total);
  1446. } else {
  1447. dataBindDialog.url = false;
  1448. if (!dataBindDialog.datasetList) {
  1449. const ret:any = await axios.post(`/api/data/datasource/get`, {
  1450. id: data.dataset.id,
  1451. });
  1452. if (ret?.data?.devices) {
  1453. data.dataset.data = ret.data.devices;
  1454. dataBindDialog.datasetList = data.dataset.data;
  1455. const set = new Set<string>();
  1456. for (const item of dataBindDialog.datasetList) {
  1457. set.add(item.device);
  1458. }
  1459. dataBindDialog.devices = Array.from(set);
  1460. } else {
  1461. dataBindDialog.dataset = [];
  1462. dataBindDialog.devices = [];
  1463. return;
  1464. }
  1465. }
  1466. if (dataBindDialog.device || dataBindDialog.input) {
  1467. dataBindDialog.dataset = dataBindDialog.datasetList.filter(
  1468. (item: any) => {
  1469. if (dataBindDialog.device && item.device !== dataBindDialog.device) {
  1470. return;
  1471. }
  1472. return (
  1473. searchPinyin(item.label, dataBindDialog.input) ||
  1474. item.id.indexOf(dataBindDialog.input) > -1
  1475. );
  1476. }
  1477. );
  1478. query.total = dataBindDialog.datasetList.length;
  1479. } else {
  1480. dataBindDialog.dataset = [...dataBindDialog.datasetList];
  1481. query.total = dataBindDialog.datasetList.length;
  1482. }
  1483. console.log("dataBindDialog",dataBindDialog,dataBindDialog);
  1484. }
  1485. dataBindDialog.loading = false;
  1486. };
  1487. const onChangePagination = (pageInfo: any) => {
  1488. // router.push({
  1489. // path: route.path,
  1490. // query: { current: pageInfo.current, pageSize: pageInfo.pageSize },
  1491. // });
  1492. query.current = pageInfo.current;
  1493. query.pageSize = pageInfo.pageSize;
  1494. getDataset();
  1495. };
  1496. const onSelectBindsChange = (value: string[], options: any) => {
  1497. if (options.type === 'check') {
  1498. dataBindDialog.selectedIds = value;
  1499. dataBindDialog.data.bind = toRaw(options.selectedRowData[0]);
  1500. if(dataBindDialog.data.bind.id){
  1501. dataBindDialog.data.enableMock = false;
  1502. }
  1503. dataBindDialog.data.mock = dataBindDialog.data.bind.mock;
  1504. dataBindDialog.data.type = dataBindDialog.data.bind.type;
  1505. doBindInit();
  1506. } else if (options.type === 'uncheck') {
  1507. dataBindDialog.selectedIds = [];
  1508. dataBindDialog.data.bind = {};
  1509. }
  1510. };
  1511. const doBindInit = () => {
  1512. let { id } = dataBindDialog.data;
  1513. if (props.pen.name === 'echarts' && id?.includes('echarts.option.series')) {
  1514. const { replaceMode } = props.pen.echarts;
  1515. const { xAxis } = props.pen.echarts.option;
  1516. let beforeV = getter(props.pen, id);
  1517. if (Array.isArray(beforeV) && replaceMode === 0) {
  1518. //追加
  1519. setter(props.pen, id, []);
  1520. let _key = 'echarts.option.xAxis.data';
  1521. if (Array.isArray(xAxis) && xAxis.length) {
  1522. _key = 'echarts.option.xAxis.0.data';
  1523. }
  1524. setter(props.pen, _key, []);
  1525. }
  1526. }
  1527. };
  1528. const dataBindonConfirm = () => {
  1529. dataBindDialog.show = false;
  1530. dataBindDialog.datasetList = undefined;
  1531. meta2d.initBinds();
  1532. };
  1533. const onRemoveBind = () => {
  1534. dataBindDialog.selectedIds = [];
  1535. dataBindDialog.data.bind = undefined;
  1536. };
  1537. const getBindsDesc = (item: any) => {
  1538. if (item.bind?.label) {
  1539. return item.bind.label;
  1540. }
  1541. if (item.enableMock && item.mock) {
  1542. return item.mock;
  1543. }
  1544. return $t('绑定变量');
  1545. };
  1546. const { selections } = useSelection();
  1547. const changeValue = (prop: string) => {
  1548. updatePen(props.pen, prop);
  1549. selections.pen[prop] = getter(props.pen, prop);
  1550. if (prop === 'iframe') {
  1551. getThumbImg();
  1552. }
  1553. if(prop === 'apiEnable'){
  1554. meta2d.penNetwork(props.pen);
  1555. meta2d.connectNetwork();
  1556. }
  1557. };
  1558. const onTrigger = (item: any) => {
  1559. if (!item.triggers) {
  1560. item.triggers = [];
  1561. }
  1562. triggersDialog.openedCollapses = [0];
  1563. triggersDialog.data = item;
  1564. triggersDialog.show = true;
  1565. penTree.value = getPenTree();
  1566. };
  1567. const onAddTrigger = () => {
  1568. const i = triggersDialog.data.triggers.length;
  1569. triggersDialog.data.triggers.push({
  1570. name: $t(`状态`)+ (i + 1),
  1571. conditionType: 'and',
  1572. conditions: [],
  1573. actions: [],
  1574. });
  1575. triggersDialog.openedCollapses.push(i);
  1576. };
  1577. const addTriggerCondition = (trigger: any) => {
  1578. trigger.conditions.push({
  1579. type: '',
  1580. operator: '=',
  1581. valueType: '',
  1582. });
  1583. };
  1584. const onChangeTriggerTarget = (c: any) => {
  1585. /*
  1586. c.targetProps = [
  1587. {
  1588. value: 'x',
  1589. label: 'X',
  1590. },
  1591. {
  1592. value: 'y',
  1593. label: 'Y',
  1594. },
  1595. {
  1596. value: 'width',
  1597. label: $t('宽'),
  1598. },
  1599. {
  1600. value: 'height',
  1601. label: $t('高'),
  1602. },
  1603. {
  1604. value: 'visible',
  1605. label: $t('显示'),
  1606. },
  1607. {
  1608. value: 'text',
  1609. label: $t('文字'),
  1610. },
  1611. {
  1612. value: 'progress',
  1613. label: $t('进度'),
  1614. },
  1615. {
  1616. value: 'showChild',
  1617. label: $t('状态'),
  1618. },
  1619. {
  1620. value: 'rotate',
  1621. label: $t('旋转'),
  1622. },
  1623. ];*/
  1624. const target: any = meta2d.findOne(c.target);
  1625. if (target && target.realTimes) {
  1626. for (const item of target.realTimes) {
  1627. const found = cProps.findIndex((elem: any) => elem.value === item.key);
  1628. if (found < 0) {
  1629. cProps.push({
  1630. value: item.key,
  1631. label: item.label,
  1632. });
  1633. }
  1634. }
  1635. }
  1636. };
  1637. const onInput = (item: any) => {
  1638. item.label = item.value;
  1639. };
  1640. const editObject = (item: any) => {
  1641. ojbectDialog.data = props.pen[item.key];
  1642. ojbectDialog.item = item;
  1643. ojbectDialog.pen = undefined;
  1644. ojbectDialog.show = true;
  1645. };
  1646. const onOkEditOjbect = () => {
  1647. if (ojbectDialog.data) {
  1648. if (
  1649. ojbectDialog.item.type === 'array' &&
  1650. !Array.isArray(ojbectDialog.data)
  1651. ) {
  1652. MessagePlugin.error($t('请输入数组格式数据'));
  1653. return;
  1654. }
  1655. const pen = ojbectDialog.pen || props.pen;
  1656. pen[ojbectDialog.item.key] = deepClone(ojbectDialog.data);
  1657. updatePen(
  1658. pen,
  1659. ojbectDialog.item.key
  1660. );
  1661. ojbectDialog.show = false;
  1662. } else {
  1663. MessagePlugin.error($t('请输入严格的JSON格式数据'));
  1664. }
  1665. };
  1666. const propsDialog = reactive<any>({
  1667. show: false,
  1668. });
  1669. // const changeValue = (prop: string) => {
  1670. // updatePen(props.pen, prop);
  1671. // selections.pen[prop] = getter(props.pen, prop);
  1672. // if (prop === 'iframe$t(') {
  1673. // getThumbImg();
  1674. // }
  1675. // };
  1676. const getThumbImg = async () => {
  1677. //改iframe地址后
  1678. let arr = props.pen.iframe.split('?');
  1679. let id = queryURLParams(arr[1]).id;
  1680. if (!id) {
  1681. return;
  1682. }
  1683. // let projection = {
  1684. // image: 1,
  1685. // _id: 1,
  1686. // name: 1,
  1687. // };
  1688. let projection = 'image,id,name';
  1689. let res: any;
  1690. if (arr[0].indexOf('2d.le5le') !== -1||arr[0].indexOf('le5le.com/2d') !== -1) {
  1691. res = await getLe5le2d(id, projection);
  1692. } else if (arr[0].indexOf('v.le5le') !== -1||arr[0].indexOf('le5le.com/v') !== -1) {
  1693. res = await getLe5leV(id, projection);
  1694. } else if (arr[0].indexOf('3d.le5le') !== -1||arr[0].indexOf('le5le.com/3d') !== -1) {
  1695. res = await getLe5le3d(id, projection);
  1696. }
  1697. if (res) {
  1698. props.pen.thumbImg = res.image;
  1699. props.pen.calculative&&(props.pen.calculative.img = null);
  1700. }
  1701. props.pen.onRenderPenRaw?.(props.pen);
  1702. };
  1703. const preShowPropsEdit = (key: string) => {
  1704. showPropsEdit({
  1705. key,
  1706. // item: {
  1707. // key,
  1708. type: 'code',
  1709. label: key,
  1710. placeholder: '支持设置动态参数,例如:{"Authorization": "Bearer ${token}"}',
  1711. // },
  1712. });
  1713. };
  1714. const showPropsEdit = (item: any) => {
  1715. propsDialog.key = item.key;
  1716. propsDialog.header = `${item.label}(${item.key})`;
  1717. propsDialog.value = props.pen[item.key];
  1718. propsDialog.placeholder = item.placeholder;
  1719. propsDialog.show = true;
  1720. };
  1721. const onOkPropsEdit = () => {
  1722. if (!propsDialog.value) {
  1723. MessagePlugin.error($t('数据不满足json格式'));
  1724. return;
  1725. }
  1726. props.pen[propsDialog.key] = propsDialog.value;
  1727. updatePen(props.pen, propsDialog.key);
  1728. propsDialog.show = false;
  1729. };
  1730. const codeChange = (e:any,a:any)=>{
  1731. a.fn = null;
  1732. }
  1733. const valueChange = (e,c:any)=>{
  1734. c.value= changeType(e);
  1735. }
  1736. const drawer = reactive<any>({
  1737. visible: false,
  1738. });
  1739. const showDrawer = (item:any)=>{
  1740. drawer.key = item.key;
  1741. drawer.header = `${item.label}(${item.key})`;
  1742. drawer.value = deepClone(props.pen[item.key]);
  1743. drawer.placeholder = item.placeholder;
  1744. drawer.randomkey = s8();//props.pen.id;
  1745. drawer.visible = true;
  1746. }
  1747. const onConfirmDrawer = () => {
  1748. if (!drawer.value) {
  1749. MessagePlugin.error($t('数据不满足json格式'));
  1750. return;
  1751. }
  1752. props.pen[drawer.key] = drawer.value;
  1753. updatePen(props.pen, drawer.key);
  1754. };
  1755. const childrenPens = ref([]);
  1756. watch(
  1757. () => props.pen.id,
  1758. (newValue) => {
  1759. penId.value = props.pen.id;
  1760. childrenPens.value = [];
  1761. // childrenKey.value = s8();
  1762. if(props.pen.children?.length){
  1763. childrenPens.value = getAllChildren(meta2d.store.pens[props.pen.id],meta2d.store);
  1764. }
  1765. getRealTimesTrigger();
  1766. }
  1767. );
  1768. onMounted(() => {
  1769. penId.value = props.pen.id;
  1770. childrenPens.value = [];
  1771. if(props.pen.children?.length){
  1772. childrenPens.value = getAllChildren(meta2d.store.pens[props.pen.id],meta2d.store);
  1773. }
  1774. getRealTimesTrigger();
  1775. });
  1776. const getRealTimesTrigger = ()=>{
  1777. if(!props.pen.triggers?.length){
  1778. return;
  1779. }
  1780. props.pen.realTimes?.forEach((real)=>{
  1781. let shouldBreak = false;
  1782. props.pen.triggers.forEach((item,index)=>{
  1783. if (shouldBreak) return;
  1784. item.status.forEach((_item)=>{
  1785. const trigger = _item.conditions.filter((i)=>i.key===real.key);
  1786. if(trigger?.length){
  1787. real.trigger = index;
  1788. shouldBreak = true;
  1789. }
  1790. })
  1791. });
  1792. })
  1793. }
  1794. const gotoTrigger = (i:any)=>{
  1795. if(i===undefined){
  1796. return;
  1797. }
  1798. emit('tabchange',i);
  1799. }
  1800. const changeCValue = (prop: string, pen:Pen)=>{
  1801. updatePen(pen, prop);
  1802. selections.pen[prop] = getter(pen, prop);
  1803. if (prop === 'iframe') {
  1804. getThumbImg();
  1805. }
  1806. if(prop === 'apiEnable'){
  1807. meta2d.penNetwork(pen);
  1808. meta2d.connectNetwork();
  1809. }
  1810. }
  1811. const editCObject = (item: any, pen:Pen) => {
  1812. ojbectDialog.data = pen[item.key];
  1813. ojbectDialog.pen = pen;
  1814. ojbectDialog.item = item;
  1815. ojbectDialog.show = true;
  1816. };
  1817. const addCRealTime = (e: any, pen:Pen) => {
  1818. if (e.keywords || e.value === 'text') {
  1819. if (!pen.realTimes) {
  1820. pen.realTimes = [];
  1821. }
  1822. pen.realTimes.push({
  1823. label: e.content,
  1824. key: e.value,
  1825. type: e.type,
  1826. keywords: e.keywords,
  1827. });
  1828. return;
  1829. }
  1830. addDataDialog.header = $t('添加动态数据');
  1831. addDataDialog.data = {
  1832. label: e.content,
  1833. key: e.value,
  1834. type: e.type,
  1835. keywords: e.keywords,
  1836. };
  1837. addDataDialog.data.pen = pen;
  1838. if (e.keywords) {
  1839. addDataDialog.data.label = '';
  1840. }
  1841. addDataDialog.show = true;
  1842. };
  1843. const onCMenuMore = (e: any, item: any, i: number, pen:any)=>{
  1844. switch (e.value) {
  1845. case 'edit':
  1846. addDataDialog.header = $t('编辑动态数据');
  1847. setter(item, 'value', getter(pen, item.key));
  1848. addDataDialog.data = item;
  1849. addDataDialog.data.pen = pen;
  1850. addDataDialog.show = true;
  1851. break;
  1852. case 'delete':
  1853. pen.realTimes.splice(i, 1);
  1854. meta2d.initBinds();
  1855. break;
  1856. case 'custom':
  1857. if(!pen.triggers){
  1858. pen.triggers = [];
  1859. }
  1860. pen.triggers.push({
  1861. name: $t(`状态场景`) + (pen.triggers.length + 1),
  1862. status:[
  1863. {
  1864. name:$t('状态1'),
  1865. conditionType: 'and',
  1866. conditions: [{
  1867. type:'',
  1868. operator: "=",
  1869. key: item.key,//"text",
  1870. keyLabel: item.label,
  1871. value: undefined
  1872. }],
  1873. actions: [],
  1874. },
  1875. {
  1876. name:$t('状态2'),
  1877. conditionType: 'and',
  1878. conditions: [],
  1879. actions: [],
  1880. }
  1881. ],
  1882. });
  1883. meta2d.active(meta2d.find(pen.id));
  1884. emit('tabchange',pen.triggers.length-1);
  1885. break;
  1886. case 'view':
  1887. meta2d.active(meta2d.find(pen.id));
  1888. emit('tabchange',-1);
  1889. default:
  1890. break;
  1891. }
  1892. }
  1893. const emit = defineEmits(['tabchange']);
  1894. const triggerOptions = computed(() => {
  1895. const options = [
  1896. {
  1897. value: 'custom',
  1898. content: $t('自定义状态'),
  1899. divider: true
  1900. },
  1901. {
  1902. value: 'warning',
  1903. content: $t('条件告警'),
  1904. },
  1905. {
  1906. value: 'warningAnimate',
  1907. content: $t('条件动画'),
  1908. divider: true
  1909. },
  1910. {
  1911. value: 'mock',
  1912. content: '本地调试',
  1913. },
  1914. {
  1915. value: 'edit',
  1916. content: $t('编辑'),
  1917. },
  1918. {
  1919. value: 'delete',
  1920. content: $t('移除'),
  1921. },
  1922. ]
  1923. if( props.pen.name === "combine"&& props.pen.showChild !== undefined){
  1924. options.splice(1,0,
  1925. {
  1926. value: 'switch',
  1927. content: $t('状态切换'),
  1928. });
  1929. }
  1930. return options
  1931. });
  1932. const addTrigger = (item:any, e:any, i:any) => {
  1933. if(!['edit', 'delete'].includes(e.value)){
  1934. if(!props.pen.triggers){
  1935. props.pen.triggers = [];
  1936. }
  1937. }
  1938. switch (e.value) {
  1939. case 'edit':
  1940. addDataDialog.header = $t('编辑动态数据');
  1941. setter(item, 'value', getter(props.pen, item.key));
  1942. addDataDialog.data = item;
  1943. addDataDialog.show = true;
  1944. break;
  1945. case 'delete':
  1946. props.pen.realTimes.splice(i, 1);
  1947. meta2d.initBinds();
  1948. break;
  1949. case 'switch':
  1950. props.pen.triggers.push({
  1951. "name": "状态切换",
  1952. "status": [
  1953. {
  1954. "name": $t("满足条件,切换到状态1"),
  1955. "conditionType": "and",
  1956. "conditions": [
  1957. {
  1958. "type": "",
  1959. "operator": ">=",
  1960. "key": item.key,
  1961. "keyLabel": item.label,
  1962. "valueType": ""
  1963. }
  1964. ],
  1965. "actions": [
  1966. {
  1967. "action": 1,
  1968. "params": "",
  1969. "value": {
  1970. "showChild": 1
  1971. },
  1972. "targetType": "id"
  1973. }
  1974. ]
  1975. },
  1976. {
  1977. "name": $t("默认显示状态"),
  1978. "conditionType": "and",
  1979. "conditions": [],
  1980. "actions": [
  1981. {
  1982. "action": 1,
  1983. "params": "",
  1984. "value": {
  1985. "showChild": 0
  1986. },
  1987. "targetType": "id"
  1988. }
  1989. ]
  1990. }
  1991. ]
  1992. });
  1993. break;
  1994. case 'warning':
  1995. props.pen.triggers.push({
  1996. "name": "条件告警",
  1997. "status": [
  1998. {
  1999. "name": $t("满足条件,触发告警"),
  2000. "conditions": [
  2001. {
  2002. "type": "",
  2003. "operator": ">=",
  2004. "valueType": "",
  2005. "key": item.key,
  2006. "keyLabel":item.label,
  2007. "value": undefined
  2008. }
  2009. ],
  2010. "actions": [
  2011. {
  2012. "action": 1,
  2013. "params": "",
  2014. "value": {
  2015. "background": "red",
  2016. },
  2017. "targetType": "id"
  2018. }
  2019. ],
  2020. "conditionType": "and"
  2021. },
  2022. {
  2023. "name": $t("恢复默认"),
  2024. "conditionType": "and",
  2025. "conditions": [],
  2026. "actions": [
  2027. {
  2028. "action": 1,
  2029. "params": "",
  2030. "value": {
  2031. "background": "#0000",
  2032. },
  2033. "targetType": "id"
  2034. }
  2035. ]
  2036. }
  2037. ]
  2038. });
  2039. break;
  2040. case 'warningAnimate':
  2041. props.pen.triggers.push({
  2042. "name": "条件动画",
  2043. "status": [
  2044. {
  2045. "name": $t("满足条件,触发动画执行"),
  2046. "conditions": [
  2047. {
  2048. "type": "",
  2049. "operator": ">=",
  2050. "valueType": "",
  2051. "key": item.key,
  2052. "keyLabel":item.label,
  2053. "value": undefined
  2054. }
  2055. ],
  2056. "actions": [
  2057. {
  2058. "action": 2,
  2059. "params": "",
  2060. "value": "",
  2061. "targetType": "id",
  2062. }
  2063. ],
  2064. "conditionType": "and"
  2065. },
  2066. {
  2067. "name": $t("恢复默认状态"),
  2068. "conditionType": "and",
  2069. "conditions": [],
  2070. "actions": [
  2071. {
  2072. "action": 4,
  2073. "params": "",
  2074. "value": "",
  2075. "targetType": "id"
  2076. }
  2077. ]
  2078. }
  2079. ]
  2080. });
  2081. break;
  2082. case 'custom':
  2083. props.pen.triggers.push({
  2084. name: $t(`状态场景`) + (props.pen.triggers.length + 1),
  2085. status:[
  2086. {
  2087. name:$t('状态1'),
  2088. conditionType: 'and',
  2089. conditions: [{
  2090. type:'',
  2091. operator: "=",
  2092. key: item.key,//"text",
  2093. keyLabel: item.label,
  2094. value: undefined
  2095. }],
  2096. actions: [],
  2097. },
  2098. {
  2099. name:$t('状态2'),
  2100. conditionType: 'and',
  2101. conditions: [],
  2102. actions: [],
  2103. }
  2104. ],
  2105. });
  2106. break;
  2107. case 'mock':
  2108. dataMockDialog.data = item;
  2109. dataMockDialog.show = true;
  2110. break;
  2111. }
  2112. if(!['edit', 'delete','mock'].includes(e.value)){
  2113. emit('tabchange',props.pen.triggers.length-1);
  2114. }
  2115. }
  2116. const cell = ref({
  2117. col:undefined,
  2118. row:undefined
  2119. });
  2120. const getPenData = (e)=>{
  2121. if(props.pen.name === 'tablePlus'){
  2122. if(props.pen.calculative.activeRow !== undefined){
  2123. cell.value.row = props.pen.calculative.activeRow;
  2124. cell.value.col = undefined;
  2125. }else if(props.pen.calculative.activeCell){
  2126. cell.value.col = props.pen.calculative.activeCell.col;
  2127. cell.value.row = props.pen.calculative.activeCell.row;
  2128. }else{
  2129. cell.value.col = undefined;
  2130. cell.value.row = undefined;
  2131. }
  2132. }
  2133. }
  2134. const onQuickBind = ()=>{
  2135. if(cell.value.row !== undefined){
  2136. if(!props.pen.realTimes){
  2137. props.pen.realTimes = [];
  2138. }
  2139. let row = cell.value.row;
  2140. if(props.pen.colHeaders){
  2141. row-=1;
  2142. }
  2143. if(cell.value.col !== undefined){
  2144. let found = props.pen.realTimes.findIndex((item:any)=>{
  2145. return item.key === `data.${cell.value.row}.${cell.value.col}`;
  2146. });
  2147. if(found === -1){
  2148. props.pen.realTimes.push({
  2149. label: `第${row+1}行,第${cell.value.col}列`,
  2150. key: `data.${row}.${cell.value.col}`,
  2151. type: 'float',
  2152. });
  2153. onBind(props.pen.realTimes[props.pen.realTimes.length-1]);
  2154. }else{
  2155. onBind(props.pen.realTimes[found]);
  2156. }
  2157. }else{
  2158. let found = props.pen.realTimes.findIndex((item:any)=>{
  2159. return item.key === `data.${cell.value.row}`;
  2160. });
  2161. if(found === -1){
  2162. props.pen.realTimes.push({
  2163. label: `第${row+1}行`,
  2164. key: `data.${row}`,
  2165. type: 'array',
  2166. });
  2167. onBind(props.pen.realTimes[props.pen.realTimes.length-1]);
  2168. }else{
  2169. onBind(props.pen.realTimes[found]);
  2170. }
  2171. }
  2172. }
  2173. }
  2174. const dataSource = ref('');
  2175. const getDataSource = async (e) => {
  2176. dataSource.value = '';
  2177. if (e === 'excel') {
  2178. let data = await importExcel();
  2179. if (Array.isArray(data) && Array.isArray(data[0])) {
  2180. meta2d.setValue({ id: props.pen.id, data: data, styles: [], mergeCells: [], colHeaders: false ,columns:[]}, { doEvent: false });
  2181. } else {
  2182. MessagePlugin.warning($t("数据格式必需是二维数组"));
  2183. }
  2184. } else if (e === 'csv') {
  2185. let data = await importCSV();
  2186. if (Array.isArray(data) && Array.isArray(data[0])) {
  2187. meta2d.setValue({ id: props.pen.id, data: data, styles: [], mergeCells: [], colHeaders: false ,columns:[]}, { doEvent: false });
  2188. } else {
  2189. MessagePlugin.warning($t("数据格式必需是二维数组"));
  2190. }
  2191. } else if (e === 'api') {
  2192. apiDialog.show = true;
  2193. } else if (e === 'sql') {
  2194. // getSqlTree();
  2195. // sqlDialog.show = true;
  2196. // props.pen.sql = true;
  2197. sqlDialog.value.dbid = props.pen.dbid;
  2198. sqlDialog.value.sql = props.pen.sql||'-- eg:SELECT * FROM "directory"';
  2199. sqlDialog.value.show = true;
  2200. sqlDialog.value.current = props.pen.pagination?.current;
  2201. sqlDialog.value.pageSize = props.pen.pagination?.pageSize;
  2202. sqlList.value = await getSqlSourceList();
  2203. }
  2204. }
  2205. const sqlDialog = ref({
  2206. show: false,
  2207. dbid: '',
  2208. sql:'',
  2209. method: 'list',
  2210. pageSize: 10,
  2211. current: 1,
  2212. });
  2213. const sqlList = ref([]);
  2214. const sqlChange = (sql)=>{
  2215. props.pen.sql = sql;
  2216. }
  2217. const sqlConnect =async ()=>{
  2218. // console.log('value',sqlDialog.value);
  2219. // if(sqlDialog.value.method === 'exec'){
  2220. // let data:any = await doSqlCode({method:'exec',dbid:sqlDialog.value.dbid,sql: sqlDialog.value.sql});
  2221. // console.log("data",data);
  2222. // }else if(sqlDialog.value.method === 'list'){
  2223. props.pen.sql = sqlDialog.value.sql;
  2224. props.pen.dbid = sqlDialog.value.dbid;
  2225. let sql = sqlDialog.value.sql+' LIMIT '+(sqlDialog.value.pageSize||20)+(sqlDialog.value.current>1?(' OFFSET '+(sqlDialog.value.current-1)*sqlDialog.value.pageSize):'');
  2226. // }
  2227. let data:any =await doSqlCode({method:'list',dbid:props.pen.dbid,sql});
  2228. if(data.error){
  2229. MessagePlugin.error(data.detail);
  2230. }else{
  2231. if(data.length){
  2232. let columns = [];
  2233. let colWidth = 0;
  2234. for(let key in data[0]){
  2235. if(key === 'id'||key === '_id'){
  2236. columns.unshift({
  2237. colKey: key,
  2238. text: key,
  2239. });
  2240. }else{
  2241. columns.push({
  2242. colKey: key,
  2243. text: key,
  2244. });
  2245. }
  2246. let len = data[0][key].length*10
  2247. if(len>colWidth){
  2248. colWidth = len;
  2249. }
  2250. }
  2251. if(colWidth>200){
  2252. colWidth = 200;
  2253. }
  2254. let _data = [];
  2255. data.forEach((item)=>{
  2256. // _data.push(Object.values(item))
  2257. let itemData = [];
  2258. columns.forEach((col)=>{
  2259. itemData.push(item[col.colKey]);
  2260. });
  2261. _data.push(itemData);
  2262. });
  2263. meta2d.setValue({
  2264. id:props.pen.id,
  2265. data: _data,
  2266. columns,
  2267. styles:[],
  2268. mergeCells:[],
  2269. colWidth,
  2270. sqlList:[],
  2271. whiteSpace:"nowrap",
  2272. ellipsis:true
  2273. });
  2274. MessagePlugin.success("导入成功");
  2275. sqlDialog.value.show = false;
  2276. }
  2277. }
  2278. }
  2279. const changeColumns = (e)=>{
  2280. props.pen.columns.forEach((item)=>{
  2281. if(e.includes(item.colKey)){
  2282. item.edited = false;
  2283. }else{
  2284. delete item.edited;
  2285. }
  2286. });
  2287. }
  2288. const apiDialog = reactive<any>({
  2289. show: false,
  2290. });
  2291. const onConfirmAPI = async () => {
  2292. if (!apiDialog.url) {
  2293. MessagePlugin.warning($t('请输入API地址'));
  2294. return;
  2295. }
  2296. const res: Response = await fetch(apiDialog.url, {
  2297. headers: apiDialog.headers,
  2298. method: apiDialog.method || 'GET',
  2299. body: apiDialog.method === 'GET' ? undefined : JSON.stringify(apiDialog.body),
  2300. });
  2301. let data: any = JSON.parse(await res.text());
  2302. if (data.data) {
  2303. data = data.data;
  2304. }
  2305. if (data.list) {
  2306. data = data.list;
  2307. }
  2308. if (Array.isArray(data)) {
  2309. if (Array.isArray(data[0])) {
  2310. } else {
  2311. let _data = [];
  2312. data.forEach((item) => {
  2313. _data.push(Object.values(item));
  2314. })
  2315. data = _data;
  2316. }
  2317. if (Array.isArray(data) && Array.isArray(data[0])) {
  2318. meta2d.setValue({ id: props.pen.id, data: data, styles: [], mergeCells: [], colHeaders: false ,columns:[] }, { doEvent: false });
  2319. MessagePlugin.success($t("导入成功"));
  2320. } else {
  2321. MessagePlugin.warning($t("数据格式必需是二维数组"));
  2322. }
  2323. apiDialog.show = false;
  2324. } else {
  2325. MessagePlugin.warning($t("数据格式必需是数组类型"));
  2326. }
  2327. };
  2328. const leaderPen = ref(null);
  2329. const watcher = watch(
  2330. () => props.pen.id,
  2331. async () => {
  2332. leaderPen.value = meta2d.store.pens[props.pen.formId];
  2333. }
  2334. );
  2335. const moreDataDialog = reactive({
  2336. show:false
  2337. })
  2338. onMounted(() => {
  2339. meta2d.on('click',getPenData);
  2340. leaderPen.value = meta2d.store.pens[props.pen.formId];
  2341. getPenData({});
  2342. });
  2343. const old = globalThis.uiVersion === 'v20250208';
  2344. onUnmounted(() => {
  2345. meta2d.off('click',getPenData);
  2346. watcher();
  2347. clearInterval(timer);
  2348. });
  2349. </script>
  2350. <style lang="postcss" scoped>
  2351. .props {
  2352. height: 100%;
  2353. .grid {
  2354. grid-template-columns: 80px 110px 75px;
  2355. padding: 0 12px;
  2356. &.head {
  2357. background: var(--color-background-input);
  2358. line-height: 36px;
  2359. margin-bottom: 6px;
  2360. .title {
  2361. line-height: 36px;
  2362. }
  2363. }
  2364. }
  2365. .t-input-number{
  2366. width: 100px;
  2367. }
  2368. .blank {
  2369. height: 70%;
  2370. img {
  2371. padding: 16px;
  2372. opacity: 0.9;
  2373. }
  2374. }
  2375. .c-titile{
  2376. margin-top: 12px;
  2377. color: var(--color-title);
  2378. font-size: 12px;
  2379. font-weight: 700;
  2380. }
  2381. .c-head{
  2382. &::before{
  2383. width: 0%;
  2384. }
  2385. color: var(--color);
  2386. font-weight: 700;
  2387. }
  2388. .real-times{
  2389. .label {
  2390. width: fix-content;
  2391. font-size: 10px;
  2392. line-height: 28px;
  2393. color: var(--color-desc);
  2394. overflow: hidden;
  2395. white-space: nowrap;
  2396. text-overflow: ellipsis;
  2397. }
  2398. }
  2399. .value {
  2400. padding-right: 8px;
  2401. display: flex;
  2402. align-items: center;
  2403. justify-content: space-between;
  2404. svg {
  2405. flex-shrink: 0;
  2406. margin-right: 4px;
  2407. }
  2408. & > div {
  2409. /* width: 72px; */
  2410. &.t-switch {
  2411. width: fit-content;
  2412. margin-left: 4px;
  2413. }
  2414. }
  2415. :deep(.t-input) {
  2416. padding-left: 4px;
  2417. height: 26px;
  2418. border-color: transparent;
  2419. &:hover {
  2420. border-color: var(--color-primary);
  2421. }
  2422. }
  2423. }
  2424. .actions {
  2425. text-align: right;
  2426. padding-right: 2px;
  2427. }
  2428. .data-list {
  2429. :deep(.t-table__header--fixed:not(.t-table__header--multiple) > tr > th) {
  2430. background: none;
  2431. }
  2432. :deep(.t-table__pagination) {
  2433. padding-bottom: 0;
  2434. }
  2435. }
  2436. /* .form-item{
  2437. height: 32px;
  2438. } */
  2439. }
  2440. .body {
  2441. :deep(.t-collapse.t--border-less) {
  2442. .t-collapse-panel__header {
  2443. border-top: none;
  2444. /* border-bottom: 1px solid var(--td-border-level-1-color); */
  2445. padding: 8px 0;
  2446. .t-input {
  2447. border: none;
  2448. padding-left: 0;
  2449. font-size: 14px;
  2450. }
  2451. }
  2452. .t-collapse-panel__content {
  2453. padding: 8px 0;
  2454. }
  2455. }
  2456. .title {
  2457. position: relative;
  2458. margin: 8px 0;
  2459. :deep(.t-input) {
  2460. border-color: var(--color-background-input);
  2461. border-radius: 0;
  2462. border-left: none;
  2463. border-top: none;
  2464. border-right: none;
  2465. padding-left: 0;
  2466. padding-bottom: 8px;
  2467. font-size: 14px;
  2468. &:hover {
  2469. border-color: var(--color-border-input);
  2470. }
  2471. }
  2472. }
  2473. .head {
  2474. margin-top: 10px;
  2475. }
  2476. .banner {
  2477. background-color: var(--color-background-input);
  2478. padding: 0 12px;
  2479. }
  2480. }
  2481. </style>
  2482. <style lang="postcss">
  2483. .t-drawer__mask{
  2484. /* background-color: #fff0; */
  2485. }
  2486. .t-drawer__body{
  2487. padding:0px;
  2488. }
  2489. </style>