sign.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. <template>
  2. <ui-group-option
  3. class="sign-measure"
  4. :class="{ active: measure.selected }"
  5. @mouseenter="measure.selected = true"
  6. @mouseleave="measure.selected = false"
  7. >
  8. <div class="info">
  9. <ui-icon :type="MeasureTypeMeta[measure.type].icon" class="type" />
  10. <div v-show="!isEditTitle">
  11. <p @click.stop="edit && (isEditTitle = true)">
  12. {{ measure.title || MeasureTypeMeta[measure.type].unitDesc }}
  13. </p>
  14. <span>{{ desc }} {{ MeasureTypeMeta[measure.type].unit }}</span>
  15. </div>
  16. <ui-input
  17. class="view-title-input"
  18. type="text"
  19. :modelValue="measure.title"
  20. :maxlength="15"
  21. @update:modelValue="(title: string) => $emit('updateTitle', title)"
  22. @blur="() => $emit('updateTitle', measure.title.trim())"
  23. v-show="isEditTitle"
  24. ref="inputRef"
  25. height="28px"
  26. />
  27. </div>
  28. <div class="actions" @click.stop>
  29. <!-- <ui-icon type="del" ctrl @click.stop="$emit('delete')" v-if="edit" /> -->
  30. <ui-icon
  31. type="pin"
  32. ctrl
  33. @click.stop="fly"
  34. :class="{ disabled: !getMeasureIsShow(measure) }"
  35. />
  36. <ui-more
  37. v-if="edit"
  38. :options="menus"
  39. style="margin-left: 20px"
  40. @click="(action: keyof typeof actions) => actions[action]()"
  41. />
  42. </div>
  43. </ui-group-option>
  44. </template>
  45. <script setup lang="ts">
  46. import { MeasureTypeMeta, getMeasureIsShow } from "@/store";
  47. import { getSceneMeasure, getSceneMeasureDesc } from "@/sdk";
  48. import { useFocus } from "bill/hook/useFocus";
  49. import type { Measure } from "@/store";
  50. import { computed, ref, watch, watchEffect } from "vue";
  51. import { Message } from "bill/index";
  52. import { custom } from "@/env";
  53. import { ui18n } from "@/lang";
  54. const props = withDefaults(defineProps<{ measure: Measure; edit?: boolean }>(), {
  55. edit: true,
  56. });
  57. const emit = defineEmits<{
  58. (e: "delete"): void;
  59. (e: "updateTitle", title: string): void;
  60. }>();
  61. const inputRef = ref();
  62. const isEditTitle = useFocus(computed(() => inputRef.value?.vmRef.root));
  63. const menus = [
  64. { label: ui18n.t("sys.rename"), value: "rename" },
  65. { label: ui18n.t("sys.del"), value: "delete" },
  66. ];
  67. const actions = {
  68. delete: () => emit("delete"),
  69. rename: () => (isEditTitle.value = true),
  70. };
  71. watchEffect(() => {
  72. if (!isEditTitle.value && !props.measure.title.trim().length) {
  73. isEditTitle.value = true;
  74. Message.warning(ui18n.t("measure.nameErr"));
  75. }
  76. });
  77. const fly = () => {
  78. getSceneMeasure(props.measure)?.fly();
  79. };
  80. const desc = ref("-");
  81. watch(
  82. () => [props.measure, custom.showMeasures, custom.showModelsMap],
  83. () => {
  84. const smeasure = getSceneMeasure(props.measure);
  85. desc.value = smeasure ? getSceneMeasureDesc(smeasure, props.measure) : "-";
  86. },
  87. { deep: true, flush: "post", immediate: true }
  88. );
  89. </script>
  90. <style lang="scss" scoped>
  91. .sign-measure {
  92. display: flex;
  93. justify-content: space-between;
  94. align-items: center;
  95. padding: 20px 0;
  96. margin: 0;
  97. border-bottom: 1px solid var(--colors-border-color);
  98. position: relative;
  99. &.active::after {
  100. content: "";
  101. position: absolute;
  102. pointer-events: none;
  103. inset: 0 -20px;
  104. background-color: rgba(0, 200, 175, 0.16);
  105. z-index: -1;
  106. }
  107. .info {
  108. flex: 1;
  109. display: flex;
  110. align-items: center;
  111. .type {
  112. width: 48px;
  113. height: 48px;
  114. border-radius: 4px;
  115. overflow: hidden;
  116. display: flex;
  117. background: rgba(0, 0, 0, 0.5);
  118. font-size: 18px;
  119. align-items: center;
  120. justify-content: center;
  121. }
  122. div {
  123. margin-left: 10px;
  124. p {
  125. color: #fff;
  126. font-size: 14px;
  127. }
  128. span {
  129. color: rgba(255, 255, 255, 0.6);
  130. font-size: 12px;
  131. }
  132. }
  133. }
  134. .actions {
  135. flex: none;
  136. > * {
  137. margin-left: 22px;
  138. }
  139. }
  140. }
  141. </style>
  142. <style>
  143. .view-title-input.ui-input .text.suffix input {
  144. padding-right: 50px;
  145. }
  146. </style>