bill 8 months ago
parent
commit
d30e247c70

+ 11 - 3
src/api/tagging-position.ts

@@ -22,6 +22,8 @@ interface ServicePosition {
   altitudeAboveGround: number
   type: string,
   mat: string
+  fontSize: number,
+  lineHeight: number,
 }
 
 export enum TaggingPositionType {
@@ -34,6 +36,8 @@ export interface TaggingPosition {
   modelId: FuseModel['id']
   normal: SceneLocalPos,
   localPos: SceneLocalPos
+  fontSize: number,
+  lineHeight: number,
   globalVisibility: boolean
 
   altitudeAboveGround: number
@@ -53,9 +57,11 @@ const serviceToLocal = (position: ServicePosition, taggingId?: Tagging['id']): T
   globalVisibility: position.globalVisibility,
   normal: position.normal ? JSON.parse(position.normal) : { x: 0, y: 0, z: 1 },
   mat: position.mat ? JSON.parse(position.mat) : {
-    scale: {x: 1, y: 1, z: 1},
-    rotation: {x: 0, y: 0, z: 0, w: 1}
+    scale: 1,
+    rotation: 0
   },
+  fontSize: position.fontSize || 12,
+  lineHeight: position.lineHeight || 1,
   altitudeAboveGround: position.altitudeAboveGround
 })
 
@@ -68,7 +74,9 @@ const localToService = (position: TaggingPosition, update = false): PartialProps
   type: position.type,
   mat: position.mat && JSON.stringify(position.mat),
   altitudeAboveGround: position.altitudeAboveGround,
-  normal: JSON.stringify(position.normal)
+  normal: JSON.stringify(position.normal),
+  fontSize: position.fontSize,
+  lineHeight: position.lineHeight
 })
 
 

+ 1 - 1
src/api/tagging-style.ts

