ananzhusen 3 bulan lalu
induk
melakukan
d87670d45c

+ 209 - 0
src/views/components/common/JsonModal.vue

@@ -0,0 +1,209 @@
+<template>
+  <t-dialog
+    v-model:visible="props.visible"
+    header="JSON值"
+    :width="470"
+    @close="close"
+    @confirm="confirm"
+  >
+    <t-tabs v-model="tabValue" @change="tabChange">
+      <t-tab-panel :value="1" label="简单模式">
+        <div class="mt-8">
+          <div class="flex mt-4 one-data" v-for="(d, i) in simpleData">
+            <t-select-input
+              class="input-key"
+              v-if="props.options?.length"
+              placeholder="可自定义输入"
+              v-model:inputValue="d.key"
+              :value="d.keyLabel"
+              v-model:popupVisible="d.keyPopupVisible"
+              allow-input
+              clearable
+              @clear="d.keyLabel = undefined"
+              @focus="d.keyPopupVisible = true"
+              @blur="d.keyPopupVisible = false"
+              @input-change="onKeyInput(d)"
+            >
+              <template #panel>
+                <ul style="padding: 8px 12px">
+                  <li
+                    v-for="item in props.options"
+                    :key="item.value"
+                    @click="
+                      d.key = item.value;
+                      d.keyLabel = item.label;
+                      d.keyPopupVisible = false;
+                    "
+                  >
+                    {{ item.label }}
+                  </li>
+                </ul>
+              </template>
+            </t-select-input>
+            <t-input v-else class="input-key" v-model="d.key" />
+            <t-input class="input-value" v-model="d.value" />
+            <div class="flex operation">
+              <add-circle-icon class="hover ml-4" @click="addData(i)" />
+              <minus-circle-icon class="hover ml-4" @click="delData(i)" />
+            </div>
+          </div>
+        </div>
+      </t-tab-panel>
+      <t-tab-panel :value="2" label="JSON模式">
+        <template #label>
+          JSON模式
+          <t-tooltip v-if="props.tips" :content="props.tips" placement="top">
+            <HelpCircleIcon class="ml-4" />
+          </t-tooltip>
+        </template>
+        <CodeEditor
+          :key="codeUpdateKey"
+          :json="true"
+          :language="'json'"
+          v-model="json"
+          style="height: 300px"
+        />
+      </t-tab-panel>
+    </t-tabs>
+  </t-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref, toRaw, onMounted, watch } from 'vue';
+import CodeEditor from '@/views/components/common/CodeEditor.vue';
+import {
+  AddCircleIcon,
+  MinusCircleIcon,
+  HelpCircleIcon,
+} from 'tdesign-icons-vue-next';
+import { s8 } from '@/services/random';
+
+const props = defineProps<{
+  visible: boolean;
+  data: any;
+  options?: any[];
+  tips?: string;
+}>();
+const emit = defineEmits(['update:visible', 'change']);
+const activedProp = ref([]);
+
+const simpleData = ref([]);
+
+const json = ref({});
+const codeUpdateKey = ref(s8());
+watch(
+  () => props.visible,
+  () => {
+    if (!props.data) {
+      json.value = {};
+      simpleData.value = [{ key: '', value: '' }];
+    }
+    json.value = JSON.parse(JSON.stringify(props.data));
+    if (json.value && !Array.isArray(json.value)) {
+      simpleData.value = objToArr(json.value);
+    }
+    if (!simpleData.value?.length) {
+      simpleData.value = [{ key: '', value: '' }];
+    }
+    if (tabValue.value === 2) {
+      codeUpdateKey.value = s8();
+    }
+  }
+);
+
+const tabChange = (val) => {
+  if (val === 1) {
+    simpleData.value = objToArr(json.value);
+    if (!simpleData.value?.length) {
+      simpleData.value.push({ key: '', value: '' });
+    }
+  } else if (val === 2) {
+    json.value = arrToObj(simpleData.value);
+  }
+};
+
+const addData = (index) => {
+  simpleData.value.splice(index + 1, 0, { key: '', value: '' });
+};
+
+const delData = (index) => {
+  if (index === 0) {
+    simpleData.value[0].key = '';
+    simpleData.value[0].value = '';
+  } else {
+    simpleData.value.splice(index, 1);
+  }
+};
+
+function close() {
+  emit('update:visible', false);
+}
+
+const tabValue = ref(1);
+
+const arrToObj = (arr) => {
+  return arr.reduce((accumulator, current) => {
+    if (current.key) {
+      accumulator[current.key] = current.value;
+    }
+    return accumulator;
+  }, {});
+};
+
+const objToArr = (obj) => {
+  return Object.entries(obj).map(([key, value]) => ({
+    key: key,
+    value: value,
+    label: key,
+    keyLabel: key,
+  }));
+};
+
+const confirm = () => {
+  if (tabValue.value === 1) {
+    json.value = arrToObj(simpleData.value);
+  }
+  emit('change', json.value);
+};
+
+const onKeyInput = (item: any) => {
+  item.keyLabel = item.key;
+};
+</script>
+
+<style lang="postcss" scoped>
+.t-tab-panel {
+  height: 300px;
+  overflow-y: hidden;
+}
+
+:deep(.t-input) {
+  border-color: transparent;
+  &:hover {
+    border-color: var(--color-primary);
+  }
+}
+
+.input-key {
+  width: 140px;
+  :deep(.t-input) {
+    width: 140px;
+  }
+}
+
+.one-data {
+  .operation {
+    display: none;
+    justify-content: center;
+    align-items: center;
+    color: var(--td-text-color-secondary);
+  }
+  &:hover {
+    .operation {
+      display: flex;
+
+      height: 30px;
+    }
+  }
+}
+</style>

