Forráskód Böngészése

添加管理员管理登录和管理页面

快乐的梦鱼 3 hónapja
szülő
commit
5c5a2c41ea

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 784 - 497
package-lock.json


+ 25 - 24
package.json

@@ -35,37 +35,38 @@
     "type-check": "vue-tsc --noEmit"
   },
   "dependencies": {
-    "@dcloudio/uni-app": "3.0.0-4030620241128001",
-    "@dcloudio/uni-app-harmony": "3.0.0-4030620241128001",
-    "@dcloudio/uni-app-plus": "3.0.0-4030620241128001",
-    "@dcloudio/uni-components": "3.0.0-4030620241128001",
-    "@dcloudio/uni-h5": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-alipay": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-baidu": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-jd": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-kuaishou": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-lark": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-qq": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-toutiao": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-weixin": "3.0.0-4030620241128001",
-    "@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
-    "@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
+    "@dcloudio/uni-app": "3.0.0-4070620250821001",
+    "@dcloudio/uni-app-harmony": "3.0.0-4070620250821001",
+    "@dcloudio/uni-app-plus": "3.0.0-4070620250821001",
+    "@dcloudio/uni-components": "3.0.0-4070620250821001",
+    "@dcloudio/uni-h5": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-alipay": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-baidu": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-harmony": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-jd": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-kuaishou": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-lark": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-qq": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-toutiao": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-weixin": "3.0.0-4070620250821001",
+    "@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
+    "@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
     "@imengyu/imengyu-utils": "^0.0.16",
-    "@imengyu/js-request-transform": "^0.3.3",
+    "@imengyu/js-request-transform": "^0.3.6",
     "@imengyu/vue-dynamic-form": "^0.1.1",
     "async-validator": "^4.2.5",
     "pinia": "^3.0.1",
     "tslib": "^2.8.1",
-    "vue": "^3.4.21",
-    "vue-i18n": "^9.1.9"
+    "vue": "3.5.22",
+    "vue-i18n": "9.14.5"
   },
   "devDependencies": {
-    "@dcloudio/types": "^3.4.8",
-    "@dcloudio/uni-automator": "3.0.0-4030620241128001",
-    "@dcloudio/uni-cli-shared": "3.0.0-4030620241128001",
-    "@dcloudio/uni-stacktracey": "3.0.0-4030620241128001",
-    "@dcloudio/vite-plugin-uni": "3.0.0-4030620241128001",
-    "@vue/runtime-core": "^3.4.21",
+    "@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",
     "@vue/tsconfig": "^0.1.3",
     "sass": "^1.86.0",
     "typescript": "^4.9.4",

+ 8 - 5
src/api/auth/UserApi.ts

@@ -1,9 +1,7 @@
-import { DataModel, transformArrayDataModel } from '@imengyu/js-request-transform';
+import { DataModel } from '@imengyu/js-request-transform';
 import { AppServerRequestModule } from '../RequestModules';
-import ApiCofig from '@/common/config/ApiCofig';
 import AppCofig from '@/common/config/AppCofig';
 
-
 export class LoginResult extends DataModel<LoginResult> {
   constructor() {
     super(LoginResult, "登录结果");
@@ -95,12 +93,17 @@ export class UserApi extends AppServerRequestModule<DataModel> {
       ...data,
     }, '登录', undefined, LoginResult)).data as LoginResult;
   }
