mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-09-12 16:40:40 +08:00
refactor: 整体优化项目页面与组件样式与结构
1. 移除冗余组件注释与老旧类名,统一样式规范 2. 新增自定义导航栏适配,修复导航栏层级与返回按钮逻辑 3. 调整列表页面布局,统一添加box-border与间距规范 4. 优化友链弹窗与联系页面样式,修复文案与交互细节 5. 重构关于与免责声明页面,适配动态站点信息 6. 统一瞬间页面与投票页面的UI风格与代码结构
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { checkUrl } from '@/utils/url'
|
||||||
|
import { useAppConfigStore } from '@/store/appConfig'
|
||||||
import type { DataLoadingStatus } from '@/hooks/useDataLoading'
|
import type { DataLoadingStatus } from '@/hooks/useDataLoading'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@@ -31,6 +33,16 @@
|
|||||||
|
|
||||||
const emit = defineEmits<{ (e : 'refresh') : void }>()
|
const emit = defineEmits<{ (e : 'refresh') : void }>()
|
||||||
|
|
||||||
|
const appConfigStore = useAppConfigStore()
|
||||||
|
|
||||||
|
const appInfo = computed(() => {
|
||||||
|
const _appInfo = (appConfigStore.configs?.appConfig?.appInfo as any)
|
||||||
|
return {
|
||||||
|
name: _appInfo.name ?? 'UniHalo',
|
||||||
|
logo: checkUrl(_appInfo?.logo ?? 'https://uni-halo.925i.cn/logo.png')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const isLoading = computed(() => props.loadingStatus === 'loading')
|
const isLoading = computed(() => props.loadingStatus === 'loading')
|
||||||
|
|
||||||
const statusScene = computed(() => {
|
const statusScene = computed(() => {
|
||||||
@@ -64,9 +76,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="w-full flex flex-col items-center justify-center gap-y-4 text-sm"
|
<view class="relative w-full flex flex-col items-center justify-center gap-y-4 text-sm"
|
||||||
:style="{ minHeight: props.minHeight }">
|
:style="{ minHeight: props.minHeight }">
|
||||||
<view class="scene relative h-[250rpx] w-[250rpx] flex items-center justify-center"
|
|
||||||
|
<!-- logo背景 -->
|
||||||
|
<image v-if="false" :src="appInfo.logo" class="absolute left-1/2 top-1/2 -translate-1/2 z-0 opacity-10 w-46 h-46"></image>
|
||||||
|
|
||||||
|
<view class="scene relative z-1 h-[250rpx] w-[250rpx] flex items-center justify-center"
|
||||||
:class="statusScene.stageClass">
|
:class="statusScene.stageClass">
|
||||||
<view class="glow absolute inset-0 m-auto h-[220rpx] w-[220rpx] rounded-full" />
|
<view class="glow absolute inset-0 m-auto h-[220rpx] w-[220rpx] rounded-full" />
|
||||||
<view class="deco-dot dot-a absolute rounded-full" />
|
<view class="deco-dot dot-a absolute rounded-full" />
|
||||||
@@ -79,7 +95,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 文案区 -->
|
<!-- 文案区 -->
|
||||||
<view class="flex flex-col items-center">
|
<view class="relative z-2 flex flex-col items-center">
|
||||||
<view class="flex items-center justify-center text-sm font-bold" :class="statusScene.mainTextClass">
|
<view class="flex items-center justify-center text-sm font-bold" :class="statusScene.mainTextClass">
|
||||||
<text>{{ statusScene.mainText }}</text>
|
<text>{{ statusScene.mainText }}</text>
|
||||||
<view v-if="isLoading" class="ml-1 flex items-end gap-1">
|
<view v-if="isLoading" class="ml-1 flex items-end gap-1">
|
||||||
|
|||||||
@@ -87,11 +87,11 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="w-full box-border">
|
<view class="w-full box-border">
|
||||||
<view class="box-border pt-safe w-full fixed left-0 top-0 z-50" :class="customCalss" :style="[customStyle]">
|
<view class="box-border pt-safe w-full fixed left-0 top-0 z-100" :class="customCalss" :style="[customStyle]">
|
||||||
<view class="w-full h-[46px] flex items-center gap-x-4 box-border px-3 backdrop-blur-[2rpx]">
|
<view class="w-full h-[46px] flex items-center gap-x-4 box-border px-3 backdrop-blur-[2rpx]">
|
||||||
<!-- 左边 -->
|
<!-- 左边 -->
|
||||||
<view class="shrink-0" @click="handleBack()">
|
<view class="shrink-0 min-w-18" @click="handleBack()">
|
||||||
<view
|
<view v-if="props.useBack"
|
||||||
class="uh-global-card-glass h-7 px-3 rounded-full border flex items-center gap-x-2 text-gray-900 text-sm">
|
class="uh-global-card-glass h-7 px-3 rounded-full border flex items-center gap-x-2 text-gray-900 text-sm">
|
||||||
<wd-icon name="arrow-left" size="32rpx"></wd-icon>
|
<wd-icon name="arrow-left" size="32rpx"></wd-icon>
|
||||||
<view class="w-[1px] h-4 bg-white/60" />
|
<view class="w-[1px] h-4 bg-white/60" />
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/**
|
|
||||||
* 投票卡片(源自旧项目 components/vote-card,新建复刻)
|
|
||||||
* 纯展示卡片:列表接口一次返回,页面加工后传入,点击跳转详情
|
|
||||||
*/
|
|
||||||
import { formatTime as formatTimeUtil } from '@/utils/formatTime'
|
import { formatTime as formatTimeUtil } from '@/utils/formatTime'
|
||||||
|
|
||||||
interface IVoteCardOption {
|
interface IVoteCardOption {
|
||||||
@@ -50,7 +46,7 @@ function formatTime(date?: string, fmt = 'yyyy-MM-dd HH:mm'): string {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="uh-vote-card uh-global-card-glass box-border w-full rounded-2xl p-4" @click="handleToDetail">
|
<view class="uh-global-card-glass box-border w-full rounded-2xl p-4" @click="handleToDetail">
|
||||||
<!-- 头部:类型/状态/已投票 -->
|
<!-- 头部:类型/状态/已投票 -->
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<view class="flex flex-wrap items-center gap-1">
|
<view class="flex flex-wrap items-center gap-1">
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/**
|
|
||||||
* 关于项目页(源自旧项目 pagesA/about,新建复刻)
|
|
||||||
* 数字名片式设计:Hero 名片卡(渐变光斑透卡 + 徽章) + 彩色瓦片链接列表,点击复制
|
|
||||||
*/
|
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { useAppConfigStore } from '@/store/appConfig'
|
||||||
|
import { checkUrl } from '@/utils/url'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
@@ -12,6 +10,16 @@ definePage({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const appConfigStore = useAppConfigStore()
|
||||||
|
|
||||||
|
const appInfo = computed(() => {
|
||||||
|
const _appInfo = (appConfigStore.configs?.appConfig?.appInfo as any)
|
||||||
|
return {
|
||||||
|
name: _appInfo.name ?? 'UniHalo',
|
||||||
|
logo: checkUrl(_appInfo?.logo ?? 'https://uni-halo.925i.cn/logo.png')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{ title: '开源组织', value: '巷子工坊', copy: 'https://www.ialley.cn', tip: '巷子工坊官网已复制成功!', tileColor: '#5c6bc0', tileLetter: '组' },
|
{ title: '开源组织', value: '巷子工坊', copy: 'https://www.ialley.cn', tip: '巷子工坊官网已复制成功!', tileColor: '#5c6bc0', tileLetter: '组' },
|
||||||
{ title: '开源作者', value: '小莫唐尼', copy: 'https://www.xiaoxiaomo.cn', tip: '作者主页地址已复制', tileColor: '#26a69a', tileLetter: '作' },
|
{ title: '开源作者', value: '小莫唐尼', copy: 'https://www.xiaoxiaomo.cn', tip: '作者主页地址已复制', tileColor: '#26a69a', tileLetter: '作' },
|
||||||
@@ -37,42 +45,40 @@ onLoad(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="app-page box-border min-h-screen w-screen flex flex-col overflow-hidden bg-page px-4 pb-8 pt-6">
|
<view class="box-border min-h-screen w-screen flex flex-col overflow-hidden bg-page px-4 pb-8 pt-2">
|
||||||
<!-- 自定义导航 -->
|
<!-- 自定义导航 -->
|
||||||
<uh-navbar default-title="关于项目" title-color="text-gray-900" />
|
<uh-navbar default-title="关于项目" title-color="text-gray-900" />
|
||||||
|
|
||||||
<!-- Hero 名片卡(主题色光斑透过毛玻璃形成柔和渐变) -->
|
<view class="relative">
|
||||||
<view class="hero-wrap relative">
|
|
||||||
<view class="absolute h-[220rpx] w-[220rpx] rounded-full bg-[rgba(185,228,36,0.32)] -right-8 -top-8" />
|
<view class="absolute h-[220rpx] w-[220rpx] rounded-full bg-[rgba(185,228,36,0.32)] -right-8 -top-8" />
|
||||||
<view class="absolute top-[150rpx] h-[180rpx] w-[180rpx] rounded-full bg-[rgba(215,249,76,0.45)] -left-10" />
|
<view
|
||||||
<view class="hero-card uh-global-card-glass relative flex flex-col items-center rounded-3xl px-6 pb-7 pt-10">
|
class="absolute top-[150rpx] h-[180rpx] w-[180rpx] rounded-full bg-[rgba(215,249,76,0.45)] -left-10" />
|
||||||
<image class="logo-img h-[140rpx] w-[140rpx] rounded-3xl shadow-lg" src="https://uni-halo.925i.cn/logo.png" mode="aspectFill" />
|
<view class="uh-global-card-glass relative flex flex-col items-center rounded-2xl px-6 pb-7 pt-10">
|
||||||
<view class="mt-4 text-[40rpx] text-gray-900 font-bold">
|
<image class="h-[140rpx] w-[140rpx] rounded-3xl shadow-lg" :src="appInfo.logo" mode="aspectFill" />
|
||||||
uni-halo
|
<view class="mt-4 text-xl text-gray-900 font-bold">
|
||||||
|
{{appInfo.name}}
|
||||||
</view>
|
</view>
|
||||||
<view class="mt-3 flex items-center gap-2">
|
<view class="mt-3 flex items-center gap-2">
|
||||||
<text class="rounded-full bg-secondary px-3 py-1 text-[20rpx] text-[#4d7c0f]">AGPL-3.0 开源协议</text>
|
<text class="rounded-full bg-secondary px-3 py-1 text-[20rpx] text-[#4d7c0f]">AGPL-3.0 开源协议</text>
|
||||||
<text class="rounded-full bg-[#f6f3ee] px-3 py-1 text-[20rpx] text-gray-500">uni-app × Halo</text>
|
<text class="rounded-full bg-[#f6f3ee] px-3 py-1 text-[20rpx] text-gray-500">UniApp × Halo</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="mt-4 text-center text-[24rpx] text-gray-500 leading-relaxed">
|
<view class="mt-4 text-center text-xs text-gray-500 leading-relaxed">
|
||||||
基于 uni-app 打造的 Halo 博客跨端客户端
|
基于 uni-app 打造的 Halo 博客跨端客户端
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 相关链接 -->
|
<!-- 相关链接 -->
|
||||||
<uh-section-title class="mb-3 mt-6 text-[30rpx]">
|
<uh-section-title class="mb-3 mt-6">
|
||||||
相关链接
|
相关链接
|
||||||
</uh-section-title>
|
</uh-section-title>
|
||||||
<view class="link-list uh-global-card-glass overflow-hidden rounded-2xl">
|
<view class="link-list uh-global-card-glass overflow-hidden rounded-2xl">
|
||||||
<view
|
<view v-for="(link, index) in links" :key="link.title" class="link-item flex items-center gap-3 px-4 py-4"
|
||||||
v-for="(link, index) in links"
|
|
||||||
:key="link.title"
|
|
||||||
class="link-item flex items-center gap-3 px-4 py-4"
|
|
||||||
:class="index < links.length - 1 ? 'border-b border-black/5' : ''"
|
:class="index < links.length - 1 ? 'border-b border-black/5' : ''"
|
||||||
@click="copyText(link.copy, link.tip)"
|
@click="copyText(link.copy, link.tip)">
|
||||||
>
|
<view
|
||||||
<view class="tile h-[76rpx] w-[76rpx] flex shrink-0 items-center justify-center rounded-xl border border-black/5" :style="{ backgroundColor: `${link.tileColor}1A` }">
|
class="tile h-[76rpx] w-[76rpx] flex shrink-0 items-center justify-center rounded-xl border border-black/5"
|
||||||
|
:style="{ backgroundColor: `${link.tileColor}1A` }">
|
||||||
<text class="text-[30rpx] font-bold" :style="{ color: link.tileColor }">{{ link.tileLetter }}</text>
|
<text class="text-[30rpx] font-bold" :style="{ color: link.tileColor }">{{ link.tileLetter }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="min-w-0 flex flex-1 flex-col">
|
<view class="min-w-0 flex flex-1 flex-col">
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/**
|
|
||||||
* 联系博主页(源自旧项目 pagesA/contact,新建复刻)
|
|
||||||
* 数字名片式设计:Hero 名片卡(渐变光斑透卡) + 品牌色字母瓦片联系方式列表,点击复制
|
|
||||||
*/
|
|
||||||
import { computed, ref, watch, watchEffect } from 'vue'
|
import { computed, ref, watch, watchEffect } from 'vue'
|
||||||
import { useAppConfigStore } from '@/store/appConfig'
|
import { useAppConfigStore } from '@/store/appConfig'
|
||||||
import { DataLoadingStatusEnum, useDataLoadingStatus } from '@/hooks/useDataLoadingStatus'
|
import { DataLoadingStatusEnum, useDataLoadingStatus } from '@/hooks/useDataLoadingStatus'
|
||||||
@@ -94,7 +90,7 @@ watch(socialConfig, () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="app-page box-border min-h-screen w-screen overflow-hidden bg-page px-4 pb-10 pt-6">
|
<view class="box-border min-h-screen w-screen overflow-hidden bg-page px-4 pb-10 pt-2">
|
||||||
<!-- 自定义导航 -->
|
<!-- 自定义导航 -->
|
||||||
<uh-navbar default-title="联系博主" title-color="text-gray-900" />
|
<uh-navbar default-title="联系博主" title-color="text-gray-900" />
|
||||||
|
|
||||||
@@ -102,7 +98,7 @@ watch(socialConfig, () => {
|
|||||||
<view class="hero-wrap relative">
|
<view class="hero-wrap relative">
|
||||||
<view class="absolute h-[220rpx] w-[220rpx] rounded-full bg-[rgba(185,228,36,0.32)] -right-8 -top-8" />
|
<view class="absolute h-[220rpx] w-[220rpx] rounded-full bg-[rgba(185,228,36,0.32)] -right-8 -top-8" />
|
||||||
<view class="absolute top-[150rpx] h-[180rpx] w-[180rpx] rounded-full bg-[rgba(215,249,76,0.45)] -left-10" />
|
<view class="absolute top-[150rpx] h-[180rpx] w-[180rpx] rounded-full bg-[rgba(215,249,76,0.45)] -left-10" />
|
||||||
<view class="hero-card uh-global-card-glass relative flex flex-col items-center rounded-3xl px-6 pb-6 pt-10">
|
<view class="hero-card uh-global-card-glass uh-shadow-xs relative flex flex-col items-center rounded-2xl px-6 pb-6 pt-10">
|
||||||
<view class="avatar relative box-border h-[150rpx] w-[150rpx] overflow-hidden border-5 border-white/90 rounded-full shadow-lg">
|
<view class="avatar relative box-border h-[150rpx] w-[150rpx] overflow-hidden border-5 border-white/90 rounded-full shadow-lg">
|
||||||
<image class="avatar-img h-full w-full" :src="bloggerInfo.avatar" mode="aspectFill" />
|
<image class="avatar-img h-full w-full" :src="bloggerInfo.avatar" mode="aspectFill" />
|
||||||
</view>
|
</view>
|
||||||
@@ -125,26 +121,26 @@ watch(socialConfig, () => {
|
|||||||
empty-text="暂无联系方式" empty-sub-text="" @refresh="handleGetData"
|
empty-text="暂无联系方式" empty-sub-text="" @refresh="handleGetData"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<uh-section-title class="mb-3 mt-6 text-[30rpx]">
|
<uh-section-title class="mb-3 mt-6">
|
||||||
联系方式
|
联系方式
|
||||||
</uh-section-title>
|
</uh-section-title>
|
||||||
<view class="flex flex-col gap-3">
|
<view class="flex flex-col gap-3">
|
||||||
<view
|
<view
|
||||||
v-for="item in result.filter(i => i.value)"
|
v-for="item in result.filter(i => i.value)"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="item uh-global-card-glass flex items-center gap-3 rounded-2xl px-4 py-3"
|
class="box-border uh-global-card-glass uh-shadow-xs flex items-center gap-3 rounded-2xl px-4 py-3"
|
||||||
@click="handleOnClick(item)"
|
@click="handleOnClick(item)"
|
||||||
>
|
>
|
||||||
<view class="tile h-[76rpx] w-[76rpx] flex shrink-0 items-center justify-center rounded-xl" :style="{ backgroundColor: platformMeta[item.key]?.color || '#8a8a7a' }">
|
<view class="tile h-[76rpx] w-[76rpx] flex shrink-0 items-center justify-center rounded-xl" :style="{ backgroundColor: platformMeta[item.key]?.color || '#8a8a7a' }">
|
||||||
<text class="text-[30rpx] text-white font-bold">{{ platformMeta[item.key]?.letter || item.name.slice(0, 1) }}</text>
|
<text class="text-[30rpx] text-white font-bold">{{ platformMeta[item.key]?.letter || item.name.slice(0, 1) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="min-w-0 flex flex-1 flex-col">
|
<view class="min-w-0 flex flex-1 flex-col">
|
||||||
<text class="text-[24rpx] text-gray-400">{{ item.name }}</text>
|
<text class="text-xs text-gray-500">{{ item.name }}</text>
|
||||||
<view class="mt-1 break-all text-[26rpx] text-gray-900 leading-snug">
|
<view class="mt-1 break-all text-[26rpx] text-gray-900 leading-snug">
|
||||||
{{ item.value }}
|
{{ item.value }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<wd-icon name="copy" size="28rpx" color="#c8c2b4" class="shrink-0" />
|
<wd-icon name="copy" size="28rpx" class="shrink-0 text-gray-400" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -40,44 +40,47 @@ function copyText(content: string, tips = '复制成功') {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="box-border min-h-screen bg-page ">
|
<view class="box-border min-h-screen bg-page p-3 pt-2">
|
||||||
<!-- 自定义导航 -->
|
<!-- 自定义导航 -->
|
||||||
<uh-navbar default-title="免责声明" title-color="text-gray-900" />
|
<uh-navbar default-title="免责声明" title-color="text-gray-900" />
|
||||||
|
|
||||||
|
<view class="w-full h-full uh-global-card-glass uh-shadow-xs rounded-xl">
|
||||||
<!-- 通过配置 -->
|
<!-- 通过配置 -->
|
||||||
<view v-if="disclaimersContent" style="min-height: 100%;" v-html="disclaimersContent" />
|
<view v-if="disclaimersContent" v-html="disclaimersContent" />
|
||||||
|
|
||||||
<!-- 静态写法 -->
|
<!-- 静态写法 -->
|
||||||
<view v-else class="box-border p-4 pt-0 text-sm text-gray-900 leading-7">
|
<view v-else class="box-border p-4 pt-0 text-sm text-gray-900 leading-6">
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
1、本应用属于个人非盈利性质的网站,所有转载的文章都以遵循原作者的版权声明注明了文章来源。
|
1、本应用属于个人非盈利性质的网站,所有转载的文章都以遵循原作者的版权声明注明了文章来源。
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
2、如果原文没有版权声明,按照目前互联网开放的原则,本博客将在不通知作者的情况下转载文章。
|
2、如果原文没有版权声明,按照目前互联网开放的原则,本博客将在不通知作者的情况下转载文章。
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
3、如果原文明确注明"禁止转载",本博客将不会转载。
|
3、如果原文明确注明"禁止转载",本博客将不会转载。
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
4、如果本博客转载的文章不符合作者的版权声明或者作者不想让本博客转载您的文章,请邮件告知
|
4、如果本博客转载的文章不符合作者的版权声明或者作者不想让本博客转载您的文章,请邮件告知
|
||||||
<text class="email mx-3 text-primary" @click="copyText(bloggerInfo.email, '电子邮箱已复制到剪贴板!')">{{ bloggerInfo.email }}</text>
|
<text class="email mx-3 text-primary"
|
||||||
|
@click="copyText(bloggerInfo.email, '电子邮箱已复制到剪贴板!')">{{ bloggerInfo.email }}</text>
|
||||||
,博主将会在第一时间删除相关信息!
|
,博主将会在第一时间删除相关信息!
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
5、本博客转载文章仅为留做备份和知识点分享的目的。
|
5、本博客转载文章仅为留做备份和知识点分享的目的。
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
6、本博客将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担相关责任。
|
6、本博客将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担相关责任。
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
7、本博客所发布、转载的文章,其版权均归原作者所有。如其他自媒体、网站或个人从本博客下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本博客注明的"原文来源"或者自行去原文处复制版权声明,并自负版权等法律责任。
|
7、本博客所发布、转载的文章,其版权均归原作者所有。如其他自媒体、网站或个人从本博客下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本博客注明的"原文来源"或者自行去原文处复制版权声明,并自负版权等法律责任。
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
8、本博客的所有原创文章皆可以任意转载,但转载时务必请注明出处。
|
8、本博客的所有原创文章皆可以任意转载,但转载时务必请注明出处。
|
||||||
</view>
|
</view>
|
||||||
<view class="item mt-6">
|
<view class="item mt-4">
|
||||||
9、尊重原创,知识共享!
|
9、尊重原创,知识共享!
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -402,10 +402,10 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 详情弹窗 -->
|
<!-- 详情弹窗 -->
|
||||||
<uh-glass-popup v-model="detail.show" position="center" custom-class="w-[90vw] rounded-xl !border">
|
<uh-glass-popup v-model="detail.show" position="bottom" :z-index="999" custom-class="rounded-xl !border">
|
||||||
<view class="relative w-full flex items-center justify-around box-border px-4 pt-4">
|
<view class="relative w-full flex items-center justify-around box-border px-4 pt-4">
|
||||||
<view class="w-full flex flex-col gap-y-1">
|
<view class="w-full flex flex-col gap-y-1">
|
||||||
<text class="text-md font-bold">小程序详情</text>
|
<text class="text-md font-bold">站点详情</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="absolute right-4 top-4 w-6 h-6 uh-global-card-glass shadow-none border rounded-lg text-center"
|
class="absolute right-4 top-4 w-6 h-6 uh-global-card-glass shadow-none border rounded-lg text-center"
|
||||||
@@ -416,26 +416,27 @@
|
|||||||
<scroll-view v-if="detail.data" :scroll-y="true" :show-scrollbar="false"
|
<scroll-view v-if="detail.data" :scroll-y="true" :show-scrollbar="false"
|
||||||
class="box-border p-4 max-h-[60vh]">
|
class="box-border p-4 max-h-[60vh]">
|
||||||
<view class="flex">
|
<view class="flex">
|
||||||
<image class="poup-logo h-[140rpx] w-[140rpx] shrink-0 rounded-full"
|
<image class="h-20 w-20 shrink-0 rounded-2xl uh-global-card-glass"
|
||||||
:src="checkImageUrl(detail.data.spec.logo)" mode="aspectFill" />
|
:src="checkImageUrl(detail.data.spec.logo)" mode="aspectFill" />
|
||||||
<view class="ml-4 flex flex-1 flex-col gap-y-1 justify-center">
|
<view class="ml-4 flex flex-1 flex-col gap-y-1 justify-center">
|
||||||
<view class="poup-name text-[34rpx] text-gray-900 font-bold">
|
<view class="text-lg text-gray-900 font-bold">
|
||||||
{{ detail.data.spec.displayName }}
|
{{ detail.data.spec.displayName }}
|
||||||
</view>
|
</view>
|
||||||
<view class="text-xs text-gray-500">
|
<view class="flex items-center gap-x-2">
|
||||||
{{ detail.data.spec.groupName }}
|
<text class="uh-global-card-glass border uh-shadow-xs text-xs text-gray-500 rounded-lg bg-secondary px-2 py-0.5 text-gray-900">{{ detail.data.spec.groupName }}</text>
|
||||||
|
<text class="uh-global-card-glass border uh-shadow-xs text-xs text-gray-500 rounded-lg bg-secondary px-2 py-0.5 text-gray-900">
|
||||||
|
复制地址
|
||||||
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view @click="handleCopyLink(detail.data)">
|
<view @click="handleCopyLink(detail.data)">
|
||||||
<text
|
<text
|
||||||
class="rounded-lg bg-secondary px-2 py-1 text-xs text-gray-900">{{ detail.data.spec.url }}</text>
|
class="text-xs text-gray-900">{{ detail.data.spec.url }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="poup-desc mt-4 text-[28rpx] text-gray-600 leading-[1.6]">
|
<view class="poup-desc mt-4 text-[28rpx] text-gray-600 leading-[1.6]">
|
||||||
{{ detail.data.spec.description || '这个博主很懒,没写简介~' }}
|
{{ detail.data.spec.description || '这个博主很懒,没写简介~' }}
|
||||||
</view>
|
</view>
|
||||||
<image class="poup-img mt-4 h-[320rpx] w-full rounded-xl"
|
|
||||||
:src="calcSiteThumbnail(detail.data.spec.url)" mode="aspectFill" />
|
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</uh-glass-popup>
|
</uh-glass-popup>
|
||||||
|
|
||||||
@@ -500,7 +501,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 小程序详情弹窗 -->
|
<!-- 小程序详情弹窗 -->
|
||||||
<uh-glass-popup v-model="miniDetail.show" position="center" custom-class="w-[90vw] rounded-xl !border">
|
<uh-glass-popup v-model="miniDetail.show" :z-index="999" position="bottom" custom-class="rounded-xl !border">
|
||||||
<view class="relative w-full flex items-center justify-around box-border px-4 pt-4">
|
<view class="relative w-full flex items-center justify-around box-border px-4 pt-4">
|
||||||
<view class="w-full flex flex-col gap-y-1">
|
<view class="w-full flex flex-col gap-y-1">
|
||||||
<text class="text-md font-bold">小程序详情</text>
|
<text class="text-md font-bold">小程序详情</text>
|
||||||
|
|||||||
@@ -184,18 +184,18 @@
|
|||||||
|
|
||||||
<wd-sticky>
|
<wd-sticky>
|
||||||
<scroll-view scroll-x class="w-full whitespace-nowrap" :show-scrollbar="false">
|
<scroll-view scroll-x class="w-full whitespace-nowrap" :show-scrollbar="false">
|
||||||
<view class="flex gap-2 px-3 pb-1 pt-2">
|
<view class="box-border flex gap-2 px-3 pb-1 pt-2">
|
||||||
<view class="uh-global-card-glass uh-shadow-xs inline-block border rounded-2xl px-4 py-1.5 text-sm"
|
<view class="flex-1 uh-global-card-glass uh-shadow-xs inline-flex border rounded-2xl px-4 py-1.5 text-sm"
|
||||||
:class="activeType === '' ? 'bg-primary font-bold' : 'text-gray-500'" @click="activeType = ''">
|
:class="activeType === '' ? 'bg-primary font-bold' : 'text-gray-500'" @click="activeType = ''">
|
||||||
全部
|
全部
|
||||||
</view>
|
</view>
|
||||||
<view v-for="(type) in typeOptions" :key="type.typeName || '__none__'"
|
<view v-for="(type) in typeOptions" :key="type.typeDisplayName"
|
||||||
class="uh-global-card-glass uh-shadow-xs inline-flex items-center gap-1 border rounded-2xl px-4 py-1.5 text-sm"
|
class="flex-1 box-border uh-global-card-glass uh-shadow-xs inline-flex items-center gap-1 border rounded-2xl px-4 py-1.5 text-sm"
|
||||||
:class="activeType === type.typeName ? 'bg-primary font-bold' : 'text-gray-500'"
|
:class="activeType === type.typeName ? 'bg-primary font-bold' : 'text-gray-500'"
|
||||||
@click="activeType = activeType === type.typeName ? '' : type.typeName">
|
@click="activeType = activeType === type.typeName ? '' : type.typeName">
|
||||||
<view v-if="type.typeColor" class="shrink-0 h-2 w-2 rounded-full"
|
<view v-if="type.typeColor" class="shrink-0 h-2 w-2 rounded-full"
|
||||||
:style="{ backgroundColor: type.typeColor }" />
|
:style="{ backgroundColor: type.typeColor }" />
|
||||||
<text class="shrink-0">{{ type.typeDisplayName }}</text>
|
<view class="shrink-0">{{ type.typeDisplayName }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/**
|
|
||||||
* 投票详情页(源自旧项目 pagesA/vote-detail,新建复刻)
|
|
||||||
* 支持 single/multiple/pk 三种投票类型,已投票展示结果
|
|
||||||
*/
|
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { onLoad, onPullDownRefresh, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
import { onLoad, onPullDownRefresh, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||||
import { getVoteDetail, submitVote } from '@/api/uni-halo'
|
import { getVoteDetail, submitVote } from '@/api/uni-halo'
|
||||||
@@ -262,34 +258,27 @@ onShareTimeline(() => ({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="app-page box-border min-h-screen w-screen flex flex-col bg-page pb-[160rpx]">
|
<view class="box-border min-h-screen w-screen flex flex-col bg-page pb-safe">
|
||||||
<!-- 自定义导航 -->
|
<!-- 自定义导航 -->
|
||||||
<uh-navbar :default-title="pageTitle" title-color="text-gray-900" />
|
<uh-navbar :default-title="pageTitle" title-color="text-gray-900" />
|
||||||
|
|
||||||
<!-- 加载/错误/空占位(状态机) -->
|
<!-- 加载/错误/空占位(状态机) -->
|
||||||
<view v-if="loadingStatus !== 'success'">
|
<uh-data-loading v-if="loadingStatus !== DataLoadingStatusEnum.Success" :loading-status="loadingStatus"
|
||||||
<uh-data-loading
|
empty-text="未查询到数据" @refresh="handleGetData" />
|
||||||
:loading-status="loadingStatus"
|
|
||||||
empty-text="未查询到数据"
|
|
||||||
@refresh="handleGetData"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<block v-else>
|
<view v-else class="box-border px-3 pt-2 pb-16 flex flex-col gap-y-3">
|
||||||
<template v-if="vote">
|
|
||||||
<!-- 投票信息 -->
|
<!-- 投票信息 -->
|
||||||
<view class="uh-global-card-glass box-border mx-6 mb-6 flex flex-col rounded-2xl p-6">
|
<view class="uh-global-card-glass box-border flex flex-col gap-y-3 rounded-2xl p-3">
|
||||||
<view class="sub-title relative box-border pl-6 text-[30rpx]">
|
<uh-section-title> 投票信息 </uh-section-title>
|
||||||
投票信息
|
<view class="flex flex-col gap-3 rounded-xl bg-gray-100 p-4 text-sm text-gray-600">
|
||||||
</view>
|
|
||||||
<view class="vote-card-body mt-3 flex flex-col gap-3 rounded-xl bg-page p-6 text-[28rpx] text-[#3f3f3f]">
|
|
||||||
<view class="info-row">
|
<view class="info-row">
|
||||||
<text>投票类型:</text>
|
<text>投票类型:</text>
|
||||||
<text class="tag">{{ vote.spec?._uh_type }}</text>
|
<text class="tag">{{ vote.spec?._uh_type }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-row">
|
<view class="info-row">
|
||||||
<text>投票状态:</text>
|
<text>投票状态:</text>
|
||||||
<text class="tag" :style="{ color: vote.spec?._uh_state?.color }">{{ vote.spec?._uh_state?.state }}</text>
|
<text class="tag"
|
||||||
|
:style="{ color: vote.spec?._uh_state?.color }">{{ vote.spec?._uh_state?.state }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-row">
|
<view class="info-row">
|
||||||
<text>投票方式:</text>
|
<text>投票方式:</text>
|
||||||
@@ -308,45 +297,43 @@ onShareTimeline(() => ({
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 投票内容 -->
|
<!-- 投票内容 -->
|
||||||
<view class="uh-global-card-glass box-border mx-6 mb-6 flex flex-col rounded-2xl p-6">
|
<view class="uh-global-card-glass box-border flex flex-col rounded-2xl p-3 gap-3">
|
||||||
<view class="sub-title relative box-border pl-6 text-[30rpx]">
|
<uh-section-title> 投票内容 </uh-section-title>
|
||||||
投票内容
|
<view class="box-border flex flex-col gap-y-2 p-4 rounded-xl bg-gray-100">
|
||||||
</view>
|
<view class="text-sm text-gray-900 font-bold">
|
||||||
<view class="sub-content mb-3 pt-3 text-[30rpx] text-[#2b2f33] font-bold">
|
|
||||||
{{ vote.spec?.title }}
|
{{ vote.spec?.title }}
|
||||||
</view>
|
</view>
|
||||||
<view v-if="vote.spec?.remark" class="sub-remark mb-9 pt-3 text-[28rpx] text-[#3f3f3f]">
|
<view v-if="vote.spec?.remark" class="text-xs text-gray-500">
|
||||||
{{ vote.spec.remark }}
|
{{ vote.spec.remark }}
|
||||||
</view>
|
</view>
|
||||||
<view class="sub-title relative box-border pl-6 text-[30rpx]">
|
|
||||||
投票选项
|
|
||||||
<text v-if="vote.spec?.type === 'multiple'" class="sub-title-count text-[24rpx] font-normal">(最多选择 {{ vote.spec?.maxVotes }} 项)</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="options mt-6 flex flex-col gap-4">
|
<view class="w-full flex flex-col gap-y-2">
|
||||||
|
<view class="relative box-border text-sm flex items-center gap-x-2">
|
||||||
|
投票选项
|
||||||
|
<text v-if="vote.spec?.type === 'multiple'"
|
||||||
|
class="text-xs font-normal">(最多选择
|
||||||
|
{{ vote.spec?.maxVotes }} 项)
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="options flex flex-col gap-3">
|
||||||
<!-- PK 对抗条(与旧项目 pk-container 一致) -->
|
<!-- PK 对抗条(与旧项目 pk-container 一致) -->
|
||||||
<view v-if="vote.spec?.type === 'pk'" class="pk-container box-border flex w-full">
|
<view v-if="vote.spec?.type === 'pk'" class="pk-container box-border flex w-full">
|
||||||
<view
|
<view v-for="(option, optionIndex) in vote.spec?.options" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in vote.spec?.options"
|
class="radio-item flex-grow" :class="optionIndex === 0 ? 'radio-left' : 'radio-right'"
|
||||||
:key="optionIndex"
|
:style="{ width: `${option._uh_percent}%` }">
|
||||||
class="radio-item flex-grow"
|
<view class="option-item box-border w-full rounded-xl py-3 px-3"
|
||||||
:class="optionIndex === 0 ? 'radio-left' : 'radio-right'"
|
:class="optionIndex === 0 ? 'option-item-left' : 'option-item-right'">
|
||||||
:style="{ width: `${option._uh_percent}%` }"
|
|
||||||
>
|
|
||||||
<view class="option-item box-border w-full rounded-xl p-6" :class="optionIndex === 0 ? 'option-item-left' : 'option-item-right'">
|
|
||||||
{{ option._uh_percent }}%
|
{{ option._uh_percent }}%
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<template v-if="isVoted || isEnded">
|
<template v-if="isVoted || isEnded">
|
||||||
<view
|
<view v-for="(option, optionIndex) in vote.spec?.options" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in vote.spec?.options"
|
class="is-voted-item relative box-border overflow-hidden rounded-xl text-xs"
|
||||||
:key="optionIndex"
|
:class="option.checked ? 'bg-primary text-gray-900 font-bold' : 'bg-gray-100'"
|
||||||
class="is-voted-item relative box-border min-h-[72rpx] overflow-hidden rounded-xl text-[24rpx]"
|
:style="{ '--percent': `${option._uh_percent}%` }">
|
||||||
:class="option.checked ? 'bg-primary/40 text-[#4d7c0f] font-bold' : 'bg-[#e5e5e5]/75'"
|
<view class="is-voted-item-content relative z-2 box-border px-4 py-3">
|
||||||
:style="{ '--percent': `${option._uh_percent}%` }"
|
|
||||||
>
|
|
||||||
<view class="is-voted-item-content relative z-2 box-border min-h-[72rpx] px-6 py-3">
|
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<view class="flex-1 text-left">
|
<view class="flex-1 text-left">
|
||||||
{{ vote.spec?.type === 'pk' ? `选项${optionIndex + 1}:` : '' }}{{ option.title }}
|
{{ vote.spec?.type === 'pk' ? `选项${optionIndex + 1}:` : '' }}{{ option.title }}
|
||||||
@@ -359,52 +346,54 @@ onShareTimeline(() => ({
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view
|
<view v-for="(option, optionIndex) in vote.spec?.options" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in vote.spec?.options"
|
class="vote-select-option box-border rounded-xl bg-gray-100 px-6 py-5 text-xs"
|
||||||
:key="optionIndex"
|
|
||||||
class="vote-select-option box-border rounded-xl bg-[#f3f4f6] px-6 py-5 text-[24rpx]"
|
|
||||||
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
||||||
@click="vote.spec?.type === 'multiple' ? handleSelectCheckboxOption(option) : handleSelectSingleOption(option)"
|
@click="vote.spec?.type === 'multiple' ? handleSelectCheckboxOption(option) : handleSelectSingleOption(option)">
|
||||||
>
|
|
||||||
{{ vote.spec?.type === 'pk' ? `选项${optionIndex + 1}:` : '' }}{{ option.title }}
|
{{ vote.spec?.type === 'pk' ? `选项${optionIndex + 1}:` : '' }}{{ option.title }}
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 投票统计 -->
|
<!-- 投票统计 -->
|
||||||
<view class="uh-global-card-glass box-border mx-6 mb-6 flex flex-col rounded-2xl p-6">
|
<view class="uh-global-card-glass box-border flex flex-col rounded-2xl p-3">
|
||||||
<view class="sub-title relative box-border pl-6 text-[30rpx]">
|
<uh-section-title> 投票统计 </uh-section-title>
|
||||||
投票统计
|
<view class="stat-text mt-3 text-xs text-gray-600">
|
||||||
</view>
|
|
||||||
<view class="stat-text mt-3 text-[26rpx] text-[#606266]">
|
|
||||||
{{ vote.stats?.voteCount || 0 }} 人已参与
|
{{ vote.stats?.voteCount || 0 }} 人已参与
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
<!-- 提交按钮 -->
|
||||||
<view class="vote-submit fixed bottom-0 left-0 z-99 box-border w-screen border-t border-black/5 bg-white/90 px-9 py-6 shadow-sm backdrop-blur" :style="{ paddingBottom: `${safeAreaBottom}rpx` }">
|
<view class="fixed bottom-0 left-0 z-99 box-border w-screen pb-safe px-3">
|
||||||
<wd-button v-if="isVoted" disabled block>
|
<view
|
||||||
|
class="uh-global-card-glass border rounded-xl w-full flex items-center justify-center gap-x-2 mb-2">
|
||||||
|
<uh-button v-if="isVoted" custom-class="flex-1 py-2 !rounded-xl">
|
||||||
您已参与投票
|
您已参与投票
|
||||||
</wd-button>
|
</uh-button>
|
||||||
<wd-button v-else-if="vote.spec?._uh_state?.state === '未开始'" plain block type="warning" @click="handleSubmitTip('投票未开始')">
|
<uh-button v-else-if="vote.spec?._uh_state?.state === '未开始'" custom-class="flex-1 py-2 !rounded-xl"
|
||||||
|
@click="handleSubmitTip('投票未开始')">
|
||||||
投票未开始
|
投票未开始
|
||||||
</wd-button>
|
</uh-button>
|
||||||
<wd-button v-else-if="vote.spec?._uh_state?.state === '已结束'" plain block type="danger" @click="handleSubmitTip('投票已结束')">
|
<uh-button v-else-if="vote.spec?._uh_state?.state === '已结束'" custom-class="flex-1 py-2 !rounded-xl"
|
||||||
|
@click="handleSubmitTip('投票已结束')">
|
||||||
投票已结束
|
投票已结束
|
||||||
</wd-button>
|
</uh-button>
|
||||||
<wd-button v-else-if="!vote.spec?.canAnonymously" plain block type="danger" @click="handleSubmit()">
|
<uh-button v-else-if="!vote.spec?.canAnonymously" custom-class="flex-1 py-2 !rounded-xl"
|
||||||
|
@click="handleSubmit()">
|
||||||
不支持匿名投票
|
不支持匿名投票
|
||||||
</wd-button>
|
</uh-button>
|
||||||
<wd-button v-else-if="submitForm.voteData.length === 0" plain block @click="handleSubmitTip('请选择选项')">
|
<uh-button v-else-if="submitForm.voteData.length === 0" custom-class="flex-1 py-2 !rounded-xl"
|
||||||
|
@click="handleSubmitTip('请选择选项')">
|
||||||
提交投票(请选择选项)
|
提交投票(请选择选项)
|
||||||
</wd-button>
|
</uh-button>
|
||||||
<wd-button v-else block type="primary" :loading="submitLoading" :disabled="submitLoading" @click="handleSubmit()">
|
<uh-button v-else custom-class="flex-1 py-2 !rounded-xl" @click="handleSubmit()">
|
||||||
提交投票
|
提交投票
|
||||||
</wd-button>
|
</uh-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/**
|
|
||||||
* 投票列表页(源自旧项目 pagesA/votes,新建复刻)
|
|
||||||
* 展示投票列表,支持搜索 + 类型/状态/排序/是否已投筛选,每个投票项用 uh-vote-card 渲染
|
|
||||||
*/
|
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
|
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
|
||||||
import { getVoteList } from '@/api/uni-halo'
|
import { getVoteList } from '@/api/uni-halo'
|
||||||
@@ -288,95 +284,64 @@ onReachBottom(() => {
|
|||||||
<!-- 自定义导航 -->
|
<!-- 自定义导航 -->
|
||||||
<uh-navbar default-title="投票中心" title-color="text-gray-900" />
|
<uh-navbar default-title="投票中心" title-color="text-gray-900" />
|
||||||
|
|
||||||
<uh-plugin-unavailable
|
<uh-plugin-unavailable v-if="!uniHaloPluginAvailable" :plugin-id="pluginId" :error-text="tips"
|
||||||
v-if="!uniHaloPluginAvailable"
|
:checking="checking" @on-refresh="handlePluginRefresh" />
|
||||||
:plugin-id="pluginId"
|
|
||||||
:error-text="tips"
|
|
||||||
:checking="checking"
|
|
||||||
@on-refresh="handlePluginRefresh"
|
|
||||||
/>
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- 顶部搜索框 -->
|
<!-- 顶部搜索框 -->
|
||||||
<view class="box-border w-screen px-3 pt-2">
|
<view class="box-border w-screen px-3 pt-2">
|
||||||
<view class="uh-global-card-glass flex h-9 items-center gap-3 rounded-full px-5">
|
<view class="uh-global-card-glass flex h-9 items-center gap-3 rounded-full px-5">
|
||||||
<wd-icon name="search" size="16px" />
|
<wd-icon name="search" size="16px" />
|
||||||
<input
|
<input v-model="queryParams.keyword as string" class="flex-1 text-[26rpx] text-gray-900"
|
||||||
v-model="queryParams.keyword as string"
|
placeholder="搜索投票..." placeholder-class="text-gray-400" confirm-type="search"
|
||||||
class="flex-1 text-[26rpx] text-gray-900"
|
@input="handleOnInput" @confirm="handleOnSearch">
|
||||||
placeholder="搜索投票..."
|
<view v-if="queryParams.keyword" class="flex items-center"
|
||||||
placeholder-class="text-gray-400"
|
@click="queryParams.keyword = ''; handleOnSearch()">
|
||||||
confirm-type="search"
|
|
||||||
@input="handleOnInput"
|
|
||||||
@confirm="handleOnSearch"
|
|
||||||
>
|
|
||||||
<view v-if="queryParams.keyword" class="flex items-center" @click="queryParams.keyword = ''; handleOnSearch()">
|
|
||||||
<wd-icon name="close" size="14px" />
|
<wd-icon name="close" size="14px" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 筛选栏 -->
|
<!-- 筛选栏 -->
|
||||||
<view class="flex items-center justify-between py-2">
|
<view class="box-border flex items-center justify-between mt-1 py-2 gap-x-2">
|
||||||
<view
|
<view v-for="f in filterConfig" :key="f.key"
|
||||||
v-for="f in filterConfig"
|
class="uh-global-card-glass border rounded-full box-border flex flex-1 items-center justify-center gap-1 px-2 py-1 text-gray-500"
|
||||||
:key="f.key"
|
:class="[filterValues[f.key]?'bg-secondary text-gray-900 font-bold':'bg-white/80 text-gray-600']" @click="handleOpenFilter(f)">
|
||||||
class="flex flex-1 items-center justify-center gap-1"
|
<text class="text-xs truncate" >
|
||||||
@click="handleOpenFilter(f)"
|
|
||||||
>
|
|
||||||
<text class="text-[24rpx]" :class="filterValues[f.key] ? 'text-primary font-bold' : 'text-gray-600'">
|
|
||||||
{{ filterLabels[f.key] }}
|
{{ filterLabels[f.key] }}
|
||||||
</text>
|
</text>
|
||||||
<wd-icon name="arrow-down" size="10px" color="#9ca3af" />
|
<wd-icon name="arrow-down" size="10px" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载/错误/空占位(状态机) -->
|
<!-- 加载/错误/空占位(状态机) -->
|
||||||
<view v-if="loadingStatus !== 'success'">
|
<uh-data-loading v-if="loadingStatus !== DataLoadingStatusEnum.Success" :loading-status="loadingStatus" empty-text="博主还未发布投票~" min-height="65vh" @refresh="handleGetData" />
|
||||||
<uh-data-loading
|
|
||||||
:loading-status="loadingStatus"
|
|
||||||
empty-text="博主还未发布投票~"
|
|
||||||
@refresh="handleGetData"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-else class="content flex flex-col gap-4 p-3">
|
<view v-else class="box-border flex flex-col gap-4 p-3">
|
||||||
<block v-if="dataList.length !== 0">
|
<block v-if="dataList.length !== 0">
|
||||||
<uh-vote-card
|
<uh-vote-card v-for="vote in dataList" :key="vote.metadata?.name" :vote="vote"
|
||||||
v-for="vote in dataList"
|
@click="handleOnVoteClick(vote)" />
|
||||||
:key="vote.metadata?.name"
|
<view class="box-border py-5 text-center text-xs text-gray-400">
|
||||||
:vote="vote"
|
|
||||||
@click="handleOnVoteClick(vote)"
|
|
||||||
/>
|
|
||||||
<view class="load-text py-5 text-center text-[24rpx] text-gray-400">
|
|
||||||
{{ loadMoreText }}
|
{{ loadMoreText }}
|
||||||
</view>
|
</view>
|
||||||
<view
|
|
||||||
class="to-top-btn uh-global-card-glass fixed bottom-[100rpx] right-6 z-6 flex h-[72rpx] w-[72rpx] items-center justify-center rounded-full"
|
|
||||||
@click="handleToTopPage()"
|
|
||||||
>
|
|
||||||
<wd-icon name="arrow-up" size="20px" color="#6b7280" />
|
|
||||||
</view>
|
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 筛选弹层 -->
|
<!-- 筛选弹层 -->
|
||||||
<wd-popup v-model="filterPopup.show" position="bottom" custom-style="border-radius: 24rpx 24rpx 0 0;">
|
<uh-glass-popup v-model="filterPopup.show" :z-index="99" position="bottom" custom-class="rounded-2xl">
|
||||||
<view v-if="filterPopup.item" class="box-border p-6 pb-10">
|
<view v-if="filterPopup.item" class="box-border p-4">
|
||||||
<view class="mb-4 text-center text-[30rpx] font-bold text-gray-900">
|
<view class="mb-4 text-center text-md font-bold text-gray-900">
|
||||||
{{ filterPopup.item.label }}
|
{{ filterPopup.item.label }}
|
||||||
</view>
|
</view>
|
||||||
<view class="flex flex-col gap-2">
|
<view class="flex flex-col gap-2">
|
||||||
<view
|
<view v-for="opt in filterPopup.item.options" :key="opt.label"
|
||||||
v-for="opt in filterPopup.item.options"
|
class="uh-global-card-glass border box-border rounded-xl px-5 py-2 text-center text-sm"
|
||||||
:key="opt.label"
|
:class="filterValues[filterPopup.item.key] === opt.value ? 'bg-primary text-gray-900 font-bold' : 'text-gray-700'"
|
||||||
class="box-border rounded-xl px-5 py-3 text-center text-[28rpx]"
|
@click="handleSelectFilter(opt)">
|
||||||
:class="filterValues[filterPopup.item.key] === opt.value ? 'bg-primary/15 text-primary font-bold' : 'bg-[#f6f3ee] text-gray-700'"
|
|
||||||
@click="handleSelectFilter(opt)"
|
|
||||||
>
|
|
||||||
{{ opt.label }}
|
{{ opt.label }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</wd-popup>
|
</uh-glass-popup>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
navigationBarTitleText: '分类',
|
navigationBarTitleText: '分类',
|
||||||
|
navigationStyle: 'custom',
|
||||||
enablePullDownRefresh: true,
|
enablePullDownRefresh: true,
|
||||||
backgroundColor: '#f6f3ee',
|
backgroundColor: '#f6f3ee',
|
||||||
},
|
},
|
||||||
@@ -151,11 +152,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="box-border min-h-screen w-screen flex flex-col bg-page p-3">
|
<view class="box-border min-h-screen w-screen flex flex-col bg-page">
|
||||||
|
<uh-navbar :use-back="false" default-title="分类" title-color="text-gray-900"></uh-navbar>
|
||||||
|
|
||||||
<uh-data-loading v-if="loadingStatus !== DataLoadingStatusEnum.Success" :loading-status="loadingStatus" />
|
<uh-data-loading v-if="loadingStatus !== DataLoadingStatusEnum.Success" :loading-status="loadingStatus" />
|
||||||
|
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="grid grid-cols-2 gap-2.5">
|
<view class="box-border grid grid-cols-2 gap-2.5 p-3 pt-0">
|
||||||
<view v-for="(item, index) in dataList" :key="index"
|
<view v-for="(item, index) in dataList" :key="index"
|
||||||
class="uh-global-card-glass relative box-border w-full overflow-hidden rounded-xl"
|
class="uh-global-card-glass relative box-border w-full overflow-hidden rounded-xl"
|
||||||
@click="handleToCategory(item)">
|
@click="handleToCategory(item)">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
navigationBarTitleText: '图库',
|
navigationBarTitleText: '图库',
|
||||||
|
navigationStyle: 'custom',
|
||||||
enablePullDownRefresh: true,
|
enablePullDownRefresh: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -191,11 +192,13 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="min-h-screen w-screen flex flex-col bg-page pb-6">
|
<view class="min-h-screen w-screen flex flex-col bg-page pb-6">
|
||||||
|
<uh-navbar :use-back="false" default-title="我的图库" title-color="text-gray-900"></uh-navbar>
|
||||||
|
|
||||||
<uh-plugin-unavailable v-if="!uniHaloPluginAvailable" :plugin-id="pluginId" :error-text="tips"
|
<uh-plugin-unavailable v-if="!uniHaloPluginAvailable" :plugin-id="pluginId" :error-text="tips"
|
||||||
:checking="checking" @on-refresh="checkPluginAvailable" />
|
:checking="checking" @on-refresh="checkPluginAvailable" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<wd-sticky v-if="category.list.length!==0">
|
<wd-sticky v-if="category.list.length!==0" class="w-full">
|
||||||
<scroll-view :scroll-x="true" class="w-full whitespace-nowrap pt-3">
|
<scroll-view :scroll-x="true" class="w-full whitespace-nowrap pt-2">
|
||||||
<view v-for="(cate, index) in category.list" :key="cate.spec.displayName"
|
<view v-for="(cate, index) in category.list" :key="cate.spec.displayName"
|
||||||
class="uh-global-card-glass uh-shadow-xs mb-1 ml-3 inline-block border rounded-2xl px-4 py-1 text-sm"
|
class="uh-global-card-glass uh-shadow-xs mb-1 ml-3 inline-block border rounded-2xl px-4 py-1 text-sm"
|
||||||
:class="{ 'bg-primary text-gray-900 font-bold': index === category.activeIndex }"
|
:class="{ 'bg-primary text-gray-900 font-bold': index === category.activeIndex }"
|
||||||
|
|||||||
@@ -210,17 +210,14 @@
|
|||||||
min-height="36vh" @refresh="handleQuery" />
|
min-height="36vh" @refresh="handleQuery" />
|
||||||
|
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="flex flex-col gap-y-3 p-3 pt-0" :class="globalAppSettings.layout.home">
|
<view class="box-border flex flex-col gap-y-3 p-3 pt-0" :class="globalAppSettings.layout.home">
|
||||||
<uh-article-card v-for="(article, index) in articleList" :key="index"
|
<uh-article-card v-for="(article, index) in articleList" :key="index"
|
||||||
from="home" :article="article" :audit-mode="calcAuditModeEnabled"
|
from="home" :article="article" :audit-mode="calcAuditModeEnabled"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="load-text mt-3 pb-5 text-center text-xs text-gray-400">
|
<view class="box-border mt-3 pb-5 text-center text-xs text-gray-400">
|
||||||
{{ loadMoreText }}
|
{{ loadMoreText }}
|
||||||
</view>
|
</view>
|
||||||
<view v-if="articleList.length > 10" class="to-top-btn" @click="handleToTopPage()">
|
|
||||||
<wd-icon name="arrow-up" size="20px" color="#03a9f4" />
|
|
||||||
</view>
|
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
<uh-notify-dialog />
|
<uh-notify-dialog />
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
navigationBarTitleText: '瞬间',
|
navigationBarTitleText: '瞬间',
|
||||||
|
navigationStyle: 'custom',
|
||||||
enablePullDownRefresh: true,
|
enablePullDownRefresh: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -287,30 +288,27 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="box-border min-h-screen w-screen flex flex-col bg-page py-3">
|
<view class="box-border min-h-screen w-screen flex flex-col bg-page ">
|
||||||
|
<uh-navbar :use-back="false" default-title="我的日常" title-color="text-gray-900"></uh-navbar>
|
||||||
|
|
||||||
<uh-plugin-unavailable v-if="!uniHaloPluginAvailable" :plugin-id="pluginId" :error-text="tips"
|
<uh-plugin-unavailable v-if="!uniHaloPluginAvailable" :plugin-id="pluginId" :error-text="tips"
|
||||||
:checking="checking" @on-refresh="handlePluginRefresh" />
|
:checking="checking" @on-refresh="handlePluginRefresh" />
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- 加载失败(可重试) -->
|
<!-- 加载失败(可重试) -->
|
||||||
<uh-data-loading v-if="loadingStatus !== DataLoadingStatusEnum.Success" :loading-status="loadingStatus"
|
<uh-data-loading v-if="loadingStatus !== DataLoadingStatusEnum.Success" :loading-status="loadingStatus"
|
||||||
min-height="60vh" @refresh="handleGetData" />
|
min-height="60vh" @refresh="handleGetData" />
|
||||||
|
|
||||||
<view v-else class="flex flex-col gap-3 px-3">
|
<view v-else class="box-border flex flex-col gap-3 p-3 pt-0">
|
||||||
<view v-if="dataList.length === 0"
|
|
||||||
class="min-h-[70vh] w-full flex items-center justify-center content-empty">
|
|
||||||
<wd-empty :description="t('common.empty')" />
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<block v-else>
|
|
||||||
<!-- 瞬间卡片-->
|
<!-- 瞬间卡片-->
|
||||||
<view v-for="moment in dataList" :key="moment.metadata.name"
|
<view v-for="moment in dataList" :key="moment.metadata.name"
|
||||||
class="moment-card uh-shadow-xs overflow-hidden rounded-[24rpx] bg-white">
|
class="uh-global-card-glass uh-shadow-xs overflow-hidden rounded-xl">
|
||||||
<!-- 作者 -->
|
<!-- 作者 -->
|
||||||
<view class="box-border flex items-center px-4 pt-4">
|
<view class="box-border flex items-center px-4 pt-4">
|
||||||
<view class="flex-1 flex items-center">
|
<view class="flex-1 flex items-center">
|
||||||
<image class="avatar h-[72rpx] w-[72rpx] shrink-0 rounded-full"
|
<image class="avatar h-[72rpx] w-[72rpx] shrink-0 rounded-full"
|
||||||
:src="checkAvatarUrl(moment.owner?.avatar || bloggerInfo.avatar)"
|
:src="checkAvatarUrl(moment.owner?.avatar || bloggerInfo.avatar)" mode="aspectFill" />
|
||||||
mode="aspectFill" />
|
|
||||||
<view class="ml-3 flex flex-col">
|
<view class="ml-3 flex flex-col">
|
||||||
<view class="text-sm text-gray-900 font-bold">
|
<view class="text-sm text-gray-900 font-bold">
|
||||||
{{ moment.owner?.displayName || bloggerInfo.nickname }}
|
{{ moment.owner?.displayName || bloggerInfo.nickname }}
|
||||||
@@ -327,7 +325,7 @@
|
|||||||
|
|
||||||
<!-- 正文-->
|
<!-- 正文-->
|
||||||
<view class="box-border px-4 pt-3">
|
<view class="box-border px-4 pt-3">
|
||||||
<view class="relative box-border bg-gray-100 p-2 rounded-lg">
|
<view class="relative box-border bg-page p-3 rounded-lg">
|
||||||
<mp-html lazy-load :domain="markdownConfig.domain ?? ''"
|
<mp-html lazy-load :domain="markdownConfig.domain ?? ''"
|
||||||
:loading-img="markdownConfig.loadingGif" scroll-table selectable
|
:loading-img="markdownConfig.loadingGif" scroll-table selectable
|
||||||
:tag-style="markdownConfig.tagStyle" :container-style="markdownConfig.containStyle"
|
:tag-style="markdownConfig.tagStyle" :container-style="markdownConfig.containStyle"
|
||||||
@@ -339,7 +337,7 @@
|
|||||||
|
|
||||||
<!-- 图片 -->
|
<!-- 图片 -->
|
||||||
<view v-if="moment.images && moment.images.length !== 0"
|
<view v-if="moment.images && moment.images.length !== 0"
|
||||||
class="images flex flex-wrap items-start px-4 pt-3">
|
class="box-border flex flex-wrap items-start px-3 pt-3">
|
||||||
<view v-for="(image, mediumIndex) in moment.images" :key="mediumIndex"
|
<view v-for="(image, mediumIndex) in moment.images" :key="mediumIndex"
|
||||||
class="image-item box-border p-1"
|
class="image-item box-border p-1"
|
||||||
:class="moment.images && moment.images.length === 1 ? 'h-[350rpx] w-full' : (moment.images && moment.images.length === 2 ? 'h-[250rpx] w-1/2' : 'h-[200rpx] w-1/3')">
|
:class="moment.images && moment.images.length === 1 ? 'h-[350rpx] w-full' : (moment.images && moment.images.length === 2 ? 'h-[250rpx] w-1/2' : 'h-[200rpx] w-1/3')">
|
||||||
@@ -350,15 +348,14 @@
|
|||||||
|
|
||||||
<!-- 音频 -->
|
<!-- 音频 -->
|
||||||
<view v-if="moment.audios && moment.audios.length !== 0"
|
<view v-if="moment.audios && moment.audios.length !== 0"
|
||||||
class="audio-list flex flex-col gap-3 px-4 pt-3">
|
class="box-border flex flex-col gap-3 px-4 pt-3">
|
||||||
<uh-audio-player v-for="audio in moment.audios" :key="audio.url" :src="audio.url"
|
<uh-audio-player v-for="audio in moment.audios" :key="audio.url" :src="audio.url"
|
||||||
:poster="bloggerInfo.avatar" :name="`来自${siteName}的声音`"
|
:poster="bloggerInfo.avatar" :name="`来自${siteName}的声音`" :author="bloggerInfo.nickname" />
|
||||||
:author="bloggerInfo.nickname" />
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 视频 -->
|
<!-- 视频 -->
|
||||||
<view v-if="moment.videos && moment.videos.length !== 0"
|
<view v-if="moment.videos && moment.videos.length !== 0"
|
||||||
class="video-list flex flex-col gap-3 px-4 pt-3">
|
class="box-border flex flex-col gap-3 px-4 pt-3">
|
||||||
<video v-for="(video, index) in moment.videos" :id="`video_${video.id}`" :key="index"
|
<video v-for="(video, index) in moment.videos" :id="`video_${video.id}`" :key="index"
|
||||||
class="video-src h-[400rpx] w-full rounded-xl" :src="video.url" :show-mute-btn="true"
|
class="video-src h-[400rpx] w-full rounded-xl" :src="video.url" :show-mute-btn="true"
|
||||||
:controls="true" :show-center-play-btn="true" :enable-progress-gesture="true"
|
:controls="true" :show-center-play-btn="true" :enable-progress-gesture="true"
|
||||||
@@ -367,7 +364,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="moment.spec.tags && moment.spec.tags.length !== 0"
|
<view v-if="moment.spec.tags && moment.spec.tags.length !== 0"
|
||||||
class="box-border px-4 mt-4 flex flex-wrap gap-2">
|
class="box-border px-4 mt-3 flex flex-wrap gap-2">
|
||||||
<text v-for="(tag, tagIndex) in moment.spec.tags" :key="tagIndex"
|
<text v-for="(tag, tagIndex) in moment.spec.tags" :key="tagIndex"
|
||||||
class="py-1 px-2 text-xs rounded-xl bg-secondary">
|
class="py-1 px-2 text-xs rounded-xl bg-secondary">
|
||||||
# {{ tag }}
|
# {{ tag }}
|
||||||
@@ -396,7 +393,6 @@
|
|||||||
<view class="load-text pb-5 pt-1 text-center text-xs text-gray-500">
|
<view class="load-text pb-5 pt-1 text-center text-xs text-gray-500">
|
||||||
{{ loadMoreText }}
|
{{ loadMoreText }}
|
||||||
</view>
|
</view>
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
Reference in New Issue
Block a user