mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-09-12 16:40:40 +08:00
b41e01106a
1. 修复双列布局下卡片样式仅允许image_top的逻辑 2. 增加卡片样式选项禁用状态与点击拦截 3. 调整设置项样式类与交互逻辑
264 lines
10 KiB
Vue
264 lines
10 KiB
Vue
<script lang="ts" setup>
|
||
import { computed, ref } from 'vue'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
import { useAppConfigStore } from '@/store/appConfig'
|
||
import { useSettingStore } from '@/store/setting'
|
||
import { collectSiteDefaults } from '@/utils/preference'
|
||
import { usePreferenceRows } from '@/hooks/usePreferenceRows'
|
||
import type { PrefDef } from '@/hooks/usePreferenceRows'
|
||
|
||
definePage({
|
||
style: {
|
||
navigationBarTitleText: '偏好设置',
|
||
navigationStyle: 'custom',
|
||
},
|
||
})
|
||
|
||
const settingStore = useSettingStore()
|
||
const appConfigStore = useAppConfigStore()
|
||
|
||
/** 公共:字段定义/三态/行构建/选值(与全局弹窗 uh-settings-popup 共用 usePreferenceRows) */
|
||
const {
|
||
SETTING_TABS,
|
||
layoutGroups,
|
||
featureRows,
|
||
prefValueOf,
|
||
isFollowing,
|
||
handleRevert,
|
||
handleBoolChange,
|
||
handleChoose,
|
||
isCardTypeOptionDisabled,
|
||
} = usePreferenceRows()
|
||
|
||
/** 确保启动合并已执行(入口页未跑或 H5 直达时兜底) */
|
||
onLoad(() => {
|
||
if (!settingStore.siteDefaults) {
|
||
settingStore.applySiteDefaults(collectSiteDefaults(appConfigStore.configs))
|
||
}
|
||
uni.setNavigationBarTitle({ title: '偏好设置' })
|
||
})
|
||
|
||
/* ---------------- 顶部分段器(布局 / 功能) ---------------- */
|
||
const activeTab = ref<'layout' | 'feature'>('layout')
|
||
|
||
/* ---------------- 交互 ---------------- */
|
||
/* ---------------- 枚举底部弹层(uh-glass-popup + wd-picker-view) ---------------- */
|
||
const enumSheet = ref<{ show : boolean, def : PrefDef | null }>({ show: false, def: null })
|
||
/** 弹层内滚动中的临时选中值(单列;确认时才落库,取消不生效) */
|
||
const pickerValue = ref<(string | number)[]>([''])
|
||
|
||
function handleOpenEnum(def : PrefDef) {
|
||
enumSheet.value = { show: true, def }
|
||
// 打开时同步当前值(跟随站点默认 → 空串哨兵)
|
||
pickerValue.value = [isFollowing(def.path) ? '' : String(prefValueOf(def.path) ?? '')]
|
||
}
|
||
|
||
function handleCloseEnum() {
|
||
enumSheet.value.show = false
|
||
}
|
||
|
||
function handleChooseEnum(value : string | null) {
|
||
const def = enumSheet.value.def
|
||
if (def) {
|
||
handleChoose(def.path, value)
|
||
}
|
||
handleCloseEnum()
|
||
}
|
||
|
||
/* ---------------- wd-picker-view 弹层数据 ---------------- */
|
||
/** 枚举弹层列(首项「跟随站点默认」,空串哨兵映射 null;双列约束下卡片样式仅保留 image_top) */
|
||
const enumColumns = computed(() => {
|
||
const def = enumSheet.value.def
|
||
if (!def) { return [] }
|
||
const options = (def.options || []).filter(opt => !isCardTypeOptionDisabled(def.path, opt.value))
|
||
return [
|
||
{ label: '跟随站点默认', value: '' },
|
||
...options.map(opt => ({ label: opt.label, value: opt.value })),
|
||
]
|
||
})
|
||
|
||
/** wd-picker-view 滚动变化:更新临时选中值(未确认不落库) */
|
||
function handlePickerChange(payload : { selectedValues : (string | number)[] }) {
|
||
pickerValue.value = payload.selectedValues
|
||
}
|
||
|
||
/** 确认:空串哨兵还原为「跟随站点默认」 */
|
||
function handlePickerConfirm() {
|
||
const picked = String(pickerValue.value[0] ?? '')
|
||
handleChooseEnum(picked === '' ? null : picked)
|
||
}
|
||
|
||
/** 取消:不落库,直接关闭 */
|
||
function handlePickerCancel() {
|
||
handleCloseEnum()
|
||
}
|
||
|
||
/* ---------------- 重置全部 ---------------- */
|
||
function handleResetAll() {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定将所有偏好恢复为站点默认吗?',
|
||
showCancel: true,
|
||
cancelText: '取消',
|
||
confirmText: '确定',
|
||
confirmColor: '#B9E424',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
settingStore.resetPreferences()
|
||
enumSheet.value.show = false
|
||
uni.showToast({ icon: 'none', title: '已恢复为站点默认' })
|
||
}
|
||
},
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<view class="box-border min-h-screen bg-page">
|
||
<!-- 自定义标题 -->
|
||
<uh-navbar default-title="偏好设置" title-color="text-gray-900" :need-placeholder="true" />
|
||
|
||
<!-- 内容区域 -->
|
||
<view class="box-border flex flex-col gap-y-6 p-3">
|
||
<!-- 顶部分段器:布局 / 功能 -->
|
||
<view class="uh-global-card-glass flex rounded-full p-1">
|
||
<view v-for="tab in SETTING_TABS" :key="tab.key" class="flex-1 rounded-full py-1.5 text-center text-sm"
|
||
:class="activeTab === tab.key ? 'bg-primary font-bold' : 'text-gray-500'"
|
||
@click="activeTab = tab.key">
|
||
{{ tab.label }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 布局:按页面分组(首页/文章列表/文章归档 × 列表布局/卡片样式) -->
|
||
<template v-if="activeTab === 'layout'">
|
||
<view v-for="group in layoutGroups" :key="group.key" class="flex flex-col gap-y-3">
|
||
<uh-section-title>{{ group.label }}</uh-section-title>
|
||
<view class="uh-global-card-glass overflow-hidden rounded-2xl">
|
||
<view v-for="(row, index) in group.rows" :key="row.key"
|
||
class="pick-row flex items-center justify-between px-4 py-4"
|
||
:class="index < group.rows.length - 1 ? 'border-b border-black/5' : ''"
|
||
@click="handleOpenEnum(row)">
|
||
<view class="row-left flex flex-col gap-1">
|
||
<text class="row-label text-[28rpx] text-gray-900 font-bold">{{ row.label }}</text>
|
||
<view class="flex items-center gap-2">
|
||
<text v-if="row.following" class="row-sub text-2xs text-gray-400">跟随站点默认</text>
|
||
<view v-else
|
||
class="rounded-full bg-secondary px-2 py-0.5 text-[20rpx] text-[#4d7c0f] leading-none">
|
||
已自定义
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="row-value flex items-center gap-2">
|
||
<text class="value-text text-[26rpx] text-gray-400">{{ row.displayValue }}</text>
|
||
<wd-icon name="arrow-right" size="12px" color="#c8c2b4" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<!-- 功能设置 -->
|
||
<template v-else>
|
||
<view class="flex flex-col gap-y-3">
|
||
<uh-section-title>
|
||
功能
|
||
<template #right>
|
||
<text class="text-2xs text-gray-400">一些常用的功能性设置</text>
|
||
</template>
|
||
</uh-section-title>
|
||
<view class="setting-sheet uh-global-card-glass overflow-hidden rounded-2xl">
|
||
<template v-for="(row, index) in featureRows" :key="row.key">
|
||
<!-- 布尔项:内联分段器(默认 / 开 / 关) -->
|
||
<view v-if="row.kind === 'bool'" class="box-border p-3"
|
||
:class="index < featureRows.length - 1 ? 'border-b border-black/5' : ''">
|
||
<view class="flex items-center justify-between">
|
||
<text class="row-label text-[28rpx] text-gray-900 font-bold">{{ row.label }}</text>
|
||
<view class="flex items-center gap-2">
|
||
<text v-if="row.following" class="row-sub text-2xs text-gray-400">默认</text>
|
||
<view v-else
|
||
class="rounded-full bg-secondary px-2 py-1 text-xs text-gray-900 leading-none">
|
||
已自定义
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="mt-3 flex flex-wrap gap-2">
|
||
<view class="rounded-full px-3 py-1 text-xs uh-global-card-glass shadow-none border"
|
||
:class="row.following ? 'bg-secondary font-bold' : 'text-gray-500'"
|
||
@click="handleRevert(row.path)">
|
||
默认
|
||
</view>
|
||
<view class="rounded-full px-3 py-1 text-xs uh-global-card-glass shadow-none border"
|
||
:class="!row.following && row.boolValue ? 'bg-secondary font-bold' : 'text-gray-500'"
|
||
@click="handleBoolChange(row.path, true)">
|
||
开
|
||
</view>
|
||
<view class="rounded-full px-3 py-1 text-xs uh-global-card-glass shadow-none border"
|
||
:class="!row.following && !row.boolValue ? 'bg-secondary font-bold' : 'text-gray-500'"
|
||
@click="handleBoolChange(row.path, false)">
|
||
关
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 枚举选择 -->
|
||
<view v-else class="pick-row flex items-center justify-between px-4 py-4"
|
||
:class="index < featureRows.length - 1 ? 'border-b border-black/5' : ''"
|
||
@click="handleOpenEnum(row)">
|
||
<view class="row-left flex flex-col gap-1">
|
||
<text class="row-label text-[28rpx] text-gray-900 font-bold">{{ row.label }}</text>
|
||
<view class="flex items-center gap-2">
|
||
<text v-if="row.following" class="row-sub text-2xs text-gray-400">跟随站点默认</text>
|
||
<template v-else>
|
||
<view
|
||
class="rounded-full bg-secondary px-2 py-0.5 text-[20rpx] text-[#4d7c0f] leading-none">
|
||
已自定义
|
||
</view>
|
||
<text class="revert-text text-2xs text-gray-400 underline"
|
||
@click.stop="handleRevert(row.path)">
|
||
恢复默认
|
||
</text>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
<view class="row-value flex items-center gap-2">
|
||
<text class="value-text text-[26rpx] text-gray-400">{{ row.displayValue }}</text>
|
||
<wd-icon name="arrow-right" size="12px" color="#c8c2b4" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<!-- 底部操作栏-->
|
||
<view class="box-border w-full">
|
||
<uh-button custom-class="uh-global-card-glass py-2 !rounded-xl" @click="handleResetAll">
|
||
恢复默认
|
||
</uh-button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 枚举选择弹层(uh-glass-popup + wd-picker-view,底部取消/确认,参考 uh-album-photo-viewer 布局) -->
|
||
<uh-glass-popup v-model="enumSheet.show" :hide-when-close="false" position="bottom" custom-class="rounded-xl">
|
||
<view class="box-border px-4 py-4">
|
||
<!-- 标题 -->
|
||
<view class="mb-3 flex items-center justify-between">
|
||
<text class="text-md font-bold">{{ enumSheet.def?.label || '请选择' }}</text>
|
||
</view>
|
||
<!-- 选择器 -->
|
||
<wd-picker-view :columns="enumColumns" v-model="pickerValue"
|
||
custom-class="!p-0 !bg-transparent !rounded-xl overflow-hidden" @change="handlePickerChange" />
|
||
<!-- 底部操作:取消 / 确认 -->
|
||
<view class="mt-4 flex items-center justify-center gap-x-3">
|
||
<uh-button custom-class="flex-1 py-2 uh-global-card-glass border !rounded-xl bg-white/90"
|
||
@click="handlePickerCancel">
|
||
取消
|
||
</uh-button>
|
||
<uh-button
|
||
custom-class="flex-1 py-2 uh-global-card-glass !rounded-xl border bg-primary text-gray-900"
|
||
@click="handlePickerConfirm">
|
||
确定
|
||
</uh-button>
|
||
</view>
|
||
</view>
|
||
</uh-glass-popup>
|
||
</view>
|
||
</template> |