快乐的梦鱼 месяцев назад: 2
Родитель
Сommit
12feaf5b00
67 измененных файлов с 4409 добавлено и 516 удалено
  1. 3525 411
      package-lock.json
  2. 6 6
      package.json
  3. 4 4
      src/App.vue
  4. 40 0
      src/common/components/parts/Box.vue
  5. 59 0
      src/common/components/parts/Box2LineImageRightShadow.vue
  6. 145 0
      src/common/components/parts/Box2LineLargeImageUserShadow.vue
  7. 25 0
      src/common/components/parts/Box2LineRightShadow.vue
  8. 19 0
      src/common/components/parts/Box2LineRightSlot.vue
  9. 63 0
      src/common/components/parts/ImageGrid.vue
  10. 51 0
      src/common/components/parts/ImageSwiper.vue
  11. 52 0
      src/common/components/parts/RoundTags.vue
  12. 0 35
      src/common/composeabe/TabControl.ts
  13. 1 0
      src/common/config/AppCofig.ts
  14. 6 3
      src/components/display/loading/Loadmore.vue
  15. 7 5
      src/components/display/title/SubTitle.vue
  16. 4 1
      src/components/feedback/Touchable.vue
  17. 2 1
      src/components/layout/BaseView.ts
  18. 6 3
      src/components/layout/FlexView.vue
  19. 3 2
      src/components/layout/grid/Grid.vue
  20. 21 23
      src/components/layout/grid/GridItem.vue
  21. 7 0
      src/components/layout/space/Height.vue
  22. 7 0
      src/components/layout/space/Width.vue
  23. 2 0
      src/components/nav/NavBar.vue
  24. 23 6
      src/components/nav/TabBarItem.vue
  25. 5 0
      src/components/theme/Theme.ts
  26. 10 1
      src/components/theme/ThemeDefine.ts
  27. 1 5
      src/pages.json
  28. 1 0
      src/pages/dig/index.vue
  29. 79 0
      src/pages/home/discover/index.vue
  30. 193 0
      src/pages/home/index.vue
  31. 41 9
      src/pages/index.vue
  32. 1 1
      src/pages/user/index.vue
  33. BIN
      src/static/images/BackgroundMask.jpg
  34. BIN
      src/static/images/MapArrow.png
  35. BIN
      src/static/images/home/UserHead.png
  36. BIN
      src/static/images/home/icon-ancient-gate.png
  37. BIN
      src/static/images/home/icon-article.png
  38. BIN
      src/static/images/home/icon-compass.png
  39. BIN
      src/static/images/home/icon-pin-distance.png
  40. BIN
      src/static/images/home/icon-shining.png
  41. BIN
      src/static/images/icons/icon-.png
  42. BIN
      src/static/images/icons/icon-all.png
  43. BIN
      src/static/images/icons/icon-buliding.png
  44. BIN
      src/static/images/icons/icon-camera.png
  45. BIN
      src/static/images/icons/icon-envirounment.png
  46. BIN
      src/static/images/icons/icon-exampke.png
  47. BIN
      src/static/images/icons/icon-foods.png
  48. BIN
      src/static/images/icons/icon-history.png
  49. BIN
      src/static/images/icons/icon-location.png
  50. BIN
      src/static/images/icons/icon-mark.png
  51. BIN
      src/static/images/icons/icon-pac.png
  52. BIN
      src/static/images/icons/icon-resource.png
  53. BIN
      src/static/images/icons/icon-route.png
  54. BIN
      src/static/images/tabs/dig.png
  55. BIN
      src/static/images/tabs/discover-active.png
  56. BIN
      src/static/images/tabs/discover.png
  57. BIN
      src/static/images/tabs/home-active.png
  58. BIN
      src/static/images/tabs/home.png
  59. BIN
      src/static/images/tabs/store-active.png
  60. BIN
      src/static/images/tabs/store.png
  61. BIN
      src/static/images/tabs/user-active.png
  62. BIN
      src/static/images/tabs/user.png
  63. BIN
      src/static/images/user/avatar.png
  64. BIN
      src/static/images/user/icon-chat.png
  65. BIN
      src/static/images/user/icon-edit.png
  66. BIN
      src/static/images/user/icon-function.png
  67. BIN
      src/static/images/user/icon-profile.png

Разница между файлами не показана из-за своего большого размера
+ 3525 - 411
package-lock.json


+ 6 - 6
package.json