-  async loginWithMobile(data?: {
+  async login(data: {
+    account: string,
+    password: string,
+  }) {
+    return (await this.post('/user/login', data, '登录', undefined, LoginResult)).data as LoginResult;
+  }
+  async loginAdmin(data: {
     account: string,
     password: string,
   }) {
     return (await this.post('/user/adminLogin', {
-      appid: AppCofig.appId,
       account: data?.account,
       password: data?.password,
     }, '登录', undefined, LoginResult)).data as LoginResult;

+ 14 - 8
src/pages.json

@@ -5,9 +5,8 @@
   },
   "pages": [
     {
-      "path" : "pages/dig/index",
-      "style" :
-      {
+      "path": "pages/index",
+      "style": {
         "navigationBarTitleText" : "乡源·乡村文化资源挖掘平台",
         "enablePullDownRefresh" : false
       }
@@ -59,6 +58,13 @@
           }
         },
         {
+          "path": "admin",
+          "style": {
+            "navigationBarTitleText": "乡源·乡村文化资源挖掘平台-管理员",
+            "enablePullDownRefresh": false
+          }
+        },
+        {
           "path": "task/environment",
           "style": {
             "navigationBarTitleText": "乡源·乡村文化资源挖掘平台-环境格局",
@@ -143,15 +149,15 @@
     "selectedColor": "#FF8719",
     "list": [
       {
-        "pagePath": "pages/dig/index",
-        "iconPath": "https://mn.wenlvti.net/uploads/20250313/ff0545051683c42559f51743e839f8c4.png",
-        "selectedIconPath": "https://mn.wenlvti.net/uploads/20250313/03e58ca265365d90339b04dc1eeff279.png",
+        "pagePath": "pages/index",
+        "iconPath": "/static/images/tabs/home.png",
+        "selectedIconPath": "/static/images/tabs/home-active.png",
         "text": "村史"
       },
       {
         "pagePath": "pages/user/index",
-        "iconPath": "https://mn.wenlvti.net/uploads/20250313/7fd9655fb996786d8624fc46b90c3210.png",
-        "selectedIconPath": "https://mn.wenlvti.net/uploads/20250313/034d699de291d1b7192b45a4493b0815.png",
+        "iconPath": "/static/images/tabs/user.png",
+        "selectedIconPath": "/static/images/tabs/user-active.png",
         "text": "我的"
       }
     ]

+ 23 - 0
src/pages/dig/admin.vue

@@ -0,0 +1,23 @@
+<template>
+  <web-view :src="src" />
+</template>
+
+<script setup lang="ts">
+import { useLoadQuerys } from '@/common/composeabe/LoadQuerys';
+import { useAuthStore } from '@/store/auth';
+import { computed } from 'vue';
+
+const { querys } = useLoadQuerys({ 
+  id: 0,  
+  name: '',
+  points: 0,
+  level: 0,
+  villageVolunteerId: 0,
+});
+const authStore = useAuthStore();
+const src = computed(() => {
+  const url = `https://mn.wenlvti.net/test8/#/admin?id=${querys.value.id}&internalAuth=${encodeURIComponent(JSON.stringify(authStore.getInternalAuth()))}&t=${Date.now()}`
+  console.log(url);
+  return url;
+});
+</script>

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

@@ -1,251 +0,0 @@
-<template>
-	<view class="submit_main">
-    <view class="img-banner">
-      <image mode="aspectFill" src="https://mn.wenlvti.net/app_static/xiangan/banner_submit.jpg"></image>
-    </view>
-    <view class="main">
-      <template v-if="!villageListLoader.content.value?.length">
-        <view class="cat">
-          <text>村庄认领</text>
-        </view>
-        <u-button type="primary" @click="goClamVillage">
-          <text>+</text>
-          认领新村庄
-        </u-button>
-      </template>
-      <view class="cat">
-        <text>已认领村庄</text>
-      </view>
-      <RequireLogin unLoginMessage="登录后查看我认领的村庄">
-        <SimplePageContentLoader
-          :loader="villageListLoader"
-          :showEmpty="villageListLoader.content.value?.length == 0"
-          :emptyView="{
-            text: '你还没有认领的村庄',
-            button: true,
-            buttonText: '去认领',
-            buttonClick: goClamVillage,
-          }"
-        >
-          <view class="village-list">
-            <view 
-              v-for="item in villageListLoader.content.value"
-              :key="item.id"
-              class="item"
-            >
-              <ImageWrapper 
-                mode="aspectFill"
-                :src="item.image" 
-                width="154rpx"
-                height="154rpx"
-              />
-              <view class="info">
-                <view class="name">{{ item.villageName }}</view>
-                <view class="d-flex flex-row align-center">
-                  <view class="btn p-1 pl-2 pr-2" @click="navTo('/pages/inherit/village/details', { id: item.villageId })">
-                    <text class="iconfont icon-view"></text>预览
-                  </view>
-                  <view class="btn p-1 pl-2 pr-2 active" @click="goSubmitDigPage(item)">
-                    <text class="iconfont icon-search"></text>采编
-                  </view>
-                </view>
-              </view>
-            </view>
-          </view>
-        </SimplePageContentLoader>
-      </RequireLogin>
-
-      <view class="cat">
-        <text>我的贡献</text>
-      </view>
-      <RequireLogin unLoginMessage="登录后贡献,加入排行榜">
-        <view class="retribution">
-          <view class="item">
-            <text class="iconfont icon-total-points"></text>
-            <view class="num">{{ volunteerInfoLoader.content.value?.points }}</view>
-            <view>文化积分</view>
-          </view>
-          <view>
-            <text class="iconfont icon-art-arrow"></text>
-          </view>
-          <view class="item">
-            <text class="iconfont icon-level"></text>
-            <view class="level">Lv.{{ volunteerInfoLoader.content.value?.level }}</view>
-            <view>等级</view>
-          </view>
-        </view>
-      </RequireLogin>
-    </view>
-	</view>
-</template>
-
-<script setup lang="ts">
-import VillageApi, { VillageListItem } from '@/api/inhert/VillageApi';
-import ImageWrapper from '@/common/components/ImageWrapper.vue';
-import RequireLogin from '@/common/components/RequireLogin.vue';
-import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
-import { useReqireLogin } from '@/common/composeabe/RequireLogin';
-import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
-import { navTo } from '@imengyu/imengyu-utils/dist/uniapp/PageAction';
-
-const villageListLoader = useSimpleDataLoader(async () => await VillageApi.getClaimedVallageList(), true);
-const volunteerInfoLoader = useSimpleDataLoader(async () => await VillageApi.getVolunteerInfo(), true);
-const rankListLoader = useSimpleDataLoader(async () => await VillageApi.getVolunteerRanklist(), true);
-
-const { requireLogin } = useReqireLogin();
-
-function goClamVillage() {
-  requireLogin(() => navTo('forms/village_claim'), '登录后才能认领村庄哦!');
-}
-function goSubmitDigPage(item: VillageListItem) {
-  navTo('details', { 
-    id: item.villageId,
-    name: item.villageName,
-    villageVolunteerId: item.villageVolunteerId,
-    points: volunteerInfoLoader.content.value?.points,
-    level: volunteerInfoLoader.content.value?.level,
-  })
-}
-</script>
-
-<style lang="scss">
-.submit_main {
-  .img-banner{
-    border-radius: 0;
-    height: 466rpx;
-  }
-  .cat{
-    font-weight: 600;
-    display: flex;
-    margin: 36rpx 0 40rpx;
-    align-items: center;
-    text{
-      display: block;
-      font-size: 36rpx;
-      color: #312520;
-      flex:1;
-    }
-    .btn{
-      border-radius: 28rpx;
-      border: 1px solid #999999;
-      font-size: 27rpx;
-      color: #666666;
-      padding:15rpx 28rpx;
-      display: inline-block;
-      margin-left: 18rpx;
-      &.active{
-        color:#fff;
-        background: #FF8719;
-        border-color: #FF8719;
-      }
-    }
-  }
-  .village-list{
-    .item{
-      background: #FFFFFF;
-      border-radius: 10rpx;
-      padding:18rpx 14rpx 11rpx;
-      display: flex;
-      margin-bottom: 27rpx;
-      image,.image-wrapper {
-        border-radius: 10rpx;
-        width: 154rpx;
-        height: 154rpx;
-        display: block;
-        margin-right: 25rpx;
-        overflow: hidden;
-      }
-      .info{
-        .name{
-          font-size: 30rpx;
-          color: #333333;
-          margin-bottom: 35rpx;
-          margin-top: 14rpx;
-        }
-        .btn{
-          border-radius: 10rpx;
-          border: 1px solid #25515E;
-          padding:16rpx 44rpx;
-          font-size: 28rpx;
-          color:#25515E;
-          margin-right: 34rpx;
-          display: inline-block;
-          &.active{
-            background: #FF8719;
-            color:#fff;
-            border-color: #FF8719;
-          }
-          text.iconfont{
-            display: inline-block;
-            margin-right: 15rpx;
-            font-size: 36rpx;
-          }
-        }
-      }
-    }
-  }
-  .retribution{
-    background: #FFFFFF;
-    border-radius: 10rpx;
-    display: flex;
-    justify-content: space-around;
-    .item{
-      text-align: center;
-      font-size: 24rpx;
-      color: #312520;
-      padding: 35rpx 0 33rpx;
-      text.iconfont{
-        font-size: 56rpx;
-        color:#25515E;
-        display: block;
-      }
-      .num{
-        font-weight: 600;
-        font-size: 48rpx;
-        color: #FF8719;
-        margin-top: 12rpx;
-        line-height: 48rpx;
-        margin-bottom: 15rpx;
-      }
-      .level{
-        margin-top: 12rpx;
-        margin-bottom: 15rpx;
-        font-family: Rockwell;
-        font-size: 44rpx;
-        line-height: 48rpx;
-        color: #FF8719;
-      }
-    }
-  }
-  .people-list{
-    .item{
-      width: 100%;
-      display: flex;
-      align-items: center;
-      padding: 28rpx 21rpx 28rpx 10rpx;
-      text-align: left;
-      .rank{
-        position: relative;
-        top:inherit;
-        left:inherit;
-        margin-right: 25rpx;
-        width: 77rpx;
-      }
-      .info{
-        flex:1;
-      }
-      .level{
-        font-family: Rockwell;
-        font-size: 36rpx;
-        color: #FF8719;
-      }
-      image.avatar{
-        margin-bottom: 0;
-        margin-right: 51rpx;
-        background-color: #efefef;
-        border-radius: 50%;
-      }
-    }
-  }
-}
-</style>

+ 212 - 497
src/pages/index.vue

@@ -1,550 +1,265 @@
 <template>
-  <view style="padding-bottom: 160rpx;">
-    <view class="top-slogan">
-      <view class="slogan">
-        <view class="h">翔安文化挖掘传承平台</view>
-        <view class="sub">
-          <text class="iconfont icon-chat"></text>香承文脉,两两相传,与你同行 ~~
-        </view>
-      </view>
-      <view class="search">
-        <uni-search-bar 
-          radius="100" 
-          bgColor="#fff" 
-          placeholder="搜一搜 马上出发" 
-          clearButton="auto" 
-          cancelButton="none"
-          @confirm="search"/>
-      </view>
+	<view class="submit_main">
+    <view class="img-banner">
+      <image mode="aspectFill" src="https://mn.wenlvti.net/app_static/xiangan/banner_submit.jpg"></image>
     </view>
-    <view class="main first">
-      <view class="banner">
-        <swiper class="swiper right-indicator" circular :indicator-dots="true" :autoplay="true" :interval="2000" :duration="1000">
-          <swiper-item v-for="item in bannerData" :key="item.id">
-            <view class="item">
-              <image :src="item.image" mode="aspectFill"></image>
-            </view>
-          </swiper-item>
-        </swiper>
-      </view>
-      <view class="navigation">
-        <view class="item">
-          <view class="left">
-            <view class="title">文化底蕴</view>
-            <view class="title_en">Cultural deposits</view>
-            <image src="https://mn.wenlvti.net/app_static/xiangan/MainBoxIcon1.png" mode="aspectFill"></image>
-            <view class="arrow">
-              <text class="iconfont icon-arrow-right"></text>
-            </view>
-          </view>
-          <view class="right">
-            <view class="cat top" @click="navTo('/pages/culture/history/calendar')"><view><view>历史</view><view>沿革</view></view></view>
-            <view class="cat" @click="navTo('/pages/culture/index', { tab: 1 })"><view><view>文化</view><view>动态</view></view></view>
-            <view class="cat bottom" @click="navTo('/pages/inherit/village/list')">村史馆</view>
-          </view>
-        </view>
-        <view class="item ">
-          <view class="left blue">
-            <view class="title">文化传承</view>
-            <view class="title_en">Cultural Heritage</view>
-            <image src="https://mn.wenlvti.net/app_static/xiangan/MainBoxIcon2.png" mode="aspectFill"></image>
-            <view class="arrow">
-              <text class="iconfont icon-arrow-right"></text>
-            </view>
-          </view>
-          <view class="right">
-            <view class="cat top" @click="navTo('/pages/inherit/intangible/index')">非遗</view>
-            <view class="cat" @click="navTo('/pages/inherit/artifact/index')">文物</view>
-            <view class="cat bottom" @click="navTo('/pages/culture/activity/calendar')"><view><view>文化</view><view>活动</view></view></view>
-          </view>
-        </view>
-        <view class="item">
-          <view class="left blue">
-            <view class="title">文化守护</view>
-            <view class="title_en">Guarding </view>
-            <image src="https://mn.wenlvti.net/app_static/xiangan/MainBoxIcon4.png" mode="aspectFill"></image>
-            <view class="arrow">
-              <text class="iconfont icon-arrow-right"></text>
-            </view>
-          </view> 
-          <view class="right">
-            <view class="cat top" @click="goWewuGuanjia"><view><view>文物</view><view>管家</view></view></view>
-            <view class="cat" @click="navTo('article/xiangjin/index')"><view><view>翔金</view><view>情缘</view></view></view>
-            <view class="cat bottom" @click="navTo('dig/index')"><view><view>共建</view><view>村史</view></view></view>
-          </view>
-        </view>
-        <view class="item ">
-          <view class="left">
-            <view class="title">文旅融合</view>
-            <view class="title_en">Cultural Tourism</view>
-            <image src="https://mn.wenlvti.net/app_static/xiangan/MainBoxIcon3.png" mode="aspectFill"></image>
-            <view class="arrow">
-              <text class="iconfont icon-arrow-right"></text>
-            </view>
-          </view>
-          <view class="right">
-            <view class="cat top" @click="navTo('running/index')">文物跑</view>
-            <view class="cat" @click="navTo('article/food/index')"><view><view>翔安</view><view>百味</view></view></view>
-            <view class="cat bottom" @click="navTo('article/mountainsea/index')"><view><view>山海</view><view>田厝</view></view></view>
-          </view>
+    <view class="main">
+      <template v-if="!villageListLoader.content.value?.length">
+        <view class="cat">
+          <text>村庄认领</text>
         </view>
+        <u-button type="primary" @click="goClamVillage">
+          <text>+</text>
+          认领新村庄
+        </u-button>
+      </template>
+      <view class="cat">
+        <text>我的村庄</text>
       </view>
-
-      <!-- 说透透 -->
-      <view class="category">
-        <view class="name">说透透</view>
-        <view class="more" @click="goNews(NewsIndexContent.modelId, 308)">更多》</view>
-      </view>
-      <view class="audio-list">
-        <SimplePageContentLoader 
-          :loader="speakLoader" lazy
-          :showEmpty="speakLoader.content.value?.length == 0"
-        >
-          <view class="item" 
-            v-for="item in speakLoader.content.value"
-            :key="item.id"
-            @click="goNewsDetails(item.id)"
-          >
-            <image :src="item.image" mode="aspectFill"></image>
-            <view class="info">
-              <view class="name">{{ item.title }}</view>
-              <view class="place">{{ item.desc || item.district }}</view>
-            </view>
-            <view class="audio">
-              <view class="play-btn">
-                <text class="iconfont icon-play-b"></text>
-              </view>
-              <view>{{ item.duration }}</view>
-            </view>
-          </view>
-        </SimplePageContentLoader>
-      </view>
-
-      <!-- 走透透 -->
-      <view class="category">
-        <view class="name">走透透</view>
-        <view class="more" @click="goNews(NewsIndexContent.modelId, 309)">更多》</view>
-      </view>
-      <view class="complex-swiper sm">
-        <SimplePageContentLoader 
-          :loader="walkLoader" lazy
-          :showEmpty="walkLoader.content.value?.length == 0"
+      <RequireLogin unLoginMessage="登录后查看我认领的村庄">
+        <SimplePageContentLoader
+          :loader="villageListLoader"
+          :showEmpty="villageListLoader.content.value?.length == 0"
+          :emptyView="{
+            text: '你还没有认领的村庄',
+            button: true,
+            buttonText: '去认领',
+            buttonClick: goClamVillage,
+          }"
         >
-          <swiper 
-            v-if="walkLoader.content.value?.length" 
-            class="swiper"
-            :autoplay="false"
-            :circular="false"
-            :duration="500"
-            :next-margin="'235rpx'"
-          >
-            <swiper-item 
-              v-for="item in walkLoader.content.value"
-              :key="item.id"
-              @click="goNewsDetails(item.id)"
-            >
-              <view class="item">
-                <image :src="item.image" mode="aspectFill"></image>
-                <view class="name">
-                  <text>{{ item.title }}</text>
-                </view>
-              </view>
-            </swiper-item>
-          </swiper>
-        </SimplePageContentLoader>
-      </view>
-
-      <!-- 看透透 -->
-      <view class="category">
-        <view class="name">看透透
-          <view class="category-tag">
-            <text class="triangle"></text>
-            <text>探秘非遗,领略时光雕琢的惊艳技艺</text>
-          </view>
-        </view>
-        <view class="more" @click="navTo('/pages/inherit/intangible/index')">更多》</view>
-      </view>
-      <SimplePageContentLoader :loader="viewsLoader" lazy>
-        <scroll-view scroll-x>
-          <view class="d-flex flex-row">
+          <view class="village-list">
             <view 
-              v-for="(item, i) in viewsLoader.content.value"
-              :key="i"
-              class="mr-2"
-              @click="navTo('/pages/inherit/intangible/details', { id: item.id })"
+              v-for="item in villageListLoader.content.value"
+              :key="item.id"
+              class="item"
             >
-              <image 
-                class="width-300 height-200 radius-base"
-                :src="item.image"
+              <ImageWrapper 
                 mode="aspectFill"
+                :src="item.image" 
+                width="154rpx"
+                height="154rpx"
               />
-            </view>
-          </view>
-        </scroll-view>
-      </SimplePageContentLoader>
-    </view>
-
-    <!-- 最新资讯 -->
-    <view class="main">
-      <!-- 最新资讯 -->
-      <view class="category">
-        <view class="name">最新资讯</view>
-        <view class="more" @click="navTo('culture/index', { tab: 1 })">更多》</view>
-      </view>
-      <view class="news-simple-list-with-stats">
-        <SimplePageContentLoader 
-          :loader="newsLoader" lazy 
-          :showEmpty="newsLoader.content.value?.length == 0"
-        >
-          <view
-            class="item" 
-            v-for="item in newsLoader.content.value"
-            :key="item.id"
-            @click="goNewsDetails(item.id)"
-          >
-            <image :src="item.image"></image>
-            <view class="info">
-              <view class="name ellipsis-2">{{ item.title }} | {{ item.desc }}</view>
-              <view class="extra">
-                <text class="iconfont icon-time"></text>
-                <text>{{ DateUtils.formatDate(item.publishAt, DateUtils.FormatStrings.YearChanese) }}</text>
-                <text class="iconfont icon-view"></text>
-                <text>{{ item.views }}</text>
-                <text class="iconfont icon-fav"></text>
-                <text>{{ item.likes }}</text>
+              <view class="info">
+                <view class="name">{{ item.villageName }}</view>
+                <view class="d-flex flex-row align-center">
+                  <view class="btn p-1 pr-2" @click="navTo('/pages/inherit/village/details', { id: item.villageId })">
+                    <text class="iconfont icon-view"></text>预览
+                  </view>
+                  <view class="btn p-1 pr-2 active" @click="goSubmitDigPage(item)">
+                    <text class="iconfont icon-search"></text>采编
+                  </view>
+                  <view v-if="authStore.isAdmin" class="btn p-1 pr-2 active" @click="goManagePage(item)">
+                    管理
+                  </view>
+                </view>
               </view>
             </view>
           </view>
         </SimplePageContentLoader>
+      </RequireLogin>
+
+      <view class="cat">
+        <text>我的贡献</text>
       </view>
-      <!-- 精彩推荐 -->
-      <view class="category">
-        <view class="name">精彩推荐</view>
-        <view class="more"></view>
-      </view>
-      <SimplePageContentLoader :loader="recommendLoader" lazy>
-        <view class="d-flex flex-row justify-between flex-wrap">
-          <view 
-            v-for="(tab, k) in recommendLoader.content.value"
-            :key="k"
-            class="grid4-item position-relative mb-3"
-            @click="handleGoDetails(tab)"
-          >
-            <text 
-              class="tag bg-mask-white color-primary radius-l p-1 position-absolute size-s text-lines-1"
-            >
-              {{ tab.title }}
-            </text> 
-            <image
-              class="w-100 height-250 radius-base"
-              :src="tab.thumbnail || tab.image || ImagesUrls.defaultImage"
-              mode="aspectFill"
-            />
+      <RequireLogin unLoginMessage="登录后贡献,加入排行榜">
+        <view class="retribution">
+          <view class="item">
+            <text class="iconfont icon-total-points"></text>
+            <view class="num">{{ volunteerInfoLoader.content.value?.points }}</view>
+            <view>文化积分</view>
+          </view>
+          <view>
+            <text class="iconfont icon-art-arrow"></text>
+          </view>
+          <view class="item">
+            <text class="iconfont icon-level"></text>
+            <view class="level">Lv.{{ volunteerInfoLoader.content.value?.level }}</view>
+            <view>等级</view>
           </view>
         </view>
-      </SimplePageContentLoader>
+      </RequireLogin>
     </view>
-    <tabbar :current="0"></tabbar>
-  </view>
+	</view>
 </template>
 
 <script setup lang="ts">
-import { ref } from "vue";
-import { onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
-import { useSimplePageContentLoader } from "@/common/composeabe/SimplePageContentLoader";
-import { navTo } from "@imengyu/imengyu-utils/dist/uniapp/PageAction";
-import CommonContent, { GetContentListParams, type GetContentListItem } from "@/api/CommonContent";
-import NewsIndexContent from "@/api/news/NewsIndexContent";
-import UniSearchBar from "../uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue";
-import Tabbar from '@/pages/component/tabbar.vue'
-import SimplePageContentLoader from "@/common/components/SimplePageContentLoader.vue";
-import DateUtils from "@imengyu/imengyu-utils/dist/DateUtils";
-import ProjectsContent from "@/api/inheritor/ProjectsContent";
-import ImagesUrls from "@/common/config/ImagesUrls";
-import ProductsContent from "@/api/inheritor/ProductsContent";
+import VillageApi, { VillageListItem } from '@/api/inhert/VillageApi';
+import ImageWrapper from '@/common/components/ImageWrapper.vue';
+import RequireLogin from '@/common/components/RequireLogin.vue';
+import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
+import { useReqireLogin } from '@/common/composeabe/RequireLogin';
+import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
+import { useAuthStore } from '@/store/auth';
+import { navTo } from '@imengyu/imengyu-utils/dist/uniapp/PageAction';
 
+const authStore = useAuthStore();
+const villageListLoader = useSimpleDataLoader(async () => await VillageApi.getClaimedVallageList(), true);
+const volunteerInfoLoader = useSimpleDataLoader(async () => await VillageApi.getVolunteerInfo(), true);
+const rankListLoader = useSimpleDataLoader(async () => await VillageApi.getVolunteerRanklist(), true);
 
-const newsLoader = useSimplePageContentLoader<GetContentListItem[]>(async (params) => {
-  const res = await NewsIndexContent.getContentList(new GetContentListParams().setSelfValues({
-  }), 1, 4);
-  return res.list;
-});
-const walkLoader = useSimplePageContentLoader<GetContentListItem[]>(async (params) => {
-  const res = await NewsIndexContent.getContentList(new GetContentListParams().setSelfValues({
-    mainBodyColumnId: 309,
-  }), 1, 4);
-  return res.list;
-});
-const speakLoader = useSimplePageContentLoader<GetContentListItem[]>(async (params) => {
-  const res = await NewsIndexContent.getContentList(new GetContentListParams().setSelfValues({
-    mainBodyColumnId: 308,
-  }), 1, 4);
-  return res.list;
-});
-const viewsLoader = useSimplePageContentLoader<GetContentListItem[]>(async (params) => {
-  const res = await ProjectsContent.getContentList(new GetContentListParams().setSelfValues({
-  }), 1, 4);
-  return res.list;
-});
-const recommendLoader = useSimplePageContentLoader<GetContentListItem[]>(async (params) => {
-  const list = [];
-  list.push(...(await ProjectsContent.getContentList(new GetContentListParams(), 1, 6)).list.map((p) => {
-    p.itemType = 'intangible';
-    return p;
-  }));
-  list.push(...(await CommonContent.getContentList(new GetContentListParams()
-    .setModelId(1)
-  , 1, 6)).list.map((p) => {
-    p.itemType = 'artifact';
-    return p;
-  }));
-  list.push(...(await ProductsContent.getContentList(new GetContentListParams(), 1, 6)).list.map((p) => {
-    p.itemType = 'intangible';
-    return p;
-  }));
-  return list;
-});
+const { requireLogin } = useReqireLogin();
 
-const bannerData = [
-  {
-    id: 1,
-    image: 'https://mn.wenlvti.net/app_static/xiangan/index_banner_1.jpg',
-  },
-  {
-    id: 2,
-    image: 'https://mn.wenlvti.net/app_static/xiangan/index_banner_1.jpg',
-  }
-];
-
-
-function goNews(modelId: number, mainBodyColumnId?: number) {
-  navTo('/pages/article/list', {
-    modelId,
-    mainBodyColumnId,
-  });
+function goClamVillage() {
+  requireLogin(() => navTo('forms/village_claim'), '登录后才能认领村庄哦!');
 }
-function goNewsDetails(id: number) {
-  navTo('/pages/article/details', { id });
-}
-function handleGoDetails(item: GetContentListItem) {
-  switch (item.itemType) {
-    case 'artifact': 
-      navTo('/pages/inherit/artifact/details', { id: item.id });
-      break;
-    case 'intangible': 
-      navTo('/pages/inherit/intangible/details', { id: item.id });
-      break;
-    default:
-      navTo('/pages/article/details', { id: item.id });
-      break;
-  }
-}
-function search() {
-
+function goSubmitDigPage(item: VillageListItem) {
+  navTo('./dig/details', { 
+    id: item.villageId,
+    name: item.villageName,
+    villageVolunteerId: item.villageVolunteerId,
+    points: volunteerInfoLoader.content.value?.points,
+    level: volunteerInfoLoader.content.value?.level,
+  })
 }
-function goWewuGuanjia() {
-  uni.navigateToMiniProgram({
-    appId: 'wxf651ba4b0025640a',
-    path: 'pages/index/index',
-    extraData: {},
+function goManagePage(item: VillageListItem) {
+  navTo('./dig/admin', { 
+    id: item.villageId,
+    name: item.villageName,
+    villageVolunteerId: item.villageVolunteerId,
+    points: volunteerInfoLoader.content.value?.points,
+    level: volunteerInfoLoader.content.value?.level,
   })
 }
-
-onShareTimeline(() => {
-  return {}; 
-})
-onShareAppMessage(() => {
-  return {}; 
-})
 </script>
 
-<style lang="scss" scoped>
-
-.grid4-item {
-  width: 320rpx;
-
-  .tag {
-    top: 2rpx; 
-    right: 2rpx;
+<style lang="scss">
+.submit_main {
+  .img-banner{
+    border-radius: 0;
+    height: 466rpx;
   }
-}
-.audio-list {
-  .item {
+  .cat{
+    font-weight: 600;
     display: flex;
-    margin-bottom: 25rpx;
-
-    image {
-      width: 126rpx;
-      height: 126rpx;
+    margin: 36rpx 0 40rpx;
+    align-items: center;
+    text{
       display: block;
-      border-radius: 10rpx;
-      margin-right: 17rpx;
+      font-size: 36rpx;
+      color: #312520;
+      flex:1;
     }
-
-    .info {
-      flex: 1;
-      .name{
-        font-weight: 800;
-        font-size: 30rpx;
-        color: #333333;
-        margin-bottom: 20rpx;
-        margin-top: 10rpx;
+    .btn{
+      border-radius: 28rpx;
+      border: 1px solid #999999;
+      font-size: 27rpx;
+      color: #666666;
+      padding:15rpx 28rpx;
+      display: inline-block;
+      margin-left: 18rpx;
+      &.active{
+        color:#fff;
+        background: #FF8719;
+        border-color: #FF8719;
       }
-      .place {
-        font-size: 24rpx;
-        color: #C66207;
-        background: rgb(249,236, 225);
-        padding:12rpx 20rpx;
-        display: inline-block;
+    }
+  }
+  .village-list{
+    .item{
+      background: #FFFFFF;
+      border-radius: 10rpx;
+      padding:18rpx 14rpx 11rpx;
+      display: flex;
+      margin-bottom: 27rpx;
+      image,.image-wrapper {
         border-radius: 10rpx;
+        width: 154rpx;
+        height: 154rpx;
+        display: block;
+        margin-right: 25rpx;
+        overflow: hidden;
       }
-    }
-
-    .audio {
-      .play-btn {
-        width: 70rpx;
-        height: 70rpx;
-        border-radius: 50%;
-        background: rgb(232, 232, 232);
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        margin-bottom: 10rpx;
-
-        text.iconfont {
-          font-size: 32rpx;
+      .info{
+        .name{
+          font-size: 30rpx;
+          color: #333333;
+          margin-bottom: 35rpx;
+          margin-top: 14rpx;
+        }
+        .btn{
+          border-radius: 10rpx;
+          border: 1px solid #25515E;
+          padding:16rpx 14rpx;
+          font-size: 28rpx;
+          color:#25515E;
+          margin-right: 34rpx;
+          display: inline-block;
+          &.active{
+            background: #FF8719;
+            color:#fff;
+            border-color: #FF8719;
+          }
+          text.iconfont{
+            display: inline-block;
+            margin-right: 15rpx;
+            font-size: 36rpx;
+          }
         }
       }
     }
   }
-}
-.navigation{
-  display: flex;
-  justify-content: space-between;
-  flex-wrap: wrap;
-  .item{
+  .retribution{
+    background: #FFFFFF;
+    border-radius: 10rpx;
     display: flex;
-    margin-bottom: 30rpx;
-    .left{
-      background: #FF8719;
-      width: 169rpx;
-      height: 196rpx;
-      border-radius: 18rpx 0rpx 0rpx 18rpx;
-      padding:48rpx 0 49rpx 31rpx;
-      margin-right: 6rpx;
-      position: relative;
-      &.blue{
-        background:#24515D;
-      }
-      image {
-        width: 110rpx;
-        height: 146rpx;
-        position: absolute;
-        bottom: 0rpx;
-        right: 0rpx;
-      }
-      .title{
-        font-weight: 800;
-        font-size: 28rpx;
-        color: #fff;
+    justify-content: space-around;
+    .item{
+      text-align: center;
+      font-size: 24rpx;
+      color: #312520;
+      padding: 35rpx 0 33rpx;
+      text.iconfont{
+        font-size: 56rpx;
+        color:#25515E;
+        display: block;
       }
-      .title_en{
-        font-size: 16rpx;
-        color: #fff;
+      .num{
+        font-weight: 600;
+        font-size: 48rpx;
+        color: #FF8719;
+        margin-top: 12rpx;
+        line-height: 48rpx;
+        margin-bottom: 15rpx;
       }
-      .arrow{
-        margin-top: 120rpx;
-        width: 35rpx;
-        height: 35rpx;
-        border-radius: 50%;
-        background: #fff;
-        text-align: center;
-        line-height: 35rpx;
-        text.iconfont{
-          font-size: 24rpx;
-        }
-      }
-    }
-    .right{
-      display: flex;
-      flex-direction: column;
-      justify-content: space-between;
-      .cat{
-        width: 122rpx;
-        height: 92rpx;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        border: 1px solid #B1C7CD;
-        word-break: break-all;
-        line-height: 30rpx;
-        &.top{
-          border-radius: 0rpx 18rpx 0rpx 0rpx;
-        }
-        &.bottom{
-          border-radius: 0rpx 0rpx 18rpx 0rpx;
-        }
+      .level{
+        margin-top: 12rpx;
+        margin-bottom: 15rpx;
+        font-family: Rockwell;
+        font-size: 44rpx;
+        line-height: 48rpx;
+        color: #FF8719;
       }
     }
   }
-}
-.banner {
-  margin-top: 0;
-  .swiper {
-    border-radius: 0;
-    height: 306rpx;
+  .people-list{
     .item{
-      height: 246rpx;
-    }
-  }
-}
-.top-slogan{
-  background: #FF8719;
-  height:430rpx;
-  .slogan{
-    padding-top: 46rpx;
-    color:#fff;
-    padding-left: 68rpx;
-    .h{
-      font-size: 48rpx;
-      font-weight: 600;
-      margin-bottom: 20rpx;
-    }
-    .sub{
+      width: 100%;
       display: flex;
       align-items: center;
-      margin-left: -10rpx;
-    }
-    text.iconfont{
-      display: inline-block;
-      font-size: 46rpx;
-      margin-right: 4rpx;
+      padding: 28rpx 21rpx 28rpx 10rpx;
+      text-align: left;
+      .rank{
+        position: relative;
+        top:inherit;
+        left:inherit;
+        margin-right: 25rpx;
+        width: 77rpx;
+      }
+      .info{
+        flex:1;
+      }
+      .level{
+        font-family: Rockwell;
+        font-size: 36rpx;
+        color: #FF8719;
+      }
+      image.avatar{
+        margin-bottom: 0;
+        margin-right: 51rpx;
+        background-color: #efefef;
+        border-radius: 50%;
+      }
     }
   }
 }
-.search{
-  border:none;
-  margin: 10rpx 20rpx 0;
-  ::v-deep .uni-searchbar__box{
-    border:none;
-  }
-}
-.main.first{
-  margin-top:-153rpx;
-  padding-bottom: 0;
-}
-.threeD{
-  top: 24rpx;
-  left: inherit;
-  bottom: inherit;
-  right: 24rpx;
-  width: 70rpx;
-  height: 70rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  text.iconfont{
-    font-size: 45rpx;
-  }
-}
 </style>

+ 29 - 1
src/pages/user/login.vue

@@ -31,6 +31,22 @@
               placeholder="请输入密码(6-16位)"
             ></u-input>
           </u-form-item>
+          <u-form-item
+            label="登录为"
+            prop="loginType"
+            borderBottom
+          >
+            <u-radio-group
+              v-model="loginFormModel.loginType"
+              :options="loginTypeOptions"
+            >
+              <u-radio 
+                v-for="(item, index) in loginTypeOptions" :key="index" 
+                :name="item.value"
+                :label="item.label"
+              /> 
+            </u-radio-group>
+          </u-form-item>
         </u-form>
       </view>
       <u-button type="primary" @click="loginMobile">
@@ -84,7 +100,18 @@ const authStore = useAuthStore();
 const loginFormModel = ref({
   mobile: '',
   password: '',
+  loginType: 0,
 });
+const loginTypeOptions = ref([
+  {
+    label: '志愿者',
+    value: 0,
+  },
+  {
+    label: '管理员',
+    value: 1,
+  },
+]);
 const loginFormRules = {
   'mobile': {
     type: 'string',
@@ -148,7 +175,8 @@ function loginMobile() {
   uni.showLoading({ title: '登录中...' });
   authStore.loginMobile(
     loginFormModel.value.mobile, 
-    loginFormModel.value.password
+    loginFormModel.value.password,
+    loginFormModel.value.loginType,
   ).then(() => {
     toast('登录成功');
     setTimeout(back, 200);

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


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


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


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


+ 34 - 9
src/store/auth.ts

@@ -9,6 +9,7 @@ export const useAuthStore = defineStore('auth', {
     expireAt: 0,
     userId: 0,
     userInfo: null as null|UserInfo,
+    loginType: 0,
   }),
   actions: {
     async loadLoginState() {
@@ -21,6 +22,7 @@ export const useAuthStore = defineStore('auth', {
         this.userId = authInfo.userId;
         this.expireAt = authInfo.expireAt;
         this.userInfo = authInfo.userInfo;
+        this.loginType = authInfo.loginType;
         setToken(this.token);
         return true;
       } catch (error) {
@@ -41,20 +43,30 @@ export const useAuthStore = defineStore('auth', {
         raw_data: JSON.parse(res.rawData),
         signature: res.signature,
       })
-      this.loginResultHandle(loginResult);
+      this.loginResultHandle(loginResult, 0);
     },
-    async loginMobile(account: string, password: string) {
-      const loginResult = await UserApi.loginWithMobile({
-        account,
-        password,
-      })
-      this.loginResultHandle(loginResult);
+    async loginMobile(account: string, password: string, loginType: number) {
+      let loginResult;
+      if (loginType == 0) {
+        loginResult = await UserApi.login({
+          account,
+          password,
+        })
+      } else if (loginType == 1) {
+        loginResult = await UserApi.loginAdmin({
+          account,
+          password,
+        })
+      } else
+        throw 'login type error';
+      this.loginResultHandle(loginResult, loginType);
     },
-    async loginResultHandle(loginResult: LoginResult) {
+    async loginResultHandle(loginResult: LoginResult, loginType: number) {
       this.token = loginResult.auth.token;
       this.userId = loginResult.mainBodyUserInfo.id;
       this.userInfo = loginResult.mainBodyUserInfo;
       this.expireAt = loginResult.auth.expiresIn + Date.now();
+      this.loginType = loginType;
 
       console.log('loginResultHandle');
       
@@ -66,6 +78,7 @@ export const useAuthStore = defineStore('auth', {
           userId: this.userId ,
           expireAt: this.expireAt,
           userInfo: this.userInfo,
+          loginType,
         }) 
       });
     },
@@ -75,9 +88,21 @@ export const useAuthStore = defineStore('auth', {
       this.userInfo = null;
       setToken('');
       uni.removeStorage({ key: STORAGE_KEY });
-    }
+    },
+    getInternalAuth() {
+      return { 
+        token: this.token, 
+        userId: this.userId ,
+        expireAt: this.expireAt,
+        userInfo: this.userInfo,
+        loginType: this.loginType,
+      }
+    },
   },
   getters: {
+    isAdmin(state) {
+      return state.loginType === 1;
+    },
     isLogged(state) {
       return state.token != '' && state.userId != 0
     },