Selaa lähdekoodia

📦 排名页面对接

快乐的梦鱼 2 viikkoa sitten
vanhempi
commit
c4ac32ff75
33 muutettua tiedostoa jossa 437 lisäystä ja 2241 poistoa
  1. 1 1
      src/App.vue
  2. 1 1
      src/api/RequestModules.ts
  3. 55 20
      src/api/light/LightVillageApi.ts
  4. 9 1
      src/components/basic/IconButton.vue
  5. 1 0
      src/components/composeabe/loader/SimplePageListLoader.ts
  6. 0 584
      src/components/display/parse/node/node.vue
  7. 0 22
      src/components/display/parse/parse.js
  8. 0 1337
      src/components/display/parse/parser.js
  9. 0 48
      src/components/display/parse/props.js
  10. 1 1
      src/components/feedback/DropdownMenuItem.vue
  11. 8 3
      src/components/layout/BaseView.ts
  12. 2 1
      src/components/layout/FlexView.vue
  13. 1 1
      src/components/layout/space/SafeAreaMargin.vue
  14. 1 1
      src/components/layout/space/SafeAreaPadding.vue
  15. 1 1
      src/components/layout/space/StatusBarSpace.vue
  16. 1 1
      src/components/layout/space/XBarSpace.vue
  17. 0 2
      src/components/list/FixedVirtualList.vue
  18. 1 1
      src/components/nav/TabBar.vue
  19. 1 1
      src/components/theme/ThemeDefine.ts
  20. 1 1
      src/components/theme/ThemeTools.ts
  21. 2 0
      src/components/utils/DialogAction.ts
  22. 18 0
      src/pages.json
  23. 4 6
      src/pages/home/components/LightMap.vue
  24. 10 26
      src/pages/home/index.vue
  25. 113 0
      src/pages/home/post/detail.vue
  26. 0 0
      src/pages/home/post/publish.vue
  27. 1 1
      src/pages/home/village/components/VillageTree.vue
  28. 68 76
      src/pages/home/village/introd/card.vue
  29. 66 53
      src/pages/home/village/rank/village.vue
  30. 59 47
      src/pages/home/village/rank/volunteer.vue
  31. 9 2
      src/pages/index.vue
  32. 1 1
      src/pages/test/render.vue
  33. 1 1
      src/uni_modules/sp-editor/components/sp-editor/fab-tool.vue

+ 1 - 1
src/App.vue