@@ -62,12 +62,12 @@
     "vue-router": "^4.6.3"
   },
   "devDependencies": {
-    "@dcloudio/types": "3.4.25",
-    "@dcloudio/uni-automator": "3.0.0-4070620250821001",
-    "@dcloudio/uni-cli-shared": "3.0.0-4070620250821001",
-    "@dcloudio/uni-stacktracey": "3.0.0-4070620250821001",
-    "@dcloudio/vite-plugin-uni": "3.0.0-4070620250821001",
-    "@vue/runtime-core": "3.5.22",
+    "@dcloudio/types": "3.4.28",
+    "@dcloudio/uni-automator": "3.0.0-4080520251106001",
+    "@dcloudio/uni-cli-shared": "3.0.0-4080520251106001",
+    "@dcloudio/uni-stacktracey": "3.0.0-4080520251106001",
+    "@dcloudio/vite-plugin-uni": "3.0.0-4080520251106001",
+    "@vue/runtime-core": "3.5.24",
     "@vue/tsconfig": "^0.1.3",
     "sass": "^1.86.0",
     "typescript": "^4.9.4",

+ 4 - 4
src/App.vue

@@ -33,9 +33,9 @@ onLaunch(async () => {
 
 //修改默认主题颜色
 configTheme((theme) => {
-  theme.colorConfigs.default.primary = '#ff8719';
-  theme.colorConfigs.pressed.primary = '#b86212';
-  theme.colorConfigs.background.primary = '#ffe8d2';
+  theme.colorConfigs.default.primary = '#00ca76';
+  theme.colorConfigs.pressed.primary = '#00814b';
+  theme.colorConfigs.background.primary = '#b4ffe0';
   return theme;
 });
 </script>
@@ -46,6 +46,6 @@ configTheme((theme) => {
   @import "@/components/index.scss";
 
   page {
-    background: #F8F8F8;
+    background: #f7f8f9;
   }
 </style>

+ 40 - 0
src/common/components/parts/Box.vue

@@ -0,0 +1,40 @@
+<template>
+  <FlexCol backgroundColor="white" :padding="25" :radius="20" shadow="light">
+    <SubTitle :title="title" :showMore="showMore" @moreClicked="emit('moreClicked')">
+      <template #left>
+        <FlexRow align="center">
+          <Icon :icon="icon" :size="36" />
+          <Width :width="15" />
+          <Text :text="title" :fontSize="30" bold />
+        </FlexRow>
+      </template>
+    </SubTitle>
+    <slot />
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import Icon from '@/components/basic/Icon.vue';
+import Text from '@/components/basic/Text.vue';
+import SubTitle from '@/components/display/title/SubTitle.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Width from '@/components/layout/space/Width.vue';
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  icon: {
+    type: String,
+    default: '',
+  },
+  showMore: {
+    type: Boolean,
+    default: false,
+  },
+});
+const emit = defineEmits(['moreClicked'])
+
+</script>

+ 59 - 0
src/common/components/parts/Box2LineImageRightShadow.vue

@@ -0,0 +1,59 @@
+<template>
+  <view 
+    class="d-flex flex-row flex-grow-1 justify-between shadow-s 
+    radius-base mb-3 p-2 pt-3 pb-3 overflow-hidden
+    border-all-light-light-primary"
+    :style="{ 
+      height: 'calc(100% - 20rpx)',
+      width: 'calc(100% - 10rpx)',
+    }"
+    @click="$emit('click')"
+  >
+    <view class="d-flex flex-row w-100">
+      <image 
+        :class="[
+          wideImage ? 'width-250' : 'width-150', 
+          'height-150', 
+          'radius-base',
+          'flex-shrink-0'
+        ]"
+        :src="image"
+        mode="aspectFill"
+      />
+      <view class="d-flex flex-col ml-3 flex-one width-500">
+        <text :class="[
+          'color-primary size-base',
+          desc || title1 ? 'text-lines-1' : 'text-lines-2',
+        ]">{{ title }}</text>
+        <text class="size-s color-second text-lines-2 mt-2">{{ desc }}</text>
+        <RoundTags v-if="tags" :tags="tags" small />
+      </view>
+    </view>
+    <text class="color-primary-second-text size-ss">{{ right }}</text>
+  </view>
+</template>
+
+<script setup lang="ts">
+import type { PropType } from 'vue';
+import RoundTags from './RoundTags.vue';
+
+defineProps({
+  title: String,
+  desc: String,
+  right: String,
+  image: String,
+
+  tags: {
+    type: Array as PropType<string[]>,
+    default: null
+  },
+  wideImage: {
+    type: Boolean,
+    default: false,
+  },
+  title1: {
+    type: Boolean,
+    default: false,
+  }
+})
+</script>

+ 145 - 0
src/common/components/parts/Box2LineLargeImageUserShadow.vue

@@ -0,0 +1,145 @@
+<template>
+  <view 
+    :class="[
+      'position-relative grid4-item',
+      'd-flex flex-col shadow-l radius-l bg-base p-2 mb-2 overflow-hidden',
+      'border-all-light-light-primary',
+      classNames,
+      fixSize ? 'flex-shrink-0' : ' flex-grow-1',
+    ]"
+    
+    :style="{ 
+      height: 'calc(100% - 20rpx)',
+      width: fixSize ? undefined : 'calc(100% - 10rpx)',
+    }"
+    @click="$emit('click')"
+  >
+    <image 
+      v-if="image" 
+      class="w-100 height-300 radius-base" 
+      :src="image" 
+      mode="aspectFill" 
+    />
+    <image 
+      v-if="videoMark" 
+      class="width-60 mr-2 video-mark" 
+      :src="PlayVideo" mode="widthFix" 
+    />
+    <view v-if="userName" class="d-flex flex-row align-center mt-2">
+      <image class="width-60 mr-2" :src="userHead" mode="widthFix" />
+      <text class="size-s">{{ userName }}</text>
+    </view>
+    <text 
+      :class="[
+        `color-${titleColor}`,
+        title1 || desc ? 'text-lines-1' : 'text-lines-2',
+        'mt-2',
+      ]"
+    >
+      {{ title }}
+    </text>
+    <text v-if="badge" class="position-absolute color-primary-text size-s bg-light-primary radius-base p-1 radius-s text-lines-1 r-0 t-0 mr-3 mt-3">{{ badge }}</text>
+    <text v-if="desc" class="color-second text-lines-2 mt-2">{{ desc }}</text>
+    <view v-if="likes !== undefined && comment !== undefined" class="d-flex flex-row mt-2">
+      <image class="width-40 mr-2" :src="IconHeart" mode="widthFix" />
+      <text class="size-s mr-3">{{ likes }}</text>
+      <image class="width-40 mr-2" :src="IconChat" mode="widthFix" />
+      <text class="size-s">{{ comment }}</text>
+    </view>
+    <RoundTags v-if="tags" :tags="tags" small />
+    <view v-if="bottomTime" class="d-flex flex-row mt-2">
+      <image class="width-40 mr-2" :src="IconTime" mode="widthFix" />
+      <text class="size-s mr-3">{{ bottomTime }}</text>
+    </view>
+    <view v-if="bottomLocate" class="d-flex flex-row justify-between mt-2">
+      <view class="d-flex flex-row align-center">
+        <image class="width-40 mr-2" :src="IconLocation" mode="widthFix" />
+        <text class="size-s">{{ bottomLocate }}</text>
+      </view>
+      <view class="d-flex flex-row align-center">
+        <image class="width-40 mr-2" :src="IconStar" mode="widthFix" />
+        <text class="size-s">{{ bottomScore }}</text>
+      </view>
+    </view> 
+  </view>
+</template>
+
+<script setup lang="ts">
+import type { PropType } from 'vue';
+import RoundTags from './RoundTags.vue';
+
+const IconHeart = 'https://mncdn.wenlvti.net/app_static/minnan/images/discover/IconHeart.png';
+const IconChat = 'https://mncdn.wenlvti.net/app_static/minnan/images/discover/IconChat.png';
+const IconLocation = 'https://mncdn.wenlvti.net/app_static/minnan/images/inhert/IconLocation.png';
+const IconTime = 'https://mncdn.wenlvti.net/app_static/minnan/images/inhert/IconTime.png';
+const IconStar = 'https://mncdn.wenlvti.net/app_static/minnan/images/inhert/IconStar.png';
+const PlayVideo = 'https://mncdn.wenlvti.net/app_static/minnan/images/inhert/PlayVideo.png';
+
+defineProps({
+  classNames: {
+    type: String,
+  },
+  title: {
+    type: String,
+  },
+  titleColor: {
+    type: String,
+    default: 'primary',
+  },
+  title1: {
+    type: Boolean,
+    default: false,
+  },
+  fixSize: {
+    type: Boolean,
+    default: false,
+  },
+  badge: {
+    type: String,
+    default: '',
+  },
+  tags: {
+    type: Array as PropType<string[]>,
+    default: [],
+  },
+  videoMark: {
+    type: Boolean,
+    default: false,
+  },
+  desc: {
+    type: String,
+  },
+  image: {
+    type: String,
+  },
+  likes: {
+    type: Number,
+  },
+  comment: {
+    type: Number,
+  },
+  userHead: {
+    type: String,
+  },
+  userName: {
+    type: String,
+  },
+  bottomLocate: {
+    type: String,
+  },
+  bottomScore: {
+    type: String,
+  },
+  bottomTime: {
+    type: String,
+  },
+})
+</script>
+
+<style scoped>
+.video-mark {
+  position: absolute;
+  left: calc(50% - 20rpx);
+  top: 145rpx
+}
+</style>