@@ -42,7 +42,7 @@ export const getStyleTypeName = (id: number, all = styleTypes): string => {
   for (const item of all) {
     if (id === item.id) {
       return item.name
-    } else if ('children' in item) {
+    } else if ('children' in item && item.children) {
       const cname = getStyleTypeName(id, item.children)
       if (cname) {
         return cname

+ 11 - 0
src/assets/style/global.css

@@ -19,3 +19,14 @@ select,
 textarea {
   color: currentColor;
 }
+.ant-slider .ant-slider-track {
+  background: var(--color-main-normal) !important;
+}
+.ant-slider .ant-slider-handle::after {
+  background: #fff !important;
+  box-shadow: 0 0 0 2px #fff !important;
+}
+.ant-slider .ant-slider-rail {
+  background: rgba(255, 255, 255, 0.1) !important;
+  border: 1px solid rgba(255, 255, 255, 0.2) !important;
+}

+ 15 - 0
src/assets/style/global.less

@@ -25,4 +25,19 @@ h1, h2, h3, h4, h5, h6 {
 
 button, input, select, textarea {
   color: currentColor;
+}
+
+.ant-slider .ant-slider-track {
+  background: var(--color-main-normal) !important;
+}
+
+.ant-slider .ant-slider-handle::after {
+  background: #fff !important;
+  box-shadow: 0 0 0 2px #fff !important;
+  
+}
+
+.ant-slider .ant-slider-rail {
+  background: rgba(255,255,255,0.1) !important;
+  border: 1px solid rgba(255,255,255,0.2) !important;
 }

+ 14 - 2
src/components/tagging/sign-new.vue

@@ -154,6 +154,19 @@ tag.bus.on("leave", () => {
 });
 tag.bus.on("click", () => iconClickHandler());
 
+const sHover = ref(isHover.value);
+let timeout: any;
+watchEffect(() => {
+  clearTimeout(timeout);
+  if (isHover.value) {
+    sHover.value = true;
+  } else {
+    timeout = setTimeout(() => {
+      sHover.value = false;
+    }, 100);
+  }
+});
+
 const changePos = () => {
   pos.value = { localPos: tag.getImageCenter(), modelId: props.scenePos.modelId };
 };
@@ -162,8 +175,7 @@ changePos();
 const pullIndex = ref(-1);
 const showContent = computed(() => {
   return (
-    !~pullIndex.value &&
-    (isHover.value || custom.showTaggingPositions.has(props.scenePos))
+    !~pullIndex.value && (sHover.value || custom.showTaggingPositions.has(props.scenePos))
   );
 });
 watchEffect(() => {

+ 2 - 2
src/sdk/sdk.ts

@@ -261,8 +261,8 @@ export type Tagging3DProps = {
   
   // 贴地图片的变换
   mat: {
-    scale: SceneLocalPos,
-    rotation: Rotation,
+    scale: number,
+    rotation: number,
   }
 }
 export type Tagging3D = {

+ 4 - 2
src/store/tagging-positions.ts

@@ -29,9 +29,11 @@ export const createTaggingPosition = (position: Partial<TaggingPosition> = {}):
   globalVisibility: false,
   altitudeAboveGround: 0.5,
   mat: {
-    scale: {x: 1, y: 1, z: 1},
-    rotation: {x: 0, y: 0, z: 0, w: 1}
+    scale: 1,
+    rotation: 0
   },
+  lineHeight: 1,
+  fontSize: 12,
   normal: {x: 0, y: 0, z: 1},
   type: TaggingPositionType['2d'],
   modelId: '',

+ 50 - 14
src/views/tagging-position/index.vue

@@ -1,26 +1,27 @@
 <template>
   <RightFillPano>
-    <ui-group :title="`${tagging?.title}放置位置`" class="position-group">
+    <h3>{{ tagging?.title }}放置位置</h3>
+    <Collapse v-model:activeKey="showId" ghost accordion expandIconPosition="end">
       <PositionSign
         v-for="(position, i) in positions"
         :key="position.id"
         :position="position"
         :title="`位置${i + 1}`"
+        @applyGlobal="(keys) => applyGlobal(position, keys)"
         @delete="deletePosition(position)"
-        @fixed="flyTaggingPosition(position)"
       />
-    </ui-group>
+    </Collapse>
   </RightFillPano>
 </template>
 
 <script lang="ts" setup>
 import PositionSign from "./sign.vue";
 import { router } from "@/router";
-import { Message } from "bill/index";
+import { Dialog, Message } from "bill/index";
 import { RightFillPano } from "@/layout";
 import { asyncTimeout } from "@/utils";
 import { useViewStack } from "@/hook";
-import { computed, nextTick, ref, watchEffect } from "vue";
+import { computed, nextTick, onUnmounted, ref, watch, watchEffect } from "vue";
 import { sdk } from "@/sdk";
 import { showTaggingPositionsStack } from "@/env";
 import {
@@ -33,29 +34,38 @@ import {
   getTagging,
   enterEdit,
 } from "@/store";
+import { Collapse, CollapsePanel } from "ant-design-vue";
 
 import type { TaggingPosition } from "@/store";
 import { distance } from "@/utils/math";
 
+const showId = ref<TaggingPosition["id"]>();
 const tagging = computed(() => getTagging(router.currentRoute.value.params.id as string));
 const positions = computed(() => tagging.value && getTaggingPositions(tagging.value));
 
+watch(showId, (id) => {
+  const position = positions.value?.find((item) => item.id === id);
+  position && flyTaggingPosition(position);
+});
+
+let pop: () => void;
 const flyTaggingPosition = (position: TaggingPosition) => {
+  pop && pop();
   const model = getFuseModel(position.modelId);
   if (!model || !getFuseModelShowVariable(model).value) {
     return;
   }
 
-  const pop = showTaggingPositionsStack.push(ref(new WeakSet([position])));
+  pop = showTaggingPositionsStack.push(ref(new WeakSet([position])));
   sdk.comeTo({
     position: position.localPos,
     modelId: position.modelId,
     dur: 300,
     distance: 3,
   });
-
-  setTimeout(pop, 2000);
+  // setTimeout(pop, 2000);
 };
+onUnmounted(() => pop && pop());
 
 const deletePosition = (position: TaggingPosition) => {
   const index = taggingPositions.value.indexOf(position);
@@ -64,8 +74,25 @@ const deletePosition = (position: TaggingPosition) => {
   }
 };
 
-const cameraPos = ref<SceneLocalPos>();
-sdk.sceneBus.on("cameraChange", (pos) => (cameraPos.value = pos));
+const applyGlobal = async (position: TaggingPosition, keys: string | string[]) => {
+  if (!(await Dialog.confirm("确定要将此属性应用到所有位置?"))) return;
+  keys = Array.isArray(keys) ? keys : [keys];
+  for (const current of positions.value!) {
+    let val: any = current;
+    let newVal: any = position;
+    for (let i = 0; i < keys.length; i++) {
+      if (i === keys.length - 1) {
+        val[keys[i]] = newVal[keys[i]];
+      } else {
+        val = val[keys[i]];
+        newVal = newVal[keys[i]];
+      }
+    }
+  }
+};
+
+// const cameraPos = ref<SceneLocalPos>();
+// sdk.sceneBus.on("cameraChange", (pos) => (cameraPos.value = pos));
 
 watchEffect((onCleanup) => {
   if (tagging.value) {
@@ -86,9 +113,10 @@ watchEffect((onCleanup) => {
         });
         taggingPositions.value.push(storePosition);
 
-        if (cameraPos.value && distance(cameraPos.value, position.worldPos) > 8) {
-          flyTaggingPosition(storePosition);
-        }
+        showId.value = storePosition.id;
+        // if (cameraPos.value && distance(cameraPos.value, position.worldPos) > 8) {
+        //   flyTaggingPosition(storePosition);
+        // }
       }
     };
     sdk.layout.addEventListener("click", clickHandler, false);
@@ -106,7 +134,15 @@ useViewStack(() => {
 });
 </script>
 
-<style lang="scss" scoped src="./style.scss"></style>
+<style lang="scss" scoped>
+h3 {
+  font-family: Microsoft YaHei, Microsoft YaHei;
+  font-weight: bold;
+  font-size: 16px;
+  color: #999999;
+  margin-bottom: 4px;
+}
+</style>
 <style lang="scss">
 .position-group .group-title {
   margin-bottom: 0;

+ 35 - 0
src/views/tagging-position/sign-item.vue

@@ -0,0 +1,35 @@
+<template>
+  <div>
+    <div class="item-header">
+      <span>
+        <slot name="label" v-if="$slots.label" />
+        <template v-else>
+          {{ label }}
+        </template>
+      </span>
+      <span @click="$emit('applyGlobal')" class="apply">应用到全部</span>
+    </div>
+    <div class="item-content" v-if="$slots.default"><slot /></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+defineProps<{ label?: string }>();
+defineEmits<{ (e: "applyGlobal"): void }>();
+</script>
+
+<style lang="scss" scoped>
+.item-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  color: #ffffff;
+  margin-bottom: 14px;
+
+  .apply {
+    color: var(--color-main-normal);
+    cursor: pointer;
+  }
+}
+</style>

+ 96 - 27
src/views/tagging-position/sign.vue

@@ -1,37 +1,106 @@
 <template>
-  <ui-group-option class="sign-position">
-    <div class="info">
-      <div>
-        <p>{{title}}</p>
-      </div>
-    </div>
-    <div class="actions" @click.stop>
-      <ui-icon 
-        type="del" 
-        ctrl  
-        @click.stop="$emit('delete')"
-      />
-      <ui-icon 
-        :class="{disabled: !getTaggingPositionIsShow(position)}"
-        type="pin" 
-        ctrl  
-        @click.stop="$emit('fixed')"
-      />
+  <CollapsePanel header="title" :key="position.id" class="tag-pos-content-item">
+    <template v-slot:header>
+      <p>{{ title }}</p>
+    </template>
+
+    <div class="sign-position">
+      <SignItem
+        label="图标放置方式"
+        class="item"
+        @apply-global="$emit('applyGlobal', 'type')"
+      >
+        <RadioGroup style="width: 100%" v-model:value="position.type">
+          <RadioButton
+            style="width: 50%; text-align: center"
+            :value="TaggingPositionType['2d']"
+            size="middle"
+          >
+            悬浮
+          </RadioButton>
+          <RadioButton
+            style="width: 50%; text-align: center"
+            :value="TaggingPositionType['3d']"
+            size="middle"
+          >
+            贴地/墙
+          </RadioButton>
+        </RadioGroup>
+      </SignItem>
+      <SignItem
+        label="图标大小"
+        class="item"
+        @apply-global="$emit('applyGlobal', ['mat', 'scale'])"
+      >
+        <Slider v-model:value="position.mat.scale" :min="0.1" :max="10" :step="0.1" />
+      </SignItem>
+      <SignItem
+        label="图标角度"
+        class="item"
+        @apply-global="$emit('applyGlobal', ['mat', 'rotation'])"
+      >
+        <Slider v-model:value="position.mat.rotation" :min="0" :max="360" :step="0.1" />
+      </SignItem>
+      <SignItem
+        label="文字大小"
+        class="item"
+        @apply-global="$emit('applyGlobal', 'fontSize')"
+      >
+        <Slider v-model:value="position.fontSize" :min="0" :max="360" :step="0.1" />
+      </SignItem>
+      <SignItem
+        label="引线高度"
+        class="item"
+        @apply-global="$emit('applyGlobal', 'lineHeight')"
+      >
+        <Slider v-model:value="position.lineHeight" :min="0" :max="10" :step="0.1" />
+      </SignItem>
+      <SignItem class="item" @apply-global="$emit('applyGlobal', 'globalVisibility')">
+        <template v-slot:label>
+          <ui-input
+            type="checkbox"
+            label="全部范围可见"
+            v-model="position.globalVisibility"
+          />
+        </template>
+      </SignItem>
+      <Button block type="primary" danger ghost size="large" @click="$emit('delete')"
+        >删除</Button
+      >
     </div>
-  </ui-group-option>
+  </CollapsePanel>
 </template>
 
 <script setup lang="ts">
-import { getTaggingPositionIsShow } from '@/store'
+import { CollapsePanel, Button, RadioGroup, Slider, RadioButton } from "ant-design-vue";
+import SignItem from "./sign-item.vue";
 
-import type { TaggingPosition } from '@/store'
+import type { TaggingPosition } from "@/store";
+import { TaggingPositionType } from "@/api";
 
-defineProps<{ position: TaggingPosition, title: string }>()
-defineEmits<{ 
-  (e: 'delete'): void 
-  (e: 'fixed'): void
-}>()
+defineProps<{ position: TaggingPosition; title: string }>();
 
+defineEmits<{
+  (e: "applyGlobal", k: string | string[]): void;
+  (e: "delete"): void;
+}>();
 </script>
 
-<style lang="scss" scoped src="./style.scss"></style>
+<style lang="scss" scoped src="./style.scss"></style>
+
+<style lang="scss">
+.tag-pos-content-item {
+  border-bottom: 1px solid var(--colors-border-color) !important;
+
+  .ant-collapse-header {
+    padding-left: 0 !important;
+    padding-right: 0 !important;
+  }
+  .ant-collapse-content-box {
+    margin-left: -20px;
+    margin-right: -20px;
+    padding: 20px !important;
+    background: rgba(0, 0, 0, 0.3);
+  }
+}
+</style>

+ 5 - 37
src/views/tagging-position/style.scss

@@ -1,46 +1,14 @@
 
 
 .sign-position {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 20px 0;
+  // display: flex;
+  // justify-content: space-between;
+  // align-items: center;
   margin: 0;
-  border-bottom: 1px solid var(--colors-border-color);
   cursor: pointer;
   position: relative;
-
-  &.active::after {
-    content: '';
-    position: absolute;
-    pointer-events: none;
-    inset: 0 -20px;
-    background-color: rgba(0, 200, 175, 0.16);
-  }
-
-  .info {
-    flex: 1;
-
-    display: flex;
-    align-items: center;
-
-    div {
-      p {
-        color: #fff;
-        font-size: 14px;
-      }
-      span {
-        color: rgba(255,255,255,.6);
-        font-size: 12px;
-      }
-    }
+  .item {
+    margin-bottom: 20px;
   }
-  
-  .actions {
-    flex: none;
-    > * {
-      margin-left: 20px;
-    }
-  }  
 }
 

+ 73 - 59
src/views/tagging/index.vue

@@ -11,18 +11,21 @@
       </ui-group>
     </template>
     <ui-group title="标签列表" class="tagging-list">
+      <template #header>
+        <StyleTypeSelect v-model:value="type" all count />
+      </template>
       <template #icon>
-        <ui-icon 
+        <ui-icon
           ctrl
-          :class="{active: showSearch}"
-          type="search" 
-          @click="showSearch = !showSearch" 
+          :class="{ active: showSearch }"
+          type="search"
+          @click="showSearch = !showSearch"
           style="margin-right: 20px"
         />
-        <ui-icon 
+        <ui-icon
           ctrl
-          :type="custom.showTaggings ? 'eye-s' : 'eye-n'" 
-          @click="custom.showTaggings = !custom.showTaggings" 
+          :type="custom.showTaggings ? 'eye-s' : 'eye-n'"
+          @click="custom.showTaggings = !custom.showTaggings"
         />
       </template>
       <ui-group-option v-if="showSearch">
@@ -32,93 +35,104 @@
           </template>
         </ui-input>
       </ui-group-option>
-      <TagingSign 
-        v-for="tagging in filterTaggings" 
-        :key="tagging.id" 
-        :tagging="tagging" 
+      <TagingSign
+        v-for="tagging in filterTaggings"
+        :key="tagging.id"
+        :tagging="tagging"
         :selected="selectTagging === tagging"
         @edit="editTagging = tagging"
         @delete="deleteTagging(tagging)"
-        @select="selected => selectTagging = selected ? tagging : null"
+        @select="(selected) => (selectTagging = selected ? tagging : null)"
         @fixed="fixedTagging(tagging)"
       />
     </ui-group>
   </RightFillPano>
 
-  <Edit 
-    v-if="editTagging" 
-    :data="editTagging" 
-    @quit="editTagging = null" 
+  <Edit
+    v-if="editTagging"
+    :data="editTagging"
+    @quit="editTagging = null"
     @save="saveHandler"
   />
 </template>
 
 <script lang="ts" setup>
-import Edit from './edit.vue'
-import TagingSign from './sign.vue'
-import { RightFillPano } from '@/layout'
-import { useViewStack } from '@/hook'
-import { computed, ref } from 'vue';
-import { router, RoutesName } from '@/router'
-import { custom } from '@/env'
-import { 
-  taggings, 
-  getTaggingStyle, 
-  Tagging, 
-  autoSaveTaggings, 
+import Edit from "./edit.vue";
+import TagingSign from "./sign.vue";
+import StyleTypeSelect from "./style-type-select.vue";
+import { RightFillPano } from "@/layout";
+import { useViewStack } from "@/hook";
+import { computed, ref } from "vue";
+import { router, RoutesName } from "@/router";
+import { custom } from "@/env";
+import {
+  taggings,
+  getTaggingStyle,
+  Tagging,
+  autoSaveTaggings,
   createTagging,
   getTaggingPositions,
   taggingPositions,
   isOld,
   save,
-  getTagging
-} from '@/store'
+  getTagging,
+  TaggingStyle,
+} from "@/store";
 
-const showSearch = ref(false)
-const keyword = ref('')
-const filterTaggings = computed(() => taggings.value.filter(tagging => tagging.title.includes(keyword.value)))
+const showSearch = ref(false);
+const type = ref<TaggingStyle["typeId"]>(-1);
+const keyword = ref("");
+const filterTaggings = computed(() =>
+  taggings.value.filter((tagging) => {
+    if (!tagging.title.includes(keyword.value)) return false;
+    if (type.value === -1) return true;
+    const style = getTaggingStyle(tagging.styleId);
+    return style?.typeId === type.value;
+  })
+);
 
-const editTagging = ref<Tagging | null>(null)
+const editTagging = ref<Tagging | null>(null);
 const saveHandler = (tagging: Tagging) => {
   if (!editTagging.value) return;
   if (!getTagging(editTagging.value.id)) {
-    taggings.value.push(tagging)
-    const style = getTaggingStyle(tagging.styleId)
+    taggings.value.push(tagging);
+    const style = getTaggingStyle(tagging.styleId);
     if (style) {
-      style.lastUse = 1
+      style.lastUse = 1;
     }
   } else {
-    Object.assign(editTagging.value, tagging)
+    Object.assign(editTagging.value, tagging);
   }
-  
-  editTagging.value = null
-}
+
+  editTagging.value = null;
+};
 
 const deleteTagging = (tagging: Tagging) => {
-  const index = taggings.value.indexOf(tagging)
-  const positions = getTaggingPositions(tagging)
-  taggingPositions.value = taggingPositions.value.filter(position => !positions.includes(position))
-  taggings.value.splice(index, 1)
-}
+  const index = taggings.value.indexOf(tagging);
+  const positions = getTaggingPositions(tagging);
+  taggingPositions.value = taggingPositions.value.filter(
+    (position) => !positions.includes(position)
+  );
+  taggings.value.splice(index, 1);
+};
 
 const fixedTagging = async (tagging: Tagging) => {
   if (isOld.value) {
-    await save()
+    await save();
   }
-  router.push({ name: RoutesName.taggingPosition, params: { id: tagging.id } })
-}
+  router.push({ name: RoutesName.taggingPosition, params: { id: tagging.id } });
+};
 
-
-const selectTagging = ref<Tagging | null>(null)
-useViewStack(autoSaveTaggings)
+const selectTagging = ref<Tagging | null>(null);
+useViewStack(autoSaveTaggings);
 </script>
 
 <style scoped>
-  .active {
-    color: var(--color-main-normal) !important;
-  }
+.active {
+  color: var(--color-main-normal) !important;
+}
 
-  .tagging-list {
-    padding-bottom: 30px;
-  }
-</style>
+.tagging-list {
+  padding-bottom: 30px;
+}
+</style>

+ 56 - 13
src/views/tagging/style-type-select.vue

@@ -1,13 +1,16 @@
 <template>
   <!-- <Menu style="width: 256px" mode="vertical" :items="getItems()" @click="handleClick" /> -->
   <Dropdown placement="bottom">
-    <span>{{ text }} <DownOutlined /></span>
+    <span
+      >{{ current.title }}<span class="count">({{ current.count }})</span>
+      <DownOutlined
+    /></span>
     <template #overlay>
       <Menu
         style="width: 120px"
         :active-key="value.toString()"
         mode="vertical"
-        :items="getItems()"
+        :items="items"
         @click="handleClick"
       />
     </template>
@@ -16,30 +19,70 @@
 
 <script lang="ts" setup>
 import { getStyleTypeName, styleTypes } from "@/api";
-import { computed } from "vue";
+import { computed, reactive, ref, shallowReactive, watchEffect } from "vue";
 import { Menu, Dropdown } from "ant-design-vue";
 import { DownOutlined } from "@ant-design/icons-vue";
+import { taggings, getTaggingStyle } from "@/store";
 
-const props = defineProps<{ value: number }>();
+const props = defineProps<{ value: number; all?: boolean; count?: boolean }>();
 const emit = defineEmits<{ (e: "update:value", v: number): void }>();
-const text = computed(() => getStyleTypeName(props.value));
-
+const allType = { name: "全部", id: -1 };
 const getItems = (types = styleTypes): any => {
-  return types.map((item) => ({
-    label: item.name,
-    title: item.name,
-    key: item.id,
-    children: "children" in item ? getItems(item.children) : null,
-  }));
+  return types.map((item) => {
+    let count = 0;
+    if (props.count) {
+      if (item.id === allType.id) {
+        count = taggings.value.length;
+      } else {
+        count = taggings.value.filter((tag) => {
+          return getTaggingStyle(tag.styleId)?.typeId === item.id;
+        }).length;
+      }
+    }
+    return {
+      label: item.name + `(${count})`,
+      title: item.name,
+      count,
+      key: item.id,
+      children: "children" in item ? getItems(item.children) : null,
+    };
+  });
+};
+const getCurrentItem = (type: number, all = items.value) => {
+  for (const item of all) {
+    if (type === item.key) {
+      return item;
+    } else if ("children" in item && item.children) {
+      const citem = getStyleTypeName(type, item.children);
+      if (citem) {
+        return citem;
+      }
+    }
+  }
 };
+const types = computed(() => {
+  if (props.all) {
+    return [allType, ...styleTypes];
+  } else {
+    return styleTypes;
+  }
+});
+const items = computed(() => getItems(types.value));
+const text = computed(() => getStyleTypeName(props.value, types.value));
+const current = computed(() => getCurrentItem(props.value));
+
 const handleClick = (info: any) => {
   emit("update:value", info.key);
 };
 </script>
 
-<style scoped>
+<style scoped lang="scss">
 span {
   font-size: 14px;
   cursor: pointer;
+
+  .count {
+    color: var(--color-main-normal);
+  }
 }
 </style>