@@ -60,7 +60,7 @@ onError((err) => {
 RequestApiConfig.setConfig({
   ...RequestApiConfig.getConfig(),
   BaseUrl: ApiCofig.server.Prod,
-  EnableApiRequestLog: isTestEnv,
+  EnableApiRequestLog: false, // isTestEnv,
   EnableApiDataLog: false,
 })
 

+ 1 - 1
src/api/RequestModules.ts

@@ -8,7 +8,7 @@ import ApiCofig from "@/common/config/ApiCofig";
 import { isDev } from "../common/config/AppCofig";
 import { BaseAppServerRequestModule } from "./BaseAppServerRequestModule";
 import type { DataModel, NewDataModel } from "@imengyu/js-request-transform";
-import { appendGetUrlParams, defaultResponseDataHandlerCatch, RequestApiError, RequestApiResult, RequestCoreInstance, RequestOptions, RequestResponse, type RequestApiInfoStruct } from "@imengyu/imengyu-utils";
+import { appendGetUrlParams, defaultResponseDataHandlerCatch, RequestApiConfig, RequestApiError, RequestApiResult, RequestCoreInstance, RequestOptions, RequestResponse, type RequestApiInfoStruct } from "@imengyu/imengyu-utils";
 
 
 /**

+ 55 - 20
src/api/light/LightVillageApi.ts

@@ -1,4 +1,4 @@
-import { DataModel, transformArrayDataModel } from '@imengyu/js-request-transform';
+import { DataModel, transformArrayDataModel, transformDataModel, type KeyValue } from '@imengyu/js-request-transform';
 import { AppServerRequestModule } from '../RequestModules';
 import { transformSomeToArray } from '../Utils';
 
@@ -56,6 +56,39 @@ export class VillageListItem extends DataModel<VillageListItem> {
   title = '';
   volunteerName = '';
 }
+
+export class PostMessage extends DataModel<PostMessage> {
+  constructor() {
+    super(PostMessage, "微信贴图");
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
+      title: { clientSide: 'string', serverSide: 'string' },
+      images: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
+      sendTime: { clientSide: 'date', serverSide: 'string' },
+    }
+    this._afterSolveServer = () => {
+      if (!this.images) {
+        this.images = [];
+      }
+      if (this.image && this.images?.length == 0) {
+        this.images = [ this.image ];
+      }
+    }
+  }
+
+  id !: number;
+  title = '';
+  content = '';
+  images = [] as string[];
+  image = '';
+  nickName = '';
+  avatar = '';
+  likeCount = 0;
+  shareCount = 0;
+  sendTime = new Date();
+}
+
 export class LightVillageApi extends AppServerRequestModule<DataModel> {
 
   constructor() {
@@ -157,31 +190,33 @@ export class LightVillageApi extends AppServerRequestModule<DataModel> {
     return res.requireData();
   }
 
-  async getMessages(page: number, pageSize: number, keywords?: string) {
+  async getMessages(page: number, pageSize: number, search?:{
+    keywords?: string;
+    villageId?: number;
+    userId?: number;
+  }) {
     const res = await this.get<{
-      data: {
-        id: number;
-        user_id: number;
-        village_id: number;
-        title: string;
-        content: string | null;
-        images: string;
-        image: string;
-        msgid: string;
-        jump_url: string;
-        send_time: string;
-        like_count: number;
-        village_name: string | null;
-        username: string | null;
-        nick_name: string | null;
-      }[],
+      data: KeyValue[],
       total: number,
     }>('/village/collect/wechatContentList', '获取微信贴图列表', {
       page,
       pageSize,
-      keywords,
+      keywords: search?.keywords,
+      village_id: search?.villageId,
+      user_id: search?.userId,
     });
-    return res.requireData();
+    const data = res.requireData();
+    return {
+      list: transformArrayDataModel<PostMessage>(PostMessage, transformSomeToArray(data.data), '微信贴图列表', true),
+      total: data.total,
+    };
+  }
+
+  async getMessageDetails(id: number) {
+    const res = await this.get<KeyValue>('/village/collect/wechatContentDetail', '获取微信贴图详情', {
+      id: id,
+    });
+    return transformDataModel<PostMessage>(PostMessage, res.requireData());
   }
 }
 

+ 9 - 1
src/components/basic/IconButton.vue

@@ -3,11 +3,14 @@
     :pressedColor="pressedBackgroundColor"
     :innerStyle="style"
     :touchable="touchable"
+    gap="gap.md"
     @click="(e) => emit('click', e)"
     v-bind="$attrs"
   >
     <Icon v-if="icon" v-bind="props" />
-    <slot />
+    <slot>
+      <Text :text="text" />
+    </slot>
   </Touchable>
 </template>
 
@@ -18,6 +21,7 @@ import { selectStyleType } from '../theme/ThemeTools';
 import type { IconProps } from './Icon.vue';
 import Icon from './Icon.vue';
 import Touchable from '../feedback/Touchable.vue';
+import Text from './Text.vue';
 
 /**
  * 图标按钮形状预设
@@ -30,6 +34,10 @@ export type IconButtonShapeType = 'round'|'round-full'|'square-full'|'custom';
 
 export interface IconButtonProps extends IconProps {
   /**
+   * 按钮文字,在自定义slot后无效。
+   */
+  text?: string;
+  /**
    * 按钮按下时的背景颜色
    * @default PressedColor(Color.white)
    */

+ 1 - 0
src/components/composeabe/loader/SimplePageListLoader.ts

@@ -55,6 +55,7 @@ export function useSimplePageListLoader<T, P = any>(
       error.value = '';
       loading = false;
     } catch(e) {
+      console.error(e);
       error.value = '' + e;
       status.value = 'error';
       loading = false;

+ 0 - 584
src/components/display/parse/node/node.vue

@@ -1,584 +0,0 @@
-<template>
-  <view :id="attrs.id" :class="'_block _'+name+' '+attrs.class" :style="attrs.style">
-    <block v-for="(n, i) in childs" v-bind:key="i">
-      <!-- 图片 -->
-      <!-- 占位图 -->
-      <image v-if="n.name==='img'&&!n.t&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
-      <!-- 显示图片 -->
-      <!-- #ifdef H5 || (APP-PLUS && VUE2) -->
-      <img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
-      <!-- #endif -->
-      <!-- #ifndef H5 || (APP-PLUS && VUE2) -->
-      <!-- 表格中的图片,使用 rich-text 防止大小不正确 -->
-      <rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="'<img class=\'_img\' style=\''+n.attrs.style+'\' src=\''+n.attrs.src+'\'>'" :data-i="i" @tap.stop="imgTap" />
-      <!-- #endif -->
-      <!-- #ifndef H5 || APP-PLUS -->
-      <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
-      <!-- #endif -->
-      <!-- #ifdef APP-PLUS && VUE3 -->
-      <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
-      <!-- #endif -->
-      <!-- 文本 -->
-      <!-- #ifdef MP-WEIXIN -->
-      <text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
-      <!-- #endif -->
-      <!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
-      <text v-else-if="n.text" decode>{{n.text}}</text>
-      <!-- #endif -->
-      <text v-else-if="n.name==='br'">\n</text>
-      <!-- 链接 -->
-      <view v-else-if="n.name==='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
-        <node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
-      </view>
-      <!-- 视频 -->
-      <!-- #ifdef APP-PLUS -->
-      <view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" @vplay.stop="play" />
-      <!-- #endif -->
-      <!-- #ifndef APP-PLUS -->
-      <video v-else-if="n.name==='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :object-fit="n.attrs['object-fit']" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
-      <!-- #endif -->
-      <!-- #ifdef H5 || APP-PLUS -->
-      <iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
-      <embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
-      <!-- #endif -->
-      <!-- #ifndef MP-TOUTIAO || ((H5 || APP-PLUS) && VUE3) -->
-      <!-- 音频 -->
-      <audio v-else-if="n.name==='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
-      <!-- #endif -->
-      <view v-else-if="(n.name==='table'&&n.c)||n.name==='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style">
-        <node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
-        <view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
-          <node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
-          <block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
-            <view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
-              <node :childs="tr.children" :opts="opts" />
-            </view>
-            <view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
-              <view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
-                <node :childs="td.children" :opts="opts" />
-              </view>
-            </view>
-          </block>
-        </view>
-      </view>
-
-      <!-- 富文本 -->
-      <!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
-      <rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :user-select="opts[4]" :nodes="[n]" />
-      <!-- #endif -->
-      <!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
-      <rich-text v-else-if="!n.c" :id="n.attrs.id" :style="n.f+';display:inline'" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
-      <!-- #endif -->
-      <!-- 继续递归 -->
-      <view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
-        <node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
-      </view>
-      <node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
-    </block>
-  </view>
-</template>
-<script module="handler" lang="wxs">
-// 行内标签列表
-var inlineTags = {
-  abbr: true,
-  b: true,
-  big: true,
-  code: true,
-  del: true,
-  em: true,
-  i: true,
-  ins: true,
-  label: true,
-  q: true,
-  small: true,
-  span: true,
-  strong: true,
-  sub: true,
-  sup: true
-}
-/**
- * @description 判断是否为行内标签
- */
-module.exports = {
-  isInline: function (tagName, style) {
-    return inlineTags[tagName] || (style || '').indexOf('display:inline') !== -1
-  }
-}
-</script>
-<script>
-
-import node from './node'
-export default {
-  name: 'node',
-  options: {
-    // #ifdef MP-WEIXIN
-    virtualHost: true,
-    // #endif
-    // #ifdef MP-TOUTIAO
-    addGlobalClass: false
-    // #endif
-  },
-  data () {
-    return {
-      ctrl: {},
-      // #ifdef MP-WEIXIN
-      isiOS: uni.getDeviceInfo().system.includes('iOS')
-      // #endif
-    }
-  },
-  props: {
-    name: String,
-    attrs: {
-      type: Object,
-      default () {
-        return {}
-      }
-    },
-    childs: Array,
-    opts: Array
-  },
-  components: {
-
-    // #ifndef (H5 || APP-PLUS) && VUE3
-    node
-    // #endif
-  },
-  mounted () {
-    this.$nextTick(() => {
-      for (this.root = this.$parent; this.root.$options.name !== 'u-parse'; this.root = this.root.$parent);
-    })
-    // #ifdef H5 || APP-PLUS
-    if (this.opts[0]) {
-      let i
-      for (i = this.childs.length; i--;) {
-        if (this.childs[i].name === 'img') break
-      }
-      if (i !== -1) {
-        this.observer = uni.createIntersectionObserver(this).relativeToViewport({
-          top: 500,
-          bottom: 500
-        })
-        this.observer.observe('._img', res => {
-          if (res.intersectionRatio) {
-            this.$set(this.ctrl, 'load', 1)
-            this.observer.disconnect()
-          }
-        })
-      }
-    }
-    // #endif
-  },
-  beforeUnmount () {
-    // #ifdef H5 || APP-PLUS
-    if (this.observer) {
-      this.observer.disconnect()
-    }
-    // #endif
-  },
-  methods:{
-    // #ifdef MP-WEIXIN
-    toJSON () { return this },
-    // #endif
-    /**
-     * @description 播放视频事件
-     * @param {Event} e
-     */
-    play (e) {
-      this.root.$emit('play')
-      // #ifndef APP-PLUS
-      if (this.root.pauseVideo) {
-        let flag = false
-        const id = e.target.id
-        for (let i = this.root._videos.length; i--;) {
-          if (this.root._videos[i].id === id) {
-            flag = true
-          } else {
-            this.root._videos[i].pause() // 自动暂停其他视频
-          }
-        }
-        // 将自己加入列表
-        if (!flag) {
-          const ctx = uni.createVideoContext(id
-            // #ifndef MP-BAIDU
-            , this
-            // #endif
-          )
-          ctx.id = id
-          if (this.root.playbackRate) {
-            ctx.playbackRate(this.root.playbackRate)
-          }
-          this.root._videos.push(ctx)
-        }
-      }
-      // #endif
-    },
-
-    /**
-     * @description 图片点击事件
-     * @param {Event} e
-     */
-    imgTap (e) {
-      const node = this.childs[e.currentTarget.dataset.i]
-      if (node.a) {
-        this.linkTap(node.a)
-        return
-      }
-      if (node.attrs.ignore) return
-      // #ifdef H5 || APP-PLUS
-      node.attrs.src = node.attrs.src || node.attrs['data-src']
-      // #endif
-      this.root.$emit('imgTap', node.attrs)
-      // 自动预览图片
-      if (this.root.previewImg) {
-        uni.previewImage({
-          // #ifdef MP-WEIXIN
-          showmenu: this.root.showImgMenu,
-          // #endif
-          // #ifdef MP-ALIPAY
-          enablesavephoto: this.root.showImgMenu,
-          enableShowPhotoDownload: this.root.showImgMenu,
-          // #endif
-          current: parseInt(node.attrs.i),
-          urls: this.root.imgList
-        })
-      }
-    },
-
-    /**
-     * @description 图片长按
-     */
-    imgLongTap (e) {
-      // #ifdef APP-PLUS
-      const attrs = this.childs[e.currentTarget.dataset.i].attrs
-      if (this.opts[3] && !attrs.ignore) {
-        uni.showActionSheet({
-          itemList: ['保存图片'],
-          success: () => {
-            const save = path => {
-              uni.saveImageToPhotosAlbum({
-                filePath: path,
-                success () {
-                  uni.showToast({
-                    title: '保存成功'
-                  })
-                }
-              })
-            }
-            if (this.root.imgList[attrs.i].startsWith('http')) {
-              uni.downloadFile({
-                url: this.root.imgList[attrs.i],
-                success: res => save(res.tempFilePath)
-              })
-            } else {
-              save(this.root.imgList[attrs.i])
-            }
-          }
-        })
-      }
-      // #endif
-    },
-
-    /**
-     * @description 图片加载完成事件
-     * @param {Event} e
-     */
-    imgLoad (e) {
-      const i = e.currentTarget.dataset.i
-      /* #ifndef H5 || (APP-PLUS && VUE2) */
-      if (!this.childs[i].w) {
-        // 设置原宽度
-        this.$set(this.ctrl, i, e.detail.width)
-      } else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
-        // 加载完毕,取消加载中占位图
-        this.$set(this.ctrl, i, 1)
-      }
-      this.checkReady()
-    },
-
-    /**
-     * @description 检查是否所有图片加载完毕
-     */
-    checkReady () {
-      if (!this.root.lazyLoad) {
-        this.root._unloadimgs -= 1
-        if (!this.root._unloadimgs) {
-          setTimeout(() => {
-            this.root.getRect().then(rect => {
-              this.root.$emit('ready', rect)
-            }).catch(() => {
-              this.root.$emit('ready', {})
-            })
-          }, 350)
-        }
-      }
-    },
-
-    /**
-     * @description 链接点击事件
-     * @param {Event} e
-     */
-    linkTap (e) {
-      const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
-      const attrs = node.attrs || e
-      const href = attrs.href
-      this.root.$emit('linkTap', Object.assign({
-        innerText: this.root.getText(node.children || []) // 链接内的文本内容
-      }, attrs))
-      if (href) {
-        if (href[0] === '#') {
-          // 跳转锚点
-          this.root.navigateTo(href.substring(1)).catch(() => { })
-        } else if (href.split('?')[0].includes('://')) {
-          // 复制外部链接
-          if (this.root.copyLink) {
-            // #ifdef H5
-            window.open(href)
-            // #endif
-            // #ifdef MP
-            uni.setClipboardData({
-              data: href,
-              success: () =>
-                uni.showToast({
-                  title: '链接已复制'
-                })
-            })
-            // #endif
-            // #ifdef APP-PLUS
-            plus.runtime.openWeb(href)
-            // #endif
-          }
-        } else {
-          // 跳转页面
-          uni.navigateTo({
-            url: href,
-            fail () {
-              uni.switchTab({
-                url: href,
-                fail () { }
-              })
-            }
-          })
-        }
-      }
-    },
-
-    /**
-     * @description 错误事件
-     * @param {Event} e
-     */
-    mediaError (e) {
-      const i = e.currentTarget.dataset.i
-      const node = this.childs[i]
-      // 加载其他源
-      if (node.name === 'video' || node.name === 'audio') {
-        let index = (this.ctrl[i] || 0) + 1
-        if (index > node.src.length) {
-          index = 0
-        }
-        if (index < node.src.length) {
-          this.$set(this.ctrl, i, index)
-          return
-        }
-      } else if (node.name === 'img') {
-        // #ifdef H5 && VUE3
-        if (this.opts[0] && !this.ctrl.load) return
-        // #endif
-        // 显示错误占位图
-        if (this.opts[2]) {
-          this.$set(this.ctrl, i, -1)
-        }
-        this.checkReady()
-      }
-      if (this.root) {
-        this.root.$emit('error', {
-          source: node.name,
-          attrs: node.attrs,
-          // #ifndef H5 && VUE3
-          errMsg: e.detail.errMsg
-          // #endif
-        })
-      }
-    }
-  }
-}
-</script>
-<style>
-/* a 标签默认效果 */
-._a {
-  padding: 1.5px 0 1.5px 0;
-  color: #366092;
-  /* #ifndef APP-NVUE */
-  word-break: break-all;
-  /* #endif */
-}
-
-/* a 标签点击态效果 */
-._hover {
-  text-decoration: underline;
-  opacity: 0.7;
-}
-
-/* 图片默认效果 */
-._img {
-  max-width: 100%;
-  -webkit-touch-callout: none;
-}
-
-/* 内部样式 */
-
-._block {
-  /* #ifndef APP-NVUE */
-  display: block;
-  /* #endif */
-}
-
-._b,
-._strong {
-  font-weight: bold;
-}
-
-._code {
-  font-family: monospace;
-}
-
-._del {
-  text-decoration: line-through;
-}
-
-._em,
-._i {
-  font-style: italic;
-}
-
-._h1 {
-  font-size: 2em;
-}
-
-._h2 {
-  font-size: 1.5em;
-}
-
-._h3 {
-  font-size: 1.17em;
-}
-
-._h5 {
-  font-size: 0.83em;
-}
-
-._h6 {
-  font-size: 0.67em;
-}
-
-._h1,
-._h2,
-._h3,
-._h4,
-._h5,
-._h6 {
-  /* #ifndef APP-NVUE */
-  display: block;
-  /* #endif */
-  font-weight: bold;
-}
-
-._image {
-  height: 1px;
-}
-
-._ins {
-  text-decoration: underline;
-}
-
-._li {
-  display: list-item;
-}
-
-._ol {
-  list-style-type: decimal;
-}
-
-._ol,
-._ul {
-  /* #ifndef APP-NVUE */
-  display: block;
-  /* #endif */
-  padding-left: 40px;
-  margin: 1em 0;
-}
-
-._q::before {
-  content: '"';
-}
-
-._q::after {
-  content: '"';
-}
-
-._sub {
-  font-size: smaller;
-  vertical-align: sub;
-}
-
-._sup {
-  font-size: smaller;
-  vertical-align: super;
-}
-
-._thead,
-._tbody,
-._tfoot {
-  display: table-row-group;
-}
-
-._tr {
-  display: table-row;
-}
-
-._td,
-._th {
-  display: table-cell;
-  vertical-align: middle;
-}
-
-._th {
-  font-weight: bold;
-  text-align: center;
-}
-
-._ul {
-  list-style-type: disc;
-}
-
-._ul ._ul {
-  margin: 0;
-  list-style-type: circle;
-}
-
-._ul ._ul ._ul {
-  list-style-type: square;
-}
-
-._abbr,
-._b,
-._code,
-._del,
-._em,
-._i,
-._ins,
-._label,
-._q,
-._span,
-._strong,
-._sub,
-._sup {
-  display: inline;
-}
-
-/* #ifdef APP-PLUS */
-._video {
-  width: 300px;
-  height: 225px;
-}
-/* #endif */
-</style>

+ 0 - 22
src/components/display/parse/parse.js

@@ -1,22 +0,0 @@
-/*
- * @Author       : LQ
- * @Description  :
- * @version      : 1.0
- * @Date         : 2021-08-20 16:44:21
- * @LastAuthor   : LQ
- * @lastTime     : 2021-08-20 17:17:33
- * @FilePath     : /u-view2.0/uview-ui/libs/config/props/parse.js
- */
-export default {
-    // parse
-    parse: {
-        copyLink: true,
-        errorImg: '',
-        lazyLoad: false,
-        loadingImg: '',
-        pauseVideo: true,
-        previewImg: true,
-        setTitle: true,
-        showImgMenu: true
-    }
-}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 1337
src/components/display/parse/parser.js


+ 0 - 48
src/components/display/parse/props.js

@@ -1,48 +0,0 @@
-import { defineMixin } from '../../libs/vue'
-import defProps from '../../libs/config/props.js'
-export const props = defineMixin({
-    props: {
-		containerStyle: {
-          type: String,
-          default: null
-		},
-        content: String,
-        copyLink: {
-		  type: Boolean,
-		  default: () => defProps.parse.copyLink
-        },
-        domain: String,
-        errorImg: {
-		  type: String,
-		  default: () => defProps.parse.errorImg
-        },
-        lazyLoad: {
-		  type: Boolean,
-		  default: () => defProps.parse.lazyLoad
-        },
-        loadingImg: {
-		  type: String,
-		  default: () => defProps.parse.loadingImg
-        },
-        pauseVideo: {
-		  type: Boolean,
-		  default: () => defProps.parse.pauseVideo
-        },
-        previewImg: {
-		  type: Boolean,
-		  default: () => defProps.parse.previewImg
-        },
-        scrollTable: Boolean,
-        selectable: Boolean,
-        setTitle: {
-		  type: Boolean,
-		  default: () => defProps.parse.setTitle
-        },
-        showImgMenu: {
-		  type: Boolean,
-		  default: () => defProps.parse.showImgMenu
-        },
-        tagStyle: Object,
-        useAnchor: null
-	  }
-}

+ 1 - 1
src/components/feedback/DropdownMenuItem.vue

@@ -245,7 +245,7 @@ async function updateDialogMargin() {
       })
       .exec();
   });
