PenDatas.vue 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  1. <template>
  2. <div class="props">
  3. <div
  4. class="real-times"
  5. v-if="props.pen.realTimes && props.pen.realTimes.length"
  6. >
  7. <div class="grid head">
  8. <div class="title">数据名</div>
  9. <div class="title">值</div>
  10. <div class="title">触发器</div>
  11. <div class="actions">
  12. <t-icon name="more" />
  13. </div>
  14. </div>
  15. <div class="grid" v-for="(item, i) in props.pen.realTimes">
  16. <t-tooltip :content="item.key" placement="top">
  17. <label class="label">{{ item.label }}</label>
  18. </t-tooltip>
  19. <div class="value">
  20. <t-tooltip :content="getBindsDesc(item)" placement="top">
  21. <t-icon
  22. name="link"
  23. class="hover"
  24. :class="{ primary: item.binds?.length }"
  25. @click="onBind(item)"
  26. />
  27. </t-tooltip>
  28. <t-input
  29. v-if="item.type === 'number'"
  30. v-model="pen[item.key]"
  31. placeholder="数字"
  32. @change="changeValue(item.key)"
  33. />
  34. <t-switch
  35. v-else-if="item.type === 'bool'"
  36. v-model="pen[item.key]"
  37. class="ml-8"
  38. size="small"
  39. @change="changeValue(item.key)"
  40. />
  41. <div
  42. v-else-if="item.type === 'array' || item.type === 'object'"
  43. class="gray ellipsis"
  44. style="height: 30px"
  45. >
  46. {{ JSON.stringify(pen[item.key]) }}
  47. </div>
  48. <t-input
  49. v-else
  50. v-model="pen[item.key]"
  51. placeholder="字符串"
  52. @change="changeValue(item.key)"
  53. />
  54. </div>
  55. <div>
  56. <t-tooltip :content="item.triggers?.length || '触发器'">
  57. <t-badge
  58. :count="item.triggers?.length"
  59. size="small"
  60. dot
  61. :offset="[0, 5]"
  62. >
  63. <t-icon
  64. name="relativity"
  65. class="hover"
  66. @click="onTrigger(item)"
  67. />
  68. </t-badge>
  69. </t-tooltip>
  70. </div>
  71. <div class="actions">
  72. <t-dropdown
  73. :options="moreOptions"
  74. @click="onMenuMore($event, item, i)"
  75. :minColumnWidth="80"
  76. >
  77. <t-icon name="more" class="more hover" />
  78. </t-dropdown>
  79. </div>
  80. </div>
  81. <div class="mt-8 pb-16">
  82. <t-dropdown
  83. :options="options"
  84. @click="addRealTime"
  85. :minColumnWidth="150"
  86. >
  87. <a class="ml-12"> <t-icon name="add-rectangle" /> 添加动态数据 </a>
  88. </t-dropdown>
  89. </div>
  90. </div>
  91. <div class="flex column center blank" v-else>
  92. <img src="/img/blank.png" />
  93. <div class="gray center">还没有动态数据</div>
  94. <div class="mt-8">
  95. <t-dropdown
  96. :options="options"
  97. @click="addRealTime"
  98. :minColumnWidth="150"
  99. >
  100. <t-button style="height: 30px"> 添加动态数据 </t-button>
  101. </t-dropdown>
  102. </div>
  103. </div>
  104. </div>
  105. <t-dialog
  106. v-if="addDataDialog.show"
  107. :visible="true"
  108. class="data-dialog"
  109. :header="addDataDialog.header"
  110. @close="addDataDialog.show = false"
  111. @confirm="onConfirmData"
  112. >
  113. <div class="form-item mt-16">
  114. <label>数据名</label>
  115. <t-input
  116. v-model="addDataDialog.data.label"
  117. placeholder="简短描述"
  118. :disabled="!!addDataDialog.data.keywords"
  119. @blur="onChangeLabel"
  120. />
  121. </div>
  122. <div class="form-item mt-16">
  123. <label>属性名</label>
  124. <t-input
  125. v-model="addDataDialog.data.key"
  126. placeholder="关键字"
  127. :disabled="!!addDataDialog.data.keywords"
  128. />
  129. </div>
  130. <div class="form-item mt-16">
  131. <label>类型</label>
  132. <t-select
  133. class="w-full"
  134. :options="typeOptions"
  135. v-model="addDataDialog.data.type"
  136. placeholder="字符串"
  137. :disabled="!!addDataDialog.data.keywords"
  138. @change="addDataDialog.data.value = null"
  139. />
  140. </div>
  141. <div class="form-item mt-16">
  142. <label>值</label>
  143. <div class="flex-grow" v-if="addDataDialog.data.type === 'number'">
  144. <t-input
  145. class="w-full"
  146. v-model="addDataDialog.data.value"
  147. placeholder="数字"
  148. />
  149. <div class="desc mt-8">
  150. 固定数字:直接输入数字。例如:5<br />
  151. 随机范围数字 :最小值-最大值。例如:0-1 或 0-100
  152. <br />
  153. 随机指定数字 :数字1,数字2,数字3... 。 例如:1,5,10,20<br />
  154. </div>
  155. </div>
  156. <div class="flex-grow" v-else-if="addDataDialog.data.type === 'bool'">
  157. <t-select v-model="addDataDialog.data.value">
  158. <t-option :key="true" :value="true" label="true"></t-option>
  159. <t-option :key="false" :value="false" label="false"></t-option>
  160. <t-option key="随机" label="随机"></t-option>
  161. </t-select>
  162. <div class="desc mt-8">
  163. 固定:指定true或false<br />
  164. 随机:随机生成一个布尔值<br />
  165. </div>
  166. </div>
  167. <div
  168. class="flex-grow"
  169. v-else-if="
  170. addDataDialog.data.type === 'array' ||
  171. addDataDialog.data.type === 'object'
  172. "
  173. >
  174. <CodeEditor v-model="addDataDialog.data.value" :json="true" />
  175. </div>
  176. <div class="flex-grow" v-else>
  177. <t-input
  178. class="w-full"
  179. v-model="addDataDialog.data.value"
  180. placeholder="字符串"
  181. />
  182. <div class="desc mt-8">
  183. 固定文字:直接输入。例如:大屏可视化<br />
  184. 随机文本:[文本长度]。例如:[8] 或 [16]<br />
  185. 随机指定文本:{文本1,文本2,文本3...} 。 例如:{大屏, 可视化}
  186. <br />
  187. </div>
  188. </div>
  189. </div>
  190. </t-dialog>
  191. <t-dialog
  192. v-if="dataBindDialog.show"
  193. :visible="true"
  194. class="data-link-dialog"
  195. header="动态数据绑定"
  196. @close="
  197. dataBindDialog.data.binds = dataBindDialog.bkBinds;
  198. dataBindDialog.show = false;
  199. "
  200. @confirm="dataBindDialog.show = false"
  201. :width="700"
  202. >
  203. <div class="form-item">
  204. <label>当前绑定:</label>
  205. <div class="label" v-if="dataBindDialog.data.binds?.length">
  206. <t-tooltip
  207. v-for="(tag, index) in dataBindDialog.data.binds"
  208. :key="index"
  209. :content="tag.id"
  210. >
  211. <t-tag class="mr-8 mb-8" closable @close="onRemoveBind(index)">
  212. {{ tag.label }}
  213. </t-tag>
  214. </t-tooltip>
  215. </div>
  216. <div class="label gray" v-else>无</div>
  217. </div>
  218. <div class="form-item mt-8">
  219. <t-input
  220. placeholder="搜索"
  221. v-model="dataBindDialog.input"
  222. @change="onSearchDataset"
  223. @enter="onSearchDataset"
  224. >
  225. <template #suffixIcon>
  226. <t-icon name="search" class="hover" @click="onSearchDataset" />
  227. </template>
  228. </t-input>
  229. </div>
  230. <t-table
  231. class="mt-12 data-list"
  232. row-key="id"
  233. :data="dataBindDialog.dataSet"
  234. :columns="dataSetColumns"
  235. size="small"
  236. bordered
  237. :loading="dataBindDialog.loading"
  238. :pagination="query"
  239. @page-change="onChangePagination"
  240. :selected-row-keys="dataBindDialog.selectedIds"
  241. @select-change="onSelectBindsChange"
  242. >
  243. </t-table>
  244. </t-dialog>
  245. <t-dialog
  246. v-if="triggersDialog.show"
  247. :visible="true"
  248. class="data-events-dialog"
  249. header="数据触发器"
  250. @confirm="triggersDialog.show = false"
  251. @close="triggersDialog.show = false"
  252. :width="700"
  253. >
  254. <div class="body">
  255. <t-collapse
  256. v-model="triggersDialog.openedCollapses"
  257. :borderless="true"
  258. :expand-on-row-click="false"
  259. >
  260. <t-collapse-panel
  261. v-for="(trigger, i) in triggersDialog.data.triggers"
  262. :value="i"
  263. >
  264. <template #header>
  265. <t-input v-model="trigger.name" class="mr-12" />
  266. </template>
  267. <template #headerRightContent>
  268. <t-popconfirm
  269. content="确认删除该触发器吗?"
  270. @confirm="triggersDialog.data.triggers.splice(i, 1)"
  271. >
  272. <t-icon name="delete" class="hover" />
  273. </t-popconfirm>
  274. </template>
  275. <section>
  276. <div class="form-item banner">
  277. <label>触发条件</label>
  278. <div class="w-full flex middle between">
  279. <div></div>
  280. <t-radio-group v-model="trigger.conditionType">
  281. <t-radio value="and"> 满足全部条件 </t-radio>
  282. <t-radio value="or"> 满足任意条件 </t-radio>
  283. </t-radio-group>
  284. </div>
  285. </div>
  286. <div v-for="(c, index) in trigger.conditions" class="mb-12">
  287. <div class="flex middle between head">
  288. <div class="flex middle">
  289. <t-icon name="arrow-right" class="mr-4" />
  290. 条件{{ index + 1 }}
  291. </div>
  292. <t-icon
  293. name="close"
  294. class="hover"
  295. @click="trigger.conditions.splice(index, 1)"
  296. />
  297. </div>
  298. <div class="px-16 py-4">
  299. <div class="form-item mt-4">
  300. <label>条件类型</label>
  301. <t-radio-group v-model="c.type">
  302. <t-radio value=""> 关系条件 </t-radio>
  303. <t-radio value="fn"> 高级条件 </t-radio>
  304. </t-radio-group>
  305. </div>
  306. <template v-if="!c.type">
  307. <div class="form-item mt-8">
  308. <label>比较条件</label>
  309. <div class="flex middle">
  310. <label class="shrink-0 mr-8">数据</label>
  311. <t-select
  312. v-model="c.operator"
  313. placeholder="关系运算"
  314. :options="operatorOptions"
  315. class="shrink-0 mr-8"
  316. style="width: 80px"
  317. />
  318. <t-select
  319. v-model="c.valueType"
  320. class="shrink-0 mr-8"
  321. style="width: 110px"
  322. placeholder="固定值"
  323. >
  324. <t-option key="" value="" label="固定值">
  325. 固定值
  326. </t-option>
  327. <t-option key="prop" value="prop" label="对象属性值">
  328. 对象属性值
  329. </t-option>
  330. </t-select>
  331. <template v-if="!c.valueType">
  332. <t-input
  333. v-model="c.value"
  334. class="shrink-0"
  335. style="width: 320px"
  336. />
  337. </template>
  338. <template v-else>
  339. <t-tree-select
  340. v-model="c.target"
  341. :data="penTree"
  342. filterable
  343. placeholder="对象"
  344. class="shrink-0 mr-8"
  345. style="width: 160px"
  346. @change="onChangeTriggerTarget(c)"
  347. />
  348. <t-select-input
  349. v-model:inputValue="c.value"
  350. :value="c.label"
  351. v-model:popupVisible="c.popupVisible"
  352. allow-input
  353. clearable
  354. @clear="c.label = undefined"
  355. @focus="c.popupVisible = true"
  356. @blur="c.popupVisible = undefined"
  357. @input-change="onInput(c)"
  358. class="shrink-0"
  359. style="width: 152px"
  360. >
  361. <template #panel>
  362. <ul style="padding: 8px 12px">
  363. <li
  364. v-for="item in c.targetProps"
  365. :key="item.value"
  366. @click="
  367. c.value = item.value;
  368. c.label = item.label;
  369. "
  370. >
  371. {{ item.label }}
  372. </li>
  373. </ul>
  374. </template>
  375. </t-select-input>
  376. </template>
  377. </div>
  378. </div>
  379. </template>
  380. <template v-else>
  381. <div>function condition(pen) {</div>
  382. <CodeEditor class="mt-4" v-model="c.fnJs" />
  383. <div class="mt-4">}</div>
  384. </template>
  385. </div>
  386. </div>
  387. <div class="mt-8">
  388. <a @click="addTriggerCondition(trigger)"> + 添加条件 </a>
  389. </div>
  390. <div class="form-item banner mt-16">
  391. <label>执行动作</label>
  392. </div>
  393. <Actions class="mt-8" :data="trigger" />
  394. </section>
  395. </t-collapse-panel>
  396. </t-collapse>
  397. <div class="mt-8">
  398. <a @click="onAddTrigger"> + 添加触发器 </a>
  399. </div>
  400. </div>
  401. </t-dialog>
  402. </template>
  403. <script lang="ts" setup>
  404. import {
  405. getCurrentInstance,
  406. onBeforeMount,
  407. onUnmounted,
  408. reactive,
  409. ref,
  410. toRaw,
  411. } from 'vue';
  412. import { useRoute, useRouter } from 'vue-router';
  413. import { MessagePlugin } from 'tdesign-vue-next';
  414. import axios from 'axios';
  415. import { debounce } from '@/services/debouce';
  416. import { getPenTree } from '@/services/common';
  417. import { updatePen } from './pen';
  418. import CodeEditor from '@/views/components/common/CodeEditor.vue';
  419. import Actions from './Actions.vue';
  420. const route = useRoute();
  421. const router = useRouter();
  422. const {
  423. proxy: { $forceUpdate },
  424. }: any = getCurrentInstance();
  425. const props = defineProps<{
  426. pen: any;
  427. }>();
  428. const options = ref<any>([
  429. {
  430. value: '',
  431. content: '自定义',
  432. divider: true,
  433. },
  434. {
  435. value: 'x',
  436. content: 'X',
  437. type: 'number',
  438. keywords: true,
  439. },
  440. {
  441. value: 'y',
  442. content: 'Y',
  443. type: 'number',
  444. keywords: true,
  445. },
  446. {
  447. value: 'width',
  448. content: '宽',
  449. type: 'number',
  450. keywords: true,
  451. },
  452. {
  453. value: 'height',
  454. content: '高',
  455. type: 'number',
  456. keywords: true,
  457. },
  458. {
  459. value: 'visible',
  460. content: '显示',
  461. type: 'bool',
  462. keywords: true,
  463. },
  464. {
  465. value: 'text',
  466. content: '文字',
  467. keywords: true,
  468. },
  469. {
  470. value: 'progress',
  471. content: '进度',
  472. type: 'number',
  473. keywords: true,
  474. },
  475. {
  476. value: 'showChild',
  477. content: '状态',
  478. type: 'number',
  479. keywords: true,
  480. },
  481. {
  482. value: 'rotate',
  483. content: '旋转',
  484. type: 'number',
  485. keywords: true,
  486. },
  487. ]);
  488. const moreOptions = ref<any>([
  489. {
  490. value: 'edit',
  491. content: '编辑',
  492. },
  493. {
  494. value: 'delete',
  495. content: '移除',
  496. },
  497. ]);
  498. const typeOptions = [
  499. {
  500. label: '字符串',
  501. },
  502. {
  503. label: '数字',
  504. value: 'number',
  505. },
  506. {
  507. label: '布尔',
  508. value: 'bool',
  509. },
  510. {
  511. label: '对象',
  512. value: 'object',
  513. },
  514. {
  515. label: '数组',
  516. value: 'array',
  517. },
  518. ];
  519. const addDataDialog = reactive<any>({
  520. show: false,
  521. data: undefined,
  522. });
  523. const dataBindDialog = reactive<any>({
  524. show: false,
  525. data: undefined,
  526. });
  527. const dataSetColumns = [
  528. {
  529. colKey: 'row-select',
  530. type: 'multiple',
  531. width: 50,
  532. },
  533. {
  534. colKey: 'id',
  535. title: '编号',
  536. width: 150,
  537. ellipsis: { theme: 'light', trigger: 'context-menu' },
  538. },
  539. {
  540. colKey: 'label',
  541. title: '动态数据名称',
  542. width: 220,
  543. ellipsis: { theme: 'light', trigger: 'context-menu' },
  544. },
  545. {
  546. colKey: 'case',
  547. title: '场景',
  548. ellipsis: { theme: 'light', trigger: 'context-menu' },
  549. },
  550. ];
  551. const operatorOptions = ref<any>([
  552. { label: '=', value: '=' },
  553. { label: '!=', value: '!=' },
  554. { label: '>', value: '>' },
  555. { label: '<', value: '<' },
  556. { label: '>=', value: '>=' },
  557. { label: '<=', value: '<=' },
  558. { label: '包含', value: '[)' },
  559. { label: '不包含', value: '![)' },
  560. ]);
  561. const query = reactive<{
  562. current: number;
  563. pageSize: number;
  564. total: number;
  565. range: any[];
  566. }>({
  567. current: 1,
  568. pageSize: 10,
  569. total: 0,
  570. range: [],
  571. });
  572. const triggersDialog = reactive<any>({
  573. show: false,
  574. data: undefined,
  575. });
  576. const penTree: any = ref([]);
  577. let timer: any;
  578. onBeforeMount(() => {
  579. // realTimesOptions - 扩展的动态数据下拉列表
  580. if (props.pen.realTimesOptions) {
  581. options.value[options.value.length - 1].divider = true;
  582. options.value.push(...props.pen.realTimesOptions);
  583. }
  584. timer = setInterval($forceUpdate, 1000);
  585. });
  586. const addRealTime = (e: any) => {
  587. if (e.keywords) {
  588. if (!props.pen.realTimes) {
  589. props.pen.realTimes = [];
  590. }
  591. props.pen.realTimes.push({
  592. label: e.content,
  593. key: e.value,
  594. type: e.type,
  595. keywords: e.keywords,
  596. });
  597. return;
  598. }
  599. addDataDialog.header = '添加动态数据';
  600. addDataDialog.data = {
  601. label: e.content,
  602. key: e.value,
  603. type: e.type,
  604. keywords: e.keywords,
  605. };
  606. if (e.keywords) {
  607. addDataDialog.data.label = '';
  608. }
  609. addDataDialog.show = true;
  610. };
  611. const onChangeLabel = () => {
  612. if (!addDataDialog.data.key) {
  613. addDataDialog.data.key = addDataDialog.data.label;
  614. }
  615. };
  616. const onConfirmData = () => {
  617. if (!props.pen.realTimes) {
  618. props.pen.realTimes = [];
  619. }
  620. if (!addDataDialog.data.label || !addDataDialog.data.key) {
  621. MessagePlugin.error('数据名或属性名不能为空!');
  622. return;
  623. }
  624. if (addDataDialog.header === '添加动态数据') {
  625. const found = props.pen.realTimes.findIndex((item: any) => {
  626. return item.key === addDataDialog.data.key;
  627. });
  628. if (found > -1) {
  629. MessagePlugin.error('已经存在相同属性数据!');
  630. return;
  631. }
  632. props.pen.realTimes.push(addDataDialog.data);
  633. }
  634. addDataDialog.show = false;
  635. };
  636. const onMenuMore = (e: any, item: any, i: number) => {
  637. switch (e.value) {
  638. case 'edit':
  639. addDataDialog.header = '编辑动态数据';
  640. addDataDialog.data = item;
  641. addDataDialog.show = true;
  642. break;
  643. case 'delete':
  644. props.pen.realTimes.splice(i, 1);
  645. break;
  646. default:
  647. break;
  648. }
  649. };
  650. const onBind = (item: any) => {
  651. if (!item.binds) {
  652. item.binds = [];
  653. }
  654. dataBindDialog.data = item;
  655. dataBindDialog.input = '';
  656. dataBindDialog.selectedIds = [];
  657. for (const i of item.binds) {
  658. dataBindDialog.selectedIds.push(i.id);
  659. }
  660. dataBindDialog.bkBinds = [];
  661. dataBindDialog.bkBinds.push(...item.binds);
  662. dataBindDialog.show = true;
  663. getDataset();
  664. };
  665. const onSearchDataset = () => {
  666. debounce(getDataset, 300);
  667. };
  668. const getDataset = async () => {
  669. // @ts-ignore
  670. const data: Meta2dBackData = meta2d.data();
  671. dataBindDialog.loading = true;
  672. // 应该从data获取url或结果列表
  673. const ret: any = await axios.get(
  674. `/api/device/data/set?mock=1&q=${dataBindDialog.input}&current=${query.current}&pageSize=${query.pageSize}`
  675. );
  676. dataBindDialog.dataSet = ret.list;
  677. query.total = ret.total;
  678. dataBindDialog.loading = false;
  679. };
  680. const onChangePagination = (pageInfo: any) => {
  681. router.push({
  682. path: route.path,
  683. query: { current: pageInfo.current, pageSize: pageInfo.pageSize },
  684. });
  685. query.current = pageInfo.current;
  686. query.pageSize = pageInfo.pageSize;
  687. getDataset();
  688. };
  689. const onSelectBindsChange = (value: string[], options: any) => {
  690. dataBindDialog.selectedIds = value;
  691. if (options.type === 'check') {
  692. for (const item of options.selectedRowData) {
  693. const found = dataBindDialog.data.binds.findIndex((elem: any) => {
  694. return elem.id === item.id;
  695. });
  696. if (found < 0) {
  697. dataBindDialog.data.binds.push(toRaw(item));
  698. }
  699. }
  700. } else if (options.type === 'uncheck') {
  701. if (options.currentRowKey === 'CHECK_ALL_BOX') {
  702. for (const data of dataBindDialog.dataSet) {
  703. const found = dataBindDialog.data.binds.findIndex((elem: any) => {
  704. return elem.id === data.id;
  705. });
  706. if (found > -1) {
  707. dataBindDialog.data.binds.splice(found, 1);
  708. }
  709. }
  710. } else {
  711. const found = dataBindDialog.data.binds.findIndex((elem: any) => {
  712. return elem.id === options.currentRowKey;
  713. });
  714. if (found > -1) {
  715. dataBindDialog.data.binds.splice(found, 1);
  716. }
  717. }
  718. }
  719. };
  720. const onRemoveBind = (index: number) => {
  721. dataBindDialog.data.binds.splice(index, 1);
  722. dataBindDialog.selectedIds = [];
  723. for (const i of dataBindDialog.data.binds) {
  724. dataBindDialog.selectedIds.push(i.id);
  725. }
  726. };
  727. const getBindsDesc = (item: any) => {
  728. if (!item.binds || !item.binds.length) {
  729. return '绑定动态数据';
  730. }
  731. let desc = '';
  732. for (const i of item.binds) {
  733. desc += i.label + ',';
  734. }
  735. if (desc && desc.length > 1) {
  736. desc = desc.substring(0, desc.length - 1);
  737. }
  738. return desc;
  739. };
  740. const changeValue = (prop: string) => {
  741. updatePen(props.pen, prop);
  742. };
  743. const onTrigger = (item: any) => {
  744. if (!item.triggers) {
  745. item.triggers = [];
  746. }
  747. triggersDialog.openedCollapses = [0];
  748. triggersDialog.data = item;
  749. triggersDialog.show = true;
  750. penTree.value = getPenTree();
  751. };
  752. const onAddTrigger = () => {
  753. const i = triggersDialog.data.triggers.length;
  754. triggersDialog.data.triggers.push({
  755. name: `触发器${i + 1}`,
  756. conditionType: 'and',
  757. conditions: [],
  758. actions: [],
  759. });
  760. triggersDialog.openedCollapses.push(i);
  761. };
  762. const addTriggerCondition = (trigger: any) => {
  763. trigger.conditions.push({
  764. type: '',
  765. operator: '=',
  766. valueType: '',
  767. });
  768. };
  769. const onChangeTriggerTarget = (c: any) => {
  770. c.targetProps = [
  771. {
  772. value: 'x',
  773. label: 'X',
  774. },
  775. {
  776. value: 'y',
  777. label: 'Y',
  778. },
  779. {
  780. value: 'width',
  781. label: '宽',
  782. },
  783. {
  784. value: 'height',
  785. label: '高',
  786. },
  787. {
  788. value: 'visible',
  789. label: '显示',
  790. },
  791. {
  792. value: 'text',
  793. label: '文字',
  794. },
  795. {
  796. value: 'progress',
  797. label: '进度',
  798. },
  799. {
  800. value: 'showChild',
  801. label: '状态',
  802. },
  803. {
  804. value: 'rotate',
  805. label: '旋转',
  806. },
  807. ];
  808. const target: any = meta2d.findOne(c.target);
  809. if (target) {
  810. for (const item of target.realTimes) {
  811. const found = c.targetProps.findIndex(
  812. (elem: any) => elem.value === item.key
  813. );
  814. if (found < 0) {
  815. c.targetProps.push({
  816. value: item.key,
  817. label: item.label,
  818. });
  819. }
  820. }
  821. }
  822. };
  823. const onInput = (item: any) => {
  824. item.label = item.value;
  825. };
  826. onUnmounted(() => {
  827. clearInterval(timer);
  828. });
  829. </script>
  830. <style lang="postcss" scoped>
  831. .props {
  832. height: 100%;
  833. .grid {
  834. grid-template-columns: 60px 140px 54px 30px;
  835. padding: 0 12px;
  836. &.head {
  837. background: var(--color-background-input);
  838. line-height: 36px;
  839. margin-bottom: 6px;
  840. .title {
  841. line-height: 36px;
  842. }
  843. }
  844. }
  845. .blank {
  846. height: 70%;
  847. img {
  848. padding: 16px;
  849. opacity: 0.9;
  850. }
  851. }
  852. .label {
  853. width: fit-content;
  854. font-size: 10px;
  855. line-height: 28px;
  856. color: var(--color-desc);
  857. }
  858. .value {
  859. padding-right: 8px;
  860. display: flex;
  861. align-items: center;
  862. svg {
  863. flex-shrink: 0;
  864. margin-right: 4px;
  865. }
  866. & > div {
  867. width: 110px;
  868. &.t-switch {
  869. width: fit-content;
  870. margin-left: 4px;
  871. }
  872. }
  873. :deep(.t-input) {
  874. padding-left: 4px;
  875. height: 26px;
  876. border-color: transparent;
  877. &:hover {
  878. border-color: var(--color-primary);
  879. }
  880. }
  881. }
  882. .actions {
  883. text-align: right;
  884. padding-right: 2px;
  885. }
  886. .data-list {
  887. height: 300px;
  888. overflow: auto;
  889. }
  890. }
  891. .body {
  892. :deep(.t-collapse.t--border-less) {
  893. .t-collapse-panel__header {
  894. border-top: none;
  895. border-bottom: 1px solid var(--td-border-level-1-color);
  896. padding: 8px 0;
  897. .t-input {
  898. border: none;
  899. padding-left: 0;
  900. font-size: 14px;
  901. }
  902. }
  903. .t-collapse-panel__content {
  904. padding: 8px 0;
  905. }
  906. }
  907. .title {
  908. position: relative;
  909. margin: 8px 0;
  910. :deep(.t-input) {
  911. border-color: var(--color-background-input);
  912. border-radius: 0;
  913. border-left: none;
  914. border-top: none;
  915. border-right: none;
  916. padding-left: 0;
  917. padding-bottom: 8px;
  918. font-size: 14px;
  919. &:hover {
  920. border-color: var(--color-border-input);
  921. }
  922. }
  923. }
  924. .head {
  925. margin-top: 10px;
  926. }
  927. .banner {
  928. background-color: var(--color-background-input);
  929. padding: 0 12px;
  930. }
  931. }
  932. </style>