9433cfb9创建于 2025年12月31日历史提交
<template>
  <list-view :id="id" class="list" :bounces="false" :scroll-y="true" :custom-nested-scroll="true"
    @scrolltolower="loadData(null)" associative-container="nested-scroll-view">
    <list-item class="list-item" v-for="(item, _) in dataList" :key="item.plugin_id" type="10">
      <view class="list-item-icon">
        <image class="list-item-icon-image" :src="item.plugin_img_link"></image>
      </view>
      <view class="list-item-fill">
        <view class="flex-row">
          <text class="title">{{item.plugin_name}}</text>
        </view>
        <view>
          <text class="description-text">{{item.plugin_intro}}</text>
        </view>
        <text class="icon-star">{{convertToStarUnicode(item.score)}}</text>
        <view class="tag-list">
          <text class="tag-item" v-for="(item2, index2) in item.tags" :key="index2">{{item2}}</text>
        </view>
        <view class="flex-row update-date">
          <text class="update-date-text">更新日期</text>
          <text class="update-date-value">{{item.update_date}}</text>
          <text class="author">{{item.author_name}}</text>
        </view>
      </view>
    </list-item type="20">
    <list-item class="loading">
      <uni-loading :loading="loading" color="#999" :text="loadingText"></uni-loading>
    </list-item>
  </list-view>
</template>

<script setup lang="uts">
const SERVER_URL = "https://unidemo.dcloud.net.cn/plugin/uniappx-plugin-list"
const PAGE_SIZE = 10

type ListItem = {
  plugin_id : number,
  plugin_img_link : string,
  plugin_name : string,
  plugin_intro : string,
  score : number,
  tags : Array<string>,
  update_date : string,
  author_name : string,
}

type ResponseDataType = {
  code : number,
  data : ListItem[]
}

const props = defineProps({
  type: { type: String, default: '' },
  preload: { type: Boolean, default: false },
  id: { type: String, default: '' }
})

const loading = ref(false)
const dataList = ref<ListItem[]>([])
const isEnded = ref(false)
const loadingError = ref('')
const currentPage = ref(1)

const loadingText = computed((): string => {
  if (loading.value) {
    return "加载中..."
  } else if (isEnded.value) {
    return "没有更多了"
  } else if (loadingError.value.length > 0) {
    return loadingError.value
  } else {
    return ""
  }
})

function convertToStarUnicode(score : number) : string {
  const fill_code = '\ue879'
  const half_code = '\ue87a'
  const null_code = '\ue87b'

  const fillStarCount = parseInt(score / 10 % 10 + '')
  const halfStarCount = score % 10 >= 5 ? 1 : 0
  const nullStarCount = 5 - fillStarCount - halfStarCount

  let result = ''
  if (fillStarCount > 0) { result += fill_code.repeat(fillStarCount) }
  if (halfStarCount > 0) { result += half_code.repeat(halfStarCount) }
  if (nullStarCount > 0) { result += null_code.repeat(nullStarCount) }

  return result
}

function loadData(loadComplete : (() => void) | null) {
  if (loading.value || isEnded.value) {
    return
  }
  loading.value = true

  uni.request<ResponseDataType>({
    url: SERVER_URL,
    data: {
      type: props.type,
      page: currentPage.value,
      page_size: PAGE_SIZE
    },
    success: (res) => {
      const responseData = res.data
      // console.log('responseData',responseData)
      if (responseData == null) {
        return
      }
      dataList.value.push(...responseData.data)
      if (responseData.data.length == 0) {
        isEnded.value = true
      } else {
        currentPage.value++
      }
    },
    fail: (err) => {
      loadingError.value = err.errMsg
    },
    complete: () => {
      loading.value = false
      if (loadComplete != null) {
        loadComplete()
      }
    }
  })
}

function refreshData(loadComplete : (() => void) | null) {
  dataList.value.length = 0
  currentPage.value = 1
  loadData(loadComplete)
}

onMounted(() => {
  if (props.preload) {
    loadData(null)
  }
})

defineExpose({
  loadData,
  refreshData
})
</script>

<style>
  @font-face {
    font-family: "UtsStarIcons";
    src: url('@/static/fonts/icon-star.ttf');
  }

  .list {
    flex: 1;
    background-color: #ffffff;
  }

  .list-item {
    flex-direction: row;
    margin-top: 10px;
    padding: 10px;
  }

  .list-item-icon {
    position: relative;
  }

  .list-item-icon-image {
    width: 80px;
    height: 80px;
  }

  .list-item-fill {
    flex: 1;
    margin-left: 15px;
  }

  .description-text {
    font-size: 13px;
    color: #666;
    line-height: 19px;
  }

  .icon-star {
    font-family: "UtsStarIcons";
    font-size: 16px;
    font-style: normal;
    color: #ffca3e;
    letter-spacing: 3px;
  }

  .tag-list {
    flex-direction: row;
    margin-top: 5px;
  }

  .tag-item {
    font-size: 12px;
    background-color: #EFF9F0;
    color: #639069;
    border-radius: 20px;
    margin-right: 5px;
    padding: 2px 5px;
  }

  .update-date {
    margin-top: 10px;
  }

  .update-date-text {
    font-size: 12px;
    color: #888888;
  }

  .update-date-value {
    font-size: 12px;
    color: #777777;
    margin-left: 5px;
  }

  .author {
    font-size: 12px;
    color: #008000;
    margin-left: auto;
  }

  .loading {
    padding: 30px;
    align-items: center;
  }

  .flex-row {
    flex-direction: row;
  }
</style>