+ 275 - 0
src/views/components/common/PenPropModal.vue

@@ -0,0 +1,275 @@
+<template>
+  <t-dialog
+    v-model:visible="props.visible"
+    header="关联图元属性"
+    :width="580"
+    @close="close"
+    @confirm="confirm"
+  >
+    <div class="flex">
+      <div style="width: 200px">
+        <div class="input-search mt-8">
+          <div class="btn">
+            <search-icon class="hover" />
+          </div>
+          <t-input
+            v-model="search"
+            @change="onSearch"
+            @enter="onSearch"
+            placeholder="搜索图元"
+          />
+        </div>
+        <div class="props-tree mt-8">
+          <t-tree
+            v-model:actived="activedId"
+            activable
+            hover
+            :data="penTree"
+            :expand-level="1"
+            :filter="bindFilter"
+            @click="onClick"
+          />
+        </div>
+      </div>
+      <t-divider
+        class="mt-8"
+        style="
+          height: 360px;
+          border-left: 2px solid var(--color-background-input);
+        "
+        layout="vertical"
+      />
+      <div style="width: 300px">
+        <div class="input-search mt-8">
+          <div class="btn">
+            <search-icon class="hover" />
+          </div>
+          <t-input
+            v-model="propSearch"
+            @change="onPropSearch"
+            @enter="onPropSearch"
+            placeholder="搜索属性"
+          />
+        </div>
+        <div class="props mt-8">
+          <t-table
+            class="data-list"
+            ref="tableRef"
+            row-key="value"
+            :columns="columns"
+            :data="propData"
+            :height="300"
+            :bordered="false"
+            :selected-row-keys="selectedIds"
+            @select-change="onselect"
+          >
+          </t-table>
+        </div>
+      </div>
+    </div>
+  </t-dialog>
+</template>
+
+<script lang="ts" setup>
+import { SearchIcon } from 'tdesign-icons-vue-next';
+import { onBeforeMount, ref, watch } from 'vue';
+import { getPenTree } from '@/services/common';
+import { penProps } from '@/services/defaults';
+import { MessagePlugin } from 'tdesign-vue-next';
+
+const props = defineProps<{
+  visible: boolean;
+  data: {
+    id: string;
+    idLabel: string;
+    keyLabel: string;
+    key: string;
+    value: any;
+  };
+}>();
+const emit = defineEmits(['update:visible', 'change']);
+const penTree = ref([]);
+const selectedData = ref({
+  id: '',
+  idLabel: '',
+  keyLabel: '',
+  key: '',
+  value: '',
+});
+
+watch(
+  () => props.visible,
+  () => {
+    if (props.visible) {
+      activedId.value = [props.data.id];
+      selectedIds.value = [props.data.key];
+      selectedData.value.id = props.data.id;
+      selectedData.value.idLabel = props.data.idLabel;
+      selectedData.value.keyLabel = props.data.keyLabel;
+      selectedData.value.key = props.data.key;
+      if (selectedData.value.id) {
+        getPropData();
+      }
+    }
+  }
+);
+
+onBeforeMount(() => {
+  penTree.value = getPenTree();
+});
+
+const search = ref('');
+const bindFilter = ref<any>(null);
+const onSearch = () => {
+  bindFilter.value = search.value
+    ? (node) => {
+        return (
+          node.value?.indexOf(search.value) >= 0 ||
+          node.data?.label.indexOf(search.value) >= 0
+        );
+      }
+    : null;
+};
+
+const propSearch = ref('');
+const onPropSearch = () => {
+  propData.value = mergedArray.filter((item) => {
+    return (
+      item.label.indexOf(propSearch.value) >= 0 ||
+      item.value.indexOf(propSearch.value) >= 0
+    );
+  });
+};
+
+let mergedArray = [];
+
+const onClick = (e) => {
+  selectedData.value.id = activedId.value[0];
+  selectedData.value.idLabel = e.node.data.label;
+  getPropData();
+};
+
+const getPropData = () => {
+  let penP = [
+    {
+      value: 'text',
+      label: '文字',
+    },
+    {
+      value: 'visible',
+      label: '显示',
+    },
+    {
+      value: 'progress',
+      label: '进度',
+    },
+    {
+      value: 'showChild',
+      label: '状态',
+    },
+    {
+      value: 'checked',
+      label: '选中',
+    },
+    {
+      value: 'selectedKey',
+      label: '单选选中值',
+    },
+  ];
+  let target: any;
+  if (selectedData.value.id && selectedData.value.id !== '固定值') {
+    target = meta2d.findOne(selectedData.value.id);
+  } else {
+    return;
+  }
+  if (target?.realTimes) {
+    for (const item of target.realTimes) {
+      const found = penP.findIndex((elem: any) => elem.value === item.key);
+      if (found < 0) {
+        penP.push({
+          value: item.key,
+          label: item.label,
+        });
+      }
+    }
+  }
+  mergedArray = penP
+    .concat(penProps)
+    .filter(
+      (item, index, self) =>
+        index === self.findIndex((t) => t.value === item.value)
+    );
+  propData.value = mergedArray;
+};
+
+const onselect = (value: string[], options: any) => {
+  selectedData.value.keyLabel = options.selectedRowData[0].label;
+  selectedData.value.key = options.selectedRowData[0].value;
+  selectedIds.value = value;
+};
+
+function close() {
+  emit('update:visible', false);
+}
+
+const confirm = () => {
+  props.data.id = selectedData.value.id;
+  props.data.idLabel = selectedData.value.idLabel;
+  props.data.keyLabel = selectedData.value.keyLabel;
+  props.data.key = selectedData.value.key;
+  close();
+  // emit('change', selectedData.value);
+};
+const propData = ref([]);
+
+const activedId = ref([]);
+
+const selectedIds = ref([]);
+
+const activedProp = ref([]);
+
+const columns = [
+  { colKey: 'row-select', type: 'single', width: 50 },
+  { colKey: 'label', title: '名称' },
+  { colKey: 'value', title: 'key名', width: 150 },
+  // { colKey: 'type', title: '类型' ,width:80},
+];
+</script>
+
+<style lang="postcss" scoped>
+.props-tree {
+  height: 300px;
+  overflow-y: auto;
+}
+.props {
+  height: 320px;
+  overflow-y: auto;
+  /* padding-bottom: 16px; */
+}
+
+.input-search {
+  width: 100%;
+  padding: 4px 0px;
+
+  .btn {
+    left: 14px;
+  }
+}
+
+:deep(.t-table) {
+  .t-table__header--fixed:not(.t-table__header--multiple) > tr > th {
+    background-color: #262d3a !important;
+  }
+  th {
+    border-bottom: 1px solid var(--color-background-input);
+  }
+  td {
+    border-bottom: 1px solid var(--color-background-input);
+  }
+  .t-table__empty-row {
+    td {
+      border-bottom: 0px;
+    }
+  }
+}
+</style>