-  const systemInfo = uni.getSystemInfoSync();
+  const systemInfo = uni.getWindowInfo();
   const pages = getCurrentPages();
   let v = 0
   if (topContext.direction.value === 'up') {

+ 8 - 3
src/components/layout/BaseView.ts

@@ -70,10 +70,15 @@ export function useBaseViewStyleBuilder(props: FlexProps) {
 
     if (props.inset) {
       if (Array.isArray(props.inset)) {
-        obj.left = themeContext.resolveThemeSize(props.inset[0]);
+        obj.top = themeContext.resolveThemeSize(props.inset[0]);
         obj.right = themeContext.resolveThemeSize(props.inset[1]);
-        obj.top = themeContext.resolveThemeSize(props.inset[2]);
-        obj.bottom = themeContext.resolveThemeSize(props.inset[3]);
+        obj.bottom = themeContext.resolveThemeSize(props.inset[2]);
+        obj.left = themeContext.resolveThemeSize(props.inset[3]);
+      } else if (typeof props.inset === 'object') {
+        obj.left = themeContext.resolveThemeSize(props.inset.l);
+        obj.right = themeContext.resolveThemeSize(props.inset.r);
+        obj.top = themeContext.resolveThemeSize(props.inset.t);
+        obj.bottom = themeContext.resolveThemeSize(props.inset.b);
       } else {
         const v = themeContext.resolveThemeSize(props.inset);
         obj.left = v;

+ 2 - 1
src/components/layout/FlexView.vue

@@ -21,7 +21,8 @@
  */
 import { computed, getCurrentInstance } from 'vue';
 import { RandomUtils } from '@imengyu/imengyu-utils';
-import { useBaseViewStyleBuilder, type ThemePaddingOrMarginType, type ThemeSizeType } from './BaseView';
+import { useBaseViewStyleBuilder } from './BaseView';
+import type { ThemePaddingOrMarginType, ThemeSizeType } from '../theme/ThemeDefine';
 
 export type FlexDirection = "row"|"column"|'row-reverse'|'column-reverse';
 export type FlexJustifyType =  'flex-start' | 'flex-end' | 'center' |'space-between' |'space-around' |'space-evenly';

+ 1 - 1
src/components/layout/space/SafeAreaMargin.vue

@@ -10,7 +10,7 @@
 </template>
 
 <script setup lang="ts">
-const systemInfo = uni.getSystemInfoSync();
+const systemInfo = uni.getWindowInfo();
 const safeAreaInsets = systemInfo?.safeAreaInsets || {
   top: 0,
   bottom: 0,

+ 1 - 1
src/components/layout/space/SafeAreaPadding.vue

@@ -14,7 +14,7 @@
 </template>
 
 <script setup lang="ts">
-const systemInfo = uni.getSystemInfoSync();
+const systemInfo = uni.getWindowInfo();
 const safeAreaInsets = systemInfo.safeAreaInsets || {
   top: 0,
   bottom: 0,

+ 1 - 1
src/components/layout/space/StatusBarSpace.vue

@@ -21,7 +21,7 @@
 <script setup lang="ts">
 import { useTheme } from '@/components/theme/ThemeDefine';
 
-const systemInfo = uni.getSystemInfoSync();
+const systemInfo = uni.getWindowInfo();
 const height = systemInfo.statusBarHeight || 0;
 
 const themeContext = useTheme();

+ 1 - 1
src/components/layout/space/XBarSpace.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script setup lang="ts">
-const systemInfo = uni.getSystemInfoSync();
+const systemInfo = uni.getWindowInfo();
 const safeAreaBottom = systemInfo.safeAreaInsets?.bottom || 0;// 底部安全区距离
 
 defineOptions({

+ 0 - 2
src/components/list/FixedVirtualList.vue

@@ -203,8 +203,6 @@ const visibleItems = computed(() => {
 // 占位容器样式(总滚动区域)
 const placeholderStyle = computed(() => {
   const size = `${props.data.length * props.itemSize}px`;
-  console.log('size', size);
-  
   return props.direction === 'vertical' 
     ? { height: size, width: '100%' }
     : { width: size, height: '100%' };

+ 1 - 1
src/components/nav/TabBar.vue

@@ -107,7 +107,7 @@ provide('TabBarContext', {
   resetCounter,
 })
 
-const systemInfo = uni.getSystemInfoSync();
+const systemInfo = uni.getWindowInfo();
 const safeAreaBottom = systemInfo.safeAreaInsets?.bottom || 0;// 底部安全区距离
 
 onMounted(() => {

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

@@ -317,7 +317,7 @@ export function configTheme(
   let defaultDarkTheme = ObjectUtils.clone(DefaultDarkTheme);
   
   const [theme, darkTheme] = cb?.(defaultTheme, defaultDarkTheme) ?? [defaultTheme, defaultDarkTheme];
-  const currentSystemDark = ref(autoMatchSystemDark !== false && uni.getSystemInfoSync().theme === 'dark');
+  const currentSystemDark = ref(autoMatchSystemDark !== false && uni.getAppBaseInfo().theme === 'dark');
   const currentTheme = shallowRef(currentSystemDark.value ? darkTheme : theme);
 
   provide(ThemeKey, currentTheme);

+ 1 - 1
src/components/theme/ThemeTools.ts

@@ -14,7 +14,7 @@ const info = {
 }
 // #endif
 // #ifndef H5
-const info = uni.getSystemInfoSync() ;
+const info = uni.getWindowInfo() ;
 // #endif
 
 export const screenWidth = info.screenWidth;

+ 2 - 0
src/components/utils/DialogAction.ts

@@ -25,11 +25,13 @@ function toast(content: string) {
 function alert(option: {
   title?: string,
   content?: string,
+  confirmText?: string,
 }) {
   uni.showModal({
     title: option.title,
     content: option.content,
     showCancel: false,
+    confirmText: option.confirmText,
   })
 };
 /**

+ 18 - 0
src/pages.json

@@ -63,6 +63,22 @@
       "path": "pages/home/village/index",
       "style": {
         "navigationBarTitleText": "村社详情",
+        "navigationStyle": "custom",
+        "enablePullDownRefresh": false
+      }
+    },
+    {
+      "path": "pages/home/post/publish",
+      "style": {
+        "navigationBarTitleText": "发布微信贴图",
+        "enablePullDownRefresh": false
+      }
+    },
+    {
+      "path": "pages/home/post/detail",
+      "style": {
+        "navigationBarTitleText": "微信贴图详情",
+        "navigationStyle": "custom",
         "enablePullDownRefresh": false
       }
     },
@@ -70,6 +86,7 @@
       "path": "pages/home/village/rank/volunteer",
       "style": {
         "navigationBarTitleText": "志愿者排名",
+        "navigationStyle": "custom",
         "enablePullDownRefresh": false
       }
     },
@@ -77,6 +94,7 @@
       "path": "pages/home/village/rank/village",
       "style": {
         "navigationBarTitleText": "村落排名",
+        "navigationStyle": "custom",
         "enablePullDownRefresh": false
       }
     },

+ 4 - 6
src/pages/home/components/LightMap.vue

@@ -104,6 +104,8 @@ const props = defineProps<{
 }>();
 
 const regionLoader = useSimpleDataLoader(async () => {
+  if (!props.city)
+    return [];
   return (await CommonContent.searchRegion(props.city)).map(p => ({
     id: p.id,
     name: p.title,
@@ -114,18 +116,14 @@ const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
     markerIds: Array.from(villageData.keys()),
   })
   villageData.clear();
-
   if (!selectedRegion.value)
     return [];
-
-    console.log('selectedRegion.value', selectedRegion.value);
-
   await waitTimeOut(200);
   const res = (await LightVillageApi.getVillageList(undefined, selectedRegion.value, undefined, 1, 100)).map((p, i) => {
     villageData.set(p.id, p);
     const maker : MapMarker = {
       id: p.id ?? i,
-      title: p.villageName,
+      title: p.name,
       longitude: Number(p.longitude),
       latitude: Number(p.latitude),
       width: 30,
@@ -134,7 +132,7 @@ const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
       joinCluster: true,
       callout: {
         display: 'ALWAYS',
-        content: p.villageName,
+        content: p.name,
         color: '#000000',
         fontSize: 12,
         padding: 5,

+ 10 - 26
src/pages/home/index.vue

@@ -115,10 +115,10 @@
         <IndexCommonImageItem
           :image="item.image"
           :title="item.title"
-          :desc="item.desc"
-          :userName="item.userName"
-          :likes="item.likes"
-          :isLike="item.isLike"
+          :desc="item.content ?? ''"
+          :userName="item.nickName ?? ''"
+          :likes="item.likeCount"
+          :isLike="false"
           @click="goMessageDetails(item)"
         />
       </MasonryGridItem>
@@ -228,20 +228,7 @@ const villageUserRankListLoader = useSimpleDataLoader(async () => {
 
   return res
 });
-const recommendLoader = useSimplePageListLoader(20, async (page, pageSize, params) => {
-  const res = await LightVillageApi.getMessages(page, pageSize, params?.keywords ?? '');
-  return {
-    list: res.data.map((item) => ({
-      image: item.image ?? '',
-      title: item.title,
-      desc: item.content ?? '',
-      userName: item.nick_name ?? '',
-      likes: item.like_count,
-      isLike: false,
-    })),
-    total: res.total,
-  };
-}, true);
+const recommendLoader = useSimplePageListLoader(20, async (page, pageSize, params) => await LightVillageApi.getMessages(page, pageSize), true);
 
 watch(currentRegion, async (newVal) => {
   await villageRankListLoader.reload();
@@ -256,12 +243,9 @@ async function goDetails(item: VillageListItem) {
   emit('goVillage')
 }
 function goMessageDetails(item: any) {
-  uni.openOfficialAccountArticle({
-    url: item.jump_url,
-    fail: (err) => {
-      console.error(err);
-    },
-  })
+  navTo('/pages/home/post/detail', {
+    id: item.id,
+  });
 }
 async function handleChangedCity(city: string) {
   currentCity.value = city;
@@ -292,14 +276,14 @@ onMounted(async () => {
     }
   } catch (error) {
     console.error(error);
-    toast('获取当前位置失败,现在显示的是');
+    toast('获取当前位置失败,您可以手动选择城市');
   }
   const res = await FollowVillageApi.getFollowVillageList({ page: 1, pageSize: 200 });
   villageStore.setMyFollowVillages(res.list);
   if (res.list.length > 0) {
     const currentVillage = villageStore.loadCurrentVillage();
     if (currentVillage) {
-      villageStore.setCurrentVillage(res.list.find(p => p.id === currentVillage) as VillageListItem);
+      villageStore.setCurrentVillage(res.list.find(p => p.id === currentVillage) as VillageListItem || res.list[0]);
     } else {
       villageStore.setCurrentVillage(res.list[0]);
     }

+ 113 - 0
src/pages/home/post/detail.vue

@@ -0,0 +1,113 @@
+<template>
+  <FlexCol :innerStyle="{
+    backgroundImage: 'url(https://xy.wenlvti.net/app_static/images/mine/TopBanner.png)',
+    backgroundSize: '100% auto',
+    backgroundRepeat: 'no-repeat',
+    backgroundPosition: 'top center',
+    minHeight: '100vh',
+  }">
+    <StatusBarSpace />
+    <NavBar :title="contentLoader.content.value?.title" leftButton="back" />
+    <SimplePageContentLoader :loader="contentLoader">
+      <template v-if="contentLoader.content.value">
+        <ImageSwiper :images="contentLoader.content.value.images" />
+        <FlexCol gap="gap.md" padding="space.md">
+          <H2>{{ contentLoader.content.value.title }}</H2>
+          <FlexRow align="center">
+            <Text :text="contentLoader.content.value.nickName" />
+          </FlexRow>
+          <Parse :content="contentLoader.content.value.content || ''" />
+        </FlexCol>
+      </template>
+    </SimplePageContentLoader>
+    <FlexCol
+      v-if="contentLoader.content.value" 
+      position="fixed" 
+      :inset="{ r: 0, b: 0, l: 0 }" 
+    >
+      <FlexRow
+        padding="space.md" 
+        justify="space-between"
+      >
+        <FlexRow align="center" gap="gap.md">
+          <Avatar
+            :image="contentLoader.content.value.avatar"
+            :size="40"
+          />
+          <Text :text="contentLoader.content.value.nickName" fontConfig="contentText" />
+        </FlexRow>
+        <FlexRow align="center" gap="gap.md">
+          <IconButton 
+            icon="favorite"
+            :text="contentLoader.content.value.likeCount.toString()"
+            @click="handleLike"
+          />
+          <button class="remove-button-style" open-type="share">
+            <IconButton 
+              icon="share"
+              :text="contentLoader.content.value.shareCount.toString()"
+              @click="handleShare"
+            />
+          </button>
+        </FlexRow>
+      </FlexRow>
+      <XBarSpace />
+    </FlexCol>
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import LightVillageApi from '@/api/light/LightVillageApi';
+import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
+import ImageSwiper from '@/common/components/parts/ImageSwiper.vue';
+import Parse from '@/components/display/parse/Parse.vue';
+import H2 from '@/components/typography/H2.vue';
+import Text from '@/components/basic/Text.vue';
+import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
+import NavBar from '@/components/nav/NavBar.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import IconButton from '@/components/basic/IconButton.vue';
+import { toast } from '@/components/utils/DialogAction';
+import Avatar from '@/components/display/Avatar.vue';
+import XBarSpace from '@/components/layout/space/XBarSpace.vue';
+import { onShareTimeline, onShareAppMessage } from '@dcloudio/uni-app';
+
+const { querys } = useLoadQuerys({ 
+  id: 0,
+}, () => contentLoader.reload());
+
+const contentLoader = useSimpleDataLoader(async () => {
+  const res = await LightVillageApi.getMessageDetails(querys.value.id);
+  uni.setNavigationBarTitle({ title: res.title });
+  return res;
+}, false);
+
+async function handleLike() {
+  toast('TODO!');
+}
+async function handleShare() {
+  uni.share({
+    title: contentLoader.content.value?.title,
+    imageUrl: contentLoader.content.value?.images[0],
+    path: `/pages/home/post/detail?id=${querys.value.id}`,
+  });
+}
+
+onShareTimeline(() => {
+  return {
+    title: contentLoader.content.value?.title,
+    imageUrl: contentLoader.content.value?.images[0],
+    path: `/pages/home/post/detail?id=${querys.value.id}`,
+  };
+});
+onShareAppMessage(() => {
+  return {
+    title: contentLoader.content.value?.title,
+    imageUrl: contentLoader.content.value?.images[0],
+    path: `/pages/home/post/detail?id=${querys.value.id}`,
+  };
+});
+</script>

src/pages/home/post/post.vue → src/pages/home/post/publish.vue


+ 1 - 1
src/pages/home/village/components/VillageTree.vue

@@ -22,7 +22,7 @@ import { getCurrentInstance, onBeforeUnmount, onMounted } from 'vue';
 
 const HEIGHT = 260;
 const instance = getCurrentInstance();
-const systemInfo = uni.getSystemInfoSync();
+const systemInfo = uni.getWindowInfo();
 const render = new MiniRender.Scene(
   {
     canvas: new UniWeappRender('villageTree', instance),

+ 68 - 76
src/pages/home/village/introd/card.vue

@@ -183,24 +183,39 @@
     </ProvideVar>
 
     <!-- 文脉乡源 -->
-    <HomeTitle title="文脉乡源" />
+    <HomeTitle title="文脉乡源">
+      <template #right>
+        <BackgroundImageButton 
+          backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagActive.png"
+          :backgroundCutBorder="[10, 10, 10, 10]"
+          :backgroundCutBorderSize="[10, 10, 10, 10]"
+          :padding="[15, 20]"
+          :innerStyle="{ marginRight: '20rpx' }"
+          @click="handleGoPublish()"
+        >
+          <Text text="去发布" fontConfig="contentText" color="white" />
+        </BackgroundImageButton>
+      </template>
+    </HomeTitle>
     <RoundTags v-model:active="listActiveTag" :tags="['广场', '老味道', '老手艺', '老物件', '老故事']" />
-    <MasonryGrid>
-      <MasonryGridItem
-        v-for="(item, i) in recommendLoader.content.value"
-        :key="i"
-        :width="340"
-      >
-        <IndexCommonImageItem
-          :image="item.image"
-          :title="item.title"
-          :desc="item.desc"
-          :userName="item.userName"
-          :likes="item.likes"
-          :isLike="item.isLike"
-        />
-      </MasonryGridItem>
-    </MasonryGrid>
+    <SimplePageListLoader :loader="recommendLoader">
+      <MasonryGrid>
+        <MasonryGridItem
+          v-for="(item, i) in recommendLoader.list.value"
+          :key="i"
+          :width="340"
+        >
+          <IndexCommonImageItem
+            :image="item.image"
+            :title="item.title"
+            :desc="item.content ?? ''"
+            :userName="item.nickName ?? ''"
+            :likes="item.likeCount"
+            :isLike="false"
+          />
+        </MasonryGridItem>
+      </MasonryGrid>
+    </SimplePageListLoader>
 
   </FlexCol>
 
@@ -208,8 +223,14 @@
 
 <script setup lang="ts">
 import { computed, ref, watch } from 'vue';
-import HomeTitle from '@/common/components/parts/HomeTitle.vue';
+import { useSimplePageListLoader } from '@/components/composeabe/loader/SimplePageListLoader';
+import { useAuthStore } from '@/store/auth';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import { useVillageStore } from '@/store/village';
+import { confirm, toast } from '@/components/utils/DialogAction';
+import { ArrayUtils } from '@imengyu/imengyu-utils';
+import { navTo } from '@/components/utils/PageAction';
+import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import Button from '@/components/basic/Button.vue';
 import Icon from '@/components/basic/Icon.vue';
 import Image from '@/components/basic/Image.vue';
@@ -229,18 +250,14 @@ import GridItem from '@/components/layout/grid/GridItem.vue';
 import MasonryGrid from '@/components/layout/masonry/MasonryGrid.vue';
 import MasonryGridItem from '@/components/layout/masonry/MasonryGridItem.vue';
 import IndexCommonImageItem from '@/common/components/parts/IndexCommonImageItem.vue';
-import { useVillageStore } from '@/store/village';
 import FollowVillageApi from '@/api/light/FollowVillageApi';
-import { confirm, toast } from '@/components/utils/DialogAction';
 import LightVillageApi from '@/api/light/LightVillageApi';
-import { ArrayUtils } from '@imengyu/imengyu-utils';
-import { navTo } from '@/components/utils/PageAction';
+import SimplePageListLoader from '@/components/loader/SimplePageListLoader.vue';
 
+const authStore = useAuthStore();
 const villageStore = useVillageStore();
 const villageInfoLoader = useSimpleDataLoader(async () => {
-  const village = villageStore.currentVillage;
-  console.log('villageInfoLoader', village);
-  
+  const village = villageStore.currentVillage;  
   return {
     title: village?.name || '',
     desc: village?.desc || '',
@@ -328,57 +345,14 @@ function onUnFollow() {
   });
 }
 
-const recommendLoader = useSimpleDataLoader(async () => {
-  return [
-    {
-      image: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest1.jpg',
-      title: '厦门村庄精选记忆',
-      desc: '描述1',
-      userName: '月下木子',
-      likes: 100,
-      isLike: true,
-    },
-    {
-      image: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest2.jpg',
-      title: '厦门村庄精选记忆',
-      desc: '描述2',
-      userName: '月下木子',
-      likes: 200,
-      isLike: false,
-    },
-    {
-      image: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest3.jpg',
-      title: '厦门村庄精选记忆',
-      desc: '描述3',
-      userName: '月下木子',
-      likes: 300,
-      isLike: true,
-    },
-    {
-      image: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest4.jpg',
-      title: '厦门村庄精选记忆',
-      desc: '描述4',
-      userName: '月下木子',
-      likes: 400,
-      isLike: false,
-    },
-    {
-      image: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest5.jpg',
-      title: '厦门村庄精选记忆',
-      desc: '描述5',
-      userName: '月下木子',
-      likes: 500,
-      isLike: true,
-    },
-    {
-      image: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest6.jpg',
-      title: '厦门村庄精选记忆',
-      desc: '描述6',
-      userName: '月下木子',
-      likes: 600,
-      isLike: false,
-    },
-  ];
+const recommendLoader = useSimplePageListLoader(20, async (page, pageSize) => {
+  return await LightVillageApi.getMessages(page, pageSize, {
+    villageId: villageStore.currentVillage?.id ?? undefined,
+    keywords: listActiveTag.value,
+  });
+});
+watch(listActiveTag, () => {
+  recommendLoader.reload();
 });
 
 function handleGoCollect(taskName: string) {
@@ -388,4 +362,22 @@ function handleGoCollect(taskName: string) {
     taskPid: -1,
   });
 }
+function handleGoPublish() {
+  if (!authStore.isLogged) {
+    confirm({
+      title: '提示',
+      content: '登录后就可以发布村落贴图了',
+      confirmText: '去登录',
+    }).then((res) => {
+      if (res) {
+        navTo('/pages/user/login');
+      }
+    });
+    return;
+  }
+  navTo('/pages/home/post/publish', {
+    tag: listActiveTag.value,
+    villageId: villageStore.currentVillage?.id ?? undefined,
+  });
+}
 </script>

+ 66 - 53
src/pages/home/village/rank/village.vue

@@ -1,56 +1,65 @@
 <template>
-  <FlexCol gap="gap.lg" padding="space.md">
-
-    <BackgroundBox
-      v-for="(item, index) in villageRankListLoader.content.value"
-      :key="item.id"
-      backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxLong.png"
-      :backgroundCutBorder="[10,10,10,10]"
-      :backgroundCutBorderSize="[10,10,10,10]"
-    >
-      <Touchable
-        direction="row"
-        justify="space-between"
-        align="center"
-        gap="gap.md"
-        :padding="[25,25]"
-        @click="handleGoDetails(item)"
+  <FlexCol :innerStyle="{
+    backgroundImage: 'url(https://xy.wenlvti.net/app_static/images/dig/TopBanner.png)',
+    backgroundSize: '100% auto',
+    backgroundRepeat: 'no-repeat',
+    backgroundPosition: 'top center',
+    minHeight: '100vh',
+  }">
+    <StatusBarSpace />
+    <NavBar title="村落排名" leftButton="back" />
+    <FlexCol gap="gap.lg" padding="space.md">
+      <BackgroundBox
+        v-for="(item, index) in villageRankListLoader.content.value"
+        :key="item.id"
+        backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxDark.png"
+        :backgroundCutBorder="[6,6,6,6]"
+        :backgroundCutBorderSize="[10,10,10,10]"
       >
-        <FlexRow align="center" gap="gap.lg">
-          <BackgroundBox
-            backgroundImage="https://xy.wenlvti.net/app_static/images/village/ImageBlessingCount.png"
-            width="60rpx"
-            height="60rpx"
-            center
-          >
-            <Text :text="index + 1" fontConfig="h4" color="white" />
-          </BackgroundBox>
-          <Image 
-            :src="item.image" 
-            width="170rpx"
-            height="120rpx"
-            mode="aspectFill"
-            radius="radius.md"
-          />
-          <Text :text="item.title" fontConfig="contentText" />
-        </FlexRow>
-        <FlexRow center gap="gap.md">
-          <BackgroundBox
-            backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagNormal.png"
-            :backgroundCutBorder="[10,10,10,10]"
-            :backgroundCutBorderSize="[10,10,10,10]"
-            :padding="[15,10]"
-            center
-            direction="row"
-            gap="gap.md"
-            width="100"
-          >
-            <Image src="https://xy.wenlvti.net/app_static/images/village/IconLight.png" width="30rpx" height="30rpx" mode="aspectFill" />
-            <Text :text="item.points" fontConfig="contentText" />
-          </BackgroundBox>
-        </FlexRow>
-      </Touchable>
-    </BackgroundBox>
+        <Touchable
+          direction="row"
+          justify="space-between"
+          align="center"
+          gap="gap.md"
+          :padding="[25,25]"
+          @click="handleGoDetails(item)"
+        >
+          <FlexRow align="center" gap="gap.lg">
+            <BackgroundBox
+              backgroundImage="https://xy.wenlvti.net/app_static/images/village/ImageBlessingCount.png"
+              width="60rpx"
+              height="60rpx"
+              center
+            >
+              <Text :text="index + 1" fontConfig="h4" color="white" />
+            </BackgroundBox>
+            <Image 
+              :src="item.image" 
+              width="170rpx"
+              height="120rpx"
+              mode="aspectFill"
+              radius="radius.md"
+            />
+            <Text :text="item.title" fontConfig="contentText" />
+          </FlexRow>
+          <FlexRow center gap="gap.md">
+            <BackgroundBox
+              backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagNormal.png"
+              :backgroundCutBorder="[10,10,10,10]"
+              :backgroundCutBorderSize="[10,10,10,10]"
+              :padding="[15,10]"
+              center
+              direction="row"
+              gap="gap.md"
+              width="100"
+            >
+              <Image src="https://xy.wenlvti.net/app_static/images/village/IconLight.png" width="30rpx" height="30rpx" mode="aspectFill" />
+              <Text :text="item.points" fontConfig="contentText" />
+            </BackgroundBox>
+          </FlexRow>
+        </Touchable>
+      </BackgroundBox>
+    </FlexCol>
   </FlexCol>
 </template>
 
@@ -58,7 +67,7 @@
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
 import { useVillageStore } from '@/store/village';
-import { navTo } from '@/components/utils/PageAction';
+import { backAndCallOnPageBack, navTo } from '@/components/utils/PageAction';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import LightVillageApi from '@/api/light/LightVillageApi';
 import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
@@ -67,6 +76,8 @@ import Image from '@/components/basic/Image.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import Touchable from '@/components/feedback/Touchable.vue';
 import { waitTimeOut } from '@imengyu/imengyu-utils';
+import NavBar from '@/components/nav/NavBar.vue';
+import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 
 const { querys } = useLoadQuerys({
   regionId: 0,
@@ -94,6 +105,8 @@ async function handleGoDetails(item: { id: number }) {
   const details = await LightVillageApi.getVillageDetails(item.id);
   villageStore.setCurrentVillage(details);
   await waitTimeOut(100);
-  navTo('/pages/home/village/index');
+  backAndCallOnPageBack('villageRank', {
+    type: 'goVillage'
+  })
 }
 </script>

+ 59 - 47
src/pages/home/village/rank/volunteer.vue

@@ -1,52 +1,62 @@
 <template>
-  <FlexCol gap="gap.lg" padding="space.md">
+  <FlexCol :innerStyle="{
+    backgroundImage: 'url(https://xy.wenlvti.net/app_static/images/dig/TopBanner.png)',
+    backgroundSize: '100% auto',
+    backgroundRepeat: 'no-repeat',
+    backgroundPosition: 'top center',
+    minHeight: '100vh',
+  }">
+    <StatusBarSpace />
+    <NavBar title="志愿者排名" leftButton="back" />
     <VillageUserRankList :list="villageUserRankListFirst3" />
-    <BackgroundBox
-      v-for="(item, index) in villageUserRankListAfter3"
-      :key="item.id"
-      backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxLong.png"
-      :backgroundCutBorder="[10,10,10,10]"
-      :backgroundCutBorderSize="[10,10,10,10]"
-      :padding="[25,25]"
-      direction="row"
-      justify="space-between"
-      align="center"
-      gap="gap.md"
-    >
-      <FlexRow align="center" gap="gap.lg">
-        <BackgroundBox
-          backgroundImage="https://xy.wenlvti.net/app_static/images/village/ImageBlessingCount.png"
-          width="100rpx"
-          height="70rpx"
-          center
-        >
-          <Text :text="index + 4" fontConfig="h3" color="white" />
-        </BackgroundBox>
-        <Avatar 
-          :url="item.image" 
-          :size="70" 
-          mode="aspectFill" 
-          radius="radius.md" 
-        />
-        <Text :text="item.title" fontConfig="contentText" />
-      </FlexRow>
-      <FlexRow center gap="gap.md">
-        <Tag v-if="item.isAdmin" text="管理员" size="small" />
-        <BackgroundBox
-          backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagNormal.png"
-          :backgroundCutBorder="[10,10,10,10]"
-          :backgroundCutBorderSize="[10,10,10,10]"
-          :padding="[15,10]"
-          center
-          direction="row"
-          gap="gap.md"
-          width="100"
-        >
-          <Image src="https://xy.wenlvti.net/app_static/images/village/IconFruit.png" width="30rpx" height="30rpx" mode="aspectFill" />
-          <Text :text="item.score" fontConfig="contentText" />
-        </BackgroundBox>
-      </FlexRow>
-    </BackgroundBox>
+    <FlexCol gap="gap.lg" padding="space.md">
+      <BackgroundBox
+        v-for="(item, index) in villageUserRankListAfter3"
+        :key="item.id"
+        backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxDark.png"
+        :backgroundCutBorder="[6,6,6,6]"
+        :backgroundCutBorderSize="[10,10,10,10]"
+        :padding="[25,25]"
+        direction="row"
+        justify="space-between"
+        align="center"
+        gap="gap.md"
+      >
+        <FlexRow align="center" gap="gap.lg">
+          <BackgroundBox
+            backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxOrder.png"
+            width="120rpx"
+            height="90rpx"
+            center
+          >
+            <Text :text="index + 4" fontConfig="h5" color="white" />
+          </BackgroundBox>
+          <Avatar 
+            :url="item.image" 
+            :size="70" 
+            mode="aspectFill" 
+            radius="radius.md" 
+          />
+          <Text :text="item.title" fontConfig="contentText" />
+        </FlexRow>
+        <FlexRow center gap="gap.md">
+          <Tag v-if="item.isAdmin" text="管理员" size="small" />
+          <BackgroundBox
+            backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagNormal.png"
+            :backgroundCutBorder="[10,10,10,10]"
+            :backgroundCutBorderSize="[10,10,10,10]"
+            :padding="[15,10]"
+            center
+            direction="row"
+            gap="gap.md"
+            width="100"
+          >
+            <Image src="https://xy.wenlvti.net/app_static/images/village/IconFruit.png" width="30rpx" height="30rpx" mode="aspectFill" />
+            <Text :text="item.score" fontConfig="contentText" />
+          </BackgroundBox>
+        </FlexRow>
+      </BackgroundBox>
+    </FlexCol>
   </FlexCol>
 </template>
 
@@ -64,6 +74,8 @@ import Avatar from '@/components/display/Avatar.vue';
 import Tag from '@/components/display/Tag.vue';
 import Image from '@/components/basic/Image.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
+import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
+import NavBar from '@/components/nav/NavBar.vue';
 
 const { querys } = useLoadQuerys({
   regionId: 0,

+ 9 - 2
src/pages/index.vue

@@ -14,8 +14,8 @@
         textColor="whweixinite"
         align="left"
       />
-      <HomeIndex v-if="tabIndex === 0" @goVillage="tabIndex = 1" />
-      <VillageIndex v-else-if="tabIndex === 1" showSwitch />
+      <HomeIndex v-show="tabIndex === 0" @goVillage="tabIndex = 1" />
+      <VillageIndex v-if="tabIndex === 1" showSwitch />
       <DigIndex v-else-if="tabIndex === 2" />
       <DiscoverIndex v-else-if="tabIndex === 3" />
       <UserIndex v-else-if="tabIndex === 4" @goSubmit="tabIndex = 2" />
@@ -55,6 +55,13 @@ import CommonRoot from '@/components/dialog/CommonRoot.vue';
 const tabIndex = ref(0);
 const themeContext = useTheme();
 
+defineExpose({
+  onPageBack: (name: string, data: Record<string, unknown>) => {
+    if (data.type === 'goVillage') {
+      tabIndex.value = 1;
+    }
+  }
+})
 onShareAppMessage(() => {
   return {
     title: '村社文化资源挖掘平台',

+ 1 - 1
src/pages/test/render.vue

@@ -20,7 +20,7 @@ import { getCurrentInstance, onBeforeUnmount, onMounted } from 'vue';
 
 const HEIGHT = 250;
 const instance = getCurrentInstance();
-const systemInfo = uni.getSystemInfoSync();
+const systemInfo = uni.getWindowInfo();
 const render = new MiniRender.Scene(
   {
     canvas: new UniWeappRender('villageTree', instance),

+ 1 - 1
src/uni_modules/sp-editor/components/sp-editor/fab-tool.vue

@@ -30,7 +30,7 @@ export default {
   watch: {
     visible(newVal) {
       if (newVal) {
-        const { screenWidth } = uni.getSystemInfoSync()
+        const { screenWidth } = uni.getWindowInfo()
 
         this.$nextTick(() => {
           let placementWidth = 0