+ 25 - 0
src/common/components/parts/Box2LineRightShadow.vue

@@ -0,0 +1,25 @@
+<template>
+  <view 
+    class="d-flex flex-row justify-between shadow-s radius-base mt-3 p-2"
+    @click="$emit('click')"
+  >
+    <view class="d-flex flex-row">
+      <slot name="left" />
+      <view class="d-flex flex-col ml-3 flex-one">
+        <text class="color-primary">{{ title }}</text>
+        <text class="color-second mt-2">{{ desc }}</text>
+      </view>
+    </view>
+    <text v-if="right" class="color-primary-second-text size-ss">{{ right }}</text>
+    <text v-else class="color-title-text iconfont icon-arrow-right"></text>
+  
+  </view>
+</template>
+
+<script setup lang="ts">
+defineProps({
+  title: String,
+  desc: String,
+  right: String,
+})
+</script>

+ 19 - 0
src/common/components/parts/Box2LineRightSlot.vue

@@ -0,0 +1,19 @@
+<template>
+  <view 
+    class="d-flex w-100 flex-row align-center bg-light-light-primary radius-base mt-2 p-2 pb-3 pt-3"
+    @click="$emit('click')"
+  >
+    <view class="d-flex flex-col ml-3 flex-one">
+      <text class="color-primary">{{ title }}</text>
+      <text class="color-primary-second-text">{{ desc }}</text>
+    </view>
+    <slot />
+  </view>
+</template>
+
+<script setup lang="ts">
+defineProps({
+  title: String,
+  desc: String,
+})
+</script>

+ 63 - 0
src/common/components/parts/ImageGrid.vue

@@ -0,0 +1,63 @@
+<template>
+  <view class="w-100 d-flex flex-row flex-wrap" :style="{ gap: `${gap}rpx` }">
+    <image 
+      v-for="(v, k) in images"
+      :key="k"
+      :src="imagekey ? v[imagekey] : v" 
+      :style="{ 
+        width: `calc(${100 / rowCount}% - ${gap}rpx)`,
+        height: imageHeight,
+        borderRadius: '10rpx',
+      }"
+      mode="aspectFill"
+      @click="itemClick(v, k)"
+    />
+  </view>
+</template>
+
+<script setup lang="ts">
+import type { PropType } from 'vue';
+
+const props = defineProps({	
+  rowCount : {
+    type: Number,
+    default: 3,
+  },
+  imagekey : {
+    type: String,
+    default: undefined,
+  },
+  imageHeight : {
+    type: String,
+    default: undefined,
+  },
+  gap: {
+    type: Number,
+    default: 10,
+  },
+  images: {
+    type: Object as PropType<any[]>,
+    default: null,
+  },
+  preview: {  
+    type: Boolean,
+    default: false,
+  }
+})
+
+const emit = defineEmits([	
+  "itemClick"	
+])
+
+function itemClick(item: any, index: number) {
+  if (props.preview) {
+    uni.previewImage({
+      urls: props.images.map((v: any) => (props.imagekey ? v[props.imagekey] : v) || v),
+      current: (props.imagekey ? item[props.imagekey] : item) || item,
+    })
+  } else {
+    emit('itemClick', item, index);
+  }
+}
+
+</script>

+ 51 - 0
src/common/components/parts/ImageSwiper.vue

@@ -0,0 +1,51 @@
+<template>
+  <swiper 
+    class="image-swiper" 
+    circular
+    :indicator-dots="true"
+    :autoplay="true"
+    :interval="2000"
+    :duration="1000"
+    :style="{
+      height: `${height}rpx`,
+    }"
+  >
+    <swiper-item v-for="(item, key) in images" :key="key">
+      <view class="item">
+        <image
+          :src="item"
+          class="w-100 radius-base"
+          mode="aspectFill"
+          @click="onPreviewImage(key)"
+        />
+      </view>
+    </swiper-item>
+  </swiper>
+</template>
+
+<script setup lang="ts">
+import { useSwiperImagePreview } from '@/common/composeabe/SwiperImagePreview';
+import type { PropType } from 'vue';
+
+const props = defineProps({
+  images: {
+    type: Array as PropType<string[]>,
+    default: () => [],
+  },
+  height: {
+    type: Number as PropType<number>,
+    default: 400,
+  },
+})
+
+const { onPreviewImage } = useSwiperImagePreview(() => props.images || [])
+
+</script>
+
+<style lang="scss">
+.image-swiper {
+  image {
+    border-radius: 20rpx;
+  }
+}
+</style>

+ 52 - 0
src/common/components/parts/RoundTags.vue

@@ -0,0 +1,52 @@
+<template>
+  <view 
+    class="d-flex flex-row flex-wrap mt-2"
+  >
+    <view 
+      v-for="(tag, k) in tags"
+      :key="k" 
+      class="bg-place mr-2 mb-2"
+      :style="{
+        maxWidth: '160rpx',
+        overflow: 'hidden',
+        textOverflow: 'ellipsis',
+        whiteSpace: 'nowrap',
+      }"
+      :class="[
+        tag ? '' : 'd-none',
+        small ? 'radius-l p-2 pt-0 pb-1' : 'radius-ll p-25 pt-1',
+      ]"
+    >
+      <text class="color-text-content-second size-ss">{{ tag }}</text>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { computed, type PropType } from 'vue';
+
+const props = defineProps({
+  tags: {
+    type: Array as PropType<string[]>,
+    default: null,
+  },
+  tags2: {
+    type: Array as PropType<string[]>,
+    default: null,
+  },
+  small: {
+    type: Boolean,
+    default: false, 
+  }
+})
+
+const tagss = computed(() => {
+  if (props.tags && props.tags.length > 0) {
+    return props.tags;
+  } else if (props.tags2) {
+    return props.tags2;
+  } else {
+    return [];
+  }
+})
+</script>

+ 0 - 35
src/common/composeabe/TabControl.ts

@@ -1,35 +0,0 @@
-import { computed, ref, watch } from "vue";
-
-export interface TabControlItem {
-  name: string,
-  [key: string]: any,
-}
-
-export function useTabControl(options: {
-  tabs?: TabControlItem[],
-  onTabChange?: (tab: number, tabId: number) => void,
-}) {
-
-  const tabCurrentIndex = ref(0)
-  const tabCurrentId = ref(0)
-  const tabsArray = ref<TabControlItem[]>(options.tabs ?? []);
-
-  watch(tabCurrentIndex, (v) => {
-    options.onTabChange?.(v, tabCurrentId.value) 
-  })
-
-  const tabs = computed(() => {
-    return tabsArray.value.filter(t => t.visible !== false)
-  })
-
-  return {
-    tabCurrentId,
-    tabCurrentIndex,
-    tabs,
-    tabsArray,
-    onTabClick(e: any) {
-      tabCurrentIndex.value = e.index
-      tabCurrentId.value = e.id
-    }
-  }
-}

+ 1 - 0
src/common/config/AppCofig.ts

@@ -7,6 +7,7 @@ export default {
   appId: 'wx954621c03f2fa912',
   qqMapKey: 'TOIBZ-CA4WB-OFQUF-J3XG4-EEB2J-DXBX7',
   amapKey: '34eb1d57f93720a871bd11a90af0c91c',
+  defaultLonLat: [ 118.161270, 24.529196 ],
   noLoginPages: [
     '/pages/user/login',
     '/pages/user/register',

+ 6 - 3
src/components/display/loading/Loadmore.vue

@@ -66,12 +66,15 @@ const text = computed(() => {
 <template>
   <FlexRow 
     :padding="[10,20]"
+    justify="center"
     align="center" 
     :backgroundColor="themeContext.resolveThemeColor('LoadMoreBackgroundColor', 'background.bar')" 
     v-bind="$attrs"
   >
-    <ActivityIndicator v-if="props.status === 'loading'" :size="30" />
-    <Width :size="20" />
-    <Text :text="text" />
+    <template v-if="props.status === 'loading'">
+      <ActivityIndicator  :size="30" />
+      <Width :size="20" />
+    </template>
+    <Text :text="text" fontConfig="subText" />
   </FlexRow>
 </template>

+ 7 - 5
src/components/display/title/SubTitle.vue

@@ -38,13 +38,15 @@ const badgeStyle = theme.useThemeStyle({
 </script>
 
 <template>
-  <FlexRow justify="space-between" align="center" :padding="[ 30, 0 ]">
+  <FlexRow justify="space-between" align="center" :padding="[ 0, 0, 30, 0 ]">
     <slot name="left">
       <FlexRow align="center">
-        <view :style="{
-          ...badgeStyle,
-          backgroundColor: theme.resolveThemeColor(badgeColor),
-        }"></view>
+        <slot name="icon">
+          <view :style="{
+            ...badgeStyle,
+            backgroundColor: theme.resolveThemeColor(badgeColor),
+          }"></view>
+        </slot>
         <Text fontConfig="h4" :text="title" />
       </FlexRow>
     </slot>

+ 4 - 1
src/components/feedback/Touchable.vue

@@ -32,21 +32,24 @@ import type { FlexProps } from '../layout/FlexView.vue';
 export interface TouchableFlexProps extends FlexProps {
   /**
    * 是否可以点击
+   * @default true
    */
   touchable?: boolean,
   /**
    * 按下时的颜色
+   * @default 'background.press'
    */
   pressedColor?: string,
   /**
    * 按下时的透明度(仅在 pressedColor 未设置时有效)
+   * @default 0.7
    */
   activeOpacity?: number,
 }
 
 const props = withDefaults(defineProps<TouchableFlexProps>(), {
   activeOpacity: 0.7,
-  touchable: false,
+  touchable: true,
 });
 
 const themeContext = useTheme();

+ 2 - 1
src/components/layout/BaseView.ts

@@ -24,9 +24,10 @@ export function useBaseViewStyleBuilder(props: FlexProps) {
       gap: themeContext.resolveThemeSize(props.gap),
       borderRadius: themeContext.resolveThemeSize(props.radius),
       overflow: props.overflow,
+      boxShadow: props.shadow ? themeContext.getVar('shadow.' + props.shadow, undefined) : undefined,
       ...(props.innerStyle ? props.innerStyle : {}),
     }
-    
+
     //内边距样式
     configPadding(obj, themeContext.theme, props.padding as any);
     //外边距样式

+ 6 - 3
src/components/layout/FlexView.vue

@@ -19,9 +19,8 @@
 /**
  * 组件说明:Flex组件,用于一些布局中快速写容器,是一系列盒子的基础组件。
  */
-import { computed, getCurrentInstance, onMounted, ref } from 'vue';
-import { useTheme, type ThemePaddingMargin } from '../theme/ThemeDefine';
-import { configMargin, configPadding } from '../theme/ThemeTools';
+import { computed, getCurrentInstance } from 'vue';
+import { type ThemePaddingMargin } from '../theme/ThemeDefine';
 import { RandomUtils } from '@imengyu/imengyu-utils';
 import { useBaseViewStyleBuilder } from './BaseView';
 
@@ -112,6 +111,10 @@ export interface FlexProps {
    */
   backgroundColor?: string,
   /**
+   * 阴影。使用主体中的阴影预设。
+   */
+  shadow?: string,
+  /**
    * 宽度
    */
   width?: number|string,

+ 3 - 2
src/components/layout/grid/Grid.vue

@@ -8,7 +8,7 @@
 <script setup lang="ts">
 import { useChildLinkParent } from '@/components/composeabe/ChildItem';
 import { propGetThemeVar, useTheme } from '@/components/theme/ThemeDefine';
-import { computed, onMounted, onUpdated, provide, toRef, type VNode } from 'vue';
+import { computed, provide, toRef } from 'vue';
 
 /**
  * 宫格可以在水平方向上把页面分隔成等宽度的区块, 主要使用场景如:热门内容等。
@@ -105,8 +105,9 @@ const itemSize = computed(() => {
     cutSizes.push(cutSize);
     cutSizes.push(cutSize);
   }
+  cutSizes.push(borderWidth.value!);
 
-  return `calc(${flexBasis}% - ${cutSizes.join(' - ')} - ${borderWidth.value})`;
+  return `calc(${flexBasis}% - ${cutSizes.join(' - ')})`;
 });
 
 const {

+ 21 - 23
src/components/layout/grid/GridItem.vue

@@ -1,7 +1,7 @@
 <template>
   <Touchable
     center
-    :direction="(flexDirection as FlexDirection)"
+    :direction="flexDirection"
     :innerStyle="style"
     :touchable="touchable"
     :radius="themeContext.resolveThemeSize(radius)"
@@ -42,16 +42,13 @@
 import type { IconProps } from '@/components/basic/Icon.vue';
 import { propGetThemeVar, useTheme } from '@/components/theme/ThemeDefine';
 import { computed, inject } from 'vue';
-import { type FlexDirection } from '../FlexView.vue';
-import Text from '@/components/basic/Text.vue';
-import Icon from '@/components/basic/Icon.vue';
+import type { FlexDirection } from '../FlexView.vue';
 import Touchable from '@/components/feedback/Touchable.vue';
+import Icon from '@/components/basic/Icon.vue';
+import Text from '@/components/basic/Text.vue';
 
-/**
- * 网格块按钮。包含一个图标和文字。
- */
 
-export interface GridItemProps {
+export interface GridItemProps {  
   /**
    * 标题
    */
@@ -104,7 +101,7 @@ export interface GridItemProps {
   /**
    * 如果填写字段,则在图片位置显示文字。同时icon不生效。
    */
-  subTitle?: string|false;
+  subTitle?: string;
   /**
    * 图片位置显示文字的颜色。
    * @default Color.text
@@ -129,15 +126,6 @@ export interface GridItemProps {
   radius?: number|string,
 }
 
-defineOptions({
-  options: {
-    virtualHost: true
-  }
-})
-const emit = defineEmits([ 'click' ]);
-
-const themeContext = useTheme();
-
 const props = withDefaults(defineProps<GridItemProps>(), {
   highlightColor: () => propGetThemeVar('GridItemHighlightColor', 'pressed.white'),
   backgroundColor: () => propGetThemeVar('GridItemBackgroundColor', 'white'),
@@ -145,14 +133,17 @@ const props = withDefaults(defineProps<GridItemProps>(), {
   iconSize: () => propGetThemeVar('GridItemIconSize', 42),
   iconStyle: () => propGetThemeVar('GridItemIconStyle', {}),
   radius: () => propGetThemeVar('GridItemRadius', 0),
-  subTitle: () => propGetThemeVar('GridItemSubTitle', false),
+  subTitle: () => propGetThemeVar('GridItemSubTitle', ''),
   subTitleStyle: () => propGetThemeVar('GridItemSubTitleStyle', {}),
   subTitleColor: () => propGetThemeVar('GridItemSubTitleColor', 'text.second'),
   titleColor: () => propGetThemeVar('GridItemTitleColor', 'text.title'),
   titleStyle: () => propGetThemeVar('GridItemTitleStyle', {}),
   innerStyle: () => propGetThemeVar('GridItemStyle', {}),
   direction: () => propGetThemeVar('GridItemDirection', inject<any>('gridContext').direction.value),
-})
+});
+
+const emit = defineEmits([ 'click' ]);
+const themeContext = useTheme();
 
 const {
   borderWidth,
@@ -167,9 +158,11 @@ const {
 const position = computed(() => getPosition());
 
 const flexDirection = computed(() => 
-  (props.direction.startsWith('horizontal') ? 'row' : 'column') + (props.direction.endsWith('-reverse') ? '-reverse' : '')
+  (props.direction.startsWith('horizontal') ? 'row' : 'column') + 
+  (props.direction.endsWith('-reverse') ? '-reverse' : '') as FlexDirection
 )
 
+
 const iconStyle = computed(() => {
   return {
     margin: themeContext.resolveThemeSize('GridItemIconMarginVertical', 10) + ' ' + themeContext.resolveThemeSize('GridItemIconMarginHorizontal', 8),
@@ -191,11 +184,11 @@ const titleImageStyle = computed(() => {
 const style = computed(() => {
   const o : Record<string, any> = {
     display: 'flex',
-    // #ifdef APP-NVUE || UNI-APP-X 
+    // #ifdef APP-NVUE
     width: itemSize.value,
     minWidth: itemSize.value,
     // #endif
-    // #ifndef APP-NVUE || UNI-APP-X 
+    // #ifndef APP-NVUE
     flexBasis: itemSize.value,
     // #endif
     aspectRatio: square.value ? 1 : undefined,
@@ -214,4 +207,9 @@ const style = computed(() => {
   return o;
 })
 
+defineOptions({
+  options: {
+    virtualHost: true
+  }
+})
 </script>

+ 7 - 0
src/components/layout/space/Height.vue

@@ -27,6 +27,13 @@ defineProps({
   },
 })
 
+defineOptions({
+  options: {
+    inheritAttrs: false,
+    virtualHost: true,
+  }
+})
+
 const { resolveThemeSize } = useTheme();
 </script>
 

+ 7 - 0
src/components/layout/space/Width.vue

@@ -22,6 +22,13 @@ defineProps({
 })
 
 const { resolveThemeSize } = useTheme();
+
+defineOptions({
+  options: {
+    inheritAttrs: false,
+    virtualHost: true,
+  }
+})
 </script>
 
 <script lang="ts">

+ 2 - 0
src/components/nav/NavBar.vue

@@ -4,10 +4,12 @@
     :style="{
       backgroundColor: theme.resolveThemeColor(backgroundColor),
       height: theme.resolveThemeSize(height),
+      paddingLeft: align === 'left' ? theme.resolveThemeSize('NavBarLeftModePaddingLeft', 20) : undefined,
       ...props.innerStyle,
     }"
   >
     <view 
+      v-if="align !== 'left'"
       class="nana-nav-button-wrapper" :style="{
         marginRight: rightPillSpace ? `${menuButtonInfo.width}px` : undefined,
       }"

+ 23 - 6
src/components/nav/TabBarItem.vue

@@ -10,9 +10,11 @@
     @click="handleClick"
   >
     <slot>
-      <view v-if="props.hump">
-        <Icon v-bind="iconProps" icon=""/>
-      </view>
+      <view v-if="props.hump" :style="{
+        width: theme.resolveSize(iconProps.size),
+        height: '1px',
+        margin: `0 ${theme.resolveSize(props.humpSpace?.[active ? 0 : 1] || 0)}`,
+      }"></view>
       <Badge
         :containerStyle="{
           ...(props.hump ? {
@@ -27,7 +29,10 @@
         :offset="{ x: 3, y: 0 }"
         v-bind="props.badgeProps"
       >
-        <slot name="icon" :selected="active" :iconProps="iconProps">
+        <slot name="icon" 
+          :selected="active" 
+          :iconProps="iconProps"
+        >
           <Icon v-bind="iconProps" />
         </slot>
       </Badge>
@@ -64,6 +69,14 @@ export interface TabBarItemProps {
    */
   iconProps?: IconProps;
   /**
+   * 选中时的图标
+   */
+  activeIcon?: string;
+  /**
+   * 选中时的图标透传样式
+   */
+  activeIconProps?: IconProps;
+  /**
    * 标签图标大小
    * @default 23
    */
@@ -90,6 +103,10 @@ export interface TabBarItemProps {
    */
   humpHeight?: number[];
   /**
+   * 指定当前标签凸起的左右布局间距,数组第0位是选中时的凸起间距,第1位是未选中时的凸起间距。
+   */
+  humpSpace?: number[];
+  /**
    * 自定义徽标的属性,传入的对象会被透传给 Badge 组件的 props
    */
   badgeProps?: BadgeProps;
@@ -136,8 +153,8 @@ const color = computed(() => theme.resolveThemeColor(
 ));
 
 const iconProps = computed(() => ({
-  ...props.iconProps,
-  icon: props.icon ?? '',
+  ...(active.value ? props.activeIconProps : props.iconProps),
+  icon: (active.value ? (props.activeIcon ?? props.icon) : props.icon) || '',
   size: props.iconSize,
   color: color.value,
 }));

+ 5 - 0
src/components/theme/Theme.ts

@@ -15,6 +15,11 @@ export const DefaultTheme : ThemeConfig = {
       large: 38,
       larger: 46,
     },
+    shadow: {
+      default: '0 0 10rpx rgba(0, 0, 0, 0.1)',
+      light: '0 0 10rpx rgba(0, 0, 0, 0.05)',
+      dark: '0 0 10rpx rgba(0, 0, 0, 0.2)',
+    },
   },
   colorConfigs: {
     default: {

+ 10 - 1
src/components/theme/ThemeDefine.ts

@@ -154,7 +154,16 @@ export function useTheme() {
     return theme.textConfigs[key]?? defaultValue;
   }
   function getVar<T>(key: string, defaultValue: T) : T {
-    return theme.varOverrides[key] ?? defaultValue;
+    let rs = undefined;
+    let type = '';
+    if (key.includes('.'))
+      [type, key] = key.split('.');
+    if (type) {
+      const group = theme.varOverrides[type];
+      rs = group?.[key];
+    } else
+      rs = theme.varOverrides[key];
+    return rs ?? defaultValue;
   } 
 
   function getVars<T extends Record<string, string|number|boolean>>(defaults: T) {

+ 1 - 5
src/pages.json

@@ -1,8 +1,4 @@
 {
-  "easycom": {
-    "^u-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
-    "^up-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue"
-  },
   "pages": [
     {
       "path": "pages/user/login",
@@ -149,7 +145,7 @@
   "globalStyle": {
     "navigationBarTextStyle": "white",
     "navigationBarTitleText": "uni-app",
-    "navigationBarBackgroundColor": "#FF8719",
+    "navigationBarBackgroundColor": "#00ca76",
     "backgroundColor": "#F8F8F8"
   },
   "uniIdRouter": {}

+ 1 - 0
src/pages/dig/index.vue

@@ -46,6 +46,7 @@
         </SimplePageContentLoader>
       </RequireLogin>
 
+      <Height :height="20" />
       <SubTitle title="我的贡献" />
       <RequireLogin unLoginMessage="登录后贡献,加入排行榜">
         <FlexRow backgroundColor="white" :radius="20" :padding="[40,20]">

+ 79 - 0
src/pages/home/discover/index.vue

@@ -0,0 +1,79 @@
+<template>
+  <FlexCol :gap="20" :padding="20">
+    <Box title="导览" icon="/static/images/home/icon-compass.png">
+      <ProvideVar :vars="{
+        GridItemIconSize: 90,
+        GridItemBackgroundColor: 'transparent',
+        GridItemPaddingHorizontal: 0,
+      }">
+        <Grid :borderGrid="false" :mainAxisCount="4">
+          <GridItem title="全部" icon="/static/images/icons/icon-all.png" touchable />
+          <GridItem title="历史文化" icon="/static/images/icons/icon-history.png" touchable />
+          <GridItem title="环境格局" icon="/static/images/icons/icon-envirounment.png" touchable />
+          <GridItem title="传统建筑" icon="/static/images/icons/icon-buliding.png" touchable />
+          <GridItem title="民俗文化" icon="/static/images/icons/icon-location.png" touchable />
+          <GridItem title="地道美食" icon="/static/images/icons/icon-foods.png" touchable />
+          <GridItem title="物产资源" icon="/static/images/icons/icon-resource.png" touchable />
+          <GridItem title="旅游线路" icon="/static/images/icons/icon-route.png" touchable />
+        </Grid>
+      </ProvideVar>
+    </Box>
+    <Box title="文章" icon="/static/images/home/icon-article.png">  
+      <SimplePageContentLoader :loader="discoverLoader">
+        <FlexCol :gap="25">
+          <Touchable 
+            v-for="(item, i) in discoverLoader.content.value"
+            :key="i"
+            align="center"
+            direction="column"
+          > 
+            <Image 
+              :src="item.thumbnail || item.image" 
+              width="100%" 
+              :height="350" 
+              :radius="15" 
+              mode="aspectFill" 
+              round
+            />
+            <Height :height="20" />
+            <FlexCol>
+              <Text :text="item.title" fontConfig="h4" />
+              <Text :text="item.desc" fontConfig="subText" />
+            </FlexCol>
+          </Touchable>
+        </FlexCol>
+      </SimplePageContentLoader>
+    </Box>
+    <Loadmore status="nomore" />
+    <Height :height="150" />
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
+import { RandomUtils } from '@imengyu/imengyu-utils';
+import Box from '@/common/components/parts/Box.vue';
+import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
+import Image from '@/components/basic/Image.vue';
+import Text from '@/components/basic/Text.vue';
+import Loadmore from '@/components/display/loading/Loadmore.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import Height from '@/components/layout/space/Height.vue';
+import ProvideVar from '@/components/theme/ProvideVar.vue';
+import Grid from '@/components/layout/grid/Grid.vue';
+import GridItem from '@/components/layout/grid/GridItem.vue';
+
+function testImage() {
+  return 'https://mncdn.wenlvti.net/app_static/minnan/images/home/ImageTest' + RandomUtils.genRandom(1, 5) +'.jpg';
+}
+
+const discoverLoader = useSimpleDataLoader(async () => {
+  return new Array(26).fill(0).map((_, i) => ({ 
+    title: '张家大院·门楼' + i, 
+    desc: '多馆联展,沉浸式交互体验。多馆联展,沉浸式交互体验。',
+    image: testImage(),
+    thumbnail: testImage(),
+  }));
+});
+</script>

+ 193 - 0
src/pages/home/index.vue

@@ -0,0 +1,193 @@
+<template>
+  <FlexCol :gap="20" :padding="20">
+    <FlexCol :radius="15" overflow="hidden">
+      <ImageSwiper 
+        :height="200"
+        :images="[
+          'https://mn.wenlvti.net/app_static/minnan/images/home/BackgroundBanner5.jpg',
+          'https://mn.wenlvti.net/app_static/minnan/images/home/BackgroundBanner4.jpg',
+          'https://mn.wenlvti.net/app_static/minnan/images/home/BackgroundBanner3.jpg',
+        ]"
+      />
+    </FlexCol>
+    <Box title="传统村落分布" icon="/static/images/home/icon-pin-distance.png">
+      <map 
+        id="map"
+        class="w-100 height-400 radius-base overflow-hidden"
+        :markers="mapLoader.content.value || []"
+        :enable-zoom="false"
+        :enable-scroll="false"
+        :scale="15"
+      />
+    </Box>
+    <Box title="优秀线上村史馆" icon="/static/images/home/icon-ancient-gate.png">
+      <SimplePageContentLoader :loader="recommendLoader">
+        <FlexRow justify="space-between" align="center" wrap :gap="15">
+          <Touchable 
+            v-for="(item, i) in recommendLoader.content.value"
+            :key="i"
+            flex="0 0 48%"
+            align="center"
+            direction="column"
+          > 
+            <Image 
+              :src="item.thumbnail || item.image" 
+              width="100%" 
+              height="200rpx" 
+              :radius="15" 
+              mode="aspectFill" 
+              round
+            />
+            <Text :text="item.title" />
+          </Touchable>
+        </FlexRow>
+      </SimplePageContentLoader>
+    </Box>
+    <Box title="发现" icon="/static/images/home/icon-compass.png" showMore @moreClicked="$emit('goDiscover')">  
+      <SimplePageContentLoader :loader="discoverLoader">
+        <FlexCol :gap="15">
+          <Touchable 
+            v-for="(item, i) in discoverLoader.content.value"
+            :key="i"
+            justify="space-between"
+            align="center"
+            direction="row"
+          > 
+            <FlexCol flex="1">
+              <Text :text="item.title" fontConfig="h5" />
+              <Text :text="item.desc" fontConfig="subText" />
+            </FlexCol>
+            <Width :width="25" />
+            <Image 
+              :src="item.thumbnail || item.image" 
+              :width="120" 
+              :height="120" 
+              :radius="15" 
+              mode="aspectFill" 
+              round
+            />
+          </Touchable>
+        </FlexCol>
+      </SimplePageContentLoader>
+    </Box>
+    <Loadmore status="nomore" />
+    <Height :height="150" />
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import Box from '@/common/components/parts/Box.vue';
+import ImageSwiper from '@/common/components/parts/ImageSwiper.vue';
+import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
+import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
+import AppCofig from '@/common/config/AppCofig';
+import Image from '@/components/basic/Image.vue';
+import Text from '@/components/basic/Text.vue';
+import Loadmore from '@/components/display/loading/Loadmore.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Height from '@/components/layout/space/Height.vue';
+import Width from '@/components/layout/space/Width.vue';
+import { RandomUtils } from '@imengyu/imengyu-utils';
+
+function testImage() {
+  return 'https://mncdn.wenlvti.net/app_static/minnan/images/home/ImageTest' + RandomUtils.genRandom(1, 5) +'.jpg';
+}
+
+const mapCtx = uni.createMapContext('map');
+const mapLoader = useSimpleDataLoader(async () => {
+  const res = [
+    {
+      title: '传统村落',
+      longitude: Number(118.11441371826822),
+      latitude: Number(24.51019917828817),
+      iconPath: testImage(),
+    },
+    {
+      title: '传统村落',
+      longitude: Number(118.18573604529888),
+      latitude: Number(24.485581319259357),
+      iconPath: testImage(),
+    },
+    {
+      title: '传统村落',
+      longitude: Number(118.1242512731751),
+      latitude: Number(24.417297514622685),
+      iconPath: testImage(),
+    },
+    {
+      title: '传统村落',
+      longitude: Number(118.23861292755578),
+      latitude: Number(24.559407658427826),
+      iconPath: testImage(),
+    },
+  ]
+  mapCtx.includePoints({
+    points: res.map(p => {
+      if (!p.longitude || !p.latitude) {
+        p.longitude = AppCofig.defaultLonLat[0];
+        p.latitude = AppCofig.defaultLonLat[1];
+      }
+      return {
+        latitude: p.latitude,
+        longitude: p.longitude,
+      }
+    }),
+    padding: [20, 20, 20, 20],
+  });
+  return res;
+});
+
+const recommendLoader = useSimpleDataLoader(async () => {
+  return [
+    { 
+      title: '茶艺传承作坊', 
+      desc: '',
+      image: testImage(),
+      thumbnail: testImage(),
+    },
+    { 
+      title: '茶艺传承作坊', 
+      desc: '', 
+      image: testImage(),
+      thumbnail: testImage(),
+    },
+    { 
+      title: '茶艺传承作坊',  
+      desc: '',
+      image: testImage(),
+      thumbnail: testImage(),
+    },
+    { 
+      title: '茶艺传承作坊',  
+      desc: '',
+      image: testImage(),
+      thumbnail: testImage(),
+    },
+  ]
+});
+
+const discoverLoader = useSimpleDataLoader(async () => {
+  return [
+    { 
+      title: '茶艺传承作坊', 
+      desc: '多馆联展,沉浸式交互体验。多馆联展,沉浸式交互体验。',
+      image: testImage(),
+      thumbnail: testImage(),
+    },
+    { 
+      title: '茶艺传承作坊', 
+      desc: '多馆联展,沉浸式交互体验。多馆联展,沉浸式交互体验。', 
+      image: testImage(),
+      thumbnail: testImage(),
+    },
+    { 
+      title: '茶艺传承作坊',  
+      desc: '多馆联展,沉浸式交互体验。多馆联展,沉浸式交互体验。',
+      image: testImage(),
+      thumbnail: testImage(),
+    },
+  ]
+});
+</script>

+ 41 - 9
src/pages/index.vue

@@ -1,33 +1,65 @@
 <template>
-	<view>
+	<view class="index">
     <StatusBarSpace backgroundColor="primary" />
     <NavBar 
-      title="乡源·乡村文化资源挖掘平台"
-      backgroundColor="primary"
-      textColor="white"
+      :title="title"
       :titleScroll="false"
+      backgroundColor="transparent"
+      textColor="white"
+      align="left"
     />
-    <DigIndex v-if="tabIndex === 0" />
-    <UserIndex v-else-if="tabIndex === 1" />
+    <HomeIndex v-if="tabIndex === 0" @goDiscover="tabIndex = 1" />
+    <DiscoverIndex v-else-if="tabIndex === 1" />
+    <DigIndex v-else-if="tabIndex === 2" />
+    <UserIndex v-else-if="tabIndex === 4" />
     <TabBar
       v-model:selectedTabIndex="tabIndex"
       fixed
       xbarSpace
     >
-      <TabBarItem :icon="tabIndex === 0 ? '/static/images/tabs/home-active.png' : '/static/images/tabs/home.png'" text="采集" />
-      <TabBarItem :icon="tabIndex === 1 ? '/static/images/tabs/user-active.png' : '/static/images/tabs/user.png'" text="我的" />
+      <TabBarItem icon="/static/images/tabs/home.png" activeIcon="/static/images/tabs/home-active.png" text="首页" />
+      <TabBarItem icon="/static/images/tabs/discover.png" activeIcon="/static/images/tabs/discover-active.png" text="发现" />
+      <TabBarItem icon="/static/images/tabs/dig.png" hump :humpHeight="[ 60, 60 ]" :humpSpace="[20,20]" :iconSize="140" />
+      <TabBarItem icon="/static/images/tabs/store.png" activeIcon="/static/images/tabs/store-active.png" text="知识库" />
+      <TabBarItem icon="/static/images/tabs/user.png" activeIcon="/static/images/tabs/user-active.png" text="我的" />
     </TabBar>
 	</view>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue';
+import { computed, ref } from 'vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 import NavBar from '@/components/nav/NavBar.vue';
 import TabBar from '@/components/nav/TabBar.vue';
 import TabBarItem from '@/components/nav/TabBarItem.vue';
 import DigIndex from './dig/index.vue';
 import UserIndex from './user/index.vue';
+import HomeIndex from './home/index.vue';
+import DiscoverIndex from './home/discover/index.vue';
 
+const title = computed(() => {
+  switch (tabIndex.value) {
+    case 0:
+      return '乡源·乡村文化资源挖掘平台';
+    case 1:
+      return '发现·乡村文化资源挖掘平台';
+    case 2:
+      return '挖掘·乡村文化资源';
+    case 3:
+      return '知识库·乡村文化资源挖掘平台';
+    case 4:
+      return '我的·乡村文化资源挖掘平台';
+  }
+  return '';
+});
 const tabIndex = ref(0);
 </script>
+
+<style scoped>
+.index {
+  background-position: top left;
+  background-size: 100% auto;
+  background-repeat: no-repeat;
+  background-image: url('@/static/images/BackgroundMask.jpg');
+}
+</style>

+ 1 - 1
src/pages/user/index.vue

@@ -30,7 +30,7 @@
 <script setup lang="ts">
 import { useAuthStore } from '@/store/auth';
 import { computed } from 'vue';
-import UserHead from '@/static/images/home/UserHead.png';
+import UserHead from '@/static/images/user/avatar.png';
 import CellGroup from '@/components/basic/CellGroup.vue';
 import Cell from '@/components/basic/Cell.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';

BIN
src/static/images/BackgroundMask.jpg


BIN
src/static/images/MapArrow.png


BIN
src/static/images/home/UserHead.png


BIN
src/static/images/home/icon-ancient-gate.png


BIN
src/static/images/home/icon-article.png


BIN
src/static/images/home/icon-compass.png


BIN
src/static/images/home/icon-pin-distance.png


BIN
src/static/images/home/icon-shining.png


BIN
src/static/images/icons/icon-.png


BIN
src/static/images/icons/icon-all.png


BIN
src/static/images/icons/icon-buliding.png


BIN
src/static/images/icons/icon-camera.png


BIN
src/static/images/icons/icon-envirounment.png


BIN
src/static/images/icons/icon-exampke.png


BIN
src/static/images/icons/icon-foods.png


BIN
src/static/images/icons/icon-history.png


BIN
src/static/images/icons/icon-location.png


BIN
src/static/images/icons/icon-mark.png


BIN
src/static/images/icons/icon-pac.png


BIN
src/static/images/icons/icon-resource.png


BIN
src/static/images/icons/icon-route.png


BIN
src/static/images/tabs/dig.png


BIN
src/static/images/tabs/discover-active.png


BIN
src/static/images/tabs/discover.png


BIN
src/static/images/tabs/home-active.png


BIN
src/static/images/tabs/home.png


BIN
src/static/images/tabs/store-active.png


BIN
src/static/images/tabs/store.png


BIN
src/static/images/tabs/user-active.png


BIN
src/static/images/tabs/user.png


BIN
src/static/images/user/avatar.png


BIN
src/static/images/user/icon-chat.png


BIN
src/static/images/user/icon-edit.png


BIN
src/static/images/user/icon-function.png


BIN
src/static/images/user/icon-profile.png